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 + } +}