From commits-return-12660-archive-asf-public=cust-asf.ponee.io@pdfbox.apache.org Tue Jun 12 18:35:16 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id 9B3A6180608 for ; Tue, 12 Jun 2018 18:35:15 +0200 (CEST) Received: (qmail 77418 invoked by uid 500); 12 Jun 2018 16:35:14 -0000 Mailing-List: contact commits-help@pdfbox.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@pdfbox.apache.org Delivered-To: mailing list commits@pdfbox.apache.org Received: (qmail 77405 invoked by uid 99); 12 Jun 2018 16:35:14 -0000 Received: from Unknown (HELO svn01-us-west.apache.org) (209.188.14.144) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 12 Jun 2018 16:35:14 +0000 Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 5F81B3A006C for ; Tue, 12 Jun 2018 16:35:13 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1833411 - /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDTextAppearanceHandler.java Date: Tue, 12 Jun 2018 16:35:12 -0000 To: commits@pdfbox.apache.org From: tilman@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20180612163513.5F81B3A006C@svn01-us-west.apache.org> Author: tilman Date: Tue Jun 12 16:35:12 2018 New Revision: 1833411 URL: http://svn.apache.org/viewvc?rev=1833411&view=rev Log: PDFBOX-3353: support /Paragraph and /NewParagraph; use fixed sizes for BBox, adjust rectangle only if NoZoom isn't set, set flags if missing Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDTextAppearanceHandler.java Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDTextAppearanceHandler.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDTextAppearanceHandler.java?rev=1833411&r1=1833410&r2=1833411&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDTextAppearanceHandler.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDTextAppearanceHandler.java Tue Jun 12 16:35:12 2018 @@ -23,6 +23,7 @@ import java.util.HashSet; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDAppearanceContentStream; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.font.PDType1Font; @@ -50,6 +51,8 @@ public class PDTextAppearanceHandler ext SUPPORTED_NAMES.add(PDAnnotationText.NAME_CROSS); SUPPORTED_NAMES.add(PDAnnotationText.NAME_HELP); SUPPORTED_NAMES.add(PDAnnotationText.NAME_CIRCLE); + SUPPORTED_NAMES.add(PDAnnotationText.NAME_PARAGRAPH); + SUPPORTED_NAMES.add(PDAnnotationText.NAME_NEW_PARAGRAPH); } public PDTextAppearanceHandler(PDAnnotation annotation) @@ -71,7 +74,10 @@ public class PDTextAppearanceHandler ext PDAnnotationText annotation = (PDAnnotationText) getAnnotation(); if (!SUPPORTED_NAMES.contains(annotation.getName())) { - //TODO Comment, Key, NewParagraph, Paragraph + //TODO Comment, Key + // BBox values: + // key 18 18 + // Comment 18 18 return; } @@ -91,26 +97,28 @@ public class PDTextAppearanceHandler ext setOpacity(contentStream, annotation.getConstantOpacity()); - PDRectangle rect = getRectangle(); - PDRectangle bbox = rect.createRetranslatedRectangle(); - annotation.getNormalAppearanceStream().setBBox(bbox); - switch (annotation.getName()) { case PDAnnotationText.NAME_NOTE: - drawNote(contentStream, bbox); + drawNote(annotation, contentStream); break; case PDAnnotationText.NAME_CROSS: - drawCross(contentStream, bbox); + drawCross(annotation, contentStream); break; case PDAnnotationText.NAME_CIRCLE: - drawCircles(contentStream, bbox); + drawCircles(annotation, contentStream); break; case PDAnnotationText.NAME_INSERT: - drawInsert(contentStream, bbox); + drawInsert(annotation, contentStream); break; case PDAnnotationText.NAME_HELP: - drawHelp(contentStream, bbox); + drawHelp(annotation, contentStream); + break; + case PDAnnotationText.NAME_PARAGRAPH: + drawParagraph(annotation, contentStream); + break; + case PDAnnotationText.NAME_NEW_PARAGRAPH: + drawNewParagraph(annotation, contentStream); break; default: break; @@ -122,10 +130,44 @@ public class PDTextAppearanceHandler ext } } - private void drawNote(final PDAppearanceContentStream contentStream, PDRectangle bbox) + private PDRectangle adjustRectAndBBox(PDAnnotationText annotation, float width, float height) + { + // For /Note (other types have different values): + // Adobe takes the left upper bound as anchor, and adjusts the rectangle to 18 x 20. + // Observed with files 007071.pdf, 038785.pdf, 038787.pdf, + // but not with 047745.pdf p133 and 084374.pdf p48, both have the NoZoom flag. + // there the BBox is also set to fixed values, but the rectangle is left untouched. + // When no flags are there, Adobe sets /F 24 = NoZoom NoRotate. + + PDRectangle rect = getRectangle(); + PDRectangle bbox; + if (!annotation.isNoZoom()) + { + rect.setUpperRightX(rect.getLowerLeftX() + width); + rect.setLowerLeftY(rect.getUpperRightY() - height); + annotation.setRectangle(rect); + } + if (!annotation.getCOSObject().containsKey(COSName.F)) + { + // We set these flags because Adobe does so, but PDFBox doesn't support them when rendering. + annotation.setNoRotate(true); + annotation.setNoZoom(true); + } + bbox = new PDRectangle(width, height); + annotation.getNormalAppearanceStream().setBBox(bbox); + return bbox; + } + + private void drawNote(PDAnnotationText annotation, final PDAppearanceContentStream contentStream) throws IOException { - contentStream.setLineJoinStyle(1); // get round edge the easy way + PDRectangle bbox = adjustRectAndBBox(annotation, 18, 20); + contentStream.setMiterLimit(4); + + // get round edge the easy way. Adobe uses 4 lines with 4 arcs of radius 0.785 which is bigger. + contentStream.setLineJoinStyle(1); + + contentStream.setLineCapStyle(0); contentStream.setLineWidth(0.61f); // value from Adobe contentStream.addRect(1, 1, bbox.getWidth() - 2, bbox.getHeight() - 2); contentStream.moveTo(bbox.getWidth() / 4, bbox.getHeight() / 7 * 2); @@ -139,9 +181,11 @@ public class PDTextAppearanceHandler ext contentStream.fillAndStroke(); } - private void drawCircles(final PDAppearanceContentStream contentStream, PDRectangle bbox) + private void drawCircles(PDAnnotationText annotation, final PDAppearanceContentStream contentStream) throws IOException { + PDRectangle bbox = adjustRectAndBBox(annotation, 20, 20); + // strategy used by Adobe: // 1) add small circle in white using /ca /CA 0.6 and width 1 // 2) fill @@ -150,10 +194,8 @@ public class PDTextAppearanceHandler ext // 5) stroke + fill // with square width 20 small r = 6.36, large r = 9.756 - // should be a square, but who knows... - float min = Math.min(bbox.getWidth(), bbox.getHeight()); - float smallR = min / 20 * 6.36f; - float largeR = min / 20 * 9.756f; + float smallR = 6.36f; + float largeR = 9.756f; contentStream.setMiterLimit(4); contentStream.setLineJoinStyle(1); @@ -177,9 +219,11 @@ public class PDTextAppearanceHandler ext contentStream.fillAndStroke(); } - private void drawInsert(final PDAppearanceContentStream contentStream, PDRectangle bbox) + private void drawInsert(PDAnnotationText annotation, final PDAppearanceContentStream contentStream) throws IOException { + PDRectangle bbox = adjustRectAndBBox(annotation, 17, 20); + contentStream.setMiterLimit(4); contentStream.setLineJoinStyle(0); contentStream.setLineCapStyle(0); @@ -190,9 +234,11 @@ public class PDTextAppearanceHandler ext contentStream.closeAndFillAndStroke(); } - private void drawCross(final PDAppearanceContentStream contentStream, PDRectangle bbox) + private void drawCross(PDAnnotationText annotation, final PDAppearanceContentStream contentStream) throws IOException { + PDRectangle bbox = adjustRectAndBBox(annotation, 19, 19); + // should be a square, but who knows... float min = Math.min(bbox.getWidth(), bbox.getHeight()); @@ -221,9 +267,11 @@ public class PDTextAppearanceHandler ext contentStream.closeAndFillAndStroke(); } - private void drawHelp(final PDAppearanceContentStream contentStream, PDRectangle bbox) + private void drawHelp(PDAnnotationText annotation, final PDAppearanceContentStream contentStream) throws IOException { + PDRectangle bbox = adjustRectAndBBox(annotation, 20, 20); + float min = Math.min(bbox.getWidth(), bbox.getHeight()); contentStream.setMiterLimit(4); @@ -249,11 +297,89 @@ public class PDTextAppearanceHandler ext // rescale so that "?" fits into circle and move "?" to circle center // values gathered by trial and error contentStream.transform(Matrix.getScaleInstance(0.001f * min / 2.25f, 0.001f * min / 2.25f)); - contentStream.transform(Matrix.getTranslateInstance(540, 375)); + contentStream.transform(Matrix.getTranslateInstance(555, 375)); // we get the shape of an Helvetica "?" and use that one. // Adobe uses a different font (which one?), or created the shape from scratch. GeneralPath path = PDType1Font.HELVETICA.getPath("question"); + addPath(contentStream, path); + contentStream.restoreGraphicsState(); + // draw the outer circle counterclockwise to fill area between circle and "?" + drawCircle2(contentStream, min / 2, min / 2, min / 2 - 1); + contentStream.fillAndStroke(); + } + + //TODO this is mostly identical to drawHelp, except for scale, translation and symbol + private void drawParagraph(PDAnnotationText annotation, final PDAppearanceContentStream contentStream) + throws IOException + { + PDRectangle bbox = adjustRectAndBBox(annotation, 20, 20); + + float min = Math.min(bbox.getWidth(), bbox.getHeight()); + + contentStream.setMiterLimit(4); + contentStream.setLineJoinStyle(1); + contentStream.setLineCapStyle(0); + contentStream.setLineWidth(0.59f); // value from Adobe + + // Adobe first fills a white circle with CA ca 0.6, so do we + contentStream.saveGraphicsState(); + contentStream.setLineWidth(1); + PDExtendedGraphicsState gs = new PDExtendedGraphicsState(); + gs.setAlphaSourceFlag(false); + gs.setStrokingAlphaConstant(0.6f); + gs.setNonStrokingAlphaConstant(0.6f); + gs.setBlendMode(BlendMode.NORMAL); + contentStream.setGraphicsStateParameters(gs); + contentStream.setNonStrokingColor(1f); + drawCircle2(contentStream, min / 2, min / 2, min / 2 - 1); + contentStream.fill(); + contentStream.restoreGraphicsState(); + + contentStream.saveGraphicsState(); + // rescale so that "?" fits into circle and move "?" to circle center + // values gathered by trial and error + contentStream.transform(Matrix.getScaleInstance(0.001f * min / 3, 0.001f * min / 3)); + contentStream.transform(Matrix.getTranslateInstance(850, 900)); + + // we get the shape of an Helvetica "?" and use that one. + // Adobe uses a different font (which one?), or created the shape from scratch. + GeneralPath path = PDType1Font.HELVETICA.getPath("paragraph"); + addPath(contentStream, path); + contentStream.restoreGraphicsState(); + // draw the outer circle counterclockwise to fill area between circle and "?" + drawCircle2(contentStream, min / 2, min / 2, min / 2 - 1); + contentStream.fillAndStroke(); + } + + private void drawNewParagraph(PDAnnotationText annotation, final PDAppearanceContentStream contentStream) + throws IOException + { + adjustRectAndBBox(annotation, 13, 20); + + contentStream.setMiterLimit(4); + contentStream.setLineJoinStyle(0); + contentStream.setLineCapStyle(0); + contentStream.setLineWidth(0.59f); // value from Adobe + + // small triangle (values from Adobe) + contentStream.moveTo(6.4995f, 20); + contentStream.lineTo(0.295f, 7.287f); + contentStream.lineTo(12.705f, 7.287f); + contentStream.closeAndFillAndStroke(); + + // rescale and translate so that "NP" fits below the triangle + // values gathered by trial and error + contentStream.transform(Matrix.getScaleInstance(0.001f * 4, 0.001f * 4)); + contentStream.transform(Matrix.getTranslateInstance(200, 0)); + addPath(contentStream, PDType1Font.HELVETICA_BOLD.getPath("N")); + contentStream.transform(Matrix.getTranslateInstance(1300, 0)); + addPath(contentStream, PDType1Font.HELVETICA_BOLD.getPath("P")); + contentStream.fill(); + } + + private void addPath(final PDAppearanceContentStream contentStream, GeneralPath path) throws IOException + { PathIterator it = path.getPathIterator(new AffineTransform()); double[] coords = new double[6]; while (!it.isDone()) @@ -283,10 +409,6 @@ public class PDTextAppearanceHandler ext } it.next(); } - contentStream.restoreGraphicsState(); - // draw the outer circle counterclockwise to fill area between circle and "?" - drawCircle2(contentStream, min / 2, min / 2, min / 2 - 1); - contentStream.fillAndStroke(); } @Override