You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ms...@apache.org on 2015/03/13 12:22:37 UTC

svn commit: r1666401 - /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java

Author: msahyoun
Date: Fri Mar 13 11:22:37 2015
New Revision: 1666401

URL: http://svn.apache.org/r1666401
Log:
PDFBOX-1402, PDFBOX-2333 refine calculation of vertical offset

Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java?rev=1666401&r1=1666400&r2=1666401&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java Fri Mar 13 11:22:37 2015
@@ -55,6 +55,9 @@ class AppearanceGeneratorHelper
 {
     private static final Log LOG = LogFactory.getLog(AppearanceGeneratorHelper.class);
 
+    // scale of font glyph units
+    private static final float GLYPH_TO_PDF_SCALE = 1000f;
+    
     private final PDVariableText parent;
 
     private String value;
@@ -292,27 +295,14 @@ class AppearanceGeneratorHelper
 
         float paddingLeft = contentEdge.getLowerLeftX();
         float paddingRight = boundingBox.getUpperRightX() - contentEdge.getUpperRightX();
-        
-        float verticalOffset = getVerticalOffset(boundingBox, font, fontSize, tokens);
-        
-        // Acrobat shifts the value so it aligns to the bottom if
-        // the font's caps are larger than the height of the borderEdge
-        //
-        // This is based on a small sample of test files and might not be generally the case.
-        // The fontHeight calculation has been taken from getVerticalOffset().
-        // We potentially need to revisit that calculation
-        float fontHeight = boundingBox.getHeight() - verticalOffset * 2;
-
-        if (fontHeight + 2 * paddingEdge.getLowerLeftX() > paddingEdge.getHeight())
-        {
-            verticalOffset = font.getBoundingBox().getHeight() / 1000 * fontSize
-                    - paddingEdge.getHeight();
-        }
 
+        // calculation of the vertical offset from where the text will be printed 
+        float verticalOffset = calculateVerticalOffset(paddingEdge, contentEdge, font, fontSize);
+        
         float leftOffset = 0f;
 
         // Acrobat aligns left regardless of the quadding if the text is wider than the remaining width
-        float stringWidth = (font.getStringWidth(value) / 1000) * fontSize;
+        float stringWidth = (font.getStringWidth(value) / GLYPH_TO_PDF_SCALE) * fontSize;
         
         int q = getQ();
         if (q == PDTextField.QUADDING_LEFT
@@ -343,10 +333,6 @@ class AppearanceGeneratorHelper
         }
         else
         {
-            // adjust offset
-            // TODO: offset is dependent on border value if there is one
-            verticalOffset = verticalOffset + font.getFontDescriptor().getAscent() / 1000 * fontSize;
-
             composer.newLineAtOffset(leftOffset, verticalOffset);
 
             PlainText textContent = new PlainText(value);
@@ -527,14 +513,14 @@ class AppearanceGeneratorHelper
         if (parent instanceof PDTextField && ((PDTextField) parent).doNotScroll())
         {
             // if we don't scroll then we will shrink the font to fit into the text area.
-            float widthAtFontSize1 = pdFont.getStringWidth(value) / 1000.f;
+            float widthAtFontSize1 = pdFont.getStringWidth(value) / GLYPH_TO_PDF_SCALE;
             float availableWidth = getAvailableWidth(boundingBox, getLineWidth(tokens));
             widthBasedFontSize = availableWidth / widthAtFontSize1;
         }
         if (fontSize == 0)
         {
             float lineWidth = getLineWidth(tokens);
-            float height = pdFont.getFontDescriptor().getFontBoundingBox().getHeight() / 1000f;
+            float height = pdFont.getFontDescriptor().getFontBoundingBox().getHeight() / GLYPH_TO_PDF_SCALE;
             float availHeight = getAvailableHeight(boundingBox, lineWidth);
             fontSize = Math.min(availHeight / height, widthBasedFontSize);
         }
@@ -544,36 +530,42 @@ class AppearanceGeneratorHelper
     /**
      * Calculates where to start putting the text in the box. The positioning is not quite as
      * accurate as when Acrobat places the elements, but it works though.
+     * 
+     * @param paddingEdge the content edge
+     * @param contentEdge the content edge
+     * @param pdFont the font to use for formatting
+     * @param fontSite the font size to use for formating
      *
      * @return the sting for representing the start position of the text
      *
      * @throws IOException If there is an error calculating the text position.
      */
-    private float getVerticalOffset(PDRectangle boundingBox, PDFont pdFont, float fontSize,
-            List<Object> tokens) throws IOException
+    private float calculateVerticalOffset(PDRectangle paddingEdge, 
+            PDRectangle contentEdge, PDFont pdFont, float fontSize) throws IOException
     {
-        float lineWidth = getLineWidth(tokens);
         float verticalOffset;
+        float capHeight = getCapHeight(pdFont, fontSize);
+        float descent = getDescent(pdFont, fontSize);
+        
         if (parent instanceof PDTextField && ((PDTextField) parent).isMultiline())
         {
-            int rows = (int) (getAvailableHeight(boundingBox, lineWidth) / ((int) fontSize));
-            verticalOffset = ((rows) * fontSize) - fontSize;
+            verticalOffset = contentEdge.getUpperRightY() + descent;
         }
         else
         {
-            // BJL 9/25/2004
-            // This algorithm is a little bit of black magic. It does
-            // not appear to be documented anywhere. Through examining a few
-            // PDF documents and the value that Acrobat places in there I
-            // have determined that the below method of computing the position
-            // is correct for certain documents, but maybe not all. It does
-            // work f1040ez.pdf and Form_1.pdf
-            PDFontDescriptor fd = pdFont.getFontDescriptor();
-            float bBoxHeight = boundingBox.getHeight();
-            float fontHeight = fd.getFontBoundingBox().getHeight() + 2 * fd.getDescent();
-            fontHeight = (fontHeight / 1000) * fontSize;
-            verticalOffset = (bBoxHeight - fontHeight) / 2;
+            // Acrobat shifts the value so it aligns to the bottom if
+            // the font's caps are larger than the height of the paddingEdge
+            if (capHeight > paddingEdge.getHeight())
+            {
+                verticalOffset = paddingEdge.getLowerLeftX() 
+                        - pdFont.getFontDescriptor().getDescent() / GLYPH_TO_PDF_SCALE * fontSize;
+            }
+            else 
+            {
+                verticalOffset = (paddingEdge.getHeight() - capHeight) / 2f + paddingEdge.getLowerLeftX();
+            }
         }
+        
         return verticalOffset;
     }
 
@@ -598,6 +590,49 @@ class AppearanceGeneratorHelper
     }
     
     /**
+     * Get the capHeight for a font.
+     * @throws IOException in case the font information can not be retrieved.
+     */
+    private float getCapHeight(PDFont pdFont, float fontSize) throws IOException
+    {
+        final PDFontDescriptor fontDescriptor = pdFont.getFontDescriptor();
+        
+        // as the font descriptor might be null or the cap height might be 0 
+        // alternate calculation for the cap height
+        if (fontDescriptor == null || fontDescriptor.getCapHeight() == 0)
+        {
+            // TODO: refine the calculation if needed
+            return pdFont.getBoundingBox().getHeight() / GLYPH_TO_PDF_SCALE * fontSize * 0.7f;
+        }
+        else
+        {
+            return pdFont.getFontDescriptor().getCapHeight() / GLYPH_TO_PDF_SCALE * fontSize;
+        }
+    }
+    
+    /**
+     * Get the descent for a font.
+     * @throws IOException in case the font information can not be retrieved.
+     */
+    private float getDescent(PDFont pdFont, float fontSize) throws IOException
+    {
+        final PDFontDescriptor fontDescriptor = pdFont.getFontDescriptor();
+        
+        // as the font descriptor might be null or the cap height might be 0 
+        // alternate calculation for the cap height
+        if (fontDescriptor == null || fontDescriptor.getDescent() == 0)
+        {
+            // TODO: refine the calculation if needed
+            return -pdFont.getBoundingBox().getHeight() / GLYPH_TO_PDF_SCALE * fontSize * 0.3f;
+        }
+        else
+        {
+            return pdFont.getFontDescriptor().getDescent() / GLYPH_TO_PDF_SCALE * fontSize;
+        }
+    }
+    
+    
+    /**
      * Apply padding to a box.
      * 
      * @param original box