git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1792597 13f79535-47bb-0310-9956-ffa450edef68tags/fop-2_3
@@ -82,7 +82,7 @@ public abstract class CIDFont extends CustomFont { | |||
/** {@inheritDoc} */ | |||
public boolean isMultiByte() { | |||
return true; | |||
return getFontType() != FontType.TYPE1C; | |||
} | |||
} |
@@ -19,6 +19,7 @@ | |||
package org.apache.fop.fonts; | |||
import java.awt.Rectangle; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.URI; | |||
@@ -81,6 +82,8 @@ public abstract class CustomFont extends Typeface | |||
protected List<CMapSegment> cmap = new ArrayList<CMapSegment>(); | |||
private boolean useAdvanced = true; | |||
private boolean simulateStyle; | |||
protected List<SimpleSingleByteEncoding> additionalEncodings; | |||
protected Map<Character, SingleByteFont.UnencodedCharacter> unencodedCharacters; | |||
/** | |||
* @param resourceResolver the URI resource resolver for controlling file access | |||
@@ -590,4 +593,93 @@ public abstract class CustomFont extends Typeface | |||
* @return The character | |||
*/ | |||
public abstract char getUnicodeFromGID(int glyphIndex); | |||
/** | |||
* Indicates whether the encoding has additional encodings besides the primary encoding. | |||
* @return true if there are additional encodings. | |||
*/ | |||
public boolean hasAdditionalEncodings() { | |||
return (this.additionalEncodings != null) && (this.additionalEncodings.size() > 0); | |||
} | |||
/** | |||
* Returns the number of additional encodings this single-byte font maintains. | |||
* @return the number of additional encodings | |||
*/ | |||
public int getAdditionalEncodingCount() { | |||
if (hasAdditionalEncodings()) { | |||
return this.additionalEncodings.size(); | |||
} else { | |||
return 0; | |||
} | |||
} | |||
/** | |||
* Returns an additional encoding. | |||
* @param index the index of the additional encoding | |||
* @return the additional encoding | |||
* @throws IndexOutOfBoundsException if the index is out of bounds | |||
*/ | |||
public SimpleSingleByteEncoding getAdditionalEncoding(int index) | |||
throws IndexOutOfBoundsException { | |||
if (hasAdditionalEncodings()) { | |||
return this.additionalEncodings.get(index); | |||
} else { | |||
throw new IndexOutOfBoundsException("No additional encodings available"); | |||
} | |||
} | |||
/** | |||
* Adds an unencoded character (one that is not supported by the primary encoding). | |||
* @param ch the named character | |||
* @param width the width of the character | |||
*/ | |||
public void addUnencodedCharacter(NamedCharacter ch, int width, Rectangle bbox) { | |||
if (this.unencodedCharacters == null) { | |||
this.unencodedCharacters = new HashMap<Character, SingleByteFont.UnencodedCharacter>(); | |||
} | |||
if (ch.hasSingleUnicodeValue()) { | |||
SingleByteFont.UnencodedCharacter uc = new SingleByteFont.UnencodedCharacter(ch, width, bbox); | |||
this.unencodedCharacters.put(ch.getSingleUnicodeValue(), uc); | |||
} else { | |||
//Cannot deal with unicode sequences, so ignore this character | |||
} | |||
} | |||
/** | |||
* Adds a character to additional encodings | |||
* @param ch character to map | |||
*/ | |||
protected char mapUnencodedChar(char ch) { | |||
if (this.unencodedCharacters != null) { | |||
SingleByteFont.UnencodedCharacter unencoded = this.unencodedCharacters.get(ch); | |||
if (unencoded != null) { | |||
if (this.additionalEncodings == null) { | |||
this.additionalEncodings = new ArrayList<SimpleSingleByteEncoding>(); | |||
} | |||
SimpleSingleByteEncoding encoding = null; | |||
char mappedStart = 0; | |||
int additionalsCount = this.additionalEncodings.size(); | |||
for (int i = 0; i < additionalsCount; i++) { | |||
mappedStart += 256; | |||
encoding = getAdditionalEncoding(i); | |||
char alt = encoding.mapChar(ch); | |||
if (alt != 0) { | |||
return (char)(mappedStart + alt); | |||
} | |||
} | |||
if (encoding != null && encoding.isFull()) { | |||
encoding = null; | |||
} | |||
if (encoding == null) { | |||
encoding = new SimpleSingleByteEncoding( | |||
getFontName() + "EncodingSupp" + (additionalsCount + 1)); | |||
this.additionalEncodings.add(encoding); | |||
mappedStart += 256; | |||
} | |||
return (char)(mappedStart + encoding.addCharacter(unencoded.getCharacter())); | |||
} | |||
} | |||
return 0; | |||
} | |||
} |
@@ -371,6 +371,9 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl | |||
if (isEmbeddable()) { | |||
glyphIndex = cidSet.mapChar(glyphIndex, c); | |||
} | |||
if (isCID() && glyphIndex > 256) { | |||
mapUnencodedChar(c); | |||
} | |||
return (char) glyphIndex; | |||
} | |||
@@ -20,11 +20,9 @@ | |||
package org.apache.fop.fonts; | |||
import java.awt.Rectangle; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.LinkedHashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.TreeSet; | |||
@@ -53,8 +51,6 @@ public class SingleByteFont extends CustomFont { | |||
private Rectangle[] boundingBoxes; | |||
private Map<Character, UnencodedCharacter> unencodedCharacters; | |||
private List<SimpleSingleByteEncoding> additionalEncodings; | |||
private Map<Character, Character> alternativeCodes; | |||
private PostScriptVersion ttPostScriptVersion; | |||
@@ -253,39 +249,6 @@ public class SingleByteFont extends CustomFont { | |||
} | |||
} | |||
private char mapUnencodedChar(char ch) { | |||
if (this.unencodedCharacters != null) { | |||
UnencodedCharacter unencoded = this.unencodedCharacters.get(ch); | |||
if (unencoded != null) { | |||
if (this.additionalEncodings == null) { | |||
this.additionalEncodings = new ArrayList<SimpleSingleByteEncoding>(); | |||
} | |||
SimpleSingleByteEncoding encoding = null; | |||
char mappedStart = 0; | |||
int additionalsCount = this.additionalEncodings.size(); | |||
for (int i = 0; i < additionalsCount; i++) { | |||
mappedStart += 256; | |||
encoding = getAdditionalEncoding(i); | |||
char alt = encoding.mapChar(ch); | |||
if (alt != 0) { | |||
return (char)(mappedStart + alt); | |||
} | |||
} | |||
if (encoding != null && encoding.isFull()) { | |||
encoding = null; | |||
} | |||
if (encoding == null) { | |||
encoding = new SimpleSingleByteEncoding( | |||
getFontName() + "EncodingSupp" + (additionalsCount + 1)); | |||
this.additionalEncodings.add(encoding); | |||
mappedStart += 256; | |||
} | |||
return (char)(mappedStart + encoding.addCharacter(unencoded.getCharacter())); | |||
} | |||
} | |||
return 0; | |||
} | |||
/** {@inheritDoc} */ | |||
@Override | |||
public boolean hasChar(char c) { | |||
@@ -406,41 +369,6 @@ public class SingleByteFont extends CustomFont { | |||
} | |||
} | |||
/** | |||
* Indicates whether the encoding has additional encodings besides the primary encoding. | |||
* @return true if there are additional encodings. | |||
*/ | |||
public boolean hasAdditionalEncodings() { | |||
return (this.additionalEncodings != null) && (this.additionalEncodings.size() > 0); | |||
} | |||
/** | |||
* Returns the number of additional encodings this single-byte font maintains. | |||
* @return the number of additional encodings | |||
*/ | |||
public int getAdditionalEncodingCount() { | |||
if (hasAdditionalEncodings()) { | |||
return this.additionalEncodings.size(); | |||
} else { | |||
return 0; | |||
} | |||
} | |||
/** | |||
* Returns an additional encoding. | |||
* @param index the index of the additional encoding | |||
* @return the additional encoding | |||
* @throws IndexOutOfBoundsException if the index is out of bounds | |||
*/ | |||
public SimpleSingleByteEncoding getAdditionalEncoding(int index) | |||
throws IndexOutOfBoundsException { | |||
if (hasAdditionalEncodings()) { | |||
return this.additionalEncodings.get(index); | |||
} else { | |||
throw new IndexOutOfBoundsException("No additional encodings available"); | |||
} | |||
} | |||
/** | |||
* Returns an array with the widths for an additional encoding. | |||
* @param index the index of the additional encoding | |||
@@ -458,7 +386,7 @@ public class SingleByteFont extends CustomFont { | |||
return arr; | |||
} | |||
private static final class UnencodedCharacter { | |||
protected static final class UnencodedCharacter { | |||
private final NamedCharacter character; | |||
private final int width; |
@@ -98,6 +98,10 @@ public abstract class Typeface implements FontMetrics { | |||
return false; | |||
} | |||
public boolean isCID() { | |||
return getFontType() == FontType.TYPE1C; | |||
} | |||
/** {@inheritDoc} */ | |||
public int getMaxAscent(int size) { | |||
return getAscender(size); |
@@ -182,6 +182,10 @@ public class OFFontLoader extends FontLoader { | |||
returnFont.setWeight(otf.getWeightClass()); | |||
if (isCid) { | |||
if (otf instanceof OTFFile) { | |||
if (((OTFFile) otf).isType1() && embeddingMode == EmbeddingMode.SUBSET) { | |||
multiFont.setFontType(FontType.TYPE1C); | |||
copyGlyphMetricsSingleByte(otf); | |||
} | |||
multiFont.setCIDType(CIDFontType.CIDTYPE0); | |||
} else { | |||
multiFont.setCIDType(CIDFontType.CIDTYPE2); | |||
@@ -223,17 +227,21 @@ public class OFFontLoader extends FontLoader { | |||
private void copyGlyphMetricsSingleByte(OpenFont otf) { | |||
int[] wx = otf.getWidths(); | |||
Rectangle[] bboxes = otf.getBoundingBoxes(); | |||
for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) { | |||
singleFont.setWidth(i, otf.getCharWidth(i)); | |||
int[] bbox = otf.getBBox(i); | |||
singleFont.setBoundingBox(i, | |||
new Rectangle(bbox[0], bbox[1], bbox[2] - bbox[0], bbox[3] - bbox[1])); | |||
if (singleFont != null) { | |||
for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) { | |||
singleFont.setWidth(i, otf.getCharWidth(i)); | |||
int[] bbox = otf.getBBox(i); | |||
singleFont.setBoundingBox(i, | |||
new Rectangle(bbox[0], bbox[1], bbox[2] - bbox[0], bbox[3] - bbox[1])); | |||
} | |||
} | |||
for (CMapSegment segment : otf.getCMaps()) { | |||
if (segment.getUnicodeStart() < 0xFFFE) { | |||
for (char u = (char)segment.getUnicodeStart(); u <= segment.getUnicodeEnd(); u++) { | |||
int codePoint = singleFont.getEncoding().mapChar(u); | |||
int codePoint = 0; | |||
if (singleFont != null) { | |||
codePoint = singleFont.getEncoding().mapChar(u); | |||
} | |||
if (codePoint <= 0) { | |||
int glyphIndex = segment.getGlyphStartIndex() + u - segment.getUnicodeStart(); | |||
String glyphName = otf.getGlyphName(glyphIndex); | |||
@@ -243,7 +251,7 @@ public class OFFontLoader extends FontLoader { | |||
if (glyphName.length() > 0) { | |||
String unicode = Character.toString(u); | |||
NamedCharacter nc = new NamedCharacter(glyphName, unicode); | |||
singleFont.addUnencodedCharacter(nc, wx[glyphIndex], bboxes[glyphIndex]); | |||
returnFont.addUnencodedCharacter(nc, wx[glyphIndex], bboxes[glyphIndex]); | |||
} | |||
} | |||
} |
@@ -24,6 +24,7 @@ import java.io.IOException; | |||
import org.apache.fontbox.cff.CFFDataInput; | |||
import org.apache.fontbox.cff.CFFFont; | |||
import org.apache.fontbox.cff.CFFParser; | |||
import org.apache.fontbox.cff.CFFType1Font; | |||
public class OTFFile extends OpenFont { | |||
@@ -133,4 +134,8 @@ public class OTFFile extends OpenFont { | |||
private static long readLong(CFFDataInput input) throws IOException { | |||
return (input.readCard16() << 16) | input.readCard16(); | |||
} | |||
public boolean isType1() { | |||
return fileFont instanceof CFFType1Font; | |||
} | |||
} |
@@ -111,10 +111,8 @@ public class OTFSubSetFile extends OTFSubSetWriter { | |||
super(); | |||
} | |||
public void readFont(FontFileReader in, String embeddedName, String header, | |||
MultiByteFont mbFont) throws IOException { | |||
this.mbFont = mbFont; | |||
readFont(in, embeddedName, header, mbFont.getUsedGlyphs()); | |||
public void readFont(FontFileReader in, String embeddedName, MultiByteFont mbFont) throws IOException { | |||
readFont(in, embeddedName, mbFont, mbFont.getUsedGlyphs()); | |||
} | |||
/** | |||
@@ -122,13 +120,13 @@ public class OTFSubSetFile extends OTFSubSetWriter { | |||
* | |||
* @param in FontFileReader to read from | |||
* @param embeddedName Name to be checked for in the font file | |||
* @param header The header of the font file | |||
* @param usedGlyphs Map of glyphs (glyphs has old index as (Integer) key and | |||
* new index as (Integer) value) | |||
* @throws IOException in case of an I/O problem | |||
*/ | |||
void readFont(FontFileReader in, String embeddedName, String header, | |||
void readFont(FontFileReader in, String embeddedName, MultiByteFont mbFont, | |||
Map<Integer, Integer> usedGlyphs) throws IOException { | |||
this.mbFont = mbFont; | |||
fontFile = in; | |||
currentPos = 0; | |||
@@ -1038,6 +1036,9 @@ public class OTFSubSetFile extends OTFSubSetWriter { | |||
} else { | |||
writeByte(0); | |||
for (int entry : gidToSID.values()) { | |||
if (entry == 0) { | |||
continue; | |||
} | |||
writeCard16(entry); | |||
} | |||
} |
@@ -22,21 +22,30 @@ package org.apache.fop.pdf; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import org.apache.fop.fonts.CustomFont; | |||
import org.apache.fop.fonts.EmbeddingMode; | |||
import org.apache.fop.fonts.FontType; | |||
/** | |||
* PDFStream for embeddable OpenType CFF fonts. | |||
*/ | |||
public class PDFCFFStreamType0C extends AbstractPDFFontStream { | |||
private byte[] cffData; | |||
private boolean fullEmbed; | |||
private String type; | |||
/** | |||
* Main constructor | |||
* @param fullEmbed Determines whether the font is fully embedded | |||
*/ | |||
public PDFCFFStreamType0C(boolean fullEmbed) { | |||
public PDFCFFStreamType0C(CustomFont font) { | |||
super(); | |||
this.fullEmbed = fullEmbed; | |||
if (font.getEmbeddingMode() == EmbeddingMode.FULL) { | |||
type = "OpenType"; | |||
} else if (font.getFontType() == FontType.TYPE0) { | |||
type = "CIDFontType0C"; | |||
} else { | |||
type = font.getFontType().getName(); | |||
} | |||
} | |||
protected int getSizeHint() throws IOException { | |||
@@ -54,7 +63,6 @@ public class PDFCFFStreamType0C extends AbstractPDFFontStream { | |||
/** {@inheritDoc} */ | |||
protected void populateStreamDict(Object lengthEntry) { | |||
String type = (fullEmbed) ? "OpenType" : "CIDFontType0C"; | |||
put("Subtype", new PDFName(type)); | |||
super.populateStreamDict(lengthEntry); | |||
} |
@@ -28,9 +28,11 @@ import java.io.InputStream; | |||
import java.net.URI; | |||
import java.net.URISyntaxException; | |||
import java.text.DecimalFormat; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.BitSet; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.SortedSet; | |||
@@ -987,6 +989,9 @@ public class PDFFactory { | |||
assert font instanceof PDFFontType0; | |||
((PDFFontType0)font).setCMAP(cmap); | |||
((PDFFontType0)font).setDescendantFonts(cidFont); | |||
} else if (fonttype == FontType.TYPE1C | |||
&& (metrics instanceof LazyFont || metrics instanceof MultiByteFont)) { | |||
handleType1CFont(pdfdesc, font, metrics, fontname, basefont, descriptor); | |||
} else { | |||
assert font instanceof PDFFontNonBase14; | |||
PDFFontNonBase14 nonBase14 = (PDFFontNonBase14)font; | |||
@@ -1123,6 +1128,84 @@ public class PDFFactory { | |||
} | |||
} | |||
private void handleType1CFont(PDFFontDescriptor pdfdesc, PDFFont font, FontMetrics metrics, String fontname, | |||
String basefont, FontDescriptor descriptor) { | |||
PDFFontNonBase14 nonBase14 = (PDFFontNonBase14)font; | |||
nonBase14.setDescriptor(pdfdesc); | |||
MultiByteFont singleByteFont; | |||
if (metrics instanceof LazyFont) { | |||
singleByteFont = (MultiByteFont)((LazyFont)metrics).getRealFont(); | |||
} else { | |||
singleByteFont = (MultiByteFont)metrics; | |||
} | |||
Map<Integer, Integer> usedGlyphs = singleByteFont.getUsedGlyphs(); | |||
SortedSet<Integer> keys = new TreeSet<Integer>(usedGlyphs.keySet()); | |||
keys.remove(0); | |||
int count = keys.size(); | |||
Iterator<String> usedGlyphNames = singleByteFont.getUsedGlyphNames().values().iterator(); | |||
count = setupFontMetrics(nonBase14, pdfdesc, usedGlyphNames, 0, count, metrics); | |||
List<PDFFontNonBase14> additionalEncodings = addAdditionalEncodings(metrics, descriptor, fontname, basefont); | |||
for (int j = 0; j < additionalEncodings.size(); j++) { | |||
PDFFontNonBase14 additional = additionalEncodings.get(j); | |||
int start = 256 * (j + 1); | |||
count = setupFontMetrics(additional, pdfdesc, usedGlyphNames, start, count, metrics); | |||
} | |||
} | |||
private int setupFontMetrics(PDFFontNonBase14 font, PDFFontDescriptor pdfdesc, | |||
Iterator<String> usedGlyphNames, int start, int count, FontMetrics metrics) { | |||
font.setDescriptor(pdfdesc); | |||
PDFArray differences = new PDFArray(); | |||
int firstChar = 0; | |||
differences.add(firstChar); | |||
int lastChar = Math.min(count, 255); | |||
int[] newWidths = new int[lastChar + 1]; | |||
for (int i = 0; i < newWidths.length; i++) { | |||
newWidths[i] = metrics.getWidth(start + i, 1); | |||
differences.add(new PDFName(usedGlyphNames.next())); | |||
count--; | |||
} | |||
font.setWidthMetrics(firstChar, | |||
lastChar, | |||
new PDFArray(null, newWidths)); | |||
PDFEncoding pdfEncoding = new PDFEncoding("WinAnsiEncoding"); | |||
getDocument().registerTrailerObject(pdfEncoding); | |||
pdfEncoding.setDifferences(differences); | |||
font.setEncoding(pdfEncoding); | |||
return count; | |||
} | |||
private List<PDFFontNonBase14> addAdditionalEncodings(FontMetrics metrics, FontDescriptor descriptor, | |||
String fontname, String basefont) { | |||
List<PDFFontNonBase14> additionalEncodings = new ArrayList<PDFFontNonBase14>(); | |||
FontType fonttype = metrics.getFontType(); | |||
if (descriptor != null && (fonttype != FontType.TYPE0)) { | |||
CustomFont singleByteFont; | |||
if (metrics instanceof LazyFont) { | |||
singleByteFont = (CustomFont)((LazyFont)metrics).getRealFont(); | |||
} else { | |||
singleByteFont = (CustomFont)metrics; | |||
} | |||
//Handle additional encodings (characters outside the primary encoding) | |||
if (singleByteFont.hasAdditionalEncodings()) { | |||
for (int i = additionalEncodings.size(), | |||
c = singleByteFont.getAdditionalEncodingCount(); i < c; i++) { | |||
SimpleSingleByteEncoding addEncoding | |||
= singleByteFont.getAdditionalEncoding(i); | |||
String name = fontname + "_" + (i + 1); | |||
Object pdfenc = createPDFEncoding(addEncoding, singleByteFont.getFontName()); | |||
PDFFontNonBase14 addFont = (PDFFontNonBase14)PDFFont.createFont( | |||
name, fonttype, | |||
basefont, pdfenc); | |||
getDocument().registerObject(addFont); | |||
getDocument().getResources().addFont(addFont); | |||
additionalEncodings.add(addFont); | |||
} | |||
} | |||
} | |||
return additionalEncodings; | |||
} | |||
private void generateToUnicodeCmap(PDFFont font, SingleByteEncoding encoding) { | |||
PDFCMap cmap = new PDFToUnicodeCMap(encoding.getUnicodeCharMap(), | |||
"fop-ucs-H", | |||
@@ -1315,10 +1398,17 @@ public class PDFFactory { | |||
((PDFT1Stream) embeddedFont).setData(pfb); | |||
} | |||
} else if (desc.getFontType() == FontType.TYPE1C) { | |||
byte[] file = IOUtils.toByteArray(in); | |||
PDFCFFStream embeddedFont2 = new PDFCFFStream("Type1C"); | |||
embeddedFont2.setData(file); | |||
return embeddedFont2; | |||
if (font.getEmbeddingMode() == EmbeddingMode.SUBSET) { | |||
FontFileReader reader = new FontFileReader(in); | |||
String header = OFFontLoader.readHeader(reader); | |||
byte[] fontBytes = getFontSubsetBytes(reader, (MultiByteFont) font, header, fontPrefix, desc, true); | |||
embeddedFont = getFontStream(font, fontBytes, true); | |||
} else { | |||
byte[] file = IOUtils.toByteArray(in); | |||
PDFCFFStream embeddedFont2 = new PDFCFFStream("Type1C"); | |||
embeddedFont2.setData(file); | |||
return embeddedFont2; | |||
} | |||
} else if (desc.getFontType() == FontType.CIDTYPE0) { | |||
byte[] file = IOUtils.toByteArray(in); | |||
PDFCFFStream embeddedFont2 = new PDFCFFStream("CIDFontType0C"); | |||
@@ -1361,7 +1451,7 @@ public class PDFFactory { | |||
String fontPrefix, FontDescriptor desc, boolean isCFF) throws IOException { | |||
if (isCFF) { | |||
OTFSubSetFile otfFile = new OTFSubSetFile(); | |||
otfFile.readFont(reader, fontPrefix + desc.getEmbedFontName(), header, mbfont); | |||
otfFile.readFont(reader, fontPrefix + desc.getEmbedFontName(), mbfont); | |||
return otfFile.getFontSubset(); | |||
} else { | |||
TTFSubSetFile otfFile = new TTFSubSetFile(); | |||
@@ -1374,7 +1464,7 @@ public class PDFFactory { | |||
throws IOException { | |||
AbstractPDFStream embeddedFont; | |||
if (isCFF) { | |||
embeddedFont = new PDFCFFStreamType0C(font.getEmbeddingMode() == EmbeddingMode.FULL); | |||
embeddedFont = new PDFCFFStreamType0C(font); | |||
((PDFCFFStreamType0C) embeddedFont).setData(fontBytes, fontBytes.length); | |||
} else { | |||
embeddedFont = new PDFTTFStream(fontBytes.length); |
@@ -51,6 +51,7 @@ public abstract class PDFTextUtil { | |||
private String startText; | |||
private String endText; | |||
private boolean useMultiByte; | |||
private boolean useCid; | |||
private StringBuffer bufTJ; | |||
private int textRenderingMode = TR_FILL; | |||
@@ -92,9 +93,9 @@ public abstract class PDFTextUtil { | |||
PDFNumber.doubleOut(lt[5], DEC, sb); | |||
} | |||
private static void writeChar(char ch, StringBuffer sb, boolean multibyte) { | |||
private static void writeChar(char ch, StringBuffer sb, boolean multibyte, boolean cid) { | |||
if (!multibyte) { | |||
if (ch < 32 || ch > 127) { | |||
if (cid || ch < 32 || ch > 127) { | |||
sb.append("\\").append(Integer.toOctalString(ch)); | |||
} else { | |||
switch (ch) { | |||
@@ -113,7 +114,7 @@ public abstract class PDFTextUtil { | |||
} | |||
private void writeChar(char ch, StringBuffer sb) { | |||
writeChar(ch, sb, useMultiByte); | |||
writeChar(ch, sb, useMultiByte, useCid); | |||
} | |||
private void checkInTextObject() { | |||
@@ -199,13 +200,14 @@ public abstract class PDFTextUtil { | |||
* @param fontSize the font size (in points) | |||
* @param multiByte true indicates the font is a multi-byte font, false means single-byte | |||
*/ | |||
public void updateTf(String fontName, double fontSize, boolean multiByte) { | |||
public void updateTf(String fontName, double fontSize, boolean multiByte, boolean cid) { | |||
checkInTextObject(); | |||
if (!fontName.equals(this.currentFontName) || (fontSize != this.currentFontSize)) { | |||
writeTJ(); | |||
this.currentFontName = fontName; | |||
this.currentFontSize = fontSize; | |||
this.useMultiByte = multiByte; | |||
this.useCid = cid; | |||
writeTf(fontName, fontSize); | |||
} | |||
} | |||
@@ -338,11 +340,11 @@ public abstract class PDFTextUtil { | |||
* Writes a "Tj" command with specified character code. | |||
* @param ch character code to write | |||
*/ | |||
public void writeTj(char ch) { | |||
public void writeTj(char ch, boolean multibyte, boolean cid) { | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append('<'); | |||
writeChar(ch, sb, true); | |||
sb.append('>'); | |||
sb.append(startText); | |||
writeChar(ch, sb, multibyte, cid); | |||
sb.append(endText); | |||
sb.append(" Tj\n"); | |||
write(sb); | |||
} |
@@ -451,15 +451,11 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> { | |||
// This assumes that *all* CIDFonts use a /ToUnicode mapping | |||
Typeface tf = getTypeface(fontKey); | |||
SingleByteFont singleByteFont = null; | |||
if (tf instanceof SingleByteFont) { | |||
singleByteFont = (SingleByteFont)tf; | |||
} | |||
Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints); | |||
String fontName = font.getFontName(); | |||
PDFTextUtil textutil = generator.getTextUtil(); | |||
textutil.updateTf(fontKey, fontSize, tf.isMultiByte()); | |||
textutil.updateTf(fontKey, fontSize, tf.isMultiByte(), tf.isCID()); | |||
double shear = 0; | |||
boolean simulateStyle = tf instanceof CustomFont && ((CustomFont) tf).getSimulateStyle(); | |||
@@ -488,7 +484,7 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> { | |||
float glyphAdjust = 0; | |||
if (font.hasChar(orgChar)) { | |||
ch = font.mapChar(orgChar); | |||
ch = selectAndMapSingleByteFont(singleByteFont, fontName, fontSize, textutil, ch); | |||
ch = selectAndMapSingleByteFont(tf, fontName, fontSize, textutil, ch); | |||
if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) { | |||
glyphAdjust += wordSpacing; | |||
} | |||
@@ -504,8 +500,7 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> { | |||
glyphAdjust += wordSpacing; | |||
} | |||
} | |||
ch = selectAndMapSingleByteFont(singleByteFont, fontName, fontSize, | |||
textutil, ch); | |||
ch = selectAndMapSingleByteFont(tf, fontName, fontSize, textutil, ch); | |||
} | |||
textutil.writeTJMappedChar(ch); | |||
@@ -533,7 +528,7 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> { | |||
assert dp != null; | |||
String fk = getFontInfo().getInternalFontKey(triplet); | |||
Typeface tf = getTypeface(fk); | |||
if (tf.isMultiByte()) { | |||
if (tf.isMultiByte() || tf.isCID()) { | |||
int fs = state.getFontSize(); | |||
float fsPoints = fs / 1000f; | |||
Font f = getFontInfo().getFontInstance(triplet, fs); | |||
@@ -544,7 +539,7 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> { | |||
double yoLast = 0f; | |||
double wox = wordSpacing; | |||
tu.writeTextMatrix(new AffineTransform(1, 0, 0, -1, x / 1000f, y / 1000f)); | |||
tu.updateTf(fk, fsPoints, true); | |||
tu.updateTf(fk, fsPoints, tf.isMultiByte(), true); | |||
generator.updateCharacterSpacing(letterSpacing / 1000f); | |||
for (int i = 0, n = text.length(); i < n; i++) { | |||
char ch = text.charAt(i); | |||
@@ -556,7 +551,9 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> { | |||
double xd = (xo - xoLast) / 1000f; | |||
double yd = (yo - yoLast) / 1000f; | |||
tu.writeTd(xd, yd); | |||
tu.writeTj(f.mapChar(ch)); | |||
ch = f.mapChar(ch); | |||
ch = selectAndMapSingleByteFont(tf, f.getFontName(), fsPoints, tu, ch); | |||
tu.writeTj(ch, tf.isMultiByte(), true); | |||
xc += xa + pa[2]; | |||
yc += ya + pa[3]; | |||
xoLast = xo; | |||
@@ -587,15 +584,15 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> { | |||
} | |||
*/ | |||
private char selectAndMapSingleByteFont(SingleByteFont singleByteFont, String fontName, | |||
float fontSize, PDFTextUtil textutil, char ch) { | |||
if (singleByteFont != null && singleByteFont.hasAdditionalEncodings()) { | |||
private char selectAndMapSingleByteFont(Typeface tf, String fontName, float fontSize, PDFTextUtil textutil, | |||
char ch) { | |||
if ((tf instanceof SingleByteFont && ((SingleByteFont)tf).hasAdditionalEncodings()) || tf.isCID()) { | |||
int encoding = ch / 256; | |||
if (encoding == 0) { | |||
textutil.updateTf(fontName, fontSize, singleByteFont.isMultiByte()); | |||
textutil.updateTf(fontName, fontSize, tf.isMultiByte(), tf.isCID()); | |||
} else { | |||
textutil.updateTf(fontName + "_" + Integer.toString(encoding), | |||
fontSize, singleByteFont.isMultiByte()); | |||
fontSize, tf.isMultiByte(), tf.isCID()); | |||
ch = (char)(ch % 256); | |||
} | |||
} |
@@ -248,7 +248,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
PSFontResource fontResource = null; | |||
PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName()); | |||
if (!(fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE | |||
|| fontType == FontType.TYPE0) || !(tf instanceof CustomFont)) { | |||
|| fontType == FontType.TYPE0 || fontType == FontType.TYPE1C) || !(tf instanceof CustomFont)) { | |||
gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes); | |||
fontResource = PSFontResource.createFontResource(fontRes); | |||
return fontResource; | |||
@@ -262,7 +262,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
if (i > 0) { | |||
fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName() + "." + i); | |||
} | |||
if (fontType == FontType.TYPE0) { | |||
if (fontType == FontType.TYPE0 || fontType == FontType.TYPE1C) { | |||
if (((MultiByteFont) tf).isOTFFile()) { | |||
checkPostScriptLevel3(gen, eventProducer, "OpenType CFF"); | |||
embedType2CFF(gen, (MultiByteFont) tf, in); | |||
@@ -525,7 +525,6 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
private static void embedType2CFF(PSGenerator gen, | |||
MultiByteFont font, InputStream fontStream) throws IOException { | |||
FontFileReader reader = new FontFileReader(fontStream); | |||
String header = OFFontLoader.readHeader(reader); | |||
String psName; | |||
CFFDataReader cffReader = new CFFDataReader(reader); | |||
if (cffReader.getFDSelect() != null) { | |||
@@ -560,7 +559,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
} else { | |||
psName = font.getEmbedFontName(); | |||
OTFSubSetFile otfFile = new OTFSubSetFile(); | |||
otfFile.readFont(reader, psName, header, font); | |||
otfFile.readFont(reader, psName, font); | |||
bytes = otfFile.getFontSubset(); | |||
} | |||
@@ -40,7 +40,6 @@ import org.apache.xmlgraphics.image.loader.ImageSessionContext; | |||
import org.apache.xmlgraphics.ps.PSGenerator; | |||
import org.apache.xmlgraphics.ps.PSResource; | |||
import org.apache.fop.fonts.EmbeddingMode; | |||
import org.apache.fop.fonts.Font; | |||
import org.apache.fop.fonts.FontTriplet; | |||
import org.apache.fop.fonts.LazyFont; | |||
@@ -491,7 +490,6 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> { | |||
if (!multiByte || isOTF) { | |||
char codepoint = (char)(ch % 256); | |||
if (isOTF) { | |||
codepoint -= (((MultiByteFont)tf).getEmbeddingMode() == EmbeddingMode.FULL) ? 0 : 1; | |||
accText.append(HexEncoder.encode(codepoint, 2)); | |||
} else { | |||
PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text |
@@ -127,7 +127,7 @@ class PDFTextPainter extends NativeTextPainter { | |||
double xoLast = 0f; | |||
double yoLast = 0f; | |||
textUtil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, initialPos.getX(), initialPos.getY())); | |||
textUtil.updateTf(fk, fsPoints, true); | |||
textUtil.updateTf(fk, fsPoints, true, false); | |||
int[][] dp = gv.getGlyphPositionAdjustments(); | |||
for (int i = 0, n = gv.getNumGlyphs(); i < n; i++) { | |||
int gc = gv.getGlyphCode(i); | |||
@@ -139,7 +139,7 @@ class PDFTextPainter extends NativeTextPainter { | |||
double xd = (xo - xoLast) / 1000f; | |||
double yd = (yo - yoLast) / 1000f; | |||
textUtil.writeTd(xd, yd); | |||
textUtil.writeTj((char) gc); | |||
textUtil.writeTj((char) gc, true, false); | |||
xc += xa + pa[2]; | |||
yc += ya + pa[3]; | |||
xoLast = xo; |
@@ -91,6 +91,11 @@ public abstract class PDFTextUtil extends org.apache.fop.pdf.PDFTextUtil { | |||
return f.isMultiByte(); | |||
} | |||
protected boolean isCIDFont(String name) { | |||
Typeface f = fontInfo.getFonts().get(name); | |||
return f.isCID(); | |||
} | |||
/** | |||
* Writes a "Tf" command, setting a new current font. | |||
* @param f the font to select | |||
@@ -99,10 +104,11 @@ public abstract class PDFTextUtil extends org.apache.fop.pdf.PDFTextUtil { | |||
String fontName = f.getFontName(); | |||
float fontSize = (float)f.getFontSize() / 1000f; | |||
boolean isMultiByte = isMultiByteFont(fontName); | |||
boolean isCid = isCIDFont(fontName); | |||
if (!isMultiByte && encoding != 0) { | |||
updateTf(fontName + "_" + Integer.toString(encoding), fontSize, isMultiByte); | |||
updateTf(fontName + "_" + Integer.toString(encoding), fontSize, isMultiByte, isCid); | |||
} else { | |||
updateTf(fontName, fontSize, isMultiByte); | |||
updateTf(fontName, fontSize, isMultiByte, isCid); | |||
} | |||
} | |||
@@ -64,8 +64,7 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { | |||
} | |||
sourceSansSubset = new OTFSubSetFile(); | |||
String sourceSansHeader = OFFontLoader.readHeader(sourceSansReader); | |||
sourceSansSubset.readFont(sourceSansReader, "SourceSansProBold", sourceSansHeader, glyphs); | |||
sourceSansSubset.readFont(sourceSansReader, "SourceSansProBold", null, glyphs); | |||
byte[] sourceSansData = sourceSansSubset.getFontSubset(); | |||
cffReaderSourceSans = new CFFDataReader(sourceSansData); | |||
} | |||
@@ -442,11 +441,8 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { | |||
private byte[] getSubset(final int opLen) throws IOException { | |||
FontFileReader reader = sourceSansReader; | |||
String header = OFFontLoader.readHeader(reader); | |||
OTFSubSetFile otfSubSetFile = new MyOTFSubSetFile(opLen); | |||
otfSubSetFile.readFont(reader, "StandardOpenType", header, new HashMap<Integer, Integer>()); | |||
otfSubSetFile.readFont(reader, "StandardOpenType", null, new HashMap<Integer, Integer>()); | |||
return otfSubSetFile.getFontSubset(); | |||
} | |||
@@ -19,12 +19,14 @@ | |||
package org.apache.fop.pdf; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.net.URI; | |||
import org.junit.Test; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertTrue; | |||
import org.apache.xmlgraphics.io.ResourceResolver; | |||
@@ -32,8 +34,11 @@ import org.apache.fop.apps.io.InternalResourceResolver; | |||
import org.apache.fop.apps.io.ResourceResolverFactory; | |||
import org.apache.fop.fonts.CIDSet; | |||
import org.apache.fop.fonts.CIDSubset; | |||
import org.apache.fop.fonts.CustomFont; | |||
import org.apache.fop.fonts.EmbeddingMode; | |||
import org.apache.fop.fonts.FontUris; | |||
import org.apache.fop.fonts.MultiByteFont; | |||
import org.apache.fop.fonts.truetype.OFFontLoader; | |||
/** | |||
* Test case for {@link PDFFactory}. | |||
@@ -75,4 +80,37 @@ public class PDFFactoryTestCase { | |||
PDFFont pdfArial = pdfFactory.makeFont("Arial", "Arial", "TTF", font, font); | |||
assertEquals("/EAAAAB+Arial", pdfArial.getBaseFont().toString()); | |||
} | |||
@Test | |||
public void testMakeOTFFont() throws IOException { | |||
InternalResourceResolver rr = | |||
ResourceResolverFactory.createDefaultInternalResourceResolver(new File(".").toURI()); | |||
PDFDocument doc = new PDFDocument(""); | |||
PDFFactory pdfFactory = new PDFFactory(doc); | |||
URI uri = new File("test/resources/fonts/otf/SourceSansProBold.otf").toURI(); | |||
CustomFont sb = OFFontLoader.loadFont(new FontUris(uri, null), | |||
null, true, EmbeddingMode.SUBSET, null, false, false, rr, false, false); | |||
for (char c = 0; c < 512; c++) { | |||
sb.mapChar(c); | |||
} | |||
pdfFactory.makeFont("a", "a", "WinAnsiEncoding", sb, sb); | |||
ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |||
doc.outputTrailer(bos); | |||
assertEquals(pdfFactory.getDocument().getFontMap().size(), 2); | |||
PDFFont pdfFont = pdfFactory.getDocument().getFontMap().get("a_1"); | |||
PDFEncoding enc = (PDFEncoding) pdfFont.get("Encoding"); | |||
PDFArray diff = (PDFArray) enc.get("Differences"); | |||
assertEquals(diff.length(), 80); | |||
assertEquals(diff.get(1).toString(), "/nacute"); | |||
pdfFont = pdfFactory.getDocument().getFontMap().get("a"); | |||
enc = (PDFEncoding) pdfFont.get("Encoding"); | |||
diff = (PDFArray) enc.get("Differences"); | |||
assertEquals(diff.length(), 257); | |||
assertEquals(diff.get(2).toString(), "/space"); | |||
assertTrue(bos.toString().contains("/Subtype /Type1\n")); | |||
assertTrue(bos.toString().contains("/Subtype /Type1C")); | |||
} | |||
} |
@@ -219,9 +219,9 @@ public class PSPainterTestCase { | |||
+ "<00000000000000000000> t\n" | |||
+ "/OTFFont.0 0.01 F\n" | |||
+ "1 0 0 -1 0 0 Tm\n" | |||
+ "<FFFFFFFFFF> t\n" | |||
+ "<0000000000> t\n" | |||
+ "1 0 0 -1 0 0 Tm\n" | |||
+ "<FFFFFFFFFF> t\n" | |||
+ "<0000000000> t\n" | |||
+ "/TTFFont 0.01 F\n" | |||
+ "1 0 0 -1 0 0 Tm\n" | |||
+ "<00000000000000000000> t\n")); |
@@ -640,6 +640,7 @@ list of possible build targets. | |||
<include name="org/apache/fop/util/CharUtilities.class"/> | |||
<include name="org/apache/fop/util/DecimalFormatCache*.class"/> | |||
<include name="org/apache/fop/util/ImageObject.class"/> | |||
<include name="org/apache/fop/util/HexEncoder.class"/> | |||
</patternset> | |||
<!-- PDF transcoder --> | |||
<patternset> |