Skip to content

Commit d474d89

Browse files
Fix autotagging of tables for pdf/a-1
Now autotagging takes into account current pdf version. DEVSIX-562
1 parent 250f1f4 commit d474d89

File tree

8 files changed

+116
-55
lines changed

8 files changed

+116
-55
lines changed

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfDocument.java

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,6 @@ This file is part of the iText (R) project.
7676
import org.slf4j.Logger;
7777
import org.slf4j.LoggerFactory;
7878

79-
import org.slf4j.Logger;
80-
import org.slf4j.LoggerFactory;
81-
8279
public class PdfDocument implements IEventDispatcher, Closeable, Serializable {
8380

8481
private static final long serialVersionUID = -7041578979319799646L;
@@ -814,15 +811,14 @@ public Integer getNextStructParentIndex() {
814811
*/
815812
public TagStructureContext getTagStructureContext() {
816813
checkClosingStatus();
817-
if (tagStructureContext != null) {
818-
return tagStructureContext;
819-
}
814+
if (tagStructureContext == null) {
815+
if (!isTagged()) {
816+
throw new PdfException(PdfException.MustBeATaggedDocument);
817+
}
820818

821-
if (!isTagged()) {
822-
throw new PdfException(PdfException.MustBeATaggedDocument);
819+
initTagStructureContext();
823820
}
824821

825-
tagStructureContext = new TagStructureContext(this);
826822
return tagStructureContext;
827823
}
828824

@@ -1211,6 +1207,19 @@ public void setUserProperties(boolean userProperties) {
12111207
this.userProperties = userProperties;
12121208
}
12131209

1210+
/**
1211+
* Gets list of indirect references.
1212+
*
1213+
* @return list of indirect references.
1214+
*/
1215+
PdfXrefTable getXref() {
1216+
return xref;
1217+
}
1218+
1219+
protected void initTagStructureContext() {
1220+
tagStructureContext = new TagStructureContext(this);
1221+
}
1222+
12141223
protected void storeLinkAnnotation(PdfPage page, PdfLinkAnnotation annotation) {
12151224
List<PdfLinkAnnotation> pageAnnotations = linkAnnotations.get(page);
12161225
if (pageAnnotations == null) {
@@ -1341,15 +1350,6 @@ protected void open(PdfVersion newPdfVersion) {
13411350
}
13421351
}
13431352

1344-
/**
1345-
* Gets list of indirect references.
1346-
*
1347-
* @return list of indirect references.
1348-
*/
1349-
PdfXrefTable getXref() {
1350-
return xref;
1351-
}
1352-
13531353
/**
13541354
* List all newly added or loaded fonts
13551355
*

kernel/src/main/java/com/itextpdf/kernel/pdf/tagging/PdfStructTreeRoot.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ public void copyTo(PdfDocument destDocument, int insertBeforePage, Map<PdfPage,
216216
}
217217

218218
public int getParentTreeNextKey() {
219-
// /ParentTreeNextKey entry is always inited on MaredContentReferencesManager initialization
219+
// /ParentTreeNextKey entry is always inited on ParentTreeHandler initialization
220220
return getPdfObject().getAsNumber(PdfName.ParentTreeNextKey).intValue();
221221
}
222222

kernel/src/main/java/com/itextpdf/kernel/pdf/tagutils/TagStructureContext.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ This file is part of the iText (R) project.
5050
import com.itextpdf.kernel.pdf.PdfName;
5151
import com.itextpdf.kernel.pdf.PdfNumber;
5252
import com.itextpdf.kernel.pdf.PdfPage;
53+
import com.itextpdf.kernel.pdf.PdfVersion;
5354
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
5455
import com.itextpdf.kernel.pdf.tagging.IPdfStructElem;
5556
import com.itextpdf.kernel.pdf.tagging.PdfMcr;
@@ -90,6 +91,7 @@ public class TagStructureContext implements Serializable {
9091
private PdfDocument document;
9192
private PdfStructElem rootTagElement;
9293
protected TagTreePointer autoTaggingPointer;
94+
private PdfVersion tagStructureTargetVersion;
9395
private boolean forbidUnknownRoles;
9496

9597
/**
@@ -113,13 +115,18 @@ public class TagStructureContext implements Serializable {
113115
* @param document the document which tag structure will be manipulated with this class.
114116
*/
115117
public TagStructureContext(PdfDocument document) {
118+
this(document, document.getPdfVersion());
119+
}
120+
121+
public TagStructureContext(PdfDocument document, PdfVersion tagStructureTargetVersion) {
116122
this.document = document;
117123
if (!document.isTagged()) {
118124
throw new PdfException(PdfException.MustBeATaggedDocument);
119125
}
120126
connectedModelToStruct = new HashMap<>();
121127
connectedStructToModel = new HashMap<>();
122128

129+
this.tagStructureTargetVersion = tagStructureTargetVersion;
123130
forbidUnknownRoles = true;
124131

125132
normalizeDocumentRootTag();
@@ -137,6 +144,10 @@ public TagStructureContext setForbidUnknownRoles(boolean forbidUnknownRoles) {
137144
return this;
138145
}
139146

147+
public PdfVersion getTagStructureTargetVersion() {
148+
return tagStructureTargetVersion;
149+
}
150+
140151
/**
141152
* All document auto tagging logic uses {@link TagTreePointer} returned by this method to manipulate tag structure.
142153
* Typically it points at the root tag. This pointer also could be used to tweak auto tagging process

layout/src/main/java/com/itextpdf/layout/renderer/TableRenderer.java

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ This file is part of the iText (R) project.
4747
import com.itextpdf.kernel.geom.Rectangle;
4848
import com.itextpdf.kernel.pdf.PdfDocument;
4949
import com.itextpdf.kernel.pdf.PdfName;
50+
import com.itextpdf.kernel.pdf.PdfVersion;
5051
import com.itextpdf.kernel.pdf.canvas.CanvasArtifact;
5152
import com.itextpdf.kernel.pdf.tagutils.IAccessibleElement;
53+
import com.itextpdf.kernel.pdf.tagutils.TagStructureContext;
5254
import com.itextpdf.kernel.pdf.tagutils.TagTreePointer;
5355
import com.itextpdf.layout.property.Property;
5456
import com.itextpdf.layout.border.Border;
@@ -453,38 +455,35 @@ public LayoutResult layout(LayoutContext layoutContext) {
453455
public void draw(DrawContext drawContext) {
454456
PdfDocument document = drawContext.getDocument();
455457
boolean isTagged = drawContext.isTaggingEnabled() && getModelElement() instanceof IAccessibleElement;
456-
if (isTagged
457-
&& ((IAccessibleElement) getModelElement()).getRole() != null
458-
&& !((IAccessibleElement) getModelElement()).getRole().equals(PdfName.Artifact)) {
459-
TagTreePointer tagPointer = document.getTagStructureContext().getAutoTaggingPointer();
458+
boolean ignoreTag = false;
459+
PdfName role = null;
460+
if (isTagged) {
461+
role = ((IAccessibleElement) getModelElement()).getRole();
462+
boolean isHeaderOrFooter = PdfName.THead.equals(role) || PdfName.TFoot.equals(role);
463+
boolean ignoreHeaderFooterTag =
464+
document.getTagStructureContext().getTagStructureTargetVersion().compareTo(PdfVersion.PDF_1_5) < 0;
465+
ignoreTag = isHeaderOrFooter && ignoreHeaderFooterTag;
466+
}
467+
if (role != null
468+
&& !role.equals(PdfName.Artifact)
469+
&& !ignoreTag) {
470+
TagStructureContext tagStructureContext = document.getTagStructureContext();
471+
TagTreePointer tagPointer = tagStructureContext.getAutoTaggingPointer();
460472

461473
IAccessibleElement accessibleElement = (IAccessibleElement) getModelElement();
462-
if (!document.getTagStructureContext().isElementConnectedToTag(accessibleElement)) {
463-
AccessibleAttributesApplier.applyLayoutAttributes(accessibleElement.getRole(), this, document);
474+
if (!tagStructureContext.isElementConnectedToTag(accessibleElement)) {
475+
AccessibleAttributesApplier.applyLayoutAttributes(role, this, document);
464476
}
465477

466-
467478
Table modelElement = (Table) getModelElement();
468-
boolean toRemoveConnectionsWithTags = isLastRendererForModelElement && modelElement.isComplete();
469-
if (accessibleElement.getRole().equals(PdfName.THead) || accessibleElement.getRole().equals(PdfName.TFoot)) {
470-
for (IRenderer renderer : childRenderers) {
471-
if (renderer instanceof AbstractRenderer) {
472-
((AbstractRenderer) renderer).isLastRendererForModelElement = toRemoveConnectionsWithTags;
473-
}
474-
}
475-
}
476-
477-
//footer/header tags order processing
478-
if (accessibleElement.getRole().equals(PdfName.THead)) {
479-
tagPointer.addTag(0, accessibleElement, true);
480-
} else {
481-
tagPointer.addTag(accessibleElement, true);
482-
}
479+
tagPointer.addTag(accessibleElement, true);
483480

484481
super.draw(drawContext);
485482

486483
tagPointer.moveToParent();
487-
if (toRemoveConnectionsWithTags) {
484+
485+
boolean toRemoveConnectionsWithTag = isLastRendererForModelElement && modelElement.isComplete();
486+
if (toRemoveConnectionsWithTag) {
488487
tagPointer.removeElementConnectionToTag(accessibleElement);
489488
}
490489
} else {
@@ -495,21 +494,33 @@ public void draw(DrawContext drawContext) {
495494
@Override
496495
public void drawChildren(DrawContext drawContext) {
497496
Table modelElement = (Table) getModelElement();
498-
boolean isTheVeryLast = isLastRendererForModelElement && modelElement.isComplete();
499-
500497
if (headerRenderer != null) {
501-
headerRenderer.isLastRendererForModelElement = isTheVeryLast;
498+
boolean firstHeader = rowRange.getStartRow() == 0 && isOriginalNonSplitRenderer && !modelElement.isSkipFirstHeader();
499+
boolean notToTagHeader = drawContext.isTaggingEnabled() && !firstHeader;
500+
if (notToTagHeader) {
501+
drawContext.setTaggingEnabled(false);
502+
drawContext.getCanvas().openTag(new CanvasArtifact());
503+
}
502504
headerRenderer.draw(drawContext);
505+
if (notToTagHeader) {
506+
drawContext.getCanvas().closeTag();
507+
drawContext.setTaggingEnabled(true);
508+
}
503509
}
504510

505511
boolean isTagged = drawContext.isTaggingEnabled() && getModelElement() instanceof IAccessibleElement && !childRenderers.isEmpty();
506512
TagTreePointer tagPointer = null;
513+
boolean shouldHaveFooterOrHeaderTag = modelElement.getHeader() != null || modelElement.getFooter() != null;
507514
if (isTagged) {
508515
PdfName role = modelElement.getRole();
509516
if (role != null && !PdfName.Artifact.equals(role)) {
510517
tagPointer = drawContext.getDocument().getTagStructureContext().getAutoTaggingPointer();
511518

512-
if (modelElement.getHeader() != null || modelElement.getFooter() != null) {
519+
boolean ignoreHeaderFooterTag = drawContext.getDocument().getTagStructureContext()
520+
.getTagStructureTargetVersion().compareTo(PdfVersion.PDF_1_5) < 0;
521+
shouldHaveFooterOrHeaderTag = shouldHaveFooterOrHeaderTag && !ignoreHeaderFooterTag
522+
&& (!modelElement.isSkipFirstHeader() || !modelElement.isSkipLastFooter());
523+
if (shouldHaveFooterOrHeaderTag) {
513524
if (tagPointer.getKidsRoles().contains(PdfName.TBody)) {
514525
tagPointer.moveToKid(PdfName.TBody);
515526
} else {
@@ -523,7 +534,11 @@ public void drawChildren(DrawContext drawContext) {
523534

524535
for (IRenderer child : childRenderers) {
525536
if (isTagged) {
526-
int cellRow = ((Cell) child.getModelElement()).getRow();
537+
int adjustByHeaderRowsNum = 0;
538+
if (modelElement.getHeader() != null && !modelElement.isSkipFirstHeader() && !shouldHaveFooterOrHeaderTag) {
539+
adjustByHeaderRowsNum = modelElement.getHeader().getNumberOfRows();
540+
}
541+
int cellRow = ((Cell) child.getModelElement()).getRow() + adjustByHeaderRowsNum;
527542
int rowsNum = tagPointer.getKidsRoles().size();
528543
if (cellRow < rowsNum) {
529544
tagPointer.moveToKid(cellRow);
@@ -540,16 +555,25 @@ public void drawChildren(DrawContext drawContext) {
540555
}
541556

542557
if (isTagged) {
543-
if (modelElement.getHeader() != null || modelElement.getFooter() != null) {
558+
if (shouldHaveFooterOrHeaderTag) {
544559
tagPointer.moveToParent();
545560
}
546561
}
547562

548563
drawBorders(drawContext);
549564

550565
if (footerRenderer != null) {
551-
footerRenderer.isLastRendererForModelElement = isTheVeryLast;
566+
boolean lastFooter = isLastRendererForModelElement && modelElement.isComplete() && !modelElement.isSkipLastFooter();
567+
boolean notToTagFooter = drawContext.isTaggingEnabled() && !lastFooter;
568+
if (notToTagFooter) {
569+
drawContext.setTaggingEnabled(false);
570+
drawContext.getCanvas().openTag(new CanvasArtifact());
571+
}
552572
footerRenderer.draw(drawContext);
573+
if (notToTagFooter) {
574+
drawContext.getCanvas().closeTag();
575+
drawContext.setTaggingEnabled(true);
576+
}
553577
}
554578
}
555579

Binary file not shown.
Binary file not shown.

pdfa/src/main/java/com/itextpdf/pdfa/PdfADocument.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,12 @@ This file is part of the iText (R) project.
5656
import com.itextpdf.kernel.pdf.PdfReader;
5757
import com.itextpdf.kernel.pdf.PdfResources;
5858
import com.itextpdf.kernel.pdf.PdfStream;
59+
import com.itextpdf.kernel.pdf.PdfVersion;
5960
import com.itextpdf.kernel.pdf.PdfWriter;
6061
import com.itextpdf.kernel.pdf.StampingProperties;
6162
import com.itextpdf.kernel.pdf.canvas.CanvasGraphicsState;
6263
import com.itextpdf.kernel.pdf.canvas.PdfCanvasConstants;
64+
import com.itextpdf.kernel.pdf.tagutils.TagStructureContext;
6365
import com.itextpdf.kernel.xmp.XMPConst;
6466
import com.itextpdf.kernel.xmp.XMPException;
6567
import com.itextpdf.kernel.xmp.XMPMeta;
@@ -203,6 +205,15 @@ public void createXmpMetadata() throws XMPException {
203205
createXmpMetadata(checker.getConformanceLevel());
204206
}
205207

208+
public void createXmpMetadata(PdfAConformanceLevel conformanceLevel) throws XMPException {
209+
super.createXmpMetadata();
210+
XMPMeta xmpMeta = XMPMetaFactory.parseFromBuffer(getXmpMetadata());
211+
if (conformanceLevel != null) {
212+
addRdfDescription(xmpMeta, conformanceLevel);
213+
}
214+
setXmpMetadata(xmpMeta);
215+
}
216+
206217
@Override
207218
protected void checkIsoConformance() {
208219
checker.checkDocument(catalog);
@@ -253,13 +264,28 @@ protected void addRdfDescription(XMPMeta xmpMeta, PdfAConformanceLevel conforman
253264
}
254265
}
255266

256-
public void createXmpMetadata(PdfAConformanceLevel conformanceLevel) throws XMPException {
257-
super.createXmpMetadata();
258-
XMPMeta xmpMeta = XMPMetaFactory.parseFromBuffer(getXmpMetadata());
259-
if (conformanceLevel != null) {
260-
addRdfDescription(xmpMeta, conformanceLevel);
267+
protected void initTagStructureContext() {
268+
tagStructureContext = new TagStructureContext(this, getPdfVersionForPdfA(checker.getConformanceLevel()));
269+
}
270+
271+
272+
private static PdfVersion getPdfVersionForPdfA(PdfAConformanceLevel conformanceLevel) {
273+
PdfVersion version;
274+
switch (conformanceLevel.getConformance()) {
275+
case "1":
276+
version = PdfVersion.PDF_1_4;
277+
break;
278+
case "2":
279+
version = PdfVersion.PDF_1_7;
280+
break;
281+
case "3":
282+
version = PdfVersion.PDF_1_7;
283+
break;
284+
default:
285+
version = PdfVersion.PDF_1_4;
286+
break;
261287
}
262-
setXmpMetadata(xmpMeta);
288+
return version;
263289
}
264290

265291
}

0 commit comments

Comments
 (0)