From fd46ff01e9ccb1627f6e5c0f41d57a8559d94085 Mon Sep 17 00:00:00 2001 From: Simon Steiner Date: Tue, 17 May 2016 13:32:40 +0000 Subject: [PATCH] FOP-2608: Convert OTF to Type 1 in postscript git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1744262 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/fonts/CFFToType1Font.java | 66 ++++++ .../apache/fop/fonts/DefaultFontConfig.java | 32 ++- .../fop/fonts/DefaultFontConfigurator.java | 2 +- .../org/apache/fop/fonts/EmbedFontInfo.java | 10 +- .../java/org/apache/fop/fonts/FontLoader.java | 5 +- .../java/org/apache/fop/fonts/LazyFont.java | 4 +- .../fop/fonts/autodetect/FontInfoFinder.java | 7 +- .../fop/fonts/truetype/OFFontLoader.java | 26 ++- .../apache/fop/fonts/truetype/OTFFile.java | 1 + .../apache/fop/fonts/truetype/OpenFont.java | 5 + .../fop/fonts/type1/Type1SubsetFile.java | 4 +- .../java2d/ConfiguredFontCollection.java | 3 +- .../org/apache/fop/render/ps/PSFontUtils.java | 8 +- .../org/apache/fop/render/ps/PSPainter.java | 3 +- .../render/ps/Type1CharStringFormatter.java | 86 +++++++ .../fop/render/ps/Type1FontFormatter.java | 213 ++++++++++++++++++ .../fop/fonts/DejaVuLGCSerifTestCase.java | 2 +- .../fop/fonts/EmbedFontInfoTestCase.java | 5 +- .../fonts/truetype/OTFToType1TestCase.java | 58 +++++ .../fonts/truetype/TTFFontLoaderTestCase.java | 4 +- .../apache/fop/svg/font/FontInfoBuilder.java | 2 +- fop/src/foschema/fop-configuration.xsd | 1 + 22 files changed, 507 insertions(+), 40 deletions(-) create mode 100644 fop-core/src/main/java/org/apache/fop/fonts/CFFToType1Font.java create mode 100644 fop-core/src/main/java/org/apache/fop/render/ps/Type1CharStringFormatter.java create mode 100644 fop-core/src/main/java/org/apache/fop/render/ps/Type1FontFormatter.java create mode 100644 fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFToType1TestCase.java diff --git a/fop-core/src/main/java/org/apache/fop/fonts/CFFToType1Font.java b/fop-core/src/main/java/org/apache/fop/fonts/CFFToType1Font.java new file mode 100644 index 000000000..c73d8bbc0 --- /dev/null +++ b/fop-core/src/main/java/org/apache/fop/fonts/CFFToType1Font.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ +package org.apache.fop.fonts; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.io.IOUtils; +import org.apache.fontbox.cff.CFFFont; +import org.apache.fontbox.cff.CFFParser; +import org.apache.fontbox.cff.CFFType1Font; + +import org.apache.fop.apps.io.InternalResourceResolver; +import org.apache.fop.fonts.type1.PFBData; +import org.apache.fop.fonts.type1.PFBParser; +import org.apache.fop.fonts.type1.Type1SubsetFile; +import org.apache.fop.render.ps.Type1FontFormatter; + +public class CFFToType1Font extends MultiByteFont { + + public CFFToType1Font(InternalResourceResolver resourceResolver, EmbeddingMode embeddingMode) { + super(resourceResolver, embeddingMode); + setEmbeddingMode(EmbeddingMode.FULL); + setFontType(FontType.TYPE1); + } + + public InputStream getInputStream() throws IOException { + InputStream cff = super.getInputStream(); + return convertOTFToType1(cff); + } + + private InputStream convertOTFToType1(InputStream in) throws IOException { + CFFFont f = new CFFParser().parse(IOUtils.toByteArray(in)).get(0); + if (!(f instanceof CFFType1Font)) { + throw new IOException(getEmbedFileURI() + ": only OTF CFF Type1 font can be converted to Type1"); + } + byte[] t1 = new Type1FontFormatter(cidSet.getGlyphs()).format((CFFType1Font) f); + PFBData pfb = new PFBParser().parsePFB(new ByteArrayInputStream(t1)); + ByteArrayOutputStream s1 = new ByteArrayOutputStream(); + s1.write(pfb.getHeaderSegment()); + ByteArrayOutputStream s2 = new ByteArrayOutputStream(); + s2.write(pfb.getEncryptedSegment()); + ByteArrayOutputStream s3 = new ByteArrayOutputStream(); + s3.write(pfb.getTrailerSegment()); + byte[] out = new Type1SubsetFile().stitchFont(s1, s2, s3); + return new ByteArrayInputStream(out); + } +} diff --git a/fop-core/src/main/java/org/apache/fop/fonts/DefaultFontConfig.java b/fop-core/src/main/java/org/apache/fop/fonts/DefaultFontConfig.java index 72d2280a8..fcae13186 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/DefaultFontConfig.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/DefaultFontConfig.java @@ -79,6 +79,7 @@ public final class DefaultFontConfig implements FontConfig { private boolean strict; + private Configuration config; private Configuration fontInfoCfg; private DefaultFontConfig instance; @@ -88,6 +89,7 @@ public final class DefaultFontConfig implements FontConfig { instance = null; } else { this.strict = strict; + this.config = cfg; this.fontInfoCfg = cfg.getChild("fonts", false); instance = new DefaultFontConfig(fontInfoCfg.getChild("auto-detect", false) != null); parse(); @@ -108,13 +110,15 @@ public final class DefaultFontConfig implements FontConfig { strict); continue; } - Font font = new Font(fontCfg.getAttribute("metrics-url", null), embed, fontCfg.getAttribute( - "embed-url-afm", null), fontCfg.getAttribute("embed-url-pfm", null), + Font font = new Font(fontCfg.getAttribute("metrics-url", null), embed, + fontCfg.getAttribute("embed-url-afm", null), + fontCfg.getAttribute("embed-url-pfm", null), fontCfg.getAttribute("sub-font", null), - fontCfg.getAttributeAsBoolean("kerning", true), fontCfg.getAttributeAsBoolean( - "advanced", true), fontCfg.getAttribute("encoding-mode", - EncodingMode.AUTO.getName()), fontCfg.getAttribute("embedding-mode", - EncodingMode.AUTO.getName())); + fontCfg.getAttributeAsBoolean("kerning", true), + fontCfg.getAttributeAsBoolean("advanced", true), + fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName()), + fontCfg.getAttribute("embedding-mode", EncodingMode.AUTO.getName()), + fontCfg.getAttributeAsBoolean("embed-as-type1", false)); instance.fonts.add(font); boolean hasTriplets = false; for (Configuration tripletCfg : fontCfg.getChildren("font-triplet")) { @@ -126,6 +130,13 @@ public final class DefaultFontConfig implements FontConfig { if (!hasTriplets) { LogUtil.handleError(log, "font without font-triplet", strict); } + try { + if (font.getEmbedAsType1() && !config.getAttribute("mime").equals("application/postscript")) { + throw new FOPException("The embed-as-type1 attribute is only supported in postscript"); + } + } catch (ConfigurationException ex) { + LogUtil.handleException(log, ex, true); + } } } @@ -289,6 +300,8 @@ public final class DefaultFontConfig implements FontConfig { return encodingMode; } + private final boolean embedAsType1; + private final List tripletList = new ArrayList(); public List getTripletList() { @@ -296,7 +309,7 @@ public final class DefaultFontConfig implements FontConfig { } private Font(String metrics, String embed, String afm, String pfm, String subFont, boolean kerning, - boolean advanced, String encodingMode, String embeddingMode) { + boolean advanced, String encodingMode, String embeddingMode, boolean embedAsType1) { this.metrics = metrics; this.embedUri = embed; this.afm = afm; @@ -306,6 +319,7 @@ public final class DefaultFontConfig implements FontConfig { this.advanced = advanced; this.encodingMode = encodingMode; this.embeddingMode = embeddingMode; + this.embedAsType1 = embedAsType1; } /** @@ -355,5 +369,9 @@ public final class DefaultFontConfig implements FontConfig { public String getPfm() { return pfm; } + + public boolean getEmbedAsType1() { + return embedAsType1; + } } } diff --git a/fop-core/src/main/java/org/apache/fop/fonts/DefaultFontConfigurator.java b/fop-core/src/main/java/org/apache/fop/fonts/DefaultFontConfigurator.java index 4a8b11f74..b5fb68537 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/DefaultFontConfigurator.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/DefaultFontConfigurator.java @@ -167,7 +167,7 @@ public class DefaultFontConfigurator implements FontConfigurator EncodingMode encodingMode = EncodingMode.getValue(font.getEncodingMode()); EmbeddingMode embeddingMode = EmbeddingMode.getValue(font.getEmbeddingMode()); EmbedFontInfo embedFontInfo = new EmbedFontInfo(fontUris, font.isKerning(), font.isAdvanced(), - tripletList, subFont, encodingMode, embeddingMode); + tripletList, subFont, encodingMode, embeddingMode, font.getEmbedAsType1()); if (fontCache != null) { if (!fontCache.containsFont(embedFontInfo)) { fontCache.addFont(embedFontInfo, resourceResolver); diff --git a/fop-core/src/main/java/org/apache/fop/fonts/EmbedFontInfo.java b/fop-core/src/main/java/org/apache/fop/fonts/EmbedFontInfo.java index cc96469f1..4ab371a14 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/EmbedFontInfo.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/EmbedFontInfo.java @@ -47,6 +47,7 @@ public class EmbedFontInfo implements Serializable { protected String postScriptName; /** the sub-fontname of the font (used for TrueType Collections, null otherwise) */ protected String subFontName; + private boolean embedAsType1; /** the list of associated font triplets */ private List fontTriplets; @@ -67,7 +68,7 @@ public class EmbedFontInfo implements Serializable { */ public EmbedFontInfo(FontUris fontUris, boolean kerning, boolean advanced, List fontTriplets, String subFontName, - EncodingMode encodingMode, EmbeddingMode embeddingMode) { + EncodingMode encodingMode, EmbeddingMode embeddingMode, boolean embedAsType1) { this.kerning = kerning; this.advanced = advanced; this.fontTriplets = fontTriplets; @@ -75,6 +76,7 @@ public class EmbedFontInfo implements Serializable { this.encodingMode = encodingMode; this.embeddingMode = embeddingMode; this.fontUris = fontUris; + this.embedAsType1 = embedAsType1; } /** @@ -88,7 +90,7 @@ public class EmbedFontInfo implements Serializable { public EmbedFontInfo(FontUris fontUris, boolean kerning, boolean advanced, List fontTriplets, String subFontName) { this(fontUris, kerning, advanced, fontTriplets, subFontName, EncodingMode.AUTO, - EmbeddingMode.AUTO); + EmbeddingMode.AUTO, false); } /** @@ -194,6 +196,10 @@ public class EmbedFontInfo implements Serializable { return this.encodingMode; } + public boolean getEmbedAsType1() { + return embedAsType1; + } + private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); diff --git a/fop-core/src/main/java/org/apache/fop/fonts/FontLoader.java b/fop-core/src/main/java/org/apache/fop/fonts/FontLoader.java index 92656ca2d..911dc34fb 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/FontLoader.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/FontLoader.java @@ -91,7 +91,8 @@ public abstract class FontLoader { */ public static CustomFont loadFont(FontUris fontUris, String subFontName, boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode, - boolean useKerning, boolean useAdvanced, InternalResourceResolver resourceResolver) throws IOException { + boolean useKerning, boolean useAdvanced, InternalResourceResolver resourceResolver, + boolean embedAsType1) throws IOException { boolean type1 = isType1(fontUris.getEmbed()); FontLoader loader; if (type1) { @@ -103,7 +104,7 @@ public abstract class FontLoader { resourceResolver); } else { loader = new OFFontLoader(fontUris.getEmbed(), subFontName, embedded, embeddingMode, - encodingMode, useKerning, useAdvanced, resourceResolver); + encodingMode, useKerning, useAdvanced, resourceResolver, embedAsType1); } return loader.getFont(); } diff --git a/fop-core/src/main/java/org/apache/fop/fonts/LazyFont.java b/fop-core/src/main/java/org/apache/fop/fonts/LazyFont.java index 06b106534..6e69ca761 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/LazyFont.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/LazyFont.java @@ -47,6 +47,7 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable, private final boolean useKerning; private final boolean useAdvanced; + private boolean embedAsType1; private final EncodingMode encodingMode; private final EmbeddingMode embeddingMode; private final String subFontName; @@ -72,6 +73,7 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable, } else { this.useAdvanced = fontInfo.getAdvanced(); } + this.embedAsType1 = fontInfo.getEmbedAsType1(); this.encodingMode = fontInfo.getEncodingMode() != null ? fontInfo.getEncodingMode() : EncodingMode.AUTO; this.embeddingMode = fontInfo.getEmbeddingMode() != null ? fontInfo.getEmbeddingMode() @@ -114,7 +116,7 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable, throw new RuntimeException("Cannot load font. No font URIs available."); } realFont = FontLoader.loadFont(fontUris, subFontName, embedded, - embeddingMode, encodingMode, useKerning, useAdvanced, resourceResolver); + embeddingMode, encodingMode, useKerning, useAdvanced, resourceResolver, embedAsType1); } if (realFont instanceof FontDescriptor) { realFontDescriptor = (FontDescriptor) realFont; diff --git a/fop-core/src/main/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java b/fop-core/src/main/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java index a1d65459a..564215911 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java @@ -150,8 +150,7 @@ public class FontInfoFinder { subFontName = ((MultiByteFont) customFont).getTTCName(); } EmbedFontInfo fontInfo = new EmbedFontInfo(fontUris, customFont.isKerningEnabled(), - customFont.isAdvancedEnabled(), fontTripletList, subFontName, - EncodingMode.AUTO, EmbeddingMode.AUTO); + customFont.isAdvancedEnabled(), fontTripletList, subFontName); fontInfo.setPostScriptName(customFont.getFontName()); if (fontCache != null) { fontCache.addFont(fontInfo, resourceResolver); @@ -224,7 +223,7 @@ public class FontInfoFinder { try { OFFontLoader ttfLoader = new OFFontLoader(fontURI, fontName, true, EmbeddingMode.AUTO, EncodingMode.AUTO, useKerning, useAdvanced, - resourceResolver); + resourceResolver, false); customFont = ttfLoader.getFont(); if (this.eventListener != null) { customFont.setEventListener(this.eventListener); @@ -252,7 +251,7 @@ public class FontInfoFinder { try { FontUris fontUris = new FontUris(fontURI, null); customFont = FontLoader.loadFont(fontUris, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, - useKerning, useAdvanced, resourceResolver); + useKerning, useAdvanced, resourceResolver, false); if (this.eventListener != null) { customFont.setEventListener(this.eventListener); } 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 1585b0d2e..1d98f22ca 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 @@ -29,6 +29,7 @@ import java.util.Set; import org.apache.commons.io.IOUtils; import org.apache.fop.apps.io.InternalResourceResolver; +import org.apache.fop.fonts.CFFToType1Font; import org.apache.fop.fonts.CIDFontType; import org.apache.fop.fonts.CMapSegment; import org.apache.fop.fonts.EmbeddingMode; @@ -51,6 +52,7 @@ public class OFFontLoader extends FontLoader { private final String subFontName; private EncodingMode encodingMode; private EmbeddingMode embeddingMode; + private boolean embedAsType1; /** * Default constructor @@ -58,7 +60,7 @@ public class OFFontLoader extends FontLoader { * @param resourceResolver the resource resolver for font URI resolution */ public OFFontLoader(URI fontFileURI, InternalResourceResolver resourceResolver) { - this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, true, resourceResolver); + this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, true, resourceResolver, false); } /** @@ -75,11 +77,12 @@ public class OFFontLoader extends FontLoader { */ public OFFontLoader(URI fontFileURI, String subFontName, boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode, boolean useKerning, - boolean useAdvanced, InternalResourceResolver resolver) { + boolean useAdvanced, InternalResourceResolver resolver, boolean embedAsType1) { super(fontFileURI, embedded, useKerning, useAdvanced, resolver); this.subFontName = subFontName; this.encodingMode = encodingMode; this.embeddingMode = embeddingMode; + this.embedAsType1 = embedAsType1; if (this.encodingMode == EncodingMode.AUTO) { this.encodingMode = EncodingMode.CID; //Default to CID mode for TrueType } @@ -110,7 +113,7 @@ public class OFFontLoader extends FontLoader { if (!supported) { throw new IOException("The font does not have a Unicode cmap table: " + fontFileURI); } - buildFont(otf, ttcFontName); + buildFont(otf, ttcFontName, embedAsType1); loaded = true; } finally { IOUtils.closeQuietly(in); @@ -125,15 +128,19 @@ public class OFFontLoader extends FontLoader { return null; } - private void buildFont(OpenFont otf, String ttcFontName) { + private void buildFont(OpenFont otf, String ttcFontName, boolean embedAsType1) { boolean isCid = this.embedded; if (this.encodingMode == EncodingMode.SINGLE_BYTE) { isCid = false; } if (isCid) { - multiFont = new MultiByteFont(resourceResolver, embeddingMode); - multiFont.setIsOTFFile(otf instanceof OTFFile); + if (otf instanceof OTFFile && embedAsType1) { + multiFont = new CFFToType1Font(resourceResolver, embeddingMode); + } else { + multiFont = new MultiByteFont(resourceResolver, embeddingMode); + multiFont.setIsOTFFile(otf instanceof OTFFile); + } returnFont = multiFont; multiFont.setTTCName(ttcFontName); } else { @@ -142,7 +149,11 @@ public class OFFontLoader extends FontLoader { } returnFont.setFontURI(fontFileURI); - returnFont.setFontName(otf.getPostScriptName()); + if (!otf.getEmbedFontName().equals("")) { + returnFont.setFontName(otf.getEmbedFontName()); + } else { + returnFont.setFontName(otf.getPostScriptName()); + } returnFont.setFullName(otf.getFullName()); returnFont.setFamilyNames(otf.getFamilyNames()); returnFont.setFontSubFamilyName(otf.getSubFamilyName()); @@ -160,7 +171,6 @@ public class OFFontLoader extends FontLoader { returnFont.setItalicAngle(Integer.parseInt(otf.getItalicAngle())); returnFont.setMissingWidth(0); returnFont.setWeight(otf.getWeightClass()); - returnFont.setEmbeddingMode(this.embeddingMode); if (isCid) { if (otf instanceof OTFFile) { multiFont.setCIDType(CIDFontType.CIDTYPE0); 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 45c08391f..a96cb3d0e 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 @@ -88,6 +88,7 @@ public class OTFFile extends OpenFont { fontFile.seekSet(0); CFFParser parser = new CFFParser(); fileFont = parser.parse(in.getAllBytes()).get(0); + embedFontName = fileFont.getName(); } protected void readName() throws IOException { diff --git a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OpenFont.java b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OpenFont.java index e94f31bb1..34cad34df 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OpenFont.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OpenFont.java @@ -184,6 +184,7 @@ public abstract class OpenFont { protected String postScriptName = ""; protected String fullName = ""; + protected String embedFontName = ""; protected String notice = ""; protected final Set familyNames = new HashSet(); protected String subFamilyName = ""; @@ -2000,6 +2001,10 @@ public abstract class OpenFont { } } + public String getEmbedFontName() { + return embedFontName; + } + public String getCopyrightNotice() { return notice; } diff --git a/fop-core/src/main/java/org/apache/fop/fonts/type1/Type1SubsetFile.java b/fop-core/src/main/java/org/apache/fop/fonts/type1/Type1SubsetFile.java index 394d5c777..f7d818aad 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/type1/Type1SubsetFile.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/type1/Type1SubsetFile.java @@ -156,8 +156,8 @@ public class Type1SubsetFile { return stitchFont(boasHeader, boasMain, baosTrailer); } - protected byte[] stitchFont(ByteArrayOutputStream boasHeader, ByteArrayOutputStream boasMain, - ByteArrayOutputStream boasTrailer) throws IOException { + public byte[] stitchFont(ByteArrayOutputStream boasHeader, ByteArrayOutputStream boasMain, + ByteArrayOutputStream boasTrailer) throws IOException { int headerLength = boasHeader.size(); int mainLength = boasMain.size(); diff --git a/fop-core/src/main/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java b/fop-core/src/main/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java index c6f02506e..dfa7e01de 100644 --- a/fop-core/src/main/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java +++ b/fop-core/src/main/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java @@ -86,7 +86,8 @@ public class ConfiguredFontCollection implements FontCollection { CustomFont fontMetrics = FontLoader.loadFont(fontUris, configFontInfo.getSubFontName(), true, configFontInfo.getEmbeddingMode(), configFontInfo.getEncodingMode(), - configFontInfo.getKerning(), configFontInfo.getAdvanced(), resourceResolver); + configFontInfo.getKerning(), configFontInfo.getAdvanced(), resourceResolver, + configFontInfo.getEmbedAsType1()); font = new CustomFontMetricsMapper(fontMetrics); } 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 e8ad32b5e..a5812da72 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 @@ -277,7 +277,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { } gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, fontRes); if (fontType == FontType.TYPE1) { - embedType1Font(gen, (SingleByteFont) tf, in); + embedType1Font(gen, (CustomFont) tf, in); fontResource = PSFontResource.createFontResource(fontRes); } else if (fontType == FontType.TRUETYPE) { embedTrueTypeFont(gen, (SingleByteFont) tf, in); @@ -316,7 +316,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { } } - private static void embedType1Font(PSGenerator gen, SingleByteFont font, + private static void embedType1Font(PSGenerator gen, CustomFont font, InputStream fontStream) throws IOException { if (font.getEmbeddingMode() == EmbeddingMode.AUTO) { font.setEmbeddingMode(EmbeddingMode.FULL); @@ -326,12 +326,12 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { boolean embed = true; if (font.getEmbeddingMode() == EmbeddingMode.SUBSET) { Type1SubsetFile subset = new Type1SubsetFile(); - byte[] byteSubset = subset.createSubset(fontStream, font); + byte[] byteSubset = subset.createSubset(fontStream, (SingleByteFont) font); fontStream = new ByteArrayInputStream(byteSubset); } embedType1Font(gen, fontStream); if (font.getEmbeddingMode() == EmbeddingMode.SUBSET) { - writeEncoding(gen, font); + writeEncoding(gen, (SingleByteFont) font); } } 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 d00fdd263..3d2228bb9 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,6 +40,7 @@ import org.apache.xmlgraphics.image.loader.ImageSessionContext; import org.apache.xmlgraphics.ps.PSGenerator; import org.apache.xmlgraphics.ps.PSResource; +import org.apache.fop.fonts.CFFToType1Font; import org.apache.fop.fonts.EmbeddingMode; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontTriplet; @@ -458,7 +459,7 @@ public class PSPainter extends AbstractIFPainter { int lineStart = 0; StringBuffer accText = new StringBuffer(initialSize); StringBuffer sb = new StringBuffer(initialSize); - boolean isOTF = multiByte && ((MultiByteFont)tf).isOTFFile(); + boolean isOTF = multiByte && ((MultiByteFont)tf).isOTFFile() || tf instanceof CFFToType1Font; for (int i = start; i < end; i++) { char orgChar = text.charAt(i); char ch; diff --git a/fop-core/src/main/java/org/apache/fop/render/ps/Type1CharStringFormatter.java b/fop-core/src/main/java/org/apache/fop/render/ps/Type1CharStringFormatter.java new file mode 100644 index 000000000..0a6613a36 --- /dev/null +++ b/fop-core/src/main/java/org/apache/fop/render/ps/Type1CharStringFormatter.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fop.render.ps; + +import java.io.ByteArrayOutputStream; +import java.util.List; + +import org.apache.fontbox.cff.CharStringCommand; + +/** + * This class represents a formatter for CharString commands of a Type1 font. + * author Villu Ruusmann + * @version $Revision: 1.0 $ + */ +public class Type1CharStringFormatter { + + private ByteArrayOutputStream output; + + /** + * Formats the given command sequence to a byte array. + * @param sequence the given command sequence + * @return the formatted seuqence as byte array + */ + public byte[] format(List sequence) { + output = new ByteArrayOutputStream(); + + for (Object object : sequence) { + if (object instanceof CharStringCommand) { + writeCommand((CharStringCommand) object); + } else if (object instanceof Integer) { + writeNumber((Integer) object); + } else { + throw new IllegalArgumentException(); + } + } + return output.toByteArray(); + } + + private void writeCommand(CharStringCommand command) { + int[] value = command.getKey().getValue(); + for (int aValue : value) { + output.write(aValue); + } + } + + private void writeNumber(Integer number) { + int value = number; + if (value >= -107 && value <= 107) { + output.write(value + 139); + } else if (value >= 108 && value <= 1131) { + int b1 = (value - 108) % 256; + int b0 = (value - 108 - b1) / 256 + 247; + output.write(b0); + output.write(b1); + } else if (value >= -1131 && value <= -108) { + int b1 = -((value + 108) % 256); + int b0 = -((value + 108 + b1) / 256 - 251); + output.write(b0); + output.write(b1); + } else { + int b1 = value >>> 24 & 0xff; + int b2 = value >>> 16 & 0xff; + int b3 = value >>> 8 & 0xff; + int b4 = value >>> 0 & 0xff; + output.write(255); + output.write(b1); + output.write(b2); + output.write(b3); + output.write(b4); + } + } +} diff --git a/fop-core/src/main/java/org/apache/fop/render/ps/Type1FontFormatter.java b/fop-core/src/main/java/org/apache/fop/render/ps/Type1FontFormatter.java new file mode 100644 index 000000000..469f66b12 --- /dev/null +++ b/fop-core/src/main/java/org/apache/fop/render/ps/Type1FontFormatter.java @@ -0,0 +1,213 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fop.render.ps; + +import java.io.IOException; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.Collection; +import java.util.Locale; +import java.util.Map; + +import org.apache.fontbox.cff.CFFType1Font; +import org.apache.fontbox.cff.DataOutput; +import org.apache.fontbox.cff.Type1FontUtil; + +/** + * This class represents a formatter for a given Type1 font. + * author Villu Ruusmann + * @version $Revision: 1.0 $ + */ +public final class Type1FontFormatter { + private Map gids; + + public Type1FontFormatter(Map gids) { + this.gids = gids; + } + + /** + * Read and convert a given CFFFont. + * @param font the given CFFFont + * @return the Type1 font + * @throws IOException if an error occurs during reading the given font + */ + public byte[] format(CFFType1Font font) throws IOException { + DataOutput output = new DataOutput(); + printFont(font, output); + return output.getBytes(); + } + + private void printFont(CFFType1Font font, DataOutput output) + throws IOException { + output.println("%!FontType1-1.0 " + font.getName() + " " + + font.getTopDict().get("version")); + + printFontDictionary(font, output); + + for (int i = 0; i < 8; i++) { + StringBuilder sb = new StringBuilder(); + + for (int j = 0; j < 64; j++) { + sb.append("0"); + } + + output.println(sb.toString()); + } + + output.println("cleartomark"); + } + + private void printFontDictionary(CFFType1Font font, DataOutput output) + throws IOException { + output.println("10 dict begin"); + output.println("/FontInfo 10 dict dup begin"); + output.println("/version (" + font.getTopDict().get("version") + + ") readonly def"); + output.println("/Notice (" + font.getTopDict().get("Notice") + + ") readonly def"); + output.println("/FullName (" + font.getTopDict().get("FullName") + + ") readonly def"); + output.println("/FamilyName (" + font.getTopDict().get("FamilyName") + + ") readonly def"); + output.println("/Weight (" + font.getTopDict().get("Weight") + + ") readonly def"); + output.println("/ItalicAngle " + font.getTopDict().get("ItalicAngle") + + " def"); + output.println("/isFixedPitch " + font.getTopDict().get("isFixedPitch") + + " def"); + output.println("/UnderlinePosition " + + font.getTopDict().get("UnderlinePosition") + " def"); + output.println("/UnderlineThickness " + + font.getTopDict().get("UnderlineThickness") + " def"); + output.println("end readonly def"); + output.println("/FontName /" + font.getName() + " def"); + output.println("/PaintType " + font.getTopDict().get("PaintType") + " def"); + output.println("/FontType 1 def"); + NumberFormat matrixFormat = new DecimalFormat("0.########", new DecimalFormatSymbols(Locale.US)); + output.println("/FontMatrix " + + formatArray(font.getTopDict().get("FontMatrix"), matrixFormat, false) + + " readonly def"); + output.println("/FontBBox " + + formatArray(font.getTopDict().get("FontBBox"), false) + + " readonly def"); + output.println("/StrokeWidth " + font.getTopDict().get("StrokeWidth") + + " def"); + + output.println("/Encoding " + gids.size() + " array"); + output.println("0 1 " + (gids.size() - 1) + " {1 index exch /.notdef put} for"); + + for (Map.Entry gid : gids.entrySet()) { + String name = font.getCharset().getNameForGID(gid.getKey()); + output.println("dup " + gid.getValue() + " /" + name + " put"); + } + output.println("readonly def"); + + output.println("currentdict end"); + + DataOutput eexecOutput = new DataOutput(); + + printEexecFontDictionary(font, eexecOutput); + + output.println("currentfile eexec"); + + byte[] eexecBytes = Type1FontUtil.eexecEncrypt(eexecOutput.getBytes()); + output.write(eexecBytes); + } + + private void printEexecFontDictionary(CFFType1Font font, DataOutput output) + throws IOException { + output.println("dup /Private 15 dict dup begin"); + output.println("/RD {string currentfile exch readstring pop} executeonly def"); + output.println("/ND {noaccess def} executeonly def"); + output.println("/NP {noaccess put} executeonly def"); + output.println("/BlueValues " + + formatArray(font.getPrivateDict().get("BlueValues"), true) + " ND"); + output.println("/OtherBlues " + + formatArray(font.getPrivateDict().get("OtherBlues"), true) + " ND"); + output.println("/BlueScale " + font.getPrivateDict().get("BlueScale") + " def"); + output.println("/BlueShift " + font.getPrivateDict().get("BlueShift") + " def"); + output.println("/BlueFuzz " + font.getPrivateDict().get("BlueFuzz") + " def"); + output.println("/StdHW " + formatArray(font.getPrivateDict().get("StdHW"), true) + + " ND"); + output.println("/StdVW " + formatArray(font.getPrivateDict().get("StdVW"), true) + + " ND"); + output.println("/ForceBold " + font.getPrivateDict().get("ForceBold") + " def"); + output.println("/MinFeature {16 16} def"); + output.println("/password 5839 def"); + + output.println("2 index /CharStrings " + gids.size() + " dict dup begin"); + Type1CharStringFormatter formatter = new Type1CharStringFormatter(); + for (int gid : gids.keySet()) { + String mapping = font.getCharset().getNameForGID(gid); + byte[] type1Bytes = formatter.format(font.getType1CharString(mapping).getType1Sequence()); + byte[] charstringBytes = Type1FontUtil.charstringEncrypt(type1Bytes, 4); + output.print("/" + mapping + " " + charstringBytes.length + " RD "); + output.write(charstringBytes); + output.print(" ND"); + output.println(); + } + + output.println("end"); + output.println("end"); + + output.println("readonly put"); + output.println("noaccess put"); + output.println("dup /FontName get exch definefont pop"); + output.println("mark currentfile closefile"); + } + + private static String formatArray(Object object, boolean executable) { + return formatArray(object, null, executable); + } + + private static String formatArray(Object object, NumberFormat format, boolean executable) { + StringBuffer sb = new StringBuffer(); + + sb.append(executable ? "{" : "["); + + if (object instanceof Collection) { + String sep = ""; + + Collection elements = (Collection) object; + for (Object element : elements) { + sb.append(sep).append(formatElement(element, format)); + + sep = " "; + } + } else if (object instanceof Number) { + sb.append(formatElement(object, format)); + } + + sb.append(executable ? "}" : "]"); + + return sb.toString(); + } + + private static String formatElement(Object object, NumberFormat format) { + if (format != null) { + if (object instanceof Double || object instanceof Float) { + Number number = (Number)object; + return format.format(number.doubleValue()); + } else if (object instanceof Long || object instanceof Integer) { + Number number = (Number)object; + return format.format(number.longValue()); + } + } + return String.valueOf(object); + } +} diff --git a/fop-core/src/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java b/fop-core/src/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java index c4b471ee5..eb6ffa653 100644 --- a/fop-core/src/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java @@ -49,7 +49,7 @@ public class DejaVuLGCSerifTestCase { File file = new File("test/resources/fonts/ttf/DejaVuLGCSerif.ttf"); FontUris fontUris = new FontUris(file.toURI(), null); font = FontLoader.loadFont(fontUris, "", true, EmbeddingMode.AUTO, EncodingMode.AUTO, - false, false, resolver); + false, false, resolver, false); } /** diff --git a/fop-core/src/test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java b/fop-core/src/test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java index e4179f1f6..9c9a0a3c5 100644 --- a/fop-core/src/test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java @@ -53,7 +53,7 @@ public class EmbedFontInfoTestCase { List triplets = new ArrayList(); triplets.add(triplet); FontUris fontUris = new FontUris(embedURI, metricsURI); - sut = new EmbedFontInfo(fontUris, kerning, useAdvanced, triplets, subFontName, encMode, embedMode); + sut = new EmbedFontInfo(fontUris, kerning, useAdvanced, triplets, subFontName, encMode, embedMode, false); } @Test @@ -83,8 +83,7 @@ public class EmbedFontInfoTestCase { @Test public void testQuirkyBoundaryCasesIsEmbedded() { FontUris fontUris = new FontUris(null, metricsURI); - sut = new EmbedFontInfo(fontUris, kerning, useAdvanced, sut.getFontTriplets(), subFontName, encMode, - embedMode); + sut = new EmbedFontInfo(fontUris, kerning, useAdvanced, sut.getFontTriplets(), subFontName); sut.setEmbedded(true); assertFalse(sut.isEmbedded()); diff --git a/fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFToType1TestCase.java b/fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFToType1TestCase.java new file mode 100644 index 000000000..35e441296 --- /dev/null +++ b/fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFToType1TestCase.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ +package org.apache.fop.fonts.truetype; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.fontbox.type1.Type1Font; + +import org.apache.fop.apps.io.InternalResourceResolver; +import org.apache.fop.apps.io.ResourceResolverFactory; +import org.apache.fop.fonts.CustomFont; +import org.apache.fop.fonts.EmbeddingMode; +import org.apache.fop.fonts.EncodingMode; +import org.apache.fop.fonts.FontLoader; +import org.apache.fop.fonts.FontUris; + +public class OTFToType1TestCase { + + @Test + public void testFont() throws IOException { + Type1Font t1 = getFont("test/resources/fonts/otf/SourceSansProBold.otf"); + Assert.assertEquals(t1.getFontName(), "SourceSansPro-Bold"); + Assert.assertEquals(t1.getCharStringsDict().keySet().toString(), "[.notdef, d]"); + t1 = getFont("test/resources/fonts/otf/AlexBrushRegular.otf"); + Assert.assertEquals(t1.getFontName(), "AlexBrush-Regular"); + } + + private Type1Font getFont(String s) throws IOException { + InternalResourceResolver rr = ResourceResolverFactory.createDefaultInternalResourceResolver( + new File(".").toURI()); + CustomFont realFont = FontLoader.loadFont(new FontUris(new File(s).toURI(), null), null, true, + EmbeddingMode.SUBSET, EncodingMode.AUTO, true, true, rr, true); + realFont.mapChar('d'); + InputStream is = realFont.getInputStream(); + return Type1Font.createWithPFB(is); + } +} diff --git a/fop-core/src/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java b/fop-core/src/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java index 899fe1d73..49002bbce 100644 --- a/fop-core/src/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java @@ -50,12 +50,12 @@ public class TTFFontLoaderTestCase { boolean useKerning = true; OFFontLoader fontLoader = new OFFontLoader(absoluteFilePath, fontName, embedded, - EmbeddingMode.AUTO, EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resourceResolver); + EmbeddingMode.AUTO, EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resourceResolver, false); assertTrue(fontLoader.getFont().hasKerningInfo()); useKerning = false; fontLoader = new OFFontLoader(absoluteFilePath, fontName, embedded, EmbeddingMode.AUTO, - EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resourceResolver); + EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resourceResolver, false); assertFalse(fontLoader.getFont().hasKerningInfo()); } } diff --git a/fop-core/src/test/java/org/apache/fop/svg/font/FontInfoBuilder.java b/fop-core/src/test/java/org/apache/fop/svg/font/FontInfoBuilder.java index c9346588c..583d7653a 100644 --- a/fop-core/src/test/java/org/apache/fop/svg/font/FontInfoBuilder.java +++ b/fop-core/src/test/java/org/apache/fop/svg/font/FontInfoBuilder.java @@ -83,7 +83,7 @@ class FontInfoBuilder { URI baseURI = new File("test/resources/fonts/ttf").toURI(); InternalResourceResolver resolver = ResourceResolverFactory.createDefaultInternalResourceResolver(baseURI); OFFontLoader fontLoader = new OFFontLoader(new URI(filename), null, true, - EmbeddingMode.AUTO, EncodingMode.AUTO, true, useAdvanced, resolver); + EmbeddingMode.AUTO, EncodingMode.AUTO, true, useAdvanced, resolver, false); FontMetrics font = fontLoader.getFont(); registerFont(font, "F" + fontKey++, fontName); return this; diff --git a/fop/src/foschema/fop-configuration.xsd b/fop/src/foschema/fop-configuration.xsd index 364af22e9..a2e68bcfb 100644 --- a/fop/src/foschema/fop-configuration.xsd +++ b/fop/src/foschema/fop-configuration.xsd @@ -291,6 +291,7 @@ + -- 2.39.5