Selaa lähdekoodia

Enhance semantic tagging support by introducing conditional management of SemanticTreeManager and updating content configuration methods

Marcin Ziąbek 2 kuukautta sitten
vanhempi
sitoutus
edb884bc8d

+ 44 - 23
Source/QuestPDF/Drawing/DocumentGenerator.cs

@@ -6,12 +6,13 @@ using QuestPDF.Drawing.DocumentCanvases;
 using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Drawing.Proxy;
 using QuestPDF.Elements;
-using QuestPDF.Elements.Table;
 using QuestPDF.Elements.Text;
 using QuestPDF.Elements.Text.Items;
 using QuestPDF.Helpers;
 using QuestPDF.Infrastructure;
 using QuestPDF.Skia;
+using PDFA_Conformance = QuestPDF.Infrastructure.PDFA_Conformance;
+using PDFUA_Conformance = QuestPDF.Infrastructure.PDFUA_Conformance;
 
 namespace QuestPDF.Drawing
 {
@@ -110,26 +111,20 @@ namespace QuestPDF.Drawing
             }
             
             // TODO: handle Header nesting values
-            
+            var semanticTreeManager = CreateSemanticTreeManager(settings);
             var useOriginalImages = canvas is ImageDocumentCanvas;
-
-            var content = ConfigureContent(document, settings, useOriginalImages);
+            var content = ConfigureContent(document, settings, semanticTreeManager, useOriginalImages);
             
             if (canvas is CompanionDocumentCanvas)
                 content.VisitChildren(x => x.CreateProxy(y => new LayoutProxy(y)));
             
             try
             {
-                var semanticTreeManager = new SemanticTreeManager();
-                content.InjectSemanticTreeManager(semanticTreeManager);
-                
                 var pageContext = new PageContext();
                 RenderPass(pageContext, new FreeDocumentCanvas(), content);
                 pageContext.ProceedToNextRenderingPhase();
-                
-                var semanticTree = semanticTreeManager.GetSemanticTree();
-                semanticTreeManager.Reset();
-                canvas.SetSemanticTree(semanticTree);
+
+                canvas.ConfigureWithSemanticTree(semanticTreeManager);
                 
                 canvas.BeginDocument();
                 RenderPass(pageContext, canvas, content);
@@ -150,36 +145,31 @@ namespace QuestPDF.Drawing
             
             var sharedPageContent = new PageContext();
             var useSharedPageContext = document.PageNumberStrategy == MergedDocumentPageNumberStrategy.Continuous;
+
+            var semanticTreeManager = CreateSemanticTreeManager(settings);
             
             var documentParts = Enumerable
                 .Range(0, document.Documents.Count)
                 .Select(index => new
                 {
                     DocumentId = index,
-                    Content = ConfigureContent(document.Documents[index], settings, useOriginalImages),
+                    Content = ConfigureContent(document.Documents[index], settings, semanticTreeManager, useOriginalImages),
                     PageContext = useSharedPageContext ? sharedPageContent : new PageContext()
                 })
                 .ToList();
             
             try
             {
-                var semanticTreeManager = new SemanticTreeManager();
-
                 foreach (var documentPart in documentParts)
-                {
-                    documentPart.Content.InjectSemanticTreeManager(semanticTreeManager);
                     documentPart.PageContext.SetDocumentId(documentPart.DocumentId);
-                }
                 
                 foreach (var documentPart in documentParts)
                 {
                     RenderPass(documentPart.PageContext, new FreeDocumentCanvas(), documentPart.Content);
                     documentPart.PageContext.ProceedToNextRenderingPhase();
                 }
-                
-                var semanticTree = semanticTreeManager.GetSemanticTree();
-                semanticTreeManager.Reset();
-                canvas.SetSemanticTree(semanticTree);
+
+                canvas.ConfigureWithSemanticTree(semanticTreeManager);
                 
                 canvas.BeginDocument();
 
@@ -197,7 +187,33 @@ namespace QuestPDF.Drawing
             }
         }
 
-        private static Container ConfigureContent(IDocument document, DocumentSettings settings, bool useOriginalImages)
+        private static SemanticTreeManager? CreateSemanticTreeManager(DocumentSettings settings)
+        {
+            return IsDocumentSemanticAware() ? new SemanticTreeManager() : null;
+
+            bool IsDocumentSemanticAware()
+            {
+                if (settings.PDFUA_Conformance is not PDFUA_Conformance.None)
+                    return true;
+                
+                if (settings.PDFA_Conformance is PDFA_Conformance.PDFA_1A or PDFA_Conformance.PDFA_2A or PDFA_Conformance.PDFA_3A)
+                    return true;
+
+                return false;
+            }
+        }
+
+        private static void ConfigureWithSemanticTree(this IDocumentCanvas canvas, SemanticTreeManager? semanticTreeManager)
+        {
+            if (semanticTreeManager == null) 
+                return;
+            
+            var semanticTree = semanticTreeManager.GetSemanticTree();
+            semanticTreeManager.Reset();
+            canvas.SetSemanticTree(semanticTree);
+        }
+
+        private static Container ConfigureContent(IDocument document, DocumentSettings settings, SemanticTreeManager? semanticTreeManager, bool useOriginalImages)
         {
             var container = new DocumentContainer();
             document.Compose(container);
@@ -207,11 +223,16 @@ namespace QuestPDF.Drawing
             content.ApplyInheritedAndGlobalTexStyle(TextStyle.Default);
             content.ApplyContentDirection(settings.ContentDirection);
             content.ApplyDefaultImageConfiguration(settings.ImageRasterDpi, settings.ImageCompressionQuality, useOriginalImages);
-            content.ApplySemanticParagraphs();
 
             if (Settings.EnableCaching)
                 content.ApplyCaching();
             
+            if (semanticTreeManager != null)
+            {
+                content.InjectSemanticTreeManager(semanticTreeManager);
+                content.ApplySemanticParagraphs();
+            }
+            
             return content;
         }
 

+ 7 - 1
Source/QuestPDF/Elements/ArtifactTag.cs

@@ -5,12 +5,18 @@ namespace QuestPDF.Elements;
 
 internal class ArtifactTag : ContainerElement, ISemanticAware
 {
-    public SemanticTreeManager SemanticTreeManager { get; set; }
+    public SemanticTreeManager? SemanticTreeManager { get; set; }
     
     public int Id { get; set; }
     
     internal override void Draw(Size availableSpace)
     {
+        if (SemanticTreeManager == null)
+        {
+            base.Draw(availableSpace);
+            return;       
+        }
+        
         Canvas.SetSemanticNodeId(Id);
         
         SemanticTreeManager.BeginArtifactContent();

+ 8 - 5
Source/QuestPDF/Elements/Dynamic.cs

@@ -9,7 +9,7 @@ namespace QuestPDF.Elements
 {
     internal sealed class DynamicHost : Element, IStateful, IContentDirectionAware, ISemanticAware
     {
-        public SemanticTreeManager SemanticTreeManager { get; set; }
+        public SemanticTreeManager? SemanticTreeManager { get; set; }
         
         private DynamicComponentProxy Child { get; }
         private object InitialComponentState { get; set; }
@@ -118,7 +118,7 @@ namespace QuestPDF.Elements
     {
         internal IPageContext PageContext { get; set; }
         internal IDrawingCanvas Canvas { get; set; }
-        internal SemanticTreeManager SemanticTreeManager { get; set; }
+        internal SemanticTreeManager? SemanticTreeManager { get; set; }
         
         internal TextStyle TextStyle { get; set; }
         internal ContentDirection ContentDirection { get; set; }
@@ -173,11 +173,14 @@ namespace QuestPDF.Elements
             container.ApplyInheritedAndGlobalTexStyle(TextStyle);
             container.ApplyContentDirection(ContentDirection);
             container.ApplyDefaultImageConfiguration(ImageTargetDpi, ImageCompressionQuality, UseOriginalImage);
-            container.ApplySemanticParagraphs();
-            
             container.InjectDependencies(PageContext, Canvas);
-            container.InjectSemanticTreeManager(SemanticTreeManager);
             container.VisitChildren(x => (x as IStateful)?.ResetState());
+            
+            if (SemanticTreeManager != null)
+            {
+                container.InjectSemanticTreeManager(SemanticTreeManager);
+                container.ApplySemanticParagraphs();
+            }
 
             container.Size = container.Measure(Size.Max);
             

+ 7 - 4
Source/QuestPDF/Elements/Lazy.cs

@@ -8,7 +8,7 @@ namespace QuestPDF.Elements;
 
 internal sealed class Lazy : ContainerElement, ISemanticAware, IContentDirectionAware, IStateful
 {
-    public SemanticTreeManager SemanticTreeManager { get; set; }
+    public SemanticTreeManager? SemanticTreeManager { get; set; }
     
     public Action<IContainer> ContentSource { get; set; }
     public bool IsCacheable { get; set; }
@@ -61,11 +61,14 @@ internal sealed class Lazy : ContainerElement, ISemanticAware, IContentDirection
         container.ApplyInheritedAndGlobalTexStyle(TextStyle);
         container.ApplyContentDirection(ContentDirection);
         container.ApplyDefaultImageConfiguration(ImageTargetDpi.Value, ImageCompressionQuality.Value, UseOriginalImage);
-        container.ApplySemanticParagraphs();
-            
         container.InjectDependencies(PageContext, Canvas);
-        container.InjectSemanticTreeManager(SemanticTreeManager);
         container.VisitChildren(x => (x as IStateful)?.ResetState());
+
+        if (SemanticTreeManager != null)
+        {
+            container.InjectSemanticTreeManager(SemanticTreeManager);
+            container.ApplySemanticParagraphs();
+        }
     }
     
     #region IStateful

+ 17 - 4
Source/QuestPDF/Elements/RepeatContent.cs

@@ -8,7 +8,7 @@ namespace QuestPDF.Elements;
 
 internal sealed class RepeatContent : ContainerElement, IStateful, ISemanticAware
 {
-    public SemanticTreeManager SemanticTreeManager { get; set; }
+    public SemanticTreeManager? SemanticTreeManager { get; set; }
     
     public enum RepeatContextType
     {
@@ -23,6 +23,15 @@ internal sealed class RepeatContent : ContainerElement, IStateful, ISemanticAwar
     {
         OptimizeContentCacheBehavior();
         
+        var childMeasurement = Child.Measure(availableSpace);
+
+        if (SemanticTreeManager == null)
+        {
+            base.Draw(availableSpace);
+            ResetChildrenIfNecessary();
+            return;      
+        }
+        
         if (IsFullyRendered)
         {
             var paginationNodeId = RepeatContext switch
@@ -35,15 +44,19 @@ internal sealed class RepeatContent : ContainerElement, IStateful, ISemanticAwar
             Canvas.SetSemanticNodeId(paginationNodeId);
             SemanticTreeManager.BeginArtifactContent();
         }
-
-        var childMeasurement = Child?.Measure(availableSpace);
+        
         base.Draw(availableSpace);
         
         if (IsFullyRendered)
             SemanticTreeManager.EndArtifactContent();
 
-        if (childMeasurement?.Type == SpacePlanType.FullRender)
+        ResetChildrenIfNecessary();
+
+        void ResetChildrenIfNecessary()
         {
+            if (childMeasurement.Type != SpacePlanType.FullRender) 
+                return;
+            
             Child.VisitChildren(x => (x as IStateful)?.ResetState(false));
             IsFullyRendered = true;
         }

+ 7 - 1
Source/QuestPDF/Elements/SemanticTag.cs

@@ -8,7 +8,7 @@ namespace QuestPDF.Elements;
 
 internal class SemanticTag : ContainerElement, ISemanticAware
 {
-    public SemanticTreeManager SemanticTreeManager { get; set; }
+    public SemanticTreeManager? SemanticTreeManager { get; set; }
     public SemanticTreeNode? SemanticTreeNode { get; private set; }
 
     public string TagType { get; set; }
@@ -17,6 +17,12 @@ internal class SemanticTag : ContainerElement, ISemanticAware
 
     internal override void Draw(Size availableSpace)
     {
+        if (SemanticTreeManager == null)
+        {
+            Child?.Draw(availableSpace);
+            return;       
+        }
+        
         RegisterCurrentSemanticNode();
 
         if (SemanticTreeManager.IsCurrentContentArtifact())

+ 4 - 1
Source/QuestPDF/Elements/Table/Table.cs

@@ -367,13 +367,16 @@ namespace QuestPDF.Elements.Table
         
         internal bool EnableAutomatedSemanticTagging { get; set; }
         private bool IsSemanticTaggingApplied { get; set; }
-        public SemanticTreeManager SemanticTreeManager { get; set; } = new();
+        public SemanticTreeManager? SemanticTreeManager { get; set; } = new();
 
         internal TablePartType PartType { get; set; }
         public List<TableCell> HeaderCells { get; set; } = []; 
 
         private void RegisterSemanticTree()
         {
+            if (SemanticTreeManager == null)
+                return;
+            
             if (SemanticTreeManager.IsCurrentContentArtifact())
                 return;
             

+ 1 - 1
Source/QuestPDF/Infrastructure/ISemanticAware.cs

@@ -4,5 +4,5 @@ namespace QuestPDF.Infrastructure;
 
 internal interface ISemanticAware
 {
-    public SemanticTreeManager SemanticTreeManager { get; set; }
+    public SemanticTreeManager? SemanticTreeManager { get; set; }
 }