Skip to content

Commit 0d589c5

Browse files
committed
Fix invalid tag structure while tagging form fields as artifacts
DEVSIX-8974
1 parent 6cd5c43 commit 0d589c5

File tree

5 files changed

+88
-26
lines changed

5 files changed

+88
-26
lines changed

forms/src/main/java/com/itextpdf/forms/form/renderer/CheckBoxRenderer.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ This file is part of the iText (R) project.
3939
import com.itextpdf.kernel.pdf.PdfConformance;
4040
import com.itextpdf.kernel.pdf.PdfDocument;
4141
import com.itextpdf.kernel.pdf.PdfPage;
42+
import com.itextpdf.kernel.pdf.canvas.CanvasArtifact;
4243
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
4344
import com.itextpdf.kernel.pdf.tagging.StandardRoles;
4445
import com.itextpdf.kernel.pdf.tagutils.TagTreePointer;
@@ -56,10 +57,10 @@ This file is part of the iText (R) project.
5657
import com.itextpdf.layout.renderer.DrawContext;
5758
import com.itextpdf.layout.renderer.IRenderer;
5859
import com.itextpdf.layout.renderer.ParagraphRenderer;
60+
import com.itextpdf.layout.tagging.LayoutTaggingHelper;
5961

6062
import java.util.Map;
6163

62-
6364
/**
6465
* The {@link AbstractFormFieldRenderer} implementation for checkboxes.
6566
*/
@@ -70,7 +71,6 @@ public class CheckBoxRenderer extends AbstractFormFieldRenderer {
7071
// 11px
7172
private static final float DEFAULT_SIZE = 8.25F;
7273

73-
7474
/**
7575
* Creates a new {@link CheckBoxRenderer} instance.
7676
*
@@ -89,7 +89,6 @@ public IRenderer getNextRenderer() {
8989
return new CheckBoxRenderer((CheckBox) modelElement);
9090
}
9191

92-
9392
/**
9493
* Gets the rendering mode of the checkbox.
9594
*
@@ -182,6 +181,7 @@ protected void adjustFieldLayout(LayoutContext layoutContext) {
182181
/**
183182
* Applies given paddings to the given rectangle.
184183
*
184+
* <p>
185185
* Checkboxes don't support setting of paddings as they are always centered.
186186
* So that this method returns the rectangle as is.
187187
*
@@ -310,18 +310,20 @@ public void drawChildren(DrawContext drawContext) {
310310
PdfCanvas canvas = drawContext.getCanvas();
311311
boolean isTaggingEnabled = drawContext.isTaggingEnabled();
312312
if (isTaggingEnabled) {
313-
TagTreePointer tp = drawContext.getDocument().getTagStructureContext().getAutoTaggingPointer();
314-
canvas.openTag(tp.getTagReference());
313+
LayoutTaggingHelper taggingHelper = this.<LayoutTaggingHelper>getProperty(Property.TAGGING_HELPER);
314+
boolean isArtifact = taggingHelper != null && taggingHelper.isArtifact(this);
315+
if (!isArtifact) {
316+
TagTreePointer tp = drawContext.getDocument().getTagStructureContext().getAutoTaggingPointer();
317+
canvas.openTag(tp.getTagReference());
318+
} else {
319+
canvas.openTag(new CanvasArtifact());
320+
}
315321
}
316322
createCheckBoxRenderStrategy().drawCheckBoxContent(drawContext, CheckBoxRenderer.this, rectangle);
317323
if (isTaggingEnabled) {
318324
canvas.closeTag();
319325
}
320-
321326
}
322327
}
323328

324329
}
325-
326-
327-

forms/src/main/java/com/itextpdf/forms/form/renderer/RadioRenderer.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ This file is part of the iText (R) project.
5656
import com.itextpdf.layout.renderer.DrawContext;
5757
import com.itextpdf.layout.renderer.IRenderer;
5858
import com.itextpdf.layout.renderer.ParagraphRenderer;
59+
import com.itextpdf.layout.tagging.LayoutTaggingHelper;
5960

6061
import java.util.Map;
6162

@@ -249,8 +250,14 @@ public void drawChildren(DrawContext drawContext) {
249250
PdfCanvas canvas = drawContext.getCanvas();
250251
boolean isTaggingEnabled = drawContext.isTaggingEnabled();
251252
if (isTaggingEnabled) {
252-
TagTreePointer tp = drawContext.getDocument().getTagStructureContext().getAutoTaggingPointer();
253-
canvas.openTag(tp.getTagReference());
253+
LayoutTaggingHelper taggingHelper = this.<LayoutTaggingHelper>getProperty(Property.TAGGING_HELPER);
254+
boolean isArtifact = taggingHelper != null && taggingHelper.isArtifact(this);
255+
if (isArtifact) {
256+
canvas.openTag(new CanvasArtifact());
257+
} else {
258+
TagTreePointer tp = drawContext.getDocument().getTagStructureContext().getAutoTaggingPointer();
259+
canvas.openTag(tp.getTagReference());
260+
}
254261
}
255262
Rectangle rectangle = getOccupiedArea().getBBox().clone();
256263
Border borderTop = this.<Border>getProperty(Property.BORDER_TOP);

forms/src/test/java/com/itextpdf/forms/FormFieldsTaggingTest.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,32 @@ This file is part of the iText (R) project.
2727
import com.itextpdf.forms.fields.PdfFormCreator;
2828
import com.itextpdf.forms.fields.PushButtonFormFieldBuilder;
2929
import com.itextpdf.forms.fields.RadioFormFieldBuilder;
30+
import com.itextpdf.forms.form.element.CheckBox;
31+
import com.itextpdf.forms.form.element.Radio;
3032
import com.itextpdf.io.logs.IoLogMessageConstant;
3133
import com.itextpdf.kernel.geom.Rectangle;
3234
import com.itextpdf.kernel.pdf.PdfDocument;
3335
import com.itextpdf.kernel.pdf.PdfReader;
36+
import com.itextpdf.kernel.pdf.PdfVersion;
3437
import com.itextpdf.kernel.pdf.PdfWriter;
38+
import com.itextpdf.kernel.pdf.WriterProperties;
3539
import com.itextpdf.kernel.pdf.tagging.StandardRoles;
3640
import com.itextpdf.kernel.pdf.tagutils.TagTreePointer;
3741
import com.itextpdf.kernel.utils.CompareTool;
42+
import com.itextpdf.layout.Document;
3843
import com.itextpdf.test.ExtendedITextTest;
3944
import com.itextpdf.test.TestUtil;
4045
import com.itextpdf.test.annotations.LogMessage;
4146
import com.itextpdf.test.annotations.LogMessages;
42-
43-
import java.io.IOException;
44-
import javax.xml.parsers.ParserConfigurationException;
4547
import org.junit.jupiter.api.Assertions;
4648
import org.junit.jupiter.api.BeforeAll;
4749
import org.junit.jupiter.api.Tag;
4850
import org.junit.jupiter.api.Test;
4951
import org.xml.sax.SAXException;
5052

53+
import javax.xml.parsers.ParserConfigurationException;
54+
import java.io.IOException;
55+
5156
@Tag("IntegrationTest")
5257
public class FormFieldsTaggingTest extends ExtendedITextTest {
5358

@@ -305,6 +310,27 @@ public void formFieldTaggingTest11() throws IOException, InterruptedException, P
305310
compareOutput(outFileName, cmpFileName);
306311
}
307312

313+
@Test
314+
public void formFieldsAsArtifactsTest() throws Exception {
315+
String outFileName = destinationFolder + "formFieldsAsArtifacts.pdf";
316+
String cmpFileName = sourceFolder + "cmp_formFieldsAsArtifacts.pdf";
317+
318+
try (PdfWriter writer = new PdfWriter(outFileName, new WriterProperties().setPdfVersion(PdfVersion.PDF_2_0));
319+
PdfDocument pdfDoc = new PdfDocument(writer);
320+
Document document = new Document(pdfDoc)) {
321+
pdfDoc.setTagged();
322+
Radio radio = new Radio("name1", "group");
323+
radio.setChecked(true);
324+
radio.getAccessibilityProperties().setRole(StandardRoles.ARTIFACT);
325+
document.add(radio);
326+
CheckBox cb = new CheckBox("name");
327+
cb.getAccessibilityProperties().setRole(StandardRoles.ARTIFACT);
328+
document.add(cb);
329+
}
330+
331+
compareOutput(outFileName, cmpFileName);
332+
}
333+
308334
private void addFormFieldsToDocument(PdfDocument pdfDoc, PdfAcroForm acroForm) {
309335
Rectangle rect = new Rectangle(36, 700, 20, 20);
310336
Rectangle rect1 = new Rectangle(36, 680, 20, 20);

pdfua/src/test/java/com/itextpdf/pdfua/checkers/PdfUAFormFieldsTest.java

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,7 +1640,7 @@ public IBlockElement generate() {
16401640

16411641
@ParameterizedTest
16421642
@MethodSource("data")
1643-
public void testCheckBoxArtifactDifferentRole(PdfUAConformance pdfUAConformance) throws IOException {
1643+
public void testCheckBoxArtifactRole(PdfUAConformance pdfUAConformance) throws IOException {
16441644
framework.addSuppliers(new UaValidationTestFramework.Generator<IBlockElement>() {
16451645
@Override
16461646
public IBlockElement generate() {
@@ -1650,14 +1650,7 @@ public IBlockElement generate() {
16501650
return cb;
16511651
}
16521652
});
1653-
if (pdfUAConformance == PdfUAConformance.PDF_UA_1) {
1654-
framework.assertBothValid("testCheckBoxArtifactRoleua1", pdfUAConformance);
1655-
} else if (pdfUAConformance == PdfUAConformance.PDF_UA_2) {
1656-
//TODO DEVSIX-8974 Tagging formfield as artifact will put the inner content into bad places in tagstructure
1657-
String message = MessageFormatUtil.format(
1658-
KernelExceptionMessageConstant.PARENT_CHILD_ROLE_RELATION_IS_NOT_ALLOWED, "Document", "CONTENT");
1659-
framework.assertBothFail("testCheckBoxArtifactRoleua2", message, pdfUAConformance);
1660-
}
1653+
framework.assertBothValid("testCheckBoxArtifactRole", pdfUAConformance);
16611654
}
16621655

16631656
@ParameterizedTest
@@ -1666,7 +1659,7 @@ public void testRadioButtonDifferentRole(PdfUAConformance pdfUAConformance) thro
16661659
framework.addSuppliers(new UaValidationTestFramework.Generator<IBlockElement>() {
16671660
@Override
16681661
public IBlockElement generate() {
1669-
Radio radio = new Radio("name", "group");
1662+
Radio radio = new Radio("name1", "group");
16701663
radio.getAccessibilityProperties().setRole(StandardRoles.FIGURE);
16711664
radio.getAccessibilityProperties()
16721665
.setAlternateDescription("Radio " + "that " + "was " + "not " + "checked");
@@ -1676,7 +1669,7 @@ public IBlockElement generate() {
16761669
framework.addSuppliers(new UaValidationTestFramework.Generator<IBlockElement>() {
16771670
@Override
16781671
public IBlockElement generate() {
1679-
Radio radio = new Radio("name", "group");
1672+
Radio radio = new Radio("name2", "group");
16801673
radio.setChecked(true);
16811674
radio.getAccessibilityProperties().setRole(StandardRoles.FIGURE);
16821675
radio.getAccessibilityProperties().setAlternateDescription("Radio that was not checked");
@@ -1686,14 +1679,48 @@ public IBlockElement generate() {
16861679
framework.addSuppliers(new UaValidationTestFramework.Generator<IBlockElement>() {
16871680
@Override
16881681
public IBlockElement generate() {
1689-
Radio radio = new Radio("name", "group");
1682+
Radio radio = new Radio("name3", "group");
16901683
radio.getAccessibilityProperties().setRole(StandardRoles.ARTIFACT);
16911684
return radio;
16921685
}
16931686
});
16941687
framework.assertBothValid("testRadioButtonDifferentRole", pdfUAConformance);
16951688
}
16961689

1690+
@ParameterizedTest
1691+
@MethodSource("data")
1692+
public void testRadioButtonArtifactRole(PdfUAConformance pdfUAConformance) throws IOException {
1693+
framework.addSuppliers(new UaValidationTestFramework.Generator<IBlockElement>() {
1694+
@Override
1695+
public IBlockElement generate() {
1696+
Radio radio = new Radio("name1", "group");
1697+
radio.getAccessibilityProperties().setRole(StandardRoles.ARTIFACT);
1698+
radio.getAccessibilityProperties()
1699+
.setAlternateDescription("Radio that was not checked");
1700+
return radio;
1701+
}
1702+
});
1703+
framework.addSuppliers(new UaValidationTestFramework.Generator<IBlockElement>() {
1704+
@Override
1705+
public IBlockElement generate() {
1706+
Radio radio = new Radio("name2", "group");
1707+
radio.setChecked(true);
1708+
radio.getAccessibilityProperties().setRole(StandardRoles.ARTIFACT);
1709+
radio.getAccessibilityProperties().setAlternateDescription("Radio that was not checked");
1710+
return radio;
1711+
}
1712+
});
1713+
framework.addSuppliers(new UaValidationTestFramework.Generator<IBlockElement>() {
1714+
@Override
1715+
public IBlockElement generate() {
1716+
Radio radio = new Radio("name3", "group");
1717+
radio.getAccessibilityProperties().setRole(StandardRoles.ARTIFACT);
1718+
return radio;
1719+
}
1720+
});
1721+
framework.assertBothValid("testRadioButtonArtifactRole", pdfUAConformance);
1722+
}
1723+
16971724
@ParameterizedTest
16981725
@MethodSource("data")
16991726
public void testButtonDifferentRole(PdfUAConformance pdfUAConformance) throws IOException {

0 commit comments

Comments
 (0)