Skip to content

Commit

Permalink
Merge branch 'develop' into devsecops
Browse files Browse the repository at this point in the history
  • Loading branch information
aleks-ivanov committed Jul 2, 2024
2 parents eb86ea1 + 12591b4 commit 599d188
Show file tree
Hide file tree
Showing 15 changed files with 479 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,12 @@ public virtual void IdTreeIsLazyTest() {
PdfDocument readPdfDoc = new PdfDocument(r);
NUnit.Framework.Assert.IsFalse(readPdfDoc.GetStructTreeRoot().GetPdfObject().ContainsKey(PdfName.IDTree));
}

[NUnit.Framework.Test]
public virtual void CyclicReferencesTest() {
String inFile = sourceFolder + "cyclicReferences.pdf";
PdfDocument pdfDoc = new PdfDocument(new PdfReader(inFile), new PdfWriter(new MemoryStream()));
NUnit.Framework.Assert.DoesNotThrow(() => pdfDoc.Close());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,28 @@ public virtual void TagTreeIteratorTagPointerNull() {
NUnit.Framework.Assert.AreEqual(e.Message, errorMessage);
}

[NUnit.Framework.Test]
public virtual void TagTreeIteratorApproverNull() {
String errorMessage = MessageFormatUtil.Format(KernelExceptionMessageConstant.ARG_SHOULD_NOT_BE_NULL, "approver"
);
PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream(), new WriterProperties()));
doc.SetTagged();
Exception e = NUnit.Framework.Assert.Catch(typeof(ArgumentException), () => new TagTreeIterator(doc.GetStructTreeRoot
(), null, TagTreeIterator.TreeTraversalOrder.PRE_ORDER));
NUnit.Framework.Assert.AreEqual(e.Message, errorMessage);
}

[NUnit.Framework.Test]
public virtual void TagTreeIteratorHandlerNull() {
String errorMessage = MessageFormatUtil.Format(KernelExceptionMessageConstant.ARG_SHOULD_NOT_BE_NULL, "handler"
);
PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream(), new WriterProperties()));
doc.SetTagged();
TagTreeIterator it = new TagTreeIterator(doc.GetStructTreeRoot());
Exception e = NUnit.Framework.Assert.Catch(typeof(ArgumentException), () => it.AddHandler(null));
NUnit.Framework.Assert.AreEqual(e.Message, errorMessage);
}

[NUnit.Framework.Test]
public virtual void TraversalWithoutElements() {
PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream(), new WriterProperties()));
Expand Down Expand Up @@ -76,6 +98,53 @@ public virtual void TraversalWithSomeElements() {
NUnit.Framework.Assert.AreEqual(PdfName.Code, handler.nodes[6].GetRole());
}

[NUnit.Framework.Test]
public virtual void PostOrderTraversal() {
PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream(), new WriterProperties()));
doc.SetTagged();
TagTreePointer tp = new TagTreePointer(doc);
tp.AddTag(StandardRoles.DIV);
tp.AddTag(StandardRoles.P);
tp.AddTag(StandardRoles.FIGURE);
tp.MoveToParent();
tp.AddTag(StandardRoles.DIV);
tp.AddTag(StandardRoles.CODE);
TagTreeIterator iterator = new TagTreeIterator(doc.GetStructTreeRoot(), new TagTreeIteratorElementApprover
(), TagTreeIterator.TreeTraversalOrder.POST_ORDER);
TagTreeIteratorTest.TestHandler handler = new TagTreeIteratorTest.TestHandler();
iterator.AddHandler(handler);
iterator.Traverse();
NUnit.Framework.Assert.AreEqual(7, handler.nodes.Count);
NUnit.Framework.Assert.AreEqual(PdfName.Figure, handler.nodes[0].GetRole());
NUnit.Framework.Assert.AreEqual(PdfName.Code, handler.nodes[1].GetRole());
NUnit.Framework.Assert.AreEqual(PdfName.Div, handler.nodes[2].GetRole());
NUnit.Framework.Assert.AreEqual(PdfName.P, handler.nodes[3].GetRole());
NUnit.Framework.Assert.AreEqual(PdfName.Div, handler.nodes[4].GetRole());
NUnit.Framework.Assert.AreEqual(PdfName.Document, handler.nodes[5].GetRole());
NUnit.Framework.Assert.IsNull(handler.nodes[6].GetRole());
}

[NUnit.Framework.Test]
public virtual void CyclicReferencesTraversal() {
PdfDocument doc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream(), new WriterProperties()));
doc.SetTagged();
PdfStructElem kid1 = new PdfStructElem(doc, PdfStructTreeRoot.ConvertRoleToPdfName(StandardRoles.P));
PdfStructElem kid2 = new PdfStructElem(doc, PdfStructTreeRoot.ConvertRoleToPdfName(StandardRoles.DIV));
doc.GetStructTreeRoot().AddKid(kid1);
doc.GetStructTreeRoot().AddKid(kid2);
kid1.AddKid(kid2);
kid2.AddKid(kid1);
TagTreeIterator iterator = new TagTreeIterator(doc.GetStructTreeRoot(), new TagTreeIteratorAvoidDuplicatesApprover
(), TagTreeIterator.TreeTraversalOrder.POST_ORDER);
TagTreeIteratorTest.TestHandler handler = new TagTreeIteratorTest.TestHandler();
iterator.AddHandler(handler);
iterator.Traverse();
NUnit.Framework.Assert.AreEqual(3, handler.nodes.Count);
NUnit.Framework.Assert.AreEqual(PdfName.Div, handler.nodes[0].GetRole());
NUnit.Framework.Assert.AreEqual(PdfName.P, handler.nodes[1].GetRole());
NUnit.Framework.Assert.IsNull(handler.nodes[2].GetRole());
}

//\cond DO_NOT_DOCUMENT
internal class TestHandler : ITagTreeIteratorHandler {
//\cond DO_NOT_DOCUMENT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,57 @@ public virtual void CannotFlushAlreadyFlushedPageTest() {
NUnit.Framework.Assert.AreEqual(KernelExceptionMessageConstant.PAGE_ALREADY_FLUSHED, exception.Message);
}

[NUnit.Framework.Test]
public virtual void CyclicReferencesWhileLookingForRoleTest() {
PdfDocument doc = CreateTestDocument();
PdfStructElem kid1 = new PdfStructElem(doc, PdfStructTreeRoot.ConvertRoleToPdfName(StandardRoles.P));
PdfStructElem kid2 = new PdfStructElem(doc, PdfStructTreeRoot.ConvertRoleToPdfName(StandardRoles.DIV));
doc.GetStructTreeRoot().AddKid(kid1);
doc.GetStructTreeRoot().AddKid(kid2);
kid1.AddKid(kid2);
kid2.AddKid(kid1);
TagTreePointer pointer = new TagTreePointer(doc);
Exception exception = NUnit.Framework.Assert.Catch(typeof(PdfException), () => pointer.MoveToKid(StandardRoles
.FIGURE));
NUnit.Framework.Assert.AreEqual(KernelExceptionMessageConstant.NO_KID_WITH_SUCH_ROLE, exception.Message);
}

[NUnit.Framework.Test]
public virtual void CyclicReferencesWhileFlushingTest() {
PdfDocument doc = CreateTestDocument();
PdfStructElem kid1 = new PdfStructElem(doc, PdfStructTreeRoot.ConvertRoleToPdfName(StandardRoles.P));
PdfStructElem kid2 = new PdfStructElem(doc, PdfStructTreeRoot.ConvertRoleToPdfName(StandardRoles.DIV));
doc.GetStructTreeRoot().AddKid(kid1);
doc.GetStructTreeRoot().AddKid(kid2);
kid1.AddKid(kid2);
kid2.AddKid(kid1);
TagTreePointer pointer = new TagTreePointer(doc);
pointer.MoveToKid(StandardRoles.P);
NUnit.Framework.Assert.DoesNotThrow(() => pointer.FlushTag());
NUnit.Framework.Assert.IsTrue(kid1.IsFlushed());
NUnit.Framework.Assert.IsTrue(kid2.IsFlushed());
}

[NUnit.Framework.Test]
public virtual void CyclicReferencesWithWaitingObjectsWhileFlushingTest() {
PdfDocument doc = CreateTestDocument();
PdfStructElem kid1 = new PdfStructElem(doc, PdfStructTreeRoot.ConvertRoleToPdfName(StandardRoles.P));
PdfStructElem kid2 = new PdfStructElem(doc, PdfStructTreeRoot.ConvertRoleToPdfName(StandardRoles.DIV));
doc.GetStructTreeRoot().AddKid(kid1);
doc.GetStructTreeRoot().AddKid(kid2);
kid1.AddKid(kid2);
kid2.AddKid(kid1);
TagTreePointer pointer = new TagTreePointer(doc);
pointer.MoveToKid(StandardRoles.P);
WaitingTagsManager waitingTagsManager = pointer.GetContext().GetWaitingTagsManager();
Object pWaitingTagObj = new Object();
waitingTagsManager.AssignWaitingState(pointer, pWaitingTagObj);
pointer.MoveToParent().MoveToKid(StandardRoles.DIV);
NUnit.Framework.Assert.DoesNotThrow(() => pointer.FlushTag());
NUnit.Framework.Assert.IsFalse(kid1.IsFlushed());
NUnit.Framework.Assert.IsTrue(kid2.IsFlushed());
}

private static PdfDocument CreateTestDocument() {
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));
pdfDoc.SetTagged();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ You should have received a copy of the GNU Affero General Public License
using iText.Commons.Utils;
using iText.Kernel.Exceptions;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Tagging;
using iText.Test;

namespace iText.Kernel.Utils {
Expand Down Expand Up @@ -77,5 +78,27 @@ public virtual void NoStructTreeRootInDocTest() {
NUnit.Framework.Assert.Fail("IOException is not expected to be triggered");
}
}

[NUnit.Framework.Test]
public virtual void CyclicReferencesTest() {
String outXmlPath = DESTINATION_FOLDER + "cyclicReferences.xml";
String cmpXmlPath = SOURCE_FOLDER + "cmp_cyclicReferences.xml";
PdfDocument doc = new PdfDocument(new PdfWriter(new MemoryStream()));
doc.SetTagged();
PdfStructElem kid1 = new PdfStructElem(doc, PdfStructTreeRoot.ConvertRoleToPdfName(StandardRoles.P));
PdfStructElem kid2 = new PdfStructElem(doc, PdfStructTreeRoot.ConvertRoleToPdfName(StandardRoles.DIV));
doc.GetStructTreeRoot().AddKid(kid1);
doc.GetStructTreeRoot().AddKid(kid2);
kid1.AddKid(kid2);
kid2.AddKid(kid1);
TaggedPdfReaderTool tool = new TaggedPdfReaderTool(doc);
using (Stream outXml = FileUtil.GetFileOutputStream(outXmlPath)) {
tool.ConvertToXml(outXml, "UTF-8");
}
CompareTool compareTool = new CompareTool();
if (!compareTool.CompareXmls(outXmlPath, cmpXmlPath)) {
NUnit.Framework.Assert.Fail("Resultant xml is different.");
}
}
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<P>
<Div>
</Div>
</P>
15 changes: 7 additions & 8 deletions itext/itext.kernel/itext/kernel/pdf/tagging/PdfStructTreeRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ You should have received a copy of the GNU Affero General Public License
using iText.Kernel.Exceptions;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Filespec;
using iText.Kernel.Pdf.Tagutils;

namespace iText.Kernel.Pdf.Tagging {
/// <summary>Represents a wrapper-class for structure tree root dictionary.</summary>
Expand Down Expand Up @@ -399,7 +400,7 @@ public override void Flush() {
GetPdfObject().Put(PdfName.IDTree, this.idTree.BuildTree().MakeIndirect(GetDocument()));
}
if (!GetDocument().IsAppendMode()) {
FlushAllKids(this);
iText.Kernel.Pdf.Tagging.PdfStructTreeRoot.FlushAllKids(this);
}
base.Flush();
}
Expand Down Expand Up @@ -597,13 +598,11 @@ protected internal override bool IsWrappedObjectMustBeIndirect() {
return true;
}

private void FlushAllKids(IStructureNode elem) {
foreach (IStructureNode kid in elem.GetKids()) {
if (kid is PdfStructElem && !((PdfStructElem)kid).IsFlushed()) {
FlushAllKids(kid);
((PdfStructElem)kid).Flush();
}
}
private static void FlushAllKids(iText.Kernel.Pdf.Tagging.PdfStructTreeRoot elem) {
TagTreeIterator iterator = new TagTreeIterator(elem, new TagTreeIteratorAvoidDuplicatesApprover(), TagTreeIterator.TreeTraversalOrder
.POST_ORDER);
iterator.AddHandler(new TagTreeIteratorFlusher());
iterator.Traverse();
}

private void IfKidIsStructElementAddToList(PdfObject kid, IList<IStructureNode> kids) {
Expand Down
74 changes: 65 additions & 9 deletions itext/itext.kernel/itext/kernel/pdf/tagutils/TagTreeIterator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,35 +31,76 @@ namespace iText.Kernel.Pdf.Tagutils {
/// <remarks>
/// This class is used to traverse the tag tree.
/// <para />
/// There is a possibility to add a handler that will be called for specific events during the traversal.
/// There is a possibility to add a handler that will be called for the elements during the traversal.
/// </remarks>
public class TagTreeIterator {
private readonly IStructureNode pointer;

private readonly ICollection<ITagTreeIteratorHandler> handlerList;

private readonly TagTreeIteratorElementApprover approver;

private readonly TagTreeIterator.TreeTraversalOrder traversalOrder;

/// <summary>
/// Creates a new instance of
/// <see cref="TagTreeIterator"/>.
/// </summary>
/// <remarks>
/// Creates a new instance of
/// <see cref="TagTreeIterator"/>
/// . It will use
/// <see cref="TagTreeIteratorElementApprover"/>
/// to filter
/// elements and TreeTraversalOrder.PRE_ORDER for tree traversal.
/// </remarks>
/// <param name="tagTreePointer">the tag tree pointer.</param>
public TagTreeIterator(IStructureNode tagTreePointer)
: this(tagTreePointer, new TagTreeIteratorElementApprover(), TagTreeIterator.TreeTraversalOrder.PRE_ORDER) {
}

/// <summary>
/// Creates a new instance of
/// <see cref="TagTreeIterator"/>.
/// </summary>
/// <param name="tagTreePointer">the tag tree pointer.</param>
public TagTreeIterator(IStructureNode tagTreePointer) {
/// <param name="approver">
/// a filter that will be called to let iterator know whether some particular element
/// should be traversed or not.
/// </param>
/// <param name="traversalOrder">an order in which the tree will be traversed.</param>
public TagTreeIterator(IStructureNode tagTreePointer, TagTreeIteratorElementApprover approver, TagTreeIterator.TreeTraversalOrder
traversalOrder) {
if (tagTreePointer == null) {
throw new ArgumentException(MessageFormatUtil.Format(KernelExceptionMessageConstant.ARG_SHOULD_NOT_BE_NULL
, "tagTreepointer"));
}
if (approver == null) {
throw new ArgumentException(MessageFormatUtil.Format(KernelExceptionMessageConstant.ARG_SHOULD_NOT_BE_NULL
, "approver"));
}
if (traversalOrder == null) {
throw new ArgumentException(MessageFormatUtil.Format(KernelExceptionMessageConstant.ARG_SHOULD_NOT_BE_NULL
, "traversalOrder"));
}
this.pointer = tagTreePointer;
this.traversalOrder = traversalOrder;
handlerList = new HashSet<ITagTreeIteratorHandler>();
this.approver = approver;
}

/// <summary>Adds a handler that will be called for specific events during the traversal.</summary>
/// <summary>Adds a handler that will be called for the elements during the traversal.</summary>
/// <param name="handler">the handler.</param>
/// <returns>
/// this
/// <see cref="TagTreeIterator"/>
/// instance.
/// </returns>
public virtual iText.Kernel.Pdf.Tagutils.TagTreeIterator AddHandler(ITagTreeIteratorHandler handler) {
if (handler == null) {
throw new ArgumentException(MessageFormatUtil.Format(KernelExceptionMessageConstant.ARG_SHOULD_NOT_BE_NULL
, "handler"));
}
this.handlerList.Add(handler);
return this;
}
Expand All @@ -71,22 +112,37 @@ public virtual iText.Kernel.Pdf.Tagutils.TagTreeIterator AddHandler(ITagTreeIter
/// Make sure the correct handlers are added before calling this method.
/// </remarks>
public virtual void Traverse() {
Traverse(this.pointer, this.handlerList);
Traverse(this.pointer);
}

private static void Traverse(IStructureNode elem, ICollection<ITagTreeIteratorHandler> handlerList) {
if (elem == null) {
private void Traverse(IStructureNode elem) {
if (!approver.Approve(elem)) {
return;
}
foreach (ITagTreeIteratorHandler handler in handlerList) {
handler.NextElement(elem);
if (traversalOrder == TagTreeIterator.TreeTraversalOrder.PRE_ORDER) {
foreach (ITagTreeIteratorHandler handler in handlerList) {
handler.NextElement(elem);
}
}
IList<IStructureNode> kids = elem.GetKids();
if (kids != null) {
foreach (IStructureNode kid in kids) {
Traverse(kid, handlerList);
Traverse(kid);
}
}
if (traversalOrder == TagTreeIterator.TreeTraversalOrder.POST_ORDER) {
foreach (ITagTreeIteratorHandler handler in handlerList) {
handler.NextElement(elem);
}
}
}

/// <summary>Tree traversal order enum.</summary>
public enum TreeTraversalOrder {
/// <summary>Preorder traversal.</summary>
PRE_ORDER,
/// <summary>Postorder traversal.</summary>
POST_ORDER
}
}
}
Loading

0 comments on commit 599d188

Please sign in to comment.