Browse Source

FOP-2608: Allow more than 256 glyphs for CFF to Type1

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1745490 13f79535-47bb-0310-9956-ffa450edef68
pull/3/head
Simon Steiner 8 years ago
parent
commit
16e07aaae8

+ 39
- 2
fop-core/src/main/java/org/apache/fop/fonts/CFFToType1Font.java View File

@@ -22,6 +22,10 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.fontbox.cff.CFFFont;
@@ -43,16 +47,49 @@ public class CFFToType1Font extends MultiByteFont {
}

public InputStream getInputStream() throws IOException {
return null;
}

public List<InputStream> getInputStreams() throws IOException {
InputStream cff = super.getInputStream();
return convertOTFToType1(cff);
}

private InputStream convertOTFToType1(InputStream in) throws IOException {
private List<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(), eventListener).format((CFFType1Font) f);
List<InputStream> fonts = new ArrayList<InputStream>();
Map<Integer, Integer> glyphs = cidSet.getGlyphs();
int i = 0;
for (Map<Integer, Integer> x : splitGlyphs(glyphs)) {
String iStr = "." + i;
fonts.add(convertOTFToType1(x, f, iStr));
i++;
}
return fonts;
}

private List<Map<Integer, Integer>> splitGlyphs(Map<Integer, Integer> glyphs) {
List<Map<Integer, Integer>> allGlyphs = new ArrayList<Map<Integer, Integer>>();
for (Map.Entry<Integer, Integer> x : glyphs.entrySet()) {
int k = x.getKey();
int v = x.getValue();
int pot = v / 256;
v = v % 256;
while (allGlyphs.size() < pot + 1) {
Map<Integer, Integer> glyphsPerFont = new HashMap<Integer, Integer>();
glyphsPerFont.put(0, 0);
allGlyphs.add(glyphsPerFont);
}
allGlyphs.get(pot).put(k, v);
}
return allGlyphs;
}

private InputStream convertOTFToType1(Map<Integer, Integer> glyphs, CFFFont f, String i) throws IOException {
byte[] t1 = new Type1FontFormatter(glyphs).format((CFFType1Font) f, i);
PFBData pfb = new PFBParser().parsePFB(new ByteArrayInputStream(t1));
ByteArrayOutputStream s1 = new ByteArrayOutputStream();
s1.write(pfb.getHeaderSegment());

+ 0
- 4
fop-core/src/main/java/org/apache/fop/fonts/FontEventAdapter.java View File

@@ -66,10 +66,6 @@ public class FontEventAdapter implements FontEventListener {
getEventProducer().fontDirectoryNotFound(source, dir);
}

public void fontType1MaxGlyphs(Object source, String fontName) {
getEventProducer().fontType1MaxGlyphs(source, fontName);
}

/** {@inheritDoc} */
public void svgTextStrokedAsShapes(Object source, String fontFamily) {
getEventProducer().svgTextStrokedAsShapes(source, fontFamily);

+ 0
- 2
fop-core/src/main/java/org/apache/fop/fonts/FontEventListener.java View File

@@ -61,6 +61,4 @@ public interface FontEventListener {
* @param fontFamily the family name of the font that is being stroked
*/
void svgTextStrokedAsShapes(Object source, String fontFamily);

void fontType1MaxGlyphs(Object source, String fontName);
}

+ 0
- 7
fop-core/src/main/java/org/apache/fop/fonts/FontEventProducer.java View File

@@ -87,11 +87,4 @@ public interface FontEventProducer extends EventProducer {
*/
void svgTextStrokedAsShapes(Object source, String fontFamily);

/**
* A method to warn the user that the font has too many glyphs
* @param source
* @param fontName
* @event.severity ERROR
*/
void fontType1MaxGlyphs(Object source, String fontName);
}

+ 1
- 1
fop-core/src/main/java/org/apache/fop/fonts/truetype/OFFontLoader.java View File

@@ -139,8 +139,8 @@ public class OFFontLoader extends FontLoader {
multiFont = new CFFToType1Font(resourceResolver, embeddingMode);
} else {
multiFont = new MultiByteFont(resourceResolver, embeddingMode);
multiFont.setIsOTFFile(otf instanceof OTFFile);
}
multiFont.setIsOTFFile(otf instanceof OTFFile);
returnFont = multiFont;
multiFont.setTTCName(ttcFontName);
} else {

+ 53
- 38
fop-core/src/main/java/org/apache/fop/render/ps/PSFontUtils.java View File

@@ -22,8 +22,10 @@ package org.apache.fop.render.ps;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -40,6 +42,7 @@ import org.apache.xmlgraphics.ps.PSResource;
import org.apache.xmlgraphics.ps.dsc.ResourceTracker;

import org.apache.fop.fonts.Base14Font;
import org.apache.fop.fonts.CFFToType1Font;
import org.apache.fop.fonts.CIDFontType;
import org.apache.fop.fonts.CIDSet;
import org.apache.fop.fonts.CMapSegment;
@@ -138,8 +141,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
Map fontResources = new HashMap();
for (String key : fonts.keySet()) {
Typeface tf = getTypeFace(fontInfo, fonts, key);
PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName());
PSFontResource fontResource = embedFont(gen, tf, fontRes, eventProducer);
PSFontResource fontResource = embedFont(gen, tf, eventProducer);
fontResources.put(key, fontResource);

if (tf instanceof SingleByteFont) {
@@ -239,11 +241,12 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
return tf;
}

private static PSFontResource embedFont(PSGenerator gen, Typeface tf, PSResource fontRes,
PSEventProducer eventProducer) throws IOException {
private static PSFontResource embedFont(PSGenerator gen, Typeface tf, PSEventProducer eventProducer)
throws IOException {
boolean embeddedFont = false;
FontType fontType = tf.getFontType();
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)) {
gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
@@ -252,42 +255,51 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
}
CustomFont cf = (CustomFont)tf;
if (isEmbeddable(cf)) {
InputStream in = getInputStreamOnFont(gen, cf);
if (in != null) {
if (fontType == FontType.TYPE0) {
if (((MultiByteFont)tf).isOTFFile()) {
checkPostScriptLevel3(gen, eventProducer, "OpenType CFF");
embedType2CFF(gen, (MultiByteFont) tf, in);
} else {
if (gen.embedIdentityH()) {
checkPostScriptLevel3(gen, eventProducer, "TrueType");
List<InputStream> ins = getInputStreamOnFont(gen, cf);
if (ins != null) {
int i = 0;
for (InputStream in : ins) {
if (i > 0) {
fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName() + "." + i);
}
if (fontType == FontType.TYPE0) {
if (((MultiByteFont) tf).isOTFFile()) {
checkPostScriptLevel3(gen, eventProducer, "OpenType CFF");
embedType2CFF(gen, (MultiByteFont) tf, in);
} else {
if (gen.embedIdentityH()) {
checkPostScriptLevel3(gen, eventProducer, "TrueType");
/*
* First CID-keyed font to be embedded; add
* %%IncludeResource: comment for ProcSet CIDInit.
*/
gen.includeProcsetCIDInitResource();
gen.includeProcsetCIDInitResource();
}
PSResource cidFontResource;
cidFontResource = embedType2CIDFont(gen,
(MultiByteFont) tf, in);
fontResource = PSFontResource.createFontResource(fontRes,
gen.getProcsetCIDInitResource(), gen.getIdentityHCMapResource(),
cidFontResource);
}
PSResource cidFontResource;
cidFontResource = embedType2CIDFont(gen,
(MultiByteFont) tf, in);
fontResource = PSFontResource.createFontResource(fontRes,
gen.getProcsetCIDInitResource(), gen.getIdentityHCMapResource(),
cidFontResource);
}
gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, fontRes);
if (fontType == FontType.TYPE1) {
embedType1Font(gen, (CustomFont) tf, in);
if (fontResource == null) {
fontResource = PSFontResource.createFontResource(fontRes);
}
} else if (fontType == FontType.TRUETYPE) {
embedTrueTypeFont(gen, (SingleByteFont) tf, in);
fontResource = PSFontResource.createFontResource(fontRes);
} else if (!((MultiByteFont) tf).isOTFFile()) {
composeType0Font(gen, (MultiByteFont) tf);
}
gen.writeDSCComment(DSCConstants.END_RESOURCE);
gen.getResourceTracker().registerSuppliedResource(fontRes);
embeddedFont = true;
i++;
}
gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, fontRes);
if (fontType == FontType.TYPE1) {
embedType1Font(gen, (CustomFont) tf, in);
fontResource = PSFontResource.createFontResource(fontRes);
} else if (fontType == FontType.TRUETYPE) {
embedTrueTypeFont(gen, (SingleByteFont) tf, in);
fontResource = PSFontResource.createFontResource(fontRes);
} else {
composeType0Font(gen, (MultiByteFont) tf, in);
}
gen.writeDSCComment(DSCConstants.END_RESOURCE);
gen.getResourceTracker().registerSuppliedResource(fontRes);
embeddedFont = true;
} else {
gen.commentln("%WARNING: Could not embed font: " + cf.getEmbedFontName());
log.warn("Font " + cf.getEmbedFontName() + " is marked as supplied in the"
@@ -297,7 +309,6 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
if (!embeddedFont) {
gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
fontResource = PSFontResource.createFontResource(fontRes);
return fontResource;
}
return fontResource;
}
@@ -502,8 +513,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
return 0;
}

private static void composeType0Font(PSGenerator gen, MultiByteFont font,
InputStream fontStream) throws IOException {
private static void composeType0Font(PSGenerator gen, MultiByteFont font) throws IOException {
String psName = font.getEmbedFontName();
gen.write("/");
gen.write(psName);
@@ -724,18 +734,23 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
return font.isEmbeddable();
}

private static InputStream getInputStreamOnFont(PSGenerator gen, CustomFont font)
private static List<InputStream> getInputStreamOnFont(PSGenerator gen, CustomFont font)
throws IOException {
if (isEmbeddable(font)) {
List<InputStream> fonts = new ArrayList<InputStream>();
InputStream in = font.getInputStream();
if (in == null) {
if (font instanceof CFFToType1Font) {
return ((CFFToType1Font) font).getInputStreams();
}
return null;
}
//Make sure the InputStream is decorated with a BufferedInputStream
if (!(in instanceof java.io.BufferedInputStream)) {
in = new java.io.BufferedInputStream(in);
}
return in;
fonts.add(in);
return fonts;
} else {
return null;
}

+ 5
- 9
fop-core/src/main/java/org/apache/fop/render/ps/PSPainter.java View File

@@ -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.CFFToType1Font;
import org.apache.fop.fonts.EmbeddingMode;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontTriplet;
@@ -429,8 +428,7 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> {
true);
start = i;
}
generator.writeln("/" + res.getName() + "." + encoding + " "
+ generator.formatDouble(sizeMillipoints / 1000f) + " F");
generator.useFont("/" + res.getName() + "." + encoding, sizeMillipoints / 1000f);
curEncoding = encoding;
}
}
@@ -459,7 +457,7 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> {
int lineStart = 0;
StringBuffer accText = new StringBuffer(initialSize);
StringBuffer sb = new StringBuffer(initialSize);
boolean isOTF = multiByte && ((MultiByteFont)tf).isOTFFile() || tf instanceof CFFToType1Font;
boolean isOTF = multiByte && ((MultiByteFont)tf).isOTFFile();
for (int i = start; i < end; i++) {
char orgChar = text.charAt(i);
char ch;
@@ -580,13 +578,11 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> {
private void useFont(String key, int size, boolean otf) throws IOException {
PSFontResource res = getDocumentHandler().getPSResourceForFontKey(key);
PSGenerator generator = getGenerator();
String name = "/" + res.getName();
if (otf) {
String name = "/" + res.getName() + ".0";
generator.getCurrentState().useFont(name, size);
generator.writeln(name + ' ' + generator.formatDouble(size / 1000f) + " F");
} else {
generator.useFont("/" + res.getName(), size / 1000f);
name += ".0";
}
generator.useFont(name, size / 1000f);
res.notifyResourceUsageOnPage(generator.getResourceTracker());
}
}

+ 9
- 15
fop-core/src/main/java/org/apache/fop/render/ps/Type1FontFormatter.java View File

@@ -28,8 +28,6 @@ import org.apache.fontbox.cff.CFFType1Font;
import org.apache.fontbox.cff.DataOutput;
import org.apache.fontbox.cff.Type1FontUtil;

import org.apache.fop.fonts.FontEventListener;

/**
* This class represents a formatter for a given Type1 font.
* author Villu Ruusmann
@@ -37,31 +35,30 @@ import org.apache.fop.fonts.FontEventListener;
*/
public final class Type1FontFormatter {
private Map<Integer, Integer> gids;
private FontEventListener eventListener;

public Type1FontFormatter(Map<Integer, Integer> gids, FontEventListener eventListener) {
public Type1FontFormatter(Map<Integer, Integer> gids) {
this.gids = gids;
this.eventListener = eventListener;
}

/**
* Read and convert a given CFFFont.
* @param font the given CFFFont
* @param i
* @return the Type1 font
* @throws IOException if an error occurs during reading the given font
*/
public byte[] format(CFFType1Font font) throws IOException {
public byte[] format(CFFType1Font font, String i) throws IOException {
DataOutput output = new DataOutput();
printFont(font, output);
printFont(font, output, i);
return output.getBytes();
}

private void printFont(CFFType1Font font, DataOutput output)
private void printFont(CFFType1Font font, DataOutput output, String iStr)
throws IOException {
output.println("%!FontType1-1.0 " + font.getName() + " "
output.println("%!FontType1-1.0 " + font.getName() + iStr + " "
+ font.getTopDict().get("version"));

printFontDictionary(font, output);
printFontDictionary(font, output, iStr);

for (int i = 0; i < 8; i++) {
StringBuilder sb = new StringBuilder();
@@ -76,7 +73,7 @@ public final class Type1FontFormatter {
output.println("cleartomark");
}

private void printFontDictionary(CFFType1Font font, DataOutput output)
private void printFontDictionary(CFFType1Font font, DataOutput output, String iStr)
throws IOException {
output.println("10 dict begin");
output.println("/FontInfo 10 dict dup begin");
@@ -99,7 +96,7 @@ public final class Type1FontFormatter {
output.println("/UnderlineThickness "
+ font.getTopDict().get("UnderlineThickness") + " def");
output.println("end readonly def");
output.println("/FontName /" + font.getName() + " def");
output.println("/FontName /" + font.getName() + iStr + " def");
output.println("/PaintType " + font.getTopDict().get("PaintType") + " def");
output.println("/FontType 1 def");
NumberFormat matrixFormat = new DecimalFormat("0.########", new DecimalFormatSymbols(Locale.US));
@@ -119,9 +116,6 @@ public final class Type1FontFormatter {
sb.append(String.format("dup %d /%s put", gid.getValue(), name)).append('\n');
max = Math.max(max, gid.getValue());
}
if (max > 255) {
eventListener.fontType1MaxGlyphs(this, font.getName());
}
output.println("/Encoding " + (max + 1) + " array");
output.println("0 1 " + max + " {1 index exch /.notdef put} for");
output.print(sb.toString());

+ 0
- 4
fop-core/src/main/java/org/apache/fop/tools/fontlist/FontListMain.java View File

@@ -154,10 +154,6 @@ public final class FontListMain {
public void svgTextStrokedAsShapes(Object source, String fontFamily) {
// ignore
}

public void fontType1MaxGlyphs(Object source, String fontName) {
//ignore
}
};

FontListGenerator listGenerator = new FontListGenerator();

+ 0
- 1
fop-core/src/main/resources/org/apache/fop/fonts/FontEventProducer.xml View File

@@ -22,5 +22,4 @@
<message key="glyphNotAvailable">Glyph "{ch}" (0x{ch,hex}[, {ch,glyph-name}]) not available in font "{fontName}".</message>
<message key="fontDirectoryNotFound">The font directory {dir} could not be found.</message>
<message key="svgTextStrokedAsShapes">The SVG text for font {fontFamily} will be stroked as shapes.</message>
<message key="fontType1MaxGlyphs">Font "{fontName}" encoding has more than 256 glyphs may cause wrong output, enable font subsetting or disable 'embed-as-type1'.</message>
</catalogue>

+ 4
- 3
fop-core/src/test/java/org/apache/fop/fonts/truetype/OTFToType1TestCase.java View File

@@ -29,6 +29,7 @@ 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.CFFToType1Font;
import org.apache.fop.fonts.CustomFont;
import org.apache.fop.fonts.EmbeddingMode;
import org.apache.fop.fonts.EncodingMode;
@@ -40,10 +41,10 @@ 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.getFontName(), "SourceSansPro-Bold.0");
Assert.assertEquals(t1.getCharStringsDict().keySet().toString(), "[.notdef, d]");
t1 = getFont("test/resources/fonts/otf/AlexBrushRegular.otf");
Assert.assertEquals(t1.getFontName(), "AlexBrush-Regular");
Assert.assertEquals(t1.getFontName(), "AlexBrush-Regular.0");
}

private Type1Font getFont(String s) throws IOException {
@@ -52,7 +53,7 @@ public class OTFToType1TestCase {
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();
InputStream is = ((CFFToType1Font)realFont).getInputStreams().get(0);
return Type1Font.createWithPFB(is);
}
}

+ 0
- 1
fop-core/src/test/java/org/apache/fop/render/ps/PSPainterTestCase.java View File

@@ -220,7 +220,6 @@ public class PSPainterTestCase {
+ "/OTFFont.0 0.01 F\n"
+ "1 0 0 -1 0 0 Tm\n"
+ "<FFFFFFFFFF> t\n"
+ "/OTFFont.0 0.01 F\n"
+ "1 0 0 -1 0 0 Tm\n"
+ "<FFFFFFFFFF> t\n"
+ "/TTFFont 0.01 F\n"

Loading…
Cancel
Save