diff --git a/barcodes/pom.xml b/barcodes/pom.xml
index f18864b69c..e1aa2840f0 100644
--- a/barcodes/pom.xml
+++ b/barcodes/pom.xml
@@ -4,7 +4,7 @@
com.itextpdfroot
- 7.2.0
+ 7.2.1barcodesiText 7 - barcodes
diff --git a/barcodes/src/test/java/com/itextpdf/barcodes/BarcodePDF417Test.java b/barcodes/src/test/java/com/itextpdf/barcodes/BarcodePDF417Test.java
index 4e57c2466f..7ecab0e0a9 100644
--- a/barcodes/src/test/java/com/itextpdf/barcodes/BarcodePDF417Test.java
+++ b/barcodes/src/test/java/com/itextpdf/barcodes/BarcodePDF417Test.java
@@ -238,7 +238,7 @@ public void barcode417XObjectTest() throws IOException, InterruptedException {
barcode.setCode(text);
PdfFormXObject xObject = barcode.createFormXObject(document);
- canvas.addXObject(xObject, 10, 650);
+ canvas.addXObjectAt(xObject, 10, 650);
document.close();
diff --git a/commons/pom.xml b/commons/pom.xml
index b6b98f2c2a..787ccd8470 100644
--- a/commons/pom.xml
+++ b/commons/pom.xml
@@ -4,7 +4,7 @@
com.itextpdfroot
- 7.2.0
+ 7.2.1commonsiText 7 - commons
diff --git a/commons/src/main/java/com/itextpdf/commons/actions/ProductEventHandler.java b/commons/src/main/java/com/itextpdf/commons/actions/ProductEventHandler.java
index 2e35486bee..51c7cf5791 100644
--- a/commons/src/main/java/com/itextpdf/commons/actions/ProductEventHandler.java
+++ b/commons/src/main/java/com/itextpdf/commons/actions/ProductEventHandler.java
@@ -25,11 +25,12 @@ This file is part of the iText (R) project.
import com.itextpdf.commons.actions.confirmations.ConfirmEvent;
import com.itextpdf.commons.actions.confirmations.ConfirmedEventWrapper;
import com.itextpdf.commons.actions.contexts.UnknownContext;
-import com.itextpdf.commons.exceptions.UnknownProductException;
-import com.itextpdf.commons.logs.CommonsLogMessageConstant;
import com.itextpdf.commons.actions.processors.DefaultITextProductEventProcessor;
import com.itextpdf.commons.actions.processors.ITextProductEventProcessor;
import com.itextpdf.commons.actions.sequence.SequenceId;
+import com.itextpdf.commons.exceptions.ProductEventHandlerRepeatException;
+import com.itextpdf.commons.exceptions.UnknownProductException;
+import com.itextpdf.commons.logs.CommonsLogMessageConstant;
import com.itextpdf.commons.utils.MessageFormatUtil;
import java.util.ArrayList;
@@ -47,7 +48,11 @@ This file is part of the iText (R) project.
*/
final class ProductEventHandler extends AbstractContextBasedEventHandler {
static final ProductEventHandler INSTANCE = new ProductEventHandler();
+
private static final Logger LOGGER = LoggerFactory.getLogger(ProductEventHandler.class);
+ // The constant has the following value for two reasons. First, to avoid the infinite loop.
+ // Second, to retry event processing several times for technical reasons.
+ private static final int MAX_EVENT_RETRY_COUNT = 4;
private final ConcurrentHashMap processors = new ConcurrentHashMap<>();
private final WeakHashMap> events = new WeakHashMap<>();
@@ -63,24 +68,17 @@ private ProductEventHandler() {
*/
@Override
protected void onAcceptedEvent(AbstractContextBasedITextEvent event) {
- if (! (event instanceof AbstractProductProcessITextEvent)) {
- return;
- }
- final AbstractProductProcessITextEvent productEvent = (AbstractProductProcessITextEvent) event;
- final String productName = productEvent.getProductName();
- final ITextProductEventProcessor productEventProcessor = getActiveProcessor(productName);
- if (productEventProcessor == null) {
- throw new UnknownProductException(
- MessageFormatUtil.format(UnknownProductException.UNKNOWN_PRODUCT, productName));
- }
- productEventProcessor.onEvent(productEvent);
- if (productEvent.getSequenceId() != null) {
- if (productEvent instanceof ConfirmEvent) {
- wrapConfirmedEvent((ConfirmEvent) productEvent, productEventProcessor);
- } else {
- addEvent(productEvent.getSequenceId(), productEvent);
+ for (int i = 0; i < MAX_EVENT_RETRY_COUNT; i++) {
+ try {
+ tryProcessEvent(event);
+ // process succeeded
+ return;
+ } catch (ProductEventHandlerRepeatException repeatException) {
+ // ignore this exception to retry the processing
}
}
+ // the final processing retry
+ tryProcessEvent(event);
}
ITextProductEventProcessor addProcessor(ITextProductEventProcessor processor) {
@@ -138,6 +136,29 @@ void addEvent(SequenceId id, AbstractProductProcessITextEvent event) {
}
}
+ private void tryProcessEvent(AbstractContextBasedITextEvent event) {
+ if (! (event instanceof AbstractProductProcessITextEvent)) {
+ return;
+ }
+ final AbstractProductProcessITextEvent productEvent = (AbstractProductProcessITextEvent) event;
+ final String productName = productEvent.getProductName();
+ final ITextProductEventProcessor productEventProcessor = getActiveProcessor(productName);
+ if (productEventProcessor == null) {
+ throw new UnknownProductException(
+ MessageFormatUtil.format(UnknownProductException.UNKNOWN_PRODUCT, productName));
+ }
+
+ productEventProcessor.onEvent(productEvent);
+
+ if (productEvent.getSequenceId() != null) {
+ if (productEvent instanceof ConfirmEvent) {
+ wrapConfirmedEvent((ConfirmEvent) productEvent, productEventProcessor);
+ } else {
+ addEvent(productEvent.getSequenceId(), productEvent);
+ }
+ }
+ }
+
private void wrapConfirmedEvent(ConfirmEvent event, ITextProductEventProcessor productEventProcessor) {
synchronized (events) {
final List eventsList = events.get(event.getSequenceId());
diff --git a/commons/src/main/java/com/itextpdf/commons/actions/data/CommonsProductData.java b/commons/src/main/java/com/itextpdf/commons/actions/data/CommonsProductData.java
index d921506e8d..13d4acf417 100644
--- a/commons/src/main/java/com/itextpdf/commons/actions/data/CommonsProductData.java
+++ b/commons/src/main/java/com/itextpdf/commons/actions/data/CommonsProductData.java
@@ -28,7 +28,7 @@ This file is part of the iText (R) project.
public final class CommonsProductData {
static final String COMMONS_PUBLIC_PRODUCT_NAME = "Commons";
static final String COMMONS_PRODUCT_NAME = "commons";
- static final String COMMONS_VERSION = "7.2.0";
+ static final String COMMONS_VERSION = "7.2.1";
static final int COMMONS_COPYRIGHT_SINCE = 2000;
static final int COMMONS_COPYRIGHT_TO = 2021;
diff --git a/commons/src/main/java/com/itextpdf/commons/actions/data/ProductData.java b/commons/src/main/java/com/itextpdf/commons/actions/data/ProductData.java
index 9ac5373645..fccda50c2e 100644
--- a/commons/src/main/java/com/itextpdf/commons/actions/data/ProductData.java
+++ b/commons/src/main/java/com/itextpdf/commons/actions/data/ProductData.java
@@ -31,6 +31,7 @@ public final class ProductData {
private final String publicProductName;
private final String productName;
private final String version;
+ private final String minimalCompatibleLicenseKeyVersion;
private final int sinceCopyrightYear;
private final int toCopyrightYear;
@@ -45,9 +46,25 @@ public final class ProductData {
*/
public ProductData(String publicProductName, String productName, String version, int sinceCopyrightYear,
int toCopyrightYear) {
+ this(publicProductName, productName, version, null, sinceCopyrightYear, toCopyrightYear);
+ }
+
+ /**
+ * Creates a new instance of product data.
+ *
+ * @param publicProductName is a product name
+ * @param productName is a technical name of the product
+ * @param version is a version of the product
+ * @param minimalCompatibleLicenseKeyVersion is a minimal compatible version of licensekey library
+ * @param sinceCopyrightYear is the first year of a product development
+ * @param toCopyrightYear is a last year of a product development
+ */
+ public ProductData(String publicProductName, String productName, String version,
+ String minimalCompatibleLicenseKeyVersion, int sinceCopyrightYear, int toCopyrightYear) {
this.publicProductName = publicProductName;
this.productName = productName;
this.version = version;
+ this.minimalCompatibleLicenseKeyVersion = minimalCompatibleLicenseKeyVersion;
this.sinceCopyrightYear = sinceCopyrightYear;
this.toCopyrightYear = toCopyrightYear;
}
@@ -97,6 +114,15 @@ public int getToCopyrightYear() {
return toCopyrightYear;
}
+ /**
+ * Getter for the minimal compatible licensekey version.
+ *
+ * @return minimal compatible version of licensekey library.
+ */
+ public String getMinCompatibleLicensingModuleVersion() {
+ return minimalCompatibleLicenseKeyVersion;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/commons/src/main/java/com/itextpdf/commons/actions/processors/DefaultITextProductEventProcessor.java b/commons/src/main/java/com/itextpdf/commons/actions/processors/DefaultITextProductEventProcessor.java
index 963489735f..0186684465 100644
--- a/commons/src/main/java/com/itextpdf/commons/actions/processors/DefaultITextProductEventProcessor.java
+++ b/commons/src/main/java/com/itextpdf/commons/actions/processors/DefaultITextProductEventProcessor.java
@@ -96,6 +96,7 @@ public void onEvent(AbstractProductProcessITextEvent event) {
if (isNeededToLogMessage) {
String message = new String(MESSAGE_FOR_LOGGING, StandardCharsets.ISO_8859_1);
LOGGER.info(message);
+ // System out added with purpose. This is not a debug code
System.out.println(message);
}
}
diff --git a/commons/src/main/java/com/itextpdf/commons/exceptions/ProductEventHandlerRepeatException.java b/commons/src/main/java/com/itextpdf/commons/exceptions/ProductEventHandlerRepeatException.java
new file mode 100644
index 0000000000..781c761abd
--- /dev/null
+++ b/commons/src/main/java/com/itextpdf/commons/exceptions/ProductEventHandlerRepeatException.java
@@ -0,0 +1,37 @@
+/*
+ This file is part of the iText (R) project.
+ Copyright (c) 1998-2021 iText Group NV
+ Authors: iText Software.
+
+ This program is offered under a commercial and under the AGPL license.
+ For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+ AGPL licensing:
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+package com.itextpdf.commons.exceptions;
+
+/**
+ * The class represents a signal to the event handler that it is necessary to repeat the handling of the current event.
+ */
+public final class ProductEventHandlerRepeatException extends ITextException {
+ /**
+ * Creates a new instance of {@link ProductEventHandlerRepeatException} based on message.
+ *
+ * @param message the detail message
+ */
+ public ProductEventHandlerRepeatException(String message) {
+ super(message);
+ }
+}
diff --git a/commons/src/main/java/com/itextpdf/commons/utils/DateTimeUtil.java b/commons/src/main/java/com/itextpdf/commons/utils/DateTimeUtil.java
index 42f6939314..04c87f94cc 100644
--- a/commons/src/main/java/com/itextpdf/commons/utils/DateTimeUtil.java
+++ b/commons/src/main/java/com/itextpdf/commons/utils/DateTimeUtil.java
@@ -49,6 +49,7 @@ This file is part of the iText (R) project.
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
+import java.util.TimeZone;
/**
* This file is a helper class for internal usage only.
@@ -146,6 +147,11 @@ public static String format(Date date, String pattern) {
return initParserSDF(pattern).format(date);
}
+ public static long getCurrentTimeZoneOffset() {
+ TimeZone tz = TimeZone.getDefault();
+ return tz.getOffset(getCurrentTimeDate().getTime());
+ }
+
private static DateFormat initParserSDF(String pattern) {
final SimpleDateFormat parserSDF = new SimpleDateFormat(pattern);
parserSDF.setCalendar(new GregorianCalendar());
diff --git a/commons/src/main/java/com/itextpdf/commons/utils/ProcessInfo.java b/commons/src/main/java/com/itextpdf/commons/utils/ProcessInfo.java
new file mode 100644
index 0000000000..d86867ca1d
--- /dev/null
+++ b/commons/src/main/java/com/itextpdf/commons/utils/ProcessInfo.java
@@ -0,0 +1,74 @@
+/*
+ This file is part of the iText (R) project.
+ Copyright (c) 1998-2021 iText Group NV
+ Authors: iText Software.
+
+ This program is offered under a commercial and under the AGPL license.
+ For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+ AGPL licensing:
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+package com.itextpdf.commons.utils;
+
+/**
+ * Class contains a process information, such as process exit code and process output.
+ */
+public final class ProcessInfo {
+
+ private final int exitCode;
+ private final String processStdOutput;
+ private final String processErrOutput;
+
+ /**
+ * Create a new instance, containing a process information,
+ * such as process exit code, process standard and error outputs.
+ *
+ * @param exitCode exit code of the process.
+ * @param processStdOutput the standard output of the process.
+ * @param processErrOutput the error output of the process.
+ */
+ public ProcessInfo(int exitCode, String processStdOutput, String processErrOutput) {
+ this.exitCode = exitCode;
+ this.processStdOutput = processStdOutput;
+ this.processErrOutput = processErrOutput;
+ }
+
+ /**
+ * Getter for a process exit code.
+ *
+ * @return Returns a process exit code.
+ */
+ public int getExitCode() {
+ return exitCode;
+ }
+
+ /**
+ * Getter for a standard process output.
+ *
+ * @return Returns a process standard output string.
+ */
+ public String getProcessStdOutput() {
+ return processStdOutput;
+ }
+
+ /**
+ * Getter for an error process output.
+ *
+ * @return Returns a process error output string.
+ */
+ public String getProcessErrOutput() {
+ return processErrOutput;
+ }
+}
diff --git a/commons/src/main/java/com/itextpdf/commons/utils/SystemUtil.java b/commons/src/main/java/com/itextpdf/commons/utils/SystemUtil.java
index 84fb04b518..616973f067 100644
--- a/commons/src/main/java/com/itextpdf/commons/utils/SystemUtil.java
+++ b/commons/src/main/java/com/itextpdf/commons/utils/SystemUtil.java
@@ -45,6 +45,7 @@ This file is part of the iText (R) project.
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
@@ -130,6 +131,14 @@ public static StringBuilder runProcessAndCollectErrors(String execPath, String p
return printProcessErrorsOutput(runProcess(execPath, params, null));
}
+ public static ProcessInfo runProcessAndGetProcessInfo(String command, String params) throws IOException,
+ InterruptedException {
+ Process p = runProcess(command, params, null);
+ String processStdOutput = printProcessStandardOutput(p).toString();
+ String processErrOutput = printProcessErrorsOutput(p).toString();
+ return new ProcessInfo(p.waitFor(), processStdOutput, processErrOutput);
+ }
+
static Process runProcess(String execPath, String params, String workingDirPath) throws IOException {
List cmdList = prepareProcessArguments(execPath, params);
String[] cmdArray = cmdList.toArray(new String[0]);
@@ -181,8 +190,16 @@ static String getProcessOutput(Process p) throws IOException {
}
static StringBuilder printProcessErrorsOutput(Process p) throws IOException {
+ return printProcessOutput(p.getErrorStream());
+ }
+
+ static StringBuilder printProcessStandardOutput(Process p) throws IOException {
+ return printProcessOutput(p.getInputStream());
+ }
+
+ private static StringBuilder printProcessOutput(InputStream processStream) throws IOException {
StringBuilder builder = new StringBuilder();
- BufferedReader bre = new BufferedReader(new InputStreamReader(p.getErrorStream()));
+ BufferedReader bre = new BufferedReader(new InputStreamReader(processStream));
String line;
while ((line = bre.readLine()) != null) {
System.out.println(line);
diff --git a/commons/src/test/java/com/itextpdf/commons/actions/ProductEventHandlerTest.java b/commons/src/test/java/com/itextpdf/commons/actions/ProductEventHandlerTest.java
index 32c09a84b3..035b7dc094 100644
--- a/commons/src/test/java/com/itextpdf/commons/actions/ProductEventHandlerTest.java
+++ b/commons/src/test/java/com/itextpdf/commons/actions/ProductEventHandlerTest.java
@@ -24,34 +24,37 @@ This file is part of the iText (R) project.
import com.itextpdf.commons.actions.confirmations.ConfirmEvent;
import com.itextpdf.commons.actions.confirmations.ConfirmedEventWrapper;
+import com.itextpdf.commons.actions.processors.ITextProductEventProcessor;
import com.itextpdf.commons.actions.sequence.SequenceId;
import com.itextpdf.commons.ecosystem.ITextTestEvent;
+import com.itextpdf.commons.exceptions.ProductEventHandlerRepeatException;
import com.itextpdf.commons.exceptions.UnknownProductException;
import com.itextpdf.commons.utils.MessageFormatUtil;
+import com.itextpdf.test.AssertUtil;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.annotations.type.UnitTest;
import org.junit.Assert;
-import org.junit.Rule;
+import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
-import org.junit.rules.ExpectedException;
@Category(UnitTest.class)
public class ProductEventHandlerTest extends ExtendedITextTest {
- @Rule
- public ExpectedException junitExpectedException = ExpectedException.none();
+ @Before
+ public void clearProcessors() {
+ ProductEventHandler.INSTANCE.clearProcessors();
+ }
@Test
public void unknownProductTest() {
ProductEventHandler handler = ProductEventHandler.INSTANCE;
- junitExpectedException.expect(UnknownProductException.class);
- junitExpectedException.expectMessage(
- MessageFormatUtil.format(UnknownProductException.UNKNOWN_PRODUCT, "Unknown Product"));
-
- handler.onAcceptedEvent(new ITextTestEvent(new SequenceId(), null, "test-event",
- "Unknown Product"));
+ AbstractContextBasedITextEvent event = new ITextTestEvent(new SequenceId(), null, "test-event", "Unknown Product");
+ Exception ex = Assert.assertThrows(UnknownProductException.class,
+ () -> handler.onAcceptedEvent(event));
+ Assert.assertEquals(MessageFormatUtil.format(UnknownProductException.UNKNOWN_PRODUCT, "Unknown Product"),
+ ex.getMessage());
}
@Test
@@ -114,4 +117,75 @@ public void confirmEventTest() {
Assert.assertTrue(handler.getEvents(sequenceId).get(0) instanceof ConfirmedEventWrapper);
Assert.assertEquals(event, ((ConfirmedEventWrapper) handler.getEvents(sequenceId).get(0)).getEvent());
}
+
+ @Test
+ public void repeatEventHandlingWithFiveExceptionOnProcessingTest() {
+ ProductEventHandler handler = ProductEventHandler.INSTANCE;
+
+ handler.addProcessor(new RepeatEventProcessor(5));
+
+ AbstractContextBasedITextEvent event = new ITextTestEvent(new SequenceId(), null, "test",
+ ProductNameConstant.ITEXT_CORE);
+
+ Exception e = Assert.assertThrows(ProductEventHandlerRepeatException.class,
+ () -> handler.onAcceptedEvent(event));
+ Assert.assertEquals("customMessage5", e.getMessage());
+ }
+
+ @Test
+ public void repeatEventHandlingWithFourExceptionOnProcessingTest() {
+ ProductEventHandler handler = ProductEventHandler.INSTANCE;
+
+ handler.addProcessor(new RepeatEventProcessor(4));
+
+ AbstractContextBasedITextEvent event = new ITextTestEvent(new SequenceId(), null, "test",
+ ProductNameConstant.ITEXT_CORE);
+
+ AssertUtil.doesNotThrow(() -> handler.onAcceptedEvent(event));
+ }
+
+ @Test
+ public void repeatEventHandlingWithOneExceptionOnProcessingTest() {
+ ProductEventHandler handler = ProductEventHandler.INSTANCE;
+
+ handler.addProcessor(new RepeatEventProcessor(1));
+
+ AbstractContextBasedITextEvent event = new ITextTestEvent(new SequenceId(), null, "test",
+ ProductNameConstant.ITEXT_CORE);
+
+ AssertUtil.doesNotThrow(() -> handler.onAcceptedEvent(event));
+ }
+
+ private static class RepeatEventProcessor implements ITextProductEventProcessor {
+ private final int exceptionsCount;
+ private int exceptionCounter = 0;
+
+ public RepeatEventProcessor(int exceptionsCount) {
+ this.exceptionsCount = exceptionsCount;
+ }
+
+ @Override
+ public void onEvent(AbstractProductProcessITextEvent event) {
+ if (exceptionCounter < exceptionsCount) {
+ exceptionCounter++;
+ throw new ProductEventHandlerRepeatException("customMessage" + exceptionCounter);
+ }
+
+ }
+
+ @Override
+ public String getProductName() {
+ return ProductNameConstant.ITEXT_CORE;
+ }
+
+ @Override
+ public String getUsageType() {
+ return "someUsage";
+ }
+
+ @Override
+ public String getProducer() {
+ return "someProducer";
+ }
+ }
}
diff --git a/commons/src/test/java/com/itextpdf/commons/actions/data/ProductDataTest.java b/commons/src/test/java/com/itextpdf/commons/actions/data/ProductDataTest.java
index 0a9b02268c..c1525e8f4a 100644
--- a/commons/src/test/java/com/itextpdf/commons/actions/data/ProductDataTest.java
+++ b/commons/src/test/java/com/itextpdf/commons/actions/data/ProductDataTest.java
@@ -42,6 +42,18 @@ public void productDataCreationTest() {
Assert.assertEquals(2100, productData.getToCopyrightYear());
}
+ @Test
+ public void productDataAnotherCreationTest() {
+ ProductData productData = new ProductData("publicProductName", "productName", "1.2", "4.0.0", 1900, 2100);
+
+ Assert.assertEquals("publicProductName", productData.getPublicProductName());
+ Assert.assertEquals("productName", productData.getProductName());
+ Assert.assertEquals("1.2", productData.getVersion());
+ Assert.assertEquals("4.0.0", productData.getMinCompatibleLicensingModuleVersion());
+ Assert.assertEquals(1900, productData.getSinceCopyrightYear());
+ Assert.assertEquals(2100, productData.getToCopyrightYear());
+ }
+
@Test
public void equalsTest() {
ProductData a = new ProductData("publicProductName", "productName", "1.2", 1900, 2100);
diff --git a/commons/src/test/java/com/itextpdf/commons/utils/DateTimeUtilTest.java b/commons/src/test/java/com/itextpdf/commons/utils/DateTimeUtilTest.java
index b39e73059d..2b000c8fec 100644
--- a/commons/src/test/java/com/itextpdf/commons/utils/DateTimeUtilTest.java
+++ b/commons/src/test/java/com/itextpdf/commons/utils/DateTimeUtilTest.java
@@ -25,6 +25,7 @@ This file is part of the iText (R) project.
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.annotations.type.UnitTest;
+import java.util.Calendar;
import java.util.Date;
import org.junit.Assert;
import org.junit.Test;
@@ -32,6 +33,10 @@ This file is part of the iText (R) project.
@Category(UnitTest.class)
public class DateTimeUtilTest extends ExtendedITextTest {
+
+ private static final double ZERO_DELTA = 1e-6;
+ private static final double ONE_SECOND_DELTA = 1000.0;
+
@Test
public void getCurrentTest() {
Date date = new Date();
@@ -43,4 +48,29 @@ public void isInPastTest() {
Date date = new Date(1);
Assert.assertTrue(DateTimeUtil.isInPast(date));
}
+
+ @Test
+ public void parseDateAndGetUtcMillisFromEpochTest() {
+ Calendar parsedDate = DateTimeUtil.getCalendar(DateTimeUtil.parseWithDefaultPattern("2020-05-05"));
+ double millisFromEpochTo2020_05_05 = DateTimeUtil.getUtcMillisFromEpoch(parsedDate);
+
+ long offset = DateTimeUtil.getCurrentTimeZoneOffset();
+ Assert.assertEquals(1588636800000d - offset, millisFromEpochTo2020_05_05, ZERO_DELTA);
+ }
+
+ @Test
+ public void compareUtcMillisFromEpochWithNullParamAndCurrentTimeTest() throws InterruptedException {
+ double getUtcMillisFromEpochWithNullParam = DateTimeUtil.getUtcMillisFromEpoch(null);
+ double millisFromEpochToCurrentTime = DateTimeUtil.getUtcMillisFromEpoch(DateTimeUtil.getCurrentTimeCalendar());
+
+ Assert.assertEquals(millisFromEpochToCurrentTime, getUtcMillisFromEpochWithNullParam, ONE_SECOND_DELTA);
+ }
+
+ @Test
+ public void parseDateAndGetRelativeTimeTest() {
+ double relativeTime = DateTimeUtil.getRelativeTime(DateTimeUtil.parseWithDefaultPattern("2020-05-05"));
+
+ long offset = DateTimeUtil.getCurrentTimeZoneOffset();
+ Assert.assertEquals(1588636800000d - offset, relativeTime, ZERO_DELTA);
+ }
}
diff --git a/commons/src/test/java/com/itextpdf/commons/utils/ProcessInfoTest.java b/commons/src/test/java/com/itextpdf/commons/utils/ProcessInfoTest.java
new file mode 100644
index 0000000000..9e8bf185c5
--- /dev/null
+++ b/commons/src/test/java/com/itextpdf/commons/utils/ProcessInfoTest.java
@@ -0,0 +1,58 @@
+/*
+ This file is part of the iText (R) project.
+ Copyright (c) 1998-2021 iText Group NV
+ Authors: iText Software.
+
+ This program is offered under a commercial and under the AGPL license.
+ For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+ AGPL licensing:
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+package com.itextpdf.commons.utils;
+
+import com.itextpdf.test.ExtendedITextTest;
+import com.itextpdf.test.annotations.type.UnitTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(UnitTest.class)
+public class ProcessInfoTest extends ExtendedITextTest {
+
+ @Test
+ public void getExitCodeTest() {
+ int exitCode = 1;
+ ProcessInfo processInfo = new ProcessInfo(exitCode, null, null);
+
+ Assert.assertEquals(exitCode, processInfo.getExitCode());
+ }
+
+ @Test
+ public void getProcessStdOutput() {
+ String stdOutput = "output";
+ ProcessInfo processInfo = new ProcessInfo(0, stdOutput, null);
+
+ Assert.assertEquals(stdOutput, processInfo.getProcessStdOutput());
+ }
+
+ @Test
+ public void getProcessErrOutput() {
+ String stdOutput = "output";
+ ProcessInfo processInfo = new ProcessInfo(0, null, stdOutput);
+
+ Assert.assertEquals(stdOutput, processInfo.getProcessErrOutput());
+ }
+}
diff --git a/commons/src/test/java/com/itextpdf/commons/utils/SystemUtilTest.java b/commons/src/test/java/com/itextpdf/commons/utils/SystemUtilTest.java
index 5e263e4e51..8b5772d1b4 100644
--- a/commons/src/test/java/com/itextpdf/commons/utils/SystemUtilTest.java
+++ b/commons/src/test/java/com/itextpdf/commons/utils/SystemUtilTest.java
@@ -165,6 +165,19 @@ public void runProcessAndWaitWithWorkingDirectoryTest() throws IOException, Inte
Assert.assertTrue(FileUtil.fileExists(diff));
}
+ @Test
+ public void runProcessAndGetProcessInfoTest() throws IOException, InterruptedException {
+ String imageMagickPath = SystemUtil.getPropertyOrEnvironmentVariable(MAGICK_COMPARE_ENVIRONMENT_VARIABLE);
+ if (imageMagickPath == null) {
+ imageMagickPath = SystemUtil.getPropertyOrEnvironmentVariable(MAGICK_COMPARE_ENVIRONMENT_VARIABLE_LEGACY);
+ }
+
+ ProcessInfo processInfo = SystemUtil.runProcessAndGetProcessInfo(imageMagickPath,"--version");
+
+ Assert.assertNotNull(processInfo);
+ Assert.assertEquals(0, processInfo.getExitCode());
+ }
+
static class TestProcess extends Process {
diff --git a/font-asian/pom.xml b/font-asian/pom.xml
index 03918b59d0..03ca569696 100644
--- a/font-asian/pom.xml
+++ b/font-asian/pom.xml
@@ -4,7 +4,7 @@
com.itextpdfroot
- 7.2.0
+ 7.2.1font-asianiText 7 - Asian fonts
diff --git a/forms/pom.xml b/forms/pom.xml
index 0e96927d20..85f191ca6f 100644
--- a/forms/pom.xml
+++ b/forms/pom.xml
@@ -4,7 +4,7 @@
com.itextpdfroot
- 7.2.0
+ 7.2.1formsiText 7 - forms
diff --git a/forms/src/main/java/com/itextpdf/forms/fields/PdfFormField.java b/forms/src/main/java/com/itextpdf/forms/fields/PdfFormField.java
index 27b63396f0..22cdfef6d1 100644
--- a/forms/src/main/java/com/itextpdf/forms/fields/PdfFormField.java
+++ b/forms/src/main/java/com/itextpdf/forms/fields/PdfFormField.java
@@ -2020,6 +2020,12 @@ public PdfFormField setBorderWidth(float borderWidth) {
return this;
}
+ /**
+ * Sets the border style for the field.
+ *
+ * @param style the new border style.
+ * @return the edited field
+ */
public PdfFormField setBorderStyle(PdfDictionary style) {
getWidgets().get(0).setBorderStyle(style);
regenerateField();
@@ -2275,10 +2281,21 @@ protected boolean isWrappedObjectMustBeIndirect() {
return true;
}
+ /**
+ * Gets the {@link PdfDocument} that owns that form field.
+ *
+ * @return the {@link PdfDocument} that owns that form field.
+ */
protected PdfDocument getDocument() {
return getPdfObject().getIndirectReference().getDocument();
}
+ /**
+ * Gets a {@link Rectangle} that matches the current size and position of this form field.
+ *
+ * @param field current form field.
+ * @return a {@link Rectangle} that matches the current size and position of this form field.
+ */
protected Rectangle getRect(PdfDictionary field) {
PdfArray rect = field.getAsArray(PdfName.Rect);
if (rect == null) {
@@ -2292,6 +2309,12 @@ protected Rectangle getRect(PdfDictionary field) {
return rect != null ? rect.toRectangle() : null;
}
+ /**
+ * Convert {@link String} multidimensional array of combo box or list options to {@link PdfArray}.
+ *
+ * @param options Two-dimensional array of options.
+ * @return a {@link PdfArray} that contains all the options.
+ */
protected static PdfArray processOptions(String[][] options) {
PdfArray array = new PdfArray();
for (String[] option : options) {
@@ -2302,6 +2325,12 @@ protected static PdfArray processOptions(String[][] options) {
return array;
}
+ /**
+ * Convert {@link String} array of combo box or list options to {@link PdfArray}.
+ *
+ * @param options array of options.
+ * @return a {@link PdfArray} that contains all the options.
+ */
protected static PdfArray processOptions(String[] options) {
PdfArray array = new PdfArray();
for (String option : options) {
diff --git a/hyph/pom.xml b/hyph/pom.xml
index be8bae62fd..d92651dc98 100644
--- a/hyph/pom.xml
+++ b/hyph/pom.xml
@@ -4,7 +4,7 @@
com.itextpdfroot
- 7.2.0
+ 7.2.1hyphiText 7 - hyph
diff --git a/io/pom.xml b/io/pom.xml
index a85fbbcd56..d4000b64f5 100644
--- a/io/pom.xml
+++ b/io/pom.xml
@@ -4,7 +4,7 @@
com.itextpdfroot
- 7.2.0
+ 7.2.1ioiText 7 - io
diff --git a/io/src/main/java/com/itextpdf/io/exceptions/IoExceptionMessage.java b/io/src/main/java/com/itextpdf/io/exceptions/IoExceptionMessage.java
index 59be5a2f37..d8f725309c 100644
--- a/io/src/main/java/com/itextpdf/io/exceptions/IoExceptionMessage.java
+++ b/io/src/main/java/com/itextpdf/io/exceptions/IoExceptionMessage.java
@@ -63,5 +63,7 @@ public final class IoExceptionMessage {
+ " environment variable to a CLI command that can run the Ghostscript application. See BUILDING.MD in the root of the repository for more details.";
public static final String GHOSTSCRIPT_FAILED = "GhostScript failed for ";
public static final String CANNOT_OPEN_OUTPUT_DIRECTORY = "Cannot open output directory for ";
-
+ public static final String IMAGE_MAGICK_OUTPUT_IS_NULL = "ImageMagick process output is null.";
+ public static final String IMAGE_MAGICK_PROCESS_EXECUTION_FAILED =
+ "ImageMagick process execution finished with errors: ";
}
diff --git a/io/src/main/java/com/itextpdf/io/logs/IoLogMessageConstant.java b/io/src/main/java/com/itextpdf/io/logs/IoLogMessageConstant.java
index 4f21551ae7..8cafd90a8c 100644
--- a/io/src/main/java/com/itextpdf/io/logs/IoLogMessageConstant.java
+++ b/io/src/main/java/com/itextpdf/io/logs/IoLogMessageConstant.java
@@ -85,6 +85,10 @@ public final class IoLogMessageConstant {
+ "as finished. Consider using com.itextpdf.layout.tagging.LayoutTaggingHelper#replaceKidHint "
+ "method for replacing not yet finished kid hint of a finished parent hint.";
+ /**
+ * @deprecated Unused constant. Will be removed in 7.3
+ */
+ @Deprecated
public static final String CANNOT_ADD_KID_HINT_WHICH_IS_ALREADY_ADDED_TO_ANOTHER_PARENT =
"Layout tagging hints addition failed: cannot add a kid hint to a new parent if it is already added to "
+ "another parent. Consider using com.itextpdf.layout.tagging.LayoutTaggingHelper#moveHint method "
@@ -168,6 +172,10 @@ public final class IoLogMessageConstant {
public static final String DOCUMENT_IDS_ARE_CORRUPTED = "The document original and/or modified id is corrupted";
+ /**
+ * @deprecated Unused constant. Will be removed in 7.3
+ */
+ @Deprecated
public static final String DOCUMENT_SERIALIZATION_EXCEPTION_RAISED = "Unhandled exception while serialization";
public static final String DOCUMENT_VERSION_IN_CATALOG_CORRUPTED = "The document version specified in catalog is "
@@ -175,6 +183,11 @@ public final class IoLogMessageConstant {
public static final String DURING_CONSTRUCTION_OF_ICC_PROFILE_ERROR_OCCURRED = "During the construction of the ICC"
+ " profile, the {0} error with message \"{1}\" occurred, the ICC profile will not be installed in the "
+ "image.";
+
+ /**
+ * @deprecated Unused constant. Will be removed in 7.3
+ */
+ @Deprecated
public static final String ELEMENT_DOES_NOT_FIT_AREA = "Element does not fit current area. {0}";
public static final String ELEMENT_WAS_FORCE_PLACED_KEEP_WITH_NEXT_WILL_BE_IGNORED =
@@ -304,6 +317,10 @@ public final class IoLogMessageConstant {
public static final String INLINE_BLOCK_ELEMENT_WILL_BE_CLIPPED =
"Inline block element does not fit into parent element and will be clipped";
+ /**
+ * @deprecated Unused constant. Will be removed in 7.3
+ */
+ @Deprecated
public static final String INPUT_STREAM_CONTENT_IS_LOST_ON_PDFSTREAM_SERIALIZATION =
"PdfStream contains not null input stream. It's content will be lost in serialized object.";
@@ -452,8 +469,16 @@ public final class IoLogMessageConstant {
public static final String TABLE_WIDTH_IS_MORE_THAN_EXPECTED_DUE_TO_MIN_WIDTH =
"Table width is more than expected due to min width of cell(s).";
+ /**
+ * @deprecated Unused constant. Will be removed in 7.3
+ */
+ @Deprecated
public static final String TAGGING_HINT_NOT_FINISHED_BEFORE_CLOSE = "Tagging hint wasn't finished before closing.";
+ /**
+ * @deprecated Unused constant. Will be removed in 7.3
+ */
+ @Deprecated
public static final String TAG_STRUCTURE_CONTEXT_WILL_BE_REINITIALIZED_ON_SERIALIZATION =
"Tag structure context is not null and will be reinitialized in the copy of document. The copy may lose "
+ "some data";
@@ -481,18 +506,34 @@ public final class IoLogMessageConstant {
+ "this means that element was added to the Canvas instance that was created not with constructor "
+ "taking PdfPage as argument. Not processed property: {0}";
+ /**
+ * @deprecated Unused constant. Will be removed in 7.3
+ */
+ @Deprecated
public static final String UNABLE_TO_INTERRUPT_THREAD = "Unable to interrupt a thread";
public static final String UNABLE_TO_INVERT_GRADIENT_TRANSFORMATION = "Unable to invert gradient transformation, "
+ "ignoring it";
+ /**
+ * @deprecated Unused constant. Will be removed in 7.3
+ */
+ @Deprecated
public static final String UNABLE_TO_REGISTER_EVENT_DATA_HANDLER_SHUTDOWN_HOOK =
"Unable to register event data handler shutdown hook because of security reasons.";
+ /**
+ * @deprecated Unused constant. Will be removed in 7.3
+ */
+ @Deprecated
public static final String UNABLE_TO_SEARCH_FOR_EVENT_CONTEXT =
"It is impossible to retrieve event context because of the security reasons. Event counting may behave in "
+ "unexpected way";
+ /**
+ * @deprecated Unused constant. Will be removed in 7.3
+ */
+ @Deprecated
public static final String UNABLE_TO_UNREGISTER_EVENT_DATA_HANDLER_SHUTDOWN_HOOK =
"Unable to unregister event data handler shutdown hook because of security permissions";
@@ -507,6 +548,10 @@ public final class IoLogMessageConstant {
public static final String UNKNOWN_COLOR_FORMAT_MUST_BE_RGB_OR_RRGGBB =
"Unknown color format: must be rgb or rrggbb.";
+ /**
+ * @deprecated Unused constant. Will be removed in 7.3
+ */
+ @Deprecated
public static final String UNKNOWN_DIGEST_METHOD =
"Unknown digest method. Valid values are MD5, SHA1 SHA256, SHA384, SHA512 and RIPEMD160.";
@@ -542,6 +587,10 @@ public final class IoLogMessageConstant {
public static final String XFDF_NO_F_OBJECT_TO_COMPARE = "Xfdf no f object to compare.";
+ /**
+ * @deprecated Unused constant. Will be removed in 7.3
+ */
+ @Deprecated
public static final String XFDF_OUTPUT_STREAM_CORRUPTED = "Xfdf output stream is corrupted.";
public static final String XFDF_UNSUPPORTED_ANNOTATION_ATTRIBUTE = "Xfdf unsupported attribute type";
diff --git a/io/src/main/java/com/itextpdf/io/source/OutputStream.java b/io/src/main/java/com/itextpdf/io/source/OutputStream.java
index d78161b335..086b5f1d91 100644
--- a/io/src/main/java/com/itextpdf/io/source/OutputStream.java
+++ b/io/src/main/java/com/itextpdf/io/source/OutputStream.java
@@ -51,19 +51,56 @@ public class OutputStream extends java.io.Output
//long=19 + max frac=6 => 26 => round to 32.
private final ByteBuffer numBuffer = new ByteBuffer(32);
-
+ private Boolean localHighPrecision;
protected java.io.OutputStream outputStream = null;
protected long currentPos = 0;
protected boolean closeStream = true;
+ /**
+ * Gets global high precision setting.
+ *
+ * @return global high precision setting.
+ */
public static boolean getHighPrecision() {
return ByteUtils.HighPrecision;
}
+ /**
+ * Sets global high precision setting for all {@link OutputStream} instances.
+ *
+ * @param value if true, all floats and double will be written with high precision
+ * in all {@link OutputStream} instances.
+ */
public static void setHighPrecision(boolean value) {
ByteUtils.HighPrecision = value;
}
+ /**
+ * Gets local high precision setting.
+ *
+ * @return local high precision setting.
+ */
+ public boolean getLocalHighPrecision() {
+ return this.localHighPrecision;
+ }
+
+ /**
+ * Sets local high precision setting for the {@link OutputStream}.
+ * Global {@link ByteUtils#HighPrecision} setting will be overridden by this one.
+ *
+ * @param value if true, all floats and double will be written with high precision
+ * in the underlying {@link OutputStream}.
+ */
+ public void setLocalHighPrecision(boolean value) {
+ this.localHighPrecision = value;
+ }
+
+ /**
+ * Creates a new {@link OutputStream} instance
+ * based on {@link java.io.OutputStream} instance.
+ *
+ * @param outputStream the {@link OutputStream} instance.
+ */
public OutputStream(java.io.OutputStream outputStream) {
super();
this.outputStream = outputStream;
@@ -76,6 +113,22 @@ protected OutputStream() {
super();
}
+ /**
+ * Creates a new {@link OutputStream} instance
+ * based on {@link java.io.OutputStream} instance and precision setting value.
+ *
+ * @param outputStream the {@link java.io.OutputStream} instance.
+ * @param localHighPrecision If true, all float and double values
+ * will be written with high precision.
+ * Global {@link ByteUtils#HighPrecision} setting
+ * will be overridden by this one.
+ */
+ public OutputStream(java.io.OutputStream outputStream, boolean localHighPrecision) {
+ super();
+ this.outputStream = outputStream;
+ this.localHighPrecision = localHighPrecision;
+ }
+
@Override
public void write(int b) throws java.io.IOException {
outputStream.write(b);
@@ -94,6 +147,13 @@ public void write(byte[] b, int off, int len) throws java.io.IOException {
currentPos += len;
}
+ /**
+ * See {@link java.io.OutputStream#write(int)}.
+ *
+ * @param value byte to write.
+ *
+ * @throws IOException if {@link java.io.IOException} occurs.
+ */
public void writeByte(byte value) {
try {
write(value);
@@ -109,10 +169,18 @@ public void flush() throws java.io.IOException {
@Override
public void close() throws java.io.IOException {
- if (closeStream)
+ if (closeStream) {
outputStream.close();
+ }
}
+ /**
+ * Writes long to internal {@link java.io.OutputStream} in ISO format.
+ *
+ * @param value value to write.
+ *
+ * @return this stream as passed generic stream.
+ */
public T writeLong(long value) {
try {
ByteUtils.getIsoBytes(value, numBuffer.reset());
@@ -123,6 +191,13 @@ public T writeLong(long value) {
}
}
+ /**
+ * Writes int to internal {@link java.io.OutputStream} in ISO format.
+ *
+ * @param value value to write.
+ *
+ * @return this stream as passed generic stream.
+ */
public T writeInteger(int value) {
try {
ByteUtils.getIsoBytes(value, numBuffer.reset());
@@ -133,27 +208,65 @@ public T writeInteger(int value) {
}
}
+ /**
+ * Writes float to internal {@link java.io.OutputStream} in ISO format.
+ *
+ * @param value value to write.
+ *
+ * @return this stream as passed generic stream.
+ */
public T writeFloat(float value) {
- return writeFloat(value, ByteUtils.HighPrecision);
+ return writeFloat(value, localHighPrecision == null ? ByteUtils.HighPrecision : localHighPrecision);
}
+ /**
+ * Writes float to internal {@link java.io.OutputStream} in ISO format.
+ *
+ * @param value value to write.
+ * @param highPrecision If true, float value will be written with high precision.
+ *
+ * @return this stream as passed generic stream.
+ */
public T writeFloat(float value, boolean highPrecision) {
return writeDouble(value, highPrecision);
}
+ /**
+ * Writes float array to internal {@link java.io.OutputStream} in ISO format.
+ *
+ * @param value float array to write.
+ *
+ * @return this stream as passed generic stream.
+ */
public T writeFloats(float[] value) {
for (int i = 0; i < value.length; i++) {
writeFloat(value[i]);
- if (i < value.length - 1)
+ if (i < value.length - 1) {
writeSpace();
+ }
}
return (T) this;
}
+ /**
+ * Writes double to internal {@link java.io.OutputStream} in ISO format.
+ *
+ * @param value value to write.
+ *
+ * @return this stream as passed generic stream.
+ */
public T writeDouble(double value) {
- return writeDouble(value, ByteUtils.HighPrecision);
+ return writeDouble(value, localHighPrecision == null ? ByteUtils.HighPrecision : localHighPrecision);
}
+ /**
+ * Writes double to internal {@link java.io.OutputStream} in ISO format.
+ *
+ * @param value value to write.
+ * @param highPrecision If true, double value will be written with high precision.
+ *
+ * @return this stream as passed generic stream.
+ */
public T writeDouble(double value, boolean highPrecision) {
try {
ByteUtils.getIsoBytes(value, numBuffer.reset(), highPrecision);
@@ -164,6 +277,13 @@ public T writeDouble(double value, boolean highPrecision) {
}
}
+ /**
+ * Writes byte to internal {@link java.io.OutputStream}.
+ *
+ * @param value value to write.
+ *
+ * @return this stream as passed generic stream.
+ */
public T writeByte(int value) {
try {
write(value);
@@ -173,18 +293,44 @@ public T writeByte(int value) {
}
}
+ /**
+ * Writes space to internal {@link java.io.OutputStream}.
+ *
+ * @return this stream as passed generic stream.
+ */
public T writeSpace() {
return writeByte(' ');
}
+ /**
+ * Writes new line to internal {@link java.io.OutputStream}.
+ *
+ * @return this stream as passed generic stream.
+ */
public T writeNewLine() {
return writeByte('\n');
}
+ /**
+ * Writes {@code String} to internal {@link java.io.OutputStream} in ISO format.
+ *
+ * @param value string to write.
+ *
+ * @return this stream as passed generic stream.
+ */
public T writeString(String value) {
return writeBytes(ByteUtils.getIsoBytes(value));
}
+ /**
+ * See {@link OutputStream#write(byte[])}.
+ *
+ * @param b byte array to write.
+ *
+ * @return this stream as passed generic stream.
+ *
+ * @throws com.itextpdf.io.exceptions.IOException if {@link java.io.IOException} is thrown.
+ */
public T writeBytes(byte[] b) {
try {
write(b);
@@ -194,6 +340,17 @@ public T writeBytes(byte[] b) {
}
}
+ /**
+ * See {@link OutputStream#write(byte[], int, int)}.
+ *
+ * @param b the data to write.
+ * @param off the start offset in the data.
+ * @param len the number of bytes to write.
+ *
+ * @return this stream as passed generic stream.
+ *
+ * @throws com.itextpdf.io.exceptions.IOException if {@link java.io.IOException} is thrown.
+ */
public T writeBytes(byte[] b, int off, int len) {
try {
write(b, off, len);
@@ -203,35 +360,70 @@ public T writeBytes(byte[] b, int off, int len) {
}
}
+ /**
+ * Gets current output stream position.
+ *
+ * @return current output stream position.
+ */
public long getCurrentPos() {
return currentPos;
}
+ /**
+ * Gets internal {@link java.io.OutputStream}.
+ *
+ * @return internal {@link java.io.OutputStream}.
+ */
public java.io.OutputStream getOutputStream() {
return outputStream;
}
+ /**
+ * Returns true, if internal {@link java.io.OutputStream} have to be closed after {@link #close()} call,
+ * false otherwise.
+ *
+ * @return true if stream needs to be closed, false if it's done manually.
+ */
public boolean isCloseStream() {
return closeStream;
}
+ /**
+ * Sets internal {@link java.io.OutputStream} to be closed after {@link OutputStream#close()}.
+ *
+ * @param closeStream true if stream needs to be closed, false if it's done manually.
+ */
public void setCloseStream(boolean closeStream) {
this.closeStream = closeStream;
}
+ /**
+ * See {@link ByteArrayOutputStream#assignBytes(byte[], int)}.
+ *
+ * @param bytes bytes to assign.
+ * @param count number of bytes to assign.
+ */
public void assignBytes(byte[] bytes, int count) {
if (outputStream instanceof ByteArrayOutputStream) {
((ByteArrayOutputStream) outputStream).assignBytes(bytes, count);
currentPos = count;
- } else
+ } else {
throw new IOException(IOException.BytesCanBeAssignedToByteArrayOutputStreamOnly);
+ }
}
+ /**
+ * See {@link ByteArrayOutputStream#reset()}.
+ *
+ * @throws com.itextpdf.io.exceptions.IOException if internal {@link OutputStream}.
+ * is not a {@link ByteArrayOutputStream} instance.
+ */
public void reset() {
if (outputStream instanceof ByteArrayOutputStream) {
((ByteArrayOutputStream) outputStream).reset();
currentPos = 0;
- } else
+ } else {
throw new IOException(IOException.BytesCanBeResetInByteArrayOutputStreamOnly);
+ }
}
}
diff --git a/io/src/main/java/com/itextpdf/io/util/ImageMagickCompareResult.java b/io/src/main/java/com/itextpdf/io/util/ImageMagickCompareResult.java
new file mode 100644
index 0000000000..c2ecb92acc
--- /dev/null
+++ b/io/src/main/java/com/itextpdf/io/util/ImageMagickCompareResult.java
@@ -0,0 +1,62 @@
+/*
+ This file is part of the iText (R) project.
+ Copyright (c) 1998-2021 iText Group NV
+ Authors: iText Software.
+
+ This program is offered under a commercial and under the AGPL license.
+ For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+ AGPL licensing:
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+package com.itextpdf.io.util;
+
+/**
+ * A helper data class, which aggregates true/false result of ImageMagick comparing
+ * as well as the number of different pixels.
+ */
+public final class ImageMagickCompareResult {
+
+ private final boolean result;
+ private final long diffPixels;
+
+ /**
+ * Creates an instance that contains ImageMagick comparing result information.
+ *
+ * @param result true, if the compared images are equal.
+ * @param diffPixels number of different pixels.
+ */
+ public ImageMagickCompareResult(boolean result, long diffPixels) {
+ this.result = result;
+ this.diffPixels = diffPixels;
+ }
+
+ /**
+ * Returns image compare boolean value.
+ *
+ * @return true if the compared images are equal.
+ */
+ public boolean isComparingResultSuccessful() {
+ return result;
+ }
+
+ /**
+ * Getter for a different pixels count.
+ *
+ * @return Returns a a different pixels count.
+ */
+ public long getDiffPixels() {
+ return diffPixels;
+ }
+}
diff --git a/io/src/main/java/com/itextpdf/io/util/ImageMagickHelper.java b/io/src/main/java/com/itextpdf/io/util/ImageMagickHelper.java
index fc3c1abf3b..1481912b3f 100644
--- a/io/src/main/java/com/itextpdf/io/util/ImageMagickHelper.java
+++ b/io/src/main/java/com/itextpdf/io/util/ImageMagickHelper.java
@@ -44,11 +44,13 @@ This file is part of the iText (R) project.
package com.itextpdf.io.util;
import com.itextpdf.commons.utils.FileUtil;
+import com.itextpdf.commons.utils.ProcessInfo;
import com.itextpdf.commons.utils.SystemUtil;
import com.itextpdf.io.exceptions.IoExceptionMessage;
-import java.io.File;
import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* A utility class that is used as an interface to run 3rd-party tool ImageMagick.
@@ -71,6 +73,9 @@ public class ImageMagickHelper {
static final String MAGICK_COMPARE_KEYWORD = "ImageMagick Studio LLC";
private static final String TEMP_FILE_PREFIX = "itext_im_io_temp";
+ private static final String DIFF_PIXELS_OUTPUT_REGEXP = "^\\d+\\.*\\d*(e\\+\\d+)?";
+
+ private static final Pattern pattern = Pattern.compile(DIFF_PIXELS_OUTPUT_REGEXP);
private String compareExec;
@@ -141,6 +146,58 @@ public boolean runImageMagickImageCompare(String outImageFilePath, String cmpIma
*/
public boolean runImageMagickImageCompare(String outImageFilePath, String cmpImageFilePath,
String diffImageName, String fuzzValue) throws IOException, InterruptedException {
+ ImageMagickCompareResult compareResult = runImageMagickImageCompareAndGetResult(outImageFilePath,
+ cmpImageFilePath, diffImageName, fuzzValue);
+
+ return compareResult.isComparingResultSuccessful();
+ }
+
+ /**
+ * Runs imageMagick to visually compare images with the specified fuzziness value and given threshold
+ * and generate difference output.
+ *
+ * @param outImageFilePath Path to the output image file
+ * @param cmpImageFilePath Path to the cmp image file
+ * @param diffImageName Path to the difference output image file
+ * @param fuzzValue String fuzziness value to compare images. Should be formatted as string with integer
+ * or decimal number. Can be null, if it is not required to use fuzziness
+ * @param threshold Long value of accepted threshold.
+ *
+ * @return boolean result of comparing: true - images are visually equal
+ *
+ * @throws IOException if there are file's reading/writing issues
+ * @throws InterruptedException if there is thread interruption while executing ImageMagick.
+ */
+ public boolean runImageMagickImageCompareWithThreshold(String outImageFilePath, String cmpImageFilePath,
+ String diffImageName, String fuzzValue, long threshold) throws IOException, InterruptedException {
+ ImageMagickCompareResult compareResult = runImageMagickImageCompareAndGetResult(outImageFilePath,
+ cmpImageFilePath, diffImageName, fuzzValue);
+
+ if (compareResult.isComparingResultSuccessful()) {
+ return true;
+ } else {
+ return compareResult.getDiffPixels() <= threshold;
+ }
+ }
+
+ /**
+ * Runs imageMagick to visually compare images with the specified fuzziness value and generate difference output.
+ * This method returns an object of {@link ImageMagickCompareResult}, containing comparing result information,
+ * such as boolean result value and the number of different pixels.
+ *
+ * @param outImageFilePath Path to the output image file
+ * @param cmpImageFilePath Path to the cmp image file
+ * @param diffImageName Path to the difference output image file
+ * @param fuzzValue String fuzziness value to compare images. Should be formatted as string with integer
+ * or decimal number. Can be null, if it is not required to use fuzziness
+ *
+ * @return an object of {@link ImageMagickCompareResult}. containing comparing result information.
+ *
+ * @throws IOException if there are file's reading/writing issues
+ * @throws InterruptedException if there is thread interruption while executing ImageMagick.
+ */
+ public ImageMagickCompareResult runImageMagickImageCompareAndGetResult(String outImageFilePath,
+ String cmpImageFilePath, String diffImageName, String fuzzValue) throws IOException, InterruptedException {
if (!validateFuzziness(fuzzValue)) {
throw new IllegalArgumentException("Invalid fuzziness value: " + fuzzValue);
}
@@ -160,12 +217,15 @@ public boolean runImageMagickImageCompare(String outImageFilePath, String cmpIma
+ replacementOutFile + "' '"
+ replacementCmpFile + "' '"
+ replacementDiff + "'";
- boolean result = SystemUtil.runProcessAndWait(compareExec, currCompareParams);
+ ProcessInfo processInfo = SystemUtil.runProcessAndGetProcessInfo(compareExec, currCompareParams);
+ boolean comparingResult = processInfo.getExitCode() == 0;
+ long diffPixels = parseImageMagickProcessOutput(processInfo.getProcessErrOutput());
+ ImageMagickCompareResult resultInfo = new ImageMagickCompareResult(comparingResult, diffPixels);
if (FileUtil.fileExists(replacementDiff)) {
FileUtil.copy(replacementDiff, diffImageName);
}
- return result;
+ return resultInfo;
} finally {
FileUtil.removeFiles(new String[] {replacementOutFile, replacementCmpFile, replacementDiff});
}
@@ -184,4 +244,29 @@ static boolean validateFuzziness(String fuzziness) {
}
}
}
+
+ private static long parseImageMagickProcessOutput(String processOutput) throws IOException {
+ if (null == processOutput) {
+ throw new IllegalArgumentException(IoExceptionMessage.IMAGE_MAGICK_OUTPUT_IS_NULL);
+ }
+
+ if (processOutput.isEmpty()) {
+ return 0L;
+ }
+
+ String[] processOutputLines = processOutput.split("\n");
+
+ for (String line : processOutputLines) {
+ try {
+ Matcher matcher = pattern.matcher(line);
+ if (matcher.find()) {
+ return (long) Double.valueOf(matcher.group()).longValue();
+ }
+ } catch (NumberFormatException e) {
+ // Nothing should be done here because of the exception, that will be thrown later.
+ }
+ }
+
+ throw new IOException(IoExceptionMessage.IMAGE_MAGICK_PROCESS_EXECUTION_FAILED + processOutput);
+ }
}
diff --git a/io/src/main/java/com/itextpdf/io/util/UrlUtil.java b/io/src/main/java/com/itextpdf/io/util/UrlUtil.java
index fb30ffae09..bdd7f586c7 100644
--- a/io/src/main/java/com/itextpdf/io/util/UrlUtil.java
+++ b/io/src/main/java/com/itextpdf/io/util/UrlUtil.java
@@ -103,21 +103,21 @@ public static InputStream openStream(URL url) throws IOException {
/**
* This method gets the last redirected url.
- * @param initialUrl an initial URL
- * @return the last redirected url
- * @throws IOException
+ *
+ * @param initialUrl an initial URL.
+ *
+ * @return the last redirected url.
+ *
+ * @throws IOException signals that an I/O exception has occurred.
+ *
+ * @deprecated {@link UrlUtil#getInputStreamOfFinalConnection(URL)} can be used to get input stream from final
+ * connection.
*/
+ @Deprecated
public static URL getFinalURL(URL initialUrl) throws IOException {
- URL finalUrl = null;
- URL nextUrl = initialUrl;
- while (nextUrl != null) {
- finalUrl = nextUrl;
- URLConnection connection = finalUrl.openConnection();
- String location = connection.getHeaderField("location");
- // Close input stream deliberately to close the handle which is created during getHeaderField invocation
- connection.getInputStream().close();
- nextUrl = location != null ? new URL(location) : null;
- }
+ final URLConnection finalConnection = getFinalConnection(initialUrl);
+ final URL finalUrl = finalConnection.getURL();
+ finalConnection.getInputStream().close();
return finalUrl;
}
@@ -138,4 +138,44 @@ public static String getFileUriString(String filename) throws MalformedURLExcept
public static String getNormalizedFileUriString(String filename) {
return "file://" + UrlUtil.toNormalizedURI(filename).getPath();
}
+
+ /**
+ * Gets the input stream of connection related to last redirected url. You should manually close input stream after
+ * calling this method to not hold any open resources.
+ *
+ * @param initialUrl an initial URL.
+ *
+ * @return an input stream of connection related to the last redirected url.
+ *
+ * @throws IOException signals that an I/O exception has occurred.
+ */
+ public static InputStream getInputStreamOfFinalConnection(URL initialUrl) throws IOException {
+ final URLConnection finalConnection = getFinalConnection(initialUrl);
+ return finalConnection.getInputStream();
+ }
+
+ /**
+ * Gets the connection related to the last redirected url. You should close connection manually after calling
+ * this method, to not hold any open resources.
+ *
+ * @param initialUrl an initial URL.
+ *
+ * @return connection related to the last redirected url.
+ *
+ * @throws IOException signals that an I/O exception has occurred.
+ */
+ static URLConnection getFinalConnection(URL initialUrl) throws IOException {
+ URL nextUrl = initialUrl;
+ URLConnection connection = null;
+ while (nextUrl != null) {
+ connection = nextUrl.openConnection();
+ final String location = connection.getHeaderField("location");
+ nextUrl = location == null ? null : new URL(location);
+ if (nextUrl != null) {
+ // close input stream deliberately to close the handle which is created during getHeaderField invocation
+ connection.getInputStream().close();
+ }
+ }
+ return connection;
+ }
}
diff --git a/io/src/test/java/com/itextpdf/io/source/OutputStreamTest.java b/io/src/test/java/com/itextpdf/io/source/OutputStreamTest.java
new file mode 100644
index 0000000000..6608ebc6c8
--- /dev/null
+++ b/io/src/test/java/com/itextpdf/io/source/OutputStreamTest.java
@@ -0,0 +1,369 @@
+/*
+
+ This file is part of the iText (R) project.
+ Copyright (c) 1998-2021 iText Group NV
+ Authors: Bruno Lowagie, Paulo Soares, et al.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License version 3
+ as published by the Free Software Foundation with the addition of the
+ following permission added to Section 15 as permitted in Section 7(a):
+ FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
+ ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
+ OF THIRD PARTY RIGHTS
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+ You should have received a copy of the GNU Affero General Public License
+ along with this program; if not, see http://www.gnu.org/licenses or write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA, 02110-1301 USA, or download the license from the following URL:
+ http://itextpdf.com/terms-of-use/
+
+ The interactive user interfaces in modified source and object code versions
+ of this program must display Appropriate Legal Notices, as required under
+ Section 5 of the GNU Affero General Public License.
+
+ In accordance with Section 7(b) of the GNU Affero General Public License,
+ a covered work must retain the producer line in every PDF that is created
+ or manipulated using iText.
+
+ You can be released from the requirements of the license by purchasing
+ a commercial license. Buying such a license is mandatory as soon as you
+ develop commercial activities involving the iText software without
+ disclosing the source code of your own applications.
+ These activities include: offering paid services to customers as an ASP,
+ serving PDFs on the fly in a web application, shipping iText with a closed
+ source product.
+
+ For more information, please contact iText Software Corp. at this
+ address: sales@itextpdf.com
+ */
+package com.itextpdf.io.source;
+
+import com.itextpdf.io.logs.IoLogMessageConstant;
+import com.itextpdf.test.AssertUtil;
+import com.itextpdf.test.ExtendedITextTest;
+import com.itextpdf.test.annotations.LogMessage;
+import com.itextpdf.test.annotations.LogMessages;
+import com.itextpdf.test.annotations.type.UnitTest;
+
+import java.io.FileOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.io.IOException;
+
+@Category(UnitTest.class)
+public class OutputStreamTest extends ExtendedITextTest {
+
+ private static final String SOURCE_FOLDER = "./src/test/resources/com/itextpdf/io/source/OSTEST.txt";
+ private static java.io.OutputStream IO_EXCEPTION_OUTPUT_STREAM;
+
+ static {
+ try {
+ IO_EXCEPTION_OUTPUT_STREAM = new FileOutputStream(SOURCE_FOLDER, true);
+ IO_EXCEPTION_OUTPUT_STREAM.close();
+ } catch (IOException e) {
+ //ignore
+ }
+ }
+
+ @Test
+ public void changePrecisionTest() throws IOException {
+ //the data is random
+ double expected = 0.100001d;
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes, false)) {
+ stream.setLocalHighPrecision(true);
+ stream.writeDouble(expected);
+ stream.flush();
+ Assert.assertEquals(Objects.toString(expected), new String(bytes.toByteArray(), StandardCharsets.UTF_8));
+ }
+ }
+
+ @Test
+ public void changePrecisionToFalseTest() throws IOException {
+ //the data is random
+ double expected = 0.000002d;
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes, false)) {
+ stream.setLocalHighPrecision(false);
+ stream.writeDouble(expected);
+ stream.flush();
+ Assert.assertEquals("0", new String(bytes.toByteArray(), StandardCharsets.UTF_8));
+ }
+ }
+
+ @LogMessages(messages = {
+ @LogMessage(messageTemplate = IoLogMessageConstant.ATTEMPT_PROCESS_NAN, count = 1)
+ })
+ @Test
+ public void writeNanTest() throws IOException {
+ //the data is random
+ String expected = "0";
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeDouble(Double.NaN);
+ stream.flush();
+ Assert.assertEquals(expected, new String(bytes.toByteArray(), StandardCharsets.UTF_8));
+ }
+ }
+
+ @Test
+ public void writeValidByteArrayTest() throws IOException {
+ //the data is random
+ byte[] expected = new byte[] {(byte) 68, (byte) 14, (byte) 173, (byte) 105};
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.write(expected);
+ stream.flush();
+ Assert.assertArrayEquals(expected, bytes.toByteArray());
+ }
+ }
+
+ @Test
+ public void writeValidBytesArrayTest() throws IOException {
+ //the data is random
+ byte[] expected = new byte[] {(byte) 15, (byte) 233, (byte) 58, (byte) 97};
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeBytes(expected);
+ stream.flush();
+ Assert.assertArrayEquals(expected, bytes.toByteArray());
+ }
+ }
+
+ @Test
+ public void writeSingleValidByteTest() throws IOException {
+ //the data is random
+ byte expected = (byte) 193;
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeByte(expected);
+ stream.flush();
+ Assert.assertArrayEquals(new byte[] {expected}, bytes.toByteArray());
+ }
+ }
+
+ @Test
+ public void writeSingleValidIntegerTest() throws IOException {
+ //the data is random
+ int expected = 1695609641;
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeInteger(expected);
+ stream.flush();
+ Assert.assertEquals(Objects.toString(expected), new String(bytes.toByteArray(), StandardCharsets.UTF_8));
+ }
+ }
+
+ @Test
+ public void writeSingleValidLongTest() throws IOException {
+ //the data is random
+ long expected = 1695609641552L;
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeLong(expected);
+ stream.flush();
+ Assert.assertEquals(Objects.toString(expected), new String(bytes.toByteArray(), StandardCharsets.UTF_8));
+ }
+ }
+
+ @Test
+ public void writeValidFloatsArrayTest() throws IOException {
+ //the data is random
+ float[] expected = new float[] {12.05f, 0.001f};
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeFloats(expected);
+ stream.flush();
+ Assert.assertEquals(expected[0] + " " + expected[1], new String(bytes.toByteArray(), StandardCharsets.UTF_8));
+ }
+ }
+
+ @Test
+ public void writeValidBytesWithOffsetTest() throws IOException {
+ //the data is random
+ byte[] expected = new byte[] {(byte) 233, (byte) 58};
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeBytes(new byte[] {(byte) 15, (byte) 233, (byte) 58, (byte) 97}, 1, 2);
+ stream.flush();
+ Assert.assertArrayEquals(expected, bytes.toByteArray());
+ }
+ }
+
+ @Test()
+ public void writeBytesIOExceptionTest() throws IOException {
+ //Testing that the exception is thrown, not using specific one because of .NET compatability
+ Assert.assertThrows(Exception.class,() -> {
+ byte[] bytesToWrite = new byte[] {(byte) 71};
+ try (java.io.OutputStream bytes = IO_EXCEPTION_OUTPUT_STREAM;
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeBytes(bytesToWrite);
+ }
+ });
+ }
+
+ @Test()
+ public void writeByteIOExceptionTest() throws IOException {
+ //Testing that the exception is thrown, not using specific one because of .NET compatability
+ Assert.assertThrows(Exception.class,() -> {
+ byte byteToWrite = (byte) 71;
+ try (java.io.OutputStream bytes = IO_EXCEPTION_OUTPUT_STREAM;
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeByte(byteToWrite);
+ }
+ });
+ }
+
+ @Test()
+ public void writeByteIntIOExceptionTest() throws IOException {
+ //Testing that the exception is thrown, not using specific one because of .NET compatability
+ Assert.assertThrows(Exception.class,() -> {
+ //the data is random
+ int byteToWrite = 71;
+ try (java.io.OutputStream bytes = IO_EXCEPTION_OUTPUT_STREAM;
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeByte(byteToWrite);
+ }
+ });
+ }
+
+ @Test()
+ public void writeDoubleIOExceptionTest() throws IOException {
+ //Testing that the exception is thrown, not using specific one because of .NET compatability
+ Assert.assertThrows(Exception.class,() -> {
+ //the data is random
+ double num = 55.55d;
+ try (java.io.OutputStream bytes = IO_EXCEPTION_OUTPUT_STREAM;
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeDouble(num);
+ }
+ });
+ }
+
+ @Test()
+ public void writeLongIOExceptionTest() throws IOException {
+ //Testing that the exception is thrown, not using specific one because of .NET compatability
+ Assert.assertThrows(Exception.class,() -> {
+ //the data is random
+ long num = 55L;
+ try (java.io.OutputStream bytes = IO_EXCEPTION_OUTPUT_STREAM;
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeLong(num);
+ }
+ });
+ }
+
+ @Test
+ public void writeValidStringTest() throws IOException {
+ String expected = "Test string to write";
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeString(expected);
+ stream.writeNewLine();
+ stream.flush();
+ Assert.assertEquals(expected + '\n', new String(bytes.toByteArray(), StandardCharsets.UTF_8));
+ }
+ }
+
+ @Test
+ public void gettersAndSettersTest() throws IOException {
+ AssertUtil.doesNotThrow(() -> {
+ //testing that stream is not closed, if setCloseStream is false
+ OutputStream stream
+ = new OutputStream<>(null);
+ stream.setCloseStream(false);
+ stream.close();
+ });
+ }
+
+ @Test
+ public void assignBytesArrayTest() throws IOException {
+ //the data is random
+ byte[] expected = new byte[] {(byte) 15, (byte) 233, (byte) 58, (byte) 97};
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.assignBytes(expected, 4);
+ Assert.assertArrayEquals(expected, bytes.toByteArray());
+ }
+ }
+
+ @Test
+ public void assignBytesExceptionTest() throws IOException {
+ //Testing that the exception is thrown, not using specific one because of .NET compatability
+ Assert.assertThrows(Exception.class,() -> {
+ //the data is random
+ byte[] bytes = new byte[] {(byte) 15, (byte) 233, (byte) 58, (byte) 97};
+ try (java.io.OutputStream outputStream = IO_EXCEPTION_OUTPUT_STREAM;
+ OutputStream stream
+ = new OutputStream<>(outputStream)) {
+ stream.assignBytes(bytes, 4);
+ }
+ });
+ }
+
+ @Test
+ public void resetTestNoException() throws IOException {
+ AssertUtil.doesNotThrow(() -> {
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.writeBytes(new byte[] {(byte) 15, (byte) 233, (byte) 58, (byte) 97});
+ stream.flush();
+ stream.reset();
+ }
+ });
+ }
+
+ @Test
+ public void resetExceptionTest() throws IOException {
+ //Testing that the exception is thrown, not using specific one because of .NET compatability
+ Assert.assertThrows(Exception.class,() -> {
+ try (java.io.OutputStream bytes = IO_EXCEPTION_OUTPUT_STREAM;
+ OutputStream stream
+ = new OutputStream<>(bytes)) {
+ stream.reset();
+ }
+ });
+ }
+
+ @Test
+ public void localHighPrecisionOverridesGlobalTest() throws IOException {
+ //the data is random
+ double numberToWrite = 2.000002d;
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStream stream
+ = new OutputStream<>(bytes, false)) {
+ OutputStream.setHighPrecision(true);
+ stream.setLocalHighPrecision(false);
+ stream.writeDouble(numberToWrite);
+ stream.flush();
+ Assert.assertEquals("2", new String(bytes.toByteArray(), StandardCharsets.UTF_8));
+ }
+ }
+}
diff --git a/io/src/test/java/com/itextpdf/io/util/ImageMagickHelperTest.java b/io/src/test/java/com/itextpdf/io/util/ImageMagickHelperTest.java
index 9f74642db5..ef771df384 100644
--- a/io/src/test/java/com/itextpdf/io/util/ImageMagickHelperTest.java
+++ b/io/src/test/java/com/itextpdf/io/util/ImageMagickHelperTest.java
@@ -51,6 +51,8 @@ This file is part of the iText (R) project.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -349,4 +351,81 @@ public void compareEqualsImagesAndCheckFuzzinessTest() {
}
}
+ @Test
+ public void compareEqualImagesAndGetResult() throws InterruptedException, IOException {
+ String image = SOURCE_FOLDER + "image.png";
+ String diff = DESTINATION_FOLDER + "diff_equalImages_result.png";
+
+ ImageMagickCompareResult result = new ImageMagickHelper().runImageMagickImageCompareAndGetResult(
+ image,
+ image,
+ diff,
+ "1");
+
+ Assert.assertTrue(result.isComparingResultSuccessful());
+ Assert.assertEquals(0, result.getDiffPixels());
+ }
+
+ @Test
+ public void compareDifferentImagesAndGetResult() throws InterruptedException, IOException {
+ String image = SOURCE_FOLDER + "image.png";
+ String image2 = SOURCE_FOLDER + "Im1_1.jpg";
+ String diff = DESTINATION_FOLDER + "diff_equalImages.png";
+
+ ImageMagickCompareResult result = new ImageMagickHelper().runImageMagickImageCompareAndGetResult(
+ image,
+ image2,
+ diff, "1");
+
+ Assert.assertFalse(result.isComparingResultSuccessful());
+ }
+
+ @Test
+ public void runImageMagickImageCompareEqualWithThreshold() throws IOException, InterruptedException {
+ String image = SOURCE_FOLDER + "image.png";
+ String image2 = SOURCE_FOLDER + "image.png";
+ String diff = DESTINATION_FOLDER + "diff_equalImages.png";
+
+ boolean result = new ImageMagickHelper().runImageMagickImageCompareWithThreshold(
+ image,
+ image2,
+ diff,
+ "0",
+ 0);
+
+ Assert.assertTrue(result);
+ }
+
+ @Test
+ public void runImageMagickImageCompareWithEnoughThreshold() throws IOException, InterruptedException {
+ String image = SOURCE_FOLDER + "image.png";
+ String image2 = SOURCE_FOLDER + "Im1_1.jpg";
+ String diff = DESTINATION_FOLDER + "diff_equalImages.png";
+
+ boolean result = new ImageMagickHelper().runImageMagickImageCompareWithThreshold(
+ image,
+ image2,
+ diff,
+ "20",
+ 2000000);
+
+ Assert.assertTrue(result);
+ }
+
+ @Test
+ public void runImageMagickImageCompareWithNotEnoughThreshold() throws IOException, InterruptedException {
+ String image = SOURCE_FOLDER + "image.png";
+ String image2 = SOURCE_FOLDER + "Im1_1.jpg";
+ String diff = DESTINATION_FOLDER + "diff_equalImages.png";
+
+ boolean result = new ImageMagickHelper().runImageMagickImageCompareWithThreshold(
+ image,
+ image2,
+ diff,
+ "20",
+ 2000);
+
+ Assert.assertFalse(result);
+ }
+
}
diff --git a/io/src/test/java/com/itextpdf/io/util/UrlUtilTest.java b/io/src/test/java/com/itextpdf/io/util/UrlUtilTest.java
index 3713bcca90..30e8ffb202 100644
--- a/io/src/test/java/com/itextpdf/io/util/UrlUtilTest.java
+++ b/io/src/test/java/com/itextpdf/io/util/UrlUtilTest.java
@@ -45,9 +45,13 @@ This file is part of the iText (R) project.
import com.itextpdf.commons.utils.FileUtil;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.annotations.type.UnitTest;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import org.junit.Assert;
@@ -65,7 +69,9 @@ public static void beforeClass() {
createDestinationFolder(destinationFolder);
}
- // Tests that after invocation of the getFinalURL method for local files, no handles are left open and the file is free to be removed
+
+ // Tests that after invocation of the getFinalURL method for local files, no handles are left open and the file
+ // is free to be removed.
@Test
public void getFinalURLDoesNotLockFileTest() throws IOException {
File tempFile = FileUtil.createTempFile(destinationFolder);
@@ -75,6 +81,44 @@ public void getFinalURLDoesNotLockFileTest() throws IOException {
Assert.assertTrue(FileUtil.deleteFile(tempFile));
}
+ // Tests, that getFinalConnection will be redirected some times for other urls, and initialUrl will be different
+ // from final url.
+ @Test
+ public void getFinalConnectionWhileRedirectingTest() throws IOException {
+ URL initialUrl = new URL("http://itextpdf.com");
+ URL expectedURL = new URL("https://itextpdf.com/en");
+ URLConnection finalConnection = null;
+
+ try {
+ finalConnection = UrlUtil.getFinalConnection(initialUrl);
+
+ Assert.assertNotNull(finalConnection);
+ Assert.assertNotEquals(initialUrl, finalConnection.getURL());
+ Assert.assertEquals(expectedURL, finalConnection.getURL());
+ } finally {
+ finalConnection.getInputStream().close();
+ }
+ }
+
+ // This test checks that when we pass invalid url and trying get stream related to final redirected url,exception
+ // would be thrown.
+ @Test
+ public void getInputStreamOfFinalConnectionThrowExceptionTest() throws IOException {
+ URL invalidUrl = new URL("http://itextpdf");
+
+ Assert.assertThrows(UnknownHostException.class, () -> UrlUtil.getInputStreamOfFinalConnection(invalidUrl));
+ }
+
+ // This test checks that when we pass valid url and trying get stream related to final redirected url, it would
+ // not be null.
+ @Test
+ public void getInputStreamOfFinalConnectionTest() throws IOException {
+ URL initialUrl = new URL("http://itextpdf.com");
+ InputStream streamOfFinalConnectionOfInvalidUrl = UrlUtil.getInputStreamOfFinalConnection(initialUrl);
+
+ Assert.assertNotNull(streamOfFinalConnectionOfInvalidUrl);
+ }
+
@Test
public void getBaseUriTest() throws IOException {
String absolutePathRoot = Paths.get("").toAbsolutePath().toUri().toURL().toExternalForm();
diff --git a/io/src/test/resources/com/itextpdf/io/source/OSTEST.txt b/io/src/test/resources/com/itextpdf/io/source/OSTEST.txt
new file mode 100644
index 0000000000..5dd01c177f
--- /dev/null
+++ b/io/src/test/resources/com/itextpdf/io/source/OSTEST.txt
@@ -0,0 +1 @@
+Hello, world!
\ No newline at end of file
diff --git a/itextcore/pom.xml b/itextcore/pom.xml
index 111811a53e..4f0c76ed3b 100644
--- a/itextcore/pom.xml
+++ b/itextcore/pom.xml
@@ -3,7 +3,7 @@
4.0.0com.itextpdfitext7-core
- 7.2.0
+ 7.2.1pomiText 7 CoreA Free Java-PDF library
diff --git a/kernel/pom.xml b/kernel/pom.xml
index 356567bdeb..22872f401b 100644
--- a/kernel/pom.xml
+++ b/kernel/pom.xml
@@ -4,7 +4,7 @@
com.itextpdfroot
- 7.2.0
+ 7.2.1kerneliText 7 - kernel
diff --git a/kernel/src/main/java/com/itextpdf/kernel/actions/data/ITextCoreProductData.java b/kernel/src/main/java/com/itextpdf/kernel/actions/data/ITextCoreProductData.java
index 038639633b..7578ebd841 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/actions/data/ITextCoreProductData.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/actions/data/ITextCoreProductData.java
@@ -30,7 +30,7 @@ This file is part of the iText (R) project.
*/
public final class ITextCoreProductData {
private static final String CORE_PUBLIC_PRODUCT_NAME = "Core";
- private static final String CORE_VERSION = "7.2.0";
+ private static final String CORE_VERSION = "7.2.1";
private static final int CORE_COPYRIGHT_SINCE = 2000;
private static final int CORE_COPYRIGHT_TO = 2021;
diff --git a/kernel/src/main/java/com/itextpdf/kernel/crypto/CryptoUtil.java b/kernel/src/main/java/com/itextpdf/kernel/crypto/CryptoUtil.java
index 92db7d0d81..ef54e5b978 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/crypto/CryptoUtil.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/crypto/CryptoUtil.java
@@ -42,20 +42,31 @@ This file is part of the iText (R) project.
*/
package com.itextpdf.kernel.crypto;
+import com.itextpdf.commons.utils.MessageFormatUtil;
+import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
+
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1OutputStream;
/**
* This file is a helper class for internal usage only.
- * Be aware that it's API and functionality may be changed in future.
+ * Be aware that it's API and functionality may be changed in the future.
*/
public class CryptoUtil {
+
+ private CryptoUtil() {
+
+ }
+
public static Certificate readPublicCertificate(InputStream is) throws CertificateException {
return CertificateFactory.getInstance("X.509").generateCertificate(is);
}
@@ -65,4 +76,22 @@ public static PrivateKey readPrivateKeyFromPKCS12KeyStore(InputStream keyStore,
keystore.load(keyStore, pkPassword);
return (PrivateKey) keystore.getKey(pkAlias, pkPassword);
}
+
+ /**
+ * Creates {@link ASN1OutputStream} instance and asserts for unexpected ASN1 encodings.
+ *
+ * @param outputStream the underlying stream
+ * @param asn1Encoding ASN1 encoding that will be used for writing. Only DER and BER are allowed as values.
+ * See also {@link ASN1Encoding}.
+ *
+ * @return an {@link ASN1OutputStream} instance. Exact stream implementation is chosen based on passed encoding.
+ */
+ public static ASN1OutputStream createAsn1OutputStream(OutputStream outputStream, String asn1Encoding) {
+ if (!ASN1Encoding.DER.equals(asn1Encoding) && !ASN1Encoding.BER.equals(asn1Encoding)) {
+ throw new UnsupportedOperationException(
+ MessageFormatUtil.format(KernelExceptionMessageConstant.UNSUPPORTED_ASN1_ENCODING, asn1Encoding)
+ );
+ }
+ return ASN1OutputStream.create(outputStream, asn1Encoding);
+ }
}
diff --git a/kernel/src/main/java/com/itextpdf/kernel/crypto/securityhandler/PubKeySecurityHandler.java b/kernel/src/main/java/com/itextpdf/kernel/crypto/securityhandler/PubKeySecurityHandler.java
index 4e6c6e30a8..f82f090486 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/crypto/securityhandler/PubKeySecurityHandler.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/crypto/securityhandler/PubKeySecurityHandler.java
@@ -44,13 +44,27 @@ This file is part of the iText (R) project.
package com.itextpdf.kernel.crypto.securityhandler;
import com.itextpdf.io.util.StreamUtil;
-import com.itextpdf.kernel.exceptions.PdfException;
+import com.itextpdf.kernel.crypto.CryptoUtil;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
+import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.pdf.PdfArray;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfLiteral;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.security.IExternalDecryptionProcess;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OutputStream;
import org.bouncycastle.asn1.ASN1Primitive;
@@ -68,18 +82,6 @@ This file is part of the iText (R) project.
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.TBSCertificateStructure;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.Key;
-import java.security.MessageDigest;
-import java.security.PrivateKey;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* @author Aiken Sam (aikensam@ieee.org)
*/
@@ -250,7 +252,7 @@ private byte[] getEncodedRecipient(int index) throws IOException, GeneralSecurit
pkcs7input[23] = one;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ASN1OutputStream k = ASN1OutputStream.create(baos);
+ ASN1OutputStream k = CryptoUtil.createAsn1OutputStream(baos, ASN1Encoding.DER);
ASN1Primitive obj = createDERForRecipient(pkcs7input, (X509Certificate) certificate);
k.writeObject(obj);
cms = baos.toByteArray();
diff --git a/kernel/src/main/java/com/itextpdf/kernel/exceptions/KernelExceptionMessageConstant.java b/kernel/src/main/java/com/itextpdf/kernel/exceptions/KernelExceptionMessageConstant.java
index d3169bdcf0..3099c5ba42 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/exceptions/KernelExceptionMessageConstant.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/exceptions/KernelExceptionMessageConstant.java
@@ -29,20 +29,20 @@ public final class KernelExceptionMessageConstant {
public static final String AMOUNT_OF_BYTES_LESS_THAN_ZERO =
"Amount of bytes in the PDF document cannot be less than zero";
public static final String ANNOTATION_SHALL_HAVE_REFERENCE_TO_PAGE = "Annotation shall have reference to page.";
- public static final String APPEND_MODE_REQUIRES_A_DOCUMENT_WITHOUT_ERRORS_EVEN_IF_RECOVERY_IS_POSSIBLE = "Append "
- + "mode requires a document without errors, even if recovery is possible.";
+ public static final String APPEND_MODE_REQUIRES_A_DOCUMENT_WITHOUT_ERRORS_EVEN_IF_RECOVERY_IS_POSSIBLE =
+ "Append mode requires a document without errors, even if recovery is possible.";
public static final String BAD_CERTIFICATE_AND_KEY = "Bad public key certificate and/or private key.";
- public static final String BAD_USER_PASSWORD = "Bad user password. Password is not provided or wrong password "
- + "provided. Correct password should be passed to PdfReader constructor with properties. "
- + "See ReaderProperties#setPassword() method.";
+ public static final String BAD_USER_PASSWORD =
+ "Bad user password. Password is not provided or wrong password provided. Correct password should be passed "
+ + "to PdfReader constructor with properties. See ReaderProperties#setPassword() method.";
public static final String CANNOT_ADD_KID_TO_THE_FLUSHED_ELEMENT = "Cannot add kid to the flushed element.";
- public static final String CANNOT_BE_EMBEDDED_DUE_TO_LICENSING_RESTRICTIONS = "{0} cannot be embedded due to "
- + "licensing restrictions.";
+ public static final String CANNOT_BE_EMBEDDED_DUE_TO_LICENSING_RESTRICTIONS =
+ "{0} cannot be embedded due to licensing restrictions.";
public static final String CANNOT_CLOSE_DOCUMENT = "Cannot close document.";
- public static final String CANNOT_CLOSE_DOCUMENT_WITH_ALREADY_FLUSHED_PDF_CATALOG = "Cannot close document with "
- + "already flushed PDF Catalog.";
- public static final String CANNOT_CONVERT_PDF_ARRAY_TO_AN_ARRAY_OF_BOOLEANS = "Cannot convert PdfArray to an "
- + "array of booleans";
+ public static final String CANNOT_CLOSE_DOCUMENT_WITH_ALREADY_FLUSHED_PDF_CATALOG =
+ "Cannot close document with already flushed PDF Catalog.";
+ public static final String CANNOT_CONVERT_PDF_ARRAY_TO_AN_ARRAY_OF_BOOLEANS =
+ "Cannot convert PdfArray to an array of booleans";
public static final String CANNOT_CONVERT_PDF_ARRAY_TO_DOUBLE_ARRAY = "Cannot convert PdfArray to an array "
+ "of doubles.";
public static final String CANNOT_CONVERT_PDF_ARRAY_TO_INT_ARRAY = "Cannot convert PdfArray to an array "
@@ -122,6 +122,9 @@ public final class KernelExceptionMessageConstant {
public static final String CONTENT_STREAM_MUST_NOT_INVOKE_OPERATORS_THAT_SPECIFY_COLORS_OR_OTHER_COLOR_RELATED_PARAMETERS =
"Content stream must not invoke operators that specify colors or other color related parameters in "
+ "the graphics state.";
+ public static final String CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP =
+ "Document outline dictionary is corrupted: some outline (PDF object: \"{0}\") has wrong first/next link "
+ + "entry.";
public static final String CORRUPTED_OUTLINE_NO_PARENT_ENTRY =
"Document outline is corrupted: some outline (PDF object: \"{0}\") lacks the required parent entry.";
public static final String CORRUPTED_OUTLINE_NO_TITLE_ENTRY =
@@ -315,10 +318,12 @@ public final class KernelExceptionMessageConstant {
public static final String UNKNOWN_ENCRYPTION_TYPE_V = "Unknown encryption type V == {0}.";
public static final String UNKNOWN_GRAPHICS_STATE_DICTIONARY = "{0} is an unknown graphics state dictionary.";
public static final String UNKNOWN_PDF_EXCEPTION = "Unknown PdfException.";
+ public static final String UNSUPPORTED_ASN1_ENCODING =
+ "Unknown ASN1-encoding {0}. Only DER and BER encodings are supported!";
public static final String UNSUPPORTED_FONT_EMBEDDING_STRATEGY = "Unsupported font embedding strategy.";
public static final String UNSUPPORTED_XOBJECT_TYPE = "Unsupported XObject type.";
- public static final String WHEN_ADDING_OBJECT_REFERENCE_TO_THE_TAG_TREE_IT_MUST_BE_CONNECTED_TO_NOT_FLUSHED_OBJECT = ""
- + "When adding object reference to the tag tree, it must be connected to not flushed object.";
+ public static final String WHEN_ADDING_OBJECT_REFERENCE_TO_THE_TAG_TREE_IT_MUST_BE_CONNECTED_TO_NOT_FLUSHED_OBJECT =
+ "When adding object reference to the tag tree, it must be connected to not flushed object.";
public static final String WHITE_POINT_IS_INCORRECTLY_SPECIFIED = "White point is incorrectly specified.";
public static final String WMF_IMAGE_EXCEPTION = "WMF image exception.";
public static final String WRONG_MEDIA_BOX_SIZE_TOO_FEW_ARGUMENTS = "Wrong media box size: {0}. Need at least 4 "
diff --git a/kernel/src/main/java/com/itextpdf/kernel/font/PdfType0Font.java b/kernel/src/main/java/com/itextpdf/kernel/font/PdfType0Font.java
index b46ba35653..109c4c9e4f 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/font/PdfType0Font.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/font/PdfType0Font.java
@@ -451,6 +451,8 @@ private int appendUniGlyphs(String text, int from, int to, List glyphs) {
if (TextUtil.isSurrogatePair(text, k)) {
val = TextUtil.convertToUtf32(text, k);
processed += 2;
+ // Since a pair is processed, need to skip next char as well
+ k += 1;
} else {
val = text.charAt(k);
processed++;
diff --git a/kernel/src/main/java/com/itextpdf/kernel/font/PdfType3Font.java b/kernel/src/main/java/com/itextpdf/kernel/font/PdfType3Font.java
index f27434587c..1d6f4496f3 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/font/PdfType3Font.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/font/PdfType3Font.java
@@ -444,6 +444,8 @@ private void addGlyphsFromDifferences(PdfArray differences, PdfDictionary charPr
PdfObject obj = differences.get(k);
if (obj.isNumber()) {
currentNumber = ((PdfNumber) obj).intValue();
+ } else if (currentNumber > SIMPLE_FONT_MAX_CHAR_CODE_VALUE) {
+ // Skip glyphs with id greater than 255
} else {
String glyphName = ((PdfName) obj).getValue();
int unicode = fontEncoding.getUnicode(currentNumber);
diff --git a/kernel/src/main/java/com/itextpdf/kernel/logs/KernelLogMessageConstant.java b/kernel/src/main/java/com/itextpdf/kernel/logs/KernelLogMessageConstant.java
index 980cc29095..71ad08a8c1 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/logs/KernelLogMessageConstant.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/logs/KernelLogMessageConstant.java
@@ -48,6 +48,10 @@ This file is part of the iText (R) project.
*/
public final class KernelLogMessageConstant {
+ public static final String CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP =
+ "Document outline dictionary is corrupted: some outline (PDF object: \"{0}\") has wrong first/next link "
+ + "entry. Next outlines in this dictionary will be unprocessed.";
+
public static final String DCTDECODE_FILTER_DECODING =
"DCTDecode filter decoding into the bit map is not supported. The stream data would be left in JPEG "
+ "baseline format";
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/OcgPropertiesCopier.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/OcgPropertiesCopier.java
index b10a19eb02..534a253f27 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/OcgPropertiesCopier.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/OcgPropertiesCopier.java
@@ -115,24 +115,25 @@ private static Set getAllUsedNonFlushedOCGs(Map());
OcgPropertiesCopier.getUsedNonFlushedOCGsFromXObject(toAnnot.getRolloverAppearanceObject(),
- fromAnnot.getRolloverAppearanceObject(), fromUsedOcgs, toOcProperties);
+ fromAnnot.getRolloverAppearanceObject(), fromUsedOcgs, toOcProperties, new HashSet<>());
OcgPropertiesCopier.getUsedNonFlushedOCGsFromXObject(toAnnot.getDownAppearanceObject(),
- fromAnnot.getDownAppearanceObject(), fromUsedOcgs, toOcProperties);
+ fromAnnot.getDownAppearanceObject(), fromUsedOcgs, toOcProperties, new HashSet<>());
}
}
}
final PdfDictionary toResources = toPage.getPdfObject().getAsDictionary(PdfName.Resources);
final PdfDictionary fromResources = fromPage.getPdfObject().getAsDictionary(PdfName.Resources);
- OcgPropertiesCopier.getUsedNonFlushedOCGsFromResources(toResources, fromResources, fromUsedOcgs, toOcProperties);
+ OcgPropertiesCopier.getUsedNonFlushedOCGsFromResources(toResources, fromResources, fromUsedOcgs,
+ toOcProperties, new HashSet<>());
}
return fromUsedOcgs;
}
private static void getUsedNonFlushedOCGsFromResources(PdfDictionary toResources, PdfDictionary fromResources,
- Set fromUsedOcgs, PdfDictionary toOcProperties) {
+ Set fromUsedOcgs, PdfDictionary toOcProperties, Set visitedObjects) {
if (toResources != null && !toResources.isFlushed()) {
// Copy OCGs from properties
final PdfDictionary toProperties = toResources.getAsDictionary(PdfName.Properties);
@@ -148,12 +149,19 @@ private static void getUsedNonFlushedOCGsFromResources(PdfDictionary toResources
// Copy OCGs from xObject
final PdfDictionary toXObject = toResources.getAsDictionary(PdfName.XObject);
final PdfDictionary fromXObject = fromResources.getAsDictionary(PdfName.XObject);
- OcgPropertiesCopier.getUsedNonFlushedOCGsFromXObject(toXObject, fromXObject, fromUsedOcgs, toOcProperties);
+ OcgPropertiesCopier.getUsedNonFlushedOCGsFromXObject(toXObject, fromXObject, fromUsedOcgs, toOcProperties,
+ visitedObjects);
}
}
private static void getUsedNonFlushedOCGsFromXObject(PdfDictionary toXObject, PdfDictionary fromXObject,
- Set fromUsedOcgs, PdfDictionary toOcProperties) {
+ Set fromUsedOcgs, PdfDictionary toOcProperties, Set visitedObjects) {
+ //Resolving cycled properties, by memorizing the visited objects
+ if (visitedObjects.contains(fromXObject)) {
+ return;
+ }
+ visitedObjects.add(fromXObject);
+
if (toXObject != null && !toXObject.isFlushed()) {
if (toXObject.isStream() && !toXObject.isFlushed()) {
final PdfStream toStream = (PdfStream) toXObject;
@@ -161,7 +169,7 @@ private static void getUsedNonFlushedOCGsFromXObject(PdfDictionary toXObject, Pd
OcgPropertiesCopier.getUsedNonFlushedOCGsFromOcDict(toStream.getAsDictionary(PdfName.OC),
fromStream.getAsDictionary(PdfName.OC), fromUsedOcgs, toOcProperties);
OcgPropertiesCopier.getUsedNonFlushedOCGsFromResources(toStream.getAsDictionary(PdfName.Resources),
- fromStream.getAsDictionary(PdfName.Resources), fromUsedOcgs, toOcProperties);
+ fromStream.getAsDictionary(PdfName.Resources), fromUsedOcgs, toOcProperties, visitedObjects);
} else {
for (final PdfName name : toXObject.keySet()) {
final PdfObject toCurrObj = toXObject.get(name);
@@ -169,7 +177,8 @@ private static void getUsedNonFlushedOCGsFromXObject(PdfDictionary toXObject, Pd
if (toCurrObj.isStream() && !toCurrObj.isFlushed()) {
final PdfStream toStream = (PdfStream) toCurrObj;
final PdfStream fromStream = (PdfStream) fromCurrObj;
- OcgPropertiesCopier.getUsedNonFlushedOCGsFromXObject(toStream, fromStream, fromUsedOcgs, toOcProperties);
+ OcgPropertiesCopier.getUsedNonFlushedOCGsFromXObject(toStream, fromStream, fromUsedOcgs,
+ toOcProperties, visitedObjects);
}
}
}
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfCatalog.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfCatalog.java
index a6c9ee3aff..39d086d554 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfCatalog.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfCatalog.java
@@ -47,6 +47,8 @@ This file is part of the iText (R) project.
import com.itextpdf.commons.utils.MessageFormatUtil;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
+import com.itextpdf.kernel.logs.KernelLogMessageConstant;
+import com.itextpdf.kernel.pdf.PdfReader.StrictnessLevel;
import com.itextpdf.kernel.pdf.action.PdfAction;
import com.itextpdf.kernel.pdf.collection.PdfCollection;
import com.itextpdf.kernel.pdf.layer.PdfOCProperties;
@@ -91,7 +93,7 @@ public class PdfCatalog extends PdfObjectWrapper {
*/
protected PdfOCProperties ocProperties;
- private static final String OutlineRoot = "Outlines";
+ private static final String ROOT_OUTLINE_TITLE = "Outlines";
private PdfOutline outlines;
@@ -561,6 +563,91 @@ void addRootOutline(PdfOutline outline) {
}
}
+ /**
+ * Construct {@link PdfCatalog dictionary} iteratively. Invalid pdf documents will be processed depending on {@link
+ * StrictnessLevel}, if it set to lenient, we will ignore and process invalid outline structure, otherwise {@link
+ * PdfException} will be thrown.
+ *
+ * @param outlineRoot {@link PdfOutline dictionary} root.
+ * @param names map containing the PdfObjects stored in the tree.
+ */
+ void constructOutlines(PdfDictionary outlineRoot, Map names) {
+ if (outlineRoot == null) {
+ return;
+ }
+
+ PdfReader reader = getDocument().getReader();
+ final boolean isLenientLevel =
+ reader == null || StrictnessLevel.CONSERVATIVE.isStricter(reader.getStrictnessLevel());
+ PdfDictionary current = outlineRoot.getAsDictionary(PdfName.First);
+
+ outlines = new PdfOutline(ROOT_OUTLINE_TITLE, outlineRoot, getDocument());
+ PdfOutline parentOutline = outlines;
+
+ Map nextUnprocessedChildForParentMap = new HashMap<>();
+ Set alreadyVisitedOutlinesSet = new HashSet<>();
+
+ while (current != null) {
+ PdfDictionary parent = current.getAsDictionary(PdfName.Parent);
+ if (null == parent && !isLenientLevel) {
+ throw new PdfException(
+ MessageFormatUtil.format(
+ KernelExceptionMessageConstant.CORRUPTED_OUTLINE_NO_PARENT_ENTRY,
+ current.indirectReference));
+ }
+ PdfString title = current.getAsString(PdfName.Title);
+ if (null == title) {
+ throw new PdfException(
+ MessageFormatUtil.format(
+ KernelExceptionMessageConstant.CORRUPTED_OUTLINE_NO_TITLE_ENTRY,
+ current.indirectReference));
+ }
+ PdfOutline currentOutline = new PdfOutline(title.toUnicodeString(), current, parentOutline);
+ alreadyVisitedOutlinesSet.add(current);
+ addOutlineToPage(currentOutline, current, names);
+ parentOutline.getAllChildren().add(currentOutline);
+
+ PdfDictionary first = current.getAsDictionary(PdfName.First);
+ PdfDictionary next = current.getAsDictionary(PdfName.Next);
+ if (first != null) {
+ if (alreadyVisitedOutlinesSet.contains(first)) {
+ if (!isLenientLevel) {
+ throw new PdfException(MessageFormatUtil.format(
+ KernelExceptionMessageConstant.CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP, first));
+ }
+ LOGGER.warn(MessageFormatUtil.format(
+ KernelLogMessageConstant.CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP, first));
+ return;
+ }
+ // Down in hierarchy; when returning up, process `next`.
+ nextUnprocessedChildForParentMap.put(parentOutline, next);
+ parentOutline = currentOutline;
+ current = first;
+ } else if (next != null) {
+ if (alreadyVisitedOutlinesSet.contains(next)) {
+ if (!isLenientLevel) {
+ throw new PdfException(MessageFormatUtil.format(
+ KernelExceptionMessageConstant.CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP, next));
+ }
+ LOGGER.warn(MessageFormatUtil.format(
+ KernelLogMessageConstant.CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP, next));
+ return;
+ }
+ // Next sibling in hierarchy
+ current = next;
+ } else {
+ // Up in hierarchy using 'nextUnprocessedChildForParentMap'.
+ current = null;
+ while (current == null && parentOutline != null) {
+ parentOutline = parentOutline.getParent();
+ if (parentOutline != null) {
+ current = nextUnprocessedChildForParentMap.get(parentOutline);
+ }
+ }
+ }
+ }
+ }
+
PdfDestination copyDestination(PdfObject dest, Map page2page, PdfDocument toDocument) {
if (null == dest) {
return null;
@@ -658,46 +745,6 @@ private void addOutlineToPage(PdfOutline outline, Map names)
}
}
- /**
- * Get the next outline of the current node in the outline tree by looking for a child or sibling node.
- * If there is no child or sibling of the current node {@link PdfCatalog#getParentNextOutline(PdfDictionary)} is called to get a hierarchical parent's next node. {@code null} is returned if one does not exist.
- *
- * @return the {@link PdfDictionary} object of the next outline if one exists, {@code null} otherwise.
- */
- private PdfDictionary getNextOutline(PdfDictionary first, PdfDictionary next, PdfDictionary parent) {
- if (first != null) {
- return first;
- } else if (next != null) {
- return next;
- } else {
- return getParentNextOutline(parent);
- }
-
- }
-
- /**
- * Gets the parent's next outline of the current node.
- * If the parent does not have a next we look at the grand parent, great-grand parent, etc until we find a next node or reach the root at which point {@code null} is returned to signify there is no next node present.
- *
- * @return the {@link PdfDictionary} object of the next outline if one exists, {@code null} otherwise.
- */
- private PdfDictionary getParentNextOutline(PdfDictionary parent) {
- if (parent == null) {
- return null;
- }
- PdfDictionary current = null;
- while (current == null) {
- current = parent.getAsDictionary(PdfName.Next);
- if (current == null) {
- parent = parent.getAsDictionary(PdfName.Parent);
- if (parent == null) {
- return null;
- }
- }
- }
- return current;
- }
-
private void addOutlineToPage(PdfOutline outline, PdfDictionary item, Map names) {
PdfObject dest = item.get(PdfName.Dest);
if (dest != null) {
@@ -723,48 +770,4 @@ private void addOutlineToPage(PdfOutline outline, PdfDictionary item, Map names) {
- if (outlineRoot == null) {
- return;
- }
- PdfDictionary first = outlineRoot.getAsDictionary(PdfName.First);
- PdfDictionary current = first;
- HashMap parentOutlineMap = new HashMap<>();
-
- outlines = new PdfOutline(OutlineRoot, outlineRoot, getDocument());
- PdfOutline parentOutline = outlines;
- parentOutlineMap.put(outlineRoot, parentOutline);
-
- while (current != null) {
- first = current.getAsDictionary(PdfName.First);
- PdfDictionary next = current.getAsDictionary(PdfName.Next);
- PdfDictionary parent = current.getAsDictionary(PdfName.Parent);
- if (null == parent) {
- throw new PdfException(
- MessageFormatUtil.format(
- KernelExceptionMessageConstant.CORRUPTED_OUTLINE_NO_PARENT_ENTRY,
- current.indirectReference));
- }
- PdfString title = current.getAsString(PdfName.Title);
- if (null == title) {
- throw new PdfException(
- MessageFormatUtil.format(
- KernelExceptionMessageConstant.CORRUPTED_OUTLINE_NO_TITLE_ENTRY,
- current.indirectReference));
- }
- parentOutline = parentOutlineMap.get(parent);
- PdfOutline currentOutline = new PdfOutline(title.toUnicodeString(), current, parentOutline);
- addOutlineToPage(currentOutline, current, names);
- parentOutline.getAllChildren().add(currentOutline);
-
- if (first != null) {
- parentOutlineMap.put(current, currentOutline);
- }
- current = getNextOutline(first, next, parent);
- }
- }
}
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfDocument.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfDocument.java
index fb446d59f2..bd506debf8 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfDocument.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfDocument.java
@@ -55,6 +55,7 @@ This file is part of the iText (R) project.
import com.itextpdf.kernel.actions.data.ITextCoreProductData;
import com.itextpdf.kernel.actions.events.FlushPdfDocumentEvent;
import com.itextpdf.kernel.actions.events.ITextCoreProductEvent;
+import com.itextpdf.kernel.colors.Color;
import com.itextpdf.kernel.events.EventDispatcher;
import com.itextpdf.kernel.events.IEventDispatcher;
import com.itextpdf.kernel.events.PdfDocumentEvent;
@@ -954,7 +955,10 @@ public void close() {
}
for (int pageNum = 1; pageNum <= getNumberOfPages(); pageNum++) {
- getPage(pageNum).flush();
+ PdfPage page = getPage(pageNum);
+ if (page != null) {
+ page.flush();
+ }
}
if (structTreeRoot != null) {
tryFlushTagStructure(false);
@@ -2405,6 +2409,15 @@ private void cloneOutlines(Set outlinesToCopy, PdfOutline newParent,
if (copiedDest != null) {
child.addDestination(copiedDest);
}
+ Integer copiedStyle = outline.getStyle();
+ if (copiedStyle != null) {
+ child.setStyle(copiedStyle.intValue());
+ }
+ Color copiedColor = outline.getColor();
+ if (copiedColor != null) {
+ child.setColor(copiedColor);
+ }
+ child.setOpen(outline.isOpen());
cloneOutlines(outlinesToCopy, child, outline, page2page, toDocument);
}
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfOutline.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfOutline.java
index 2efd532298..8200fe2280 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfOutline.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfOutline.java
@@ -46,6 +46,7 @@ This file is part of the iText (R) project.
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.colors.Color;
import com.itextpdf.kernel.pdf.action.PdfAction;
+import com.itextpdf.kernel.pdf.colorspace.PdfColorSpace;
import com.itextpdf.kernel.pdf.navigation.PdfDestination;
import java.util.ArrayList;
@@ -143,6 +144,21 @@ public void setColor(Color color) {
content.put(PdfName.C, new PdfArray(color.getColorValue()));
}
+ /**
+ * Gets color for the outline entry's text, {@code C} key.
+ *
+ * @return color {@link Color}.
+ */
+ public Color getColor() {
+ PdfArray colorArray = content.getAsArray(PdfName.C);
+ if (colorArray == null) {
+ return null;
+ } else {
+ return Color.makeColor(PdfColorSpace.makeColorSpace(PdfName.DeviceRGB),
+ colorArray.toFloatArray());
+ }
+ }
+
/**
* Sets text style for the outline entry’s text, {@code F} key.
*
@@ -154,6 +170,15 @@ public void setStyle(int style) {
}
}
+ /**
+ * Gets text style for the outline entry's text, {@code F} key.
+ *
+ * @return style value.
+ */
+ public Integer getStyle() {
+ return content.getAsInt(PdfName.F);
+ }
+
/**
* Gets content dictionary.
*
@@ -224,6 +249,16 @@ else if (children.size() > 0)
content.remove(PdfName.Count);
}
+ /**
+ * Defines if the outline is open or closed.
+ *
+ * @return true if open,false otherwise.
+ */
+ public boolean isOpen() {
+ Integer count = content.getAsInt(PdfName.Count);
+ return count == null || count >= 0;
+ }
+
/**
* Adds a new {@code PdfOutline} with specified parameters as a child to existing {@code PdfOutline}
* and put it to specified position in the existing {@code PdfOutline} children list.
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfPagesTree.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfPagesTree.java
index 6eb592ce69..b034d6cfb9 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfPagesTree.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/PdfPagesTree.java
@@ -47,6 +47,9 @@ This file is part of the iText (R) project.
import com.itextpdf.commons.utils.MessageFormatUtil;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
+
+import java.util.HashSet;
+import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -337,6 +340,17 @@ protected PdfPages findPageParent(PdfPage pdfPage) {
}
private void loadPage(int pageNum) {
+ loadPage(pageNum, new HashSet<>());
+ }
+
+ /**
+ * Load page from pages tree node structure
+ *
+ * @param pageNum page number to load
+ * @param processedParents set with already processed parents object reference numbers
+ * if this method was called recursively to avoid infinite recursion.
+ */
+ private void loadPage(int pageNum, Set processedParents) {
PdfIndirectReference targetPage = pageRefs.get(pageNum);
if (targetPage != null)
return;
@@ -344,6 +358,15 @@ private void loadPage(int pageNum) {
//if we go here, we have to split PdfPages that contains pageNum
int parentIndex = findPageParent(pageNum);
PdfPages parent = parents.get(parentIndex);
+ PdfIndirectReference parentIndirectReference = parent.getPdfObject().getIndirectReference();
+ if (parentIndirectReference != null) {
+ if (processedParents.contains(parentIndirectReference)) {
+ throw new PdfException(KernelExceptionMessageConstant.INVALID_PAGE_STRUCTURE)
+ .setMessageParams(pageNum + 1);
+ } else {
+ processedParents.add(parentIndirectReference);
+ }
+ }
PdfArray kids = parent.getKids();
if (kids == null) {
throw new PdfException(KernelExceptionMessageConstant.INVALID_PAGE_STRUCTURE).setMessageParams(pageNum + 1);
@@ -430,7 +453,7 @@ private void loadPage(int pageNum) {
// recursive call, to load needed pageRef.
// NOTE optimization? add to loadPage startParentIndex.
- loadPage(pageNum);
+ loadPage(pageNum, processedParents);
} else {
int from = parent.getFrom();
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/canvas/parser/listener/LocationTextExtractionStrategy.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/canvas/parser/listener/LocationTextExtractionStrategy.java
index febe1b78dd..15b0a9a3a0 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/canvas/parser/listener/LocationTextExtractionStrategy.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/canvas/parser/listener/LocationTextExtractionStrategy.java
@@ -82,11 +82,7 @@ public class LocationTextExtractionStrategy implements ITextExtractionStrategy {
* Creates a new text extraction renderer.
*/
public LocationTextExtractionStrategy() {
- this(new ITextChunkLocationStrategy() {
- public ITextChunkLocation createLocation(TextRenderInfo renderInfo, LineSegment baseline) {
- return new TextChunkLocationDefaultImp(baseline.getStartPoint(), baseline.getEndPoint(), renderInfo.getSingleSpaceWidth());
- }
- });
+ this(new ITextChunkLocationStrategyImpl());
}
/**
@@ -349,4 +345,14 @@ private static class TextChunkMarks {
List succeeding = new ArrayList<>();
}
+ private static final class ITextChunkLocationStrategyImpl
+ implements LocationTextExtractionStrategy.ITextChunkLocationStrategy {
+
+ @Override
+ public ITextChunkLocation createLocation(TextRenderInfo renderInfo, LineSegment baseline) {
+ return new TextChunkLocationDefaultImp(baseline.getStartPoint(), baseline.getEndPoint(),
+ renderInfo.getSingleSpaceWidth());
+ }
+ }
+
}
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/canvas/parser/listener/RegexBasedLocationExtractionStrategy.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/canvas/parser/listener/RegexBasedLocationExtractionStrategy.java
index 352afed9cf..80b284d21f 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/canvas/parser/listener/RegexBasedLocationExtractionStrategy.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/canvas/parser/listener/RegexBasedLocationExtractionStrategy.java
@@ -62,7 +62,7 @@ This file is part of the iText (R) project.
* This class is designed to search for the occurrences of a regular expression and return the resultant rectangles.
*/
public class RegexBasedLocationExtractionStrategy implements ILocationExtractionStrategy {
-
+ private static final float EPS = 1.0E-4F;
private Pattern pattern;
private List parseResult = new ArrayList<>();
@@ -100,18 +100,7 @@ public Collection getResultantLocations() {
* This is to ensure that tests that use this functionality (for instance to generate pdf with
* areas of interest highlighted) will not break when compared.
*/
- java.util.Collections.sort(retval, new Comparator() {
- @Override
- public int compare(IPdfTextLocation l1, IPdfTextLocation l2) {
- Rectangle o1 = l1.getRectangle();
- Rectangle o2 = l2.getRectangle();
- if (o1.getY() == o2.getY()) {
- return o1.getX() == o2.getX() ? 0 : (o1.getX() < o2.getX() ? -1 : 1);
- } else {
- return o1.getY() < o2.getY() ? -1 : 1;
- }
- }
- });
+ Collections.sort(retval, new PdfTextLocationComparator());
// ligatures can produces same rectangle
removeDuplicates(retval);
@@ -214,4 +203,19 @@ private static Integer getEndIndex(Map indexMap, int index) {
}
return indexMap.get(index);
}
+
+ private static final class PdfTextLocationComparator
+ implements Comparator {
+ @Override
+ public int compare(com.itextpdf.kernel.pdf.canvas.parser.listener.IPdfTextLocation l1,
+ com.itextpdf.kernel.pdf.canvas.parser.listener.IPdfTextLocation l2) {
+ Rectangle o1 = l1.getRectangle();
+ Rectangle o2 = l2.getRectangle();
+ if (Math.abs(o1.getY() - o2.getY()) < EPS) {
+ return Math.abs(o1.getX() - o2.getX()) < EPS ? 0 : ((o2.getX() - o1.getX()) > EPS ? -1 : 1);
+ } else {
+ return (o2.getY() - o1.getY()) > EPS ? -1 : 1;
+ }
+ }
+ }
}
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/tagging/StandardNamespaces.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/tagging/StandardNamespaces.java
index 8d53d763ac..7c9afee927 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/tagging/StandardNamespaces.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/tagging/StandardNamespaces.java
@@ -130,6 +130,8 @@ public final class StandardNamespaces {
StandardRoles.DOCUMENT,
StandardRoles.DOCUMENTFRAGMENT,
StandardRoles.PART,
+ StandardRoles.SECT,
+ StandardRoles.NONSTRUCT,
StandardRoles.DIV,
StandardRoles.ASIDE,
StandardRoles.TITLE,
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/tagging/StructureTreeCopier.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/tagging/StructureTreeCopier.java
index 41ce9b42c2..da9ca70fd6 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/tagging/StructureTreeCopier.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/tagging/StructureTreeCopier.java
@@ -353,7 +353,6 @@ private static CopyStructureResult copyStructure(PdfDocument destDocument, Map
excludeKeys = Collections.singletonList(PdfName.RoleMapNS);
PdfDocument toDocument = copyingParams.getToDocument();
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/AccessibilityProperties.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/AccessibilityProperties.java
index 644b37e2f6..95b9bccad9 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/AccessibilityProperties.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/AccessibilityProperties.java
@@ -42,101 +42,290 @@ This file is part of the iText (R) project.
*/
package com.itextpdf.kernel.pdf.tagutils;
+import com.itextpdf.kernel.pdf.PdfName;
+import com.itextpdf.kernel.pdf.PdfString;
import com.itextpdf.kernel.pdf.tagging.PdfNamespace;
+import com.itextpdf.kernel.pdf.tagging.PdfStructElem;
import com.itextpdf.kernel.pdf.tagging.PdfStructureAttributes;
+import com.itextpdf.kernel.pdf.tagging.StandardRoles;
import java.util.Collections;
import java.util.List;
+/**
+ * The accessibility properties are used to define properties of {@link PdfStructElem structure elements}
+ * in Tagged PDF documents via {@link TagTreePointer} API.
+ */
public abstract class AccessibilityProperties {
+ /**
+ * Gets the role of element.
+ *
+ *
+ * See also {@link StandardRoles}.
+ *
+ * @return the role
+ */
public String getRole() {
return null;
}
+ /**
+ * Sets the role of element.
+ *
+ *
+ * See also {@link StandardRoles}.
+ *
+ * @param role the role to be set
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties setRole(String role) {
return this;
}
+ /**
+ * Gets the language identifier of element. Should be in format xy-ZK (for example en-US).
+ *
+ *
+ * For more information see PDF Specification ISO 32000-1 section 14.9.2.
+ *
+ * @return the language
+ */
public String getLanguage() {
return null;
}
+ /**
+ * Sets the language identifier of element. Should be in format xy-ZK (for example en-US).
+ *
+ *
+ * For more information see PDF Specification ISO 32000-1 section 14.9.2.
+ *
+ * @param language the language to be set
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties setLanguage(String language) {
return this;
}
+ /**
+ * Gets the actual text of element.
+ *
+ * @return the actual text
+ */
public String getActualText() {
return null;
}
+ /**
+ * Sets the actual text of element.
+ *
+ * @param actualText the actual text to be set
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties setActualText(String actualText) {
return this;
}
+ /**
+ * Gets the alternate description of element.
+ *
+ * @return the alternate description
+ */
public String getAlternateDescription() {
return null;
}
+ /**
+ * Sets the alternate description of element.
+ *
+ * @param alternateDescription the alternation description to be set
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties setAlternateDescription(String alternateDescription) {
return this;
}
+ /**
+ * Gets the expansion of element.
+ *
+ *
+ * Expansion it is the expanded form of an abbreviation of structure element.
+ *
+ * @return the expansion
+ */
public String getExpansion() {
return null;
}
+ /**
+ * Sets the expansion of element.
+ *
+ *
+ * Expansion it is the expanded form of an abbreviation of structure element.
+ *
+ * @param expansion the expansion to be set
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties setExpansion(String expansion) {
return this;
}
+ /**
+ * Gets the phoneme of element.
+ *
+ *
+ * For more information see {@link PdfStructElem#setPhoneme(PdfString)}.
+ *
+ * @return the phoneme
+ */
public String getPhoneme() {
return null;
}
+ /**
+ * Sets the phoneme of element.
+ *
+ *
+ * For more information see {@link PdfStructElem#setPhoneme(PdfString)}.
+ *
+ * @param phoneme the phoneme to be set
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties setPhoneme(String phoneme) {
return this;
}
+ /**
+ * Gets the phonetic alphabet of element.
+ *
+ *
+ * For more information see {@link PdfStructElem#setPhoneticAlphabet(PdfName)}.
+ *
+ * @return the phonetic alphabet
+ */
public String getPhoneticAlphabet() {
return null;
}
+ /**
+ * Sets the phonetic alphabet of element.
+ *
+ *
+ * For more information see {@link PdfStructElem#setPhoneticAlphabet(PdfName)}.
+ *
+ * @param phoneticAlphabet the phonetic alphabet to be set
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties setPhoneticAlphabet(String phoneticAlphabet) {
return this;
}
+ /**
+ * Gets the namespace of element.
+ *
+ * @return the namespace
+ */
public PdfNamespace getNamespace() {
return null;
}
+ /**
+ * Sets the namespace of element.
+ *
+ * @param namespace the namespace to be set
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties setNamespace(PdfNamespace namespace) {
return this;
}
+ /**
+ * Adds the reference to other tagged element.
+ *
+ *
+ * For more information see {@link PdfStructElem#addRef(PdfStructElem)}.
+ *
+ * @param treePointer the reference to be set
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties addRef(TagTreePointer treePointer) {
return this;
}
+ /**
+ * Gets the list of references to other tagged elements.
+ *
+ *
+ * For more information see {@link PdfStructElem#addRef(PdfStructElem)}.
+ *
+ * @return the list of references
+ */
public List getRefsList() {
return Collections.emptyList();
}
+ /**
+ * Clears the list of references to other tagged elements.
+ *
+ *
+ * For more information see {@link PdfStructElem#addRef(PdfStructElem)}.
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties clearRefs() {
return this;
}
+ /**
+ * Adds the attributes to the element.
+ *
+ * @param attributes the attributes to be added
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties addAttributes(PdfStructureAttributes attributes) {
return this;
}
+ /**
+ * Adds the attributes to the element with specified index.
+ *
+ *
+ * If an attribute with the same O and NS entries is specified more than once, the later (in array order)
+ * entry shall take precedence. For more information see PDF Specification ISO-32000 section 14.7.6.
+ *
+ * @param index the attributes index
+ * @param attributes the attributes to be added
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties addAttributes(int index, PdfStructureAttributes attributes) {
return this;
}
+ /**
+ * Clears the list of attributes.
+ *
+ * @return this {@link AccessibilityProperties} instance
+ */
public AccessibilityProperties clearAttributes() {
return this;
}
+ /**
+ * Gets the attributes list.
+ *
+ * @return the attributes list
+ */
public List getAttributesList() {
return Collections.emptyList();
}
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/DefaultAccessibilityProperties.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/DefaultAccessibilityProperties.java
index ee539bc7b8..bcfd5ff4ea 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/DefaultAccessibilityProperties.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/DefaultAccessibilityProperties.java
@@ -44,14 +44,19 @@ This file is part of the iText (R) project.
package com.itextpdf.kernel.pdf.tagutils;
import com.itextpdf.kernel.pdf.tagging.PdfNamespace;
+import com.itextpdf.kernel.pdf.tagging.PdfStructElem;
import com.itextpdf.kernel.pdf.tagging.PdfStructureAttributes;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+/**
+ * The class represents a basic implementation of {@link AccessibilityProperties} that preserves specified
+ * accessibility properties. Accessibility properties are used to define properties of
+ * {@link PdfStructElem structure elements} in Tagged PDF documents via {@link TagTreePointer} API.
+ */
public class DefaultAccessibilityProperties extends AccessibilityProperties {
-
-
protected String role;
protected String language;
protected String actualText;
@@ -64,6 +69,11 @@ public class DefaultAccessibilityProperties extends AccessibilityProperties {
protected PdfNamespace namespace;
protected List refs = new ArrayList<>();
+ /**
+ * Instantiates a new {@link DefaultAccessibilityProperties} instance based on structure element role.
+ *
+ * @param role the structure element role
+ */
public DefaultAccessibilityProperties(String role) {
this.role = role;
}
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/TagReference.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/TagReference.java
index 762a05338e..3a89f38133 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/TagReference.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/TagReference.java
@@ -46,8 +46,16 @@ This file is part of the iText (R) project.
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfObject;
+import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.tagging.PdfStructElem;
+/**
+ * The class is used to provide connection between structure element of
+ * Tagged PDF document and marked content sequence in PDF stream.
+ *
+ *
+ * See {@link TagTreePointer#getTagReference(int)} and {@link PdfCanvas#openTag(TagReference)}.
+ */
public class TagReference {
protected TagTreePointer tagPointer;
protected int insertIndex;
@@ -56,6 +64,15 @@ public class TagReference {
protected PdfName role;
protected PdfDictionary properties;
+ /**
+ * Creates a {@link TagReference} instance which represents a reference to {@link PdfStructElem}.
+ *
+ * @param referencedTag a structure element to which marked content will link (if insertIndex is -1,
+ * otherwise to MC will link to kid with insertIndex of passed structure element)
+ * @param tagPointer the tag pointer to structure element
+ * @param insertIndex if insertIndex is -1, the referencedTag will be used as a
+ * source of reference, otherwise the kid will be used
+ */
protected TagReference(PdfStructElem referencedTag, TagTreePointer tagPointer, int insertIndex) {
this.role = referencedTag.getRole();
this.referencedTag = referencedTag;
@@ -63,14 +80,32 @@ protected TagReference(PdfStructElem referencedTag, TagTreePointer tagPointer, i
this.insertIndex = insertIndex;
}
+ /**
+ * Gets role of structure element.
+ *
+ * @return the role of structure element
+ */
public PdfName getRole() {
return role;
}
+ /**
+ * Creates next marked content identifier, which will be used to mark content in PDF stream.
+ *
+ * @return the marked content identifier
+ */
public int createNextMcid() {
return tagPointer.createNextMcidForStructElem(referencedTag, insertIndex);
}
+ /**
+ * Adds property, which will be associated with marked-content sequence.
+ *
+ * @param name the name of the property
+ * @param value the value of the property
+ *
+ * @return the {@link TagReference} instance
+ */
public TagReference addProperty(PdfName name, PdfObject value) {
if (properties == null) {
properties = new PdfDictionary();
@@ -80,6 +115,13 @@ public TagReference addProperty(PdfName name, PdfObject value) {
return this;
}
+ /**
+ * Removes property. The property will not be associated with marked-content sequence.
+ *
+ * @param name the name of property to be deleted
+ *
+ * @return the {@link TagReference} instance
+ */
public TagReference removeProperty(PdfName name) {
if (properties != null) {
properties.remove(name);
@@ -87,6 +129,13 @@ public TagReference removeProperty(PdfName name) {
return this;
}
+ /**
+ * Gets property which related to specified name.
+ *
+ * @param name the name of the property
+ *
+ * @return the value of the property
+ */
public PdfObject getProperty(PdfName name) {
if (properties == null) {
return null;
@@ -94,6 +143,11 @@ public PdfObject getProperty(PdfName name) {
return properties.get(name);
}
+ /**
+ * Gets properties, which will be associated with marked-content sequence as {@link PdfDictionary}.
+ *
+ * @return the properties
+ */
public PdfDictionary getProperties() {
return properties;
}
diff --git a/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/TagStructureContext.java b/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/TagStructureContext.java
index 46798c604b..6e26b15a4d 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/TagStructureContext.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/TagStructureContext.java
@@ -44,8 +44,8 @@ This file is part of the iText (R) project.
package com.itextpdf.kernel.pdf.tagutils;
import com.itextpdf.io.logs.IoLogMessageConstant;
-import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
+import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.pdf.PdfArray;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfDocument;
@@ -64,7 +64,6 @@ This file is part of the iText (R) project.
import com.itextpdf.kernel.pdf.tagging.StandardNamespaces;
import com.itextpdf.kernel.pdf.tagging.StandardRoles;
-
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
@@ -119,6 +118,7 @@ public class TagStructureContext {
*
* Creates {@code TagStructureContext} for document. There shall be only one instance of this
* class per {@code PdfDocument}.
+ *
* @param document the document which tag structure will be manipulated with this class.
*/
public TagStructureContext(PdfDocument document) {
@@ -128,9 +128,11 @@ public TagStructureContext(PdfDocument document) {
/**
* Do not use this constructor, instead use {@link PdfDocument#getTagStructureContext()}
* method.
+ *
*
* Creates {@code TagStructureContext} for document. There shall be only one instance of this
* class per {@code PdfDocument}.
+ *
* @param document the document which tag structure will be manipulated with this class.
* @param tagStructureTargetVersion the version of the pdf standard to which the tag structure shall adhere.
*/
@@ -156,7 +158,9 @@ public TagStructureContext(PdfDocument document, PdfVersion tagStructureTargetVe
* If forbidUnknownRoles is set to true, then if you would try to add new tag which has not a standard role and
* it's role is not mapped through RoleMap, an exception will be raised.
* Default value - true.
+ *
* @param forbidUnknownRoles new value of the flag
+ *
* @return current {@link TagStructureContext} instance.
*/
public TagStructureContext setForbidUnknownRoles(boolean forbidUnknownRoles) {
@@ -164,6 +168,11 @@ public TagStructureContext setForbidUnknownRoles(boolean forbidUnknownRoles) {
return this;
}
+ /**
+ * Gets the version of the PDF standard to which the tag structure shall adhere.
+ *
+ * @return the tag structure target version
+ */
public PdfVersion getTagStructureTargetVersion() {
return tagStructureTargetVersion;
}
@@ -174,6 +183,7 @@ public PdfVersion getTagStructureTargetVersion() {
* Typically it points at the root tag. This pointer also could be used to tweak auto tagging process
* (e.g. move this pointer to the Section tag, which would result in placing all automatically tagged content
* under Section tag).
+ *
* @return the {@code TagTreePointer} which is used for all automatic tagging of the document.
*/
public TagTreePointer getAutoTaggingPointer() {
@@ -186,6 +196,7 @@ public TagTreePointer getAutoTaggingPointer() {
/**
* Gets {@link WaitingTagsManager} for the current document. It allows to mark tags as waiting,
* which would indicate that they are incomplete and are not ready to be flushed.
+ *
* @return document's {@link WaitingTagsManager} class instance.
*/
public WaitingTagsManager getWaitingTagsManager() {
@@ -199,8 +210,10 @@ public WaitingTagsManager getWaitingTagsManager() {
*
* By default, this value is defined based on the PDF document version and the existing tag structure inside
* a document. For the new empty PDF 2.0 documents this namespace is set to {@link StandardNamespaces#PDF_2_0}.
+ *
*
* This value has meaning only for the PDF documents of version 2.0 and higher.
+ *
* @return a {@link PdfNamespace} which is used as a default value for the document tagging.
*/
public PdfNamespace getDocumentDefaultNamespace() {
@@ -210,16 +223,19 @@ public PdfNamespace getDocumentDefaultNamespace() {
/**
* Sets a namespace that will be used as a default value for the tagging for any new {@link TagTreePointer} created.
* See {@link #getDocumentDefaultNamespace()} for more info.
+ *
*
* Be careful when changing this property value. It is most recommended to do it right after the {@link PdfDocument} was
* created, before any content was added. Changing this value after any content was added might result in the mingled
* tag structure from the namespaces point of view. So in order to maintain the document consistent but in the namespace
* different from default, set this value before any modifications to the document were made and before
* {@link #getAutoTaggingPointer()} method was called for the first time.
+ *
*
* This value has meaning only for the PDF documents of version 2.0 and higher.
*
* @param namespace a {@link PdfNamespace} which is to be used as a default value for the document tagging.
+ *
* @return current {@link TagStructureContext} instance.
*/
public TagStructureContext setDocumentDefaultNamespace(PdfNamespace namespace) {
@@ -229,6 +245,7 @@ public TagStructureContext setDocumentDefaultNamespace(PdfNamespace namespace) {
/**
* This method defines a recommended way to obtain {@link PdfNamespace} class instances.
+ *
*
* Returns either a wrapper over an already existing namespace dictionary in the document or over a new one
* if such namespace wasn't encountered before. Calling this method is considered as encountering a namespace,
@@ -239,6 +256,7 @@ public TagStructureContext setDocumentDefaultNamespace(PdfNamespace namespace) {
* {@link PdfName#Namespaces /Namespaces} array unless they were set to the certain element of the tag structure.
*
* @param namespaceName a {@link String} defining the namespace name (conventionally a uniform resource identifier, or URI).
+ *
* @return {@link PdfNamespace} wrapper over either already existing namespace object or over the new one.
*/
public PdfNamespace fetchNamespace(String namespaceName) {
@@ -254,7 +272,9 @@ public PdfNamespace fetchNamespace(String namespaceName) {
/**
* Gets an instance of the {@link IRoleMappingResolver} corresponding to the current tag structure target version.
* This method implies that role is in the default standard structure namespace.
+ *
* @param role a role in the default standard structure namespace which mapping is to be resolved.
+ *
* @return a {@link IRoleMappingResolver} instance, with the giving role as current.
*/
public IRoleMappingResolver getRoleMappingResolver(String role) {
@@ -263,8 +283,10 @@ public IRoleMappingResolver getRoleMappingResolver(String role) {
/**
* Gets an instance of the {@link IRoleMappingResolver} corresponding to the current tag structure target version.
+ *
* @param role a role in the given namespace which mapping is to be resolved.
* @param namespace a {@link PdfNamespace} which this role belongs to.
+ *
* @return a {@link IRoleMappingResolver} instance, with the giving role in the given {@link PdfNamespace} as current.
*/
public IRoleMappingResolver getRoleMappingResolver(String role, PdfNamespace namespace) {
@@ -278,9 +300,11 @@ public IRoleMappingResolver getRoleMappingResolver(String role, PdfNamespace nam
/**
* Checks if the given role and namespace are specified to be obligatory mapped to the standard structure namespace
* in order to be a valid role in the Tagged PDF.
+ *
* @param role a role in the given namespace which mapping necessity is to be checked.
* @param namespace a {@link PdfNamespace} which this role belongs to, null value refers to the default standard
* structure namespace.
+ *
* @return true, if the given role in the given namespace is either mapped to the standard structure role or doesn't
* have to; otherwise false.
*/
@@ -290,10 +314,13 @@ public boolean checkIfRoleShallBeMappedToStandardRole(String role, PdfNamespace
/**
* Gets an instance of the {@link IRoleMappingResolver} which is already in the "resolved" state: it returns
- * role in the standard or domain-specific namespace for the {@link IRoleMappingResolver#getRole()} and {@link IRoleMappingResolver#getNamespace()}
- * methods calls which correspond to the mapping of the given role; or null if the given role is not mapped to the standard or domain-specific one.
+ * role in the standard or domain-specific namespace for the {@link IRoleMappingResolver#getRole()} and
+ * {@link IRoleMappingResolver#getNamespace()} methods calls which correspond to the mapping of the given role;
+ * or null if the given role is not mapped to the standard or domain-specific one.
+ *
* @param role a role in the given namespace which mapping is to be resolved.
* @param namespace a {@link PdfNamespace} which this role belongs to.
+ *
* @return an instance of the {@link IRoleMappingResolver} which returns false
* for the {@link IRoleMappingResolver#currentRoleShallBeMappedToStandard()} method call; if mapping cannot be resolved
* to this state, this method returns null, which means that the given role
@@ -323,6 +350,7 @@ public IRoleMappingResolver resolveMappingToStandardOrDomainSpecificRole(String
* If annotation is not added to the document or is not tagged, nothing will happen.
*
* @param annotation the {@link PdfAnnotation} that will be removed from the tag structure
+ *
* @return {@link TagTreePointer} instance which points at annotation tag parent if annotation was removed,
* otherwise returns null
*/
@@ -353,8 +381,10 @@ public TagTreePointer removeAnnotationTag(PdfAnnotation annotation) {
* Removes content item from the tag structure.
*
* Nothing happens if there is no such mcid on given page.
+ *
* @param page page, which contains this content item
* @param mcid marked content id of this content item
+ *
* @return {@code TagTreePointer} which points at the parent of the removed content item, or null if there is no
* such mcid on given page.
*/
@@ -374,6 +404,7 @@ public TagTreePointer removeContentItem(PdfPage page, int mcid) {
* at {@link #flushPageTags(PdfPage)}.
*
* @param page page that defines which tags are to be removed
+ *
* @return current {@link TagStructureContext} instance
*/
public TagStructureContext removePageTags(PdfPage page) {
@@ -402,6 +433,7 @@ public TagStructureContext removePageTags(PdfPage page) {
* as not yet finished ones, and they and their children won't be flushed.
*
* @param page a page which tags will be flushed
+ *
* @return current {@link TagStructureContext} instance
*/
public TagStructureContext flushPageTags(PdfPage page) {
@@ -474,6 +506,7 @@ public void prepareToDocumentClosing() {
* especially in conjunction with high level {@link TagTreePointer} and {@link TagStructureContext} classes.
*
* @param pointer a {@link TagTreePointer} which points at desired {@link PdfStructElem}.
+ *
* @return a {@link PdfStructElem} at which given {@link TagTreePointer} points.
*/
public PdfStructElem getPointerStructElem(TagTreePointer pointer) {
@@ -482,7 +515,9 @@ public PdfStructElem getPointerStructElem(TagTreePointer pointer) {
/**
* Creates a new {@link TagTreePointer} which points at given {@link PdfStructElem}.
+ *
* @param structElem a {@link PdfStructElem} for which {@link TagTreePointer} will be created.
+ *
* @return a new {@link TagTreePointer}.
*/
public TagTreePointer createPointerForStructElem(PdfStructElem structElem) {
diff --git a/kernel/src/main/java/com/itextpdf/kernel/utils/PdfSplitter.java b/kernel/src/main/java/com/itextpdf/kernel/utils/PdfSplitter.java
index 072c0ef2cd..de29bc9190 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/utils/PdfSplitter.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/utils/PdfSplitter.java
@@ -169,12 +169,7 @@ public void splitByPageNumbers(List pageNumbers, IDocumentReadyListener
public List splitByPageNumbers(List pageNumbers) {
final List splitDocuments = new ArrayList<>();
- splitByPageNumbers(pageNumbers, new IDocumentReadyListener() {
- @Override
- public void documentReady(PdfDocument pdfDocument, PageRange pageRange) {
- splitDocuments.add(pdfDocument);
- }
- });
+ splitByPageNumbers(pageNumbers, new SplitReadyListener(splitDocuments));
return splitDocuments;
}
@@ -206,12 +201,7 @@ public void splitByPageCount(int pageCount, IDocumentReadyListener documentReady
public List splitByPageCount(int pageCount) {
final List splitDocuments = new ArrayList<>();
- splitByPageCount(pageCount, new IDocumentReadyListener() {
- @Override
- public void documentReady(PdfDocument pdfDocument, PageRange pageRange) {
- splitDocuments.add(pdfDocument);
- }
- });
+ splitByPageCount(pageCount, new SplitReadyListener(splitDocuments));
return splitDocuments;
}
@@ -408,4 +398,18 @@ private PageRange getNextRange(int startPage, int endPage, long size) {
private long xrefLength(int size) {
return 20L * (size + 1);
}
+
+ private static final class SplitReadyListener implements IDocumentReadyListener {
+
+ private List splitDocuments;
+
+ public SplitReadyListener(List splitDocuments) {
+ this.splitDocuments = splitDocuments;
+ }
+
+ @Override
+ public void documentReady(PdfDocument pdfDocument, PageRange pageRange) {
+ splitDocuments.add(pdfDocument);
+ }
+ }
}
diff --git a/kernel/src/main/java/com/itextpdf/kernel/xmp/XMPMetaFactory.java b/kernel/src/main/java/com/itextpdf/kernel/xmp/XMPMetaFactory.java
index a4efa4d66a..acb6a57af2 100644
--- a/kernel/src/main/java/com/itextpdf/kernel/xmp/XMPMetaFactory.java
+++ b/kernel/src/main/java/com/itextpdf/kernel/xmp/XMPMetaFactory.java
@@ -262,43 +262,59 @@ public static XMPVersionInfo getVersionInfo() {
// Adobe XMP Core 5.0-jc001 DEBUG-., 2009 Jan 28 15:22:38-CET
final String message = "Adobe XMP Core 5.1.0-jc003";
+ versionInfo = new XMPVersionInfoImpl(major, minor, micro, engBuild, debug, message);
+ } catch (Throwable e) {
+ // empty, severe error would be detected during the tests
+ System.out.println(e);
+ }
+ }
+ return versionInfo;
+ }
+ }
- versionInfo = new XMPVersionInfo() {
- public int getMajor() {
- return major;
- }
+ private static final class XMPVersionInfoImpl implements XMPVersionInfo {
+ private final int major;
+ private final int minor;
+ private final int micro;
+ private final int engBuild;
+ private final boolean debug;
+ private final String message;
+
+ public XMPVersionInfoImpl(int major, int minor, int micro, int engBuild, boolean debug, String message) {
+ this.major = major;
+ this.minor = minor;
+ this.micro = micro;
+ this.engBuild = engBuild;
+ this.debug = debug;
+ this.message = message;
+ }
- public int getMinor() {
- return minor;
- }
+ public int getMajor() {
+ return major;
+ }
- public int getMicro() {
- return micro;
- }
+ public int getMinor() {
+ return minor;
+ }
- public boolean isDebug() {
- return debug;
- }
+ public int getMicro() {
+ return micro;
+ }
- public int getBuild() {
- return engBuild;
- }
+ public boolean isDebug() {
+ return debug;
+ }
- public String getMessage() {
- return message;
- }
+ public int getBuild() {
+ return engBuild;
+ }
- public String toString() {
- return message;
- }
- };
+ public String getMessage() {
+ return message;
+ }
- } catch (Throwable e) {
- // EMTPY, severe error would be detected during the tests
- System.out.println(e);
- }
- }
- return versionInfo;
+ public String toString() {
+ return message;
}
}
}
diff --git a/kernel/src/test/java/com/itextpdf/kernel/crypto/CryptoUtilTest.java b/kernel/src/test/java/com/itextpdf/kernel/crypto/CryptoUtilTest.java
new file mode 100644
index 0000000000..1330ad08f3
--- /dev/null
+++ b/kernel/src/test/java/com/itextpdf/kernel/crypto/CryptoUtilTest.java
@@ -0,0 +1,62 @@
+/*
+ This file is part of the iText (R) project.
+ Copyright (c) 1998-2021 iText Group NV
+ Authors: iText Software.
+
+ This program is offered under a commercial and under the AGPL license.
+ For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+ AGPL licensing:
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+package com.itextpdf.kernel.crypto;
+
+import com.itextpdf.commons.utils.MessageFormatUtil;
+import com.itextpdf.io.source.ByteArrayOutputStream;
+import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
+import com.itextpdf.test.ExtendedITextTest;
+import com.itextpdf.test.annotations.type.UnitTest;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1OutputStream;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(UnitTest.class)
+public class CryptoUtilTest extends ExtendedITextTest {
+ @Test
+ public void createBerStreamTest() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ASN1OutputStream stream = CryptoUtil.createAsn1OutputStream(baos, ASN1Encoding.BER);
+ Assert.assertNotNull(stream);
+ }
+
+ @Test
+ public void createDerStreamTest() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ASN1OutputStream stream = CryptoUtil.createAsn1OutputStream(baos, ASN1Encoding.DER);
+ Assert.assertNotNull(stream);
+ }
+
+ @Test
+ public void createUnsupportedEncodingStreamTest() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ Exception e = Assert.assertThrows(UnsupportedOperationException.class,
+ () -> CryptoUtil.createAsn1OutputStream(baos, "DL")
+ );
+ Assert.assertEquals(MessageFormatUtil.format(KernelExceptionMessageConstant.UNSUPPORTED_ASN1_ENCODING, "DL"),
+ e.getMessage());
+ }
+}
diff --git a/kernel/src/test/java/com/itextpdf/kernel/font/PdfType0FontTest.java b/kernel/src/test/java/com/itextpdf/kernel/font/PdfType0FontTest.java
index dcfa6020d1..9584908420 100644
--- a/kernel/src/test/java/com/itextpdf/kernel/font/PdfType0FontTest.java
+++ b/kernel/src/test/java/com/itextpdf/kernel/font/PdfType0FontTest.java
@@ -25,6 +25,7 @@ This file is part of the iText (R) project.
import com.itextpdf.io.font.CMapEncoding;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.font.TrueTypeFont;
+import com.itextpdf.io.font.otf.Glyph;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
import com.itextpdf.kernel.pdf.PdfDictionary;
@@ -35,6 +36,8 @@ This file is part of the iText (R) project.
import com.itextpdf.test.annotations.type.UnitTest;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@@ -94,4 +97,16 @@ public void dictionaryConstructorTest() throws IOException {
Assert.assertEquals(0, cmap.getSupplement());
Assert.assertEquals(PdfEncodings.IDENTITY_H, cmap.getCmapName());
}
+
+ @Test
+ public void appendThreeSurrogatePairsTest() throws IOException {
+ // this text contains three successive surrogate pairs, which should result in three glyphs
+ String textWithThreeSurrogatePairs = "\uD800\uDF10\uD800\uDF00\uD800\uDF11";
+ PdfFont type0Font =
+ PdfFontFactory.createFont(sourceFolder + "NotoSansOldItalic-Regular.ttf", PdfEncodings.IDENTITY_H);
+
+ List glyphs = new ArrayList<>();
+ type0Font.appendGlyphs(textWithThreeSurrogatePairs, 0, textWithThreeSurrogatePairs.length() - 1, glyphs);
+ Assert.assertEquals(3, glyphs.size());
+ }
}
diff --git a/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfDocumentTest.java b/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfDocumentTest.java
index b06ba7b5f9..232ab9eda7 100644
--- a/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfDocumentTest.java
+++ b/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfDocumentTest.java
@@ -483,7 +483,7 @@ public void addAssociatedFilesTest02() throws IOException, InterruptedException
imageXObject.addAssociatedFile(PdfFileSpec
.createEmbeddedFileSpec(pdfDocument, "Associated File 1".getBytes(), "af_1.txt", PdfName.Data));
- pageCanvas.addXObject(imageXObject, 40, 400);
+ pageCanvas.addXObjectAt(imageXObject, 40, 400);
PdfFormXObject formXObject = new PdfFormXObject(new Rectangle(200, 200));
PdfCanvas formCanvas = new PdfCanvas(formXObject, pdfDocument);
@@ -497,7 +497,7 @@ public void addAssociatedFilesTest02() throws IOException, InterruptedException
formXObject.addAssociatedFile(PdfFileSpec
.createEmbeddedFileSpec(pdfDocument, "Associated File 2".getBytes(), "af_2.txt", PdfName.Data));
- pageCanvas.addXObject(formXObject, 40, 100);
+ pageCanvas.addXObjectAt(formXObject, 40, 100);
pdfDocument.close();
Assert.assertNull(new CompareTool().compareByContent(DESTINATION_FOLDER + "add_associated_files02.pdf",
diff --git a/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfOutlineTest.java b/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfOutlineTest.java
index e1088cd55f..cadb25445e 100644
--- a/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfOutlineTest.java
+++ b/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfOutlineTest.java
@@ -42,31 +42,36 @@ This file is part of the iText (R) project.
*/
package com.itextpdf.kernel.pdf;
-import com.itextpdf.io.logs.IoLogMessageConstant;
import com.itextpdf.commons.utils.MessageFormatUtil;
-import com.itextpdf.kernel.exceptions.PdfException;
+import com.itextpdf.io.font.PdfEncodings;
+import com.itextpdf.io.logs.IoLogMessageConstant;
+import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
+import com.itextpdf.kernel.exceptions.PdfException;
+import com.itextpdf.kernel.logs.KernelLogMessageConstant;
+import com.itextpdf.kernel.pdf.PdfReader.StrictnessLevel;
import com.itextpdf.kernel.pdf.navigation.PdfDestination;
import com.itextpdf.kernel.pdf.navigation.PdfExplicitDestination;
import com.itextpdf.kernel.pdf.navigation.PdfStringDestination;
import com.itextpdf.kernel.utils.CompareTool;
+import com.itextpdf.test.AssertUtil;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.annotations.LogMessage;
import com.itextpdf.test.annotations.LogMessages;
import com.itextpdf.test.annotations.type.IntegrationTest;
import java.io.ByteArrayOutputStream;
-import java.util.HashMap;
-import org.xml.sax.SAXException;
-import javax.xml.parsers.ParserConfigurationException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import javax.xml.parsers.ParserConfigurationException;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
+import org.xml.sax.SAXException;
@Category(IntegrationTest.class)
public class PdfOutlineTest extends ExtendedITextTest {
@@ -99,7 +104,7 @@ public void createSimpleDocWithOutlines() throws IOException, InterruptedExcepti
Assert.assertNull(new CompareTool().compareByContent(DESTINATION_FOLDER + filename, SOURCE_FOLDER + "cmp_" + filename,
DESTINATION_FOLDER, "diff_"));
}
-
+
@Test
public void outlinesTest() throws IOException {
PdfDocument pdfDoc = new PdfDocument(new PdfReader(SOURCE_FOLDER + "iphone_user_guide.pdf"));
@@ -225,10 +230,18 @@ public void getOutlinesInvalidParentLink() throws IOException {
String filename = "updateOutlineTitleInvalidParentLink.pdf";
PdfWriter writer = new PdfWriter(DESTINATION_FOLDER + filename);
PdfDocument pdfDoc = new PdfDocument(reader, writer);
-
- Assert.assertThrows(NullPointerException.class,
- () -> pdfDoc.getOutlines(false)
- );
+ PdfOutline outlines = pdfDoc.getOutlines(true);
+ PdfOutline firstOutline = outlines.getAllChildren().get(0);
+ PdfOutline secondOutline = outlines.getAllChildren().get(1);
+ try {
+ Assert.assertEquals(2, outlines.getAllChildren().size());
+ Assert.assertEquals("First Page", firstOutline.getTitle());
+ Assert.assertEquals(outlines, firstOutline.getParent());
+ Assert.assertEquals("Second Page", secondOutline.getTitle());
+ Assert.assertEquals(outlines, secondOutline.getParent());
+ } finally {
+ pdfDoc.close();
+ }
}
@Test
@@ -526,15 +539,11 @@ public void constructOutlinesNoParentTest() throws IOException {
PdfDictionary outlineDictionary = new PdfDictionary();
outlineDictionary.put(PdfName.First, first);
+ outlineDictionary.put(PdfName.Title, new PdfString("title", PdfEncodings.UNICODE_BIG));
+ first.put(PdfName.Title, new PdfString("title", PdfEncodings.UNICODE_BIG));
- Exception exception = Assert.assertThrows(
- PdfException.class,
- () -> pdfDocument.getCatalog().constructOutlines(outlineDictionary, new HashMap())
- );
- Assert.assertEquals(
- MessageFormatUtil.format(KernelExceptionMessageConstant.CORRUPTED_OUTLINE_NO_PARENT_ENTRY,
- first.indirectReference),
- exception.getMessage());
+ AssertUtil.doesNotThrow(() -> pdfDocument.getCatalog()
+ .constructOutlines(outlineDictionary, new HashMap()));
}
}
@@ -565,4 +574,242 @@ public void constructOutlinesNoTitleTest() throws IOException {
exception.getMessage());
}
}
+
+ @Test
+ public void checkParentOfOutlinesTest() throws IOException {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PdfDocument pdfDocument = new PdfDocument(new PdfWriter(baos))) {
+ pdfDocument.getCatalog().setPageMode(PdfName.UseOutlines);
+
+ PdfPage firstPage = pdfDocument.addNewPage();
+
+ PdfOutline rootOutline = pdfDocument.getOutlines(false);
+ PdfOutline firstOutline = rootOutline.addOutline("First outline");
+ PdfOutline firstSubOutline = firstOutline.addOutline("First suboutline");
+ PdfOutline secondSubOutline = firstOutline.addOutline("Second suboutline");
+ PdfOutline secondOutline = rootOutline.addOutline("SecondOutline");
+
+ firstOutline.addDestination(PdfExplicitDestination.createFit(firstPage));
+
+ PdfOutline resultedRoot = pdfDocument.getOutlines(true);
+ Assert.assertEquals(2, resultedRoot.getAllChildren().size());
+ Assert.assertEquals(resultedRoot, resultedRoot.getAllChildren().get(0).getParent());
+ Assert.assertEquals(resultedRoot, resultedRoot.getAllChildren().get(1).getParent());
+
+ PdfOutline resultedFirstOutline = resultedRoot.getAllChildren().get(0);
+ Assert.assertEquals(2, resultedFirstOutline.getAllChildren().size());
+ Assert.assertEquals(resultedFirstOutline, resultedFirstOutline.getAllChildren().get(0).getParent());
+ Assert.assertEquals(resultedFirstOutline, resultedFirstOutline.getAllChildren().get(1).getParent());
+ }
+ }
+
+ @Test
+ public void checkNestedOutlinesParentTest() throws IOException {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PdfDocument pdfDocument = new PdfDocument(new PdfWriter(baos))) {
+ pdfDocument.getCatalog().setPageMode(PdfName.UseOutlines);
+
+ PdfPage firstPage = pdfDocument.addNewPage();
+ PdfOutline rootOutline = pdfDocument.getOutlines(false);
+ PdfOutline firstOutline = rootOutline.addOutline("First outline");
+ PdfOutline secondOutline = firstOutline.addOutline("Second outline");
+ PdfOutline thirdOutline = secondOutline.addOutline("Third outline");
+
+ firstOutline.addDestination(PdfExplicitDestination.createFit(firstPage));
+
+ PdfOutline resultedRoot = pdfDocument.getOutlines(true);
+ Assert.assertEquals(1, resultedRoot.getAllChildren().size());
+ Assert.assertEquals(resultedRoot, resultedRoot.getAllChildren().get(0).getParent());
+
+ PdfOutline resultedFirstOutline = resultedRoot.getAllChildren().get(0);
+ Assert.assertEquals(1, resultedFirstOutline.getAllChildren().size());
+ Assert.assertEquals(resultedFirstOutline, resultedFirstOutline.getAllChildren().get(0).getParent());
+
+ PdfOutline resultedSecondOutline = resultedFirstOutline.getAllChildren().get(0);
+ Assert.assertEquals(1, resultedSecondOutline.getAllChildren().size());
+ Assert.assertEquals(resultedSecondOutline, resultedSecondOutline.getAllChildren().get(0).getParent());
+ }
+ }
+
+ @Test
+ public void setOutlinePropertiesTest() throws IOException {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PdfDocument pdfDocument = new PdfDocument(new PdfWriter(baos))) {
+
+ PdfPage firstPage = pdfDocument.addNewPage();
+
+ PdfOutline rootOutline = pdfDocument.getOutlines(true);
+ PdfOutline outline = rootOutline.addOutline("Outline");
+
+ Assert.assertTrue(outline.isOpen());
+ Assert.assertNull(outline.getStyle());
+ Assert.assertNull(outline.getColor());
+
+ outline.getContent().put(PdfName.C, new PdfArray(ColorConstants.BLACK.getColorValue()));
+ outline.getContent().put(PdfName.F, new PdfNumber(2));
+ outline.getContent().put(PdfName.Count, new PdfNumber(4));
+
+ Assert.assertTrue(outline.isOpen());
+ Assert.assertEquals(new Integer(2), outline.getStyle());
+ Assert.assertEquals(ColorConstants.BLACK, outline.getColor());
+
+ outline.getContent().put(PdfName.Count, new PdfNumber(0));
+ Assert.assertTrue(outline.isOpen());
+
+ outline.getContent().put(PdfName.Count, new PdfNumber(-5));
+ Assert.assertFalse(outline.isOpen());
+ }
+ }
+
+ @Test
+ @LogMessages(messages = @LogMessage(messageTemplate =
+ KernelLogMessageConstant.CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP))
+ public void checkPossibleInfiniteLoopWithSameNextAndPrevLinkTest() throws IOException {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PdfDocument pdfDocument = new PdfDocument(new PdfWriter(baos))) {
+
+ pdfDocument.addNewPage();
+
+ PdfDictionary first = new PdfDictionary();
+ first.makeIndirect(pdfDocument);
+ PdfDictionary second = new PdfDictionary();
+ second.makeIndirect(pdfDocument);
+
+ PdfDictionary outlineDictionary = new PdfDictionary();
+ outlineDictionary.makeIndirect(pdfDocument);
+
+ outlineDictionary.put(PdfName.First, first);
+ outlineDictionary.put(PdfName.Last, second);
+ first.put(PdfName.Parent, outlineDictionary);
+ second.put(PdfName.Parent, outlineDictionary);
+ first.put(PdfName.Next, second);
+ first.put(PdfName.Prev, second);
+ second.put(PdfName.Next, first);
+ second.put(PdfName.Prev, first);
+ outlineDictionary.put(PdfName.Title, new PdfString("title", PdfEncodings.UNICODE_BIG));
+ first.put(PdfName.Title, new PdfString("title", PdfEncodings.UNICODE_BIG));
+ second.put(PdfName.Title, new PdfString("title", PdfEncodings.UNICODE_BIG));
+
+ AssertUtil.doesNotThrow(() -> pdfDocument.getCatalog()
+ .constructOutlines(outlineDictionary, new HashMap()));
+ PdfOutline resultedOutline = pdfDocument.getOutlines(false);
+ Assert.assertEquals(2, resultedOutline.getAllChildren().size());
+ Assert.assertEquals(resultedOutline.getAllChildren().get(1).getParent(),
+ resultedOutline.getAllChildren().get(0).getParent());
+ }
+ }
+
+ @Test
+ @LogMessages(messages = @LogMessage(messageTemplate =
+ KernelLogMessageConstant.CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP))
+ public void checkPossibleInfiniteLoopWithSameFirstAndLastLinkTest() throws IOException {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PdfDocument pdfDocument = new PdfDocument(new PdfWriter(baos))) {
+
+ pdfDocument.addNewPage();
+
+ PdfDictionary first = new PdfDictionary();
+ first.makeIndirect(pdfDocument);
+
+ PdfDictionary outlineDictionary = new PdfDictionary();
+ outlineDictionary.makeIndirect(pdfDocument);
+
+ outlineDictionary.put(PdfName.First, first);
+ first.put(PdfName.Parent, outlineDictionary);
+ first.put(PdfName.First, outlineDictionary);
+ first.put(PdfName.Last, outlineDictionary);
+ outlineDictionary.put(PdfName.Title, new PdfString("title", PdfEncodings.UNICODE_BIG));
+ first.put(PdfName.Title, new PdfString("title", PdfEncodings.UNICODE_BIG));
+
+ AssertUtil.doesNotThrow(() -> pdfDocument.getCatalog()
+ .constructOutlines(outlineDictionary, new HashMap()));
+ PdfOutline resultedOutline = pdfDocument.getOutlines(false);
+ Assert.assertEquals(1, resultedOutline.getAllChildren().size());
+ Assert.assertEquals(resultedOutline,
+ resultedOutline.getAllChildren().get(0).getParent());
+ }
+ }
+
+ @Test
+ public void outlineNoParentLinkInConservativeModeTest() throws IOException {
+ try (
+ PdfDocument pdfDocument = new PdfDocument(
+ new PdfReader(SOURCE_FOLDER + "outlinesNoParentLink.pdf"))) {
+ pdfDocument.getReader().setStrictnessLevel(StrictnessLevel.CONSERVATIVE);
+ Exception exception = Assert.assertThrows(PdfException.class, () -> pdfDocument.getOutlines(true));
+
+ //Hardcode indirectReference, cause there is no option to get this outline due to #getOutlines method
+ // will be thrown an exception.
+ Assert.assertEquals(
+ MessageFormatUtil.format(KernelExceptionMessageConstant.CORRUPTED_OUTLINE_NO_PARENT_ENTRY, "9 0 R"),
+ exception.getMessage());
+ }
+ }
+
+ @Test
+ public void outlineHasInfiniteLoopInConservativeModeTest() throws IOException {
+ try (
+ PdfDocument pdfDocument = new PdfDocument(
+ new PdfReader(SOURCE_FOLDER + "outlinesHaveInfiniteLoop.pdf"))) {
+ pdfDocument.getReader().setStrictnessLevel(StrictnessLevel.CONSERVATIVE);
+ Exception exception = Assert.assertThrows(PdfException.class, () -> pdfDocument.getOutlines(true));
+
+ //Hardcode indirectReference, cause there is no option to get this outline due to #getOutlines method
+ // will be thrown an exception.
+ Assert.assertEquals(
+ MessageFormatUtil.format(
+ KernelExceptionMessageConstant.CORRUPTED_OUTLINE_DICTIONARY_HAS_INFINITE_LOOP,
+ "<> /Prev 10 0 R /Title First Page >>"),
+ exception.getMessage());
+ }
+ }
+
+ @Test
+ public void createOutlinesWithDifferentVariantsOfChildrenTest() throws IOException {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PdfDocument pdfDocument = new PdfDocument(new PdfWriter(baos))) {
+ pdfDocument.getCatalog().setPageMode(PdfName.UseOutlines);
+
+ PdfPage firstPage = pdfDocument.addNewPage();
+
+ PdfOutline a = pdfDocument.getOutlines(false);
+ PdfOutline b = a.addOutline("B");
+ PdfOutline e = b.addOutline("E");
+ PdfOutline f = e.addOutline("F");
+ PdfOutline d = b.addOutline("D");
+ PdfOutline c = a.addOutline("C");
+ PdfOutline g = f.addOutline("G");
+ PdfOutline h = f.addOutline("H");
+
+ a.addDestination(PdfExplicitDestination.createFit(firstPage));
+
+ PdfOutline resultedA = pdfDocument.getOutlines(true);
+
+ // Asserting children of root outline.
+ Assert.assertEquals(2, resultedA.getAllChildren().size());
+ Assert.assertEquals(resultedA, resultedA.getAllChildren().get(0).getParent());
+ Assert.assertEquals(resultedA, resultedA.getAllChildren().get(1).getParent());
+ Assert.assertTrue(resultedA.getAllChildren().get(1).getAllChildren().isEmpty());
+ Assert.assertEquals(2, resultedA.getAllChildren().get(0).getAllChildren().size());
+
+ //Asserting children of B outline after reconstructing.
+ PdfOutline resultedB = resultedA.getAllChildren().get(0);
+ Assert.assertEquals(resultedB, resultedB.getAllChildren().get(0).getParent());
+ Assert.assertEquals(resultedB, resultedB.getAllChildren().get(1).getParent());
+ Assert.assertTrue(resultedB.getAllChildren().get(1).getAllChildren().isEmpty());
+ Assert.assertEquals(1, resultedB.getAllChildren().get(0).getAllChildren().size());
+
+ //Asserting children of E outline after reconstructing.
+ PdfOutline resultedE = resultedB.getAllChildren().get(0);
+ Assert.assertEquals(resultedE, resultedE.getAllChildren().get(0).getParent());
+ Assert.assertEquals(2, resultedE.getAllChildren().get(0).getAllChildren().size());
+
+ //Asserting children of F outline after reconstructing.
+ PdfOutline resultedF = resultedE.getAllChildren().get(0);
+ Assert.assertEquals(resultedF, resultedF.getAllChildren().get(0).getParent());
+ Assert.assertEquals(resultedF, resultedF.getAllChildren().get(1).getParent());
+ Assert.assertTrue(resultedF.getAllChildren().get(0).getAllChildren().isEmpty());
+ Assert.assertTrue(resultedF.getAllChildren().get(1).getAllChildren().isEmpty());
+ }
+ }
}
diff --git a/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfPagesTest.java b/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfPagesTest.java
index e0c3e2b2d7..77359b49d1 100644
--- a/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfPagesTest.java
+++ b/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfPagesTest.java
@@ -57,6 +57,7 @@ This file is part of the iText (R) project.
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
import com.itextpdf.kernel.pdf.xobject.PdfImageXObject;
import com.itextpdf.kernel.utils.CompareTool;
+import com.itextpdf.test.AssertUtil;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.annotations.LogMessage;
import com.itextpdf.test.annotations.LogMessages;
@@ -549,6 +550,40 @@ public void pageGetMediaBoxTooManyArgumentsTest() throws IOException {
}
+ @Test
+ public void closeDocumentWithRecursivePagesNodeReferencesThrowsExTest() throws IOException {
+ try (PdfReader reader = new PdfReader(sourceFolder + "recursivePagesNodeReference.pdf");
+ PdfWriter writer = new PdfWriter(new ByteArrayOutputStream());
+ ) {
+ PdfDocument pdfDocument = new PdfDocument(reader, writer);
+ Exception e = Assert.assertThrows(PdfException.class, () -> pdfDocument.close());
+ Assert.assertEquals(MessageFormatUtil.format(KernelExceptionMessageConstant.INVALID_PAGE_STRUCTURE, 2), e.getMessage());
+ }
+ }
+
+ @Test
+ public void getPageWithRecursivePagesNodeReferenceInAppendModeThrowExTest() throws IOException {
+ try (PdfReader reader = new PdfReader(sourceFolder + "recursivePagesNodeReference.pdf");
+ PdfWriter writer = new PdfWriter(new ByteArrayOutputStream());
+ PdfDocument pdfDocument = new PdfDocument(reader, writer, new StampingProperties().useAppendMode());
+ ) {
+ Assert.assertEquals(2, pdfDocument.getNumberOfPages());
+ Assert.assertNotNull(pdfDocument.getPage(1));
+ Exception e = Assert.assertThrows(PdfException.class, () -> pdfDocument.getPage(2));
+ Assert.assertEquals(MessageFormatUtil.format(KernelExceptionMessageConstant.INVALID_PAGE_STRUCTURE, 2), e.getMessage());
+ }
+ }
+
+ @Test
+ public void closeDocumentWithRecursivePagesNodeInAppendModeDoesNotThrowsTest() throws IOException {
+ try (PdfReader reader = new PdfReader(sourceFolder + "recursivePagesNodeReference.pdf");
+ PdfWriter writer = new PdfWriter(new ByteArrayOutputStream());
+ PdfDocument pdfDocument = new PdfDocument(reader, writer, new StampingProperties().useAppendMode());
+ ) {
+ AssertUtil.doesNotThrow(() -> pdfDocument.close());
+ }
+ }
+
@Test
public void pageGetMediaBoxNotEnoughArgumentsTest() throws IOException {
PdfReader reader = new PdfReader(sourceFolder + "helloWorldMediaboxNotEnoughArguments.pdf");
diff --git a/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfXObjectTest.java b/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfXObjectTest.java
index d9cdd9c04f..1664bfa5db 100644
--- a/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfXObjectTest.java
+++ b/kernel/src/test/java/com/itextpdf/kernel/pdf/PdfXObjectTest.java
@@ -164,7 +164,7 @@ public void createDocumentWithForms() throws IOException, InterruptedException
//Create page1 and add forms to the page.
PdfPage page1 = document.addNewPage();
canvas = new PdfCanvas(page1);
- canvas.addXObject(form, 0, 0).addXObject(form, 50, 0).addXObject(form, 0, 50).addXObject(form, 50, 50);
+ canvas.addXObjectAt(form, 0, 0).addXObjectAt(form, 50, 0).addXObjectAt(form, 0, 50).addXObjectAt(form, 50, 50);
canvas.release();
//Create form from the page1 and flush it.
@@ -177,10 +177,10 @@ public void createDocumentWithForms() throws IOException, InterruptedException
//Create page2 and add forms to the page.
PdfPage page2 = document.addNewPage();
canvas = new PdfCanvas(page2);
- canvas.addXObject(form, 0, 0);
- canvas.addXObject(form, 0, 200);
- canvas.addXObject(form, 200, 0);
- canvas.addXObject(form, 200, 200);
+ canvas.addXObjectAt(form, 0, 0);
+ canvas.addXObjectAt(form, 0, 200);
+ canvas.addXObjectAt(form, 200, 0);
+ canvas.addXObjectAt(form, 200, 200);
canvas.release();
page2.flush();
diff --git a/kernel/src/test/java/com/itextpdf/kernel/pdf/SmartModeTest.java b/kernel/src/test/java/com/itextpdf/kernel/pdf/SmartModeTest.java
index 31613a8e3f..5bf98cf3b5 100644
--- a/kernel/src/test/java/com/itextpdf/kernel/pdf/SmartModeTest.java
+++ b/kernel/src/test/java/com/itextpdf/kernel/pdf/SmartModeTest.java
@@ -231,7 +231,7 @@ public void pageCopyAsFormXObjectWithInheritedResourcesTest() throws IOException
PdfPage page = copyPdfX.addNewPage(new PageSize(ps));
PdfCanvas canvas = new PdfCanvas(page);
PdfFormXObject pageCopy = origPage.copyAsFormXObject(copyPdfX);
- canvas.addXObject(pageCopy, 0, 0);
+ canvas.addXObjectAt(pageCopy, 0, 0);
}
}
diff --git a/kernel/src/test/java/com/itextpdf/kernel/pdf/canvas/parser/TextExtractIllegalDifferencesTest.java b/kernel/src/test/java/com/itextpdf/kernel/pdf/canvas/parser/TextExtractIllegalDifferencesTest.java
index 8d848707fe..9e18e4c49d 100644
--- a/kernel/src/test/java/com/itextpdf/kernel/pdf/canvas/parser/TextExtractIllegalDifferencesTest.java
+++ b/kernel/src/test/java/com/itextpdf/kernel/pdf/canvas/parser/TextExtractIllegalDifferencesTest.java
@@ -45,6 +45,7 @@ This file is part of the iText (R) project.
import com.itextpdf.io.logs.IoLogMessageConstant;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
+import com.itextpdf.test.AssertUtil;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.annotations.LogMessage;
import com.itextpdf.test.annotations.LogMessages;
@@ -59,10 +60,20 @@ This file is part of the iText (R) project.
public class TextExtractIllegalDifferencesTest extends ExtendedITextTest {
private static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/parser/TextExtractIllegalDifferencesTest/";
+
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.DOCFONT_HAS_ILLEGAL_DIFFERENCES, count = 1))
+ @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.DOCFONT_HAS_ILLEGAL_DIFFERENCES))
public void illegalDifference() throws IOException {
- PdfDocument pdf = new PdfDocument(new PdfReader(sourceFolder + "illegalDifference.pdf"));
- PdfTextExtractor.getTextFromPage(pdf.getFirstPage());
+ try (PdfDocument pdf = new PdfDocument(new PdfReader(sourceFolder + "illegalDifference.pdf"))) {
+ AssertUtil.doesNotThrow(() -> PdfTextExtractor.getTextFromPage(pdf.getFirstPage()));
+ }
+ }
+
+ @Test
+ @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.DOCFONT_HAS_ILLEGAL_DIFFERENCES, count = 2))
+ public void illegalDifferenceType3Font() throws IOException {
+ try (PdfDocument pdf = new PdfDocument(new PdfReader(sourceFolder + "illegalDifferenceType3Font.pdf"))) {
+ AssertUtil.doesNotThrow(() -> PdfTextExtractor.getTextFromPage(pdf.getFirstPage()));
+ }
}
}
diff --git a/kernel/src/test/java/com/itextpdf/kernel/pdf/canvas/parser/listener/RegexBasedLocationExtractionStrategyTest.java b/kernel/src/test/java/com/itextpdf/kernel/pdf/canvas/parser/listener/RegexBasedLocationExtractionStrategyTest.java
index 668e0d24d9..99b52faf4d 100644
--- a/kernel/src/test/java/com/itextpdf/kernel/pdf/canvas/parser/listener/RegexBasedLocationExtractionStrategyTest.java
+++ b/kernel/src/test/java/com/itextpdf/kernel/pdf/canvas/parser/listener/RegexBasedLocationExtractionStrategyTest.java
@@ -333,4 +333,16 @@ public void regexWithOnlyWhiteSpace() throws IOException {
Assert.assertEquals(0, locations.size());
}
+
+ @Test
+ public void sortCompareTest() throws IOException {
+ try (PdfDocument pdfDocument = new PdfDocument(new PdfReader(sourceFolder + "sortCompare.pdf"))) {
+ RegexBasedLocationExtractionStrategy extractionStrategy = new RegexBasedLocationExtractionStrategy("a");
+ PdfCanvasProcessor pdfCanvasProcessor = new PdfCanvasProcessor(extractionStrategy);
+ pdfCanvasProcessor.processPageContent(pdfDocument.getPage(1));
+ pdfCanvasProcessor.processPageContent(pdfDocument.getPage(2));
+ List locations = new ArrayList<>(extractionStrategy.getResultantLocations());
+ Assert.assertEquals(13, locations.size());
+ }
+ }
}
diff --git a/kernel/src/test/java/com/itextpdf/kernel/pdf/tagging/StructureTreeCopierUnitTest.java b/kernel/src/test/java/com/itextpdf/kernel/pdf/tagging/StructureTreeCopierUnitTest.java
new file mode 100644
index 0000000000..587330a3a5
--- /dev/null
+++ b/kernel/src/test/java/com/itextpdf/kernel/pdf/tagging/StructureTreeCopierUnitTest.java
@@ -0,0 +1,97 @@
+/*
+ This file is part of the iText (R) project.
+ Copyright (c) 1998-2021 iText Group NV
+ Authors: iText Software.
+
+ This program is offered under a commercial and under the AGPL license.
+ For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+ AGPL licensing:
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+package com.itextpdf.kernel.pdf.tagging;
+
+import com.itextpdf.kernel.pdf.PdfDictionary;
+import com.itextpdf.kernel.pdf.PdfName;
+import com.itextpdf.kernel.pdf.PdfObject;
+import com.itextpdf.test.ExtendedITextTest;
+import com.itextpdf.test.annotations.type.UnitTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Category(UnitTest.class)
+public class StructureTreeCopierUnitTest extends ExtendedITextTest {
+
+ private static final Map td = new HashMap<>();
+ private static final Map tr = new HashMap<>();
+ private static final Map th = new HashMap<>();
+
+ static {
+ td.put(PdfName.S, PdfName.TD);
+ tr.put(PdfName.S, PdfName.TR);
+ th.put(PdfName.S, PdfName.TH);
+ }
+
+ @Test
+ public void shouldTableElementBeCopiedTdTrTest() {
+ PdfDictionary obj = new PdfDictionary(td);
+ PdfDictionary parent = new PdfDictionary(tr);
+
+ Assert.assertTrue(StructureTreeCopier.shouldTableElementBeCopied(obj, parent));
+ }
+
+ @Test
+ public void shouldTableElementBeCopiedThTrTest() {
+ PdfDictionary obj = new PdfDictionary(th);
+ PdfDictionary parent = new PdfDictionary(tr);
+
+ Assert.assertTrue(StructureTreeCopier.shouldTableElementBeCopied(obj, parent));
+ }
+
+ @Test
+ public void shouldTableElementBeCopiedTdTdTest() {
+ PdfDictionary obj = new PdfDictionary(td);
+ PdfDictionary parent = new PdfDictionary(td);
+
+ Assert.assertFalse(StructureTreeCopier.shouldTableElementBeCopied(obj, parent));
+ }
+
+ @Test
+ public void shouldTableElementBeCopiedTrTdTest() {
+ PdfDictionary obj = new PdfDictionary(tr);
+ PdfDictionary parent = new PdfDictionary(td);
+
+ Assert.assertFalse(StructureTreeCopier.shouldTableElementBeCopied(obj, parent));
+ }
+
+ @Test
+ public void shouldTableElementBeCopiedTrTrTest() {
+ PdfDictionary obj = new PdfDictionary(tr);
+ PdfDictionary parent = new PdfDictionary(tr);
+
+ Assert.assertFalse(StructureTreeCopier.shouldTableElementBeCopied(obj, parent));
+ }
+
+ @Test
+ public void shouldTableElementBeCopiedThThTest() {
+ PdfDictionary obj = new PdfDictionary(th);
+ PdfDictionary parent = new PdfDictionary(th);
+
+ Assert.assertFalse(StructureTreeCopier.shouldTableElementBeCopied(obj, parent));
+ }
+}
diff --git a/kernel/src/test/java/com/itextpdf/kernel/pdf/xobject/ImageFromLanguageStandardLibraryTest.java b/kernel/src/test/java/com/itextpdf/kernel/pdf/xobject/ImageFromLanguageStandardLibraryTest.java
index 4c4f107896..c53c7c8a7a 100644
--- a/kernel/src/test/java/com/itextpdf/kernel/pdf/xobject/ImageFromLanguageStandardLibraryTest.java
+++ b/kernel/src/test/java/com/itextpdf/kernel/pdf/xobject/ImageFromLanguageStandardLibraryTest.java
@@ -98,7 +98,7 @@ public void imageBinaryTransparencySameColorTest() throws java.io.IOException {
.setFontAndSize(PdfFontFactory.createFont(), 12)
.showText("Invisible image (both opaque and non opaque pixels have the same color)")
.endText();
- canvas.addXObject(new PdfImageXObject(
+ canvas.addXObjectAt(new PdfImageXObject(
ImageDataFactory.create(createBinaryTransparentAWTImage(null), null)), 36, 580);
PdfDocument cmpDoc = new PdfDocument(new PdfReader(cmpFile));
@@ -135,7 +135,7 @@ public void imageBinaryTransparencyDifferentColorsTest() throws java.io.IOExcept
.setFontAndSize(PdfFontFactory.createFont(), 12)
.showText("Invisible image (both opaque and non opaque pixels have different colors)")
.endText();
- canvas.addXObject(new PdfImageXObject(
+ canvas.addXObjectAt(new PdfImageXObject(
ImageDataFactory.create(createBinaryTransparentAWTImage(Color.red), null)), 36, 580);
PdfDocument cmpDoc = new PdfDocument(new PdfReader(cmpFile));
diff --git a/kernel/src/test/java/com/itextpdf/kernel/utils/PdfMergerTest.java b/kernel/src/test/java/com/itextpdf/kernel/utils/PdfMergerTest.java
index 34446d3bb1..0d2ccfb946 100644
--- a/kernel/src/test/java/com/itextpdf/kernel/utils/PdfMergerTest.java
+++ b/kernel/src/test/java/com/itextpdf/kernel/utils/PdfMergerTest.java
@@ -43,15 +43,21 @@ This file is part of the iText (R) project.
package com.itextpdf.kernel.utils;
import com.itextpdf.io.logs.IoLogMessageConstant;
+import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.pdf.PdfDocument;
+import com.itextpdf.kernel.pdf.PdfName;
+import com.itextpdf.kernel.pdf.PdfOutline;
+import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
+import com.itextpdf.kernel.pdf.navigation.PdfExplicitDestination;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.annotations.LogMessage;
import com.itextpdf.test.annotations.LogMessages;
import com.itextpdf.test.annotations.type.IntegrationTest;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
@@ -102,11 +108,7 @@ public void mergeDocumentTest01() throws IOException, InterruptedException {
pdfDoc3.close();
- CompareTool compareTool = new CompareTool();
- String errorMessage = compareTool.compareByContent(resultFile, sourceFolder + "cmp_mergedResult01.pdf", destinationFolder, "diff_");
- if (errorMessage != null) {
- Assert.fail(errorMessage);
- }
+ Assert.assertNull(new CompareTool().compareByContent(resultFile, sourceFolder + "cmp_mergedResult01.pdf", destinationFolder, "diff_"));
}
@Test
@@ -123,11 +125,7 @@ public void mergeDocumentOutlinesWithNullDestinationTest01() throws IOException,
resultDocument.close();
sourceDocument.close();
- CompareTool compareTool = new CompareTool();
- String errorMessage = compareTool.compareByContent(resultFile, sourceFolder + "cmp_mergeDocumentOutlinesWithNullDestinationTest01.pdf", destinationFolder, "diff_");
- if (errorMessage != null) {
- Assert.fail(errorMessage);
- }
+ Assert.assertNull(new CompareTool().compareByContent(resultFile, sourceFolder + "cmp_mergeDocumentOutlinesWithNullDestinationTest01.pdf", destinationFolder, "diff_"));
}
@Test
@@ -151,11 +149,7 @@ public void mergeDocumentTest02() throws IOException, InterruptedException {
merger.merge(pdfDoc, 1, 1).merge(pdfDoc1, 1, 1).merge(pdfDoc2, 1, 1).close();
- CompareTool compareTool = new CompareTool();
- String errorMessage = compareTool.compareByContent(resultFile, sourceFolder + "cmp_mergedResult02.pdf", destinationFolder, "diff_");
- if (errorMessage != null) {
- Assert.fail(errorMessage);
- }
+ Assert.assertNull(new CompareTool().compareByContent(resultFile, sourceFolder + "cmp_mergedResult02.pdf", destinationFolder, "diff_"));
}
@Test
@@ -246,29 +240,35 @@ public void mergeDocumentTest04() throws IOException, InterruptedException, Pars
}
@Test
- public void mergeTableWithEmptyTdTest() throws IOException, ParserConfigurationException, SAXException {
- String filename = sourceFolder + "tableWithEmptyTd.pdf";
- String resultFile = destinationFolder + "tableWithEmptyTdResult.pdf";
+ public void mergeTableWithEmptyTdTest() throws IOException, ParserConfigurationException, SAXException, InterruptedException {
+ mergeAndCompareTagStructures("tableWithEmptyTd.pdf", 1, 1);
+ }
- PdfReader reader = new PdfReader(filename);
+ @Test
+ public void mergeSplitTableWithEmptyTdTest() throws IOException, ParserConfigurationException, SAXException, InterruptedException {
+ mergeAndCompareTagStructures("splitTableWithEmptyTd.pdf", 2, 2);
+ }
- PdfDocument sourceDoc = new PdfDocument(reader);
- PdfDocument output = new PdfDocument(new PdfWriter(resultFile));
- output.setTagged();
- PdfMerger merger = new PdfMerger(output).setCloseSourceDocuments(true);
- merger.merge(sourceDoc, 1, sourceDoc.getNumberOfPages());
- sourceDoc.close();
- reader.close();
- merger.close();
- output.close();
+ @Test
+ public void mergeEmptyRowWithTagsTest() throws IOException, ParserConfigurationException, SAXException, InterruptedException {
+ mergeAndCompareTagStructures("emptyRowWithTags.pdf", 1, 1);
+ }
- CompareTool compareTool = new CompareTool();
- String tagStructErrorMessage = compareTool.compareTagStructures(resultFile, sourceFolder + "cmp_tableWithEmptyTd.pdf");
+ @Test
+ @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.SOURCE_DOCUMENT_HAS_ACROFORM_DICTIONARY))
+ public void trInsideTdTableTest() throws ParserConfigurationException, SAXException, IOException, InterruptedException {
+ mergeAndCompareTagStructures("trInsideTdTable.pdf", 1, 1);
+ }
- String errorMessage = tagStructErrorMessage == null ? "" : tagStructErrorMessage + "\n";
- if (!errorMessage.isEmpty()) {
- Assert.fail(errorMessage);
- }
+ @Test
+ public void tdInsideTdTableTest() throws ParserConfigurationException, SAXException, IOException, InterruptedException {
+ mergeAndCompareTagStructures("tdInsideTdTable.pdf", 1, 1);
+ }
+
+ @Test
+ // TODO DEVSIX-5974 Empty tr isn't copied.
+ public void emptyTrTableTest() throws ParserConfigurationException, SAXException, IOException, InterruptedException {
+ mergeAndCompareTagStructures("emptyTrTable.pdf", 1, 1);
}
@Test
@@ -401,7 +401,25 @@ public void mergePdfWithComplexOCGTwiceTest() throws IOException, InterruptedExc
}
@Test
- @Ignore ("TODO: DEVSIX-5064 (when doing merge with outlines infinite loop occurs )")
+ public void stackOverflowErrorCycleReferenceOcgMergeTest() throws IOException, InterruptedException {
+ String outPdf = destinationFolder + "cycleReferenceMerged.pdf";
+ String cmpPdf = sourceFolder + "cmp_stackOverflowErrorCycleReferenceOcrMerge.pdf";
+
+ PdfDocument pdfWithOCG = new PdfDocument(new PdfReader(sourceFolder + "sourceOCG1.pdf"),
+ new PdfWriter(outPdf));
+ PdfDocument pdfWithOCGToMerge = new PdfDocument
+ (new PdfReader( sourceFolder + "stackOverflowErrorCycleReferenceOcgMerge.pdf")); // problem file
+ PdfMerger merger = new PdfMerger(pdfWithOCG);
+ merger.merge(pdfWithOCGToMerge, 1, pdfWithOCGToMerge.getNumberOfPages());
+ pdfWithOCGToMerge.close();
+ pdfWithOCG.close();
+ Assert.assertNull(new CompareTool().compareByContent(outPdf, cmpPdf, destinationFolder));
+ }
+
+ @Test
+ @LogMessages(messages = {
+ @LogMessage(messageTemplate = IoLogMessageConstant.SOURCE_DOCUMENT_HAS_ACROFORM_DICTIONARY)
+ })
public void mergeOutlinesWithWrongStructureTest() throws IOException, InterruptedException {
PdfDocument inputDoc = new PdfDocument(new PdfReader(
sourceFolder + "infiniteLoopInOutlineStructure.pdf"));
@@ -420,6 +438,87 @@ public void mergeOutlinesWithWrongStructureTest() throws IOException, Interrupte
sourceFolder + "cmp_infiniteLoopInOutlineStructure.pdf", destinationFolder));
}
+ private static void mergeAndCompareTagStructures(String testName, int fromPage, int toPage)
+ throws IOException, ParserConfigurationException, SAXException, InterruptedException {
+ String src = sourceFolder + testName;
+ String dest = destinationFolder + testName;
+ String cmp = sourceFolder + "cmp_" + testName;
+
+ PdfReader reader = new PdfReader(src);
+
+ PdfDocument sourceDoc = new PdfDocument(reader);
+ PdfDocument output = new PdfDocument(new PdfWriter(dest));
+ output.setTagged();
+ PdfMerger merger = new PdfMerger(output).setCloseSourceDocuments(true);
+ merger.merge(sourceDoc, fromPage, toPage);
+ sourceDoc.close();
+ reader.close();
+ merger.close();
+ output.close();
+
+ Assert.assertNull(new CompareTool().compareTagStructures(dest, cmp));
+ }
+
+ @Test
+ public void mergeDocumentWithColorPropertyInOutlineTest() throws IOException, InterruptedException {
+ String firstDocument = sourceFolder + "firstDocumentWithColorPropertyInOutline.pdf";
+ String secondDocument = sourceFolder + "SecondDocumentWithColorPropertyInOutline.pdf";
+ String cmpDocument = sourceFolder + "cmp_mergeOutlinesWithColorProperty.pdf";
+ String mergedPdf = destinationFolder + "mergeOutlinesWithColorProperty.pdf";
+ try (PdfDocument merged = new PdfDocument(new PdfWriter(mergedPdf));
+ PdfDocument fileA = new PdfDocument(new PdfReader(firstDocument));
+ PdfDocument fileB = new PdfDocument(new PdfReader(secondDocument))) {
+ PdfMerger merger = new PdfMerger(merged, false, true);
+
+ merger.merge(fileA, 1, fileA.getNumberOfPages());
+ merger.merge(fileB, 1, fileB.getNumberOfPages());
+
+ merger.close();
+ }
+
+ Assert.assertNull(new CompareTool().compareByContent(mergedPdf, cmpDocument, destinationFolder));
+ }
+
+ @Test
+ public void mergeDocumentWithStylePropertyInOutlineTest() throws IOException, InterruptedException {
+ String firstDocument = sourceFolder + "firstDocumentWithStylePropertyInOutline.pdf";
+ String secondDocument = sourceFolder + "secondDocumentWithStylePropertyInOutline.pdf";
+ String cmpPdf = sourceFolder + "cmp_mergeOutlineWithStyleProperty.pdf";
+ String mergedPdf = destinationFolder + "mergeOutlineWithStyleProperty.pdf";
+
+ try (PdfDocument documentA = new PdfDocument(new PdfReader(firstDocument));
+ PdfDocument documentB = new PdfDocument(new PdfReader(secondDocument));
+ PdfDocument merged = new PdfDocument(new PdfWriter(mergedPdf))) {
+ PdfMerger merger = new PdfMerger(merged, false, true);
+
+ merger.merge(documentA, 1, documentA.getNumberOfPages());
+ merger.merge(documentB, 1, documentB.getNumberOfPages());
+ merger.close();
+ }
+
+ Assert.assertNull(new CompareTool().compareByContent(mergedPdf, cmpPdf, destinationFolder));
+ }
+
+ @Test
+ public void mergePdfDocumentsWithCopingOutlinesTest() throws IOException, InterruptedException {
+ String firstPdfDocument = sourceFolder + "firstDocumentWithOutlines.pdf";
+ String secondPdfDocument = sourceFolder + "secondDocumentWithOutlines.pdf";
+ String cmpDocument = sourceFolder + "cmp_mergeDocumentsWithOutlines.pdf";
+ String mergedDocument = destinationFolder + "mergeDocumentsWithOutlines.pdf";
+
+ try (PdfDocument documentA = new PdfDocument(new PdfReader(firstPdfDocument));
+ PdfDocument documentB = new PdfDocument(new PdfReader(secondPdfDocument));
+ PdfDocument mergedPdf = new PdfDocument(new PdfWriter(mergedDocument))) {
+ PdfMerger merger = new PdfMerger(mergedPdf, false, true);
+ merger.merge(documentA, 1, documentA.getNumberOfPages());
+ merger.merge(documentB, 1, documentB.getNumberOfPages());
+
+ merger.close();
+ }
+
+ Assert.assertNull(new CompareTool().compareByContent(mergedDocument, cmpDocument, destinationFolder));
+ }
+
private void mergePdfs(List sources, String destination) throws IOException {
PdfDocument mergedDoc = new PdfDocument(new PdfWriter(destination));
PdfMerger merger = new PdfMerger(mergedDoc);
diff --git a/kernel/src/test/java/com/itextpdf/kernel/utils/PdfSplitterTest.java b/kernel/src/test/java/com/itextpdf/kernel/utils/PdfSplitterTest.java
index 93f32a019e..c6c2bfc409 100644
--- a/kernel/src/test/java/com/itextpdf/kernel/utils/PdfSplitterTest.java
+++ b/kernel/src/test/java/com/itextpdf/kernel/utils/PdfSplitterTest.java
@@ -256,4 +256,26 @@ protected PdfWriter getNextPdfWriter(PageRange documentPageRange) {
sourceFolder + "cmp/" + "cmp_splitBySize_part" + i + ".pdf", destinationFolder, "diff_"));
}
}
+
+ @Test
+ @LogMessages(messages = {
+ @LogMessage(messageTemplate = IoLogMessageConstant.SOURCE_DOCUMENT_HAS_ACROFORM_DICTIONARY , count = 10)
+ })
+ public void splitByPageCountTest() throws IOException {
+ String inputFileName = sourceFolder + "iphone_user_guide.pdf";
+ try (PdfDocument inputPdfDoc = new PdfDocument(new PdfReader(inputFileName))) {
+ PdfSplitter splitter = new PdfSplitter(inputPdfDoc);
+
+ int pagesCount = inputPdfDoc.getNumberOfPages();
+ int pagesCountInSplitDoc = 13;
+
+ List splitDocuments = splitter.splitByPageCount(pagesCountInSplitDoc);
+
+ for (PdfDocument doc : splitDocuments) {
+ doc.close();
+ }
+
+ Assert.assertEquals(pagesCount / pagesCountInSplitDoc, splitDocuments.size());
+ }
+ }
}
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/LICENSE-OFL-1.1.txt b/kernel/src/test/resources/com/itextpdf/kernel/LICENSE-OFL-1.1.txt
new file mode 100644
index 0000000000..d952d62c06
--- /dev/null
+++ b/kernel/src/test/resources/com/itextpdf/kernel/LICENSE-OFL-1.1.txt
@@ -0,0 +1,92 @@
+This Font Software is licensed under the SIL Open Font License,
+Version 1.1.
+
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font
+creation efforts of academic and linguistic communities, and to
+provide a free and open framework in which fonts may be shared and
+improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply to
+any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software
+components as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to,
+deleting, or substituting -- in part or in whole -- any of the
+components of the Original Version, by changing formats or by porting
+the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed,
+modify, redistribute, and sell modified and unmodified copies of the
+Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in
+Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the
+corresponding Copyright Holder. This restriction only applies to the
+primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created using
+the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/NOTICE.txt b/kernel/src/test/resources/com/itextpdf/kernel/NOTICE.txt
new file mode 100644
index 0000000000..5f9909f1e4
--- /dev/null
+++ b/kernel/src/test/resources/com/itextpdf/kernel/NOTICE.txt
@@ -0,0 +1,2 @@
+This software uses the following test resources under the following licenses:
+| NotoSansOldItalic-Regular.ttf | OFL-1.1 | LICENSE-OFL-1.1.txt | In development version of font based on commit 4cdde035fd5138d6653a2176ba728b5b6f8cc533 (30.10.2019) from repository: "https://github.com/googlefonts/noto-fonts"
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/font/PdfType0FontTest/NotoSansOldItalic-Regular.ttf b/kernel/src/test/resources/com/itextpdf/kernel/font/PdfType0FontTest/NotoSansOldItalic-Regular.ttf
new file mode 100644
index 0000000000..83ff12fe2d
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/font/PdfType0FontTest/NotoSansOldItalic-Regular.ttf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/parser/RegexBasedLocationExtractionStrategyTest/sortCompare.pdf b/kernel/src/test/resources/com/itextpdf/kernel/parser/RegexBasedLocationExtractionStrategyTest/sortCompare.pdf
new file mode 100644
index 0000000000..b021d30a9c
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/parser/RegexBasedLocationExtractionStrategyTest/sortCompare.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/parser/TextExtractIllegalDifferencesTest/illegalDifferenceType3Font.pdf b/kernel/src/test/resources/com/itextpdf/kernel/parser/TextExtractIllegalDifferencesTest/illegalDifferenceType3Font.pdf
new file mode 100644
index 0000000000..e178b4f8b4
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/parser/TextExtractIllegalDifferencesTest/illegalDifferenceType3Font.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfOutlineTest/outlinesHaveInfiniteLoop.pdf b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfOutlineTest/outlinesHaveInfiniteLoop.pdf
new file mode 100644
index 0000000000..333ba62f85
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfOutlineTest/outlinesHaveInfiniteLoop.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfOutlineTest/outlinesNoParentLink.pdf b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfOutlineTest/outlinesNoParentLink.pdf
new file mode 100644
index 0000000000..f1ff14ed38
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfOutlineTest/outlinesNoParentLink.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfPagesTest/recursivePagesNodeReference.pdf b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfPagesTest/recursivePagesNodeReference.pdf
new file mode 100644
index 0000000000..7185254aa8
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfPagesTest/recursivePagesNodeReference.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest01.pdf b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest01.pdf
index c7fcffe880..e5a21f5fac 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest01.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest01.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest02.pdf b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest02.pdf
index bcbe2ceab0..8d18fef5a8 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest02.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest02.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest03.pdf b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest03.pdf
index 78836e7f9a..202c3e93f8 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest03.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest03.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest04.pdf b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest04.pdf
index 6ec3dc2eb3..81fc95e570 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest04.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest04.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest06.pdf b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest06.pdf
index 7509180be7..876c213778 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest06.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfStructElemTest/cmp_structTreeCopyingTest06.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfXObjectTest/cmp_documentWithForms1.pdf b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfXObjectTest/cmp_documentWithForms1.pdf
index 7775092002..1b4d0d7b49 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfXObjectTest/cmp_documentWithForms1.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/pdf/PdfXObjectTest/cmp_documentWithForms1.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/pdf/SmartModeTest/cmp_pageCopyAsFormXObjectWithInheritedResourcesTest.pdf b/kernel/src/test/resources/com/itextpdf/kernel/pdf/SmartModeTest/cmp_pageCopyAsFormXObjectWithInheritedResourcesTest.pdf
index c99d141a14..408b7d333a 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/pdf/SmartModeTest/cmp_pageCopyAsFormXObjectWithInheritedResourcesTest.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/pdf/SmartModeTest/cmp_pageCopyAsFormXObjectWithInheritedResourcesTest.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/SecondDocumentWithColorPropertyInOutline.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/SecondDocumentWithColorPropertyInOutline.pdf
new file mode 100644
index 0000000000..8b49345354
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/SecondDocumentWithColorPropertyInOutline.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_emptyRowWithTags.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_emptyRowWithTags.pdf
new file mode 100644
index 0000000000..9da1545ba8
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_emptyRowWithTags.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_emptyTrTable.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_emptyTrTable.pdf
new file mode 100644
index 0000000000..69190ea2a4
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_emptyTrTable.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_infiniteLoopInOutlineStructure.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_infiniteLoopInOutlineStructure.pdf
new file mode 100644
index 0000000000..3abdf93c53
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_infiniteLoopInOutlineStructure.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergeDocumentsWithOutlines.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergeDocumentsWithOutlines.pdf
new file mode 100644
index 0000000000..a5ac075937
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergeDocumentsWithOutlines.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergeOutlineWithStyleProperty.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergeOutlineWithStyleProperty.pdf
new file mode 100644
index 0000000000..37402db81d
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergeOutlineWithStyleProperty.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergeOutlinesWithColorProperty.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergeOutlinesWithColorProperty.pdf
new file mode 100644
index 0000000000..c9f417d1ee
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergeOutlinesWithColorProperty.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergedResult03.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergedResult03.pdf
index 25896c31e6..6af763b90f 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergedResult03.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergedResult03.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergedResult04.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergedResult04.pdf
index e29f19d068..f362958bff 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergedResult04.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_mergedResult04.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_splitTableWithEmptyTd.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_splitTableWithEmptyTd.pdf
new file mode 100644
index 0000000000..ecb9409696
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_splitTableWithEmptyTd.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_stackOverflowErrorCycleReferenceOcrMerge.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_stackOverflowErrorCycleReferenceOcrMerge.pdf
new file mode 100644
index 0000000000..a5d73f83dc
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_stackOverflowErrorCycleReferenceOcrMerge.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_tableWithEmptyTd.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_tableWithEmptyTd.pdf
index 84e16d7352..a1b56027b7 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_tableWithEmptyTd.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_tableWithEmptyTd.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_tdInsideTdTable.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_tdInsideTdTable.pdf
new file mode 100644
index 0000000000..e9348b5dc5
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_tdInsideTdTable.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_trInsideTdTable.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_trInsideTdTable.pdf
new file mode 100644
index 0000000000..8076713366
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/cmp_trInsideTdTable.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/emptyRowWithTags.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/emptyRowWithTags.pdf
new file mode 100644
index 0000000000..60902297da
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/emptyRowWithTags.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/emptyTrTable.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/emptyTrTable.pdf
new file mode 100644
index 0000000000..7130771cf9
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/emptyTrTable.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/firstDocumentWithColorPropertyInOutline.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/firstDocumentWithColorPropertyInOutline.pdf
new file mode 100644
index 0000000000..f4239c215f
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/firstDocumentWithColorPropertyInOutline.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/firstDocumentWithOutlines.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/firstDocumentWithOutlines.pdf
new file mode 100644
index 0000000000..8f746e17aa
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/firstDocumentWithOutlines.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/firstDocumentWithStylePropertyInOutline.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/firstDocumentWithStylePropertyInOutline.pdf
new file mode 100644
index 0000000000..81fb035767
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/firstDocumentWithStylePropertyInOutline.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/secondDocumentWithOutlines.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/secondDocumentWithOutlines.pdf
new file mode 100644
index 0000000000..4a497363ac
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/secondDocumentWithOutlines.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/secondDocumentWithStylePropertyInOutline.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/secondDocumentWithStylePropertyInOutline.pdf
new file mode 100644
index 0000000000..a2bd50332e
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/secondDocumentWithStylePropertyInOutline.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/splitTableWithEmptyTd.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/splitTableWithEmptyTd.pdf
new file mode 100644
index 0000000000..246efa644f
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/splitTableWithEmptyTd.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/stackOverflowErrorCycleReferenceOcgMerge.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/stackOverflowErrorCycleReferenceOcgMerge.pdf
new file mode 100644
index 0000000000..9c92896635
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/stackOverflowErrorCycleReferenceOcgMerge.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/tdInsideTdTable.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/tdInsideTdTable.pdf
new file mode 100644
index 0000000000..f0dec670bc
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/tdInsideTdTable.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/trInsideTdTable.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/trInsideTdTable.pdf
new file mode 100644
index 0000000000..78f5667477
Binary files /dev/null and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfMergerTest/trInsideTdTable.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument1_1.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument1_1.pdf
index aeeab3800e..b279fe39a1 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument1_1.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument1_1.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument1_2.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument1_2.pdf
index adae0d9911..b0a8fba077 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument1_2.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument1_2.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument1_3.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument1_3.pdf
index 52b80e8451..8f03e39f17 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument1_3.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument1_3.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument2_1.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument2_1.pdf
index ec642d8832..91ac094652 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument2_1.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument2_1.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument2_2.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument2_2.pdf
index 5ed0194333..c5614e113f 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument2_2.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument2_2.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument3_1.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument3_1.pdf
index 4d3b2901b1..46e56162f9 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument3_1.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument3_1.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument3_2.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument3_2.pdf
index e7866d3ff3..e4d87449c2 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument3_2.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument3_2.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument4_1.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument4_1.pdf
index 29e78a6165..8666246166 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument4_1.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument4_1.pdf differ
diff --git a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument4_2.pdf b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument4_2.pdf
index 5535ad7b51..c56f9f751d 100644
Binary files a/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument4_2.pdf and b/kernel/src/test/resources/com/itextpdf/kernel/utils/PdfSplitterTest/cmp/cmp_splitDocument4_2.pdf differ
diff --git a/layout/pom.xml b/layout/pom.xml
index 9f44a1e938..c2e71edee7 100644
--- a/layout/pom.xml
+++ b/layout/pom.xml
@@ -4,7 +4,7 @@
com.itextpdfroot
- 7.2.0
+ 7.2.1layoutiText 7 - layout
diff --git a/layout/src/main/java/com/itextpdf/layout/element/Table.java b/layout/src/main/java/com/itextpdf/layout/element/Table.java
index 86ee4f6348..6fba6a517f 100644
--- a/layout/src/main/java/com/itextpdf/layout/element/Table.java
+++ b/layout/src/main/java/com/itextpdf/layout/element/Table.java
@@ -576,7 +576,6 @@ public Table startNewRow() {
rows.add(new Cell[columnWidths.length]);
}
return this;
- //TODO when rendering starts, make sure, that last row is not empty.
}
/**
diff --git a/layout/src/main/java/com/itextpdf/layout/logs/LayoutLogMessageConstant.java b/layout/src/main/java/com/itextpdf/layout/logs/LayoutLogMessageConstant.java
new file mode 100644
index 0000000000..5efd7a5197
--- /dev/null
+++ b/layout/src/main/java/com/itextpdf/layout/logs/LayoutLogMessageConstant.java
@@ -0,0 +1,35 @@
+/*
+ This file is part of the iText (R) project.
+ Copyright (c) 1998-2021 iText Group NV
+ Authors: iText Software.
+
+ This program is offered under a commercial and under the AGPL license.
+ For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+ AGPL licensing:
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+package com.itextpdf.layout.logs;
+
+/**
+ * Class containing constants to be used in layout.
+ */
+public final class LayoutLogMessageConstant {
+
+ public static final String ELEMENT_DOES_NOT_FIT_AREA = "Element does not fit current area. {0}";
+
+ private LayoutLogMessageConstant() {
+ //Private constructor will prevent the instantiation of this class directly
+ }
+}
diff --git a/layout/src/main/java/com/itextpdf/layout/margincollapse/MarginsCollapseHandler.java b/layout/src/main/java/com/itextpdf/layout/margincollapse/MarginsCollapseHandler.java
index efbbf5c6f9..cbddee1b97 100644
--- a/layout/src/main/java/com/itextpdf/layout/margincollapse/MarginsCollapseHandler.java
+++ b/layout/src/main/java/com/itextpdf/layout/margincollapse/MarginsCollapseHandler.java
@@ -207,8 +207,8 @@ public void endChildMarginsHandling(Rectangle layoutBox) {
}
public void startMarginsCollapse(Rectangle parentBBox) {
- collapseInfo.getCollapseBefore().joinMargin(getModelTopMargin(renderer));
- collapseInfo.getCollapseAfter().joinMargin(getModelBottomMargin(renderer));
+ collapseInfo.getCollapseBefore().joinMargin(defineTopMarginValueForCollapse(renderer));
+ collapseInfo.getCollapseAfter().joinMargin(defineBottomMarginValueForCollapse(renderer));
if (!firstChildMarginAdjoinedToParent(renderer)) {
float topIndent = collapseInfo.getCollapseBefore().getCollapsedMarginsSize();
@@ -253,7 +253,7 @@ public void endMarginsCollapse(Rectangle layoutBox) {
} else {
ownCollapseAfter = new MarginsCollapse();
}
- ownCollapseAfter.joinMargin(getModelBottomMargin(renderer));
+ ownCollapseAfter.joinMargin(defineBottomMarginValueForCollapse(renderer));
collapseInfo.setOwnCollapseAfter(ownCollapseAfter);
if (collapseInfo.isSelfCollapsing()) {
@@ -544,33 +544,19 @@ private static boolean hasPositiveHeight(IRenderer renderer) {
}
private static boolean hasTopPadding(IRenderer renderer) {
- UnitValue padding = renderer.getModelElement().getProperty(Property.PADDING_TOP);
- if (null != padding && !padding.isPointValue()) {
- Logger logger = LoggerFactory.getLogger(MarginsCollapseHandler.class);
- logger.error(MessageFormatUtil.format(IoLogMessageConstant.PROPERTY_IN_PERCENTS_NOT_SUPPORTED,
- Property.PADDING_TOP));
- }
- return padding != null && padding.getValue() > 0;
+ return MarginsCollapseHandler.hasPadding(renderer, Property.PADDING_TOP);
}
private static boolean hasBottomPadding(IRenderer renderer) {
- UnitValue padding = renderer.getModelElement().getProperty(Property.PADDING_BOTTOM);
- if (null != padding && !padding.isPointValue()) {
- Logger logger = LoggerFactory.getLogger(MarginsCollapseHandler.class);
- logger.error(MessageFormatUtil.format(IoLogMessageConstant.PROPERTY_IN_PERCENTS_NOT_SUPPORTED,
- Property.PADDING_BOTTOM));
- }
- return padding != null && padding.getValue() > 0;
+ return MarginsCollapseHandler.hasPadding(renderer, Property.PADDING_BOTTOM);
}
private static boolean hasTopBorders(IRenderer renderer) {
- IPropertyContainer modelElement = renderer.getModelElement();
- return modelElement.hasProperty(Property.BORDER_TOP) || modelElement.hasProperty(Property.BORDER);
+ return MarginsCollapseHandler.hasBorders(renderer, Property.BORDER_TOP);
}
private static boolean hasBottomBorders(IRenderer renderer) {
- IPropertyContainer modelElement = renderer.getModelElement();
- return modelElement.hasProperty(Property.BORDER_BOTTOM) || modelElement.hasProperty(Property.BORDER);
+ return MarginsCollapseHandler.hasBorders(renderer, Property.BORDER_BOTTOM);
}
private static boolean rendererIsFloated(IRenderer renderer) {
@@ -581,41 +567,56 @@ private static boolean rendererIsFloated(IRenderer renderer) {
return floatPropertyValue != null && !floatPropertyValue.equals(FloatPropertyValue.NONE);
}
- private static float getModelTopMargin(IRenderer renderer) {
- UnitValue marginUV = renderer.getModelElement().getProperty(Property.MARGIN_TOP);
- if (null != marginUV && !marginUV.isPointValue()) {
- Logger logger = LoggerFactory.getLogger(MarginsCollapseHandler.class);
- logger.error(MessageFormatUtil.format(IoLogMessageConstant.PROPERTY_IN_PERCENTS_NOT_SUPPORTED,
- Property.MARGIN_TOP));
- }
- // TODO Concerning "renderer instanceof CellRenderer" check: may be try to apply more general solution in future
- return marginUV != null && !(renderer instanceof CellRenderer) ? marginUV.getValue() : 0;
+ private static float defineTopMarginValueForCollapse(IRenderer renderer) {
+ return MarginsCollapseHandler.defineMarginValueForCollapse(renderer, Property.MARGIN_TOP);
}
private static void ignoreModelTopMargin(IRenderer renderer) {
- renderer.setProperty(Property.MARGIN_TOP, UnitValue.createPointValue(0f));
+ MarginsCollapseHandler.overrideModelTopMargin(renderer, 0f);
}
private static void overrideModelTopMargin(IRenderer renderer, float collapsedMargins) {
- renderer.setProperty(Property.MARGIN_TOP, UnitValue.createPointValue(collapsedMargins));
+ MarginsCollapseHandler.overrideModelMargin(renderer, Property.MARGIN_TOP, collapsedMargins);
}
- private static float getModelBottomMargin(IRenderer renderer) {
- UnitValue marginUV = renderer.getModelElement().getProperty(Property.MARGIN_BOTTOM);
+ private static float defineBottomMarginValueForCollapse(IRenderer renderer) {
+ return MarginsCollapseHandler.defineMarginValueForCollapse(renderer, Property.MARGIN_BOTTOM);
+ }
+
+ private static void ignoreModelBottomMargin(IRenderer renderer) {
+ MarginsCollapseHandler.overrideModelBottomMargin(renderer, 0f);
+ }
+
+ private static void overrideModelBottomMargin(IRenderer renderer, float collapsedMargins) {
+ MarginsCollapseHandler.overrideModelMargin(renderer, Property.MARGIN_BOTTOM, collapsedMargins);
+ }
+
+ private static float defineMarginValueForCollapse(IRenderer renderer, int property) {
+ UnitValue marginUV = renderer.getModelElement().getProperty(property);
if (null != marginUV && !marginUV.isPointValue()) {
Logger logger = LoggerFactory.getLogger(MarginsCollapseHandler.class);
logger.error(MessageFormatUtil.format(IoLogMessageConstant.PROPERTY_IN_PERCENTS_NOT_SUPPORTED,
- Property.MARGIN_TOP));
+ property));
}
- // TODO Concerning "renderer instanceof CellRenderer" check: may be try to apply more general solution in future
return marginUV != null && !(renderer instanceof CellRenderer) ? marginUV.getValue() : 0;
}
- private static void ignoreModelBottomMargin(IRenderer renderer) {
- renderer.setProperty(Property.MARGIN_BOTTOM, UnitValue.createPointValue(0f));
+ private static void overrideModelMargin(IRenderer renderer, int property, float collapsedMargins) {
+ renderer.setProperty(property, UnitValue.createPointValue(collapsedMargins));
}
- private static void overrideModelBottomMargin(IRenderer renderer, float collapsedMargins) {
- renderer.setProperty(Property.MARGIN_BOTTOM, UnitValue.createPointValue(collapsedMargins));
+ private static boolean hasPadding(IRenderer renderer, int property) {
+ UnitValue padding = renderer.getModelElement().getProperty(property);
+ if (null != padding && !padding.isPointValue()) {
+ Logger logger = LoggerFactory.getLogger(MarginsCollapseHandler.class);
+ logger.error(MessageFormatUtil.format(IoLogMessageConstant.PROPERTY_IN_PERCENTS_NOT_SUPPORTED,
+ property));
+ }
+ return padding != null && padding.getValue() > 0;
+ }
+
+ private static boolean hasBorders(IRenderer renderer, int property) {
+ IPropertyContainer modelElement = renderer.getModelElement();
+ return modelElement.hasProperty(property) || modelElement.hasProperty(Property.BORDER);
}
}
diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/BlockRenderer.java b/layout/src/main/java/com/itextpdf/layout/renderer/BlockRenderer.java
index 974bcadf87..133b8d6628 100644
--- a/layout/src/main/java/com/itextpdf/layout/renderer/BlockRenderer.java
+++ b/layout/src/main/java/com/itextpdf/layout/renderer/BlockRenderer.java
@@ -58,6 +58,7 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.layout.LayoutResult;
import com.itextpdf.layout.layout.MinMaxWidthLayoutResult;
import com.itextpdf.layout.layout.PositionedLayoutContext;
+import com.itextpdf.layout.logs.LayoutLogMessageConstant;
import com.itextpdf.layout.margincollapse.MarginsCollapseHandler;
import com.itextpdf.layout.margincollapse.MarginsCollapseInfo;
import com.itextpdf.layout.minmaxwidth.MinMaxWidth;
@@ -446,7 +447,7 @@ public LayoutResult layout(LayoutContext layoutContext) {
if (isNotFittingLayoutArea(layoutContext.getArea())) {
if (isNotFittingWidth(layoutContext.getArea()) && !isNotFittingHeight(layoutContext.getArea())) {
LoggerFactory.getLogger(getClass())
- .warn(MessageFormatUtil.format(IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA,
+ .warn(MessageFormatUtil.format(LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA,
"It fits by height so it will be forced placed"));
} else if (!Boolean.TRUE.equals(getPropertyAsBoolean(Property.FORCED_PLACEMENT))) {
floatRendererAreas.retainAll(nonChildFloatingRendererAreas);
diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/CollapsedTableBorders.java b/layout/src/main/java/com/itextpdf/layout/renderer/CollapsedTableBorders.java
index 35d1391405..42e4ea46c6 100644
--- a/layout/src/main/java/com/itextpdf/layout/renderer/CollapsedTableBorders.java
+++ b/layout/src/main/java/com/itextpdf/layout/renderer/CollapsedTableBorders.java
@@ -50,21 +50,33 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.properties.Property;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
class CollapsedTableBorders extends TableBorders {
/**
- * The list of the cells' borders which should be collapsed
- * with the first border of this TableRenderer instance, to be drawn on the area.
+ * Horizontal borders to be collapsed with
+ * the first-on-the-area row's cell top borders of this TableRenderer instance.
*/
private List topBorderCollapseWith = new ArrayList();
/**
- * The list of the cells' borders which should be collapsed
- * with the last border of this TableRenderer instance, to be drawn on the area.
+ * Horizontal borders to be collapsed with
+ * the last-on-the-area row's cell bottom borders of this TableRenderer instance.
*/
private List bottomBorderCollapseWith = new ArrayList();
+ // NOTE: Currently body's top border is written at header level and footer's top border is written
+ // at body's level, hence there is no need in the same array for vertical top borders.
+ /**
+ * Vertical borders to be collapsed with
+ * the last-on-the-area row's cell bottom borders of this TableRenderer instance.
+ */
+ private List verticalBottomBorderCollapseWith = null;
+
+ private static Comparator borderComparator = new BorderComparator();
+
// region constructors
public CollapsedTableBorders(List rows, int numberOfColumns, Border[] tableBoundingBorders) {
super(rows, numberOfColumns, tableBoundingBorders);
@@ -124,12 +136,32 @@ public float[] getCellBorderIndents(int row, int col, int rowspan, int colspan)
return indents;
}
+ /**
+ * Gets vertical borders which cross the top horizontal border.
+ *
+ * @return vertical borders which cross the top horizontal border
+ */
+ public List getVerticalBordersCrossingTopHorizontalBorder() {
+ List borders = new ArrayList<>(numberOfColumns + 1);
+ for (int i = 0; i <= numberOfColumns; i++) {
+ final List verticalBorder = getVerticalBorder(i);
+ // the passed index indicates the index of the border on the page, not in the entire document
+ Border borderToAdd = startRow - largeTableIndexOffset < verticalBorder.size()
+ ? verticalBorder.get(startRow - largeTableIndexOffset) : null;
+ borders.add(borderToAdd);
+ }
+ return borders;
+ }
+
+ @Override
public List getVerticalBorder(int index) {
if (index == 0) {
- List borderList = TableBorderUtil.createAndFillBorderList(null, tableBoundingBorders[3], verticalBorders.get(0).size());
+ List borderList = TableBorderUtil
+ .createAndFillBorderList(null, tableBoundingBorders[3], verticalBorders.get(0).size());
return getCollapsedList(verticalBorders.get(0), borderList);
} else if (index == numberOfColumns) {
- List borderList = TableBorderUtil.createAndFillBorderList(null, tableBoundingBorders[1], verticalBorders.get(0).size());
+ List borderList = TableBorderUtil.createAndFillBorderList(null, tableBoundingBorders[1],
+ verticalBorders.get(verticalBorders.size() - 1).size());
return getCollapsedList(verticalBorders.get(verticalBorders.size() - 1), borderList);
} else {
return verticalBorders.get(index);
@@ -213,11 +245,16 @@ public CollapsedTableBorders setTopBorderCollapseWith(List topBorderColl
return this;
}
- public CollapsedTableBorders setBottomBorderCollapseWith(List bottomBorderCollapseWith) {
+ public CollapsedTableBorders setBottomBorderCollapseWith(List bottomBorderCollapseWith,
+ List verticalBordersCrossingBottomBorder) {
this.bottomBorderCollapseWith = new ArrayList();
if (null != bottomBorderCollapseWith) {
this.bottomBorderCollapseWith.addAll(bottomBorderCollapseWith);
}
+ this.verticalBottomBorderCollapseWith = null;
+ if (null != verticalBordersCrossingBottomBorder) {
+ this.verticalBottomBorderCollapseWith = new ArrayList(verticalBordersCrossingBottomBorder);
+ }
return this;
}
//endregion
@@ -329,28 +366,7 @@ protected void buildBordersArrays(CellRenderer cell, int row, boolean isNeighbou
// region lowlevel
protected boolean checkAndReplaceBorderInArray(List> borderArray, int i, int j, Border borderToAdd, boolean hasPriority) {
-// if (borderArray.size() <= i) {
-// for (int count = borderArray.size(); count <= i; count++) {
-// borderArray.add(new ArrayList());
-// }
-// }
List borders = borderArray.get(i);
-// if (borders.isEmpty()) {
-// for (int count = 0; count < j; count++) {
-// borders.add(null);
-// }
-// borders.add(borderToAdd);
-// return true;
-// }
-// if (borders.size() == j) {
-// borders.add(borderToAdd);
-// return true;
-// }
-// if (borders.size() < j) {
-// for (int count = borders.size(); count <= j; count++) {
-// borders.add(count, null);
-// }
-// }
Border neighbour = borders.get(j);
if (neighbour == null) {
borders.set(j, borderToAdd);
@@ -372,85 +388,132 @@ protected boolean checkAndReplaceBorderInArray(List> borderArray, i
// endregion
// region draw
- protected TableBorders drawHorizontalBorder(int i, float startX, float y1, PdfCanvas canvas, float[] countedColumnWidth) {
- List borders = getHorizontalBorder(startRow /*- largeTableIndexOffset*/ + i);
+ protected TableBorders drawHorizontalBorder(PdfCanvas canvas, TableBorderDescriptor borderDescriptor) {
+ int i = borderDescriptor.getBorderIndex();
+ float startX = borderDescriptor.getMainCoordinateStart();
+ float y1 = borderDescriptor.getCrossCoordinate();
+ float[] countedColumnWidth = borderDescriptor.getMainCoordinateWidths();
+
+ List horizontalBorder = getHorizontalBorder(startRow + i);
float x1 = startX;
float x2 = x1 + countedColumnWidth[0];
- if (i == 0) {
- Border firstBorder = getFirstVerticalBorder().get(startRow - largeTableIndexOffset);
- if (firstBorder != null) {
- x1 -= firstBorder.getWidth() / 2;
- }
- } else if (i == finishRow - startRow + 1) {
- Border firstBorder = getFirstVerticalBorder().get(startRow - largeTableIndexOffset + finishRow - startRow + 1 - 1);
- if (firstBorder != null) {
- x1 -= firstBorder.getWidth() / 2;
- }
- }
- int j;
- for (j = 1; j < borders.size(); j++) {
- Border prevBorder = borders.get(j - 1);
- Border curBorder = borders.get(j);
- if (prevBorder != null) {
- if (!prevBorder.equals(curBorder)) {
- prevBorder.drawCellBorder(canvas, x1, y1, x2, y1, Border.Side.NONE);
- x1 = x2;
+ for (int j = 1; j <= horizontalBorder.size(); j++) {
+ Border currentBorder = horizontalBorder.get(j - 1);
+ Border nextBorder = j < horizontalBorder.size() ? horizontalBorder.get(j) : null;
+ if (currentBorder != null) {
+ List crossingBordersAtStart = getCrossingBorders(i, j - 1);
+ float startCornerWidth = getWidestBorderWidth(crossingBordersAtStart.get(1),
+ crossingBordersAtStart.get(3));
+ List crossingBordersAtEnd = getCrossingBorders(i, j);
+ float endCornerWidth = getWidestBorderWidth(crossingBordersAtEnd.get(1), crossingBordersAtEnd.get(3));
+
+ // TODO DEVSIX-5962 Once the ticket is done, remove this workaround, which allows
+ // horizontal borders to win at vertical-0. Bear in mind that this workaround helps
+ // in standard cases, when borders are of the same width. If they are not then
+ // this workaround doesn't help to improve corner collapsing
+ if (1 == j) {
+ crossingBordersAtStart.add(0, currentBorder);
+ }
+ if (0 == i) {
+ if (1 != j) {
+ crossingBordersAtStart.add(0, crossingBordersAtStart.get(3));
+ }
+ crossingBordersAtEnd.add(0, crossingBordersAtEnd.get(3));
}
+
+ Collections.sort(crossingBordersAtStart, borderComparator);
+ Collections.sort(crossingBordersAtEnd, borderComparator);
+
+ float x1Offset = currentBorder.equals(crossingBordersAtStart.get(0))
+ ? -startCornerWidth / 2
+ : startCornerWidth / 2;
+ float x2Offset = currentBorder.equals(crossingBordersAtEnd.get(0))
+ ? endCornerWidth / 2
+ : -endCornerWidth / 2;
+ currentBorder.drawCellBorder(canvas, x1 + x1Offset, y1, x2 + x2Offset, y1, Border.Side.NONE);
+ x1 = x2;
} else {
+ // if current border is null, then just skip it's processing.
+ // Border corners will be processed by borders which are not null.
x1 += countedColumnWidth[j - 1];
x2 = x1;
}
- if (curBorder != null) {
+ if (nextBorder != null && j != horizontalBorder.size()) {
x2 += countedColumnWidth[j];
}
}
-
- Border lastBorder = borders.size() > j - 1 ? borders.get(j - 1) : null;
- if (lastBorder != null) {
- if (i == 0) {
- if (getVerticalBorder(j).get(startRow - largeTableIndexOffset + i) != null)
- x2 += getVerticalBorder(j).get(startRow - largeTableIndexOffset + i).getWidth() / 2;
- } else if (i == finishRow - startRow + 1 && getVerticalBorder(j).size() > startRow - largeTableIndexOffset + i - 1 && getVerticalBorder(j).get(startRow - largeTableIndexOffset + i - 1) != null) {
- x2 += getVerticalBorder(j).get(startRow - largeTableIndexOffset + i - 1).getWidth() / 2;
- }
-
- lastBorder.drawCellBorder(canvas, x1, y1, x2, y1, Border.Side.NONE);
- }
return this;
}
- protected TableBorders drawVerticalBorder(int i, float startY, float x1, PdfCanvas canvas, List heights) {
+ protected TableBorders drawVerticalBorder(PdfCanvas canvas, TableBorderDescriptor borderDescriptor) {
+ int i = borderDescriptor.getBorderIndex();
+ float startY = borderDescriptor.getMainCoordinateStart();
+ float x1 = borderDescriptor.getCrossCoordinate();
+ float[] heights = borderDescriptor.getMainCoordinateWidths();
+
List borders = getVerticalBorder(i);
float y1 = startY;
float y2 = y1;
- if (!heights.isEmpty()) {
- y2 = y1 - (float) heights.get(0);
- }
- int j;
- for (j = 1; j < heights.size(); j++) {
- Border prevBorder = borders.get(startRow - largeTableIndexOffset + j - 1);
- Border curBorder = borders.get(startRow - largeTableIndexOffset + j);
- if (prevBorder != null) {
- if (!prevBorder.equals(curBorder)) {
- prevBorder.drawCellBorder(canvas, x1, y1, x1, y2, Border.Side.NONE);
+ if (0 != heights.length) {
+ y2 = y1 - heights[0];
+ }
+ Float y1Offset = null;
+ for (int j = 1; j <= heights.length; j++) {
+ Border currentBorder = borders.get(startRow - largeTableIndexOffset + j - 1);
+ Border nextBorder = j < heights.length ? borders.get(startRow - largeTableIndexOffset + j) : null;
+ if (currentBorder != null) {
+ List crossingBordersAtStart = getCrossingBorders(j - 1, i);
+ float startCornerWidth = getWidestBorderWidth(crossingBordersAtStart.get(0),
+ crossingBordersAtStart.get(2));
+ // TODO DEVSIX-5962 Once the ticket is done, remove this workaround, which allows
+ // vertical borders to win at horizontal-0. Bear in mind that this workaround helps
+ // in standard cases, when borders are of the same width. If they are not then
+ // this workaround doesn't help to improve corner collapsing
+ if (1 == j) {
+ crossingBordersAtStart.add(0, currentBorder);
+ }
+ Collections.sort(crossingBordersAtStart, borderComparator);
+
+ List crossingBordersAtEnd = getCrossingBorders(j, i);
+ float endCornerWidth = getWidestBorderWidth(crossingBordersAtEnd.get(0), crossingBordersAtEnd.get(2));
+ Collections.sort(crossingBordersAtEnd, borderComparator);
+
+ // if all the borders are equal, we need to draw them at the end
+ if (!currentBorder.equals(nextBorder)) {
+ if (null == y1Offset) {
+ y1Offset = currentBorder.equals(crossingBordersAtStart.get(0))
+ ? startCornerWidth / 2
+ : -startCornerWidth / 2;
+ }
+ float y2Offset = currentBorder.equals(crossingBordersAtEnd.get(0))
+ ? -endCornerWidth / 2
+ : endCornerWidth / 2;
+
+ currentBorder
+ .drawCellBorder(canvas, x1, y1 + (float) y1Offset, x1, y2 + y2Offset, Border.Side.NONE);
y1 = y2;
+ y1Offset = null;
+ } else {
+ // if current border equal the next one, we apply an optimization here, which allows us
+ // to draw equal borders at once and not by part. Therefore for the first of such borders
+ // we store its start offset
+ if (null == y1Offset) {
+ y1Offset = currentBorder.equals(crossingBordersAtStart.get(0))
+ ? startCornerWidth / 2
+ : -startCornerWidth / 2;
+ }
}
} else {
- y1 -= (float) heights.get(j - 1);
+ // if current border is null, then just skip it's processing.
+ // Border corners will be processed by borders which are not null.
+ y1 -= heights[j - 1];
y2 = y1;
}
- if (curBorder != null) {
- y2 -= (float) heights.get(j);
+ if (nextBorder != null) {
+ y2 -= heights[j];
}
}
- if (borders.size() == 0) {
- return this;
- }
- Border lastBorder = borders.get(startRow - largeTableIndexOffset + j - 1);
- if (lastBorder != null) {
- lastBorder.drawCellBorder(canvas, x1, y1, x1, y2, Border.Side.NONE);
- }
return this;
}
// endregion
@@ -555,10 +618,11 @@ protected TableBorders updateBordersOnNewPage(boolean isOriginalNonSplitRenderer
rightBorderMaxWidth = getMaxRightWidth();
leftBorderMaxWidth = getMaxLeftWidth();
}
+ // in case of large table and no content (Table#complete is called right after Table#flush)
setTopBorderCollapseWith(((Table) currentRenderer.getModelElement()).getLastRowBottomBorder());
} else {
setTopBorderCollapseWith(null);
- setBottomBorderCollapseWith(null);
+ setBottomBorderCollapseWith(null, null);
}
}
if (null != footerRenderer) {
@@ -582,7 +646,7 @@ protected TableBorders updateBordersOnNewPage(boolean isOriginalNonSplitRenderer
protected TableBorders skipFooter(Border[] borders) {
setTableBoundingBorders(borders);
- setBottomBorderCollapseWith(null);
+ setBottomBorderCollapseWith(null, null);
return this;
}
@@ -593,15 +657,19 @@ protected TableBorders skipHeader(Border[] borders) {
}
protected TableBorders collapseTableWithFooter(TableBorders footerBordersHandler, boolean hasContent) {
- ((CollapsedTableBorders) footerBordersHandler).setTopBorderCollapseWith(hasContent ? getLastHorizontalBorder() : getTopBorderCollapseWith());
- setBottomBorderCollapseWith(footerBordersHandler.getHorizontalBorder(0));
+ ((CollapsedTableBorders) footerBordersHandler).setTopBorderCollapseWith(
+ hasContent ? getLastHorizontalBorder() : getTopBorderCollapseWith());
+ setBottomBorderCollapseWith(footerBordersHandler.getHorizontalBorder(0),
+ ((CollapsedTableBorders) footerBordersHandler).getVerticalBordersCrossingTopHorizontalBorder());
return this;
}
protected TableBorders collapseTableWithHeader(TableBorders headerBordersHandler, boolean updateBordersHandler) {
- ((CollapsedTableBorders) headerBordersHandler).setBottomBorderCollapseWith(getHorizontalBorder(startRow));
+ ((CollapsedTableBorders) headerBordersHandler).setBottomBorderCollapseWith(getHorizontalBorder(startRow),
+ getVerticalBordersCrossingTopHorizontalBorder());
if (updateBordersHandler) {
- setTopBorderCollapseWith(headerBordersHandler.getLastHorizontalBorder());
+ setTopBorderCollapseWith(
+ headerBordersHandler.getLastHorizontalBorder());
}
return this;
}
@@ -613,4 +681,113 @@ protected TableBorders fixHeaderOccupiedArea(Rectangle occupiedBox, Rectangle la
return this;
}
// endregion
+
+ /**
+ * Returns the {@link Border} instances, which intersect in the specified point.
+ *
+ *
+ * The order of the borders: first the left one, then the top, the right and the bottom ones.
+ *
+ * @param horizontalIndex index of horizontal border
+ * @param verticalIndex index of vertical border
+ * @return a list of {@link Border} instances, which intersect in the specified point
+ */
+ List getCrossingBorders(int horizontalIndex, int verticalIndex) {
+ List horizontalBorder = getHorizontalBorder(startRow + horizontalIndex);
+ List verticalBorder = getVerticalBorder(verticalIndex);
+
+ List crossingBorders = new ArrayList<>(4);
+ crossingBorders.add(verticalIndex > 0 ? horizontalBorder.get(verticalIndex - 1) : null);
+ crossingBorders.add(horizontalIndex > 0
+ ? verticalBorder.get(startRow - largeTableIndexOffset + horizontalIndex - 1) : null);
+ crossingBorders.add(verticalIndex < numberOfColumns ? horizontalBorder.get(verticalIndex) : null);
+ crossingBorders.add(horizontalIndex <= finishRow - startRow
+ ? verticalBorder.get(startRow - largeTableIndexOffset + horizontalIndex) : null);
+
+ // In case the last horizontal border on the page is specified,
+ // we need to consider a vertical border of the table's bottom part
+ // (f.e., for header it is table's body).
+ if (horizontalIndex == finishRow - startRow + 1 && null != verticalBottomBorderCollapseWith) {
+ if (isBorderWider(verticalBottomBorderCollapseWith.get(verticalIndex), crossingBorders.get(3))) {
+ crossingBorders.set(3, verticalBottomBorderCollapseWith.get(verticalIndex));
+ }
+ }
+ return crossingBorders;
+ }
+
+ /**
+ * A comparison function to compare two {@link Border} instances.
+ */
+ private static class BorderComparator implements Comparator {
+
+ @Override
+ /**
+ * Compares its two {@link Border} instances for order. Returns a negative integer,
+ * zero, or a positive integer as the first argument is wider than, of equal width,
+ * or more narrow than the second.
+ */
+ public int compare(Border o1, Border o2) {
+ if (o1 == o2) {
+ return 0;
+ } else if (null == o1) {
+ return 1;
+ } else if (null == o2) {
+ return -1;
+ } else {
+ return Float.compare(o2.getWidth(), o1.getWidth());
+ }
+ }
+ }
+
+ /**
+ * Gets the width of the widest border in the specified list.
+ *
+ * @param borders the borders which widths should be considered
+ * @return the width of the widest border in the specified list
+ */
+ private float getWidestBorderWidth(Border... borders) {
+ float maxWidth = 0;
+ for (Border border : borders) {
+ if (null != border && maxWidth < border.getWidth()) {
+ maxWidth = border.getWidth();
+ }
+ }
+ return maxWidth;
+ }
+
+ /**
+ * Compares borders and defines whether this border is wider than the other.
+ *
+ *
+ * Note that by default the comparison will be strict, e.g. if this border
+ * is of the same width as the other border, then false will be returned.
+ *
+ * @param thisBorder this border
+ * @param otherBorder the other border to be compared with
+ * @return whether this border is wider than the other
+ */
+ private static boolean isBorderWider(Border thisBorder, Border otherBorder) {
+ return isBorderWider(thisBorder, otherBorder, true);
+ }
+
+ /**
+ * Compares borders and defines whether this border is wider than the other.
+ *
+ * @param thisBorder this border
+ * @param otherBorder the other border to be compared with
+ * @param strict if true, then in case this border is of the same width as the other border,
+ * true will be returned. If false, it will be checked whether the width
+ * of this border is strictly greater than the other border's width
+ * @return whether this border is wider than the other
+ */
+ private static boolean isBorderWider(Border thisBorder, Border otherBorder, boolean strict) {
+ if (null == thisBorder) {
+ return false;
+ }
+ if (null == otherBorder) {
+ return true;
+ }
+ int comparisonResult = Float.compare(thisBorder.getWidth(), otherBorder.getWidth());
+ return strict ? comparisonResult > 0 : comparisonResult >= 0;
+ }
}
diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/ListItemRenderer.java b/layout/src/main/java/com/itextpdf/layout/renderer/ListItemRenderer.java
index b8bf85f609..f304ff43ef 100644
--- a/layout/src/main/java/com/itextpdf/layout/renderer/ListItemRenderer.java
+++ b/layout/src/main/java/com/itextpdf/layout/renderer/ListItemRenderer.java
@@ -45,7 +45,11 @@ This file is part of the iText (R) project.
import com.itextpdf.io.logs.IoLogMessageConstant;
import com.itextpdf.kernel.font.PdfFont;
+import com.itextpdf.kernel.geom.Rectangle;
+import com.itextpdf.kernel.pdf.PdfDocument;
+import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.tagging.StandardRoles;
+import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.ListItem;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.layout.LayoutContext;
@@ -135,7 +139,7 @@ public void draw(DrawContext drawContext) {
// It will be null in case of overflow (only the "split" part will contain symbol renderer.
if (symbolRenderer != null && !symbolAddedInside) {
- boolean isRtl = BaseDirection.RIGHT_TO_LEFT.equals(this.getProperty(Property.BASE_DIRECTION));
+ boolean isRtl = BaseDirection.RIGHT_TO_LEFT == this.getProperty(Property.BASE_DIRECTION);
symbolRenderer.setParent(this);
float x = isRtl ? occupiedArea.getBBox().getRight() : occupiedArea.getBBox().getLeft();
ListSymbolPosition symbolPosition = (ListSymbolPosition) ListRenderer.getListItemOrListProperty(this, parent, Property.LIST_SYMBOL_POSITION);
@@ -223,7 +227,12 @@ public void draw(DrawContext drawContext) {
symbolRenderer.move(dxPosition, 0);
}
- if (symbolRenderer.getOccupiedArea().getBBox().getRight() > parent.getOccupiedArea().getBBox().getLeft()) {
+ // consider page area without margins
+ Rectangle effectiveArea = obtainEffectiveArea(drawContext);
+
+ // symbols are not drawn here, because they are in page margins
+ if (!isRtl && symbolRenderer.getOccupiedArea().getBBox().getRight() > effectiveArea.getLeft()
+ || isRtl && symbolRenderer.getOccupiedArea().getBBox().getLeft() < effectiveArea.getRight()) {
beginElementOpacityApplying(drawContext);
symbolRenderer.draw(drawContext);
endElementOpacityApplying(drawContext);
@@ -340,4 +349,23 @@ private float[] calculateAscenderDescender() {
return new float[] {0, 0};
}
+ private Rectangle obtainEffectiveArea(DrawContext drawContext) {
+ PdfDocument pdfDocument = drawContext.getDocument();
+
+ // for the time being iText creates a single symbol renderer for a list.
+ // This renderer will be used for all the items across all the pages, which mean that it could
+ // be layouted at page i and used at page j, j>i.
+ int pageNumber = parent.getOccupiedArea().getPageNumber();
+ Rectangle pageSize;
+ if (pageNumber != 0) {
+ PdfPage page = pdfDocument.getPage(pageNumber);
+ pageSize = page.getPageSize();
+ } else {
+ pageSize = pdfDocument.getDefaultPageSize();
+ }
+
+ Document document = new Document(pdfDocument);
+ return new Rectangle(pageSize).applyMargins(document.getTopMargin(), document.getRightMargin(), document.getBottomMargin(),
+ document.getLeftMargin(), false);
+ }
}
diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/ListRenderer.java b/layout/src/main/java/com/itextpdf/layout/renderer/ListRenderer.java
index 7d7f8e4165..01d65e4e43 100644
--- a/layout/src/main/java/com/itextpdf/layout/renderer/ListRenderer.java
+++ b/layout/src/main/java/com/itextpdf/layout/renderer/ListRenderer.java
@@ -219,16 +219,7 @@ private IRenderer createListSymbolRenderer(int index, IRenderer renderer) {
final String constantFont = (numberingType == ListNumberingType.GREEK_LOWER || numberingType == ListNumberingType.GREEK_UPPER) ?
StandardFonts.SYMBOL : StandardFonts.ZAPFDINGBATS;
- textRenderer = new TextRenderer(textElement) {
- @Override
- public void draw(DrawContext drawContext) {
- try {
- setProperty(Property.FONT, PdfFontFactory.createFont(constantFont));
- } catch (IOException ignored) {
- }
- super.draw(drawContext);
- }
- };
+ textRenderer = new ConstantFontTextRenderer(textElement, constantFont);
try {
textRenderer.setProperty(Property.FONT, PdfFontFactory.createFont(constantFont));
} catch (IOException exc) {
@@ -403,4 +394,23 @@ private LayoutResult initializeListSymbols(LayoutContext layoutContext) {
}
return null;
}
+
+ private static final class ConstantFontTextRenderer extends TextRenderer {
+ private String constantFontName;
+
+ public ConstantFontTextRenderer(Text textElement, String font) {
+ super(textElement);
+ constantFontName = font;
+ }
+
+ @Override
+ public void draw(DrawContext drawContext) {
+ try {
+ setProperty(Property.FONT, PdfFontFactory.createFont(constantFontName));
+ } catch (IOException ignored) {
+ // Do nothing
+ }
+ super.draw(drawContext);
+ }
+ }
}
diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/ParagraphRenderer.java b/layout/src/main/java/com/itextpdf/layout/renderer/ParagraphRenderer.java
index 282faa42a6..c331058978 100644
--- a/layout/src/main/java/com/itextpdf/layout/renderer/ParagraphRenderer.java
+++ b/layout/src/main/java/com/itextpdf/layout/renderer/ParagraphRenderer.java
@@ -43,7 +43,6 @@ This file is part of the iText (R) project.
*/
package com.itextpdf.layout.renderer;
-import com.itextpdf.io.logs.IoLogMessageConstant;
import com.itextpdf.commons.utils.MessageFormatUtil;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.layout.borders.Border;
@@ -54,6 +53,7 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.layout.LineLayoutContext;
import com.itextpdf.layout.layout.LineLayoutResult;
import com.itextpdf.layout.layout.MinMaxWidthLayoutResult;
+import com.itextpdf.layout.logs.LayoutLogMessageConstant;
import com.itextpdf.layout.margincollapse.MarginsCollapseHandler;
import com.itextpdf.layout.minmaxwidth.MinMaxWidth;
import com.itextpdf.layout.minmaxwidth.MinMaxWidthUtils;
@@ -484,7 +484,7 @@ protected LayoutResult directLayout(LayoutContext layoutContext) {
if (isNotFittingLayoutArea(layoutContext.getArea())) {
if(isNotFittingWidth(layoutContext.getArea()) && !isNotFittingHeight(layoutContext.getArea())) {
LoggerFactory.getLogger(getClass())
- .warn(MessageFormatUtil.format(IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA,
+ .warn(MessageFormatUtil.format(LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA,
"It fits by height so it will be forced placed"));
} else if (!Boolean.TRUE.equals(getPropertyAsBoolean(Property.FORCED_PLACEMENT))) {
floatRendererAreas.retainAll(nonChildFloatingRendererAreas);
diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/RootRenderer.java b/layout/src/main/java/com/itextpdf/layout/renderer/RootRenderer.java
index c772084574..b0ec812026 100644
--- a/layout/src/main/java/com/itextpdf/layout/renderer/RootRenderer.java
+++ b/layout/src/main/java/com/itextpdf/layout/renderer/RootRenderer.java
@@ -57,6 +57,7 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.layout.LayoutResult;
import com.itextpdf.layout.layout.PositionedLayoutContext;
import com.itextpdf.layout.layout.RootLayoutArea;
+import com.itextpdf.layout.logs.LayoutLogMessageConstant;
import com.itextpdf.layout.margincollapse.MarginsCollapseHandler;
import com.itextpdf.layout.margincollapse.MarginsCollapseInfo;
import com.itextpdf.layout.properties.ClearPropertyValue;
@@ -168,7 +169,7 @@ public void addChild(IRenderer renderer) {
((ImageRenderer) result.getOverflowRenderer()).autoScale(currentArea);
result.getOverflowRenderer().setProperty(Property.FORCED_PLACEMENT, true);
Logger logger = LoggerFactory.getLogger(RootRenderer.class);
- logger.warn(MessageFormatUtil.format(IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, ""));
+ logger.warn(MessageFormatUtil.format(LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, ""));
}
} else {
if (currentArea.isEmptyArea() && result.getAreaBreak() == null) {
@@ -499,7 +500,7 @@ private boolean updateForcedPlacement(IRenderer currentRenderer, IRenderer overf
overflowRenderer.setProperty(Property.FORCED_PLACEMENT, true);
Logger logger = LoggerFactory.getLogger(RootRenderer.class);
if (logger.isWarnEnabled()) {
- logger.warn(MessageFormatUtil.format(IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, ""));
+ logger.warn(MessageFormatUtil.format(LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, ""));
}
return true;
}
@@ -530,7 +531,7 @@ private boolean tryDisableKeepTogether(LayoutResult result,
Logger logger = LoggerFactory.getLogger(RootRenderer.class);
if (logger.isWarnEnabled()) {
logger.warn(MessageFormatUtil.format(
- IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA,
+ LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA,
"KeepTogether property will be ignored."));
}
if (!rendererIsFloat) {
diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/SeparatedTableBorders.java b/layout/src/main/java/com/itextpdf/layout/renderer/SeparatedTableBorders.java
index 9f16ff71b8..a6af26db26 100644
--- a/layout/src/main/java/com/itextpdf/layout/renderer/SeparatedTableBorders.java
+++ b/layout/src/main/java/com/itextpdf/layout/renderer/SeparatedTableBorders.java
@@ -64,12 +64,12 @@ public SeparatedTableBorders(List rows, int numberOfColumns, Bor
}
@Override
- protected TableBorders drawHorizontalBorder(int i, float startX, float y1, PdfCanvas canvas, float[] countedColumnWidth) {
+ protected TableBorders drawHorizontalBorder(PdfCanvas canvas, TableBorderDescriptor borderDescriptor) {
return this;
}
@Override
- protected TableBorders drawVerticalBorder(int i, float startY, float x1, PdfCanvas canvas, List heights) {
+ protected TableBorders drawVerticalBorder(PdfCanvas canvas, TableBorderDescriptor borderDescriptor) {
return this;
}
@@ -222,28 +222,7 @@ protected void buildBordersArrays(CellRenderer cell, int row, int col, int[] row
}
protected boolean checkAndReplaceBorderInArray(List> borderArray, int i, int j, Border borderToAdd, boolean hasPriority) {
-// if (borderArray.size() <= i) {
-// for (int count = borderArray.size(); count <= i; count++) {
-// borderArray.add(new ArrayList());
-// }
-// }
List borders = borderArray.get(i);
-// if (borders.isEmpty()) {
-// for (int count = 0; count < j; count++) {
-// borders.add(null);
-// }
-// borders.add(borderToAdd);
-// return true;
-// }
-// if (borders.size() == j) {
-// borders.add(borderToAdd);
-// return true;
-// }
-// if (borders.size() < j) {
-// for (int count = borders.size(); count <= j; count++) {
-// borders.add(count, null);
-// }
-// }
Border neighbour = borders.get(j);
if (neighbour == null) {
borders.set(j, borderToAdd);
diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/TableBorderDescriptor.java b/layout/src/main/java/com/itextpdf/layout/renderer/TableBorderDescriptor.java
new file mode 100644
index 0000000000..174adbbd1f
--- /dev/null
+++ b/layout/src/main/java/com/itextpdf/layout/renderer/TableBorderDescriptor.java
@@ -0,0 +1,83 @@
+/*
+
+ This file is part of the iText (R) project.
+ Copyright (c) 1998-2021 iText Group NV
+ Authors: Bruno Lowagie, Paulo Soares, et al.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License version 3
+ as published by the Free Software Foundation with the addition of the
+ following permission added to Section 15 as permitted in Section 7(a):
+ FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
+ ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
+ OF THIRD PARTY RIGHTS
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+ You should have received a copy of the GNU Affero General Public License
+ along with this program; if not, see http://www.gnu.org/licenses or write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA, 02110-1301 USA, or download the license from the following URL:
+ http://itextpdf.com/terms-of-use/
+
+ The interactive user interfaces in modified source and object code versions
+ of this program must display Appropriate Legal Notices, as required under
+ Section 5 of the GNU Affero General Public License.
+
+ In accordance with Section 7(b) of the GNU Affero General Public License,
+ a covered work must retain the producer line in every PDF that is created
+ or manipulated using iText.
+
+ You can be released from the requirements of the license by purchasing
+ a commercial license. Buying such a license is mandatory as soon as you
+ develop commercial activities involving the iText software without
+ disclosing the source code of your own applications.
+ These activities include: offering paid services to customers as an ASP,
+ serving PDFs on the fly in a web application, shipping iText with a closed
+ source product.
+
+ For more information, please contact iText Software Corp. at this
+ address: sales@itextpdf.com
+ */
+package com.itextpdf.layout.renderer;
+
+class TableBorderDescriptor {
+ private int borderIndex;
+ private float mainCoordinateStart;
+ private float crossCoordinate;
+ private float[] mainCoordinateWidths;
+
+ /**
+ * Creates a table border descriptor which will be used while drawing the described border.
+ *
+ * @param borderIndex the index of the described border
+ * @param mainCoordinateStart the border's start main-axis coordinate
+ * @param crossCoordinate fixed cross-axis coordinate of the whole border
+ * @param mainCoordinateWidths the sizes (widths or heights) of rows or columns depending on the type of main axis
+ */
+ public TableBorderDescriptor(int borderIndex, float mainCoordinateStart, float crossCoordinate,
+ float[] mainCoordinateWidths) {
+ this.borderIndex = borderIndex;
+ this.mainCoordinateStart = mainCoordinateStart;
+ this.crossCoordinate = crossCoordinate;
+ this.mainCoordinateWidths = mainCoordinateWidths;
+ }
+
+ public int getBorderIndex() {
+ return borderIndex;
+ }
+
+ public float getMainCoordinateStart() {
+ return mainCoordinateStart;
+ }
+
+ public float getCrossCoordinate() {
+ return crossCoordinate;
+ }
+
+ public float[] getMainCoordinateWidths() {
+ return mainCoordinateWidths;
+ }
+}
diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/TableBorders.java b/layout/src/main/java/com/itextpdf/layout/renderer/TableBorders.java
index 9e860b4cdf..6f6f9e73a5 100644
--- a/layout/src/main/java/com/itextpdf/layout/renderer/TableBorders.java
+++ b/layout/src/main/java/com/itextpdf/layout/renderer/TableBorders.java
@@ -48,11 +48,11 @@ This file is part of the iText (R) project.
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.properties.Property;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
abstract class TableBorders {
/**
@@ -138,9 +138,9 @@ public TableBorders(List rows, int numberOfColumns, Border[] tab
// region abstract
// region draw
- protected abstract TableBorders drawHorizontalBorder(int i, float startX, float y1, PdfCanvas canvas, float[] countedColumnWidth);
+ protected abstract TableBorders drawHorizontalBorder(PdfCanvas canvas, TableBorderDescriptor borderDescriptor);
- protected abstract TableBorders drawVerticalBorder(int i, float startY, float x1, PdfCanvas canvas, List heights);
+ protected abstract TableBorders drawVerticalBorder(PdfCanvas canvas, TableBorderDescriptor borderDescriptor);
// endregion
// region area occupation
diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/TableRenderer.java b/layout/src/main/java/com/itextpdf/layout/renderer/TableRenderer.java
index 60c696cdf4..2c90714add 100644
--- a/layout/src/main/java/com/itextpdf/layout/renderer/TableRenderer.java
+++ b/layout/src/main/java/com/itextpdf/layout/renderer/TableRenderer.java
@@ -43,8 +43,8 @@ This file is part of the iText (R) project.
*/
package com.itextpdf.layout.renderer;
-import com.itextpdf.io.logs.IoLogMessageConstant;
import com.itextpdf.commons.utils.MessageFormatUtil;
+import com.itextpdf.io.logs.IoLogMessageConstant;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.canvas.CanvasArtifact;
import com.itextpdf.kernel.pdf.tagutils.TagTreePointer;
@@ -65,8 +65,6 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.properties.UnitValue;
import com.itextpdf.layout.properties.VerticalAlignment;
import com.itextpdf.layout.tagging.LayoutTaggingHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -75,6 +73,8 @@ This file is part of the iText (R) project.
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* This class represents the {@link IRenderer renderer} object for a {@link Table}
@@ -781,7 +781,7 @@ public LayoutResult layout(LayoutContext layoutContext) {
// And now, when we possess such knowledge, we are performing the second attempt, but we need to nullify results
// from the previous attempt
if (bordersHandler instanceof CollapsedTableBorders) {
- ((CollapsedTableBorders)bordersHandler).setBottomBorderCollapseWith(null);
+ ((CollapsedTableBorders) bordersHandler).setBottomBorderCollapseWith(null, null);
}
bordersHandler.collapseTableWithFooter(footerRenderer.bordersHandler, hasContent || 0 != childRenderers.size());
@@ -1050,10 +1050,6 @@ public LayoutResult layout(LayoutContext layoutContext) {
}
}
} else {
- // the bottom border should be processed and placed lately
- if (0 != heights.size()) {
- heights.set(heights.size() - 1, heights.get(heights.size() - 1) - bottomTableBorderWidth / 2);
- }
if (null == footerRenderer) {
if (0 != childRenderers.size()) {
bordersHandler.applyBottomTableBorder(occupiedArea.getBBox(), layoutBox, 0 == childRenderers.size(), false, true);
@@ -1454,7 +1450,7 @@ private void drawBorders(DrawContext drawContext, boolean hasHeader, boolean has
}
- // process halves of the borders here
+ // process halves of horizontal bounding borders
if (childRenderers.size() == 0) {
Border[] borders = bordersHandler.tableBoundingBorders;
if (null != borders[0]) {
@@ -1486,56 +1482,60 @@ private void drawBorders(DrawContext drawContext, boolean hasHeader, boolean has
if (bordersHandler instanceof CollapsedTableBorders) {
if (hasFooter) {
- ((CollapsedTableBorders) bordersHandler).setBottomBorderCollapseWith(footerRenderer.bordersHandler.getFirstHorizontalBorder());
+ ((CollapsedTableBorders) bordersHandler).setBottomBorderCollapseWith(
+ footerRenderer.bordersHandler.getFirstHorizontalBorder(),
+ ((CollapsedTableBorders) footerRenderer.bordersHandler)
+ .getVerticalBordersCrossingTopHorizontalBorder());
} else if (isBottomTablePart) {
- ((CollapsedTableBorders) bordersHandler).setBottomBorderCollapseWith(null);
+ ((CollapsedTableBorders) bordersHandler).setBottomBorderCollapseWith(null, null);
}
}
// we do not need to fix top border, because either this is header or the top border has been already written
float y1 = startY;
- if (isFooterRendererOfLargeTable) {
- bordersHandler.drawHorizontalBorder(0, startX, y1, drawContext.getCanvas(), countedColumnWidth);
- }
- if (0 != heights.size()) {
- y1 -= (float) heights.get(0);
- }
- for (int i = 1; i < heights.size(); i++) {
- bordersHandler.drawHorizontalBorder(i, startX, y1, drawContext.getCanvas(), countedColumnWidth);
- if (i < heights.size()) {
- y1 -= (float) heights.get(i);
- }
- }
- if (!isBottomTablePart && isComplete) {
- bordersHandler.drawHorizontalBorder(heights.size(), startX, y1, drawContext.getCanvas(), countedColumnWidth);
+
+ float[] heightsArray = new float[heights.size()];
+ for (int j = 0; j < heights.size(); j++) {
+ heightsArray[j] = heights.get(j);
}
+ // draw vertical borders
float x1 = startX;
- if (countedColumnWidth.length > 0) {
- x1 += countedColumnWidth[0];
- }
- for (int i = 1; i < bordersHandler.getNumberOfColumns(); i++) {
- bordersHandler.drawVerticalBorder(i, startY, x1, drawContext.getCanvas(), heights);
+ for (int i = 0; i <= bordersHandler.getNumberOfColumns(); i++) {
+ bordersHandler.drawVerticalBorder(drawContext.getCanvas(),
+ new TableBorderDescriptor(i, startY, x1, heightsArray));
if (i < countedColumnWidth.length) {
x1 += countedColumnWidth[i];
}
}
- // Draw bounding borders. Vertical borders are the last to draw in order to collapse with header / footer
- if (isTopTablePart) {
- bordersHandler.drawHorizontalBorder(0, startX, startY, drawContext.getCanvas(), countedColumnWidth);
+ // draw horizontal borders
+
+ boolean shouldDrawTopBorder = isFooterRendererOfLargeTable || isTopTablePart;
+
+ // if top border is already drawn, we should decrease ordinate
+ if (!heights.isEmpty() && !shouldDrawTopBorder) {
+ y1 -= (float) heights.get(0);
+ }
+ for (int i = shouldDrawTopBorder ? 0 : 1; i < heights.size(); i++) {
+ bordersHandler.drawHorizontalBorder(drawContext.getCanvas(),
+ new TableBorderDescriptor(i, startX, y1, countedColumnWidth));
+ y1 -= (float) heights.get(i);
}
+
+ // draw bottom border
+
+ // Note for the second condition:
//!isLastRendererForModelElement is a check that this is a split render. This is the case with the splitting of
// one cell when part of the cell moves to the next page. Therefore, if such a splitting occurs, a bottom border
// should be drawn. However, this should not be done for empty renderers that are also created during splitting,
// but this splitting, if the table does not fit on the page and the next cell is added to the next page.
// In this case, this code should not be processed, since the border in the above code has already been drawn.
- if (isBottomTablePart && (isComplete || (!isLastRendererForModelElement && !isEmptyTableRenderer()))) {
- bordersHandler.drawHorizontalBorder(heights.size(), startX, y1, drawContext.getCanvas(), countedColumnWidth);
+ // TODO DEVSIX-5867 Check hasFooter, so that two footers are not drawn
+ if ((!isBottomTablePart && isComplete)
+ || (isBottomTablePart && (isComplete || (!isLastRendererForModelElement && !isEmptyTableRenderer())))) {
+ bordersHandler.drawHorizontalBorder(drawContext.getCanvas(),
+ new TableBorderDescriptor(heights.size(), startX, y1, countedColumnWidth));
}
- // draw left
- bordersHandler.drawVerticalBorder(0, startY, startX, drawContext.getCanvas(), heights);
- // draw right
- bordersHandler.drawVerticalBorder(bordersHandler.getNumberOfColumns(), startY, x1, drawContext.getCanvas(), heights);
if (isTagged) {
drawContext.getCanvas().closeTag();
diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/TableWidths.java b/layout/src/main/java/com/itextpdf/layout/renderer/TableWidths.java
index 2a870d0dc1..1a31fbf007 100644
--- a/layout/src/main/java/com/itextpdf/layout/renderer/TableWidths.java
+++ b/layout/src/main/java/com/itextpdf/layout/renderer/TableWidths.java
@@ -837,12 +837,22 @@ public String toString() {
static private final UnitValue ZeroWidth = UnitValue.createPointValue(0);
+ /**
+ * Gets width of the cell, adding paddings and extra spacing if necessary.
+ *
+ * @param cell renderer from which width will be taken.
+ * Note that this method will not change original width of the element.
+ * @param zeroIsValid defines if 0 width is valid
+ * @return increased width of the renderer
+ */
private UnitValue getCellWidth(CellRenderer cell, boolean zeroIsValid) {
- UnitValue widthValue = cell.getProperty(Property.WIDTH);
+ UnitValue widthValue = new UnitValue(cell.getProperty(Property.WIDTH, UnitValue.createPointValue(-1)));
//zero has special meaning in fixed layout, we shall not add padding to zero value
- if (widthValue == null || widthValue.getValue() < 0) {
+ if (widthValue.getValue() < -AbstractRenderer.EPS) {
return null;
- } else if (widthValue.getValue() == 0) {
+ }
+
+ if (widthValue.getValue() < AbstractRenderer.EPS) {
return zeroIsValid ? ZeroWidth : null;
} else if (widthValue.isPercentValue()) {
return widthValue;
diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/TextRenderer.java b/layout/src/main/java/com/itextpdf/layout/renderer/TextRenderer.java
index d1af43e540..aa6362ce73 100644
--- a/layout/src/main/java/com/itextpdf/layout/renderer/TextRenderer.java
+++ b/layout/src/main/java/com/itextpdf/layout/renderer/TextRenderer.java
@@ -961,12 +961,7 @@ public void draw(DrawContext drawContext) {
canvas.setHorizontalScaling((float) horizontalScaling * 100);
}
- GlyphLine.IGlyphLineFilter filter = new GlyphLine.IGlyphLineFilter() {
- @Override
- public boolean accept(Glyph glyph) {
- return !noPrint(glyph);
- }
- };
+ GlyphLine.IGlyphLineFilter filter = new CustomGlyphLineFilter();
boolean appearanceStreamLayout = Boolean.TRUE.equals(getPropertyAsBoolean(Property.APPEARANCE_STREAM_LAYOUT));
@@ -1875,4 +1870,11 @@ private static class ScriptRange {
this.rangeEnd = rangeEnd;
}
}
+
+ private static final class CustomGlyphLineFilter implements GlyphLine.IGlyphLineFilter {
+ @Override
+ public boolean accept(Glyph glyph) {
+ return !noPrint(glyph);
+ }
+ }
}
diff --git a/layout/src/test/java/com/itextpdf/layout/BorderTest.java b/layout/src/test/java/com/itextpdf/layout/BorderTest.java
index 469817f335..13efaa4b86 100644
--- a/layout/src/test/java/com/itextpdf/layout/BorderTest.java
+++ b/layout/src/test/java/com/itextpdf/layout/BorderTest.java
@@ -42,7 +42,6 @@ This file is part of the iText (R) project.
*/
package com.itextpdf.layout;
-import com.itextpdf.io.logs.IoLogMessageConstant;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.colors.DeviceCmyk;
@@ -67,6 +66,7 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.element.List;
import com.itextpdf.layout.element.ListItem;
import com.itextpdf.layout.element.Paragraph;
+import com.itextpdf.layout.logs.LayoutLogMessageConstant;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.annotations.LogMessage;
import com.itextpdf.test.annotations.LogMessages;
@@ -313,7 +313,7 @@ public void borderBoxTest() throws IOException, InterruptedException {
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 1)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 1)
})
public void rotatedBordersTest() throws IOException, InterruptedException {
fileName = "rotatedBordersTest.pdf";
diff --git a/layout/src/test/java/com/itextpdf/layout/DefaultLayoutTest.java b/layout/src/test/java/com/itextpdf/layout/DefaultLayoutTest.java
index aa52f83823..a67044ff78 100644
--- a/layout/src/test/java/com/itextpdf/layout/DefaultLayoutTest.java
+++ b/layout/src/test/java/com/itextpdf/layout/DefaultLayoutTest.java
@@ -61,6 +61,7 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Text;
+import com.itextpdf.layout.logs.LayoutLogMessageConstant;
import com.itextpdf.test.AssertUtil;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.LogLevelConstants;
@@ -188,7 +189,7 @@ public void textWithWhitespacesTest01() throws IOException, InterruptedException
@Test
@LogMessages(messages = {
- @LogMessage(count = 1, messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(count = 1, messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void addParagraphOnShortPage1() throws IOException, InterruptedException {
String outFileName = destinationFolder + "addParagraphOnShortPage1.pdf";
@@ -213,7 +214,7 @@ public void addParagraphOnShortPage1() throws IOException, InterruptedException
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void addParagraphOnShortPage2() throws IOException, InterruptedException {
String outFileName = destinationFolder + "addParagraphOnShortPage2.pdf";
@@ -234,7 +235,7 @@ public void addParagraphOnShortPage2() throws IOException, InterruptedException
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void addWordOnShortPageTest01() throws IOException, InterruptedException {
String outFileName = destinationFolder + "addWordOnShortPageTest01.pdf";
diff --git a/layout/src/test/java/com/itextpdf/layout/FloatTest.java b/layout/src/test/java/com/itextpdf/layout/FloatTest.java
index a200f2d888..d22439bf37 100644
--- a/layout/src/test/java/com/itextpdf/layout/FloatTest.java
+++ b/layout/src/test/java/com/itextpdf/layout/FloatTest.java
@@ -62,6 +62,7 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.element.Text;
+import com.itextpdf.layout.logs.LayoutLogMessageConstant;
import com.itextpdf.layout.properties.AreaBreakType;
import com.itextpdf.layout.properties.ClearPropertyValue;
import com.itextpdf.layout.properties.FloatPropertyValue;
@@ -823,7 +824,7 @@ public void clearancePageSplitFloatPartialInBlock03() throws IOException, Interr
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void clearancePageSplitFloatNothingInRoot01() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_clearancePageSplitFloatNothingInRoot01.pdf";
String outFile = destinationFolder + "clearancePageSplitFloatNothingInRoot01.pdf";
@@ -912,7 +913,7 @@ public void clearancePageSplitFloatNothingInRoot03() throws IOException, Interru
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void clearancePageSplitFloatNothingInBlock01() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_clearancePageSplitFloatNothingInBlock01.pdf";
String outFile = destinationFolder + "clearancePageSplitFloatNothingInBlock01.pdf";
@@ -1073,7 +1074,7 @@ public void clearanceNoContentPageSplitFloatPartialInBlock01() throws IOExceptio
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 2))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 2))
public void floatsOnPageSplit01() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsOnPageSplit01.pdf";
String outFile = destinationFolder + "floatsOnPageSplit01.pdf";
@@ -1140,7 +1141,7 @@ public void floatsOnPageSplit03() throws IOException, InterruptedException {
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void floatsOnPageSplit04() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsOnPageSplit04.pdf";
String outFile = destinationFolder + "floatsOnPageSplit04.pdf";
@@ -1246,7 +1247,7 @@ public void floatsOnPageSplit06_02() throws IOException, InterruptedException {
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void floatsOnPageSplit06_03() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsOnPageSplit06_03.pdf";
String outFile = destinationFolder + "floatsOnPageSplit06_03.pdf";
@@ -1332,7 +1333,7 @@ public void floatsOnPageSplit08_01() throws IOException, InterruptedException {
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void floatsOnPageSplit08_02() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsOnPageSplit08_02.pdf";
String outFile = destinationFolder + "floatsOnPageSplit08_02.pdf";
@@ -1365,7 +1366,7 @@ public void floatsOnPageSplit08_02() throws IOException, InterruptedException {
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void floatsOnPageSplit08_03() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsOnPageSplit08_03.pdf";
String outFile = destinationFolder + "floatsOnPageSplit08_03.pdf";
@@ -1422,7 +1423,7 @@ public void floatsOnPageSplit09() throws IOException, InterruptedException {
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void floatsOnPageSplit10() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsOnPageSplit10.pdf";
String outFile = destinationFolder + "floatsOnPageSplit10.pdf";
@@ -1446,7 +1447,7 @@ public void floatsOnPageSplit10() throws IOException, InterruptedException {
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void floatsOnPageSplit11() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsOnPageSplit11.pdf";
String outFile = destinationFolder + "floatsOnPageSplit11.pdf";
@@ -1695,7 +1696,7 @@ public void floatsOnPageSplit19() throws IOException, InterruptedException {
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void floatsKeepTogetherOnPageSplit01() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsKeepTogetherOnPageSplit01.pdf";
String outFile = destinationFolder + "floatsKeepTogetherOnPageSplit01.pdf";
@@ -1716,7 +1717,7 @@ public void floatsKeepTogetherOnPageSplit01() throws IOException, InterruptedExc
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void floatsKeepTogetherOnPageSplit02() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsKeepTogetherOnPageSplit02.pdf";
String outFile = destinationFolder + "floatsKeepTogetherOnPageSplit02.pdf";
@@ -1740,7 +1741,7 @@ public void floatsKeepTogetherOnPageSplit02() throws IOException, InterruptedExc
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 2))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 2))
public void floatsKeepTogetherOnPageSplit03() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsKeepTogetherOnPageSplit03.pdf";
String outFile = destinationFolder + "floatsKeepTogetherOnPageSplit03.pdf";
@@ -2088,7 +2089,7 @@ public void floatingTextInParagraphPartialSplit03() throws IOException, Interrup
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void floatsFirstOnPageNotFit01() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsFirstOnPageNotFit01.pdf";
String outFile = destinationFolder + "floatsFirstOnPageNotFit01.pdf";
@@ -2115,7 +2116,7 @@ public void floatsFirstOnPageNotFit01() throws IOException, InterruptedException
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void floatsFirstOnPageNotFit02() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsFirstOnPageNotFit02.pdf";
String outFile = destinationFolder + "floatsFirstOnPageNotFit02.pdf";
@@ -2142,7 +2143,7 @@ public void floatsFirstOnPageNotFit02() throws IOException, InterruptedException
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void floatsFirstOnPageNotFit03() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_floatsFirstOnPageNotFit03.pdf";
String outFile = destinationFolder + "floatsFirstOnPageNotFit03.pdf";
@@ -3118,7 +3119,7 @@ public void floatTableTest01() throws IOException, InterruptedException {
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void keepTogetherEnoughSpaceOnNewPageWithFloatTest() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_keepTogetherEnoughSpaceOnNewPageWithFloatTest.pdf";
String outFile = destinationFolder + "keepTogetherEnoughSpaceOnNewPageWithFloatTest.pdf";
@@ -3133,7 +3134,7 @@ public void keepTogetherEnoughSpaceOnNewPageWithFloatTest() throws IOException,
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void keepTogetherNotEnoughSpaceOnNewPageWithFloatEnoughOnEmptyTest()
throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_keepTogetherNotEnoughSpaceOnNewPageWithFloatEnoughOnEmptyTest.pdf";
@@ -3149,7 +3150,7 @@ public void keepTogetherNotEnoughSpaceOnNewPageWithFloatEnoughOnEmptyTest()
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 2))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 2))
public void keepTogetherNotEnoughSpaceOnNewEmptyPageTest() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_keepTogetherNotEnoughSpaceOnNewEmptyPageTest.pdf";
String outFile = destinationFolder + "keepTogetherNotEnoughSpaceOnNewEmptyPageTest.pdf";
@@ -3164,7 +3165,7 @@ public void keepTogetherNotEnoughSpaceOnNewEmptyPageTest() throws IOException, I
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 1))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 1))
public void keepTogetherNotEnoughSpaceOnNewEmptyPageShortFloatTest() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_keepTogetherNotEnoughSpaceOnNewEmptyPageShortFloatTest.pdf";
String outFile = destinationFolder + "keepTogetherNotEnoughSpaceOnNewEmptyPageShortFloatTest.pdf";
@@ -3180,7 +3181,7 @@ public void keepTogetherNotEnoughSpaceOnNewEmptyPageShortFloatTest() throws IOEx
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void innerKeepTogetherEnoughSpaceOnNewPageWithFloatTest() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_innerKeepTogetherEnoughSpaceOnNewPageWithFloatTest.pdf";
String outFile = destinationFolder + "innerKeepTogetherEnoughSpaceOnNewPageWithFloatTest.pdf";
@@ -3195,7 +3196,7 @@ public void innerKeepTogetherEnoughSpaceOnNewPageWithFloatTest() throws IOExcept
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA))
public void innerKeepTogetherNotEnoughSpaceOnNewPageWithFloatEnoughOnEmptyTest()
throws IOException, InterruptedException {
String cmpFileName =
@@ -3212,7 +3213,7 @@ public void innerKeepTogetherNotEnoughSpaceOnNewPageWithFloatEnoughOnEmptyTest()
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 2))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 2))
public void innerKeepTogetherNotEnoughSpaceOnNewEmptyPageTest() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_innerKeepTogetherNotEnoughSpaceOnNewEmptyPageTest.pdf";
String outFile = destinationFolder + "innerKeepTogetherNotEnoughSpaceOnNewEmptyPageTest.pdf";
diff --git a/layout/src/test/java/com/itextpdf/layout/FontSelectorTest.java b/layout/src/test/java/com/itextpdf/layout/FontSelectorTest.java
index 0881e27e5f..3a7b37af1e 100644
--- a/layout/src/test/java/com/itextpdf/layout/FontSelectorTest.java
+++ b/layout/src/test/java/com/itextpdf/layout/FontSelectorTest.java
@@ -1063,6 +1063,24 @@ public void openSansOutOfNotBoldFontWeightTest() {
new FontSelector(set.getFonts(), fontFamilies, fc).bestMatch().getDescriptor().getFontName());
}
+ @Test
+ //TODO DEVSIX-6077 FontSelector: iText checks monospaceness before looking at font-family
+ public void monospaceFontIsNotSelectedInPreferenceToTestFamilyTest() {
+ FontSet set = new FontSet();
+ set.addFont(StandardFonts.COURIER);
+ set.addFont(StandardFonts.HELVETICA);
+
+ List fontFamilies = new ArrayList<>();
+ fontFamilies.add("test");
+ fontFamilies.add("monospace");
+
+ FontCharacteristics fc = new FontCharacteristics();
+
+ //Expected font is Courier
+ Assert.assertEquals("Helvetica",
+ new FontSelector(set.getFonts(), fontFamilies, fc).bestMatch().getDescriptor().getFontName());
+ }
+
private void checkSelector(Collection fontInfoCollection, String fontFamily,
String expectedNormal, String expectedBold, String expectedItalic, String expectedBoldItalic) {
List fontFamilies = new ArrayList<>();
diff --git a/layout/src/test/java/com/itextpdf/layout/ImageTest.java b/layout/src/test/java/com/itextpdf/layout/ImageTest.java
index 2a6c6f6f78..591ba8f961 100644
--- a/layout/src/test/java/com/itextpdf/layout/ImageTest.java
+++ b/layout/src/test/java/com/itextpdf/layout/ImageTest.java
@@ -64,6 +64,7 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.element.Text;
+import com.itextpdf.layout.logs.LayoutLogMessageConstant;
import com.itextpdf.layout.properties.BorderRadius;
import com.itextpdf.layout.properties.HorizontalAlignment;
import com.itextpdf.layout.properties.Property;
@@ -254,7 +255,7 @@ public void imageTest06() throws IOException, InterruptedException {
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void imageTest07() throws IOException, InterruptedException {
String outFileName = destinationFolder + "imageTest07.pdf";
@@ -277,7 +278,7 @@ public void imageTest07() throws IOException, InterruptedException {
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void imageTest08() throws IOException, InterruptedException {
String outFileName = destinationFolder + "imageTest08.pdf";
@@ -302,7 +303,7 @@ public void imageTest08() throws IOException, InterruptedException {
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void imageTest09() throws IOException, InterruptedException {
String outFileName = destinationFolder + "imageTest09.pdf";
@@ -416,7 +417,7 @@ public void imageTest14_HorizontalAlignment_LEFT() throws IOException, Interrupt
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void imageTest15() throws IOException, InterruptedException {
String outFileName = destinationFolder + "imageTest15.pdf";
@@ -459,7 +460,7 @@ public void imageTest16() throws IOException, InterruptedException {
@Test()
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 50)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 50)
})
public void imageTest17() throws IOException, InterruptedException {
String outFileName = destinationFolder + "imageTest17.pdf";
@@ -593,7 +594,7 @@ public void imageTest21() throws IOException, InterruptedException {
}
@Test
- @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 1))
+ @LogMessages(messages = @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 1))
public void imageTest22() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_imageTest22.pdf";
String outFile = destinationFolder + "imageTest22.pdf";
@@ -947,7 +948,7 @@ public void imageBorderRadiusTest01() throws IOException, InterruptedException {
}
@Test
- @LogMessages(messages = {@LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 3)})
+ @LogMessages(messages = {@LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 3)})
public void createTiffImageTest() throws IOException, InterruptedException {
String outFileName = destinationFolder + "createTiffImageTest.pdf";
String cmpFileName = sourceFolder + "cmp_createTiffImageTest.pdf";
diff --git a/layout/src/test/java/com/itextpdf/layout/KeepTogetherTest.java b/layout/src/test/java/com/itextpdf/layout/KeepTogetherTest.java
index c0cb8ddf5c..e9ab0f69c7 100644
--- a/layout/src/test/java/com/itextpdf/layout/KeepTogetherTest.java
+++ b/layout/src/test/java/com/itextpdf/layout/KeepTogetherTest.java
@@ -63,6 +63,7 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.element.List;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
+import com.itextpdf.layout.logs.LayoutLogMessageConstant;
import com.itextpdf.layout.properties.ClearPropertyValue;
import com.itextpdf.layout.properties.FloatPropertyValue;
import com.itextpdf.layout.properties.ListNumberingType;
@@ -154,7 +155,7 @@ public void skipKeepTogetherInCaseOfAreaBreak() throws IOException, InterruptedE
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void keepTogetherParagraphTest02() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_keepTogetherParagraphTest02.pdf";
@@ -258,7 +259,7 @@ public void keepTogetherMinHeightTest() throws IOException, InterruptedException
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void keepTogetherDivTest02() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_keepTogetherDivTest02.pdf";
@@ -348,7 +349,7 @@ public void keepTogetherDivWithInnerClearDiv() throws IOException, InterruptedEx
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void keepTogetherDefaultTest01() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_keepTogetherDefaultTest01.pdf";
@@ -393,7 +394,7 @@ public void keepTogetherInlineDiv01() throws IOException, InterruptedException {
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void keepTogetherInlineDiv02() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_keepTogetherInlineDiv02.pdf";
@@ -532,7 +533,7 @@ public void narrowPageTest02A() throws IOException, InterruptedException {
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 1)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 1)
})
public void updateHeightTest01() throws IOException, InterruptedException {
String testName = "updateHeightTest01.pdf";
@@ -594,7 +595,7 @@ public void partialTest01() throws IOException, InterruptedException {
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void fixedHeightOverflowTest01() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_fixedHeightOverflowTest01.pdf";
@@ -622,7 +623,7 @@ public void fixedHeightOverflowTest01() throws IOException, InterruptedException
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void marginCollapseKeptTogetherDivGoesBackTest01() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_marginCollapseKeptTogetherDivGoesBackTest01.pdf";
@@ -654,7 +655,7 @@ public void marginCollapseKeptTogetherDivGoesBackTest01() throws IOException, In
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
// TODO DEVSIX-3995 The margin between the divs occupies 100 points instead of 300. After a fix the cmp should be updated
public void marginCollapseKeptTogetherDivGoesBackTest02() throws IOException, InterruptedException {
@@ -686,7 +687,7 @@ public void marginCollapseKeptTogetherDivGoesBackTest02() throws IOException, In
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void keepTogetherNotEmptyPageTest() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_keepTogetherNotEmptyPageTest.pdf";
@@ -717,7 +718,7 @@ public void keepTogetherNotEmptyPageTest() throws IOException, InterruptedExcept
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void keepTogetherOnFirstInnerElementNotEmptyPageTest() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_keepTogetherOnFirstInnerElementNotEmptyPageTest.pdf";
@@ -809,7 +810,7 @@ public void marginCollapseKeptTogetherGoesOnNextAreaTest02() throws IOException,
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
// TODO DEVSIX-4023 cmp should be updated
public void keepTogetherOnSecondInnerElementNotEmptyPageTest() throws IOException, InterruptedException {
@@ -844,7 +845,7 @@ public void keepTogetherOnSecondInnerElementNotEmptyPageTest() throws IOExceptio
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void smallFloatInsideKeptTogetherDivTest01() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_smallFloatInsideKeptTogetherDivTest01.pdf";
@@ -863,7 +864,7 @@ public void smallFloatInsideKeptTogetherDivTest01() throws IOException, Interrup
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void smallFloatInsideKeptTogetherDivTest02() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_smallFloatInsideKeptTogetherDivTest02.pdf";
@@ -885,7 +886,7 @@ public void smallFloatInsideKeptTogetherDivTest02() throws IOException, Interrup
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void smallFloatInsideKeptTogetherParagraphTest01() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_smallFloatInsideKeptTogetherParagraphTest01.pdf";
@@ -904,7 +905,7 @@ public void smallFloatInsideKeptTogetherParagraphTest01() throws IOException, In
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void smallFloatInsideKeptTogetherParagraphTest02() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_smallFloatInsideKeptTogetherParagraphTest02.pdf";
@@ -926,7 +927,7 @@ public void smallFloatInsideKeptTogetherParagraphTest02() throws IOException, In
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
// TODO DEVSIX-4023 cmp should be updated
public void keepTogetherOnInnerElementTestEmptyPageTest() throws IOException, InterruptedException {
@@ -951,7 +952,7 @@ public void keepTogetherOnInnerElementTestEmptyPageTest() throws IOException, In
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
// TODO DEVSIX-4023 cmp should be updated
public void keepTogetherOnInnerElementMargin01EmptyPageTest() throws IOException, InterruptedException {
@@ -979,7 +980,7 @@ public void keepTogetherOnInnerElementMargin01EmptyPageTest() throws IOException
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
// TODO DEVSIX-4023 cmp should be updated
public void keepTogetherOnInnerElementMargin02EmptyPageTest() throws IOException, InterruptedException {
@@ -1007,7 +1008,7 @@ public void keepTogetherOnInnerElementMargin02EmptyPageTest() throws IOException
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
// TODO DEVSIX-1092 There should be no path of the 15th row on the first page,
// since the layout box is only of 1 px height
@@ -1029,7 +1030,7 @@ public void smallFloatInsideKeptTogetherTableTest01() throws IOException, Interr
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void smallFloatInsideKeptTogetherTableTest02() throws IOException, InterruptedException {
String cmpFileName = sourceFolder + "cmp_smallFloatInsideKeptTogetherTableTest02.pdf";
@@ -1051,7 +1052,7 @@ public void smallFloatInsideKeptTogetherTableTest02() throws IOException, Interr
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void keepTogetherTreeWithParentNotFitOnDocumentTest() throws IOException, InterruptedException {
String filename = "keepTogetherTreeWithParentNotFitOnDocument.pdf";
@@ -1083,7 +1084,7 @@ public void keepTogetherTreeWithParentNotFitOnDocumentTest() throws IOException,
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void keepTogetherSubTreeWithParentNotFitOnDocumentTest() throws IOException, InterruptedException {
String filename = "keepTogetherSubTreeWithParentNotFitOnDocument.pdf";
@@ -1117,7 +1118,7 @@ public void keepTogetherSubTreeWithParentNotFitOnDocumentTest() throws IOExcepti
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void keepTogetherSubTreeWithChildKeepTogetherFalseAndParentNotFitOnDocumentTest()
throws IOException, InterruptedException {
@@ -1152,7 +1153,7 @@ public void keepTogetherSubTreeWithChildKeepTogetherFalseAndParentNotFitOnDocume
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void keepTogetherTreeWithParentNotFitOnPageCanvasTest() throws IOException, InterruptedException {
String filename = "keepTogetherTreeWithParentNotFitOnPageCanvas.pdf";
@@ -1226,7 +1227,7 @@ public void keepTogetherInDivWithKidsFloatTest() throws IOException, Interrupted
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
//TODO: update cmp file when DEVSIX-4681 will be fixed
public void floatingElementsInDivAndKeepTogetherElemTest() throws IOException, InterruptedException {
@@ -1266,7 +1267,7 @@ public void floatingElementsInDivAndKeepTogetherElemTest() throws IOException, I
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
//TODO: update cmp file when DEVSIX-4681 will be fixed
public void floatingEmptyElementsInDivAndKeepTogetherElemTest() throws IOException, InterruptedException {
diff --git a/layout/src/test/java/com/itextpdf/layout/LargeElementTest.java b/layout/src/test/java/com/itextpdf/layout/LargeElementTest.java
index 90cf101524..a1c40af29e 100644
--- a/layout/src/test/java/com/itextpdf/layout/LargeElementTest.java
+++ b/layout/src/test/java/com/itextpdf/layout/LargeElementTest.java
@@ -61,6 +61,7 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.layout.LayoutArea;
import com.itextpdf.layout.layout.LayoutContext;
import com.itextpdf.layout.layout.LayoutResult;
+import com.itextpdf.layout.logs.LayoutLogMessageConstant;
import com.itextpdf.layout.properties.BorderCollapsePropertyValue;
import com.itextpdf.layout.properties.UnitValue;
import com.itextpdf.layout.renderer.DocumentRenderer;
@@ -148,6 +149,95 @@ public void largeTableTest02() throws IOException, InterruptedException {
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, testName + "_diff"));
}
+ @Test
+ @LogMessages(messages = {
+ @LogMessage(messageTemplate = IoLogMessageConstant.LAST_ROW_IS_NOT_COMPLETE)
+ })
+ public void largeTableWithEmptyLastRowTest() throws IOException, InterruptedException {
+ String testName = "largeTableWithEmptyLastRowTest.pdf";
+ String outFileName = destinationFolder + testName;
+ String cmpFileName = sourceFolder + "cmp_" + testName;
+
+ PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
+ Document doc = new Document(pdfDoc);
+
+ Table table = new Table(UnitValue.createPercentArray(5), true);
+
+ doc.add(table);
+ for (int i = 0; i < 20; i++) {
+ for (int j = 0; j < 5; j++) {
+ table.addCell(new Cell().add(new Paragraph(MessageFormatUtil.format("Cell {0}, {1}", i + 1, j + 1))));
+ }
+ if (i % 10 == 0) {
+ table.flush();
+ }
+ }
+ table.startNewRow();
+ table.complete();
+ doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
+
+ doc.close();
+ Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, testName + "_diff"));
+ }
+
+ @Test
+ //TODO DEVSIX-6025 Unexpected NPE, when trying to flush after starting new row
+ @LogMessages(messages = {
+ @LogMessage(messageTemplate = IoLogMessageConstant.LAST_ROW_IS_NOT_COMPLETE, count = 2)
+ })
+ public void flushingLargeTableAfterStartingNewRowTest() throws IOException, InterruptedException {
+ String testName = "flushingLargeTableAfterStartingNewRowTest.pdf";
+ String outFileName = destinationFolder + testName;
+ String cmpFileName = sourceFolder + "cmp_" + testName;
+
+ PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
+ Document doc = new Document(pdfDoc);
+
+ Table table = new Table(UnitValue.createPercentArray(5), true);
+
+ doc.add(table);
+
+ table.addCell(new Cell().add(new Paragraph("Hello")));
+ table.addCell(new Cell().add(new Paragraph("World")));
+ table.startNewRow();
+ Assert.assertThrows(NullPointerException.class, () -> table.flush());
+ table.complete();
+ doc.close();
+ Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, testName + "_diff"));
+ }
+
+ @Test
+ @LogMessages(messages = {
+ @LogMessage(messageTemplate = IoLogMessageConstant.LAST_ROW_IS_NOT_COMPLETE)
+ })
+ public void largeTableWithCollapsedFooterTest() throws IOException, InterruptedException {
+ String testName = "largeTableWithCollapsedFooterTest.pdf";
+ String outFileName = destinationFolder + testName;
+ String cmpFileName = sourceFolder + "cmp_" + testName;
+
+ PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
+ Document doc = new Document(pdfDoc);
+
+ Table table = new Table(UnitValue.createPercentArray(5), true);
+
+ doc.add(table);
+ for (int i = 0; i < 20; i++) {
+ for (int j = 0; j < 5; j++) {
+ table.addCell(new Cell().add(new Paragraph(MessageFormatUtil.format("Cell {0}, {1}", i + 1, j + 1))));
+ }
+ if (i % 10 == 0) {
+ table.flush();
+ }
+ }
+ table.startNewRow();
+ Cell cell = new Cell(1, 5).add(new Paragraph("Collapsed footer"));
+ table.addFooterCell(cell);
+ table.complete();
+
+ doc.close();
+ Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, testName + "_diff"));
+ }
+
@Test
public void largeTableWithHeaderFooterTest01A() throws IOException, InterruptedException {
String testName = "largeTableWithHeaderFooterTest01A.pdf";
@@ -252,6 +342,7 @@ public void largeTableWithHeaderFooterTest01B() throws IOException, InterruptedE
}
@Test
+ // TODO DEVSIX-5868 Look at page 2: large table's vertical borders are shorter in length than expected
public void largeTableWithHeaderFooterTest01C() throws IOException, InterruptedException {
String testName = "largeTableWithHeaderFooterTest01C.pdf";
String outFileName = destinationFolder + testName;
@@ -620,7 +711,7 @@ public void tableWithLayoutResultNothingTest01() throws IOException, Interrupted
@Test
- @LogMessages(messages = {@LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 1)})
+ @LogMessages(messages = {@LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 1)})
public void largeTableWithLayoutResultNothingTest02() throws IOException, InterruptedException {
String testName = "largeTableWithLayoutResultNothingTest02.pdf";
String outFileName = destinationFolder + testName;
@@ -777,6 +868,7 @@ private void largeTableSplitTest(String outFileName, float pageHeight, float row
}
@Test
+ // TODO DEVSIX-5865 Table last horizontal border is drawn twice: at final Table#flush and then at Table#complete
public void largeTableWithTableBorderSplitTest() throws IOException, InterruptedException {
String testName = "largeTableWithTableBorderSplitTest.pdf";
String outFileName = destinationFolder + testName;
@@ -808,7 +900,68 @@ public void largeTableWithTableBorderSplitTest() throws IOException, Interrupted
}
@Test
- @LogMessages(messages = {@LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)})
+ // TODO DEVSIX-5865 Table last horizontal border is drawn twice: at final Table#flush and then at Table#complete
+ public void largeTableWithTableBorderSplitTest02() throws IOException, InterruptedException {
+ String testName = "largeTableWithTableBorderSplitTest02.pdf";
+ String outFileName = destinationFolder + testName;
+ String cmpFileName = sourceFolder + "cmp_" + testName;
+
+ PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
+ Document doc = new Document(pdfDoc, new PageSize(595, 100));
+
+ Table table = new Table(2, true);
+ doc.add(table);
+
+ table.setBorder(new SolidBorder(ColorConstants.BLUE, 2));
+
+ table.addCell(new Cell().setBackgroundColor(ColorConstants.RED).setHeight(50).setMargin(0).setPadding(0));
+ table.addCell(new Cell().setBackgroundColor(ColorConstants.RED).setHeight(50).setMargin(0).setPadding(0));
+
+ table.flush();
+ table.complete();
+
+ doc.close();
+ Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder));
+ }
+
+ @Test
+ // TODO DEVSIX-5866 at #complete left border is initialized as null
+ public void largeTableWithCellBordersSplitTest1() throws IOException, InterruptedException {
+ String testName = "largeTableWithCellBordersSplitTest1.pdf";
+ String outFileName = destinationFolder + testName;
+ String cmpFileName = sourceFolder + "cmp_" + testName;
+
+ PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
+ Document doc = new Document(pdfDoc);
+
+ float[] colWidths = new float[]{30, 30, 30};
+
+ Table table = new Table(colWidths, true).setWidth(290);
+ doc.add(table);
+
+ table.addCell(new Cell().add(new Paragraph("Cell" + 0))
+ .setPadding(0).setMargin(0)
+ .setBorder(new SolidBorder(ColorConstants.MAGENTA, 50))
+ .setBorderBottom(new SolidBorder(ColorConstants.BLUE, 50)));
+ table.addCell(new Cell().add(new Paragraph("Cell" + 1))
+ .setPadding(0).setMargin(0)
+ .setBorder(new SolidBorder(ColorConstants.MAGENTA, 50))
+ .setBorderBottom(new SolidBorder(ColorConstants.RED, 50)));
+ table.addCell(new Cell().add(new Paragraph("Cell" + 3))
+ .setPadding(0).setMargin(0)
+ .setBorder(new SolidBorder(ColorConstants.MAGENTA, 50))
+ .setBorderBottom(new SolidBorder(ColorConstants.BLUE, 50)));
+
+ table.flush();
+ table.complete();
+
+ doc.close();
+ Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder));
+ }
+
+ @Test
+ @LogMessages(messages = {@LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)})
+ // TODO DEVSIX-5866 at #complete left border is initialized as null
public void largeTableWithCellBordersSplitTest() throws IOException, InterruptedException {
String testName = "largeTableWithCellBordersSplitTest.pdf";
String outFileName = destinationFolder + testName;
@@ -822,27 +975,79 @@ public void largeTableWithCellBordersSplitTest() throws IOException, Interrupted
Table table = new Table(UnitValue.createPointArray(colWidths), true);
doc.add(table);
- for (int i = 0; i < 1; i++) {
- table.addCell(new Cell().add(new Paragraph("Cell" + (i * 4 + 0)))
- .setBorderBottom(new SolidBorder(ColorConstants.BLUE, 2)));
- table.addCell(new Cell().add(new Paragraph("Cell" + (i * 4 + 1)))
- .setBorderBottom(new SolidBorder(ColorConstants.RED, 5)));
- table.addCell(new Cell().add(new Paragraph("Cell" + (i * 4 + 2)))
- .setBorderBottom(new SolidBorder(ColorConstants.GREEN, 7)));
- table.addCell(new Cell().add(new Paragraph("Cell" + (i * 4 + 3)))
- .setBorderBottom(new SolidBorder(ColorConstants.BLUE, 10)));
-
- table.flush();
- }
+ table.addCell(new Cell().add(new Paragraph("Cell" + 0))
+ .setBorderBottom(new SolidBorder(ColorConstants.BLUE, 2)));
+ table.addCell(new Cell().add(new Paragraph("Cell" + 1))
+ .setBorderBottom(new SolidBorder(ColorConstants.RED, 5)));
+ table.addCell(new Cell().add(new Paragraph("Cell" + 2))
+ .setBorderBottom(new SolidBorder(ColorConstants.GREEN, 7)));
+ table.addCell(new Cell().add(new Paragraph("Cell" + 3))
+ .setBorderBottom(new SolidBorder(ColorConstants.BLUE, 10)));
+ table.flush();
table.complete();
doc.close();
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder));
}
+ @Test
+ @LogMessages(messages = {@LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)})
+ // TODO DEVSIX-5866 at #complete left border is initialized as null
+ public void largeTableWithCellBordersSplitTest02() throws IOException, InterruptedException {
+ String testName = "largeTableWithCellBordersSplitTest02.pdf";
+ String outFileName = destinationFolder + testName;
+ String cmpFileName = sourceFolder + "cmp_" + testName;
+
+ PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
+ Document doc = new Document(pdfDoc, new PageSize(595, 100));
+
+ float[] colWidths = new float[]{200, 40};
+
+ Table table = new Table(UnitValue.createPointArray(colWidths), true);
+ doc.add(table);
+
+ table.addCell(new Cell().add(new Paragraph("Cell" + 0)).setBackgroundColor(ColorConstants.YELLOW)
+ .setBorderBottom(new SolidBorder(ColorConstants.BLUE, 2)));
+ table.addCell(new Cell().add(new Paragraph("Cell" + 3)).setBackgroundColor(ColorConstants.YELLOW)
+ .setBorderBottom(new SolidBorder(ColorConstants.BLUE, 10)));
+
+ table.flush();
+
+ doc.close();
+ Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder));
+ }
@Test
+ // TODO DEVSIX-5866 at #complete left border is initialized as null
+ public void simpleLargeTableDifferentCellBottomBorderTest() throws IOException, InterruptedException {
+ String testName = "simpleLargeTableDifferentCellBottomBorderTest.pdf";
+ String outFileName = destinationFolder + testName;
+ String cmpFileName = sourceFolder + "cmp_" + testName;
+
+ PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
+ Document doc = new Document(pdfDoc);
+
+ Table table = new Table(2, true);
+ doc.add(table);
+
+ table.addCell(new Cell().add(new Paragraph("Cell" + 0)).setHeight(30).setMargin(0).setPadding(0)
+ .setBackgroundColor(ColorConstants.RED).setBorder(new SolidBorder(ColorConstants.BLUE, 10)));
+ table.addCell(new Cell().add(new Paragraph("Cell" + 1)).setHeight(30).setMargin(0).setPadding(0)
+ .setBackgroundColor(ColorConstants.RED).setBorder(new SolidBorder(10))
+ .setBorderBottom(new SolidBorder(ColorConstants.BLUE, 100)));
+
+ table.flush();
+ table.complete();
+
+ doc.close();
+ Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder));
+ }
+
+ @Test
+ // TODO DEVSIX-5867 footer's top / table body's bottom border gets drawn twice at different coordinates
+ // (Look at yellow border at page 2: it might not be tat obvious, however, there are two yelow borders
+ // there which overlap each other a bit)
public void largeTableSplitFooter2Test() throws IOException, InterruptedException {
String testName = "largeTableSplitFooter2Test.pdf";
String outFileName = destinationFolder + testName;
@@ -867,6 +1072,61 @@ public void largeTableSplitFooter2Test() throws IOException, InterruptedExceptio
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder));
}
+ @Test
+ // TODO DEVSIX-5867 footer's top / table body's bottom border gets drawn twice at different coordinates
+ // (Look at yellow border: it might not be tat obvious, however, there are two yelow borders
+ // there which overlap each other a bit)
+ public void largeTableSplitFooter2ATest() throws IOException, InterruptedException {
+ String testName = "largeTableSplitFooter2ATest.pdf";
+ String outFileName = destinationFolder + testName;
+ String cmpFileName = sourceFolder + "cmp_" + testName;
+
+ PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
+ Document doc = new Document(pdfDoc);
+
+ Table table = new Table(1, true);
+ doc.add(table);
+ table.addFooterCell(new Cell().add(new Paragraph("Footer"))
+ .setBorderTop(new SolidBorder(ColorConstants.YELLOW, 15))
+ );
+
+ table.addCell(new Cell().add(new Paragraph("Cell1")).setHeight(50)
+ .setBorderBottom(new SolidBorder(ColorConstants.BLUE, 20)));
+ table.flush();
+ table.addCell(new Cell().add(new Paragraph("Cell2")).setHeight(50));
+
+ table.complete();
+
+ doc.close();
+ Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder));
+ }
+
+ @Test
+ // TODO DEVSIX-5869 large table's width should not change between flushes
+ public void largeTableSplitFooter2BTest() throws IOException, InterruptedException {
+ String testName = "largeTableSplitFooter2BTest.pdf";
+ String outFileName = destinationFolder + testName;
+ String cmpFileName = sourceFolder + "cmp_" + testName;
+
+ PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
+ Document doc = new Document(pdfDoc, new PageSize(595, 900));
+
+ addSpecificTableConsideringFlushes(doc, false, false);
+ doc.add(new AreaBreak());
+
+ addSpecificTableConsideringFlushes(doc, true, false);
+ doc.add(new AreaBreak());
+
+ addSpecificTableConsideringFlushes(doc, false, true);
+ doc.add(new AreaBreak());
+
+ addSpecificTableConsideringFlushes(doc, true, true);
+ doc.add(new AreaBreak());
+
+ doc.close();
+ Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder));
+ }
+
@Test
public void largeTableCollapsingSplitTest() throws IOException, InterruptedException {
String testName = "largeTableCollapsingSplitTest.pdf";
@@ -1016,7 +1276,6 @@ public void tableOnDifferentPages02() throws IOException, InterruptedException {
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, testName + "_diff"));
}
-
@Test
@LogMessages(messages = {@LogMessage(messageTemplate = IoLogMessageConstant.LAST_ROW_IS_NOT_COMPLETE, count = 1)})
public void reuseLargeTableTest01() throws IOException, InterruptedException {
@@ -1237,8 +1496,8 @@ public void largeEmptyTableTest02Separated() throws IOException, InterruptedExce
}
@Test
- // TODO DEVSIX-3953
- @LogMessages(messages = {@LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 2)})
+ // TODO DEVSIX-3953 Footer is not placed on the first page in case of large table, but fits the page for a usual table
+ @LogMessages(messages = {@LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)})
public void largeTableFooterNotFitTest() throws IOException, InterruptedException {
String testName = "largeTableFooterNotFitTest.pdf";
String outFileName = destinationFolder + testName;
@@ -1247,25 +1506,51 @@ public void largeTableFooterNotFitTest() throws IOException, InterruptedExceptio
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
Document doc = new Document(pdfDoc, new PageSize(595, 100));
- float[] colWidths = new float[]{200, -1, 40, 40};
-
- Table table = new Table(UnitValue.createPointArray(colWidths), true);
- Cell footerCell = new Cell(1, 4).add(new Paragraph("Table footer: continue on next page"));
+ Table table = new Table(1, true);
+ Cell footerCell = new Cell().add(new Paragraph("Table footer: continue on next page"));
table.addFooterCell(footerCell);
doc.add(table);
- for (int i = 0; i < 2; i++) {
- table.addCell(new Cell().add(new Paragraph("Cell" + (i * 4 + 0))));
- table.addCell(new Cell().add(new Paragraph("Cell" + (i * 4 + 1))));
- table.addCell(new Cell().add(new Paragraph("Cell" + (i * 4 + 2))));
- table.addCell(new Cell().add(new Paragraph("Cell" + (i * 4 + 3))));
+ table.addCell(new Cell().add(new Paragraph("Cell")).setBackgroundColor(ColorConstants.RED));
- table.flush();
- }
+ // If one comments flush, then the table fits the page
+ table.flush();
table.complete();
doc.close();
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, testName + "_diff"));
}
+
+ private static void addSpecificTableConsideringFlushes(Document doc, boolean flushFirst, boolean flushSecond) {
+ Table table = new Table(UnitValue.createPercentArray(1), true);
+ doc.add(table);
+
+ table.addFooterCell(new Cell().add(new Paragraph("Footer"))
+ .setBorderTop(new SolidBorder(ColorConstants.YELLOW, 15))
+ .setHeight(100).setMargin(0).setPadding(0)
+ );
+
+ table.addCell(new Cell().add(new Paragraph("Cell1"))
+ .setHeight(100).setMargin(0).setPadding(0)
+ .setBackgroundColor(ColorConstants.RED)
+ );
+
+ if (flushFirst) {
+ table.flush();
+ }
+
+ table.addCell(new Cell().add(new Paragraph("Cell2"))
+ .setHeight(100).setMargin(0).setPadding(0)
+ .setBackgroundColor(ColorConstants.RED)
+ .setBorderLeft(new SolidBorder(ColorConstants.GREEN, 50))
+ .setBorderRight(new SolidBorder(ColorConstants.GREEN, 50))
+
+ .setBorderTop(new SolidBorder(ColorConstants.MAGENTA, 10)));
+
+ if (flushSecond) {
+ table.flush();
+ }
+ table.complete();
+ }
}
diff --git a/layout/src/test/java/com/itextpdf/layout/LayoutTaggingPdf2Test.java b/layout/src/test/java/com/itextpdf/layout/LayoutTaggingPdf2Test.java
index da69f9a97a..550683191d 100644
--- a/layout/src/test/java/com/itextpdf/layout/LayoutTaggingPdf2Test.java
+++ b/layout/src/test/java/com/itextpdf/layout/LayoutTaggingPdf2Test.java
@@ -58,6 +58,7 @@ This file is part of the iText (R) project.
import com.itextpdf.kernel.utils.CompareTool;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.Cell;
+import com.itextpdf.layout.element.Div;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.List;
import com.itextpdf.layout.element.ListItem;
@@ -651,6 +652,36 @@ public void copyTest01() throws IOException, InterruptedException, ParserConfigu
compareResult("copyTest01");
}
+ @Test
+ public void docWithSectInPdf2() throws IOException, ParserConfigurationException, SAXException,
+ InterruptedException {
+ PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destinationFolder + "docWithSectInPdf2.pdf",
+ new WriterProperties().setPdfVersion(PdfVersion.PDF_2_0)));
+ pdfDocument.setTagged();
+
+ Document document = new Document(pdfDocument);
+
+ Div section = new Div();
+ section.getAccessibilityProperties().setRole(StandardRoles.SECT);
+ Paragraph h1 = new Paragraph("This is a header");
+ h1.getAccessibilityProperties().setRole("H1");
+ section.add(h1);
+
+ section.add(new Paragraph("This is a paragraph."));
+ Paragraph para = new Paragraph("This is another paragraph, ");
+
+ Text emphasised = new Text("with semantic emphasis!");
+ emphasised.setUnderline();
+ emphasised.getAccessibilityProperties().setRole(StandardRoles.EM);
+ para.add(emphasised);
+ section.add(para);
+
+ document.add(section);
+ document.close();
+
+ compareResult("docWithSectInPdf2");
+ }
+
@Test
public void copyTest02() throws IOException, InterruptedException, ParserConfigurationException, SAXException {
PdfDocument srcPdf = new PdfDocument(new PdfReader(sourceFolder + "docSeveralNs.pdf"));
diff --git a/layout/src/test/java/com/itextpdf/layout/LayoutTaggingTest.java b/layout/src/test/java/com/itextpdf/layout/LayoutTaggingTest.java
index df21800cf6..54666bacb2 100644
--- a/layout/src/test/java/com/itextpdf/layout/LayoutTaggingTest.java
+++ b/layout/src/test/java/com/itextpdf/layout/LayoutTaggingTest.java
@@ -83,6 +83,7 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.element.Text;
import com.itextpdf.layout.font.FontProvider;
+import com.itextpdf.layout.logs.LayoutLogMessageConstant;
import com.itextpdf.layout.properties.FloatPropertyValue;
import com.itextpdf.layout.properties.ListNumberingType;
import com.itextpdf.layout.properties.Property;
@@ -138,7 +139,7 @@ public void textInParagraphTest01() throws IOException, InterruptedException, Pa
@Test
@LogMessages(messages = {
- @LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
+ @LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
})
public void imageTest01() throws IOException, InterruptedException, ParserConfigurationException, SAXException {
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destinationFolder + "imageTest01.pdf"));
@@ -1034,7 +1035,7 @@ public void checkParentTreeIfFormXObjectTaggedTest() throws IOException, Interru
canvas.add(new Paragraph(txt));
PdfCanvas canvas1 = new PdfCanvas(page1);
- canvas1.addXObject(template, 10, 10);
+ canvas1.addXObjectAt(template, 10, 10);
pdfDoc.close();
diff --git a/layout/src/test/java/com/itextpdf/layout/LinkTest.java b/layout/src/test/java/com/itextpdf/layout/LinkTest.java
index 138be0ae8b..0959ffb1bb 100644
--- a/layout/src/test/java/com/itextpdf/layout/LinkTest.java
+++ b/layout/src/test/java/com/itextpdf/layout/LinkTest.java
@@ -62,6 +62,7 @@ This file is part of the iText (R) project.
import com.itextpdf.layout.element.Link;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
+import com.itextpdf.layout.logs.LayoutLogMessageConstant;
import com.itextpdf.layout.properties.Property;
import com.itextpdf.layout.properties.UnitValue;
import com.itextpdf.test.ExtendedITextTest;
@@ -224,7 +225,7 @@ public void rotatedLinkAtFixedPosition() throws IOException, InterruptedExceptio
}
@Test
- @LogMessages(messages = {@LogMessage(messageTemplate = IoLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)})
+ @LogMessages(messages = {@LogMessage(messageTemplate = LayoutLogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)})
public void rotatedLinkInnerRotation() throws IOException, InterruptedException {
String outFileName = destinationFolder + "rotatedLinkInnerRotation.pdf";
String cmpFileName = sourceFolder + "cmp_rotatedLinkInnerRotation.pdf";
diff --git a/layout/src/test/java/com/itextpdf/layout/ListItemPositionAlignmentTest.java b/layout/src/test/java/com/itextpdf/layout/ListItemPositionAlignmentTest.java
new file mode 100644
index 0000000000..6aa94cb3dd
--- /dev/null
+++ b/layout/src/test/java/com/itextpdf/layout/ListItemPositionAlignmentTest.java
@@ -0,0 +1,217 @@
+/*
+ This file is part of the iText (R) project.
+ Copyright (c) 1998-2021 iText Group NV
+ Authors: iText Software.
+
+ This program is offered under a commercial and under the AGPL license.
+ For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
+
+ AGPL licensing:
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+package com.itextpdf.layout;
+
+import com.itextpdf.commons.utils.MessageFormatUtil;
+import com.itextpdf.io.logs.IoLogMessageConstant;
+import com.itextpdf.io.util.UrlUtil;
+import com.itextpdf.kernel.colors.ColorConstants;
+import com.itextpdf.kernel.pdf.PdfDocument;
+import com.itextpdf.kernel.pdf.PdfWriter;
+import com.itextpdf.kernel.utils.CompareTool;
+import com.itextpdf.layout.element.List;
+import com.itextpdf.layout.element.ListItem;
+import com.itextpdf.layout.element.Paragraph;
+import com.itextpdf.layout.properties.BaseDirection;
+import com.itextpdf.layout.properties.ListSymbolAlignment;
+import com.itextpdf.layout.properties.ListSymbolPosition;
+import com.itextpdf.layout.properties.Property;
+import com.itextpdf.test.ExtendedITextTest;
+import com.itextpdf.test.annotations.LogMessage;
+import com.itextpdf.test.annotations.LogMessages;
+import com.itextpdf.test.annotations.type.IntegrationTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+
+@RunWith(Parameterized.class)
+@Category(IntegrationTest.class)
+public class ListItemPositionAlignmentTest extends ExtendedITextTest {
+
+ public static final String SOURCE_FOLDER = "./src/test/resources/com/itextpdf/layout/ListItemPositionAlignmentTest/";
+ public static final String DESTINATION_FOLDER = "./target/test/com/itextpdf/layout/ListItemPositionAlignmentTest/";
+
+ private static final String PARAMETERS_NAME_PATTERN = "{index}: list-base-direction: {0}; list-item-base-direction: {1};" +
+ " list-symbol-alignment: {2}; list-symbol-position: {3};";
+ private static final String RESULTANT_FILE_NAME_PATTERN
+ = "list-dir-{0}_item-dir-{1}_symbol-align-{2}_symbol-position-{3}";
+
+ private static final String HTML_PATTERN =
+ "