diff --git a/itext.tests/itext.pdfua.tests/itext/pdfua/PdfUACanvasTest.cs b/itext.tests/itext.pdfua.tests/itext/pdfua/PdfUACanvasTest.cs
new file mode 100644
index 0000000000..a7fa0ea68c
--- /dev/null
+++ b/itext.tests/itext.pdfua.tests/itext/pdfua/PdfUACanvasTest.cs
@@ -0,0 +1,542 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2023 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.Font;
+using iText.IO.Font.Constants;
+using iText.IO.Font.Otf;
+using iText.Kernel.Colors;
+using iText.Kernel.Font;
+using iText.Kernel.Geom;
+using iText.Kernel.Pdf;
+using iText.Kernel.Pdf.Canvas;
+using iText.Kernel.Pdf.Tagging;
+using iText.Kernel.Pdf.Tagutils;
+using iText.Kernel.Utils;
+using iText.Pdfua.Exceptions;
+using iText.Test;
+using iText.Test.Pdfa;
+
+namespace iText.Pdfua {
+ [NUnit.Framework.Category("UnitTest")]
+ public class PdfUACanvasTest : ExtendedITextTest {
+ private static readonly String FONT = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext
+ .CurrentContext.TestDirectory) + "/resources/itext/pdfua/font/FreeSans.ttf";
+
+ private static readonly String DESTINATION_FOLDER = NUnit.Framework.TestContext.CurrentContext.TestDirectory
+ + "/test/itext/pdfua/PdfUACanvasTest/";
+
+ private static readonly String SOURCE_FOLDER = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext
+ .CurrentContext.TestDirectory) + "/resources/itext/pdfua/PdfUACanvasTest/";
+
+ [NUnit.Framework.OneTimeSetUp]
+ public static void Before() {
+ CreateOrClearDestinationFolder(DESTINATION_FOLDER);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_TextContentIsNotTagged() {
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(new MemoryStream(), PdfUATestPdfDocument
+ .CreateWriterProperties()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ canvas.SaveState().BeginText().SetFontAndSize(PdfFontFactory.CreateFont(StandardFonts.COURIER), 12);
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => {
+ canvas.ShowText("Hello World!");
+ }
+ );
+ NUnit.Framework.Assert.AreEqual(PdfUAExceptionMessageConstants.TAG_HASNT_BEEN_ADDED_BEFORE_CONTENT_ADDING,
+ e.Message);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_TextContentIsCorrectlyTaggedAsContent() {
+ String outPdf = DESTINATION_FOLDER + "01_005_TextContentIsCorrectlyTaggedAsContent.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfPage page1 = pdfDoc.AddNewPage();
+ PdfCanvas canvas = new PdfCanvas(page1);
+ TagTreePointer tagPointer = new TagTreePointer(pdfDoc).SetPageForTagging(page1).AddTag(StandardRoles.P);
+ canvas.OpenTag(tagPointer.GetTagReference()).SaveState().BeginText().SetFontAndSize(font, 12).MoveText(200
+ , 200).ShowText("Hello World!").EndText().RestoreState().CloseTag();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_01_005_TextContentIsCorrectlyTaggedAsContent.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_TextContentIsNotInTagTree() {
+ String outPdf = DESTINATION_FOLDER + "01_005_TextContentIsNotInTagTree.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfPage page1 = pdfDoc.AddNewPage();
+ PdfCanvas canvas = new PdfCanvas(page1);
+ canvas.OpenTag(new CanvasTag(PdfName.P)).SaveState().BeginText().SetFontAndSize(font, 12).MoveText(200, 200
+ );
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => {
+ canvas.ShowText("Hello World!");
+ }
+ );
+ NUnit.Framework.Assert.AreEqual(PdfUAExceptionMessageConstants.CONTENT_IS_NOT_REAL_CONTENT_AND_NOT_ARTIFACT
+ , e.Message);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_TextArtifactIsNotInTagTree() {
+ String outPdf = DESTINATION_FOLDER + "01_005_TextArtifactIsNotInTagTree.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfPage page1 = pdfDoc.AddNewPage();
+ PdfCanvas canvas = new PdfCanvas(page1);
+ canvas.OpenTag(new CanvasTag(PdfName.Artifact)).SaveState().BeginText().SetFontAndSize(font, 12).MoveText(
+ 200, 200).ShowText("Hello World!").EndText().RestoreState().CloseTag();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_01_005_TextArtifactIsNotInTagTree.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_TextContentWithMCIDButNotInTagTree() {
+ String outPdf = DESTINATION_FOLDER + "01_005_TextContentWithMCIDButNotInTagTree.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfPage page1 = pdfDoc.AddNewPage();
+ PdfCanvas canvas = new PdfCanvas(page1);
+ canvas.OpenTag(new CanvasTag(PdfName.P, 99)).SaveState().BeginText().SetFontAndSize(font, 12).MoveText(200
+ , 200);
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => {
+ canvas.ShowText("Hello World!");
+ }
+ );
+ NUnit.Framework.Assert.AreEqual(PdfUAExceptionMessageConstants.CONTENT_WITH_MCID_BUT_MCID_NOT_FOUND_IN_STRUCT_TREE_ROOT
+ , e.Message);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_TextGlyphLineContentIsTaggedButNotInTagTree() {
+ String outPdf = DESTINATION_FOLDER + "01_005_TextGlyphLineContentIsTagged.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ GlyphLine glyphLine = font.CreateGlyphLine("Hello World!");
+ canvas.SaveState().OpenTag(new CanvasTag(PdfName.H1)).SetFontAndSize(font, 12).BeginText().MoveText(200, 200
+ ).SetColor(ColorConstants.RED, true);
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => {
+ canvas.ShowText(glyphLine);
+ }
+ );
+ NUnit.Framework.Assert.AreEqual(PdfUAExceptionMessageConstants.CONTENT_IS_NOT_REAL_CONTENT_AND_NOT_ARTIFACT
+ , e.Message);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_TextGlyphLineContentIsArtifact() {
+ String outPdf = DESTINATION_FOLDER + "01_005_TextGlyphLineContentIsArtifact.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ GlyphLine glyphLine = font.CreateGlyphLine("Hello World!");
+ canvas.SaveState().OpenTag(new CanvasTag(PdfName.Artifact)).SetFontAndSize(font, 12).BeginText().MoveText(
+ 200, 200).SetColor(ColorConstants.RED, true).ShowText(glyphLine).EndText().CloseTag().RestoreState();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_01_005_TextGlyphLineContentIsArtifact.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_TextGlyphLineContentIsContentCorrect() {
+ String outPdf = DESTINATION_FOLDER + "01_005_TextGlyphLineContentIsContentCorrect.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ GlyphLine glyphLine = font.CreateGlyphLine("Hello World!");
+ TagTreePointer tagPointer = new TagTreePointer(pdfDoc).SetPageForTagging(pdfDoc.GetFirstPage()).AddTag(StandardRoles
+ .H1);
+ canvas.SaveState().OpenTag(tagPointer.GetTagReference()).SetFontAndSize(font, 12).BeginText().MoveText(200
+ , 200).SetColor(ColorConstants.RED, true).ShowText(glyphLine).EndText().CloseTag().RestoreState();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_01_005_TextGlyphLineContentIsContentCorrect.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_allowPureBmcInArtifact() {
+ String outPdf = DESTINATION_FOLDER + "01_005_allowPureBmcInArtifact.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ GlyphLine glyphLine = font.CreateGlyphLine("Hello World!");
+ canvas.SaveState().OpenTag(new CanvasTag(PdfName.Artifact)).SetFontAndSize(font, 12).BeginMarkedContent(PdfName
+ .P).BeginText().MoveText(200, 200).SetColor(ColorConstants.RED, true).ShowText(glyphLine).EndMarkedContent
+ ().EndText().CloseTag().RestoreState();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_01_005_allowPureBmcInArtifact.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_allowNestedPureBmcInArtifact() {
+ String outPdf = DESTINATION_FOLDER + "01_005_allowNestedPureBmcInArtifact.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ GlyphLine glyphLine = font.CreateGlyphLine("Hello World!");
+ canvas.SaveState().OpenTag(new CanvasTag(PdfName.Artifact)).SetFontAndSize(font, 12).BeginMarkedContent(PdfName
+ .P).OpenTag(new CanvasTag(PdfName.Artifact)).BeginText().MoveText(200, 200).SetColor(ColorConstants.RED
+ , true).ShowText(glyphLine).CloseTag().EndMarkedContent().EndText().CloseTag().RestoreState();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_01_005_allowNestedPureBmcInArtifact.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_LineContentThatIsContentIsNotTagged() {
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(new MemoryStream(), PdfUATestPdfDocument
+ .CreateWriterProperties()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ canvas.SetColor(ColorConstants.RED, true).SetLineWidth(2);
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => {
+ canvas.LineTo(200, 200);
+ }
+ );
+ NUnit.Framework.Assert.AreEqual(PdfUAExceptionMessageConstants.TAG_HASNT_BEEN_ADDED_BEFORE_CONTENT_ADDING,
+ e.Message);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_LineContentThatIsContentIsTaggedButIsNotAnArtifact() {
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(new MemoryStream(), PdfUATestPdfDocument
+ .CreateWriterProperties()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ canvas.OpenTag(new CanvasTag(PdfName.H1)).SetColor(ColorConstants.RED, true).SetLineWidth(2);
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => {
+ canvas.LineTo(200, 200);
+ }
+ );
+ NUnit.Framework.Assert.AreEqual(PdfUAExceptionMessageConstants.CONTENT_IS_NOT_REAL_CONTENT_AND_NOT_ARTIFACT
+ , e.Message);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_LineContentThatIsMarkedAsArtifact() {
+ String outPdf = DESTINATION_FOLDER + "01_005_LineContentThatIsMarkedAsArtifact.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ TagTreePointer tagPointer = new TagTreePointer(pdfDoc).SetPageForTagging(pdfDoc.GetFirstPage()).AddTag(StandardRoles
+ .H);
+ canvas.OpenTag(tagPointer.GetTagReference()).SaveState().SetStrokeColor(ColorConstants.MAGENTA).MoveTo(300
+ , 300).LineTo(400, 350).Stroke().RestoreState().CloseTag();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_01_005_LineContentThatIsMarkedAsArtifact.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_RectangleNotMarked() {
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(new MemoryStream(), PdfUATestPdfDocument
+ .CreateWriterProperties()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ canvas.SetColor(ColorConstants.RED, true).SetLineWidth(2);
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => {
+ canvas.Rectangle(new Rectangle(200, 200, 100, 100));
+ }
+ );
+ NUnit.Framework.Assert.AreEqual(PdfUAExceptionMessageConstants.TAG_HASNT_BEEN_ADDED_BEFORE_CONTENT_ADDING,
+ e.Message);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_RectangleMarkedArtifact() {
+ String outPdf = DESTINATION_FOLDER + "01_005_RectangleMarkedArtifact.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ canvas.SaveState().OpenTag(new CanvasTag(PdfName.Artifact)).SetFillColor(ColorConstants.RED).Rectangle(new
+ Rectangle(200, 200, 100, 100)).Fill().CloseTag().RestoreState();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_01_005_RectangleMarkedArtifact.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_RectangleMarkedContentWithoutMcid() {
+ String outPdf = DESTINATION_FOLDER + "01_005_RectangleMarkedContentWithoutMcid.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ canvas.SaveState().OpenTag(new CanvasTag(PdfName.Rect)).SetFillColor(ColorConstants.RED);
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => {
+ canvas.Rectangle(new Rectangle(200, 200, 100, 100));
+ }
+ );
+ NUnit.Framework.Assert.AreEqual(PdfUAExceptionMessageConstants.CONTENT_IS_NOT_REAL_CONTENT_AND_NOT_ARTIFACT
+ , e.Message);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_RectangleMarkedContent() {
+ String outPdf = DESTINATION_FOLDER + "01_005_RectangleMarkedContent.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ TagTreePointer tagPointer = new TagTreePointer(pdfDoc).SetPageForTagging(pdfDoc.GetFirstPage()).AddTag(StandardRoles
+ .H);
+ canvas.SaveState().OpenTag(tagPointer.GetTagReference()).SetFillColor(ColorConstants.RED).Rectangle(new Rectangle
+ (200, 200, 100, 100)).Fill().CloseTag().RestoreState();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_01_005_RectangleMarkedContent.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_004_bezierMarkedAsContent() {
+ String outPdf = DESTINATION_FOLDER + "01_004_bezierCurveShouldBeTagged.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ TagTreePointer tagPointer = new TagTreePointer(pdfDoc).SetPageForTagging(pdfDoc.GetFirstPage()).AddTag(StandardRoles
+ .DIV);
+ canvas.SaveState().OpenTag(tagPointer.GetTagReference()).SetColor(ColorConstants.RED, true).SetLineWidth(5
+ ).SetStrokeColor(ColorConstants.RED).Arc(400, 400, 500, 500, 30, 50).Stroke().CloseTag().RestoreState(
+ );
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_01_004_bezierCurveShouldBeTagged.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_004_bezierMarkedAsArtifact() {
+ String outPdf = DESTINATION_FOLDER + "01_004_bezierMarkedAsArtifact.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ canvas.SaveState().OpenTag(new CanvasTag(PdfName.Artifact)).SetColor(ColorConstants.RED, true).SetLineWidth
+ (5).SetStrokeColor(ColorConstants.RED).Arc(400, 400, 500, 500, 30, 50).Stroke().CloseTag().RestoreState
+ ();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_01_004_bezierMarkedAsArtifact.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_004_bezierCurveInvalidMCID() {
+ String outPdf = DESTINATION_FOLDER + "01_004_bezierCurveInvalidMCID.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ canvas.SaveState().OpenTag(new CanvasTag(PdfName.P, 420)).SetColor(ColorConstants.RED, true).SetLineWidth(
+ 5).SetStrokeColor(ColorConstants.RED);
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => {
+ canvas.Arc(400, 400, 500, 500, 30, 50);
+ }
+ );
+ NUnit.Framework.Assert.AreEqual(PdfUAExceptionMessageConstants.CONTENT_WITH_MCID_BUT_MCID_NOT_FOUND_IN_STRUCT_TREE_ROOT
+ , e.Message);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_005_RandomOperationsWithoutActuallyAddingContent() {
+ String outPdf = DESTINATION_FOLDER + "01_005_RandomOperationsWithoutActuallyAddingContent.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ canvas.SetColor(ColorConstants.RED, true).SetLineCapStyle(1).SetTextMatrix(20, 2).SetLineWidth(2);
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_01_005_RandomOperationsWithoutActuallyAddingContent.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_003_ContentMarkedAsArtifactsPresentInsideTaggedContent() {
+ String outPdf = DESTINATION_FOLDER + "01_003_ContentMarkedAsArtifactsPresentInsideTaggedContent.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfPage page1 = pdfDoc.AddNewPage();
+ PdfCanvas canvas = new PdfCanvas(page1);
+ TagTreePointer tagPointer = new TagTreePointer(pdfDoc).SetPageForTagging(page1).AddTag(StandardRoles.P);
+ canvas.OpenTag(tagPointer.GetTagReference()).SaveState().BeginText().SetFontAndSize(font, 12).MoveText(200
+ , 200).ShowText("Hello World!").EndText();
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => {
+ canvas.OpenTag(new CanvasTag(PdfName.Artifact));
+ }
+ );
+ NUnit.Framework.Assert.AreEqual(PdfUAExceptionMessageConstants.ARTIFACT_CANT_BE_INSIDE_REAL_CONTENT, e.Message
+ );
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_validRoleAddedInsideMarkedContent() {
+ String outPdf = DESTINATION_FOLDER + "validRoleAddedInsideMarkedContent.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfPage page1 = pdfDoc.AddNewPage();
+ PdfCanvas canvas = new PdfCanvas(page1);
+ //Have to use low level tagging otherwise it throws error earlier
+ PdfStructElem doc = pdfDoc.GetStructTreeRoot().AddKid(new PdfStructElem(pdfDoc, PdfName.Document));
+ PdfStructElem paragraph = doc.AddKid(new PdfStructElem(pdfDoc, PdfName.P, page1));
+ PdfMcr mcr = paragraph.AddKid(new PdfMcrNumber(page1, paragraph));
+ canvas.OpenTag(new CanvasTag(mcr)).SaveState().BeginMarkedContent(PdfName.P).BeginText().SetFontAndSize(font
+ , 12).MoveText(200, 200).ShowText("Hello World!").EndText().EndMarkedContent().RestoreState().CloseTag
+ ();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_validRoleAddedInsideMarkedContent.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_validRoleAddedInsideMarkedContentMultiple() {
+ String outPdf = DESTINATION_FOLDER + "validRoleAddedInsideMarkedContentMultiple.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfPage page1 = pdfDoc.AddNewPage();
+ PdfCanvas canvas = new PdfCanvas(page1);
+ //Have to use low level tagging otherwise it throws error earlier
+ PdfStructElem doc = pdfDoc.GetStructTreeRoot().AddKid(new PdfStructElem(pdfDoc, PdfName.Document));
+ PdfStructElem paragraph = doc.AddKid(new PdfStructElem(pdfDoc, PdfName.P, page1));
+ PdfMcr mcr = paragraph.AddKid(new PdfMcrNumber(page1, paragraph));
+ canvas.OpenTag(new CanvasTag(mcr)).SaveState().BeginMarkedContent(PdfName.P).BeginText().SetFontAndSize(font
+ , 12).MoveText(200, 200).ShowText("Hello World!").EndText().EndMarkedContent().BeginMarkedContent(PdfName
+ .H1).BeginText().ShowText("Hello but nested").EndText().EndMarkedContent().RestoreState().CloseTag();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_validRoleAddedInsideMarkedContentMultiple.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_validRoleAddedInsideMarkedContentMCR_IN_MCR() {
+ String outPdf = DESTINATION_FOLDER + "validRoleAddedInsideMarkedContentMCR_IN_MCR.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfPage page1 = pdfDoc.AddNewPage();
+ PdfCanvas canvas = new PdfCanvas(page1);
+ PdfStructElem doc = pdfDoc.GetStructTreeRoot().AddKid(new PdfStructElem(pdfDoc, PdfName.Document));
+ PdfStructElem paragraph = doc.AddKid(new PdfStructElem(pdfDoc, PdfName.P, page1));
+ PdfStructElem paragraph2 = doc.AddKid(new PdfStructElem(pdfDoc, PdfName.P, page1));
+ PdfMcr mcr = paragraph.AddKid(new PdfMcrNumber(page1, paragraph));
+ PdfMcr mcr1 = paragraph2.AddKid(new PdfMcrNumber(page1, paragraph2));
+ canvas.OpenTag(new CanvasTag(mcr)).SaveState().BeginMarkedContent(PdfName.P).BeginText().SetFontAndSize(font
+ , 12).MoveText(200, 200).ShowText("Hello World!").EndText().EndMarkedContent().OpenTag(new CanvasTag(mcr1
+ )).BeginMarkedContent(PdfName.H1).BeginText().ShowText("Hello but nested").EndText().EndMarkedContent(
+ ).CloseTag().RestoreState().CloseTag();
+ pdfDoc.Close();
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_validRoleAddedInsideMarkedContentMCR_IN_MCR.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_01_004_TaggedContentShouldNotBeInsideArtifact() {
+ String outPdf = DESTINATION_FOLDER + "01_004_TaggedContentShouldNotBeInsideArtifact.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ PdfPage page1 = pdfDoc.AddNewPage();
+ PdfCanvas canvas = new PdfCanvas(page1);
+ TagTreePointer tagPointer = new TagTreePointer(pdfDoc).SetPageForTagging(page1).AddTag(StandardRoles.P);
+ canvas.OpenTag(new CanvasTag(PdfName.Artifact)).SaveState().BeginText().SetFontAndSize(font, 12).MoveText(
+ 200, 200).ShowText("Hello World!").EndText();
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => {
+ canvas.OpenTag(tagPointer.GetTagReference());
+ }
+ );
+ NUnit.Framework.Assert.AreEqual(PdfUAExceptionMessageConstants.REAL_CONTENT_CANT_BE_INSIDE_ARTIFACT, e.Message
+ );
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint_31_009_FontIsNotEmbedded() {
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(new MemoryStream(), PdfUATestPdfDocument
+ .CreateWriterProperties()));
+ PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
+ PdfFont font = PdfFontFactory.CreateFont(StandardFonts.COURIER);
+ TagTreePointer tagPointer = new TagTreePointer(pdfDoc);
+ tagPointer.SetPageForTagging(pdfDoc.GetFirstPage());
+ tagPointer.AddTag(StandardRoles.P);
+ canvas.BeginText().OpenTag(tagPointer.GetTagReference()).SetFontAndSize(font, 12).ShowText("Please crash on close, tyvm"
+ ).EndText().CloseTag();
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => {
+ pdfDoc.Close();
+ }
+ );
+ NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(PdfUAExceptionMessageConstants.FONT_SHOULD_BE_EMBEDDED
+ , "Courier"), e.Message);
+ }
+ }
+}
diff --git a/itext.tests/itext.pdfua.tests/itext/pdfua/PdfUALayoutTest.cs b/itext.tests/itext.pdfua.tests/itext/pdfua/PdfUALayoutTest.cs
new file mode 100644
index 0000000000..f496d10de6
--- /dev/null
+++ b/itext.tests/itext.pdfua.tests/itext/pdfua/PdfUALayoutTest.cs
@@ -0,0 +1,108 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2023 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 iText.IO.Font;
+using iText.Kernel.Colors;
+using iText.Kernel.Font;
+using iText.Kernel.Geom;
+using iText.Kernel.Pdf;
+using iText.Kernel.Pdf.Canvas;
+using iText.Kernel.Utils;
+using iText.Layout;
+using iText.Layout.Borders;
+using iText.Layout.Element;
+using iText.Test;
+using iText.Test.Pdfa;
+
+namespace iText.Pdfua {
+ [NUnit.Framework.Category("IntegrationTest")]
+ public class PdfUALayoutTest : ExtendedITextTest {
+ private static readonly String DESTINATION_FOLDER = NUnit.Framework.TestContext.CurrentContext.TestDirectory
+ + "/test/itext/pdfua/PdfUALayoutTest/";
+
+ private static readonly String SOURCE_FOLDER = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext
+ .CurrentContext.TestDirectory) + "/resources/itext/pdfua/PdfUALayoutTest/";
+
+ private static readonly String FONT = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext
+ .CurrentContext.TestDirectory) + "/resources/itext/pdfua/font/FreeSans.ttf";
+
+ [NUnit.Framework.OneTimeSetUp]
+ public static void Before() {
+ CreateOrClearDestinationFolder(DESTINATION_FOLDER);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void SimpleParagraphTest() {
+ String outPdf = DESTINATION_FOLDER + "simpleParagraphTest.pdf";
+ String cmpPdf = SOURCE_FOLDER + "cmp_simpleParagraphTest.pdf";
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ Document doc = new Document(pdfDoc);
+ doc.Add(new Paragraph("Simple layout PDF/UA-1 test").SetFont(font));
+ doc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, cmpPdf, DESTINATION_FOLDER, "diff_"
+ ));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void SimpleBorderTest() {
+ String outPdf = DESTINATION_FOLDER + "simpleBorderTest.pdf";
+ String cmpPdf = SOURCE_FOLDER + "cmp_simpleBorderTest.pdf";
+ using (PdfDocument pdfDocument = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()))) {
+ PdfPage page = pdfDocument.AddNewPage();
+ PdfCanvas canvas = new PdfCanvas(page);
+ canvas.OpenTag(new CanvasTag(PdfName.Artifact));
+ new DottedBorder(DeviceRgb.GREEN, 5).Draw(canvas, new Rectangle(350, 700, 100, 100));
+ canvas.CloseTag();
+ }
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, cmpPdf, DESTINATION_FOLDER, "diff"
+ ));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void SimpleTableTest() {
+ String outPdf = DESTINATION_FOLDER + "simpleTableTest.pdf";
+ String cmpPdf = SOURCE_FOLDER + "cmp_simpleTableTest.pdf";
+ PdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()));
+ Document doc = new Document(pdfDoc);
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED
+ );
+ Table table = new Table(new float[] { 50, 50 }).AddCell(new Cell().Add(new Paragraph("cell 1, 1").SetFont(
+ font))).AddCell(new Cell().Add(new Paragraph("cell 1, 2").SetFont(font)));
+ doc.Add(table);
+ doc.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, cmpPdf, DESTINATION_FOLDER, "diff_"
+ ));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ }
+}
diff --git a/itext.tests/itext.pdfua.tests/itext/pdfua/PdfUATest.cs b/itext.tests/itext.pdfua.tests/itext/pdfua/PdfUATest.cs
new file mode 100644
index 0000000000..12b7e2651c
--- /dev/null
+++ b/itext.tests/itext.pdfua.tests/itext/pdfua/PdfUATest.cs
@@ -0,0 +1,155 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2023 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.IO.Font;
+using iText.IO.Image;
+using iText.Kernel.Font;
+using iText.Kernel.Geom;
+using iText.Kernel.Pdf;
+using iText.Kernel.Utils;
+using iText.Layout;
+using iText.Layout.Element;
+using iText.Pdfua.Exceptions;
+using iText.Test;
+using iText.Test.Pdfa;
+
+namespace iText.Pdfua {
+ [NUnit.Framework.Category("IntegrationTest")]
+ public class PdfUATest : ExtendedITextTest {
+ private static readonly String DESTINATION_FOLDER = NUnit.Framework.TestContext.CurrentContext.TestDirectory
+ + "/test/itext/pdfua/PdfUATest/";
+
+ private static readonly String SOURCE_FOLDER = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext
+ .CurrentContext.TestDirectory) + "/resources/itext/pdfua/PdfUATest/";
+
+ private static readonly String DOG = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext
+ .CurrentContext.TestDirectory) + "/resources/itext/pdfua/img/DOG.bmp";
+
+ private static readonly String FONT = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext
+ .CurrentContext.TestDirectory) + "/resources/itext/pdfua/font/FreeSans.ttf";
+
+ private static readonly String FOX = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext
+ .CurrentContext.TestDirectory) + "/resources/itext/pdfua/img/FOX.bmp";
+
+ [NUnit.Framework.OneTimeSetUp]
+ public static void Before() {
+ CreateOrClearDestinationFolder(DESTINATION_FOLDER);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint01_007_suspectsHasEntryTrue() {
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(new MemoryStream(), PdfUATestPdfDocument
+ .CreateWriterProperties()));
+ PdfDictionary markInfo = (PdfDictionary)pdfDoc.GetCatalog().GetPdfObject().Get(PdfName.MarkInfo);
+ NUnit.Framework.Assert.IsNotNull(markInfo);
+ markInfo.Put(PdfName.Suspects, new PdfBoolean(true));
+ Exception e = NUnit.Framework.Assert.Catch(typeof(PdfUAConformanceException), () => pdfDoc.Close());
+ NUnit.Framework.Assert.AreEqual(PdfUAExceptionMessageConstants.SUSPECTS_ENTRY_IN_MARK_INFO_DICTIONARY_SHALL_NOT_HAVE_A_VALUE_OF_TRUE
+ , e.Message);
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint01_007_suspectsHasEntryFalse() {
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(new MemoryStream(), PdfUATestPdfDocument
+ .CreateWriterProperties()));
+ PdfDictionary markInfo = (PdfDictionary)pdfDoc.GetCatalog().GetPdfObject().Get(PdfName.MarkInfo);
+ markInfo.Put(PdfName.Suspects, new PdfBoolean(false));
+ NUnit.Framework.Assert.DoesNotThrow(() => pdfDoc.Close());
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void CheckPoint01_007_suspectsHasNoEntry() {
+ // suspects entry is optional so it is ok to not have it according to the spec
+ PdfUATestPdfDocument pdfDoc = new PdfUATestPdfDocument(new PdfWriter(new MemoryStream(), PdfUATestPdfDocument
+ .CreateWriterProperties()));
+ NUnit.Framework.Assert.DoesNotThrow(() => pdfDoc.Close());
+ }
+
+ [NUnit.Framework.Test]
+ public virtual void EmptyPageDocument() {
+ String outPdf = DESTINATION_FOLDER + "emptyPageDocument.pdf";
+ using (PdfDocument pdfDocument = new PdfUATestPdfDocument(new PdfWriter(outPdf, PdfUATestPdfDocument.CreateWriterProperties
+ ()))) {
+ pdfDocument.AddNewPage();
+ }
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_emptyPageDocument.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ [NUnit.Framework.Test]
+ public virtual void ManualPdfUaCreation() {
+ String outPdf = DESTINATION_FOLDER + "manualPdfUaCreation.pdf";
+ PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outPdf, new WriterProperties().AddUAXmpMetadata().SetPdfVersion
+ (PdfVersion.PDF_1_7)));
+ Document document = new Document(pdfDoc, PageSize.A4.Rotate());
+ //TAGGED PDF
+ //Make document tagged
+ pdfDoc.SetTagged();
+ //PDF/UA
+ //Set document metadata
+ pdfDoc.GetCatalog().SetViewerPreferences(new PdfViewerPreferences().SetDisplayDocTitle(true));
+ pdfDoc.GetCatalog().SetLang(new PdfString("en-US"));
+ PdfDocumentInfo info = pdfDoc.GetDocumentInfo();
+ info.SetTitle("English pangram");
+ Paragraph p = new Paragraph();
+ //PDF/UA
+ //Embed font
+ PdfFont font = PdfFontFactory.CreateFont(FONT, PdfEncodings.WINANSI, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED
+ );
+ p.SetFont(font);
+ p.Add("The quick brown ");
+ iText.Layout.Element.Image img = new Image(ImageDataFactory.Create(FOX));
+ //PDF/UA
+ //Set alt text
+ img.GetAccessibilityProperties().SetAlternateDescription("Fox");
+ p.Add(img);
+ p.Add(" jumps over the lazy ");
+ img = new iText.Layout.Element.Image(ImageDataFactory.Create(DOG));
+ //PDF/UA
+ //Set alt text
+ img.GetAccessibilityProperties().SetAlternateDescription("Dog");
+ p.Add(img);
+ document.Add(p);
+ p = new Paragraph("\n\n\n\n\n\n\n\n\n\n\n\n").SetFont(font).SetFontSize(20);
+ document.Add(p);
+ List list = new List().SetFont(font).SetFontSize(20);
+ list.Add(new ListItem("quick"));
+ list.Add(new ListItem("brown"));
+ list.Add(new ListItem("fox"));
+ list.Add(new ListItem("jumps"));
+ list.Add(new ListItem("over"));
+ list.Add(new ListItem("the"));
+ list.Add(new ListItem("lazy"));
+ list.Add(new ListItem("dog"));
+ document.Add(list);
+ document.Close();
+ NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, SOURCE_FOLDER + "cmp_manualPdfUaCreation.pdf"
+ , DESTINATION_FOLDER, "diff_"));
+ NUnit.Framework.Assert.IsNull(new UAVeraPdfValidator().Validate(outPdf));
+ }
+ // Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf/ua validation on Android)
+ }
+}
diff --git a/itext.tests/itext.pdfua.tests/itext/pdfua/PdfUATestPdfDocument.cs b/itext.tests/itext.pdfua.tests/itext/pdfua/PdfUATestPdfDocument.cs
new file mode 100644
index 0000000000..6f7cd886f0
--- /dev/null
+++ b/itext.tests/itext.pdfua.tests/itext/pdfua/PdfUATestPdfDocument.cs
@@ -0,0 +1,81 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2023 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 iText.Kernel.Pdf;
+using iText.Kernel.Utils;
+using iText.Pdfua.Checkers;
+
+namespace iText.Pdfua {
+ /// PdfDocument extension for testing purposes.
+ public class PdfUATestPdfDocument : PdfDocument {
+ private readonly IConformanceLevel conformanceLevel = PdfUAConformanceLevel.PDFUA_1;
+
+ public PdfUATestPdfDocument(PdfReader reader)
+ : this(reader, new DocumentProperties()) {
+ }
+
+ public PdfUATestPdfDocument(PdfReader reader, DocumentProperties properties)
+ : base(reader, properties) {
+ SetupUAConfiguration();
+ }
+
+ public PdfUATestPdfDocument(PdfWriter writer)
+ : this(writer, new DocumentProperties()) {
+ }
+
+ public PdfUATestPdfDocument(PdfWriter writer, DocumentProperties properties)
+ : base(writer, properties) {
+ SetupUAConfiguration();
+ }
+
+ public PdfUATestPdfDocument(PdfReader reader, PdfWriter writer)
+ : base(reader, writer) {
+ SetupUAConfiguration();
+ }
+
+ public static WriterProperties CreateWriterProperties() {
+ return new WriterProperties().AddUAXmpMetadata();
+ }
+
+ public PdfUATestPdfDocument(PdfReader reader, PdfWriter writer, StampingProperties properties)
+ : base(reader, writer, properties) {
+ }
+
+ /// {inheritDoc}
+ public override IConformanceLevel GetConformanceLevel() {
+ return conformanceLevel;
+ }
+
+ private void SetupUAConfiguration() {
+ //basic configuration
+ this.SetTagged();
+ this.GetCatalog().SetViewerPreferences(new PdfViewerPreferences().SetDisplayDocTitle(true));
+ this.GetCatalog().SetLang(new PdfString("en-US"));
+ PdfDocumentInfo info = this.GetDocumentInfo();
+ info.SetTitle("English pangram");
+ //validation
+ ValidationContainer validationContainer = new ValidationContainer();
+ validationContainer.AddChecker(new PdfUA1Checker(this));
+ this.GetDiContainer().Register(typeof(ValidationContainer), validationContainer);
+ }
+ }
+}
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_003_ContentMarkedAsArtifactsPresentInsideTaggedContent.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_003_ContentMarkedAsArtifactsPresentInsideTaggedContent.pdf
new file mode 100644
index 0000000000..be27b91d32
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_003_ContentMarkedAsArtifactsPresentInsideTaggedContent.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_003_ContentMarkedAsArtifactsPresentInsideTaggedContentMultiple.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_003_ContentMarkedAsArtifactsPresentInsideTaggedContentMultiple.pdf
new file mode 100644
index 0000000000..af11deb43a
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_003_ContentMarkedAsArtifactsPresentInsideTaggedContentMultiple.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_004_bezierCurveShouldBeTagged.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_004_bezierCurveShouldBeTagged.pdf
new file mode 100644
index 0000000000..c80d0884fc
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_004_bezierCurveShouldBeTagged.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_004_bezierMarkedAsArtifact.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_004_bezierMarkedAsArtifact.pdf
new file mode 100644
index 0000000000..190f03dfd4
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_004_bezierMarkedAsArtifact.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_LineContentThatIsMarkedAsArtifact.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_LineContentThatIsMarkedAsArtifact.pdf
new file mode 100644
index 0000000000..42c415fc25
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_LineContentThatIsMarkedAsArtifact.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_RandomOperationsWithoutActuallyAddingContent.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_RandomOperationsWithoutActuallyAddingContent.pdf
new file mode 100644
index 0000000000..420e71c01b
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_RandomOperationsWithoutActuallyAddingContent.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_RectangleMarkedArtifact.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_RectangleMarkedArtifact.pdf
new file mode 100644
index 0000000000..d7bcea1d4b
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_RectangleMarkedArtifact.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_RectangleMarkedContent.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_RectangleMarkedContent.pdf
new file mode 100644
index 0000000000..03c51fd650
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_RectangleMarkedContent.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextArtifactIsNotInTagTree.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextArtifactIsNotInTagTree.pdf
new file mode 100644
index 0000000000..17ddb95af9
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextArtifactIsNotInTagTree.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextContentIsCorrectlyTaggedAsContent.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextContentIsCorrectlyTaggedAsContent.pdf
new file mode 100644
index 0000000000..21483ca758
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextContentIsCorrectlyTaggedAsContent.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextContentIsNotInTagTree.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextContentIsNotInTagTree.pdf
new file mode 100644
index 0000000000..f7d58f9479
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextContentIsNotInTagTree.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextGlyphLineContentIsArtifact.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextGlyphLineContentIsArtifact.pdf
new file mode 100644
index 0000000000..df099016c8
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextGlyphLineContentIsArtifact.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextGlyphLineContentIsContentCorrect.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextGlyphLineContentIsContentCorrect.pdf
new file mode 100644
index 0000000000..6b314072c2
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_TextGlyphLineContentIsContentCorrect.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_allowNestedPureBmcInArtifact.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_allowNestedPureBmcInArtifact.pdf
new file mode 100644
index 0000000000..f7b60a3ac6
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_allowNestedPureBmcInArtifact.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_allowPureBmcInArtifact.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_allowPureBmcInArtifact.pdf
new file mode 100644
index 0000000000..58dac3ee48
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_01_005_allowPureBmcInArtifact.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_validRoleAddedInsideMarkedContent.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_validRoleAddedInsideMarkedContent.pdf
new file mode 100644
index 0000000000..96600e44bf
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_validRoleAddedInsideMarkedContent.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_validRoleAddedInsideMarkedContentMCR_IN_MCR.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_validRoleAddedInsideMarkedContentMCR_IN_MCR.pdf
new file mode 100644
index 0000000000..18cd5abf13
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_validRoleAddedInsideMarkedContentMCR_IN_MCR.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_validRoleAddedInsideMarkedContentMultiple.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_validRoleAddedInsideMarkedContentMultiple.pdf
new file mode 100644
index 0000000000..a6b19404d5
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUACanvasTest/cmp_validRoleAddedInsideMarkedContentMultiple.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUALayoutTest/cmp_simpleBorderTest.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUALayoutTest/cmp_simpleBorderTest.pdf
new file mode 100644
index 0000000000..4f00f15084
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUALayoutTest/cmp_simpleBorderTest.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUALayoutTest/cmp_simpleParagraphTest.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUALayoutTest/cmp_simpleParagraphTest.pdf
new file mode 100644
index 0000000000..a3963f0cfe
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUALayoutTest/cmp_simpleParagraphTest.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUALayoutTest/cmp_simpleTableTest.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUALayoutTest/cmp_simpleTableTest.pdf
new file mode 100644
index 0000000000..fbd68c88a3
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUALayoutTest/cmp_simpleTableTest.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUATest/cmp_emptyPageDocument.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUATest/cmp_emptyPageDocument.pdf
new file mode 100644
index 0000000000..5f068de98d
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUATest/cmp_emptyPageDocument.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUATest/cmp_manualPdfUaCreation.pdf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUATest/cmp_manualPdfUaCreation.pdf
new file mode 100644
index 0000000000..db16029824
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/PdfUATest/cmp_manualPdfUaCreation.pdf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/font/FreeSans.ttf b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/font/FreeSans.ttf
new file mode 100644
index 0000000000..4b06bd7822
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/font/FreeSans.ttf differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/img/DOG.bmp b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/img/DOG.bmp
new file mode 100644
index 0000000000..efc64911b9
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/img/DOG.bmp differ
diff --git a/itext.tests/itext.pdfua.tests/resources/itext/pdfua/img/FOX.bmp b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/img/FOX.bmp
new file mode 100644
index 0000000000..4ea0ae2cc1
Binary files /dev/null and b/itext.tests/itext.pdfua.tests/resources/itext/pdfua/img/FOX.bmp differ
diff --git a/itext/itext.pdfua/itext/pdfua/checkers/PdfUA1Checker.cs b/itext/itext.pdfua/itext/pdfua/checkers/PdfUA1Checker.cs
new file mode 100644
index 0000000000..bff5c81b48
--- /dev/null
+++ b/itext/itext.pdfua/itext/pdfua/checkers/PdfUA1Checker.cs
@@ -0,0 +1,176 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2023 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.Collections.Generic;
+using iText.Commons.Datastructures;
+using iText.Commons.Utils;
+using iText.Kernel.Font;
+using iText.Kernel.Pdf;
+using iText.Kernel.Pdf.Tagging;
+using iText.Kernel.Utils;
+using iText.Pdfua.Exceptions;
+
+namespace iText.Pdfua.Checkers {
+ /// The class defines the requirements of the PDF/UA-1 standard.
+ ///
+ /// The class defines the requirements of the PDF/UA-1 standard.
+ ///
+ /// The specification implemented by this class is ISO 14289-1
+ ///
+ public class PdfUA1Checker : IValidationChecker {
+ private readonly PdfDocument pdfDocument;
+
+ /// Creates PdfUA1Checker instance with PDF document which will be validated against PDF/UA-1 standard.
+ ///
+ /// the document to validate
+ public PdfUA1Checker(PdfDocument pdfDocument) {
+ this.pdfDocument = pdfDocument;
+ }
+
+ ///
+ public virtual void ValidateDocument(ValidationContext validationContext) {
+ CheckCatalog(validationContext.GetPdfDocument().GetCatalog());
+ CheckFonts(validationContext.GetFonts());
+ }
+
+ ///
+ public virtual void ValidateObject(Object obj, IsoKey key, PdfResources resources, PdfStream contentStream
+ , Object extra) {
+ switch (key) {
+ case IsoKey.CANVAS_WRITING_CONTENT: {
+ CheckOnWritingCanvasToContent(obj);
+ break;
+ }
+
+ case IsoKey.CANVAS_BEGIN_MARKED_CONTENT: {
+ CheckOnOpeningBeginMarkedContent(obj, extra);
+ break;
+ }
+ }
+ }
+
+ private void CheckOnWritingCanvasToContent(Object data) {
+ Stack> tagStack = GetTagStack(data);
+ if (tagStack.IsEmpty()) {
+ throw new PdfUAConformanceException(PdfUAExceptionMessageConstants.TAG_HASNT_BEEN_ADDED_BEFORE_CONTENT_ADDING
+ );
+ }
+ bool insideRealContent = IsInsideRealContent(tagStack);
+ bool insideArtifact = IsInsideArtifact(tagStack);
+ if (insideRealContent && insideArtifact) {
+ throw new PdfUAConformanceException(PdfUAExceptionMessageConstants.REAL_CONTENT_INSIDE_ARTIFACT_OR_VICE_VERSA
+ );
+ }
+ else {
+ if (!insideRealContent && !insideArtifact) {
+ throw new PdfUAConformanceException(PdfUAExceptionMessageConstants.CONTENT_IS_NOT_REAL_CONTENT_AND_NOT_ARTIFACT
+ );
+ }
+ }
+ }
+
+ private Stack> GetTagStack(Object data) {
+ return (Stack>)data;
+ }
+
+ private void CheckOnOpeningBeginMarkedContent(Object obj, Object extra) {
+ Stack> stack = GetTagStack(obj);
+ if (stack.IsEmpty()) {
+ return;
+ }
+ Tuple2 currentBmc = (Tuple2)extra;
+ bool isRealContent = IsRealContent(currentBmc);
+ bool isArtifact = PdfName.Artifact.Equals(currentBmc.GetFirst());
+ if (isArtifact && IsInsideRealContent(stack)) {
+ throw new PdfUAConformanceException(PdfUAExceptionMessageConstants.ARTIFACT_CANT_BE_INSIDE_REAL_CONTENT);
+ }
+ if (isRealContent && IsInsideArtifact(stack)) {
+ throw new PdfUAConformanceException(PdfUAExceptionMessageConstants.REAL_CONTENT_CANT_BE_INSIDE_ARTIFACT);
+ }
+ }
+
+ private bool IsInsideArtifact(Stack> tagStack) {
+ foreach (Tuple2 tag in tagStack) {
+ if (PdfName.Artifact.Equals(tag.GetFirst())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private bool IsInsideRealContent(Stack> tagStack) {
+ foreach (Tuple2 tag in tagStack) {
+ if (IsRealContent(tag)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private bool IsRealContent(Tuple2 tag) {
+ if (PdfName.Artifact.Equals(tag.GetFirst())) {
+ return false;
+ }
+ PdfDictionary properties = tag.GetSecond();
+ if (properties == null || !properties.ContainsKey(PdfName.MCID)) {
+ return false;
+ }
+ PdfMcr mcr = this.pdfDocument.GetStructTreeRoot().FindMcrByMcid(pdfDocument, (int)properties.GetAsInt(PdfName
+ .MCID));
+ if (mcr == null) {
+ throw new PdfUAConformanceException(PdfUAExceptionMessageConstants.CONTENT_WITH_MCID_BUT_MCID_NOT_FOUND_IN_STRUCT_TREE_ROOT
+ );
+ }
+ return true;
+ }
+
+ private void CheckCatalog(PdfCatalog catalog) {
+ PdfDictionary catalogDict = catalog.GetPdfObject();
+ if (!catalogDict.ContainsKey(PdfName.Metadata)) {
+ throw new PdfUAConformanceException(PdfUAExceptionMessageConstants.METADATA_SHALL_BE_PRESENT_IN_THE_CATALOG_DICTIONARY
+ );
+ }
+ PdfDictionary markInfo = catalogDict.GetAsDictionary(PdfName.MarkInfo);
+ if (markInfo != null && markInfo.ContainsKey(PdfName.Suspects)) {
+ PdfBoolean markInfoSuspects = markInfo.GetAsBoolean(PdfName.Suspects);
+ if (markInfoSuspects != null && markInfoSuspects.GetValue()) {
+ throw new PdfUAConformanceException(PdfUAExceptionMessageConstants.SUSPECTS_ENTRY_IN_MARK_INFO_DICTIONARY_SHALL_NOT_HAVE_A_VALUE_OF_TRUE
+ );
+ }
+ }
+ }
+
+ private void CheckFonts(ICollection fontsInDocument) {
+ ICollection fontNamesThatAreNotEmbedded = new HashSet();
+ foreach (PdfFont font in fontsInDocument) {
+ if (!font.IsEmbedded()) {
+ fontNamesThatAreNotEmbedded.Add(font.GetFontProgram().GetFontNames().GetFontName());
+ }
+ }
+ if (!fontNamesThatAreNotEmbedded.IsEmpty()) {
+ throw new PdfUAConformanceException(MessageFormatUtil.Format(PdfUAExceptionMessageConstants.FONT_SHOULD_BE_EMBEDDED
+ , String.Join(", ", fontNamesThatAreNotEmbedded)));
+ }
+ }
+ }
+}
diff --git a/itext/itext.pdfua/itext/pdfua/exceptions/PdfUAConformanceException.cs b/itext/itext.pdfua/itext/pdfua/exceptions/PdfUAConformanceException.cs
new file mode 100644
index 0000000000..533bb1ebcd
--- /dev/null
+++ b/itext/itext.pdfua/itext/pdfua/exceptions/PdfUAConformanceException.cs
@@ -0,0 +1,35 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2023 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 iText.Kernel.Exceptions;
+
+namespace iText.Pdfua.Exceptions {
+ /// Exception that is thrown when the PDF Document doesn't adhere to the PDF/UA specification.
+ public class PdfUAConformanceException : PdfException {
+ /// Creates a PdfUAConformanceException.
+ /// the error message
+ public PdfUAConformanceException(String message)
+ : base(message) {
+ }
+ }
+}
diff --git a/itext/itext.pdfua/itext/pdfua/exceptions/PdfUAExceptionMessageConstants.cs b/itext/itext.pdfua/itext/pdfua/exceptions/PdfUAExceptionMessageConstants.cs
new file mode 100644
index 0000000000..16f9a7d527
--- /dev/null
+++ b/itext/itext.pdfua/itext/pdfua/exceptions/PdfUAExceptionMessageConstants.cs
@@ -0,0 +1,50 @@
+/*
+This file is part of the iText (R) project.
+Copyright (c) 1998-2023 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;
+
+namespace iText.Pdfua.Exceptions {
+ /// Class that bundles all the error message templates as constants.
+ public sealed class PdfUAExceptionMessageConstants {
+ public const String TAG_HASNT_BEEN_ADDED_BEFORE_CONTENT_ADDING = "Tag hasn't been added before adding content to the canvas.";
+
+ public const String CONTENT_IS_NOT_REAL_CONTENT_AND_NOT_ARTIFACT = "Content is neither marked as Artifact nor tagged as real content.";
+
+ public const String ARTIFACT_CANT_BE_INSIDE_REAL_CONTENT = "Content marked as artifact may only reside in Artifact content.";
+
+ public const String REAL_CONTENT_CANT_BE_INSIDE_ARTIFACT = "Content marked as content may not reside in Artifact content.";
+
+ public const String CONTENT_WITH_MCID_BUT_MCID_NOT_FOUND_IN_STRUCT_TREE_ROOT = "Content with MCID, but MCID wasn't found in StructTreeRoot.";
+
+ public const String FONT_SHOULD_BE_EMBEDDED = "Following font(s) are not embedded: {0}";
+
+ public const String METADATA_SHALL_BE_PRESENT_IN_THE_CATALOG_DICTIONARY = "Metadata shall be present in the catalog dictionary";
+
+ public const String REAL_CONTENT_INSIDE_ARTIFACT_OR_VICE_VERSA = "Tagged content is present inside content marked as Artifact or vice versa.";
+
+ public const String SUSPECTS_ENTRY_IN_MARK_INFO_DICTIONARY_SHALL_NOT_HAVE_A_VALUE_OF_TRUE = "Suspects entry in mark info dictionary shall not have a value of true.";
+
+ private PdfUAExceptionMessageConstants() {
+ }
+ // Empty constructor
+ }
+}