diff --git a/itext.tests/itext.kernel.tests/itext/kernel/pdf/PdfDocumentTest.cs b/itext.tests/itext.kernel.tests/itext/kernel/pdf/PdfDocumentTest.cs
index 272f412bc7..32c9b14ac2 100644
--- a/itext.tests/itext.kernel.tests/itext/kernel/pdf/PdfDocumentTest.cs
+++ b/itext.tests/itext.kernel.tests/itext/kernel/pdf/PdfDocumentTest.cs
@@ -529,6 +529,62 @@ public virtual void GetDefaultConformanceLevelTest() {
NUnit.Framework.Assert.IsNull(document.GetConformanceLevel());
}
+ //TODO DEVSIX-8490 remove this test when implemented
+ [NUnit.Framework.Test]
+ [LogMessage(KernelLogMessageConstant.DUPLICATE_ENTRIES_IN_ORDER_ARRAY_REMOVED)]
+ public virtual void RemoveDuplicatesInOrderArrayTest() {
+ String inputPdf = "removeDuplicatesInOrderArray.pdf";
+ String outputPdf = "removedDuplicateInOrderArray.pdf";
+ PdfDocument doc = new PdfDocument(new PdfReader(SOURCE_FOLDER + inputPdf), CompareTool.CreateTestPdfWriter
+ (DESTINATION_FOLDER + outputPdf));
+ //Need to update OCProperties
+ doc.GetCatalog().GetOCProperties(false);
+ doc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(DESTINATION_FOLDER + outputPdf, SOURCE_FOLDER
+ + "cmp_" + outputPdf, DESTINATION_FOLDER));
+ }
+
+ //TODO DEVSIX-8490 remove this test when implemented
+ [NUnit.Framework.Test]
+ [LogMessage(KernelLogMessageConstant.DUPLICATE_ENTRIES_IN_ORDER_ARRAY_REMOVED)]
+ public virtual void RemoveNestedDuplicatesInOrderArrayTest() {
+ String inputPdf = "removeNestedDuplicatesInOrderArray.pdf";
+ String outputPdf = "removedNestedDuplicatesInOrderArray.pdf";
+ PdfDocument doc = new PdfDocument(new PdfReader(SOURCE_FOLDER + inputPdf), new PdfWriter(DESTINATION_FOLDER
+ + outputPdf));
+ //Need to update OCProperties
+ doc.GetCatalog().GetOCProperties(false);
+ doc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(DESTINATION_FOLDER + outputPdf, SOURCE_FOLDER
+ + "cmp_" + outputPdf, DESTINATION_FOLDER));
+ }
+
+ //TODO DEVSIX-8490 remove this test when implemented
+ [NUnit.Framework.Test]
+ public virtual void RemoveDuplicatesHasChildInOrderArrayTest() {
+ String inputPdf = "removeDuplicatesHasChildInOrderArray.pdf";
+ String outputPdf = "removedDuplicatesHasChildInOrderArray.pdf";
+ PdfDocument doc = new PdfDocument(new PdfReader(SOURCE_FOLDER + inputPdf), CompareTool.CreateTestPdfWriter
+ (DESTINATION_FOLDER + outputPdf));
+ PdfCatalog catalog = doc.GetCatalog();
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfException), () => catalog.GetOCProperties(false));
+ NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(KernelExceptionMessageConstant.UNABLE_TO_REMOVE_DUPLICATE_LAYER
+ , "4 0 R"), e.Message);
+ }
+
+ //TODO DEVSIX-8490 remove this test when implemented
+ [NUnit.Framework.Test]
+ public virtual void RemoveNestedDuplicatesHasChildInOrderArrayTest() {
+ String inputPdf = "removeNestedDuplicatesHasChildInOrderArray.pdf";
+ String outputPdf = "removedNestedDuplicatesHasChildInOrderArray.pdf";
+ PdfDocument doc = new PdfDocument(new PdfReader(SOURCE_FOLDER + inputPdf), CompareTool.CreateTestPdfWriter
+ (DESTINATION_FOLDER + outputPdf));
+ PdfCatalog catalog = doc.GetCatalog();
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfException), () => catalog.GetOCProperties(false));
+ NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(KernelExceptionMessageConstant.UNABLE_TO_REMOVE_DUPLICATE_LAYER
+ , "27 0 R"), e.Message);
+ }
+
private class IgnoreTagStructurePdfDocument : PdfDocument {
//\cond DO_NOT_DOCUMENT
internal IgnoreTagStructurePdfDocument(PdfReader reader)
diff --git a/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfLayerTest.cs b/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfLayerTest.cs
index b529d1b5dd..5a1073eb2d 100644
--- a/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfLayerTest.cs
+++ b/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfLayerTest.cs
@@ -24,6 +24,8 @@ You should have received a copy of the GNU Affero General Public License
using System.Collections.Generic;
using iText.Commons.Utils;
using iText.IO.Font.Constants;
+using iText.IO.Source;
+using iText.Kernel.Exceptions;
using iText.Kernel.Font;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Canvas;
@@ -354,5 +356,22 @@ public virtual void TestInStamperMode2() {
NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(destinationFolder + "output_layered.pdf",
sourceFolder + "cmp_output_layered.pdf", destinationFolder, "diff"));
}
+
+ //TODO DEVSIX-8490 remove this test when implemented
+ [NUnit.Framework.Test]
+ public virtual void AddSecondParentlayerTest() {
+ using (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+ using (PdfDocument doc = new PdfDocument(new PdfWriter(outputStream))) {
+ PdfLayer childLayer = new PdfLayer("childLayer", doc);
+ PdfLayer parentLayer1 = new PdfLayer("firstParentLayer", doc);
+ PdfLayer parentLayer2 = new PdfLayer("secondParentLayer", doc);
+ parentLayer1.AddChild(childLayer);
+ PdfIndirectReference @ref = childLayer.GetIndirectReference();
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfException), () => parentLayer2.AddChild(childLayer));
+ NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(KernelExceptionMessageConstant.UNABLE_TO_ADD_SECOND_PARENT_LAYER
+ , @ref.ToString()), e.Message);
+ }
+ }
+ }
}
}
diff --git a/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfOCPropertiesUnitTest.cs b/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfOCPropertiesUnitTest.cs
new file mode 100644
index 0000000000..f2a257799f
--- /dev/null
+++ b/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfOCPropertiesUnitTest.cs
@@ -0,0 +1,110 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2024 Apryse Group NV
+Authors: Apryse 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 .
+*/
+using System;
+using System.IO;
+using iText.Commons.Utils;
+using iText.IO.Source;
+using iText.Kernel.Exceptions;
+using iText.Kernel.Pdf;
+
+namespace iText.Kernel.Pdf.Layer {
+ [NUnit.Framework.Category("UnitTest")]
+ public class PdfOCPropertiesUnitTest {
+ //TODO DEVSIX-8490 remove this test when implemented
+ [NUnit.Framework.Test]
+ public virtual void RemoveOrderDuplicatesTest() {
+ byte[] docBytes;
+ using (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+ using (PdfDocument document = new PdfDocument(new PdfWriter(outputStream))) {
+ PdfDictionary ocgDic = new PdfDictionary();
+ ocgDic.MakeIndirect(document);
+ PdfArray orderArray = new PdfArray();
+ for (int i = 0; i < 3; i++) {
+ orderArray.Add(ocgDic);
+ }
+ PdfDictionary ocgDic2 = new PdfDictionary();
+ ocgDic.MakeIndirect(document);
+ for (int i = 0; i < 3; i++) {
+ PdfArray layerArray = new PdfArray();
+ layerArray.Add(new PdfString("layerName" + i));
+ layerArray.Add(ocgDic2);
+ orderArray.Add(layerArray);
+ }
+ PdfDictionary DDictionary = new PdfDictionary();
+ DDictionary.Put(PdfName.Order, orderArray);
+ PdfArray OCGsArray = new PdfArray();
+ OCGsArray.Add(ocgDic);
+ OCGsArray.Add(ocgDic2);
+ PdfDictionary OCPropertiesDic = new PdfDictionary();
+ OCPropertiesDic.Put(PdfName.D, DDictionary);
+ OCPropertiesDic.Put(PdfName.OCGs, OCGsArray);
+ document.GetCatalog().GetPdfObject().Put(PdfName.OCProperties, OCPropertiesDic);
+ document.GetCatalog().GetOCProperties(false);
+ }
+ docBytes = outputStream.ToArray();
+ }
+ using (PdfDocument docReopen = new PdfDocument(new PdfReader(new MemoryStream(docBytes)))) {
+ PdfArray resultArray = docReopen.GetCatalog().GetPdfObject().GetAsDictionary(PdfName.OCProperties).GetAsDictionary
+ (PdfName.D).GetAsArray(PdfName.Order);
+ NUnit.Framework.Assert.AreEqual(2, resultArray.Size());
+ }
+ }
+
+ //TODO DEVSIX-8490 remove this test when implemented
+ [NUnit.Framework.Test]
+ public virtual void RemoveOrderDuplicateHasChildTest() {
+ using (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+ using (PdfDocument document = new PdfDocument(new PdfWriter(outputStream))) {
+ PdfDictionary ocgDic = new PdfDictionary();
+ PdfDictionary ocgDicChild1 = new PdfDictionary();
+ PdfDictionary ocgDicChild2 = new PdfDictionary();
+ ocgDic.MakeIndirect(document);
+ PdfArray orderArray = new PdfArray();
+ PdfArray childArray1 = new PdfArray();
+ childArray1.Add(ocgDicChild1);
+ PdfArray childArray2 = new PdfArray();
+ childArray2.Add(ocgDicChild2);
+ orderArray.Add(ocgDic);
+ orderArray.Add(childArray1);
+ orderArray.Add(ocgDic);
+ orderArray.Add(childArray2);
+ PdfDictionary DDictionary = new PdfDictionary();
+ DDictionary.Put(PdfName.Order, orderArray);
+ PdfArray OCGsArray = new PdfArray();
+ OCGsArray.Add(ocgDic);
+ OCGsArray.Add(ocgDicChild1);
+ OCGsArray.Add(ocgDicChild2);
+ PdfDictionary OCPropertiesDic = new PdfDictionary();
+ OCPropertiesDic.Put(PdfName.D, DDictionary);
+ OCPropertiesDic.Put(PdfName.OCGs, OCGsArray);
+ document.GetCatalog().GetPdfObject().Put(PdfName.OCProperties, OCPropertiesDic);
+ PdfIndirectReference @ref = ocgDic.GetIndirectReference();
+ PdfCatalog catalog = document.GetCatalog();
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfException), () => catalog.GetOCProperties(false));
+ NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(KernelExceptionMessageConstant.UNABLE_TO_REMOVE_DUPLICATE_LAYER
+ , @ref.ToString()), e.Message);
+ }
+ }
+ }
+ }
+}
diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/cmp_removedDuplicateInOrderArray.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/cmp_removedDuplicateInOrderArray.pdf
new file mode 100644
index 0000000000..602e3a4c81
Binary files /dev/null and b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/cmp_removedDuplicateInOrderArray.pdf differ
diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/cmp_removedNestedDuplicatesInOrderArray.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/cmp_removedNestedDuplicatesInOrderArray.pdf
new file mode 100644
index 0000000000..9da2248dfa
Binary files /dev/null and b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/cmp_removedNestedDuplicatesInOrderArray.pdf differ
diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/removeDuplicatesHasChildInOrderArray.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/removeDuplicatesHasChildInOrderArray.pdf
new file mode 100644
index 0000000000..2fa10d0386
Binary files /dev/null and b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/removeDuplicatesHasChildInOrderArray.pdf differ
diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/removeDuplicatesInOrderArray.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/removeDuplicatesInOrderArray.pdf
new file mode 100644
index 0000000000..5afded545d
Binary files /dev/null and b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/removeDuplicatesInOrderArray.pdf differ
diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/removeNestedDuplicatesHasChildInOrderArray.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/removeNestedDuplicatesHasChildInOrderArray.pdf
new file mode 100644
index 0000000000..754950dda9
Binary files /dev/null and b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/removeNestedDuplicatesHasChildInOrderArray.pdf differ
diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/removeNestedDuplicatesInOrderArray.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/removeNestedDuplicatesInOrderArray.pdf
new file mode 100644
index 0000000000..1f43c432c2
Binary files /dev/null and b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/PdfDocumentTest/removeNestedDuplicatesInOrderArray.pdf differ
diff --git a/itext/itext.kernel/itext/kernel/exceptions/KernelExceptionMessageConstant.cs b/itext/itext.kernel/itext/kernel/exceptions/KernelExceptionMessageConstant.cs
index f8e582445e..b66c96c2cd 100644
--- a/itext/itext.kernel/itext/kernel/exceptions/KernelExceptionMessageConstant.cs
+++ b/itext/itext.kernel/itext/kernel/exceptions/KernelExceptionMessageConstant.cs
@@ -619,6 +619,12 @@ public const String WHEN_ADDING_OBJECT_REFERENCE_TO_THE_TAG_TREE_IT_MUST_BE_CONN
public const String INVALID_OBJECT_STREAM_NUMBER = "Unable to read object {0} with object stream " + "number {1} and index {2} from object stream.";
+ //TODO DEVSIX-8490 remove this exception message when implemented
+ public const String UNABLE_TO_ADD_SECOND_PARENT_LAYER = "Unable to add second parent layer to " + "{0} ocg layer";
+
+ //TODO DEVSIX-8490 remove this exception message when implemented
+ public const String UNABLE_TO_REMOVE_DUPLICATE_LAYER = "Unable to remove duplicated layer {0} " + "because it has child layers.";
+
private KernelExceptionMessageConstant() {
}
}
diff --git a/itext/itext.kernel/itext/kernel/logs/KernelLogMessageConstant.cs b/itext/itext.kernel/itext/kernel/logs/KernelLogMessageConstant.cs
index 3ed81a33f3..b7f484f675 100644
--- a/itext/itext.kernel/itext/kernel/logs/KernelLogMessageConstant.cs
+++ b/itext/itext.kernel/itext/kernel/logs/KernelLogMessageConstant.cs
@@ -90,6 +90,10 @@ public sealed class KernelLogMessageConstant {
public const String XOBJECT_STRUCT_PARENT_INDEX_MISSED_AND_RECREATED = "XObject has no StructParents index in its stream, so index is recreated";
+ //TODO DEVSIX-8490 remove this log message when implemented
+ public const String DUPLICATE_ENTRIES_IN_ORDER_ARRAY_REMOVED = "Duplicated entries in order array are " +
+ "removed";
+
private KernelLogMessageConstant() {
}
//Private constructor will prevent the instantiation of this class directly
diff --git a/itext/itext.kernel/itext/kernel/pdf/layer/PdfLayer.cs b/itext/itext.kernel/itext/kernel/pdf/layer/PdfLayer.cs
index dc677a1ed5..de5a67994a 100644
--- a/itext/itext.kernel/itext/kernel/pdf/layer/PdfLayer.cs
+++ b/itext/itext.kernel/itext/kernel/pdf/layer/PdfLayer.cs
@@ -24,6 +24,7 @@ You should have received a copy of the GNU Affero General Public License
using System.Collections.Generic;
using iText.Commons.Utils;
using iText.IO.Font;
+using iText.Kernel.Exceptions;
using iText.Kernel.Pdf;
namespace iText.Kernel.Pdf.Layer {
@@ -114,8 +115,11 @@ public static void AddOCGRadioGroup(PdfDocument document, IListAdds a child layer. Nested layers can only have one parent.
/// the child layer
public virtual void AddChild(iText.Kernel.Pdf.Layer.PdfLayer childLayer) {
+ //TODO DEVSIX-8490 implement multiple parent support
if (childLayer.parent != null) {
- throw new ArgumentException("Illegal argument: childLayer");
+ PdfIndirectReference @ref = childLayer.GetIndirectReference();
+ throw new PdfException(MessageFormatUtil.Format(KernelExceptionMessageConstant.UNABLE_TO_ADD_SECOND_PARENT_LAYER
+ , @ref.ToString()));
}
childLayer.parent = this;
if (children == null) {
diff --git a/itext/itext.kernel/itext/kernel/pdf/layer/PdfOCProperties.cs b/itext/itext.kernel/itext/kernel/pdf/layer/PdfOCProperties.cs
index 3bfbb72e00..f61c976b11 100644
--- a/itext/itext.kernel/itext/kernel/pdf/layer/PdfOCProperties.cs
+++ b/itext/itext.kernel/itext/kernel/pdf/layer/PdfOCProperties.cs
@@ -26,6 +26,7 @@ You should have received a copy of the GNU Affero General Public License
using iText.Commons;
using iText.Commons.Utils;
using iText.IO.Font;
+using iText.Kernel.Exceptions;
using iText.Kernel.Logs;
using iText.Kernel.Pdf;
@@ -51,6 +52,12 @@ public class PdfOCProperties : PdfObjectWrapper {
private IList layers = new List();
+ //TODO DEVSIX-8490 remove this field when implemented
+ private ICollection references;
+
+ //TODO DEVSIX-8490 remove this field when implemented
+ private bool isDuplicateRemoved;
+
/// Creates a new PdfOCProperties instance.
/// the document the optional content belongs to
public PdfOCProperties(PdfDocument document)
@@ -406,7 +413,14 @@ private void ReadLayersFromDictionary() {
}
PdfArray orderArray = d.GetAsArray(PdfName.Order);
if (orderArray != null && !orderArray.IsEmpty()) {
+ references = new HashSet();
+ isDuplicateRemoved = false;
ReadOrderFromDictionary(null, orderArray, layerMap);
+ //TODO DEVSIX-8490 remove this check when implemented
+ if (isDuplicateRemoved) {
+ ILogger logger = ITextLogManager.GetLogger(typeof(iText.Kernel.Pdf.Layer.PdfOCProperties));
+ logger.LogWarning(KernelLogMessageConstant.DUPLICATE_ENTRIES_IN_ORDER_ARRAY_REMOVED);
+ }
}
}
// Add the layers which should not be displayed on the panel to the order list
@@ -424,7 +438,25 @@ private void ReadOrderFromDictionary(PdfLayer parent, PdfArray orderArray, IDict
PdfObject item = orderArray.Get(i);
if (item.GetObjectType() == PdfObject.DICTIONARY) {
PdfLayer layer = layerMap.Get(item.GetIndirectReference());
- if (layer != null) {
+ if (layer == null) {
+ continue;
+ }
+ //TODO DEVSIX-8490 remove this check and it statement when implemented
+ if (references.Contains(layer.GetIndirectReference())) {
+ //We want to check if this duplicate layer has childLayers, if it has - throw an exception,
+ // else just don't add this layer.
+ if (i + 1 < orderArray.Size() && orderArray.Get(i + 1).GetObjectType() == PdfObject.ARRAY) {
+ PdfArray nextArray = orderArray.GetAsArray(i + 1);
+ if (nextArray.Size() > 0 && nextArray.Get(0).GetObjectType() != PdfObject.STRING) {
+ PdfIndirectReference @ref = layer.GetIndirectReference();
+ throw new PdfException(MessageFormatUtil.Format(KernelExceptionMessageConstant.UNABLE_TO_REMOVE_DUPLICATE_LAYER
+ , @ref.ToString()));
+ }
+ }
+ isDuplicateRemoved = true;
+ }
+ else {
+ references.Add(layer.GetIndirectReference());
layers.Add(layer);
layer.onPanel = true;
if (parent != null) {
diff --git a/port-hash b/port-hash
index 6174884e88..2f5e19c137 100644
--- a/port-hash
+++ b/port-hash
@@ -1 +1 @@
-cd91477a6b5dcb768f827dfa4d1083fecb2c8f52
+c8f9dfd8aab3ecfc3815c096cd36fa8109b9b0ab