From ccd2678cd0e9c0d3b85117725bb7fb59d8c7338d Mon Sep 17 00:00:00 2001 From: Mehdi Houshmand Date: Fri, 14 Sep 2012 09:13:40 +0000 Subject: [PATCH] Bugzilla#53868: Full font embedding in PDF git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1384690 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fonts/CIDFont.java | 4 +- src/java/org/apache/fop/fonts/CIDFull.java | 113 +++++++++++++++ src/java/org/apache/fop/fonts/CIDSet.java | 90 ++++++++++++ src/java/org/apache/fop/fonts/CIDSubset.java | 129 +++++++----------- src/java/org/apache/fop/fonts/FontReader.java | 2 +- src/java/org/apache/fop/fonts/LazyFont.java | 3 + .../org/apache/fop/fonts/MultiByteFont.java | 69 +++++++--- .../fop/fonts/truetype/TTFFontLoader.java | 2 +- src/java/org/apache/fop/pdf/PDFFactory.java | 75 ++++------ .../render/bitmap/TIFFDocumentHandler.java | 1 - .../org/apache/fop/render/ps/PSFontUtils.java | 30 +++- status.xml | 3 + .../org/apache/fop/fonts/CIDFullTestCase.java | 120 ++++++++++++++++ .../apache/fop/pdf/PDFFactoryTestCase.java | 12 +- 14 files changed, 492 insertions(+), 161 deletions(-) create mode 100644 src/java/org/apache/fop/fonts/CIDFull.java create mode 100644 src/java/org/apache/fop/fonts/CIDSet.java create mode 100644 test/java/org/apache/fop/fonts/CIDFullTestCase.java diff --git a/src/java/org/apache/fop/fonts/CIDFont.java b/src/java/org/apache/fop/fonts/CIDFont.java index 82213dd65..dc398263e 100644 --- a/src/java/org/apache/fop/fonts/CIDFont.java +++ b/src/java/org/apache/fop/fonts/CIDFont.java @@ -29,7 +29,7 @@ import org.apache.fop.apps.io.InternalResourceResolver; public abstract class CIDFont extends CustomFont { /** Contains the character widths for all characters in the font */ - protected int[] width = null; + protected int[] width; /** * @param resourceResolver the URI resolver for controlling file access @@ -69,7 +69,7 @@ public abstract class CIDFont extends CustomFont { * Returns the subset information for this font. * @return the subset information */ - public abstract CIDSubset getCIDSubset(); + public abstract CIDSet getCIDSet(); // ---- Optional ---- /** diff --git a/src/java/org/apache/fop/fonts/CIDFull.java b/src/java/org/apache/fop/fonts/CIDFull.java new file mode 100644 index 000000000..ee062a2bb --- /dev/null +++ b/src/java/org/apache/fop/fonts/CIDFull.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fonts; + +import java.util.BitSet; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.fop.util.CharUtilities; + +/** + * Provides methods to get font information. + * Naming: + * glyph index: original index of the glyph in the non-subset font (!= unicode index) + * character selector: index into a set of glyphs. For subset CID fonts, this starts at 0. For non-subset + * fonts, this is the same as the glyph index. + * Unicode index: The Unicode codepoint of a character. + * Glyph name: the Adobe glyph name (as found in Glyphs.java) + */ +public class CIDFull implements CIDSet { + + private BitSet glyphIndices; + private final MultiByteFont font; + + public CIDFull(MultiByteFont mbf) { + font = mbf; + } + + private void initGlyphIndices() { + // this cannot be called in the constructor since the font is not ready... + if (glyphIndices == null) { + glyphIndices = font.getGlyphIndices(); + } + } + + /** {@inheritDoc} */ + public int getOriginalGlyphIndex(int index) { + return index; + } + + /** {@inheritDoc} */ + public char getUnicode(int index) { + initGlyphIndices(); + if (glyphIndices.get(index)) { + return (char) index; + } else { + return CharUtilities.NOT_A_CHARACTER; + } + } + + /** {@inheritDoc} */ + public int mapChar(int glyphIndex, char unicode) { + return (char) glyphIndex; + } + + /** {@inheritDoc} */ + public Map getGlyphs() { + // this is never really called for full embedded fonts but the equivalent map would be the identity + initGlyphIndices(); + Map glyphs = new HashMap(); + int nextBitSet = 0; + for (int j = 0; j < glyphIndices.cardinality(); j++) { + nextBitSet = glyphIndices.nextSetBit(nextBitSet); + glyphs.put(Integer.valueOf(nextBitSet), Integer.valueOf(nextBitSet)); + nextBitSet++; + } + return Collections.unmodifiableMap(glyphs); + } + + /** {@inheritDoc} */ + public char[] getChars() { + return font.getChars(); + } + + /** {@inheritDoc} */ + public int getNumberOfGlyphs() { + initGlyphIndices(); + // note: the real number of glyphs is given by the cardinality() method (not the length()) but since + // we will pad gaps in the indices with zeros we really want the length() here. this method is only + // called when embedding a font in PostScript and this will be the value of the CIDCount entry + return glyphIndices.length(); + } + + /** {@inheritDoc} */ + public BitSet getGlyphIndices() { + initGlyphIndices(); + return glyphIndices; + } + + /** {@inheritDoc} */ + public int[] getWidths() { + return font.getWidths(); + } + +} diff --git a/src/java/org/apache/fop/fonts/CIDSet.java b/src/java/org/apache/fop/fonts/CIDSet.java new file mode 100644 index 000000000..30d1a5ec5 --- /dev/null +++ b/src/java/org/apache/fop/fonts/CIDSet.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fonts; + +import java.util.BitSet; +import java.util.Map; + +/** + * Declares methods to retrieve font information (glyph indices, widths, unicode values) from a CID font. + */ +public interface CIDSet { + + /** + * Returns the original index of the glyph inside the (non-subset) font's glyph list. This + * index can be used to access the character width information, for example. + * @param index the subset index (character selector) to access the glyph + * @return the original index (or -1 if no glyph index is available for the subset index) + */ + int getOriginalGlyphIndex(int index); + + /** + * Returns the Unicode value for a subset index (character selector). If there's no such + * Unicode value, the "NOT A CHARACTER" (0xFFFF) is returned. + * @param subsetIndex the subset index (character selector) + * @return the Unicode value or "NOT A CHARACTER" (0xFFFF) + */ + char getUnicode(int index); + + /** + * Maps a character to a character selector for a font subset. If the character isn't in the + * subset, yet, it is added and a new character selector returned. Otherwise, the already + * allocated character selector is returned from the existing map/subset. + * @param glyphIndex the glyph index of the character + * @param unicode the Unicode index of the character + * @return the subset index + */ + int mapChar(int glyphIndex, char unicode); + + /** + * Returns an unmodifiable Map of the font subset. It maps from glyph index to + * character selector (i.e. the subset index in this case). + * @return Map Map<Integer, Integer> of the font subset + */ + Map getGlyphs(); + + /** + * Returns a char array containing all Unicode characters that are in the subset. + * @return a char array with all used Unicode characters + */ + char[] getChars(); + + /** + * Returns the number of glyphs in the subset. + * @return the number of glyphs in the subset + */ + int getNumberOfGlyphs(); + + /** + * Returns a BitSet with bits set for each available glyph index in the subset. + * @return a BitSet indicating available glyph indices + */ + BitSet getGlyphIndices(); + + /** + * Return the array of widths. + *

+ * This is used to get an array for inserting in an output format. + * It should not be used for lookup. + * @return an array of widths + */ + int[] getWidths(); + +} diff --git a/src/java/org/apache/fop/fonts/CIDSubset.java b/src/java/org/apache/fop/fonts/CIDSubset.java index 372a638d1..f442c13ed 100644 --- a/src/java/org/apache/fop/fonts/CIDSubset.java +++ b/src/java/org/apache/fop/fonts/CIDSubset.java @@ -26,18 +26,16 @@ import java.util.Map; import org.apache.fop.util.CharUtilities; -//Naming: -//glyph index: original index of the glyph in the non-subset font (!= unicode index) -//character selector: index into a set of glyphs. For subset CID fonts, this starts at 0. For -// non-subset fonts, this is the same as the glyph index. -//Unicode index: The Unicode codepoint of a character. -//Glyph name: the Adobe glyph name (as found in Glyphs.java) - /** - * Keeps track of the glyphs used in a document. This information is later used to build - * a subset of a font. + * Provides methods to get font information. + * Naming: + * glyph index: original index of the glyph in the non-subset font (!= unicode index) + * character selector: index into a set of glyphs. For subset CID fonts, this starts at 0. For non-subset + * fonts, this is the same as the glyph index. + * Unicode index: The Unicode codepoint of a character. + * Glyph name: the Adobe glyph name (as found in Glyphs.java) */ -public class CIDSubset { +public class CIDSubset implements CIDSet { /** * usedGlyphs contains orginal, new glyph index (glyph index -> char selector) @@ -48,51 +46,36 @@ public class CIDSubset { * usedGlyphsIndex contains new glyph, original index (char selector -> glyph index) */ private Map usedGlyphsIndex = new HashMap(); - private int usedGlyphsCount = 0; + private int usedGlyphsCount; /** * usedCharsIndex contains new glyph, original char (char selector -> Unicode) */ private Map usedCharsIndex = new HashMap(); - /** - * Default constructor. - */ - public CIDSubset() { - } + private final MultiByteFont font; - /** - * Adds the first glyph which is reserved for .notdef for all CID subsets. - */ - public void setupFirstGlyph() { - usedGlyphs.put(Integer.valueOf(0), Integer.valueOf(0)); - usedGlyphsIndex.put(Integer.valueOf(0), Integer.valueOf(0)); + public CIDSubset(MultiByteFont mbf) { + font = mbf; + // The zeroth value is reserved for .notdef + usedGlyphs.put(0, 0); + usedGlyphsIndex.put(0, 0); usedGlyphsCount++; } - /** - * Returns the original index of the glyph inside the (non-subset) font's glyph list. This - * index can be used to access the character width information, for example. - * @param subsetIndex the subset index (character selector) to access the glyph - * @return the original index (or -1 if no glyph index is available for the subset index) - */ - public int getGlyphIndexForSubsetIndex(int subsetIndex) { - Integer glyphIndex = usedGlyphsIndex.get(Integer.valueOf(subsetIndex)); + /** {@inheritDoc} */ + public int getOriginalGlyphIndex(int index) { + Integer glyphIndex = usedGlyphsIndex.get(index); if (glyphIndex != null) { - return glyphIndex.intValue(); + return glyphIndex; } else { return -1; } } - /** - * Returns the Unicode value for a subset index (character selector). If there's no such - * Unicode value, the "NOT A CHARACTER" (0xFFFF) is returned. - * @param subsetIndex the subset index (character selector) - * @return the Unicode value or "NOT A CHARACTER" (0xFFFF) - */ - public char getUnicodeForSubsetIndex(int subsetIndex) { - Character mapValue = usedCharsIndex.get(Integer.valueOf(subsetIndex)); + /** {@inheritDoc} */ + public char getUnicode(int index) { + Character mapValue = usedCharsIndex.get(index); if (mapValue != null) { return mapValue.charValue(); } else { @@ -100,72 +83,60 @@ public class CIDSubset { } } - /** - * Maps a character to a character selector for a font subset. If the character isn't in the - * subset, yet, it is added and a new character selector returned. Otherwise, the already - * allocated character selector is returned from the existing map/subset. - * @param glyphIndex the glyph index of the character - * @param unicode the Unicode index of the character - * @return the subset index - */ - public int mapSubsetChar(int glyphIndex, char unicode) { + /** {@inheritDoc} */ + public int mapChar(int glyphIndex, char unicode) { // Reencode to a new subset font or get the reencoded value // IOW, accumulate the accessed characters and build a character map for them - Integer subsetCharSelector = usedGlyphs.get(Integer.valueOf(glyphIndex)); + Integer subsetCharSelector = usedGlyphs.get(glyphIndex); if (subsetCharSelector == null) { int selector = usedGlyphsCount; - usedGlyphs.put(Integer.valueOf(glyphIndex), - Integer.valueOf(selector)); - usedGlyphsIndex.put(Integer.valueOf(selector), - Integer.valueOf(glyphIndex)); - usedCharsIndex.put(Integer.valueOf(selector), - Character.valueOf(unicode)); + usedGlyphs.put(glyphIndex, selector); + usedGlyphsIndex.put(selector, glyphIndex); + usedCharsIndex.put(selector, unicode); usedGlyphsCount++; return selector; } else { - return subsetCharSelector.intValue(); + return subsetCharSelector; } } - /** - * Returns an unmodifiable Map of the font subset. It maps from glyph index to - * character selector (i.e. the subset index in this case). - * @return Map Map<Integer, Integer> of the font subset - */ - public Map getSubsetGlyphs() { + /** {@inheritDoc} */ + public Map getGlyphs() { return Collections.unmodifiableMap(this.usedGlyphs); } - /** - * Returns a char array containing all Unicode characters that are in the subset. - * @return a char array with all used Unicode characters - */ - public char[] getSubsetChars() { + /** {@inheritDoc} */ + public char[] getChars() { char[] charArray = new char[usedGlyphsCount]; for (int i = 0; i < usedGlyphsCount; i++) { - charArray[i] = getUnicodeForSubsetIndex(i); + charArray[i] = getUnicode(i); } return charArray; } - /** - * Returns the number of glyphs in the subset. - * @return the number of glyphs in the subset - */ - public int getSubsetSize() { + /** {@inheritDoc} */ + public int getNumberOfGlyphs() { return this.usedGlyphsCount; } - /** - * Returns a BitSet with bits set for each available glyph index in the subset. - * @return a BitSet indicating available glyph indices - */ - public BitSet getGlyphIndexBitSet() { + /** {@inheritDoc} */ + public BitSet getGlyphIndices() { BitSet bitset = new BitSet(); for (Integer cid : usedGlyphs.keySet()) { - bitset.set(cid.intValue()); + bitset.set(cid); } return bitset; } + /** {@inheritDoc} */ + public int[] getWidths() { + int[] widths = font.getWidths(); + int[] tmpWidth = new int[getNumberOfGlyphs()]; + for (int i = 0, c = getNumberOfGlyphs(); i < c; i++) { + int nwx = Math.max(0, getOriginalGlyphIndex(i)); + tmpWidth[i] = widths[nwx]; + } + return tmpWidth; + } + } diff --git a/src/java/org/apache/fop/fonts/FontReader.java b/src/java/org/apache/fop/fonts/FontReader.java index 68c5c7177..0448c317e 100644 --- a/src/java/org/apache/fop/fonts/FontReader.java +++ b/src/java/org/apache/fop/fonts/FontReader.java @@ -157,7 +157,7 @@ public class FontReader extends DefaultHandler { throws SAXException { if (localName.equals("font-metrics")) { if ("TYPE0".equals(attributes.getValue("type"))) { - multiFont = new MultiByteFont(resourceResolver); + multiFont = new MultiByteFont(resourceResolver, EmbeddingMode.AUTO); returnFont = multiFont; isCID = true; TTFReader.checkMetricsVersion(attributes); diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java index 4c42387f2..fef42f74c 100644 --- a/src/java/org/apache/fop/fonts/LazyFont.java +++ b/src/java/org/apache/fop/fonts/LazyFont.java @@ -438,6 +438,9 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable, */ public boolean isSubsetEmbedded() { load(true); + if (realFont.isMultiByte() && this.embeddingMode == EmbeddingMode.FULL) { + return false; + } return realFont.isMultiByte(); } diff --git a/src/java/org/apache/fop/fonts/MultiByteFont.java b/src/java/org/apache/fop/fonts/MultiByteFont.java index a460140cb..da454c504 100644 --- a/src/java/org/apache/fop/fonts/MultiByteFont.java +++ b/src/java/org/apache/fop/fonts/MultiByteFont.java @@ -21,6 +21,7 @@ package org.apache.fop.fonts; import java.nio.CharBuffer; import java.nio.IntBuffer; +import java.util.BitSet; import java.util.Map; import org.apache.commons.logging.Log; @@ -44,13 +45,13 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl private static final Log log // CSOK: ConstantNameCheck = LogFactory.getLog(MultiByteFont.class); - private String ttcName = null; + private String ttcName; private String encoding = "Identity-H"; - private int defaultWidth = 0; + private int defaultWidth; private CIDFontType cidType = CIDFontType.CIDTYPE2; - private CIDSubset subset = new CIDSubset(); + private final CIDSet cidSet; /* advanced typographic support */ private GlyphDefinitionTable gdef; @@ -69,10 +70,15 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl /** * Default constructor */ - public MultiByteFont(InternalResourceResolver resourceResolver) { + public MultiByteFont(InternalResourceResolver resourceResolver, EmbeddingMode embeddingMode) { super(resourceResolver); - subset.setupFirstGlyph(); setFontType(FontType.TYPE0); + setEmbeddingMode(embeddingMode); + if (embeddingMode != EmbeddingMode.FULL) { + cidSet = new CIDSubset(this); + } else { + cidSet = new CIDFull(this); + } } /** {@inheritDoc} */ @@ -129,13 +135,16 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl } public boolean isSubsetEmbedded() { + if (getEmbeddingMode() == EmbeddingMode.FULL) { + return false; + } return true; } /** {@inheritDoc} */ @Override - public CIDSubset getCIDSubset() { - return this.subset; + public CIDSet getCIDSet() { + return this.cidSet; } /** {@inheritDoc} */ @@ -147,7 +156,7 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl /** {@inheritDoc} */ public int getWidth(int i, int size) { if (isEmbeddable()) { - int glyphIndex = subset.getGlyphIndexForSubsetIndex(i); + int glyphIndex = cidSet.getOriginalGlyphIndex(i); return size * width[glyphIndex]; } else { return size * width[i]; @@ -283,9 +292,39 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl glyphIndex = findGlyphIndex(Typeface.NOT_FOUND); } if (isEmbeddable()) { - glyphIndex = subset.mapSubsetChar(glyphIndex, c); + glyphIndex = cidSet.mapChar(glyphIndex, c); } - return (char)glyphIndex; + return (char) glyphIndex; + } + + protected BitSet getGlyphIndices() { + BitSet bitset = new BitSet(); + bitset.set(0); + bitset.set(1); + bitset.set(2); + for (int i = 0; i < cmap.length; i++) { + int start = cmap[i].getUnicodeStart(); + int end = cmap[i].getUnicodeEnd(); + int glyphIndex = cmap[i].getGlyphStartIndex(); + while (start++ < end + 1) { + bitset.set(glyphIndex++); + } + } + return bitset; + } + + protected char[] getChars() { + // the width array is set when the font is built + char[] chars = new char[width.length]; + for (int i = 0; i < cmap.length; i++) { + int start = cmap[i].getUnicodeStart(); + int end = cmap[i].getUnicodeEnd(); + int glyphIndex = cmap[i].getGlyphStartIndex(); + while (start < end + 1) { + chars[glyphIndex++] = (char) start++; + } + } + return chars; } /** {@inheritDoc} */ @@ -331,15 +370,7 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl * @return Map Map of used Glyphs */ public Map getUsedGlyphs() { - return subset.getSubsetGlyphs(); - } - - /** @return an array of the chars used */ - public char[] getCharsUsed() { - if (!isEmbeddable()) { - return null; - } - return subset.getSubsetChars(); + return cidSet.getGlyphs(); } /** diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java index c97b17211..a427fe54e 100644 --- a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java +++ b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java @@ -127,7 +127,7 @@ public class TTFFontLoader extends FontLoader { } if (isCid) { - multiFont = new MultiByteFont(resourceResolver); + multiFont = new MultiByteFont(resourceResolver, embeddingMode); returnFont = multiFont; multiFont.setTTCName(ttcFontName); } else { diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 585f6d86d..31be73a00 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -44,9 +44,9 @@ import org.apache.xmlgraphics.java2d.color.NamedColorSpace; import org.apache.xmlgraphics.xmp.Metadata; import org.apache.fop.fonts.CIDFont; -import org.apache.fop.fonts.CIDSubset; import org.apache.fop.fonts.CodePointMapping; import org.apache.fop.fonts.CustomFont; +import org.apache.fop.fonts.EmbeddingMode; import org.apache.fop.fonts.FontDescriptor; import org.apache.fop.fonts.FontMetrics; import org.apache.fop.fonts.FontType; @@ -1369,23 +1369,14 @@ public class PDFFactory { } else { cidMetrics = (CIDFont)metrics; } - PDFCIDSystemInfo sysInfo - = new PDFCIDSystemInfo(cidMetrics.getRegistry(), - cidMetrics.getOrdering(), - cidMetrics.getSupplement()); - PDFCIDFont cidFont = new PDFCIDFont(subsetFontName, - cidMetrics.getCIDType(), - cidMetrics.getDefaultWidth(), - getSubsetWidths(cidMetrics), sysInfo, - (PDFCIDFontDescriptor)pdfdesc); + PDFCIDSystemInfo sysInfo = new PDFCIDSystemInfo(cidMetrics.getRegistry(), + cidMetrics.getOrdering(), cidMetrics.getSupplement()); + PDFCIDFont cidFont = new PDFCIDFont(subsetFontName, cidMetrics.getCIDType(), + cidMetrics.getDefaultWidth(), getFontWidths(cidMetrics), sysInfo, + (PDFCIDFontDescriptor) pdfdesc); getDocument().registerObject(cidFont); - - PDFCMap cmap = new PDFToUnicodeCMap( - cidMetrics.getCIDSubset().getSubsetChars(), - "fop-ucs-H", - new PDFCIDSystemInfo("Adobe", - "Identity", - 0), false); + PDFCMap cmap = new PDFToUnicodeCMap(cidMetrics.getCIDSet().getChars(), "fop-ucs-H", + new PDFCIDSystemInfo("Adobe", "Identity", 0), false); getDocument().registerObject(cmap); ((PDFFontType0)font).setCMAP(cmap); ((PDFFontType0)font).setDescendantFonts(cidFont); @@ -1476,23 +1467,11 @@ public class PDFFactory { return PDFEncoding.createPDFEncoding(encoding, fontName); } - /** - * Creates and returns a width array with the widths of all the characters in the subset. - * @param cidFont the font - * @return the width array - */ - public PDFWArray getSubsetWidths(CIDFont cidFont) { + private PDFWArray getFontWidths(CIDFont cidFont) { // Create widths for reencoded chars PDFWArray warray = new PDFWArray(); - int[] widths = cidFont.getWidths(); - CIDSubset subset = cidFont.getCIDSubset(); - int[] tmpWidth = new int[subset.getSubsetSize()]; - - for (int i = 0, c = subset.getSubsetSize(); i < c; i++) { - int nwx = Math.max(0, subset.getGlyphIndexForSubsetIndex(i)); - tmpWidth[i] = widths[nwx]; - } - warray.addEntry(0, tmpWidth); + int[] widths = cidFont.getCIDSet().getWidths(); + warray.addEntry(0, widths); return warray; } @@ -1560,13 +1539,13 @@ public class PDFFactory { } private void buildCIDSet(PDFFontDescriptor descriptor, CIDFont cidFont) { - BitSet cidSubset = cidFont.getCIDSubset().getGlyphIndexBitSet(); - PDFStream cidSet = makeStream(null, true); - ByteArrayOutputStream baout = new ByteArrayOutputStream(cidSubset.length() / 8 + 1); + BitSet cidSet = cidFont.getCIDSet().getGlyphIndices(); + PDFStream pdfStream = makeStream(null, true); + ByteArrayOutputStream baout = new ByteArrayOutputStream(cidSet.length() / 8 + 1); int value = 0; - for (int i = 0, c = cidSubset.length(); i < c; i++) { + for (int i = 0, c = cidSet.length(); i < c; i++) { int shift = i % 8; - boolean b = cidSubset.get(i); + boolean b = cidSet.get(i); if (b) { value |= 1 << 7 - shift; } @@ -1577,8 +1556,8 @@ public class PDFFactory { } baout.write(value); try { - cidSet.setData(baout.toByteArray()); - descriptor.setCIDSet(cidSet); + pdfStream.setData(baout.toByteArray()); + descriptor.setCIDSet(pdfStream); } catch (IOException ioe) { log.error( "Failed to write CIDSet [" + cidFont + "] " @@ -1609,14 +1588,16 @@ public class PDFFactory { if (desc.getFontType() == FontType.TYPE0) { MultiByteFont mbfont = (MultiByteFont) font; FontFileReader reader = new FontFileReader(in); - - TTFSubSetFile subset = new TTFSubSetFile(); - subset.readFont(reader, mbfont.getTTCName(), mbfont.getUsedGlyphs()); - byte[] subsetFont = subset.getFontSubset(); - // Only TrueType CID fonts are supported now - - embeddedFont = new PDFTTFStream(subsetFont.length); - ((PDFTTFStream) embeddedFont).setData(subsetFont, subsetFont.length); + byte[] fontBytes; + if (font.getEmbeddingMode() == EmbeddingMode.FULL) { + fontBytes = reader.getAllBytes(); + } else { + TTFSubSetFile ttfFile = new TTFSubSetFile(); + ttfFile.readFont(reader, mbfont.getTTCName(), mbfont.getUsedGlyphs()); + fontBytes = ttfFile.getFontSubset(); + } + embeddedFont = new PDFTTFStream(fontBytes.length); + ((PDFTTFStream) embeddedFont).setData(fontBytes, fontBytes.length); } else if (desc.getFontType() == FontType.TYPE1) { PFBParser parser = new PFBParser(); PFBData pfb = parser.parsePFB(in); diff --git a/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java b/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java index d536167c3..0de02c766 100644 --- a/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java +++ b/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java @@ -24,7 +24,6 @@ import org.apache.xmlgraphics.image.writer.ResolutionUnit; import org.apache.fop.apps.MimeConstants; import org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererConfigParser; import org.apache.fop.render.intermediate.IFContext; -import org.apache.fop.render.intermediate.IFDocumentHandler; import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; /** diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java index c99bb21e1..9e810c824 100644 --- a/src/java/org/apache/fop/render/ps/PSFontUtils.java +++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java @@ -38,7 +38,7 @@ import org.apache.xmlgraphics.ps.dsc.ResourceTracker; import org.apache.fop.fonts.Base14Font; import org.apache.fop.fonts.CIDFontType; -import org.apache.fop.fonts.CIDSubset; +import org.apache.fop.fonts.CIDSet; import org.apache.fop.fonts.CMapSegment; import org.apache.fop.fonts.CustomFont; import org.apache.fop.fonts.EmbeddingMode; @@ -457,15 +457,17 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { // TODO /FontInfo gen.write("/CIDCount "); - CIDSubset cidSubset = font.getCIDSubset(); - int subsetSize = cidSubset.getSubsetSize(); - gen.write(subsetSize); + CIDSet cidSet = font.getCIDSet(); + int numberOfGlyphs = cidSet.getNumberOfGlyphs(); + gen.write(numberOfGlyphs); gen.writeln(" def"); gen.writeln("/GDBytes 2 def"); // TODO always 2? gen.writeln("/CIDMap [<"); int colCount = 0; int lineCount = 1; - for (int cid = 0; cid < subsetSize; cid++) { + int nextBitSet = 0; + int previousBitSet = 0; + for (int cid = 0; cid < numberOfGlyphs; cid++) { if (colCount++ == 20) { gen.newLine(); colCount = 1; @@ -478,7 +480,23 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { if (font.getEmbeddingMode() != EmbeddingMode.FULL) { gid = HexEncoder.encode(cid, 4); } else { - gid = HexEncoder.encode(cidSubset.getGlyphIndexForSubsetIndex(cid), 4); + previousBitSet = nextBitSet; + nextBitSet = cidSet.getGlyphIndices().nextSetBit(nextBitSet); + while (previousBitSet++ < nextBitSet) { + // if there are gaps in the indices we pad them with zeros + gen.write("0000"); + cid++; + if (colCount++ == 20) { + gen.newLine(); + colCount = 1; + if (lineCount++ == 800) { + gen.writeln("> <"); + lineCount = 1; + } + } + } + gid = HexEncoder.encode(nextBitSet, 4); + nextBitSet++; } gen.write(gid); } diff --git a/status.xml b/status.xml index b39d3d9e0..7571ddfda 100644 --- a/status.xml +++ b/status.xml @@ -62,6 +62,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Full font embedding in PDF + Added configuration for RowPerStrip configuration in the Tiff renderer. RowsPerStrip can be configured to 1 or to the total # of rows. diff --git a/test/java/org/apache/fop/fonts/CIDFullTestCase.java b/test/java/org/apache/fop/fonts/CIDFullTestCase.java new file mode 100644 index 000000000..7df6cc25b --- /dev/null +++ b/test/java/org/apache/fop/fonts/CIDFullTestCase.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fonts; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.fop.util.CharUtilities; + +public class CIDFullTestCase { + + private CIDFull cidFull; + private MultiByteFont mbFont; + private BitSet bs; + private char[] chars; + private int[] widths; + private Map glyphs; + + @Before + public void setup() { + bs = new BitSet(); + glyphs = new HashMap(); + chars = new char[18]; + widths = new int[18]; + int i = 0; + for (int j = 0; j < 20; j++) { + if (j == 10 || j == 11) { + continue; + } + bs.set(j); + glyphs.put(Integer.valueOf(j), Integer.valueOf(j)); + chars[i] = (char) j; + widths[i] = 100; + i++; + } + mbFont = mock(MultiByteFont.class); + when(mbFont.getGlyphIndices()).thenReturn(bs); + when(mbFont.getChars()).thenReturn(chars); + when(mbFont.getWidths()).thenReturn(widths); + cidFull = new CIDFull(mbFont); + } + + @Test + public void testGetOriginalGlyphIndex() { + // index 5 exists + assertEquals(cidFull.getOriginalGlyphIndex(5), 5); + } + + @Test + public void testGetUnicode() { + // index 9 exists + assertEquals(cidFull.getUnicode(9), (char) 9); + // index 10 does not + assertEquals(cidFull.getUnicode(10), CharUtilities.NOT_A_CHARACTER); + } + + @Test + public void testMapChar() { + // index 9 exists + char c = 'a'; + assertEquals(cidFull.mapChar(9, c), (char) 9); + } + + @Test + public void testGetGlyphs() { + Map fontGlyphs = cidFull.getGlyphs(); + for (Integer key : fontGlyphs.keySet()) { + assertEquals(fontGlyphs.get(key), glyphs.get(key)); + } + assertTrue(fontGlyphs.size() == glyphs.size()); + } + + @Test + public void testGetChars() { + assertArrayEquals(cidFull.getChars(), chars); + } + + @Test + public void testGetNumberOfGlyphs() { + assertTrue(cidFull.getNumberOfGlyphs() == 20); + } + + @Test + public void testGetGlyphIndices() { + assertEquals(bs, cidFull.getGlyphIndices()); + } + + @Test + public void testGetWidths() { + assertArrayEquals(cidFull.getWidths(), widths); + } + +} diff --git a/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java b/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java index ac9df4046..971471fa2 100644 --- a/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java @@ -24,14 +24,16 @@ import java.net.URI; import org.junit.Test; +import static org.junit.Assert.assertEquals; + import org.apache.fop.apps.io.InternalResourceResolver; import org.apache.fop.apps.io.ResourceResolver; import org.apache.fop.apps.io.ResourceResolverFactory; +import org.apache.fop.fonts.CIDSet; import org.apache.fop.fonts.CIDSubset; +import org.apache.fop.fonts.EmbeddingMode; import org.apache.fop.fonts.MultiByteFont; -import static org.junit.Assert.assertEquals; - /** * Test case for {@link PDFFactory}. */ @@ -45,7 +47,7 @@ public class PDFFactoryTestCase { public void testSubsetFontNamePrefix() { class MockedFont extends MultiByteFont { public MockedFont(InternalResourceResolver resolver) { - super(resolver); + super(resolver, EmbeddingMode.AUTO); } @Override @@ -54,8 +56,8 @@ public class PDFFactoryTestCase { } @Override - public CIDSubset getCIDSubset() { - return new CIDSubset(); + public CIDSet getCIDSet() { + return new CIDSubset(this); } } PDFDocument doc = new PDFDocument("Test"); -- 2.39.5