git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1384690 13f79535-47bb-0310-9956-ffa450edef68tags/fop-2_0
public abstract class CIDFont extends CustomFont { | public abstract class CIDFont extends CustomFont { | ||||
/** Contains the character widths for all characters in the font */ | /** 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 | * @param resourceResolver the URI resolver for controlling file access | ||||
* Returns the subset information for this font. | * Returns the subset information for this font. | ||||
* @return the subset information | * @return the subset information | ||||
*/ | */ | ||||
public abstract CIDSubset getCIDSubset(); | |||||
public abstract CIDSet getCIDSet(); | |||||
// ---- Optional ---- | // ---- Optional ---- | ||||
/** | /** |
/* | |||||
* 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<Integer, Integer> getGlyphs() { | |||||
// this is never really called for full embedded fonts but the equivalent map would be the identity | |||||
initGlyphIndices(); | |||||
Map<Integer, Integer> glyphs = new HashMap<Integer, Integer>(); | |||||
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(); | |||||
} | |||||
} |
/* | |||||
* 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<Integer, Integer> 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. | |||||
* <p> | |||||
* 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(); | |||||
} |
import org.apache.fop.util.CharUtilities; | 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) | * usedGlyphs contains orginal, new glyph index (glyph index -> char selector) | ||||
* usedGlyphsIndex contains new glyph, original index (char selector -> glyph index) | * usedGlyphsIndex contains new glyph, original index (char selector -> glyph index) | ||||
*/ | */ | ||||
private Map<Integer, Integer> usedGlyphsIndex = new HashMap<Integer, Integer>(); | private Map<Integer, Integer> usedGlyphsIndex = new HashMap<Integer, Integer>(); | ||||
private int usedGlyphsCount = 0; | |||||
private int usedGlyphsCount; | |||||
/** | /** | ||||
* usedCharsIndex contains new glyph, original char (char selector -> Unicode) | * usedCharsIndex contains new glyph, original char (char selector -> Unicode) | ||||
*/ | */ | ||||
private Map<Integer, Character> usedCharsIndex = new HashMap<Integer, Character>(); | private Map<Integer, Character> usedCharsIndex = new HashMap<Integer, Character>(); | ||||
/** | |||||
* 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++; | 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) { | if (glyphIndex != null) { | ||||
return glyphIndex.intValue(); | |||||
return glyphIndex; | |||||
} else { | } else { | ||||
return -1; | 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) { | if (mapValue != null) { | ||||
return mapValue.charValue(); | return mapValue.charValue(); | ||||
} else { | } else { | ||||
} | } | ||||
} | } | ||||
/** | |||||
* 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 | // Reencode to a new subset font or get the reencoded value | ||||
// IOW, accumulate the accessed characters and build a character map for them | // 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) { | if (subsetCharSelector == null) { | ||||
int selector = usedGlyphsCount; | 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++; | usedGlyphsCount++; | ||||
return selector; | return selector; | ||||
} else { | } 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<Integer, Integer> getSubsetGlyphs() { | |||||
/** {@inheritDoc} */ | |||||
public Map<Integer, Integer> getGlyphs() { | |||||
return Collections.unmodifiableMap(this.usedGlyphs); | 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]; | char[] charArray = new char[usedGlyphsCount]; | ||||
for (int i = 0; i < usedGlyphsCount; i++) { | for (int i = 0; i < usedGlyphsCount; i++) { | ||||
charArray[i] = getUnicodeForSubsetIndex(i); | |||||
charArray[i] = getUnicode(i); | |||||
} | } | ||||
return charArray; | 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; | 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(); | BitSet bitset = new BitSet(); | ||||
for (Integer cid : usedGlyphs.keySet()) { | for (Integer cid : usedGlyphs.keySet()) { | ||||
bitset.set(cid.intValue()); | |||||
bitset.set(cid); | |||||
} | } | ||||
return bitset; | 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; | |||||
} | |||||
} | } |
throws SAXException { | throws SAXException { | ||||
if (localName.equals("font-metrics")) { | if (localName.equals("font-metrics")) { | ||||
if ("TYPE0".equals(attributes.getValue("type"))) { | if ("TYPE0".equals(attributes.getValue("type"))) { | ||||
multiFont = new MultiByteFont(resourceResolver); | |||||
multiFont = new MultiByteFont(resourceResolver, EmbeddingMode.AUTO); | |||||
returnFont = multiFont; | returnFont = multiFont; | ||||
isCID = true; | isCID = true; | ||||
TTFReader.checkMetricsVersion(attributes); | TTFReader.checkMetricsVersion(attributes); |
*/ | */ | ||||
public boolean isSubsetEmbedded() { | public boolean isSubsetEmbedded() { | ||||
load(true); | load(true); | ||||
if (realFont.isMultiByte() && this.embeddingMode == EmbeddingMode.FULL) { | |||||
return false; | |||||
} | |||||
return realFont.isMultiByte(); | return realFont.isMultiByte(); | ||||
} | } | ||||
import java.nio.CharBuffer; | import java.nio.CharBuffer; | ||||
import java.nio.IntBuffer; | import java.nio.IntBuffer; | ||||
import java.util.BitSet; | |||||
import java.util.Map; | import java.util.Map; | ||||
import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||
private static final Log log // CSOK: ConstantNameCheck | private static final Log log // CSOK: ConstantNameCheck | ||||
= LogFactory.getLog(MultiByteFont.class); | = LogFactory.getLog(MultiByteFont.class); | ||||
private String ttcName = null; | |||||
private String ttcName; | |||||
private String encoding = "Identity-H"; | private String encoding = "Identity-H"; | ||||
private int defaultWidth = 0; | |||||
private int defaultWidth; | |||||
private CIDFontType cidType = CIDFontType.CIDTYPE2; | private CIDFontType cidType = CIDFontType.CIDTYPE2; | ||||
private CIDSubset subset = new CIDSubset(); | |||||
private final CIDSet cidSet; | |||||
/* advanced typographic support */ | /* advanced typographic support */ | ||||
private GlyphDefinitionTable gdef; | private GlyphDefinitionTable gdef; | ||||
/** | /** | ||||
* Default constructor | * Default constructor | ||||
*/ | */ | ||||
public MultiByteFont(InternalResourceResolver resourceResolver) { | |||||
public MultiByteFont(InternalResourceResolver resourceResolver, EmbeddingMode embeddingMode) { | |||||
super(resourceResolver); | super(resourceResolver); | ||||
subset.setupFirstGlyph(); | |||||
setFontType(FontType.TYPE0); | setFontType(FontType.TYPE0); | ||||
setEmbeddingMode(embeddingMode); | |||||
if (embeddingMode != EmbeddingMode.FULL) { | |||||
cidSet = new CIDSubset(this); | |||||
} else { | |||||
cidSet = new CIDFull(this); | |||||
} | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
} | } | ||||
public boolean isSubsetEmbedded() { | public boolean isSubsetEmbedded() { | ||||
if (getEmbeddingMode() == EmbeddingMode.FULL) { | |||||
return false; | |||||
} | |||||
return true; | return true; | ||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
@Override | @Override | ||||
public CIDSubset getCIDSubset() { | |||||
return this.subset; | |||||
public CIDSet getCIDSet() { | |||||
return this.cidSet; | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public int getWidth(int i, int size) { | public int getWidth(int i, int size) { | ||||
if (isEmbeddable()) { | if (isEmbeddable()) { | ||||
int glyphIndex = subset.getGlyphIndexForSubsetIndex(i); | |||||
int glyphIndex = cidSet.getOriginalGlyphIndex(i); | |||||
return size * width[glyphIndex]; | return size * width[glyphIndex]; | ||||
} else { | } else { | ||||
return size * width[i]; | return size * width[i]; | ||||
glyphIndex = findGlyphIndex(Typeface.NOT_FOUND); | glyphIndex = findGlyphIndex(Typeface.NOT_FOUND); | ||||
} | } | ||||
if (isEmbeddable()) { | 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} */ | /** {@inheritDoc} */ | ||||
* @return Map Map of used Glyphs | * @return Map Map of used Glyphs | ||||
*/ | */ | ||||
public Map<Integer, Integer> getUsedGlyphs() { | public Map<Integer, Integer> getUsedGlyphs() { | ||||
return subset.getSubsetGlyphs(); | |||||
} | |||||
/** @return an array of the chars used */ | |||||
public char[] getCharsUsed() { | |||||
if (!isEmbeddable()) { | |||||
return null; | |||||
} | |||||
return subset.getSubsetChars(); | |||||
return cidSet.getGlyphs(); | |||||
} | } | ||||
/** | /** |
} | } | ||||
if (isCid) { | if (isCid) { | ||||
multiFont = new MultiByteFont(resourceResolver); | |||||
multiFont = new MultiByteFont(resourceResolver, embeddingMode); | |||||
returnFont = multiFont; | returnFont = multiFont; | ||||
multiFont.setTTCName(ttcFontName); | multiFont.setTTCName(ttcFontName); | ||||
} else { | } else { |
import org.apache.xmlgraphics.xmp.Metadata; | import org.apache.xmlgraphics.xmp.Metadata; | ||||
import org.apache.fop.fonts.CIDFont; | import org.apache.fop.fonts.CIDFont; | ||||
import org.apache.fop.fonts.CIDSubset; | |||||
import org.apache.fop.fonts.CodePointMapping; | import org.apache.fop.fonts.CodePointMapping; | ||||
import org.apache.fop.fonts.CustomFont; | import org.apache.fop.fonts.CustomFont; | ||||
import org.apache.fop.fonts.EmbeddingMode; | |||||
import org.apache.fop.fonts.FontDescriptor; | import org.apache.fop.fonts.FontDescriptor; | ||||
import org.apache.fop.fonts.FontMetrics; | import org.apache.fop.fonts.FontMetrics; | ||||
import org.apache.fop.fonts.FontType; | import org.apache.fop.fonts.FontType; | ||||
} else { | } else { | ||||
cidMetrics = (CIDFont)metrics; | 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); | 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); | getDocument().registerObject(cmap); | ||||
((PDFFontType0)font).setCMAP(cmap); | ((PDFFontType0)font).setCMAP(cmap); | ||||
((PDFFontType0)font).setDescendantFonts(cidFont); | ((PDFFontType0)font).setDescendantFonts(cidFont); | ||||
return PDFEncoding.createPDFEncoding(encoding, fontName); | 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 | // Create widths for reencoded chars | ||||
PDFWArray warray = new PDFWArray(); | 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; | return warray; | ||||
} | } | ||||
} | } | ||||
private void buildCIDSet(PDFFontDescriptor descriptor, CIDFont cidFont) { | 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; | 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; | int shift = i % 8; | ||||
boolean b = cidSubset.get(i); | |||||
boolean b = cidSet.get(i); | |||||
if (b) { | if (b) { | ||||
value |= 1 << 7 - shift; | value |= 1 << 7 - shift; | ||||
} | } | ||||
} | } | ||||
baout.write(value); | baout.write(value); | ||||
try { | try { | ||||
cidSet.setData(baout.toByteArray()); | |||||
descriptor.setCIDSet(cidSet); | |||||
pdfStream.setData(baout.toByteArray()); | |||||
descriptor.setCIDSet(pdfStream); | |||||
} catch (IOException ioe) { | } catch (IOException ioe) { | ||||
log.error( | log.error( | ||||
"Failed to write CIDSet [" + cidFont + "] " | "Failed to write CIDSet [" + cidFont + "] " | ||||
if (desc.getFontType() == FontType.TYPE0) { | if (desc.getFontType() == FontType.TYPE0) { | ||||
MultiByteFont mbfont = (MultiByteFont) font; | MultiByteFont mbfont = (MultiByteFont) font; | ||||
FontFileReader reader = new FontFileReader(in); | 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) { | } else if (desc.getFontType() == FontType.TYPE1) { | ||||
PFBParser parser = new PFBParser(); | PFBParser parser = new PFBParser(); | ||||
PFBData pfb = parser.parsePFB(in); | PFBData pfb = parser.parsePFB(in); |
import org.apache.fop.apps.MimeConstants; | import org.apache.fop.apps.MimeConstants; | ||||
import org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererConfigParser; | import org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererConfigParser; | ||||
import org.apache.fop.render.intermediate.IFContext; | import org.apache.fop.render.intermediate.IFContext; | ||||
import org.apache.fop.render.intermediate.IFDocumentHandler; | |||||
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; | import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; | ||||
/** | /** |
import org.apache.fop.fonts.Base14Font; | import org.apache.fop.fonts.Base14Font; | ||||
import org.apache.fop.fonts.CIDFontType; | 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.CMapSegment; | ||||
import org.apache.fop.fonts.CustomFont; | import org.apache.fop.fonts.CustomFont; | ||||
import org.apache.fop.fonts.EmbeddingMode; | import org.apache.fop.fonts.EmbeddingMode; | ||||
// TODO /FontInfo | // TODO /FontInfo | ||||
gen.write("/CIDCount "); | 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(" def"); | ||||
gen.writeln("/GDBytes 2 def"); // TODO always 2? | gen.writeln("/GDBytes 2 def"); // TODO always 2? | ||||
gen.writeln("/CIDMap [<"); | gen.writeln("/CIDMap [<"); | ||||
int colCount = 0; | int colCount = 0; | ||||
int lineCount = 1; | 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) { | if (colCount++ == 20) { | ||||
gen.newLine(); | gen.newLine(); | ||||
colCount = 1; | colCount = 1; | ||||
if (font.getEmbeddingMode() != EmbeddingMode.FULL) { | if (font.getEmbeddingMode() != EmbeddingMode.FULL) { | ||||
gid = HexEncoder.encode(cid, 4); | gid = HexEncoder.encode(cid, 4); | ||||
} else { | } 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); | gen.write(gid); | ||||
} | } |
documents. Example: the fix of marks layering will be such a case when it's done. | documents. Example: the fix of marks layering will be such a case when it's done. | ||||
--> | --> | ||||
<release version="FOP Trunk" date="TBD"> | <release version="FOP Trunk" date="TBD"> | ||||
<action context="Fonts" dev="MH" type="add" fixes-bug="53868" importance="low" due-to="Luis Bernardo"> | |||||
Full font embedding in PDF | |||||
</action> | |||||
<action context="Renderers" dev="PH" type="add" fixes-bug="53865" importance="low"> | <action context="Renderers" dev="PH" type="add" fixes-bug="53865" importance="low"> | ||||
Added configuration for RowPerStrip configuration in the Tiff renderer. | Added configuration for RowPerStrip configuration in the Tiff renderer. | ||||
RowsPerStrip can be configured to 1 or to the total # of rows. | RowsPerStrip can be configured to 1 or to the total # of rows. |
/* | |||||
* 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<Integer, Integer> glyphs; | |||||
@Before | |||||
public void setup() { | |||||
bs = new BitSet(); | |||||
glyphs = new HashMap<Integer, Integer>(); | |||||
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<Integer, Integer> 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); | |||||
} | |||||
} |
import org.junit.Test; | import org.junit.Test; | ||||
import static org.junit.Assert.assertEquals; | |||||
import org.apache.fop.apps.io.InternalResourceResolver; | import org.apache.fop.apps.io.InternalResourceResolver; | ||||
import org.apache.fop.apps.io.ResourceResolver; | import org.apache.fop.apps.io.ResourceResolver; | ||||
import org.apache.fop.apps.io.ResourceResolverFactory; | import org.apache.fop.apps.io.ResourceResolverFactory; | ||||
import org.apache.fop.fonts.CIDSet; | |||||
import org.apache.fop.fonts.CIDSubset; | import org.apache.fop.fonts.CIDSubset; | ||||
import org.apache.fop.fonts.EmbeddingMode; | |||||
import org.apache.fop.fonts.MultiByteFont; | import org.apache.fop.fonts.MultiByteFont; | ||||
import static org.junit.Assert.assertEquals; | |||||
/** | /** | ||||
* Test case for {@link PDFFactory}. | * Test case for {@link PDFFactory}. | ||||
*/ | */ | ||||
public void testSubsetFontNamePrefix() { | public void testSubsetFontNamePrefix() { | ||||
class MockedFont extends MultiByteFont { | class MockedFont extends MultiByteFont { | ||||
public MockedFont(InternalResourceResolver resolver) { | public MockedFont(InternalResourceResolver resolver) { | ||||
super(resolver); | |||||
super(resolver, EmbeddingMode.AUTO); | |||||
} | } | ||||
@Override | @Override | ||||
} | } | ||||
@Override | @Override | ||||
public CIDSubset getCIDSubset() { | |||||
return new CIDSubset(); | |||||
public CIDSet getCIDSet() { | |||||
return new CIDSubset(this); | |||||
} | } | ||||
} | } | ||||
PDFDocument doc = new PDFDocument("Test"); | PDFDocument doc = new PDFDocument("Test"); |