git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1745490 13f79535-47bb-0310-9956-ffa450edef68pull/3/head
@@ -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()); |
@@ -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); |
@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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 { |
@@ -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; | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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()); |
@@ -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(); |
@@ -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> |
@@ -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); | |||
} | |||
} |
@@ -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" |