diff options
author | Simon Steiner <ssteiner@apache.org> | 2017-04-25 10:18:07 +0000 |
---|---|---|
committer | Simon Steiner <ssteiner@apache.org> | 2017-04-25 10:18:07 +0000 |
commit | 0ede8c8d8198c6a072dbb589d9f1d665b3a9e274 (patch) | |
tree | 7fbb9686dbc3a3160670bf56bbd5a481265352a1 /fop-core/src | |
parent | 6803be47127d99fc7ab425ecc0ce4ac9a8ddf830 (diff) | |
download | xmlgraphics-fop-0ede8c8d8198c6a072dbb589d9f1d665b3a9e274.tar.gz xmlgraphics-fop-0ede8c8d8198c6a072dbb589d9f1d665b3a9e274.zip |
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
Diffstat (limited to 'fop-core/src')
19 files changed, 317 insertions, 142 deletions
diff --git a/fop-core/src/main/java/org/apache/fop/fonts/CIDFont.java b/fop-core/src/main/java/org/apache/fop/fonts/CIDFont.java index dc398263e..5aa7237e9 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/CIDFont.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/CIDFont.java @@ -82,7 +82,7 @@ public abstract class CIDFont extends CustomFont { /** {@inheritDoc} */ public boolean isMultiByte() { - return true; + return getFontType() != FontType.TYPE1C; } } diff --git a/fop-core/src/main/java/org/apache/fop/fonts/CustomFont.java b/fop-core/src/main/java/org/apache/fop/fonts/CustomFont.java index ed9f1f039..4422ef8c3 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/CustomFont.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/CustomFont.java @@ -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; + } } diff --git a/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java b/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java index 9e542f71e..c5af86822 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java @@ -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; } diff --git a/fop-core/src/main/java/org/apache/fop/fonts/SingleByteFont.java b/fop-core/src/main/java/org/apache/fop/fonts/SingleByteFont.java index eb273d172..262ea0a75 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/SingleByteFont.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/SingleByteFont.java @@ -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) { @@ -407,41 +370,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 * @return the width array @@ -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; diff --git a/fop-core/src/main/java/org/apache/fop/fonts/Typeface.java b/fop-core/src/main/java/org/apache/fop/fonts/Typeface.java index faf79abc7..c36f0ca91 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/Typeface.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/Typeface.java @@ -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); diff --git a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OFFontLoader.java b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OFFontLoader.java index 3a4b1d771..a0060ccb6 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OFFontLoader.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OFFontLoader.java @@ -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]); } } } diff --git a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFFile.java b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFFile.java index a96cb3d0e..2f52fa791 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFFile.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFFile.java @@ -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; + } } diff --git a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java index 84ad09858..942bcae79 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java @@ -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); } } diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFCFFStreamType0C.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFCFFStreamType0C.java index 53f0b36b4..1756cf107 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFCFFStreamType0C.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFCFFStreamType0C.java @@ -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); } diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java index 4e9a58cdf..856e7d765 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java @@ -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); diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFTextUtil.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFTextUtil.java index bd7c8c4ae..2a1dc5039 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFTextUtil.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFTextUtil.java @@ -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); } diff --git a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java index c08dd86c8..942027673 100644 --- a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java @@ -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); } } diff --git a/fop-core/src/main/java/org/apache/fop/render/ps/PSFontUtils.java b/fop-core/src/main/java/org/apache/fop/render/ps/PSFontUtils.java index 1a8291f05..f2f6bdaf3 100644 --- a/fop-core/src/main/java/org/apache/fop/render/ps/PSFontUtils.java +++ b/fop-core/src/main/java/org/apache/fop/render/ps/PSFontUtils.java @@ -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(); } diff --git a/fop-core/src/main/java/org/apache/fop/render/ps/PSPainter.java b/fop-core/src/main/java/org/apache/fop/render/ps/PSPainter.java index f0f10a1e0..2400ff7d3 100644 --- a/fop-core/src/main/java/org/apache/fop/render/ps/PSPainter.java +++ b/fop-core/src/main/java/org/apache/fop/render/ps/PSPainter.java @@ -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 diff --git a/fop-core/src/main/java/org/apache/fop/svg/PDFTextPainter.java b/fop-core/src/main/java/org/apache/fop/svg/PDFTextPainter.java index 0320438f8..5c5308ace 100644 --- a/fop-core/src/main/java/org/apache/fop/svg/PDFTextPainter.java +++ b/fop-core/src/main/java/org/apache/fop/svg/PDFTextPainter.java @@ -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; diff --git a/fop-core/src/main/java/org/apache/fop/svg/PDFTextUtil.java b/fop-core/src/main/java/org/apache/fop/svg/PDFTextUtil.java index 23fadc542..b3a346509 100644 --- a/fop-core/src/main/java/org/apache/fop/svg/PDFTextUtil.java +++ b/fop-core/src/main/java/org/apache/fop/svg/PDFTextUtil.java @@ -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); } } diff --git a/fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java b/fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java index 73fd6028a..608d8c3dc 100644 --- a/fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java @@ -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(); } diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java index edbc7e449..220464e6e 100644 --- a/fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java @@ -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")); + } } diff --git a/fop-core/src/test/java/org/apache/fop/render/ps/PSPainterTestCase.java b/fop-core/src/test/java/org/apache/fop/render/ps/PSPainterTestCase.java index 8ad8d753b..c2bc2017f 100644 --- a/fop-core/src/test/java/org/apache/fop/render/ps/PSPainterTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/render/ps/PSPainterTestCase.java @@ -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")); |