From cd053c0ddceddb82f5af07ce5e7642f82254474b Mon Sep 17 00:00:00 2001 From: danfickle Date: Thu, 18 Feb 2021 23:35:01 +1100 Subject: [PATCH 1/3] #642 One possible solution for double ups of inline-blocks Not yet sure it is the best one. --- .../render/displaylist/DisplayListPainter.java | 16 +++++++++------- .../expected/issue-642-transform-inline.pdf | Bin 0 -> 1244 bytes .../html/issue-642-transform-inline.html | 3 +++ .../VisualRegressionTest.java | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-642-transform-inline.pdf diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/displaylist/DisplayListPainter.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/displaylist/DisplayListPainter.java index 599035993..1176e3a44 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/displaylist/DisplayListPainter.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/displaylist/DisplayListPainter.java @@ -120,13 +120,15 @@ private void paintInlineContent(RenderingContext c, List inline OperatorSetClip setClip = (OperatorSetClip) dli; setClip(c, setClip); } else if (dli instanceof BlockBox) { - // Inline blocks need to be painted as a layer. - BlockBox bb = (BlockBox) dli; - List pageBoxes = bb.getContainingLayer().getPages(); - DisplayListCollector dlCollector = new DisplayListCollector(pageBoxes); - DisplayListPageContainer pageInstructions = dlCollector.collectInlineBlock(c, bb, EnumSet.noneOf(CollectFlags.class), c.getShadowPageNumber()); - - paint(c, pageInstructions); + // Inline blocks need to be painted as a layer, if not already done so. + BlockBox bb = (BlockBox) dli; + if (!bb.getStyle().requiresLayer()) { + List pageBoxes = bb.getContainingLayer().getPages(); + DisplayListCollector dlCollector = new DisplayListCollector(pageBoxes); + DisplayListPageContainer pageInstructions = dlCollector.collectInlineBlock(c, bb, EnumSet.noneOf(CollectFlags.class), c.getShadowPageNumber()); + + paint(c, pageInstructions); + } } else { InlinePaintable paintable = (InlinePaintable) dli; Object token = c.getOutputDevice().startStructure(StructureType.INLINE, (Box) dli); diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-642-transform-inline.pdf b/openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-642-transform-inline.pdf new file mode 100644 index 0000000000000000000000000000000000000000..74373f06f3c635a5084f5d6cf935ce4b6fd35993 GIT binary patch literal 1244 zcmah}O;6iE5WSDYf0#=IsEYT)Sv#%}5^)?AS{ev$IYb;xya`6J7i>qR{jZB%vVNz>67`~TW3i|h@>vFE%39--l{vj!q zCSA>Ri3jFE7k72Otj$9@cV;F(+>*Ag{*9~&0ocbZt#I12k=y_a&8nzTY#G{<*M`8( zb($pu^Mq6GaI}vQ(oyIuhm+7nQfg9K-;iXBnzj`x>j&6GS7uetCu5FF28dP9aOJQ6~OIkdsxa`U<|TM}XGmAFf| zgKH=(PQP*igBaLIppY2Pn~KuaD9&Eb=?kea&fy@&YaAb9d=F9l7T*<)+b8`s{{v1i z!l<=*r0A_XiCsxNqIEs#NXwt)Zz-z2BQ4b`j`8!0+9lolMbC2-$w}#O8}8e3!gm61 zbDRzgTHV&o zv{+C88zhx(^jVzcy6R3$o)o~MVrJ5;SRlL3ij$(sT41YPpGrNaQU<`E7kWy$3g^^1 zn(hYmV0}RY)?#&;WJ_H(pO1+# zG>A}#I;TUN9t9$zUI`;L7|B3{T!mrS3!;-zG!U)|f@AzYg@*Jh`=KdS T&2S}U-Q+G1E_6E4cm#g|vHB|b literal 0 HcmV?d00001 diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-642-transform-inline.html b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-642-transform-inline.html index 028e288e8..0e613af45 100644 --- a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-642-transform-inline.html +++ b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-642-transform-inline.html @@ -9,5 +9,8 @@
O N E
+
T W O
+
T H R E E
+
F O U R
diff --git a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java index 6e3e75bde..4fc718aff 100644 --- a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java +++ b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java @@ -1367,10 +1367,10 @@ public void testExternalAccessControl() throws IOException { /** * Transforms on inline-block or inline elements output twice, * once as the non-transformed output and once as transformed. + * Also affected inline-blocks with a z-index. * https://github.com/danfickle/openhtmltopdf/issues/642 */ @Test - @Ignore // Not debugged yet. public void testIssue642TransformInline() throws IOException { assertTrue(vt.runTest("issue-642-transform-inline")); } From a666a5452a38431beb1524382ed8c0320f2f1d13 Mon Sep 17 00:00:00 2001 From: danfickle Date: Fri, 19 Feb 2021 22:32:09 +1100 Subject: [PATCH 2/3] #642 Test runner for repeat content with some tests But need more... --- .../RepeatContentRegressionTest.java | 203 ++++++++++++++++++ .../repeated-content-tests/floats-pages.html | 50 +++++ .../inline-block-multiple.html | 28 +++ .../inline-block-pages.html | 36 ++++ .../inline-block-z-index.html | 20 ++ 5 files changed, 337 insertions(+) create mode 100644 openhtmltopdf-examples/src/test/java/com/openhtmltopdf/nonvisualregressiontests/RepeatContentRegressionTest.java create mode 100644 openhtmltopdf-examples/src/test/resources/repeated-content-tests/floats-pages.html create mode 100644 openhtmltopdf-examples/src/test/resources/repeated-content-tests/inline-block-multiple.html create mode 100644 openhtmltopdf-examples/src/test/resources/repeated-content-tests/inline-block-pages.html create mode 100644 openhtmltopdf-examples/src/test/resources/repeated-content-tests/inline-block-z-index.html diff --git a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/nonvisualregressiontests/RepeatContentRegressionTest.java b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/nonvisualregressiontests/RepeatContentRegressionTest.java new file mode 100644 index 000000000..e0b97812f --- /dev/null +++ b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/nonvisualregressiontests/RepeatContentRegressionTest.java @@ -0,0 +1,203 @@ +package com.openhtmltopdf.nonvisualregressiontests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.apache.commons.io.FileUtils; +import org.apache.pdfbox.io.IOUtils; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.text.PDFTextStripper; +import org.apache.pdfbox.util.Charsets; +import org.junit.Test; + +import com.openhtmltopdf.pdfboxout.PdfRendererBuilder; +import com.openhtmltopdf.testcases.TestcaseRunner; +import com.openhtmltopdf.visualtest.VisualTester.BuilderConfig; + +public class RepeatContentRegressionTest { + private static final String RES_PATH = "/repeated-content-tests/"; + private static final String OUT_PATH = "target/test/visual-tests/test-output/"; + + interface StringMatcher { + List problems(String expected, String actual); + void doAssert(String expected, String actual, String fileName, List problems); + } + + /** + * Simple tests with content all in one layer + * (ie. no z-index, absolute, relative, fixed, transform) + * should generally use an ordered matcher. + */ + private static class OrderedMatcher implements StringMatcher { + public List problems(String expected, String actual) { + if (!expected.equals(actual)) { + return Collections.singletonList("Mismatched ordered"); + } + + return Collections.emptyList(); + } + + public void doAssert(String expected, String actual, String fileName, List problems) { + assertEquals("Mismatched: " + fileName, expected, actual); + } + } + + /** + * Layers have to follow a specific painting order so they may be out of order. + */ + private static class UnOrderedMatcher implements StringMatcher { + public List problems(String expected, String actual) { + String[] expWords = expected.split("\\s"); + String[] actWords = actual.split("\\s"); + + Predicate filter = w -> !w.trim().isEmpty(); + + Set exp = Arrays.stream(expWords) + .filter(filter) + .collect(Collectors.toSet()); + + List act = Arrays.stream(actWords) + .filter(filter) + .collect(Collectors.toList()); + + Set seen = new HashSet<>(); + + List problems = new ArrayList<>(); + + for (String word : act) { + if (seen.contains(word)) { + problems.add("Repeat content: " + word); + } + + seen.add(word); + + if (!exp.contains(word)) { + problems.add("Unexpected content: " + word); + } + } + + for (String word : exp) { + if (!act.contains(word)) { + problems.add("Missing: " + word); + } + } + + return problems; + } + + public void doAssert(String expected, String actual, String fileName, List problems) { + fail(problems.stream().collect(Collectors.joining("\n"))); + } + } + + private static final StringMatcher ORDERED = new OrderedMatcher(); + private static final StringMatcher UNORDERED = new UnOrderedMatcher(); + + private static void render(String fileName, String html, BuilderConfig config, String proof, StringMatcher matcher) throws IOException { + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + + PdfRendererBuilder builder = new PdfRendererBuilder(); + builder.withHtmlContent(html, NonVisualRegressionTest.class.getResource(RES_PATH).toString()); + builder.toStream(actual); + builder.useFastMode(); + builder.testMode(true); + config.configure(builder); + + try { + builder.run(); + } catch (IOException e) { + System.err.println("Failed to render resource (" + fileName + ")"); + e.printStackTrace(); + throw e; + } + + byte[] pdfBytes = actual.toByteArray(); + + try (PDDocument doc = PDDocument.load(pdfBytes)) { + PDFTextStripper stripper = new PDFTextStripper(); + stripper.setSuppressDuplicateOverlappingText(false); + stripper.setLineSeparator("\n"); + + String text = stripper.getText(doc).trim(); + String expected = proof.trim().replace("\r\n", "\n"); + + List problems = matcher.problems(expected, text); + + if (!problems.isEmpty()) { + FileUtils.writeByteArrayToFile(new File(OUT_PATH, fileName + ".pdf"), pdfBytes); + + matcher.doAssert(expected, text, fileName, problems); + } + } + } + + private static void run(String fileName, BuilderConfig config, StringMatcher matcher) throws IOException { + String absResPath = RES_PATH + fileName + ".html"; + + try (InputStream is = TestcaseRunner.class.getResourceAsStream(absResPath)) { + byte[] htmlBytes = IOUtils.toByteArray(is); + String htmlWithProof = new String(htmlBytes, Charsets.UTF_8); + + String[] parts = htmlWithProof.split(Pattern.quote("=======")); + String html = parts[0]; + String proof = parts[1]; + + render(fileName, html, config, proof, matcher); + } + } + + private static void runOrdered(String fileName) throws IOException { + run(fileName, builder -> { }, ORDERED); + } + + private static void runUnOrdered(String fileName) throws IOException { + run(fileName, builder -> { }, UNORDERED); + } + + /** + * inline-blocks/relative/absolute with z-index. + */ + @Test + public void testInlineBlockZIndex() throws IOException { + runOrdered("inline-block-z-index"); + } + + /** + * Multiple in-flow inline-blocks on the one page. + */ + @Test + public void testInlineBlockMultiple() throws IOException { + runUnOrdered("inline-block-multiple"); + } + + /** + * Multiple in-flow inline-blocks across pages with large page margin. + */ + @Test + public void testInlineBlockPages() throws IOException { + runUnOrdered("inline-block-pages"); + } + + /** + * Float that goes over two pages. + */ + @Test + public void testFloatsPages() throws IOException { + runUnOrdered("floats-pages"); + } + +} diff --git a/openhtmltopdf-examples/src/test/resources/repeated-content-tests/floats-pages.html b/openhtmltopdf-examples/src/test/resources/repeated-content-tests/floats-pages.html new file mode 100644 index 000000000..1f7c87d42 --- /dev/null +++ b/openhtmltopdf-examples/src/test/resources/repeated-content-tests/floats-pages.html @@ -0,0 +1,50 @@ + + + + + +
one two three
+four five six seven eleven +
eight nine ten sixteen seventeen eighteen
+ twelve thirteen fourteen +fifteen + + +======= +one +two +three +four +five +six +seven +eight +nine +ten +eleven +twelve +thirteen +fourteen +fifteen +sixteen +seventeen +eighteen diff --git a/openhtmltopdf-examples/src/test/resources/repeated-content-tests/inline-block-multiple.html b/openhtmltopdf-examples/src/test/resources/repeated-content-tests/inline-block-multiple.html new file mode 100644 index 000000000..a74ab7645 --- /dev/null +++ b/openhtmltopdf-examples/src/test/resources/repeated-content-tests/inline-block-multiple.html @@ -0,0 +1,28 @@ + + + + + +
one
+
two
+three +four +
five six
+
seven eight nine ten
+ + +======= +one +two +three +four +five +six +seven +eight +nine +ten diff --git a/openhtmltopdf-examples/src/test/resources/repeated-content-tests/inline-block-pages.html b/openhtmltopdf-examples/src/test/resources/repeated-content-tests/inline-block-pages.html new file mode 100644 index 000000000..3aa137b2d --- /dev/null +++ b/openhtmltopdf-examples/src/test/resources/repeated-content-tests/inline-block-pages.html @@ -0,0 +1,36 @@ + + + + + +
one
+
two
+three +four +
five six
+
seven eight nine ten
+ + +======= +one +two +three +four +five +six +seven +eight +nine +ten diff --git a/openhtmltopdf-examples/src/test/resources/repeated-content-tests/inline-block-z-index.html b/openhtmltopdf-examples/src/test/resources/repeated-content-tests/inline-block-z-index.html new file mode 100644 index 000000000..145c72d67 --- /dev/null +++ b/openhtmltopdf-examples/src/test/resources/repeated-content-tests/inline-block-z-index.html @@ -0,0 +1,20 @@ + + + + + +
one
+
two
+three +four + + +======= +one +two +three +four From 84803bc4d296ebd2fed839a16458422ca4dd5bcb Mon Sep 17 00:00:00 2001 From: danfickle Date: Mon, 1 Mar 2021 23:00:40 +1100 Subject: [PATCH 3/3] #642 Big test of inline-block elements --- .../expected/issue-642-inline-blocks.pdf | Bin 0 -> 2775 bytes .../html/issue-642-inline-blocks.html | 56 ++++++++++++++++++ .../VisualRegressionTest.java | 9 +++ 3 files changed, 65 insertions(+) create mode 100644 openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-642-inline-blocks.pdf create mode 100644 openhtmltopdf-examples/src/main/resources/visualtest/html/issue-642-inline-blocks.html diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-642-inline-blocks.pdf b/openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-642-inline-blocks.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e9611caf3b15debf8b9863e349f24aa7417ff26e GIT binary patch literal 2775 zcmb7G+iv4F5Pc`G|1dAXCMZmil&DJ)1h8Xy<2IW(jv5yT_CagQjssgtWCh*!XZK~l zp-+A2jBd7U$H^)eP}CgGaCqi$jt1RcOLB`H{rls;?@WS#kvv;u|ZLw0t?qIg0ua|3>!Mq!44ZkR=I-2GJvC50)ZQrgk( zFH(LK;^PUflqQpxag@Sw@)X4n*>aI3Pm?JhC(DzKrO8z@vOEN4{63$o;NGH0K-2#( z%s5Y8;tbRVoWAeEnejQA%)?Id4ekXOKz~vyuEBIP7r4?OOe0!~7N&LM3}07Z7w^Mp zmAs^5%!HFqtTRE8{ER=T(f8~A+--l}mOl!p@^=}^8}$;F(&&3dtf6L(--_nXRH%2qbETs z2&OrkFrnaJLM0~DbP*CP*|FdMbP=3D@W}k&b%+u+cgPDtZU^mie|Qk%akQ15!e7j%bh)Fnkf1bHt3#th>nh1@w+b$}=nn&boroKDvp=Fd z8I64fYt-J8C1XlMql^=Gow<~`H zgZ8JIAU{{tSk$g86^K}~xq)U?3zs90k^Ne6(#8WNt<+jRd;Q+#ny=P#SuZFs9QFA9 z1?FOX<@WEbtu{x;~5yEdEAay^J-$Fq4<_Y)0~QoPOx~u z)*9(J#mlyQBwhgYKwQsBDwC}_frYU>r%c?%mYr!~AG0-QX}tTJbJEf~cBs%hK9saE zl})UY-cHO)sa>qv#AazYUqor%Hx82og(*K&qaRNbpso;`r+ydiPqlQh4|E;hZVTVR tez4PZJx8~_j_umItZb|?E literal 0 HcmV?d00001 diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-642-inline-blocks.html b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-642-inline-blocks.html new file mode 100644 index 000000000..8b44bbabb --- /dev/null +++ b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-642-inline-blocks.html @@ -0,0 +1,56 @@ + + + + + + +
+SIXTEEN +
+ +
ONE
+ +
TWO THREE
+ +
FOUR
+ +
FIVE SIX
+
+
SPACER
+ +
SEVEN
+ +EIGHT + +NINE + +TEN
ELEVEN
+ +TWELVETHIRTEEN +
NEW PAGE
+ +FOURTEENFIFTEEN + + + diff --git a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java index 4fc718aff..49a2839ba 100644 --- a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java +++ b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java @@ -1375,6 +1375,15 @@ public void testIssue642TransformInline() throws IOException { assertTrue(vt.runTest("issue-642-transform-inline")); } + /** + * Combinations of inline-blocks with other display and positioned + * content. + */ + @Test + public void testIssue642InlineBlocks() throws IOException { + assertTrue(vt.runTest("issue-642-inline-blocks")); + } + /** * Tests that the background-image property allows multiple values. */