You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ja...@apache.org on 2018/05/10 06:50:05 UTC
svn commit: r1831310 - in
/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel:
PDAbstractContentStream.java PDAppearanceContentStream.java
PDFormContentStream.java PDPageContentStream.java PDPatternContentStream.java
Author: jahewson
Date: Thu May 10 06:50:05 2018
New Revision: 1831310
URL: http://svn.apache.org/viewvc?rev=1831310&view=rev
Log:
PDFBOX-4068: don't override "common" functionality - abstract content stream must track subsetting
Modified:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAbstractContentStream.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAppearanceContentStream.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDFormContentStream.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageContentStream.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPatternContentStream.java
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAbstractContentStream.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAbstractContentStream.java?rev=1831310&r1=1831309&r2=1831310&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAbstractContentStream.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAbstractContentStream.java Thu May 10 06:50:05 2018
@@ -18,18 +18,35 @@ package org.apache.pdfbox.pdmodel;
import java.awt.Color;
import java.awt.geom.AffineTransform;
+import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
import java.util.Stack;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fontbox.ttf.CmapLookup;
+import org.apache.fontbox.ttf.gsub.CompoundCharacterTokenizer;
+import org.apache.fontbox.ttf.gsub.GsubWorker;
+import org.apache.fontbox.ttf.gsub.GsubWorkerFactory;
+import org.apache.fontbox.ttf.model.GsubData;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSNumber;
import org.apache.pdfbox.pdfwriter.COSWriter;
import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDPropertyList;
import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceCMYK;
@@ -44,6 +61,7 @@ import org.apache.pdfbox.pdmodel.graphic
import org.apache.pdfbox.pdmodel.graphics.image.PDInlineImage;
import org.apache.pdfbox.pdmodel.graphics.shading.PDShading;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
+import org.apache.pdfbox.pdmodel.graphics.state.RenderingMode;
import org.apache.pdfbox.util.Charsets;
import org.apache.pdfbox.util.Matrix;
import org.apache.pdfbox.util.NumberFormatUtil;
@@ -55,6 +73,10 @@ import org.apache.pdfbox.util.NumberForm
*/
abstract class PDAbstractContentStream implements Closeable
{
+ private static final Log LOG = LogFactory.getLog(PDAbstractContentStream.class);
+
+ protected final PDDocument document; // may be null
+
protected final OutputStream outputStream;
protected final PDResources resources;
@@ -68,13 +90,19 @@ abstract class PDAbstractContentStream i
private final NumberFormat formatDecimal = NumberFormat.getNumberInstance(Locale.US);
private final byte[] formatBuffer = new byte[32];
+ private final Map<PDType0Font, GsubWorker> gsubWorkers = new HashMap<>();
+ private final GsubWorkerFactory gsubWorkerFactory = new GsubWorkerFactory();
+
/**
* Create a new appearance stream.
- *
+ *
+ * @param document may be null
* @param outputStream The appearances output stream to write to.
+ * @param resources The resources to use
*/
- PDAbstractContentStream(OutputStream outputStream, PDResources resources)
+ PDAbstractContentStream(PDDocument document, OutputStream outputStream, PDResources resources)
{
+ this.document = document;
this.outputStream = outputStream;
this.resources = resources;
@@ -145,6 +173,32 @@ abstract class PDAbstractContentStream i
fontStack.setElementAt(font, fontStack.size() - 1);
}
+ // keep track of fonts which are configured for subsetting
+ if (font.willBeSubset())
+ {
+ if (document != null)
+ {
+ document.getFontsToSubset().add(font);
+ }
+ else
+ {
+ LOG.warn("attempting to use subset font " + font.getName() + " without proper context");
+ }
+ }
+
+ // complex text layout
+ if (font instanceof PDType0Font)
+ {
+ PDType0Font pdType0Font = (PDType0Font) font;
+ GsubData gsubData = pdType0Font.getGsubData();
+ if (gsubData != GsubData.NO_DATA_FOUND)
+ {
+ GsubWorker gsubWorker = gsubWorkerFactory.getGsubWorker(pdType0Font.getCmapLookup(),
+ gsubData);
+ gsubWorkers.put((PDType0Font) font, gsubWorker);
+ }
+ }
+
writeOperand(resources.add(font));
writeOperand(fontSize);
writeOperator("Tf");
@@ -219,6 +273,29 @@ abstract class PDAbstractContentStream i
PDFont font = fontStack.peek();
+ // complex text layout
+ byte[] encodedText = null;
+ if (font instanceof PDType0Font)
+ {
+
+ GsubWorker gsubWorker = gsubWorkers.get(font);
+ if (gsubWorker != null)
+ {
+ PDType0Font pdType0Font = (PDType0Font) font;
+ Set<Integer> glyphIds = new HashSet<>();
+ encodedText = encodeForGsub(gsubWorker, glyphIds, pdType0Font, text);
+ if (pdType0Font.willBeSubset())
+ {
+ pdType0Font.addGlyphsToSubset(glyphIds);
+ }
+ }
+ }
+
+ if (encodedText == null)
+ {
+ encodedText = font.encode(text);
+ }
+
// Unicode code points to keep when subsetting
if (font.willBeSubset())
{
@@ -230,7 +307,7 @@ abstract class PDAbstractContentStream i
}
}
- COSWriter.writeString(font.encode(text), outputStream);
+ COSWriter.writeString(encodedText, outputStream);
}
/**
@@ -1534,4 +1611,86 @@ abstract class PDAbstractContentStream i
writeOperand(scale);
writeOperator("Tz");
}
+
+ /**
+ * Set the text rendering mode. This determines whether showing text shall cause glyph outlines
+ * to be stroked, filled, used as a clipping boundary, or some combination of the three.
+ *
+ * @param rm The text rendering mode.
+ * @throws IOException If the content stream could not be written.
+ */
+ public void setRenderingMode(RenderingMode rm) throws IOException
+ {
+ writeOperand(rm.intValue());
+ writeOperator("Tr");
+ }
+
+ /**
+ * Set the text rise value, i.e. move the baseline up or down. This is useful for drawing
+ * superscripts or subscripts.
+ *
+ * @param rise Specifies the distance, in unscaled text space units, to move the baseline up or
+ * down from its default location. 0 restores the default location.
+ * @throws IOException
+ */
+ public void setTextRise(float rise) throws IOException
+ {
+ writeOperand(rise);
+ writeOperator("Ts");
+ }
+
+ private byte[] encodeForGsub(GsubWorker gsubWorker,
+ Set<Integer> glyphIds, PDType0Font font, String text) throws IOException
+ {
+
+ String spaceRegexPattern = "\\s";
+ Pattern spaceRegex = Pattern.compile(spaceRegexPattern);
+
+ // break the entire chunk of text into words by splitting it with space
+ List<String> words = new CompoundCharacterTokenizer("\\s").tokenize(text);
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ for (String word : words)
+ {
+ if (spaceRegex.matcher(word).matches())
+ {
+ out.write(font.encode(word));
+ }
+ else
+ {
+ glyphIds.addAll(applyGSUBRules(gsubWorker, out, font, word));
+ }
+ }
+
+ return out.toByteArray();
+ }
+
+ private List<Integer> applyGSUBRules(GsubWorker gsubWorker, ByteArrayOutputStream out, PDType0Font font, String word) throws IOException
+ {
+ List<Integer> originalGlyphIds = new ArrayList<>();
+ CmapLookup cmapLookup = font.getCmapLookup();
+
+ // convert characters into glyphIds
+ for (char unicodeChar : word.toCharArray())
+ {
+ int glyphId = cmapLookup.getGlyphId(unicodeChar);
+ if (glyphId <= 0)
+ {
+ throw new IllegalStateException(
+ "could not find the glyphId for the character: " + unicodeChar);
+ }
+ originalGlyphIds.add(glyphId);
+ }
+
+ List<Integer> glyphIdsAfterGsub = gsubWorker.applyTransforms(originalGlyphIds);
+
+ for (Integer glyphId : glyphIdsAfterGsub)
+ {
+ out.write(font.encodeGlyphId(glyphId));
+ }
+
+ return glyphIdsAfterGsub;
+
+ }
}
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAppearanceContentStream.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAppearanceContentStream.java?rev=1831310&r1=1831309&r2=1831310&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAppearanceContentStream.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAppearanceContentStream.java Thu May 10 06:50:05 2018
@@ -56,7 +56,7 @@ public final class PDAppearanceContentSt
*/
public PDAppearanceContentStream(PDAppearanceStream appearance, OutputStream outputStream)
{
- super(outputStream, appearance.getResources());
+ super(null, outputStream, appearance.getResources());
}
/**
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDFormContentStream.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDFormContentStream.java?rev=1831310&r1=1831309&r2=1831310&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDFormContentStream.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDFormContentStream.java Thu May 10 06:50:05 2018
@@ -34,6 +34,6 @@ public final class PDFormContentStream e
*/
public PDFormContentStream(PDFormXObject form) throws IOException
{
- super(form.getContentStream().createOutputStream(), form.getResources());
+ super(null, form.getContentStream().createOutputStream(), form.getResources());
}
}
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageContentStream.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageContentStream.java?rev=1831310&r1=1831309&r2=1831310&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageContentStream.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageContentStream.java Thu May 10 06:50:05 2018
@@ -18,33 +18,17 @@ package org.apache.pdfbox.pdmodel;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
-import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.fontbox.ttf.CmapLookup;
-import org.apache.fontbox.ttf.gsub.CompoundCharacterTokenizer;
-import org.apache.fontbox.ttf.gsub.GsubWorker;
-import org.apache.fontbox.ttf.gsub.GsubWorkerFactory;
-import org.apache.fontbox.ttf.model.GsubData;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSName;
-import org.apache.pdfbox.pdfwriter.COSWriter;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDPropertyList;
-import org.apache.pdfbox.pdmodel.font.PDFont;
-import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
@@ -54,8 +38,6 @@ import org.apache.pdfbox.pdmodel.graphic
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.graphics.image.PDInlineImage;
-import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
-import org.apache.pdfbox.pdmodel.graphics.state.RenderingMode;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.util.Charsets;
import org.apache.pdfbox.util.Matrix;
@@ -98,11 +80,6 @@ public final class PDPageContentStream e
private static final Log LOG = LogFactory.getLog(PDPageContentStream.class);
- private final PDDocument document;
-
- private final Map<PDType0Font, GsubWorker> gsubWorkers = new HashMap<>();
- private final GsubWorkerFactory gsubWorkerFactory = new GsubWorkerFactory();
-
/**
* Create a new PDPage content stream. This constructor overwrites all existing content streams
* of this page.
@@ -158,7 +135,7 @@ public final class PDPageContentStream e
boolean compress, boolean resetContext,PDStream stream,
PDResources resources) throws IOException
{
- super(stream.createOutputStream(compress ? COSName.FLATE_DECODE : null), resources);
+ super(document, stream.createOutputStream(compress ? COSName.FLATE_DECODE : null), resources);
// propagate resources to the page
if (sourcePage.getResources() == null)
@@ -166,8 +143,6 @@ public final class PDPageContentStream e
sourcePage.setResources(resources);
}
- this.document = document;
-
// If request specifies the need to append/prepend to the document
if (!appendContent.isOverwrite() && sourcePage.hasContents())
{
@@ -254,51 +229,7 @@ public final class PDPageContentStream e
*/
public PDPageContentStream(PDDocument doc, PDAppearanceStream appearance, OutputStream outputStream)
{
- super(outputStream, appearance.getResources());
- this.document = doc;
-
- //setResources(appearance.getResources());
- }
-
- /**
- * Set the font and font size to draw text with.
- *
- * @param font The font to use.
- * @param fontSize The font size to draw the text.
- * @throws IOException If there is an error writing the font information.
- */
- @Override
- public void setFont(PDFont font, float fontSize) throws IOException
- {
- if (fontStack.isEmpty())
- {
- fontStack.add(font);
- }
- else
- {
- fontStack.setElementAt(font, fontStack.size() - 1);
- }
-
- if (font.willBeSubset())
- {
- document.getFontsToSubset().add(font);
- }
-
- if (font instanceof PDType0Font)
- {
- PDType0Font pdType0Font = (PDType0Font) font;
- GsubData gsubData = pdType0Font.getGsubData();
- if (gsubData != GsubData.NO_DATA_FOUND)
- {
- GsubWorker gsubWorker = gsubWorkerFactory.getGsubWorker(pdType0Font.getCmapLookup(),
- gsubData);
- gsubWorkers.put((PDType0Font) font, gsubWorker);
- }
- }
-
- writeOperand(resources.add(font));
- writeOperand(fontSize);
- writeOperator("Tf");
+ super(doc, outputStream, appearance.getResources());
}
/**
@@ -315,65 +246,6 @@ public final class PDPageContentStream e
}
/**
- * Outputs a string using the correct encoding and subsetting as required.
- *
- * @param text The Unicode text to show.
- *
- * @throws IOException If an io exception occurs.
- */
- @Override
- protected void showTextInternal(String text) throws IOException
- {
- if (!inTextMode)
- {
- throw new IllegalStateException("Must call beginText() before showText()");
- }
-
- if (fontStack.isEmpty())
- {
- throw new IllegalStateException("Must call setFont() before showText()");
- }
-
- PDFont font = fontStack.peek();
-
- byte[] encodedText = null;
-
- if (font instanceof PDType0Font)
- {
-
- GsubWorker gsubWorker = gsubWorkers.get(font);
- if (gsubWorker != null)
- {
- PDType0Font pdType0Font = (PDType0Font) font;
- Set<Integer> glyphIds = new HashSet<>();
- encodedText = encodeForGsub(gsubWorker, glyphIds, pdType0Font, text);
- if (pdType0Font.willBeSubset())
- {
- pdType0Font.addGlyphsToSubset(glyphIds);
- }
- }
- }
-
- if (encodedText == null)
- {
- encodedText = font.encode(text);
- }
-
- // Unicode code points to keep when subsetting
- if (font.willBeSubset())
- {
- for (int offset = 0; offset < text.length(); )
- {
- int codePoint = text.codePointAt(offset);
- font.addToSubset(codePoint);
- offset += Character.charCount(codePoint);
- }
- }
-
- COSWriter.writeString(encodedText, outputStream);
- }
-
- /**
* The Td operator.
* A current text matrix will be replaced with a new one (1 0 0 1 x y).
* @param tx The x translation.
@@ -587,50 +459,6 @@ public final class PDPageContentStream e
}
/**
- * q operator. Saves the current graphics state.
- * @throws IOException If an error occurs while writing to the stream.
- */
- @Override
- public void saveGraphicsState() throws IOException
- {
- if (!fontStack.isEmpty())
- {
- fontStack.push(fontStack.peek());
- }
- if (!strokingColorSpaceStack.isEmpty())
- {
- strokingColorSpaceStack.push(strokingColorSpaceStack.peek());
- }
- if (!nonStrokingColorSpaceStack.isEmpty())
- {
- nonStrokingColorSpaceStack.push(nonStrokingColorSpaceStack.peek());
- }
- writeOperator("q");
- }
-
- /**
- * Q operator. Restores the current graphics state.
- * @throws IOException If an error occurs while writing to the stream.
- */
- @Override
- public void restoreGraphicsState() throws IOException
- {
- if (!fontStack.isEmpty())
- {
- fontStack.pop();
- }
- if (!strokingColorSpaceStack.isEmpty())
- {
- strokingColorSpaceStack.pop();
- }
- if (!nonStrokingColorSpaceStack.isEmpty())
- {
- nonStrokingColorSpaceStack.pop();
- }
- writeOperator("Q");
- }
-
- /**
* Set the stroking color space. This will add the colorspace to the PDResources
* if necessary.
*
@@ -1147,100 +975,4 @@ public final class PDPageContentStream e
{
writeOperand(name);
}
-
- /**
- * Set an extended graphics state.
- *
- * @param state The extended graphics state.
- * @throws IOException If the content stream could not be written.
- */
- @Override
- public void setGraphicsStateParameters(PDExtendedGraphicsState state) throws IOException
- {
- writeOperand(resources.add(state));
- writeOperator("gs");
- }
-
- /**
- * Set the text rendering mode. This determines whether showing text shall cause glyph outlines
- * to be stroked, filled, used as a clipping boundary, or some combination of the three.
- *
- * @param rm The text rendering mode.
- * @throws IOException If the content stream could not be written.
- */
- public void setRenderingMode(RenderingMode rm) throws IOException
- {
- writeOperand(rm.intValue());
- writeOperator("Tr");
- }
-
- /**
- * Set the text rise value, i.e. move the baseline up or down. This is useful for drawing
- * superscripts or subscripts.
- *
- * @param rise Specifies the distance, in unscaled text space units, to move the baseline up or
- * down from its default location. 0 restores the default location.
- * @throws IOException
- */
- public void setTextRise(float rise) throws IOException
- {
- writeOperand(rise);
- writeOperator("Ts");
- }
-
- private byte[] encodeForGsub(GsubWorker gsubWorker,
- Set<Integer> glyphIds, PDType0Font font, String text) throws IOException
- {
-
- String spaceRegexPattern = "\\s";
- Pattern spaceRegex = Pattern.compile(spaceRegexPattern);
-
- // break the entire chunk of text into words by splitting it with space
- List<String> words = new CompoundCharacterTokenizer("\\s").tokenize(text);
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
-
- for (String word : words)
- {
- if (spaceRegex.matcher(word).matches())
- {
- out.write(font.encode(word));
- }
- else
- {
- glyphIds.addAll(applyGSUBRules(gsubWorker, out, font, word));
- }
- }
-
- return out.toByteArray();
- }
-
- private List<Integer> applyGSUBRules(GsubWorker gsubWorker, ByteArrayOutputStream out, PDType0Font font, String word) throws IOException
- {
- List<Integer> originalGlyphIds = new ArrayList<>();
- CmapLookup cmapLookup = font.getCmapLookup();
-
- // convert characters into glyphIds
- for (char unicodeChar : word.toCharArray())
- {
- int glyphId = cmapLookup.getGlyphId(unicodeChar);
- if (glyphId <= 0)
- {
- throw new IllegalStateException(
- "could not find the glyphId for the character: " + unicodeChar);
- }
- originalGlyphIds.add(glyphId);
- }
-
- List<Integer> glyphIdsAfterGsub = gsubWorker.applyTransforms(originalGlyphIds);
-
- for (Integer glyphId : glyphIdsAfterGsub)
- {
- out.write(font.encodeGlyphId(glyphId));
- }
-
- return glyphIdsAfterGsub;
-
- }
-
}
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPatternContentStream.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPatternContentStream.java?rev=1831310&r1=1831309&r2=1831310&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPatternContentStream.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPatternContentStream.java Thu May 10 06:50:05 2018
@@ -34,6 +34,6 @@ public final class PDPatternContentStrea
*/
public PDPatternContentStream(PDTilingPattern pattern) throws IOException
{
- super(pattern.getContentStream().createOutputStream(), pattern.getResources());
+ super(null, pattern.getContentStream().createOutputStream(), pattern.getResources());
}
}