diff --git a/pdfbox-layout-awt/src/main/java/org/apache/pdfbox/glyphlayout/awt/GlyphLayoutFontLoaderAwt.java b/pdfbox-layout-awt/src/main/java/org/apache/pdfbox/glyphlayout/awt/GlyphLayoutFontLoaderAwt.java index cfd5d6eb2c3..da57839ae8c 100644 --- a/pdfbox-layout-awt/src/main/java/org/apache/pdfbox/glyphlayout/awt/GlyphLayoutFontLoaderAwt.java +++ b/pdfbox-layout-awt/src/main/java/org/apache/pdfbox/glyphlayout/awt/GlyphLayoutFontLoaderAwt.java @@ -42,18 +42,23 @@ */ public class GlyphLayoutFontLoaderAwt { + protected final GlyphLayoutProcessorAwt glyphLayoutProcessor; /** * Mapping from PDFBox font to AWT font */ private final Map awtFontMap = new ConcurrentHashMap<>(); + public GlyphLayoutFontLoaderAwt(GlyphLayoutProcessorAwt glyphLayoutProcessor) { + this.glyphLayoutProcessor = glyphLayoutProcessor; + } + /** * Loads the AWT font needed for layout * * @param pdDocument document * @param inputStream of the font - * @return pdType0Font PDFBox font + * @return PDType0Font PDFBox font * @throws IOException if font can not be loaded * @throws FontFormatException if the font is bad */ @@ -70,7 +75,7 @@ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream) * @param inputStream of the font * @param embedSubset True if the font will be subset before embedding. Set this to false when * creating a font for AcroForm. - * @return pdType0Font PDFBox font + * @return PDType0Font PDFBox font * @throws IOException if font can not be loaded * @throws FontFormatException if the font is bad */ @@ -86,7 +91,7 @@ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream, bool * @param pdDocument document * @param inputStream of the font * @param fontOptions options for font - * @return pdType0Font PDFBox font + * @return PDType0Font PDFBox font * @throws IOException if font can not be loaded * @throws FontFormatException if the font is bad */ @@ -104,7 +109,7 @@ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream, Font * @param embedSubset True if the font will be subset before embedding. Set this to false when * creating a font for AcroForm. * @param fontOptions Options for font - * @return pdType0Font PDFBox font + * @return PDType0Font PDFBox font * @throws IOException if font can not be loaded * @throws FontFormatException if the font is bad */ @@ -117,6 +122,7 @@ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream, bool try (ByteArrayInputStream bais = new ByteArrayInputStream(inputStream.readAllBytes())) { PDType0Font pdType0Font = PDType0Font.load(pdDocument, bais, embedSubset); + pdType0Font.setGlyphLayoutProcessor(glyphLayoutProcessor); bais.reset(); loadAwtFont(pdType0Font, bais, fontOptions); return pdType0Font; @@ -155,7 +161,8 @@ protected void loadAwtFont(PDType0Font pdType0Font, InputStream inputStream, Fon */ public boolean supportsFont(PDFont font) { - return awtFontMap.containsKey(font); + return (font instanceof PDType0Font) + && awtFontMap.containsKey((PDType0Font)font); } /** @@ -164,7 +171,7 @@ public boolean supportsFont(PDFont font) * @param font PDFBox font * @return AWT font if available */ - public Font getAwtFont(PDType0Font font) + protected Font getAwtFont(PDType0Font font) { return awtFontMap.get(font); } diff --git a/pdfbox-layout-awt/src/main/java/org/apache/pdfbox/glyphlayout/awt/GlyphLayoutProcessorAwt.java b/pdfbox-layout-awt/src/main/java/org/apache/pdfbox/glyphlayout/awt/GlyphLayoutProcessorAwt.java index 1d09799972d..6f6ed5c35ad 100644 --- a/pdfbox-layout-awt/src/main/java/org/apache/pdfbox/glyphlayout/awt/GlyphLayoutProcessorAwt.java +++ b/pdfbox-layout-awt/src/main/java/org/apache/pdfbox/glyphlayout/awt/GlyphLayoutProcessorAwt.java @@ -47,7 +47,6 @@ */ public class GlyphLayoutProcessorAwt extends AbstractGlyphLayoutProcessor implements GlyphLayoutProcessorInterface { - private final GlyphLayoutFontLoaderAwt glyphLayoutFontLoaderAwt; /** @@ -56,7 +55,7 @@ public class GlyphLayoutProcessorAwt extends AbstractGlyphLayoutProcessor implem */ public GlyphLayoutProcessorAwt() { - this.glyphLayoutFontLoaderAwt = new GlyphLayoutFontLoaderAwt(); + this.glyphLayoutFontLoaderAwt = new GlyphLayoutFontLoaderAwt(this); } /** @@ -211,6 +210,7 @@ protected float getStringWidthUni(PDType0Font font, float fontSize, String text, return (float) rect.getWidth(); } + /** * Shows a text using glyph positioning (if needed) This text must have a uniform run direction. * diff --git a/pdfbox-layout-awt/src/test/java/org/apache/pdfbox/glyphlayout/awt/GlyphLayoutLigaturesAndKerningTest.java b/pdfbox-layout-awt/src/test/java/org/apache/pdfbox/glyphlayout/awt/GlyphLayoutLigaturesAndKerningTest.java index e08625b57cb..a0c485bfaa5 100644 --- a/pdfbox-layout-awt/src/test/java/org/apache/pdfbox/glyphlayout/awt/GlyphLayoutLigaturesAndKerningTest.java +++ b/pdfbox-layout-awt/src/test/java/org/apache/pdfbox/glyphlayout/awt/GlyphLayoutLigaturesAndKerningTest.java @@ -34,7 +34,7 @@ /** * Examples for ligatures and kerning * See PDFBOX-4951 - * + *

* The default processing of GlyphLayoutProcessor is with ligatures and kerning disabled. * You can enable ligatures and kerning using FontOptions, see below. * @@ -52,8 +52,8 @@ class GlyphLayoutLigaturesAndKerningTest extends TestBase /** * Check that missing glyph is caught like in main pdfbox. * - * @throws IOException - * @throws FontFormatException + * @throws IOException if an I/O error occurs + * @throws FontFormatException if the font contains errors */ @Test void testMissingGlyph() throws IOException, FontFormatException @@ -152,6 +152,11 @@ void testLigaturesAndKerning() throws IOException, FontFormatException, URISynta float f3 = glyphLayoutProcessor.getStringWidth(dejavuFont, fontSize, DEJAVU_STRING); float f4 = glyphLayoutProcessor.getStringWidth(dejavuLigKernFont, fontSize, DEJAVU_STRING); + float f5 = glyphLayoutProcessor.getStringWidth(dejavuLigKernFont, fontSize, DEJAVU_STRING+" "); + float f6 = glyphLayoutProcessor.getStringWidth(dejavuLigKernFont, fontSize, DEJAVU_STRING); + System.out.printf("mit leerz %f ohne %f%n", f5, f6); + + // equality is expected here, but this shows that the ordinary getStringWidth() isn't helpful assertEquals(f1, f2); diff --git a/pdfbox-layout-fop/src/main/java/org/apache/pdfbox/glyphlayout/fop/GlyphLayoutFontLoaderFop.java b/pdfbox-layout-fop/src/main/java/org/apache/pdfbox/glyphlayout/fop/GlyphLayoutFontLoaderFop.java index ee45853b7cf..ecfaae1f7b8 100644 --- a/pdfbox-layout-fop/src/main/java/org/apache/pdfbox/glyphlayout/fop/GlyphLayoutFontLoaderFop.java +++ b/pdfbox-layout-fop/src/main/java/org/apache/pdfbox/glyphlayout/fop/GlyphLayoutFontLoaderFop.java @@ -34,6 +34,7 @@ import org.apache.fop.fonts.FontUris; import org.apache.fop.fonts.MultiByteFont; +import org.apache.pdfbox.pdmodel.GlyphLayoutProcessorInterface; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDType0Font; @@ -50,18 +51,28 @@ */ public class GlyphLayoutFontLoaderFop { + private final GlyphLayoutProcessorInterface glyphLayoutProcessor; /** * Mapping from PDFBox font to AWT font */ private final Map fopFontMap = new ConcurrentHashMap<>(); + /** + * Creates a GlyphLayoutFontLoaderFop + * @param glyphLayoutProcessor the corresponding glyph layout processor + */ + public GlyphLayoutFontLoaderFop(GlyphLayoutProcessorInterface glyphLayoutProcessor) { + this.glyphLayoutProcessor = glyphLayoutProcessor; + } + /** * Loads the AWT font needed for layout * * @param pdDocument document * @param inputStream of the font - * @param embedSubset + * @param embedSubset True if the font will be subset before embedding. Set this to false when + * creating a font for AcroForm. * @return pdType0Font PDFBox font * @throws IOException if font can not be loaded */ @@ -75,6 +86,7 @@ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream, bool try (ByteArrayInputStream bais = new ByteArrayInputStream(inputStream.readAllBytes())) { pdType0Font = PDType0Font.load(pdDocument, bais, embedSubset); + pdType0Font.setGlyphLayoutProcessor(glyphLayoutProcessor); bais.reset(); loadFopFont(pdType0Font, bais); } @@ -143,7 +155,7 @@ public boolean supportsFont(PDFont font) * @param font PDFBox font * @return fop font if available */ - public MultiByteFont getFopFont(PDType0Font font) + protected MultiByteFont getFopFont(PDType0Font font) { return fopFontMap.get(font); } @@ -151,7 +163,7 @@ public MultiByteFont getFopFont(PDType0Font font) /** * Resolver needed to construct a FOP MultibyteFont */ - static class FopInputStreamResourceResolver implements ResourceResolver + protected static class FopInputStreamResourceResolver implements ResourceResolver { private final InputStream inputStream; diff --git a/pdfbox-layout-fop/src/main/java/org/apache/pdfbox/glyphlayout/fop/GlyphLayoutProcessorFop.java b/pdfbox-layout-fop/src/main/java/org/apache/pdfbox/glyphlayout/fop/GlyphLayoutProcessorFop.java index eae5e72458c..5c62d8759dc 100644 --- a/pdfbox-layout-fop/src/main/java/org/apache/pdfbox/glyphlayout/fop/GlyphLayoutProcessorFop.java +++ b/pdfbox-layout-fop/src/main/java/org/apache/pdfbox/glyphlayout/fop/GlyphLayoutProcessorFop.java @@ -60,7 +60,7 @@ public class GlyphLayoutProcessorFop extends AbstractGlyphLayoutProcessor implem */ public GlyphLayoutProcessorFop() { - this.glyphLayoutFontLoaderFop = new GlyphLayoutFontLoaderFop(); + this.glyphLayoutFontLoaderFop = new GlyphLayoutFontLoaderFop(this); } /** @@ -100,6 +100,7 @@ public boolean supportsFont(PDFont font) return glyphLayoutFontLoaderFop.supportsFont(font); } + /** * Loads the font needed for layout * @@ -139,7 +140,7 @@ protected float getStringWidthUni(PDType0Font font, float fontSize, String text, { TextAndGpa textAndGpa = computeGlyphsAndPositions(font, fontSize, text, bidiLevel); // PDType0Font.getStringWidth returns glyph widths in 1000-units. Convert to user space using font matrix and fontSize - float raw = font.getStringWidth(textAndGpa.getText()); + float raw = font.getStringWidthBasic(textAndGpa.getText()); float scaleX = font.getFontMatrix().getScaleX(); return raw * scaleX * fontSize; } diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/AbstractGlyphLayoutProcessor.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/AbstractGlyphLayoutProcessor.java index 52dd42120d1..c28fd9d867a 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/AbstractGlyphLayoutProcessor.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/AbstractGlyphLayoutProcessor.java @@ -76,9 +76,14 @@ protected abstract float getStringWidthUni(PDType0Font font, float fontSize, Str * @param fontSize font size * @param text text * @return string width + * @throws IllegalArgumentException if the font is not supported or glyphs are missing */ public float getStringWidth(PDType0Font font, float fontSize, String text) throws IOException { + if (!supportsFont(font)) + { + throw new IllegalArgumentException("font must be supported by the GlyphLayoutProcessor"); + } float width = 0f; List textAndBidiLevels = doBidiSplittingAndReordering(text); for (TextAndBidiLevel textAndBidiLevel: textAndBidiLevels) @@ -112,11 +117,15 @@ protected abstract void showTextUni(ContentStreamForGlyphLayoutInterface content * @param text text to show * * @throws IOException if an I/O exception occurs - * @throws IllegalArgumentException if glyphs are missing + * @throws IllegalArgumentException if the font is not supported or glyphs are missing */ public void showText(ContentStreamForGlyphLayoutInterface contentStream, PDType0Font font, float fontSize, String text) throws IOException { + if (!supportsFont(font)) + { + throw new IllegalArgumentException("font must be supported by the GlyphLayoutProcessor"); + } List textAndBidiLevels = doBidiSplittingAndReordering(text); for (TextAndBidiLevel textAndBidiLevel: textAndBidiLevels) { diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAbstractContentStream.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAbstractContentStream.java index 85497119f72..7d2d7683995 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAbstractContentStream.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAbstractContentStream.java @@ -353,12 +353,22 @@ public void showText(String text) throws IOException } else { - showTextInternal(text); - writeBytes(ASCII_SPACE); - writeOperator(OperatorName.SHOW_TEXT); + showTextBasic(text); } } + /** + * Shows the text + * + * @param text text to be shown + * @throws IOException if an I/O exception occurs + */ + protected void showTextBasic(String text) throws IOException { + showTextInternal(text); + writeBytes(ASCII_SPACE); + writeOperator(OperatorName.SHOW_TEXT); + } + /** * Shows the glyphs for the given glyph codes - only for PDType0Font * diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java index 82a54d36727..da92354b7ee 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDType0Font.java @@ -22,6 +22,7 @@ import java.io.InputStream; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import org.apache.logging.log4j.Logger; @@ -40,6 +41,7 @@ import org.apache.pdfbox.io.RandomAccessRead; import org.apache.pdfbox.io.RandomAccessReadBuffer; import org.apache.pdfbox.io.RandomAccessReadBufferedFile; +import org.apache.pdfbox.pdmodel.GlyphLayoutProcessorInterface; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.ResourceCache; import org.apache.pdfbox.util.Matrix; @@ -64,6 +66,8 @@ public class PDType0Font extends PDFont implements PDVectorFont private boolean isDescendantCJK; private PDCIDFontType2Embedder embedder; private TrueTypeFont ttf; + private GlyphLayoutProcessorInterface glyphLayoutProcessor; + /** * Constructor for reading a Type0 font from a PDF file. @@ -755,4 +759,41 @@ public CmapLookup getCmapLookup() return cmapLookup; } + /** + * + * @param glyphLayoutProcessor + */ + public void setGlyphLayoutProcessor(GlyphLayoutProcessorInterface glyphLayoutProcessor) { + Objects.requireNonNull(glyphLayoutProcessor, "glyphLayoutProcessor must be not null"); + this.glyphLayoutProcessor = glyphLayoutProcessor; + } + + /** + * Returns the width of the given Unicode string. + * If a glyph layout processor is set, it is used to compute the string width + * + * @param text The text to get the width of. + * @return The width of the string in 1/1000 units of text space. + * @throws IOException If there is an error getting the width information. + * @throws IllegalArgumentException if a character isn't supported by the font. + */ + @Override + public float getStringWidth(String text) throws IOException + { + return glyphLayoutProcessor != null && glyphLayoutProcessor.supportsFont(this) + ? glyphLayoutProcessor.getStringWidth((PDType0Font) this, 1.0f, text) / getFontMatrix().getScaleX() + : getStringWidthBasic(text); + } + /** + * Returns the width of the given Unicode string. + * + * @param text The text to get the width of. + * @return The width of the string in 1/1000 units of text space. + * @throws IOException If there is an error getting the width information. + * @throws IllegalArgumentException if a character isn't supported by the font. + */ + public float getStringWidthBasic(String text) throws IOException + { + return super.getStringWidth(text); + } } diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/AppearanceStyle.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/AppearanceStyle.java index df04d44adbf..ba7e53b83bd 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/AppearanceStyle.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/AppearanceStyle.java @@ -16,6 +16,7 @@ */ package org.apache.pdfbox.pdmodel.interactive; +import org.apache.pdfbox.pdmodel.GlyphLayoutProcessorInterface; import org.apache.pdfbox.pdmodel.font.PDFont; /** @@ -38,7 +39,12 @@ public class AppearanceStyle * Defaulting to 1.2*fontSize to match Acrobats default. */ private float leading = 14.4f; - + + /** + * Glyph layout processor + */ + private GlyphLayoutProcessorInterface glyphLayoutProcessor; + /** * Get the font used for text formatting. * @@ -99,4 +105,5 @@ public void setLeading(float leading) { this.leading = leading; } + } diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/PlainText.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/PlainText.java index eca1689bd4a..db1b1019fc1 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/PlainText.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/PlainText.java @@ -32,21 +32,21 @@ * A block of text can contain multiple paragraphs which will * be treated individually within the block placement. *

- * + * */ public class PlainText { private static final float FONTSCALE = 1000f; - + private final List paragraphs; - + /** * Construct the text block from a single value. - * + *

* Constructs the text block from a single value splitting - * into individual {@link Paragraph} when a new line character is + * into individual {@link Paragraph} when a new line character is * encountered. - * + * * @param textValue the text block string. */ public PlainText(String textValue) @@ -71,13 +71,13 @@ public PlainText(String textValue) } } } - + /** * Construct the text block from a list of values. - * + *

* Constructs the text block from a list of values treating each * entry as an individual {@link Paragraph}. - * + * * @param listValue the text block string. */ public PlainText(List listValue) @@ -85,20 +85,20 @@ public PlainText(List listValue) paragraphs = new ArrayList<>(listValue.size()); listValue.forEach(part -> paragraphs.add(new Paragraph(part))); } - + /** * Get the list of paragraphs. - * + * * @return the paragraphs. */ public List getParagraphs() { return paragraphs; } - + /** * Attribute keys and attribute values used for text handling. - * + *

* This is similar to {@link java.awt.font.TextAttribute} but * handled individually as to avoid a dependency on awt. */ @@ -113,12 +113,12 @@ static class TextAttribute extends Attribute * Attribute width of the text. */ public static final Attribute WIDTH = new TextAttribute("width"); - + protected TextAttribute(String name) { super(name); } - + } @@ -207,15 +207,15 @@ public List getLines(PDFont font, float fontSize, float width) throws IOEx wordNeedsSplit = true; // PDFBOX-5049: The original approach was to decrement splitOffset - // until the substring fits, but this can be very expensive for long words and + // until the substring fits, but this can be very expensive for long words and // narrow widths (e.g. a long URL in a narrow column). - // + // // Optimization: instead of decrementing splitOffset one step at a time and // calling getStringWidth on progressively shorter substrings: // - compute the scaled width of every individual character once // - build a prefix-sum array // - binary-search for the largest prefix that fits - // + // // TODO: The special case in PDFBOX-5049 should be handled by not generating an appearance // stream at all as the the height of the text box is only 1pt and the text is not visible. @@ -315,7 +315,7 @@ private static int findMaxFittingChars(float[] prefixWidth, float width) return lo; } } - + /** * An individual line of text. */ @@ -328,12 +328,12 @@ float getWidth() { return lineWidth; } - + void setWidth(float width) { lineWidth = width; } - + float calculateWidth(PDFont font, float fontSize) throws IOException { final float scale = fontSize/FONTSCALE; @@ -341,7 +341,7 @@ float calculateWidth(PDFont font, float fontSize) throws IOException int indexOfWord = 0; for (Word word : words) { - calculatedWidth = calculatedWidth + + calculatedWidth = calculatedWidth + (Float) word.getAttributes().getIterator().getAttribute(TextAttribute.WIDTH); String text = word.getText(); if (indexOfWord == words.size() -1 && Character.isWhitespace(text.charAt(text.length()-1))) @@ -358,7 +358,7 @@ List getWords() { return words; } - + float getInterWordSpacing(float width) { return (width - lineWidth)/(words.size()-1); @@ -369,10 +369,10 @@ void addWord(Word word) words.add(word); } } - + /** * An individual word. - * + *

* A word is defined as a string which must be kept * on the same line. */ @@ -380,22 +380,22 @@ static class Word { private AttributedString attributedString; private final String textContent; - + Word(String text) { textContent = text; } - + String getText() { return textContent; } - + AttributedString getAttributes() { return attributedString; } - + void setAttributes(AttributedString as) { this.attributedString = as; diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/PlainTextFormatter.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/PlainTextFormatter.java index 3e41fe2ec76..ab2791fe5c7 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/PlainTextFormatter.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/PlainTextFormatter.java @@ -27,7 +27,7 @@ /** * TextFormatter to handle plain text formatting for annotation rectangles. - * + *

* The text formatter will take a single value or an array of values which * are treated as paragraphs. */ @@ -39,18 +39,18 @@ public class PlainTextFormatter * The scaling factor for font units to PDF units */ private static final int FONTSCALE = 1000; - + private final AppearanceStyle appearanceStyle; private final boolean wrapLines; private final float width; - + private final PDAppearanceContentStream contents; private final PlainText textContent; private final TextAlign textAlignment; - + private float horizontalOffset; private float verticalOffset; - + public static class Builder { @@ -63,12 +63,12 @@ public static class Builder private float width = 0f; private PlainText textContent; private TextAlign textAlignment = TextAlign.LEFT; - - + + // initial offset from where to start the position of the first line private float horizontalOffset = 0f; private float verticalOffset = 0f; - + public Builder(PDAppearanceContentStream contents) { this.contents = contents; @@ -79,7 +79,7 @@ public Builder style(AppearanceStyle appearanceStyle) this.appearanceStyle = appearanceStyle; return this; } - + public Builder wrapLines(boolean wrapLines) { this.wrapLines = wrapLines; @@ -97,33 +97,33 @@ public Builder textAlign(int alignment) this.textAlignment = TextAlign.valueOf(alignment); return this; } - + public Builder textAlign(TextAlign alignment) { this.textAlignment = alignment; return this; } - - + + public Builder text(PlainText textContent) { this.textContent = textContent; return this; } - + public Builder initialOffset(float horizontalOffset, float verticalOffset) { this.horizontalOffset = horizontalOffset; this.verticalOffset = verticalOffset; return this; } - + public PlainTextFormatter build() { return new PlainTextFormatter(this); } } - + private PlainTextFormatter(Builder builder) { appearanceStyle = builder.appearanceStyle; @@ -135,10 +135,10 @@ private PlainTextFormatter(Builder builder) horizontalOffset = builder.horizontalOffset; verticalOffset = builder.verticalOffset; } - + /** * Format the text block. - * + * * @throws IOException if there is an error writing to the stream. */ public void format() throws IOException @@ -151,37 +151,37 @@ public void format() throws IOException if (wrapLines) { List lines = paragraph.getLines( - appearanceStyle.getFont(), - appearanceStyle.getFontSize(), - width - ); + appearanceStyle.getFont(), + appearanceStyle.getFontSize(), + width + ); processLines(lines, isFirstParagraph); isFirstParagraph = false; } else { float startOffset = 0f; - - + + float lineWidth = appearanceStyle.getFont().getStringWidth(paragraph.getText()) * appearanceStyle.getFontSize() / FONTSCALE; - - if (lineWidth < width) + + if (lineWidth < width) { switch (textAlignment) { - case CENTER: - startOffset = (width - lineWidth)/2; - break; - case RIGHT: - startOffset = width - lineWidth; - break; - case JUSTIFY: - default: - startOffset = 0f; + case CENTER: + startOffset = (width - lineWidth)/2; + break; + case RIGHT: + startOffset = width - lineWidth; + break; + case JUSTIFY: + default: + startOffset = 0f; } } - + contents.newLineAtOffset(horizontalOffset + startOffset, verticalOffset); contents.showText(paragraph.getText()); } @@ -191,10 +191,10 @@ public void format() throws IOException /** * Process lines for output. - * + *

* Process lines for an individual paragraph and generate the * commands for the content stream to show the text. - * + * * @param lines the lines to process. * @throws IOException if there is an error writing to the stream. */ @@ -205,29 +205,29 @@ private void processLines(List lines, boolean isFirstParagraph) throws IOE float lastPos = 0f; float startOffset = 0f; float interWordSpacing = 0f; - + for (Line line : lines) { switch (textAlignment) { - case CENTER: - startOffset = (width - line.getWidth())/2; - break; - case RIGHT: - startOffset = width - line.getWidth(); - break; - case JUSTIFY: - if (lines.indexOf(line) != lines.size() -1) - { - interWordSpacing = line.getInterWordSpacing(width); - } - break; - default: - startOffset = 0f; + case CENTER: + startOffset = (width - line.getWidth())/2; + break; + case RIGHT: + startOffset = width - line.getWidth(); + break; + case JUSTIFY: + if (lines.indexOf(line) != lines.size() -1) + { + interWordSpacing = line.getInterWordSpacing(width); + } + break; + default: + startOffset = 0f; } - + float offset = -lastPos + startOffset + horizontalOffset; - + if (lines.indexOf(line) == 0 && isFirstParagraph) { contents.newLineAtOffset(offset, verticalOffset); @@ -239,7 +239,7 @@ private void processLines(List lines, boolean isFirstParagraph) throws IOE contents.newLineAtOffset(offset, - appearanceStyle.getLeading()); } - lastPos += offset; + lastPos += offset; List words = line.getWords(); int wordIndex = 0; diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java index 29d21c86a3d..e4717ca9431 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java @@ -39,6 +39,7 @@ import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDSimpleFont; +import org.apache.pdfbox.pdmodel.font.PDType0Font; import org.apache.pdfbox.pdmodel.font.PDType3CharProc; import org.apache.pdfbox.pdmodel.font.PDType3Font; import org.apache.pdfbox.pdmodel.font.PDVectorFont; @@ -531,6 +532,9 @@ private void insertGeneratedAppearance(PDAnnotationWidget widget, // get the font PDFont font = defaultAppearance.getFont(); + if (glyphLayoutProcessor != null && glyphLayoutProcessor.supportsFont(font)) { + ((PDType0Font)font).setGlyphLayoutProcessor(glyphLayoutProcessor); + } if (font == null) { throw new IllegalArgumentException("font is null, check whether /DA entry is incomplete or incorrect"); @@ -637,7 +641,7 @@ else if (field instanceof PDListBox) AppearanceStyle appearanceStyle = new AppearanceStyle(); appearanceStyle.setFont(font); appearanceStyle.setFontSize(fontSize); - + // Adobe Acrobat uses the font's bounding box for the leading between the lines appearanceStyle.setLeading(font.getBoundingBox().getHeight() * fontScaleY); @@ -938,7 +942,7 @@ private float calculateFontSize(PDFont font, PDRectangle contentRect) throws IOE { return Math.max(fs - 1, MINIMUM_FONT_SIZE); } - fs += 1.0; + fs += 1.0f; } return Math.min(fs, DEFAULT_FONT_SIZE); }