Skip to content

Commit 6f3ab99

Browse files
committed
#480 Test case for link shapes returned by custom object drawer.
As promised @rototor. Link shapes are working perfectly in Acrobat.
1 parent f82c54b commit 6f3ab99

File tree

3 files changed

+200
-1
lines changed

3 files changed

+200
-1
lines changed

openhtmltopdf-examples/src/main/java/com/openhtmltopdf/visualtest/TestSupport.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,35 @@
11
package com.openhtmltopdf.visualtest;
22

3+
import java.awt.Color;
4+
import java.awt.Graphics2D;
5+
import java.awt.Polygon;
6+
import java.awt.Shape;
7+
import java.awt.geom.AffineTransform;
8+
import java.awt.geom.Rectangle2D;
39
import java.io.File;
410
import java.io.IOException;
511
import java.io.InputStream;
612
import java.io.PrintWriter;
713
import java.io.StringWriter;
814
import java.nio.file.Files;
15+
import java.util.HashMap;
916
import java.util.Locale;
17+
import java.util.Map;
1018
import java.util.logging.Level;
1119
import java.util.regex.Matcher;
1220
import java.util.regex.Pattern;
1321

22+
import org.w3c.dom.Element;
23+
1424
import com.openhtmltopdf.bidi.support.ICUBidiReorderer;
1525
import com.openhtmltopdf.bidi.support.ICUBidiSplitter;
1626
import com.openhtmltopdf.bidi.support.ICUBreakers;
27+
import com.openhtmltopdf.extend.FSObjectDrawer;
28+
import com.openhtmltopdf.extend.FSObjectDrawerFactory;
1729
import com.openhtmltopdf.extend.FSTextBreaker;
30+
import com.openhtmltopdf.extend.OutputDevice;
1831
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.TextDirection;
32+
import com.openhtmltopdf.render.RenderingContext;
1933
import com.openhtmltopdf.svgsupport.BatikSVGDrawer;
2034
import com.openhtmltopdf.util.XRLogger;
2135
import com.openhtmltopdf.visualtest.Java2DVisualTester.Java2DBuilderConfig;
@@ -158,4 +172,56 @@ public void setText(String newText) {
158172
* Configures the builder to use SVG drawer but not font.
159173
*/
160174
public static final BuilderConfig WITH_SVG = (builder) -> builder.useSVGDrawer(new BatikSVGDrawer());
175+
176+
public static class ShapesObjectDrawer implements FSObjectDrawer {
177+
public Map<Shape, String> drawObject(Element e, double x, double y, double width, double height,
178+
OutputDevice outputDevice, RenderingContext ctx, int dotsPerPixel) {
179+
180+
Map<Shape, String> shapes = new HashMap<>();
181+
182+
outputDevice.drawWithGraphics((float) x, (float) y, (float) width / dotsPerPixel,
183+
(float) height / dotsPerPixel, (Graphics2D g2d) -> {
184+
185+
double realWidth = width / dotsPerPixel;
186+
double realHeight = height / dotsPerPixel;
187+
188+
Rectangle2D rectUpperLeft = new Rectangle2D.Double(0, 0, realWidth / 4d, realHeight / 4d);
189+
Rectangle2D rectLowerRight = new Rectangle2D.Double(realWidth * (3d/4d), realHeight * (3d/4d), realWidth / 4d, realHeight / 4d);
190+
191+
int[] xpoints = new int[] { (int) (realWidth / 2d), (int) (realWidth * (1d/4d)), (int) (realWidth * (3d/4d)), (int) (realWidth / 2d) };
192+
int[] ypoints = new int[] { (int) (realHeight * (1d/4d)), (int) (realHeight * (3d/4d)), (int) (realHeight * (3d/4d)), (int) (realHeight * (1d/4d)) };
193+
Polygon centreTriangle = new Polygon(xpoints, ypoints, xpoints.length);
194+
195+
g2d.setColor(Color.CYAN);
196+
197+
g2d.draw(rectUpperLeft);
198+
g2d.draw(rectLowerRight);
199+
g2d.draw(centreTriangle);
200+
201+
AffineTransform scale = AffineTransform.getScaleInstance(dotsPerPixel, dotsPerPixel);
202+
203+
shapes.put(scale.createTransformedShape(rectUpperLeft), "http://example.com/1");
204+
shapes.put(scale.createTransformedShape(rectLowerRight), "http://example.com/2");
205+
shapes.put(scale.createTransformedShape(centreTriangle), "http://example.com/3");
206+
});
207+
208+
return shapes;
209+
}
210+
}
211+
212+
public static class ShapesObjectDrawerFactory implements FSObjectDrawerFactory {
213+
public FSObjectDrawer createDrawer(Element e) {
214+
if (!isReplacedObject(e)) {
215+
return null;
216+
}
217+
218+
return new ShapesObjectDrawer();
219+
}
220+
221+
public boolean isReplacedObject(Element e) {
222+
return e.getAttribute("type").equals("shapes");
223+
}
224+
}
225+
226+
public static final BuilderConfig WITH_SHAPES_DRAWER = (builder) -> { builder.useObjectDrawerFactory(new ShapesObjectDrawerFactory()); };
161227
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<html>
2+
<head>
3+
<style>
4+
@page {
5+
size: 700px 600px;
6+
margin: 20px 102px 40px 15px;
7+
@right-middle {
8+
content: element(right-shapes);
9+
min-width: 102px;
10+
min-height: 102px;
11+
}
12+
}
13+
object {
14+
display: block;
15+
width: 400px;
16+
height: 400px;
17+
border: 1px solid red;
18+
}
19+
</style>
20+
</head>
21+
<body>
22+
<object type="shapes" style="position: running(right-shapes); width: 100px; height: 100px;"></object>
23+
24+
<object type="shapes"></object>
25+
26+
<div style="page-break-before: always;"></div>
27+
28+
<object type="shapes"></object>
29+
</body>
30+
</html>

openhtmltopdf-examples/src/test/java/com/openhtmltopdf/nonvisualregressiontests/NonVisualRegressionTest.java

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import java.io.ByteArrayOutputStream;
99
import java.io.File;
1010
import java.io.IOException;
11+
import java.util.ArrayList;
12+
import java.util.List;
1113

1214
import org.apache.commons.io.FileUtils;
1315
import org.apache.pdfbox.io.IOUtils;
@@ -26,11 +28,13 @@
2628
import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
2729
import org.apache.pdfbox.util.Charsets;
2830
import org.hamcrest.CustomTypeSafeMatcher;
31+
import org.junit.Assert;
2932
import org.junit.Ignore;
3033
import org.junit.Test;
3134

3235
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
3336
import com.openhtmltopdf.testcases.TestcaseRunner;
37+
import com.openhtmltopdf.visualtest.TestSupport;
3438
import com.openhtmltopdf.visualtest.VisualTester.BuilderConfig;
3539

3640
public class NonVisualRegressionTest {
@@ -717,7 +721,106 @@ public void testInputWithoutNameAttribute() throws IOException {
717721
assertEquals(0, form.getFields().size());
718722
remove("input-without-name-attribute", doc);
719723
}
720-
724+
725+
private static float[] getQuadPoints(PDDocument doc, int pg, int linkIndex) throws IOException {
726+
return ((PDAnnotationLink) doc.getPage(pg).getAnnotations().get(linkIndex)).getQuadPoints();
727+
}
728+
729+
private static String print(float[] floats) {
730+
StringBuilder sb = new StringBuilder();
731+
732+
sb.append("new float[] { ");
733+
for (float floater : floats) {
734+
sb.append(floater);
735+
sb.append("f, ");
736+
}
737+
738+
sb.deleteCharAt(sb.length() - 2);
739+
sb.append("}");
740+
741+
return sb.toString();
742+
}
743+
744+
private static final float QUAD_DELTA = 0.5f;
745+
private static boolean qAssert(List<float[]> expectedList, float[] actual, StringBuilder sb, int pg, int linkIndex) {
746+
sb.append("PAGE: " + pg + ", LINK: " + linkIndex + "\n");
747+
sb.append(" ACT(" + actual.length + "): " + print(actual) + "\n");
748+
749+
// NOTE: The shapes are returned as a map and are therefore placed on
750+
// the page in a non-determined order. So we just searh the expected
751+
// list for a match.
752+
ALL_EXPECTED:
753+
for (float[] expected : expectedList) {
754+
if (expected.length != actual.length) {
755+
continue;
756+
}
757+
758+
for (int i = 0; i < expected.length; i++) {
759+
float diff = Math.abs(expected[i] - actual[i]);
760+
761+
if (diff > QUAD_DELTA) {
762+
continue ALL_EXPECTED;
763+
}
764+
}
765+
766+
return false;
767+
}
768+
769+
sb.append(" !FAILED!");
770+
sb.append("\n\n");
771+
return true;
772+
}
773+
774+
/**
775+
* Tests the shaped links support for custom object drawers
776+
* in the main document area and in the page margin on multiple
777+
* pages.
778+
*/
779+
@Test
780+
public void testPR480LinkShapes() throws IOException {
781+
try (PDDocument doc = run("pr-480-link-shapes", TestSupport.WITH_SHAPES_DRAWER)) {
782+
StringBuilder sb = new StringBuilder();
783+
List<float[]> page0 = new ArrayList<>();
784+
List<float[]> page1 = new ArrayList<>();
785+
boolean failure = false;
786+
787+
page0.add(new float[] { 486.75f, 251.25f, 468.0f, 213.75f, 486.75f, 213.75f, 505.5f, 213.75f, 486.75f, 251.25f, 505.5f, 213.75f, 496.125f, 232.5f, 486.75f, 251.25f });
788+
page0.add(new float[] { 449.25f, 270.0f, 449.25f, 251.25f, 458.625f, 251.25f, 468.0f, 251.25f, 449.25f, 270.0f, 468.0f, 251.25f, 468.0f, 260.625f, 468.0f, 270.0f });
789+
page0.add(new float[] { 505.5f, 213.75f, 505.5f, 195.0f, 514.875f, 195.0f, 524.25f, 195.0f, 505.5f, 213.75f, 524.25f, 195.0f, 524.25f, 204.375f, 524.25f, 213.75f });
790+
page0.add(new float[] { 243.0f, 203.25f, 243.0f, 128.25f, 280.5f, 128.25f, 318.0f, 128.25f, 243.0f, 203.25f, 318.0f, 128.25f, 318.0f, 165.75f, 318.0f, 203.25f });
791+
page0.add(new float[] { 168.0f, 353.25f, 93.0f, 203.25f, 168.0f, 203.25f, 243.0f, 203.25f, 168.0f, 353.25f, 243.0f, 203.25f, 205.5f, 278.25f, 168.0f, 353.25f });
792+
page0.add(new float[] { 18.0f, 428.25f, 18.0f, 353.25f, 55.5f, 353.25f, 93.0f, 353.25f, 18.0f, 428.25f, 93.0f, 353.25f, 93.0f, 390.75f, 93.0f, 428.25f });
793+
794+
failure |= qAssert(page0, getQuadPoints(doc, 0, 0), sb, 0, 0);
795+
failure |= qAssert(page0, getQuadPoints(doc, 0, 1), sb, 0, 1);
796+
failure |= qAssert(page0, getQuadPoints(doc, 0, 2), sb, 0, 2);
797+
failure |= qAssert(page0, getQuadPoints(doc, 0, 3), sb, 0, 3);
798+
failure |= qAssert(page0, getQuadPoints(doc, 0, 4), sb, 0, 4);
799+
failure |= qAssert(page0, getQuadPoints(doc, 0, 5), sb, 0, 5);
800+
801+
page1.add(new float[] { 486.75f, 251.25f, 468.0f, 213.75f, 486.75f, 213.75f, 505.5f, 213.75f, 486.75f, 251.25f, 505.5f, 213.75f, 496.125f, 232.5f, 486.75f, 251.25f });
802+
page1.add(new float[] { 449.25f, 270.0f, 449.25f, 251.25f, 458.625f, 251.25f, 468.0f, 251.25f, 449.25f, 270.0f, 468.0f, 251.25f, 468.0f, 260.625f, 468.0f, 270.0f });
803+
page1.add(new float[] { 505.5f, 213.75f, 505.5f, 195.0f, 514.875f, 195.0f, 524.25f, 195.0f, 505.5f, 213.75f, 524.25f, 195.0f, 524.25f, 204.375f, 524.25f, 213.75f });
804+
page1.add(new float[] { 243.0f, 209.25f, 243.0f, 134.25f, 280.5f, 134.25f, 318.0f, 134.25f, 243.0f, 209.25f, 318.0f, 134.25f, 318.0f, 171.75f, 318.0f, 209.25f });
805+
page1.add(new float[] { 168.0f, 359.25f, 93.0f, 209.25f, 168.0f, 209.25f, 243.0f, 209.25f, 168.0f, 359.25f, 243.0f, 209.25f, 205.5f, 284.25f, 168.0f, 359.25f });
806+
page1.add(new float[] { 18.0f, 434.25f, 18.0f, 359.25f, 55.5f, 359.25f, 93.0f, 359.25f, 18.0f, 434.25f, 93.0f, 359.25f, 93.0f, 396.75f, 93.0f, 434.25f });
807+
808+
failure |= qAssert(page1, getQuadPoints(doc, 1, 0), sb, 1, 0);
809+
failure |= qAssert(page1, getQuadPoints(doc, 1, 1), sb, 1, 1);
810+
failure |= qAssert(page1, getQuadPoints(doc, 1, 2), sb, 1, 2);
811+
failure |= qAssert(page1, getQuadPoints(doc, 1, 3), sb, 1, 3);
812+
failure |= qAssert(page1, getQuadPoints(doc, 1, 4), sb, 1, 4);
813+
failure |= qAssert(page1, getQuadPoints(doc, 1, 5), sb, 1, 5);
814+
815+
if (failure) {
816+
System.out.print(sb.toString());
817+
Assert.fail("Quad points were not correct");
818+
}
819+
820+
remove("pr-480-link-shapes", doc);
821+
}
822+
}
823+
721824
// TODO:
722825
// + More form controls.
723826
// + Custom meta info.

0 commit comments

Comments
 (0)