Author: tilman Date: Fri Feb 2 16:11:25 2018 New Revision: 1822981 URL: http://svn.apache.org/viewvc?rev=1822981&view=rev Log: PDFBOX-3353: use form content stream; do some rect padding for curves
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDHighlightAppearanceHandler.java Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDHighlightAppearanceHandler.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDHighlightAppearanceHandler.java?rev=1822981&r1=1822980&r2=1822981&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDHighlightAppearanceHandler.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDHighlightAppearanceHandler.java Fri Feb 2 16:11:25 2018 @@ -17,25 +17,24 @@ package org.apache.pdfbox.pdmodel.interactive.annotation.handlers; import java.io.IOException; -import java.io.OutputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.PDFormContentStream; import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.graphics.blend.BlendMode; -import org.apache.pdfbox.pdmodel.graphics.color.PDColor; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationHighlight; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceContentStream; -import org.apache.pdfbox.util.Charsets; /** - * + * + * @author Tilman Hausherr */ public class PDHighlightAppearanceHandler extends PDAbstractAppearanceHandler { @@ -90,10 +89,23 @@ public class PDHighlightAppearanceHandle maxX = Math.max(maxX, x); maxY = Math.max(maxY, y); } - rect.setLowerLeftX(Math.min(minX - ab.width / 2, rect.getLowerLeftX())); - rect.setLowerLeftY(Math.min(minY - ab.width / 2, rect.getLowerLeftY())); - rect.setUpperRightX(Math.max(maxX + ab.width, rect.getUpperRightX())); - rect.setUpperRightY(Math.max(maxY + ab.width, rect.getUpperRightY())); + + // get the delta used for curves and use it for padding + float maxDelta = 0; + for (int i = 0; i < pathsArray.length / 8; ++i) + { + // one of the two is 0, depending whether the rectangle is + // horizontal or vertical + // if it is diagonal then... uh... + float delta = Math.max((pathsArray[i + 0] - pathsArray[i + 4]) / 4, + (pathsArray[i + 1] - pathsArray[i + 5]) / 4); + maxDelta = Math.max(delta, maxDelta); + } + + rect.setLowerLeftX(Math.min(minX - ab.width / 2 - maxDelta, rect.getLowerLeftX())); + rect.setLowerLeftY(Math.min(minY - ab.width / 2 - maxDelta, rect.getLowerLeftY())); + rect.setUpperRightX(Math.max(maxX + ab.width + maxDelta, rect.getUpperRightX())); + rect.setUpperRightY(Math.max(maxY + ab.width + maxDelta, rect.getUpperRightY())); annotation.setRectangle(rect); try @@ -111,132 +123,101 @@ public class PDHighlightAppearanceHandle cs.setGraphicsStateParameters(r0); cs.setGraphicsStateParameters(r1); //TODO replace with document.getDocument().createCOSStream() - COSStream mwfoformStrm = new COSStream(); - OutputStream os = mwfoformStrm.createOutputStream(); - os.write("/Form Do".getBytes(Charsets.ISO_8859_1)); - os.close(); - PDFormXObject mwfofrm = new PDFormXObject(mwfoformStrm); - cs.drawForm(mwfofrm); - COSStream frmStrm2 = new COSStream(); - PDFormXObject frm2 = new PDFormXObject(frmStrm2); - PDResources res = new PDResources(); - mwfofrm.setBBox(annotation.getRectangle()); - mwfofrm.setResources(res); + // or call new PDFormXObject(document); + PDFormXObject frm1 = new PDFormXObject(new COSStream()); + PDFormXObject frm2 = new PDFormXObject(new COSStream()); + frm1.setResources(new PDResources()); + try (PDFormContentStream mwfofrmCS = new PDFormContentStream(frm1)) + { + mwfofrmCS.drawForm(frm2); + } + frm1.setBBox(annotation.getRectangle()); COSDictionary groupDict = new COSDictionary(); groupDict.setItem(COSName.S, COSName.TRANSPARENCY); //TODO PDFormXObject.setGroup() is missing - mwfofrm.getCOSObject().setItem(COSName.GROUP, groupDict); - res.put(COSName.getPDFName("Form"), frm2); + frm1.getCOSObject().setItem(COSName.GROUP, groupDict); + cs.drawForm(frm1); frm2.setBBox(annotation.getRectangle()); - os = frm2.getCOSObject().createOutputStream(); - //TODO why can't we get a "classic" content stream? - PDColor color = ab.color; - switch (color.getComponents().length) - { - case 1: - os.write(String.format(java.util.Locale.US, "%.6f g%n", - color.getComponents()[0]).getBytes(Charsets.ISO_8859_1)); - break; - case 3: - os.write(String.format(java.util.Locale.US, "%.6f %.6f %.6f rg%n", - color.getComponents()[0], - color.getComponents()[1], - color.getComponents()[2]).getBytes(Charsets.ISO_8859_1)); - break; - case 4: - os.write(String.format(java.util.Locale.US, "%.6f %.6f %.6f %.6f k%n", - color.getComponents()[0], - color.getComponents()[1], - color.getComponents()[2], - color.getComponents()[3]).getBytes(Charsets.ISO_8859_1)); - break; - default: - break; - } - int of = 0; - while (of + 7 < pathsArray.length) + try (PDFormContentStream frm2CS = new PDFormContentStream(frm2)) { - // quadpoints spec sequence is incorrect, correct one is (4,5 0,1 2,3 6,7) - // https://stackoverflow.com/questions/9855814/pdf-spec-vs-acrobat-creation-quadpoints - - // for "curvy" highlighting, two Bézier control points are used that seem to have a - // distance of about 1/4 of the height. - // note that curves won't appear if outside of the rectangle - float delta = 0; - if (pathsArray[of + 0] == pathsArray[of + 4] && - pathsArray[of + 1] == pathsArray[of + 3] && - pathsArray[of + 2] == pathsArray[of + 6] && - pathsArray[of + 5] == pathsArray[of + 7]) - { - // horizontal highlight - delta = (pathsArray[of + 1] - pathsArray[of + 5]) / 4; + frm2CS.setNonStrokingColor(ab.color); + int of = 0; + while (of + 7 < pathsArray.length) + { + // quadpoints spec sequence is incorrect, correct one is (4,5 0,1 2,3 6,7) + // https://stackoverflow.com/questions/9855814/pdf-spec-vs-acrobat-creation-quadpoints + + // for "curvy" highlighting, two Bézier control points are used that seem to have a + // distance of about 1/4 of the height. + // note that curves won't appear if outside of the rectangle + float delta = 0; + if (pathsArray[of + 0] == pathsArray[of + 4] && + pathsArray[of + 1] == pathsArray[of + 3] && + pathsArray[of + 2] == pathsArray[of + 6] && + pathsArray[of + 5] == pathsArray[of + 7]) + { + // horizontal highlight + delta = (pathsArray[of + 1] - pathsArray[of + 5]) / 4; + } + else if (pathsArray[of + 1] == pathsArray[of + 5] && + pathsArray[of + 0] == pathsArray[of + 2] && + pathsArray[of + 3] == pathsArray[of + 7] && + pathsArray[of + 4] == pathsArray[of + 6]) + { + // vertical highlight + delta = (pathsArray[of + 0] - pathsArray[of + 4]) / 4; + } + + frm2CS.moveTo(pathsArray[of + 4], pathsArray[of + 5]); + + if (pathsArray[of + 0] == pathsArray[of + 4]) + { + // horizontal highlight + frm2CS.curveTo(pathsArray[of + 4] - delta, pathsArray[of + 5] + delta, + pathsArray[of + 0] - delta, pathsArray[of + 1] - delta, + pathsArray[of + 0], pathsArray[of + 1]); + } + else if (pathsArray[of + 5] == pathsArray[of + 1]) + { + // vertical highlight + frm2CS.curveTo(pathsArray[of + 4] + delta, pathsArray[of + 5] + delta, + pathsArray[of + 0] - delta, pathsArray[of + 1] + delta, + pathsArray[of + 0], pathsArray[of + 1]); + } + else + { + frm2CS.lineTo(pathsArray[of + 0], pathsArray[of + 1]); + } + frm2CS.lineTo(pathsArray[of + 2], pathsArray[of + 3]); + if (pathsArray[of + 2] == pathsArray[of + 6]) + { + // horizontal highlight + frm2CS.curveTo(pathsArray[of + 2] + delta, pathsArray[of + 3] - delta, + pathsArray[of + 6] + delta, pathsArray[of + 7] + delta, + pathsArray[of + 6], pathsArray[of + 7]); + } + else if (pathsArray[of + 3] == pathsArray[of + 7]) + { + // vertical highlight + frm2CS.curveTo(pathsArray[of + 2] - delta, pathsArray[of + 3] - delta, + pathsArray[of + 6] + delta, pathsArray[of + 7] - delta, + pathsArray[of + 6], pathsArray[of + 7]); + } + else + { + frm2CS.lineTo(pathsArray[of + 6], pathsArray[of + 7]); + } + + frm2CS.fill(); + of += 8; + + //TODO Adobe puts a "w" (line width). Why? + //TODO If quadpoints is not present or the conforming reader does not recognize it, + // the region specified by the Rect entry should be used. + // QuadPoints shall be ignored if any coordinate in the array lies + // outside the region specified by Rect } - else if (pathsArray[of + 1] == pathsArray[of + 5] && - pathsArray[of + 0] == pathsArray[of + 2] && - pathsArray[of + 3] == pathsArray[of + 7] && - pathsArray[of + 4] == pathsArray[of + 6]) - { - // vertical highlight - delta = (pathsArray[of + 0] - pathsArray[of + 4]) / 4; - } - - os.write(String.format(java.util.Locale.US, "%.4f %.4f m%n", - pathsArray[of + 4], pathsArray[of + 5]).getBytes(Charsets.ISO_8859_1)); - if (pathsArray[of + 0] == pathsArray[of + 4]) - { - // horizontal highlight - os.write(String.format(java.util.Locale.US, "%.4f %.4f %.4f %.4f %.4f %.4f c%n", - pathsArray[of + 4] - delta, pathsArray[of + 5] + delta, - pathsArray[of + 0] - delta, pathsArray[of + 1] - delta, - pathsArray[of + 0], pathsArray[of + 1]).getBytes(Charsets.ISO_8859_1)); - } - else if (pathsArray[of + 5] == pathsArray[of + 1]) - { - // vertical highlight - os.write(String.format(java.util.Locale.US, "%.4f %.4f %.4f %.4f %.4f %.4f c%n", - pathsArray[of + 4] + delta, pathsArray[of + 5] + delta, - pathsArray[of + 0] - delta, pathsArray[of + 1] + delta, - pathsArray[of + 0], pathsArray[of + 1]).getBytes(Charsets.ISO_8859_1)); - } - else - { - os.write(String.format(java.util.Locale.US, "%.4f %.4f l%n", - pathsArray[of + 0], pathsArray[of + 1]).getBytes(Charsets.ISO_8859_1)); - } - os.write(String.format(java.util.Locale.US, "%.4f %.4f l%n", - pathsArray[of + 2], pathsArray[of + 3]).getBytes(Charsets.ISO_8859_1)); - if (pathsArray[of + 2] == pathsArray[of + 6]) - { - // horizontal highlight - os.write(String.format(java.util.Locale.US, "%.4f %.4f %.4f %.4f %.4f %.4f c%n", - pathsArray[of + 2] + delta, pathsArray[of + 3] - delta, - pathsArray[of + 6] + delta, pathsArray[of + 7] + delta, - pathsArray[of + 6], pathsArray[of + 7]).getBytes(Charsets.ISO_8859_1)); - } - else if (pathsArray[of + 3] == pathsArray[of + 7]) - { - // vertical highlight - os.write(String.format(java.util.Locale.US, "%.4f %.4f %.4f %.4f %.4f %.4f c%n", - pathsArray[of + 2] - delta, pathsArray[of + 3] - delta, - pathsArray[of + 6] + delta, pathsArray[of + 7] - delta, - pathsArray[of + 6], pathsArray[of + 7]).getBytes(Charsets.ISO_8859_1)); - } - else - { - os.write(String.format(java.util.Locale.US, "%.4f %.4f l%n", - pathsArray[of + 6], pathsArray[of + 7]).getBytes(Charsets.ISO_8859_1)); - } - - os.write("f\n".getBytes(Charsets.ISO_8859_1)); - of += 8; - - //TODO Adobe puts a "w" (line width). Why? - //TODO If quadpoints is not present or the conforming reader does not recognize it, - // the region specified by the Rect entry should be used. - // QuadPoints shall be ignored if any coordinate in the array lies - // outside the region specified by Rect } - os.close(); } } catch (IOException ex)