浏览代码

FOP-2702: OTF fonts not working on Mac Preview

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1792597 13f79535-47bb-0310-9956-ffa450edef68
tags/fop-2_3
Simon Steiner 7 年前
父节点
当前提交
0ede8c8d81

+ 1
- 1
fop-core/src/main/java/org/apache/fop/fonts/CIDFont.java 查看文件



/** {@inheritDoc} */ /** {@inheritDoc} */
public boolean isMultiByte() { public boolean isMultiByte() {
return true;
return getFontType() != FontType.TYPE1C;
} }


} }

+ 92
- 0
fop-core/src/main/java/org/apache/fop/fonts/CustomFont.java 查看文件



package org.apache.fop.fonts; package org.apache.fop.fonts;


import java.awt.Rectangle;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI; import java.net.URI;
protected List<CMapSegment> cmap = new ArrayList<CMapSegment>(); protected List<CMapSegment> cmap = new ArrayList<CMapSegment>();
private boolean useAdvanced = true; private boolean useAdvanced = true;
private boolean simulateStyle; private boolean simulateStyle;
protected List<SimpleSingleByteEncoding> additionalEncodings;
protected Map<Character, SingleByteFont.UnencodedCharacter> unencodedCharacters;


/** /**
* @param resourceResolver the URI resource resolver for controlling file access * @param resourceResolver the URI resource resolver for controlling file access
* @return The character * @return The character
*/ */
public abstract char getUnicodeFromGID(int glyphIndex); 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;
}
} }

+ 3
- 0
fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java 查看文件

if (isEmbeddable()) { if (isEmbeddable()) {
glyphIndex = cidSet.mapChar(glyphIndex, c); glyphIndex = cidSet.mapChar(glyphIndex, c);
} }
if (isCID() && glyphIndex > 256) {
mapUnencodedChar(c);
}
return (char) glyphIndex; return (char) glyphIndex;
} }



+ 1
- 73
fop-core/src/main/java/org/apache/fop/fonts/SingleByteFont.java 查看文件

package org.apache.fop.fonts; package org.apache.fop.fonts;


import java.awt.Rectangle; import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;


private Rectangle[] boundingBoxes; private Rectangle[] boundingBoxes;


private Map<Character, UnencodedCharacter> unencodedCharacters;
private List<SimpleSingleByteEncoding> additionalEncodings;
private Map<Character, Character> alternativeCodes; private Map<Character, Character> alternativeCodes;


private PostScriptVersion ttPostScriptVersion; private PostScriptVersion ttPostScriptVersion;
} }
} }


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} */ /** {@inheritDoc} */
@Override @Override
public boolean hasChar(char c) { public boolean hasChar(char c) {
} }
} }


/**
* 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. * Returns an array with the widths for an additional encoding.
* @param index the index of the additional encoding * @param index the index of the additional encoding
return arr; return arr;
} }


private static final class UnencodedCharacter {
protected static final class UnencodedCharacter {


private final NamedCharacter character; private final NamedCharacter character;
private final int width; private final int width;

+ 4
- 0
fop-core/src/main/java/org/apache/fop/fonts/Typeface.java 查看文件

return false; return false;
} }


public boolean isCID() {
return getFontType() == FontType.TYPE1C;
}

/** {@inheritDoc} */ /** {@inheritDoc} */
public int getMaxAscent(int size) { public int getMaxAscent(int size) {
return getAscender(size); return getAscender(size);

+ 16
- 8
fop-core/src/main/java/org/apache/fop/fonts/truetype/OFFontLoader.java 查看文件

returnFont.setWeight(otf.getWeightClass()); returnFont.setWeight(otf.getWeightClass());
if (isCid) { if (isCid) {
if (otf instanceof OTFFile) { if (otf instanceof OTFFile) {
if (((OTFFile) otf).isType1() && embeddingMode == EmbeddingMode.SUBSET) {
multiFont.setFontType(FontType.TYPE1C);
copyGlyphMetricsSingleByte(otf);
}
multiFont.setCIDType(CIDFontType.CIDTYPE0); multiFont.setCIDType(CIDFontType.CIDTYPE0);
} else { } else {
multiFont.setCIDType(CIDFontType.CIDTYPE2); multiFont.setCIDType(CIDFontType.CIDTYPE2);
private void copyGlyphMetricsSingleByte(OpenFont otf) { private void copyGlyphMetricsSingleByte(OpenFont otf) {
int[] wx = otf.getWidths(); int[] wx = otf.getWidths();
Rectangle[] bboxes = otf.getBoundingBoxes(); 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()) { for (CMapSegment segment : otf.getCMaps()) {
if (segment.getUnicodeStart() < 0xFFFE) { if (segment.getUnicodeStart() < 0xFFFE) {
for (char u = (char)segment.getUnicodeStart(); u <= segment.getUnicodeEnd(); u++) { 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) { if (codePoint <= 0) {
int glyphIndex = segment.getGlyphStartIndex() + u - segment.getUnicodeStart(); int glyphIndex = segment.getGlyphStartIndex() + u - segment.getUnicodeStart();
String glyphName = otf.getGlyphName(glyphIndex); String glyphName = otf.getGlyphName(glyphIndex);
if (glyphName.length() > 0) { if (glyphName.length() > 0) {
String unicode = Character.toString(u); String unicode = Character.toString(u);
NamedCharacter nc = new NamedCharacter(glyphName, unicode); NamedCharacter nc = new NamedCharacter(glyphName, unicode);
singleFont.addUnencodedCharacter(nc, wx[glyphIndex], bboxes[glyphIndex]);
returnFont.addUnencodedCharacter(nc, wx[glyphIndex], bboxes[glyphIndex]);
} }
} }
} }

+ 5
- 0
fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFFile.java 查看文件

import org.apache.fontbox.cff.CFFDataInput; import org.apache.fontbox.cff.CFFDataInput;
import org.apache.fontbox.cff.CFFFont; import org.apache.fontbox.cff.CFFFont;
import org.apache.fontbox.cff.CFFParser; import org.apache.fontbox.cff.CFFParser;
import org.apache.fontbox.cff.CFFType1Font;


public class OTFFile extends OpenFont { public class OTFFile extends OpenFont {


private static long readLong(CFFDataInput input) throws IOException { private static long readLong(CFFDataInput input) throws IOException {
return (input.readCard16() << 16) | input.readCard16(); return (input.readCard16() << 16) | input.readCard16();
} }

public boolean isType1() {
return fileFont instanceof CFFType1Font;
}
} }

+ 7
- 6
fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java 查看文件

super(); 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());
} }


/** /**
* *
* @param in FontFileReader to read from * @param in FontFileReader to read from
* @param embeddedName Name to be checked for in the font file * @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 * @param usedGlyphs Map of glyphs (glyphs has old index as (Integer) key and
* new index as (Integer) value) * new index as (Integer) value)
* @throws IOException in case of an I/O problem * @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 { Map<Integer, Integer> usedGlyphs) throws IOException {
this.mbFont = mbFont;
fontFile = in; fontFile = in;


currentPos = 0; currentPos = 0;
} else { } else {
writeByte(0); writeByte(0);
for (int entry : gidToSID.values()) { for (int entry : gidToSID.values()) {
if (entry == 0) {
continue;
}
writeCard16(entry); writeCard16(entry);
} }
} }

+ 13
- 5
fop-core/src/main/java/org/apache/fop/pdf/PDFCFFStreamType0C.java 查看文件

import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; 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. * PDFStream for embeddable OpenType CFF fonts.
*/ */
public class PDFCFFStreamType0C extends AbstractPDFFontStream { public class PDFCFFStreamType0C extends AbstractPDFFontStream {


private byte[] cffData; private byte[] cffData;
private boolean fullEmbed;
private String type;


/** /**
* Main constructor * Main constructor
* @param fullEmbed Determines whether the font is fully embedded
*/ */
public PDFCFFStreamType0C(boolean fullEmbed) {
public PDFCFFStreamType0C(CustomFont font) {
super(); 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 { protected int getSizeHint() throws IOException {


/** {@inheritDoc} */ /** {@inheritDoc} */
protected void populateStreamDict(Object lengthEntry) { protected void populateStreamDict(Object lengthEntry) {
String type = (fullEmbed) ? "OpenType" : "CIDFontType0C";
put("Subtype", new PDFName(type)); put("Subtype", new PDFName(type));
super.populateStreamDict(lengthEntry); super.populateStreamDict(lengthEntry);
} }

+ 96
- 6
fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java 查看文件

import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.BitSet; import java.util.BitSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SortedSet; import java.util.SortedSet;
assert font instanceof PDFFontType0; assert font instanceof PDFFontType0;
((PDFFontType0)font).setCMAP(cmap); ((PDFFontType0)font).setCMAP(cmap);
((PDFFontType0)font).setDescendantFonts(cidFont); ((PDFFontType0)font).setDescendantFonts(cidFont);
} else if (fonttype == FontType.TYPE1C
&& (metrics instanceof LazyFont || metrics instanceof MultiByteFont)) {
handleType1CFont(pdfdesc, font, metrics, fontname, basefont, descriptor);
} else { } else {
assert font instanceof PDFFontNonBase14; assert font instanceof PDFFontNonBase14;
PDFFontNonBase14 nonBase14 = (PDFFontNonBase14)font; PDFFontNonBase14 nonBase14 = (PDFFontNonBase14)font;
} }
} }


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) { private void generateToUnicodeCmap(PDFFont font, SingleByteEncoding encoding) {
PDFCMap cmap = new PDFToUnicodeCMap(encoding.getUnicodeCharMap(), PDFCMap cmap = new PDFToUnicodeCMap(encoding.getUnicodeCharMap(),
"fop-ucs-H", "fop-ucs-H",
((PDFT1Stream) embeddedFont).setData(pfb); ((PDFT1Stream) embeddedFont).setData(pfb);
} }
} else if (desc.getFontType() == FontType.TYPE1C) { } 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) { } else if (desc.getFontType() == FontType.CIDTYPE0) {
byte[] file = IOUtils.toByteArray(in); byte[] file = IOUtils.toByteArray(in);
PDFCFFStream embeddedFont2 = new PDFCFFStream("CIDFontType0C"); PDFCFFStream embeddedFont2 = new PDFCFFStream("CIDFontType0C");
String fontPrefix, FontDescriptor desc, boolean isCFF) throws IOException { String fontPrefix, FontDescriptor desc, boolean isCFF) throws IOException {
if (isCFF) { if (isCFF) {
OTFSubSetFile otfFile = new OTFSubSetFile(); OTFSubSetFile otfFile = new OTFSubSetFile();
otfFile.readFont(reader, fontPrefix + desc.getEmbedFontName(), header, mbfont);
otfFile.readFont(reader, fontPrefix + desc.getEmbedFontName(), mbfont);
return otfFile.getFontSubset(); return otfFile.getFontSubset();
} else { } else {
TTFSubSetFile otfFile = new TTFSubSetFile(); TTFSubSetFile otfFile = new TTFSubSetFile();
throws IOException { throws IOException {
AbstractPDFStream embeddedFont; AbstractPDFStream embeddedFont;
if (isCFF) { if (isCFF) {
embeddedFont = new PDFCFFStreamType0C(font.getEmbeddingMode() == EmbeddingMode.FULL);
embeddedFont = new PDFCFFStreamType0C(font);
((PDFCFFStreamType0C) embeddedFont).setData(fontBytes, fontBytes.length); ((PDFCFFStreamType0C) embeddedFont).setData(fontBytes, fontBytes.length);
} else { } else {
embeddedFont = new PDFTTFStream(fontBytes.length); embeddedFont = new PDFTTFStream(fontBytes.length);

+ 10
- 8
fop-core/src/main/java/org/apache/fop/pdf/PDFTextUtil.java 查看文件

private String startText; private String startText;
private String endText; private String endText;
private boolean useMultiByte; private boolean useMultiByte;
private boolean useCid;
private StringBuffer bufTJ; private StringBuffer bufTJ;
private int textRenderingMode = TR_FILL; private int textRenderingMode = TR_FILL;


PDFNumber.doubleOut(lt[5], DEC, sb); 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 (!multibyte) {
if (ch < 32 || ch > 127) {
if (cid || ch < 32 || ch > 127) {
sb.append("\\").append(Integer.toOctalString(ch)); sb.append("\\").append(Integer.toOctalString(ch));
} else { } else {
switch (ch) { switch (ch) {
} }


private void writeChar(char ch, StringBuffer sb) { private void writeChar(char ch, StringBuffer sb) {
writeChar(ch, sb, useMultiByte);
writeChar(ch, sb, useMultiByte, useCid);
} }


private void checkInTextObject() { private void checkInTextObject() {
* @param fontSize the font size (in points) * @param fontSize the font size (in points)
* @param multiByte true indicates the font is a multi-byte font, false means single-byte * @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(); checkInTextObject();
if (!fontName.equals(this.currentFontName) || (fontSize != this.currentFontSize)) { if (!fontName.equals(this.currentFontName) || (fontSize != this.currentFontSize)) {
writeTJ(); writeTJ();
this.currentFontName = fontName; this.currentFontName = fontName;
this.currentFontSize = fontSize; this.currentFontSize = fontSize;
this.useMultiByte = multiByte; this.useMultiByte = multiByte;
this.useCid = cid;
writeTf(fontName, fontSize); writeTf(fontName, fontSize);
} }
} }
* Writes a "Tj" command with specified character code. * Writes a "Tj" command with specified character code.
* @param ch character code to write * @param ch character code to write
*/ */
public void writeTj(char ch) {
public void writeTj(char ch, boolean multibyte, boolean cid) {
StringBuffer sb = new StringBuffer(); 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"); sb.append(" Tj\n");
write(sb); write(sb);
} }

+ 13
- 16
fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java 查看文件



// This assumes that *all* CIDFonts use a /ToUnicode mapping // This assumes that *all* CIDFonts use a /ToUnicode mapping
Typeface tf = getTypeface(fontKey); Typeface tf = getTypeface(fontKey);
SingleByteFont singleByteFont = null;
if (tf instanceof SingleByteFont) {
singleByteFont = (SingleByteFont)tf;
}
Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints); Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints);
String fontName = font.getFontName(); String fontName = font.getFontName();


PDFTextUtil textutil = generator.getTextUtil(); PDFTextUtil textutil = generator.getTextUtil();
textutil.updateTf(fontKey, fontSize, tf.isMultiByte());
textutil.updateTf(fontKey, fontSize, tf.isMultiByte(), tf.isCID());


double shear = 0; double shear = 0;
boolean simulateStyle = tf instanceof CustomFont && ((CustomFont) tf).getSimulateStyle(); boolean simulateStyle = tf instanceof CustomFont && ((CustomFont) tf).getSimulateStyle();
float glyphAdjust = 0; float glyphAdjust = 0;
if (font.hasChar(orgChar)) { if (font.hasChar(orgChar)) {
ch = font.mapChar(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)) { if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) {
glyphAdjust += wordSpacing; glyphAdjust += wordSpacing;
} }
glyphAdjust += wordSpacing; glyphAdjust += wordSpacing;
} }
} }
ch = selectAndMapSingleByteFont(singleByteFont, fontName, fontSize,
textutil, ch);
ch = selectAndMapSingleByteFont(tf, fontName, fontSize, textutil, ch);
} }
textutil.writeTJMappedChar(ch); textutil.writeTJMappedChar(ch);


assert dp != null; assert dp != null;
String fk = getFontInfo().getInternalFontKey(triplet); String fk = getFontInfo().getInternalFontKey(triplet);
Typeface tf = getTypeface(fk); Typeface tf = getTypeface(fk);
if (tf.isMultiByte()) {
if (tf.isMultiByte() || tf.isCID()) {
int fs = state.getFontSize(); int fs = state.getFontSize();
float fsPoints = fs / 1000f; float fsPoints = fs / 1000f;
Font f = getFontInfo().getFontInstance(triplet, fs); Font f = getFontInfo().getFontInstance(triplet, fs);
double yoLast = 0f; double yoLast = 0f;
double wox = wordSpacing; double wox = wordSpacing;
tu.writeTextMatrix(new AffineTransform(1, 0, 0, -1, x / 1000f, y / 1000f)); 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); generator.updateCharacterSpacing(letterSpacing / 1000f);
for (int i = 0, n = text.length(); i < n; i++) { for (int i = 0, n = text.length(); i < n; i++) {
char ch = text.charAt(i); char ch = text.charAt(i);
double xd = (xo - xoLast) / 1000f; double xd = (xo - xoLast) / 1000f;
double yd = (yo - yoLast) / 1000f; double yd = (yo - yoLast) / 1000f;
tu.writeTd(xd, yd); 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]; xc += xa + pa[2];
yc += ya + pa[3]; yc += ya + pa[3];
xoLast = xo; xoLast = xo;
} }
*/ */


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; int encoding = ch / 256;
if (encoding == 0) { if (encoding == 0) {
textutil.updateTf(fontName, fontSize, singleByteFont.isMultiByte());
textutil.updateTf(fontName, fontSize, tf.isMultiByte(), tf.isCID());
} else { } else {
textutil.updateTf(fontName + "_" + Integer.toString(encoding), textutil.updateTf(fontName + "_" + Integer.toString(encoding),
fontSize, singleByteFont.isMultiByte());
fontSize, tf.isMultiByte(), tf.isCID());
ch = (char)(ch % 256); ch = (char)(ch % 256);
} }
} }

+ 3
- 4
fop-core/src/main/java/org/apache/fop/render/ps/PSFontUtils.java 查看文件

PSFontResource fontResource = null; PSFontResource fontResource = null;
PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName()); PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName());
if (!(fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE 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); gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
fontResource = PSFontResource.createFontResource(fontRes); fontResource = PSFontResource.createFontResource(fontRes);
return fontResource; return fontResource;
if (i > 0) { if (i > 0) {
fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName() + "." + i); fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName() + "." + i);
} }
if (fontType == FontType.TYPE0) {
if (fontType == FontType.TYPE0 || fontType == FontType.TYPE1C) {
if (((MultiByteFont) tf).isOTFFile()) { if (((MultiByteFont) tf).isOTFFile()) {
checkPostScriptLevel3(gen, eventProducer, "OpenType CFF"); checkPostScriptLevel3(gen, eventProducer, "OpenType CFF");
embedType2CFF(gen, (MultiByteFont) tf, in); embedType2CFF(gen, (MultiByteFont) tf, in);
private static void embedType2CFF(PSGenerator gen, private static void embedType2CFF(PSGenerator gen,
MultiByteFont font, InputStream fontStream) throws IOException { MultiByteFont font, InputStream fontStream) throws IOException {
FontFileReader reader = new FontFileReader(fontStream); FontFileReader reader = new FontFileReader(fontStream);
String header = OFFontLoader.readHeader(reader);
String psName; String psName;
CFFDataReader cffReader = new CFFDataReader(reader); CFFDataReader cffReader = new CFFDataReader(reader);
if (cffReader.getFDSelect() != null) { if (cffReader.getFDSelect() != null) {
} else { } else {
psName = font.getEmbedFontName(); psName = font.getEmbedFontName();
OTFSubSetFile otfFile = new OTFSubSetFile(); OTFSubSetFile otfFile = new OTFSubSetFile();
otfFile.readFont(reader, psName, header, font);
otfFile.readFont(reader, psName, font);
bytes = otfFile.getFontSubset(); bytes = otfFile.getFontSubset();
} }



+ 0
- 2
fop-core/src/main/java/org/apache/fop/render/ps/PSPainter.java 查看文件

import org.apache.xmlgraphics.ps.PSGenerator; import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.xmlgraphics.ps.PSResource; import org.apache.xmlgraphics.ps.PSResource;


import org.apache.fop.fonts.EmbeddingMode;
import org.apache.fop.fonts.Font; import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontTriplet; import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.fonts.LazyFont; import org.apache.fop.fonts.LazyFont;
if (!multiByte || isOTF) { if (!multiByte || isOTF) {
char codepoint = (char)(ch % 256); char codepoint = (char)(ch % 256);
if (isOTF) { if (isOTF) {
codepoint -= (((MultiByteFont)tf).getEmbeddingMode() == EmbeddingMode.FULL) ? 0 : 1;
accText.append(HexEncoder.encode(codepoint, 2)); accText.append(HexEncoder.encode(codepoint, 2));
} else { } else {
PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text

+ 2
- 2
fop-core/src/main/java/org/apache/fop/svg/PDFTextPainter.java 查看文件

double xoLast = 0f; double xoLast = 0f;
double yoLast = 0f; double yoLast = 0f;
textUtil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, initialPos.getX(), initialPos.getY())); 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(); int[][] dp = gv.getGlyphPositionAdjustments();
for (int i = 0, n = gv.getNumGlyphs(); i < n; i++) { for (int i = 0, n = gv.getNumGlyphs(); i < n; i++) {
int gc = gv.getGlyphCode(i); int gc = gv.getGlyphCode(i);
double xd = (xo - xoLast) / 1000f; double xd = (xo - xoLast) / 1000f;
double yd = (yo - yoLast) / 1000f; double yd = (yo - yoLast) / 1000f;
textUtil.writeTd(xd, yd); textUtil.writeTd(xd, yd);
textUtil.writeTj((char) gc);
textUtil.writeTj((char) gc, true, false);
xc += xa + pa[2]; xc += xa + pa[2];
yc += ya + pa[3]; yc += ya + pa[3];
xoLast = xo; xoLast = xo;

+ 8
- 2
fop-core/src/main/java/org/apache/fop/svg/PDFTextUtil.java 查看文件

return f.isMultiByte(); 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. * Writes a "Tf" command, setting a new current font.
* @param f the font to select * @param f the font to select
String fontName = f.getFontName(); String fontName = f.getFontName();
float fontSize = (float)f.getFontSize() / 1000f; float fontSize = (float)f.getFontSize() / 1000f;
boolean isMultiByte = isMultiByteFont(fontName); boolean isMultiByte = isMultiByteFont(fontName);
boolean isCid = isCIDFont(fontName);
if (!isMultiByte && encoding != 0) { if (!isMultiByte && encoding != 0) {
updateTf(fontName + "_" + Integer.toString(encoding), fontSize, isMultiByte);
updateTf(fontName + "_" + Integer.toString(encoding), fontSize, isMultiByte, isCid);
} else { } else {
updateTf(fontName, fontSize, isMultiByte);
updateTf(fontName, fontSize, isMultiByte, isCid);
} }
} }



+ 2
- 6
fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java 查看文件

} }


sourceSansSubset = new OTFSubSetFile(); sourceSansSubset = new OTFSubSetFile();
String sourceSansHeader = OFFontLoader.readHeader(sourceSansReader);
sourceSansSubset.readFont(sourceSansReader, "SourceSansProBold", sourceSansHeader, glyphs);
sourceSansSubset.readFont(sourceSansReader, "SourceSansProBold", null, glyphs);
byte[] sourceSansData = sourceSansSubset.getFontSubset(); byte[] sourceSansData = sourceSansSubset.getFontSubset();
cffReaderSourceSans = new CFFDataReader(sourceSansData); cffReaderSourceSans = new CFFDataReader(sourceSansData);
} }


private byte[] getSubset(final int opLen) throws IOException { private byte[] getSubset(final int opLen) throws IOException {
FontFileReader reader = sourceSansReader; FontFileReader reader = sourceSansReader;
String header = OFFontLoader.readHeader(reader);

OTFSubSetFile otfSubSetFile = new MyOTFSubSetFile(opLen); 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(); return otfSubSetFile.getFontSubset();
} }



+ 39
- 1
fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java 查看文件



package org.apache.fop.pdf; package org.apache.fop.pdf;


import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.net.URI; import java.net.URI;


import org.junit.Test; import org.junit.Test;

import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;


import org.apache.xmlgraphics.io.ResourceResolver; import org.apache.xmlgraphics.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.CIDSet;
import org.apache.fop.fonts.CIDSubset; import org.apache.fop.fonts.CIDSubset;
import org.apache.fop.fonts.CustomFont;
import org.apache.fop.fonts.EmbeddingMode; import org.apache.fop.fonts.EmbeddingMode;
import org.apache.fop.fonts.FontUris;
import org.apache.fop.fonts.MultiByteFont; import org.apache.fop.fonts.MultiByteFont;
import org.apache.fop.fonts.truetype.OFFontLoader;


/** /**
* Test case for {@link PDFFactory}. * Test case for {@link PDFFactory}.
PDFFont pdfArial = pdfFactory.makeFont("Arial", "Arial", "TTF", font, font); PDFFont pdfArial = pdfFactory.makeFont("Arial", "Arial", "TTF", font, font);
assertEquals("/EAAAAB+Arial", pdfArial.getBaseFont().toString()); 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"));
}
} }

+ 2
- 2
fop-core/src/test/java/org/apache/fop/render/ps/PSPainterTestCase.java 查看文件

+ "<00000000000000000000> t\n" + "<00000000000000000000> t\n"
+ "/OTFFont.0 0.01 F\n" + "/OTFFont.0 0.01 F\n"
+ "1 0 0 -1 0 0 Tm\n" + "1 0 0 -1 0 0 Tm\n"
+ "<FFFFFFFFFF> t\n"
+ "<0000000000> t\n"
+ "1 0 0 -1 0 0 Tm\n" + "1 0 0 -1 0 0 Tm\n"
+ "<FFFFFFFFFF> t\n"
+ "<0000000000> t\n"
+ "/TTFFont 0.01 F\n" + "/TTFFont 0.01 F\n"
+ "1 0 0 -1 0 0 Tm\n" + "1 0 0 -1 0 0 Tm\n"
+ "<00000000000000000000> t\n")); + "<00000000000000000000> t\n"));

+ 1
- 0
fop/build.xml 查看文件

<include name="org/apache/fop/util/CharUtilities.class"/> <include name="org/apache/fop/util/CharUtilities.class"/>
<include name="org/apache/fop/util/DecimalFormatCache*.class"/> <include name="org/apache/fop/util/DecimalFormatCache*.class"/>
<include name="org/apache/fop/util/ImageObject.class"/> <include name="org/apache/fop/util/ImageObject.class"/>
<include name="org/apache/fop/util/HexEncoder.class"/>
</patternset> </patternset>
<!-- PDF transcoder --> <!-- PDF transcoder -->
<patternset> <patternset>

正在加载...
取消
保存