git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1885366 13f79535-47bb-0310-9956-ffa450edef68tags/fop-2_6
@@ -413,7 +413,7 @@ public class FOUserAgent { | |||
try { | |||
// Have to do this so we can resolve data URIs | |||
StreamSource src = new StreamSource(resourceResolver.getResource(uri)); | |||
src.setSystemId(uri); | |||
src.setSystemId(getResourceResolver().getBaseURI().toASCIIString()); | |||
return src; | |||
} catch (URISyntaxException use) { | |||
return null; |
@@ -141,7 +141,7 @@ public class FopConfParser { | |||
*/ | |||
public FopConfParser(File fopConfFile, ResourceResolver resourceResolver) | |||
throws SAXException, IOException { | |||
this(new FileInputStream(fopConfFile), fopConfFile.toURI(), resourceResolver); | |||
this(new FileInputStream(fopConfFile), fopConfFile.getParentFile().toURI(), resourceResolver); | |||
} | |||
public FopConfParser(InputStream fopConfStream, URI baseURI, EnvironmentProfile enviro) |
@@ -215,7 +215,9 @@ public final class OTFAdvancedTypographicTableReader { | |||
log.debug(tableTag + " lang sys default: " + dt); | |||
} | |||
} | |||
seScripts.put(scriptTag, new Object[] { dt, ll, seLanguages }); | |||
if (seLanguages != null) { | |||
seScripts.put(scriptTag, new Object[]{dt, ll, seLanguages}); | |||
} | |||
seLanguages = null; | |||
} | |||
@@ -32,6 +32,7 @@ import java.util.Map; | |||
import java.util.Set; | |||
import org.apache.fop.apps.io.InternalResourceResolver; | |||
import org.apache.fop.fonts.truetype.SVGGlyphData; | |||
/** | |||
@@ -80,6 +81,7 @@ public abstract class CustomFont extends Typeface | |||
private boolean useKerning = true; | |||
/** the character map, mapping Unicode ranges to glyph indices. */ | |||
protected List<CMapSegment> cmap = new ArrayList<CMapSegment>(); | |||
protected Map<Integer, SVGGlyphData> svgs; | |||
private boolean useAdvanced = true; | |||
private boolean simulateStyle; | |||
protected List<SimpleSingleByteEncoding> additionalEncodings; | |||
@@ -682,4 +684,12 @@ public abstract class CustomFont extends Typeface | |||
} | |||
return 0; | |||
} | |||
public boolean hasSVG() { | |||
return svgs != null; | |||
} | |||
public void setSVG(Map<Integer, SVGGlyphData> svgs) { | |||
this.svgs = svgs; | |||
} | |||
} |
@@ -132,7 +132,8 @@ public final class DefaultFontConfig implements FontConfig { | |||
fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName()), | |||
fontCfg.getAttribute("embedding-mode", EncodingMode.AUTO.getName()), | |||
fontCfg.getAttributeAsBoolean("simulate-style", false), | |||
fontCfg.getAttributeAsBoolean("embed-as-type1", false)); | |||
fontCfg.getAttributeAsBoolean("embed-as-type1", false), | |||
fontCfg.getAttributeAsBoolean("svg", true)); | |||
instance.fonts.add(font); | |||
boolean hasTriplets = false; | |||
for (Configuration tripletCfg : fontCfg.getChildren("font-triplet")) { | |||
@@ -322,6 +323,8 @@ public final class DefaultFontConfig implements FontConfig { | |||
private final boolean embedAsType1; | |||
private final boolean simulateStyle; | |||
private final boolean useSVG; | |||
private final List<FontTriplet> tripletList = new ArrayList<FontTriplet>(); | |||
public List<FontTriplet> getTripletList() { | |||
@@ -330,7 +333,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 simulateStyle, | |||
boolean embedAsType1) { | |||
boolean embedAsType1, boolean useSVG) { | |||
this.metrics = metrics; | |||
this.embedUri = embed; | |||
this.afm = afm; | |||
@@ -342,6 +345,7 @@ public final class DefaultFontConfig implements FontConfig { | |||
this.embeddingMode = embeddingMode; | |||
this.simulateStyle = simulateStyle; | |||
this.embedAsType1 = embedAsType1; | |||
this.useSVG = useSVG; | |||
} | |||
/** | |||
@@ -399,5 +403,9 @@ public final class DefaultFontConfig implements FontConfig { | |||
public boolean getEmbedAsType1() { | |||
return embedAsType1; | |||
} | |||
public boolean getUseSVG() { | |||
return useSVG; | |||
} | |||
} | |||
} |
@@ -168,7 +168,7 @@ public class DefaultFontConfigurator implements FontConfigurator<EmbedFontInfo> | |||
EmbeddingMode embeddingMode = EmbeddingMode.getValue(font.getEmbeddingMode()); | |||
EmbedFontInfo embedFontInfo = new EmbedFontInfo(fontUris, font.isKerning(), | |||
font.isAdvanced(), tripletList, subFont, encodingMode, embeddingMode, | |||
font.getSimulateStyle(), font.getEmbedAsType1()); | |||
font.getSimulateStyle(), font.getEmbedAsType1(), font.getUseSVG()); | |||
if (fontCache != null) { | |||
if (!fontCache.containsFont(embedFontInfo)) { | |||
fontCache.addFont(embedFontInfo, resourceResolver); |
@@ -45,6 +45,7 @@ public class EmbedFontInfo implements Serializable { | |||
/** simulates bold or italic on a regular font */ | |||
private final boolean simulateStyle; | |||
private final boolean embedAsType1; | |||
private final boolean useSVG; | |||
/** the PostScript name of the font */ | |||
protected String postScriptName; | |||
@@ -69,7 +70,7 @@ public class EmbedFontInfo implements Serializable { | |||
*/ | |||
public EmbedFontInfo(FontUris fontUris, boolean kerning, boolean advanced, | |||
List<FontTriplet> fontTriplets, String subFontName, EncodingMode encodingMode, | |||
EmbeddingMode embeddingMode, boolean simulateStyle, boolean embedAsType1) { | |||
EmbeddingMode embeddingMode, boolean simulateStyle, boolean embedAsType1, boolean useSVG) { | |||
this.kerning = kerning; | |||
this.advanced = advanced; | |||
this.fontTriplets = fontTriplets; | |||
@@ -79,6 +80,7 @@ public class EmbedFontInfo implements Serializable { | |||
this.fontUris = fontUris; | |||
this.simulateStyle = simulateStyle; | |||
this.embedAsType1 = embedAsType1; | |||
this.useSVG = useSVG; | |||
} | |||
/** | |||
@@ -91,7 +93,7 @@ public class EmbedFontInfo implements Serializable { | |||
public EmbedFontInfo(FontUris fontUris, boolean kerning, boolean advanced, | |||
List<FontTriplet> fontTriplets, String subFontName) { | |||
this(fontUris, kerning, advanced, fontTriplets, subFontName, EncodingMode.AUTO, | |||
EmbeddingMode.AUTO, false, false); | |||
EmbeddingMode.AUTO, false, false, true); | |||
} | |||
/** | |||
@@ -209,6 +211,10 @@ public class EmbedFontInfo implements Serializable { | |||
return embedAsType1; | |||
} | |||
public boolean getUseSVG() { | |||
return useSVG; | |||
} | |||
private void readObject(java.io.ObjectInputStream in) | |||
throws IOException, ClassNotFoundException { | |||
in.defaultReadObject(); |
@@ -93,7 +93,7 @@ public abstract class FontLoader { | |||
public static CustomFont loadFont(FontUris fontUris, String subFontName, | |||
boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode, | |||
boolean useKerning, boolean useAdvanced, InternalResourceResolver resourceResolver, | |||
boolean simulateStyle, boolean embedAsType1) throws IOException { | |||
boolean simulateStyle, boolean embedAsType1, boolean useSVG) throws IOException { | |||
boolean type1 = isType1(fontUris); | |||
FontLoader loader; | |||
if (type1) { | |||
@@ -105,7 +105,7 @@ public abstract class FontLoader { | |||
resourceResolver); | |||
} else { | |||
loader = new OFFontLoader(fontUris.getEmbed(), subFontName, embedded, embeddingMode, | |||
encodingMode, useKerning, useAdvanced, resourceResolver, simulateStyle, embedAsType1); | |||
encodingMode, useKerning, useAdvanced, resourceResolver, simulateStyle, embedAsType1, useSVG); | |||
} | |||
return loader.getFont(); | |||
} |
@@ -47,6 +47,7 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable, | |||
private final boolean useAdvanced; | |||
private boolean simulateStyle; | |||
private boolean embedAsType1; | |||
private boolean useSVG; | |||
private final EncodingMode encodingMode; | |||
private final EmbeddingMode embeddingMode; | |||
private final String subFontName; | |||
@@ -74,6 +75,7 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable, | |||
} | |||
this.simulateStyle = fontInfo.getSimulateStyle(); | |||
this.embedAsType1 = fontInfo.getEmbedAsType1(); | |||
useSVG = fontInfo.getUseSVG(); | |||
this.encodingMode = fontInfo.getEncodingMode() != null ? fontInfo.getEncodingMode() | |||
: EncodingMode.AUTO; | |||
this.embeddingMode = fontInfo.getEmbeddingMode() != null ? fontInfo.getEmbeddingMode() | |||
@@ -116,8 +118,8 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable, | |||
if (fontUris.getEmbed() == null) { | |||
throw new RuntimeException("Cannot load font. No font URIs available."); | |||
} | |||
realFont = FontLoader.loadFont(fontUris, subFontName, embedded, embeddingMode, | |||
encodingMode, useKerning, useAdvanced, resourceResolver, simulateStyle, embedAsType1); | |||
realFont = FontLoader.loadFont(fontUris, subFontName, embedded, embeddingMode, encodingMode, | |||
useKerning, useAdvanced, resourceResolver, simulateStyle, embedAsType1, useSVG); | |||
} | |||
if (realFont instanceof FontDescriptor) { | |||
realFontDescriptor = (FontDescriptor) realFont; |
@@ -42,6 +42,7 @@ import org.apache.fop.complexscripts.fonts.Substitutable; | |||
import org.apache.fop.complexscripts.util.CharAssociation; | |||
import org.apache.fop.complexscripts.util.CharNormalize; | |||
import org.apache.fop.complexscripts.util.GlyphSequence; | |||
import org.apache.fop.fonts.truetype.SVGGlyphData; | |||
import org.apache.fop.util.CharUtilities; | |||
/** | |||
@@ -857,5 +858,10 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl | |||
public InputStream getCmapStream() { | |||
return null; | |||
} | |||
public SVGGlyphData getSVG(char c) { | |||
int gid = findGlyphIndex(c); | |||
return svgs.get(gid); | |||
} | |||
} | |||
@@ -223,7 +223,7 @@ public class FontInfoFinder { | |||
try { | |||
OFFontLoader ttfLoader = new OFFontLoader(fontURI, fontName, true, | |||
EmbeddingMode.AUTO, EncodingMode.AUTO, useKerning, useAdvanced, | |||
resourceResolver, false, false); | |||
resourceResolver, false, false, true); | |||
customFont = ttfLoader.getFont(); | |||
if (this.eventListener != null) { | |||
customFont.setEventListener(this.eventListener); | |||
@@ -251,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, false, false); | |||
useKerning, useAdvanced, resourceResolver, false, false, true); | |||
if (this.eventListener != null) { | |||
customFont.setEventListener(this.eventListener); | |||
} |
@@ -55,6 +55,7 @@ public class OFFontLoader extends FontLoader { | |||
private EmbeddingMode embeddingMode; | |||
private boolean simulateStyle; | |||
private boolean embedAsType1; | |||
private boolean useSVG; | |||
/** | |||
* Default constructor | |||
@@ -63,7 +64,7 @@ public class OFFontLoader extends FontLoader { | |||
*/ | |||
public OFFontLoader(URI fontFileURI, InternalResourceResolver resourceResolver) { | |||
this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, true, resourceResolver, false, | |||
false); | |||
false, true); | |||
} | |||
/** | |||
@@ -79,15 +80,16 @@ public class OFFontLoader extends FontLoader { | |||
* @param resolver the FontResolver for font URI resolution | |||
* @param simulateStyle Determines whether to simulate font styles if a font does not support those by default. | |||
*/ | |||
public OFFontLoader(URI fontFileURI, String subFontName, boolean embedded, | |||
EmbeddingMode embeddingMode, EncodingMode encodingMode, boolean useKerning, | |||
boolean useAdvanced, InternalResourceResolver resolver, boolean simulateStyle, boolean embedAsType1) { | |||
public OFFontLoader(URI fontFileURI, String subFontName, boolean embedded, EmbeddingMode embeddingMode, | |||
EncodingMode encodingMode, boolean useKerning, boolean useAdvanced, InternalResourceResolver resolver, | |||
boolean simulateStyle, boolean embedAsType1, boolean useSVG) { | |||
super(fontFileURI, embedded, useKerning, useAdvanced, resolver); | |||
this.subFontName = subFontName; | |||
this.encodingMode = encodingMode; | |||
this.embeddingMode = embeddingMode; | |||
this.simulateStyle = simulateStyle; | |||
this.embedAsType1 = embedAsType1; | |||
this.useSVG = useSVG; | |||
if (this.encodingMode == EncodingMode.AUTO) { | |||
this.encodingMode = EncodingMode.CID; //Default to CID mode for TrueType | |||
} | |||
@@ -118,7 +120,7 @@ public class OFFontLoader extends FontLoader { | |||
if (!supported) { | |||
throw new IOException("The font does not have a Unicode cmap table: " + fontFileURI); | |||
} | |||
buildFont(otf, ttcFontName, embedAsType1); | |||
buildFont(otf, ttcFontName); | |||
loaded = true; | |||
} finally { | |||
IOUtils.closeQuietly(in); | |||
@@ -133,7 +135,7 @@ public class OFFontLoader extends FontLoader { | |||
return null; | |||
} | |||
private void buildFont(OpenFont otf, String ttcFontName, boolean embedAsType1) { | |||
private void buildFont(OpenFont otf, String ttcFontName) { | |||
boolean isCid = this.embedded; | |||
if (this.encodingMode == EncodingMode.SINGLE_BYTE) { | |||
isCid = false; | |||
@@ -201,6 +203,9 @@ public class OFFontLoader extends FontLoader { | |||
copyGlyphMetricsSingleByte(otf); | |||
} | |||
returnFont.setCMap(getCMap(otf)); | |||
if (useSVG) { | |||
returnFont.setSVG(otf.svgs); | |||
} | |||
if (otf.getKerning() != null && useKerning) { | |||
copyKerning(otf, isCid); |
@@ -119,6 +119,8 @@ public final class OFTableName { | |||
/** Vertical Metrics. */ | |||
public static final OFTableName VMTX = new OFTableName("vmtx"); | |||
public static final OFTableName SVG = new OFTableName("SVG "); | |||
private final String name; | |||
private OFTableName(String name) { |
@@ -31,6 +31,7 @@ import java.util.Comparator; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.Iterator; | |||
import java.util.LinkedHashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Map.Entry; | |||
@@ -219,6 +220,7 @@ public abstract class OpenFont { | |||
private int[] ansiWidth; | |||
private Map<Integer, List<Integer>> ansiIndex; | |||
protected Map<Integer, SVGGlyphData> svgs; | |||
// internal mapping of glyph indexes to unicode indexes | |||
// used for quick mappings in this class | |||
@@ -839,6 +841,7 @@ public abstract class OpenFont { | |||
readPostScript(); | |||
readOS2(); | |||
determineAscDesc(); | |||
readSVG(); | |||
readName(); | |||
boolean pcltFound = readPCLT(); | |||
@@ -1329,6 +1332,33 @@ public abstract class OpenFont { | |||
} | |||
} | |||
private void readSVG() throws IOException { | |||
OFDirTabEntry dirTab = dirTabs.get(OFTableName.SVG); | |||
if (dirTab != null) { | |||
svgs = new LinkedHashMap<>(); | |||
fontFile.seekSet(dirTab.getOffset()); | |||
fontFile.readTTFUShort(); //version | |||
fontFile.readTTFULong(); //svgDocumentListOffset | |||
fontFile.readTTFULong(); //reserved | |||
int numEntries = fontFile.readTTFUShort(); | |||
for (int i = 0; i < numEntries; i++) { | |||
int startGlyphID = fontFile.readTTFUShort(); | |||
fontFile.readTTFUShort(); //endGlyphID | |||
SVGGlyphData svgGlyph = new SVGGlyphData(); | |||
svgGlyph.svgDocOffset = fontFile.readTTFULong(); | |||
svgGlyph.svgDocLength = fontFile.readTTFULong(); | |||
svgs.put(startGlyphID, svgGlyph); | |||
} | |||
for (SVGGlyphData entry : svgs.values()) { | |||
seekTab(fontFile, OFTableName.SVG, entry.svgDocOffset); | |||
fontFile.readTTFUShort(); //version | |||
fontFile.readTTFULong(); //svgDocumentListOffset | |||
fontFile.readTTFULong(); //reserved | |||
entry.setSVG(fontFile.readTTFString((int) entry.svgDocLength)); | |||
} | |||
} | |||
} | |||
/** | |||
* Read "hmtx" table and put the horizontal metrics | |||
* in the mtxTab array. If the number of metrics is less |
@@ -0,0 +1,100 @@ | |||
/* | |||
* 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.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import java.io.StringReader; | |||
import java.io.StringWriter; | |||
import javax.xml.parsers.DocumentBuilder; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
import javax.xml.parsers.ParserConfigurationException; | |||
import javax.xml.transform.Transformer; | |||
import javax.xml.transform.TransformerException; | |||
import javax.xml.transform.TransformerFactory; | |||
import javax.xml.transform.dom.DOMSource; | |||
import javax.xml.transform.stream.StreamResult; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Element; | |||
import org.w3c.dom.Node; | |||
import org.w3c.dom.NodeList; | |||
import org.xml.sax.InputSource; | |||
import org.xml.sax.SAXException; | |||
import org.apache.xmlgraphics.util.uri.DataURLUtil; | |||
public class SVGGlyphData { | |||
protected long svgDocOffset; | |||
protected long svgDocLength; | |||
private String svg; | |||
public float scale = 1; | |||
public void setSVG(String svg) { | |||
this.svg = svg; | |||
} | |||
public String getDataURL(int height) { | |||
try { | |||
String modifiedSVG = updateTransform(svg, height); | |||
return DataURLUtil.createDataURL(new ByteArrayInputStream(modifiedSVG.getBytes("UTF-8")), "image/svg"); | |||
} catch (IOException | TransformerException | SAXException | ParserConfigurationException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
private String updateTransform(String svg, int height) | |||
throws IOException, ParserConfigurationException, SAXException, TransformerException { | |||
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); | |||
InputSource inputSource = new InputSource(); | |||
inputSource.setCharacterStream(new StringReader(svg)); | |||
Document doc = documentBuilder.parse(inputSource); | |||
NodeList nodes = doc.getElementsByTagName("g"); | |||
Element gElement = (Element) nodes.item(0); | |||
if (gElement != null) { | |||
String transform = gElement.getAttribute("transform"); | |||
if (transform.contains("scale(")) { | |||
String scaleStr = transform.split("scale\\(")[1].split("\\)")[0].trim(); | |||
scale = Float.parseFloat(scaleStr); | |||
gElement.removeAttribute("transform"); | |||
} else { | |||
gElement.setAttribute("transform", "translate(0," + height + ")"); | |||
} | |||
} else { | |||
Element svgElement = (Element) doc.getElementsByTagName("svg").item(0); | |||
svgElement.setAttribute("viewBox", "0 0 1000 " + height); | |||
gElement = doc.createElement("g"); | |||
gElement.setAttribute("transform", "translate(0," + height + ")"); | |||
NodeList paths = doc.getElementsByTagName("path"); | |||
for (int i = 0; i < paths.getLength(); i++) { | |||
Node path = paths.item(i); | |||
if (i == 0) { | |||
path.getParentNode().insertBefore(gElement, path); | |||
} | |||
gElement.appendChild(path); | |||
} | |||
} | |||
Transformer transformer = TransformerFactory.newInstance().newTransformer(); | |||
StreamResult result = new StreamResult(new StringWriter()); | |||
DOMSource source = new DOMSource(doc); | |||
transformer.transform(source, result); | |||
return result.getWriter().toString(); | |||
} | |||
} |
@@ -384,7 +384,7 @@ public final class AFPFontConfig implements FontConfig { | |||
try { | |||
FontUris fontUris = new FontUris(new URI(fontUri), null); | |||
EmbedFontInfo embedFontInfo = new EmbedFontInfo(fontUris, false, true, null, subfont, EncodingMode.AUTO, | |||
EmbeddingMode.FULL, false, false); | |||
EmbeddingMode.FULL, false, false, true); | |||
Typeface tf = new LazyFont(embedFontInfo, resourceResolver, false).getRealFont(); | |||
AFPResourceAccessor accessor = getAccessor(resourceResolver); | |||
CharacterSet characterSet = CharacterSetBuilder.getDoubleByteInstance().build(characterset, |
@@ -46,8 +46,11 @@ import org.apache.xmlgraphics.image.loader.util.ImageUtil; | |||
import org.apache.fop.ResourceEventProducer; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.fo.Constants; | |||
import org.apache.fop.fonts.Font; | |||
import org.apache.fop.fonts.FontInfo; | |||
import org.apache.fop.fonts.FontTriplet; | |||
import org.apache.fop.fonts.MultiByteFont; | |||
import org.apache.fop.fonts.truetype.SVGGlyphData; | |||
import org.apache.fop.render.ImageHandler; | |||
import org.apache.fop.render.ImageHandlerRegistry; | |||
import org.apache.fop.render.ImageHandlerUtil; | |||
@@ -469,4 +472,25 @@ public abstract class AbstractIFPainter<T extends IFDocumentHandler> implements | |||
int[][] dp, String text, boolean nextIsSpace) throws IFException { | |||
drawText(x, y, letterSpacing, wordSpacing, dp, text); | |||
} | |||
protected void drawSVGText(MultiByteFont multiByteFont, FontTriplet triplet, int x, int y, String text, | |||
IFState state) throws IFException { | |||
int sizeMillipoints = state.getFontSize(); | |||
Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints); | |||
int newx = x; | |||
for (char c : text.toCharArray()) { | |||
SVGGlyphData svg = multiByteFont.getSVG(c); | |||
if (svg != null) { | |||
int codePoint = font.mapCodePoint(c); | |||
String dataURL = svg.getDataURL(multiByteFont.getCapHeight()); | |||
Rectangle boundingBox = multiByteFont.getBoundingBox(codePoint, (int) (sizeMillipoints / 1000f)); | |||
boundingBox.y = y - boundingBox.height - boundingBox.y; | |||
boundingBox.x = newx; | |||
boundingBox.width = (int) (sizeMillipoints * svg.scale); | |||
boundingBox.height = (int) (sizeMillipoints * svg.scale); | |||
drawImage(dataURL, boundingBox); | |||
} | |||
newx += font.getCharWidth(c); | |||
} | |||
} | |||
} |
@@ -83,11 +83,11 @@ public class ConfiguredFontCollection implements FontCollection { | |||
font = new CustomFontMetricsMapper(fontMetrics, fontSource); | |||
} else { | |||
FontUris fontUris = configFontInfo.getFontUris(); | |||
CustomFont fontMetrics = FontLoader.loadFont(fontUris, | |||
configFontInfo.getSubFontName(), true, | |||
CustomFont fontMetrics = FontLoader.loadFont(fontUris, configFontInfo.getSubFontName(), true, | |||
configFontInfo.getEmbeddingMode(), configFontInfo.getEncodingMode(), | |||
configFontInfo.getKerning(), configFontInfo.getAdvanced(), resourceResolver, | |||
configFontInfo.getSimulateStyle(), configFontInfo.getEmbedAsType1()); | |||
configFontInfo.getSimulateStyle(), configFontInfo.getEmbedAsType1(), | |||
configFontInfo.getUseSVG()); | |||
font = new CustomFontMetricsMapper(fontMetrics); | |||
} | |||
@@ -43,6 +43,7 @@ import org.apache.fop.fonts.CustomFont; | |||
import org.apache.fop.fonts.Font; | |||
import org.apache.fop.fonts.FontTriplet; | |||
import org.apache.fop.fonts.LazyFont; | |||
import org.apache.fop.fonts.MultiByteFont; | |||
import org.apache.fop.fonts.SingleByteFont; | |||
import org.apache.fop.fonts.Typeface; | |||
import org.apache.fop.pdf.PDFArray; | |||
@@ -431,8 +432,11 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> { | |||
FontTriplet triplet = new FontTriplet( | |||
state.getFontFamily(), state.getFontStyle(), state.getFontWeight()); | |||
if ((dp == null) || IFUtil.isDPOnlyDX(dp)) { | |||
String fontKey = getFontInfo().getInternalFontKey(triplet); | |||
Typeface typeface = getTypeface(fontKey); | |||
if (typeface instanceof MultiByteFont && ((MultiByteFont) typeface).hasSVG()) { | |||
drawSVGText((MultiByteFont) typeface, triplet, x, y, text, state); | |||
} else if ((dp == null) || IFUtil.isDPOnlyDX(dp)) { | |||
drawTextWithDX(x, y, text, triplet, letterSpacing, | |||
wordSpacing, IFUtil.convertDPToDX(dp)); | |||
} else { |
@@ -21,6 +21,7 @@ package org.apache.fop.render.ps; | |||
import java.awt.Color; | |||
import java.awt.Dimension; | |||
import java.awt.Graphics2D; | |||
import java.awt.Rectangle; | |||
import java.awt.geom.AffineTransform; | |||
import java.awt.geom.Rectangle2D; | |||
@@ -43,14 +44,11 @@ import org.w3c.dom.NodeList; | |||
import org.apache.batik.bridge.BridgeContext; | |||
import org.apache.batik.bridge.GVTBuilder; | |||
import org.apache.batik.gvt.GraphicsNode; | |||
import org.apache.batik.transcoder.SVGAbstractTranscoder; | |||
import org.apache.batik.transcoder.TranscoderException; | |||
import org.apache.batik.transcoder.TranscoderInput; | |||
import org.apache.batik.transcoder.TranscoderOutput; | |||
import org.apache.batik.transcoder.image.PNGTranscoder; | |||
import org.apache.xmlgraphics.image.loader.Image; | |||
import org.apache.xmlgraphics.image.loader.ImageException; | |||
import org.apache.xmlgraphics.image.loader.ImageFlavor; | |||
import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; | |||
import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; | |||
import org.apache.xmlgraphics.ps.ImageEncoder; | |||
import org.apache.xmlgraphics.ps.ImageEncodingHelper; | |||
@@ -58,6 +56,7 @@ import org.apache.xmlgraphics.ps.PSGenerator; | |||
import org.apache.fop.image.loader.batik.BatikImageFlavors; | |||
import org.apache.fop.image.loader.batik.BatikUtil; | |||
import org.apache.fop.image.loader.batik.ImageConverterSVG2G2D; | |||
import org.apache.fop.render.ImageHandler; | |||
import org.apache.fop.render.RenderingContext; | |||
import org.apache.fop.render.ps.svg.PSSVGGraphics2D; | |||
@@ -85,7 +84,7 @@ public class PSImageHandlerSVG implements ImageHandler { | |||
ImageXMLDOM imageSVG = (ImageXMLDOM)image; | |||
if (shouldRaster(imageSVG)) { | |||
InputStream is = renderSVGToInputStream(context, imageSVG); | |||
InputStream is = renderSVGToInputStream(imageSVG, pos); | |||
float x = (float) pos.getX() / 1000f; | |||
float y = (float) pos.getY() / 1000f; | |||
@@ -175,25 +174,35 @@ public class PSImageHandlerSVG implements ImageHandler { | |||
} | |||
} | |||
private InputStream renderSVGToInputStream(RenderingContext context, ImageXMLDOM imageSVG) throws IOException { | |||
PNGTranscoder png = new PNGTranscoder(); | |||
Float width = getDimension(imageSVG.getDocument(), "width") * 8; | |||
png.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH, width); | |||
Float height = getDimension(imageSVG.getDocument(), "height") * 8; | |||
png.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT, height); | |||
TranscoderInput input = new TranscoderInput(imageSVG.getDocument()); | |||
ByteArrayOutputStream os = new ByteArrayOutputStream(); | |||
TranscoderOutput output = new TranscoderOutput(os); | |||
private InputStream renderSVGToInputStream(ImageXMLDOM imageSVG, Rectangle destinationRect) | |||
throws IOException { | |||
Rectangle rectangle; | |||
int width; | |||
int height; | |||
Float widthSVG = getDimension(imageSVG.getDocument(), "width"); | |||
Float heightSVG = getDimension(imageSVG.getDocument(), "height"); | |||
if (widthSVG != null && heightSVG != null) { | |||
width = (int) (widthSVG * 8); | |||
height = (int) (heightSVG * 8); | |||
rectangle = new Rectangle(0, 0, width, height); | |||
} else { | |||
int scale = 10; | |||
width = destinationRect.width / scale; | |||
height = destinationRect.height / scale; | |||
rectangle = new Rectangle(0, 0, destinationRect.width / 100, destinationRect.height / 100); | |||
destinationRect.width *= scale; | |||
destinationRect.height *= scale; | |||
} | |||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | |||
Graphics2D graphics2D = image.createGraphics(); | |||
try { | |||
png.transcode(input, output); | |||
} catch (TranscoderException ex) { | |||
SVGEventProducer eventProducer = SVGEventProducer.Provider.get( | |||
context.getUserAgent().getEventBroadcaster()); | |||
eventProducer.svgRenderingError(this, ex, imageSVG.getInfo().getOriginalURI()); | |||
} finally { | |||
os.flush(); | |||
os.close(); | |||
ImageGraphics2D img = (ImageGraphics2D) new ImageConverterSVG2G2D().convert(imageSVG, new HashMap()); | |||
img.getGraphics2DImagePainter().paint(graphics2D, rectangle); | |||
} catch (ImageException e) { | |||
throw new IOException(e); | |||
} | |||
ByteArrayOutputStream os = new ByteArrayOutputStream(); | |||
ImageIO.write(image, "png", os); | |||
return new ByteArrayInputStream(os.toByteArray()); | |||
} | |||
@@ -359,25 +359,28 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> { | |||
} | |||
PSGenerator generator = getGenerator(); | |||
generator.useColor(state.getTextColor()); | |||
FontTriplet triplet = new FontTriplet(state.getFontFamily(), state.getFontStyle(), state.getFontWeight()); | |||
String fontKey = getFontKey(triplet); | |||
Typeface typeface = getTypeface(fontKey); | |||
if (typeface instanceof MultiByteFont && ((MultiByteFont) typeface).hasSVG()) { | |||
drawSVGText((MultiByteFont) typeface, triplet, x, y, text, state); | |||
return; | |||
} | |||
beginTextObject(); | |||
FontTriplet triplet = new FontTriplet( | |||
state.getFontFamily(), state.getFontStyle(), state.getFontWeight()); | |||
//TODO Ignored: state.getFontVariant() | |||
//TODO Opportunity for font caching if font state is more heavily used | |||
String fontKey = getFontKey(triplet); | |||
int sizeMillipoints = state.getFontSize(); | |||
// This assumes that *all* CIDFonts use a /ToUnicode mapping | |||
Typeface tf = getTypeface(fontKey); | |||
SingleByteFont singleByteFont = null; | |||
if (tf instanceof SingleByteFont) { | |||
singleByteFont = (SingleByteFont)tf; | |||
if (typeface instanceof SingleByteFont) { | |||
singleByteFont = (SingleByteFont)typeface; | |||
} | |||
Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints); | |||
PSFontResource res = getDocumentHandler().getPSResourceForFontKey(fontKey); | |||
boolean otf = tf instanceof MultiByteFont && ((MultiByteFont)tf).isOTFFile(); | |||
useFont(fontKey, sizeMillipoints, otf); | |||
boolean isOpenTypeFont = typeface instanceof MultiByteFont && ((MultiByteFont)typeface).isOTFFile(); | |||
useFont(fontKey, sizeMillipoints, isOpenTypeFont); | |||
if (dp != null && dp[0] != null) { | |||
x += dp[0][0]; | |||
@@ -393,12 +396,12 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> { | |||
int currentEncoding = -1; | |||
for (int i = 0; i < textLen; i++) { | |||
char c = text.charAt(i); | |||
char mapped = tf.mapChar(c); | |||
char mapped = typeface.mapChar(c); | |||
int encoding = mapped / 256; | |||
if (currentEncoding != encoding) { | |||
if (i > 0) { | |||
writeText(text, start, i - start, | |||
letterSpacing, wordSpacing, dp, font, tf, false); | |||
letterSpacing, wordSpacing, dp, font, typeface, false); | |||
} | |||
if (encoding == 0) { | |||
useFont(fontKey, sizeMillipoints, false); | |||
@@ -410,20 +413,20 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> { | |||
} | |||
} | |||
} else { | |||
if (tf instanceof MultiByteFont && ((MultiByteFont)tf).isOTFFile()) { | |||
if (typeface instanceof MultiByteFont && ((MultiByteFont)typeface).isOTFFile()) { | |||
//Analyze string and split up in order to paint in different sub-fonts/encodings | |||
int curEncoding = 0; | |||
for (int i = start; i < textLen; i++) { | |||
char orgChar = text.charAt(i); | |||
MultiByteFont mbFont = (MultiByteFont)tf; | |||
MultiByteFont mbFont = (MultiByteFont)typeface; | |||
mbFont.mapChar(orgChar); | |||
int origGlyphIdx = mbFont.findGlyphIndex(orgChar); | |||
int newGlyphIdx = mbFont.getUsedGlyphs().get(origGlyphIdx); | |||
int encoding = newGlyphIdx / 256; | |||
if (encoding != curEncoding) { | |||
if (i != 0) { | |||
writeText(text, start, i - start, letterSpacing, wordSpacing, dp, font, tf, | |||
writeText(text, start, i - start, letterSpacing, wordSpacing, dp, font, typeface, | |||
true); | |||
start = i; | |||
} | |||
@@ -435,8 +438,8 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> { | |||
useFont(fontKey, sizeMillipoints, false); | |||
} | |||
} | |||
writeText(text, start, textLen - start, letterSpacing, wordSpacing, dp, font, tf, | |||
tf instanceof MultiByteFont); | |||
writeText(text, start, textLen - start, letterSpacing, wordSpacing, dp, font, typeface, | |||
typeface instanceof MultiByteFont); | |||
} catch (IOException ioe) { | |||
throw new IFException("I/O error in drawText()", ioe); | |||
} |
@@ -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); | |||
false, false, resolver, false, false, true); | |||
} | |||
/** |
@@ -54,7 +54,7 @@ public class EmbedFontInfoTestCase { | |||
triplets.add(triplet); | |||
FontUris fontUris = new FontUris(embedURI, metricsURI); | |||
sut = new EmbedFontInfo(fontUris, kerning, useAdvanced, triplets, subFontName, encMode, | |||
embedMode, false, false); | |||
embedMode, false, false, true); | |||
} | |||
@Test |
@@ -78,7 +78,7 @@ public class OTFToType1TestCase { | |||
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, false, true); | |||
EmbeddingMode.SUBSET, EncodingMode.AUTO, true, true, rr, false, true, true); | |||
realFont.mapChar('d'); | |||
return realFont; | |||
} |
@@ -51,12 +51,12 @@ public class TTFFontLoaderTestCase { | |||
OFFontLoader fontLoader = new OFFontLoader(absoluteFilePath, fontName, embedded, | |||
EmbeddingMode.AUTO, EncodingMode.AUTO, useKerning, useComplexScriptFeatures, | |||
resourceResolver, false, false); | |||
resourceResolver, false, false, true); | |||
assertTrue(fontLoader.getFont().hasKerningInfo()); | |||
useKerning = false; | |||
fontLoader = new OFFontLoader(absoluteFilePath, fontName, embedded, EmbeddingMode.AUTO, | |||
EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resourceResolver, false, false); | |||
EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resourceResolver, false, false, true); | |||
assertFalse(fontLoader.getFont().hasKerningInfo()); | |||
} | |||
} |
@@ -29,6 +29,7 @@ import org.junit.Test; | |||
import org.apache.commons.io.IOUtils; | |||
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; | |||
@@ -50,8 +51,10 @@ public class Type1FontLoaderTestCase { | |||
fos.close(); | |||
FontUris fontUris = new FontUris(pfbNoAFM.toURI(), null, null, pfm.toURI()); | |||
InternalResourceResolver resourceResolver = | |||
ResourceResolverFactory.createDefaultInternalResourceResolver(new File(".").toURI()); | |||
CustomFont x = FontLoader.loadFont(fontUris, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, true, | |||
ResourceResolverFactory.createDefaultInternalResourceResolver(new File(".").toURI()), false, false); | |||
resourceResolver, false, false, true); | |||
Assert.assertEquals(x.getBoundingBox(0, 12).getBounds(), new Rectangle(-240, -60, 0, 60)); | |||
} finally { | |||
pfbNoAFM.delete(); |
@@ -144,7 +144,7 @@ public class PDFFactoryTestCase { | |||
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); | |||
null, true, EmbeddingMode.SUBSET, null, false, false, rr, false, false, true); | |||
for (char c = 0; c < 512; c++) { | |||
sb.mapChar(c); | |||
} |
@@ -25,7 +25,9 @@ import java.awt.Rectangle; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.HashMap; | |||
import java.util.Locale; | |||
import java.util.Map; | |||
import javax.xml.transform.stream.StreamResult; | |||
@@ -49,6 +51,7 @@ import org.apache.fop.fo.Constants; | |||
import org.apache.fop.fonts.FontInfo; | |||
import org.apache.fop.fonts.FontTriplet; | |||
import org.apache.fop.fonts.MultiByteFont; | |||
import org.apache.fop.fonts.truetype.SVGGlyphData; | |||
import org.apache.fop.pdf.PDFDocument; | |||
import org.apache.fop.pdf.PDFFilterList; | |||
import org.apache.fop.pdf.PDFPage; | |||
@@ -305,4 +308,37 @@ public class PDFPainterTestCase { | |||
+ "\n" | |||
+ "endstream"); | |||
} | |||
@Test | |||
public void testSVGFont() throws IFException, IOException { | |||
FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI()); | |||
foUserAgent = fopFactory.newFOUserAgent(); | |||
PDFDocumentHandler pdfDocumentHandler = new PDFDocumentHandler(new IFContext(foUserAgent)); | |||
pdfDocumentHandler.setResult(new StreamResult(new ByteArrayOutputStream())); | |||
pdfDocumentHandler.startDocument(); | |||
pdfDocumentHandler.startPage(0, "", "", new Dimension()); | |||
FontInfo fi = new FontInfo(); | |||
fi.addFontProperties("f1", new FontTriplet("a", "normal", 400)); | |||
MultiByteFont font = new MultiByteFont(null, null); | |||
font.setWidthArray(new int[1]); | |||
Map<Integer, SVGGlyphData> svgs = new HashMap<>(); | |||
SVGGlyphData svgGlyph = new SVGGlyphData(); | |||
svgGlyph.setSVG("<svg xmlns=\"http://www.w3.org/2000/svg\">\n" | |||
+ "<circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"black\" stroke-width=\"3\" fill=\"red\" />\n" | |||
+ "</svg>"); | |||
svgs.put(0, svgGlyph); | |||
font.setSVG(svgs); | |||
font.setBBoxArray(new Rectangle[] {new Rectangle()}); | |||
fi.addMetrics("f1", font); | |||
pdfDocumentHandler.setFontInfo(fi); | |||
PDFPainter pdfPainter = new PDFPainter(pdfDocumentHandler, null); | |||
pdfPainter.setFont("a", "normal", 400, null, 12, null); | |||
pdfPainter.drawText(0, 0, 0, 0, null, "test"); | |||
PDFFilterList filters = pdfPainter.generator.getStream().getFilterList(); | |||
filters.setDisableAllFilters(true); | |||
ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |||
pdfPainter.generator.getStream().output(bos); | |||
Assert.assertTrue(bos.toString().contains("0.00012 0 0 0.00012 0 0 cm")); | |||
Assert.assertTrue(bos.toString().contains("1 0 0 rg")); | |||
} | |||
} |
@@ -17,6 +17,7 @@ | |||
package org.apache.fop.render.ps; | |||
import java.awt.Color; | |||
import java.awt.Dimension; | |||
import java.awt.Rectangle; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
@@ -54,6 +55,7 @@ import org.apache.fop.fonts.FontInfo; | |||
import org.apache.fop.fonts.FontTriplet; | |||
import org.apache.fop.fonts.MultiByteFont; | |||
import org.apache.fop.fonts.Typeface; | |||
import org.apache.fop.fonts.truetype.SVGGlyphData; | |||
import org.apache.fop.render.intermediate.IFContext; | |||
import org.apache.fop.render.intermediate.IFException; | |||
import org.apache.fop.render.intermediate.IFState; | |||
@@ -242,4 +244,35 @@ public class PSPainterTestCase { | |||
mbf.setIsOTFFile(otf); | |||
fi.addMetrics(name, mbf); | |||
} | |||
@Test | |||
public void testSVGFont() throws IFException { | |||
FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI()); | |||
FOUserAgent foUserAgent = fopFactory.newFOUserAgent(); | |||
PSDocumentHandler psDocumentHandler = new PSDocumentHandler(new IFContext(foUserAgent)); | |||
ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |||
psDocumentHandler.setResult(new StreamResult(bos)); | |||
psDocumentHandler.startDocument(); | |||
psDocumentHandler.startPage(0, "", "", new Dimension()); | |||
FontInfo fi = new FontInfo(); | |||
fi.addFontProperties("f1", new FontTriplet("a", "normal", 400)); | |||
MultiByteFont font = new MultiByteFont(null, null); | |||
font.setWidthArray(new int[1]); | |||
Map<Integer, SVGGlyphData> svgs = new HashMap<>(); | |||
SVGGlyphData svgGlyph = new SVGGlyphData(); | |||
svgGlyph.setSVG("<svg xmlns=\"http://www.w3.org/2000/svg\">\n" | |||
+ "<circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"black\" stroke-width=\"3\" fill=\"red\" />\n" | |||
+ "</svg>"); | |||
svgs.put(0, svgGlyph); | |||
font.setSVG(svgs); | |||
font.setBBoxArray(new Rectangle[] {new Rectangle()}); | |||
fi.addMetrics("f1", font); | |||
psDocumentHandler.setFontInfo(fi); | |||
PSPainter psPainter = new PSPainter(psDocumentHandler); | |||
psPainter.setFont("a", "normal", 400, null, 12, Color.black); | |||
psPainter.drawText(0, 0, 0, 0, null, "test"); | |||
Assert.assertTrue(bos.toString().contains("%FOPBeginSVG")); | |||
Assert.assertTrue(bos.toString().contains("[0.00012 0 0 0.00012 0 0] CT")); | |||
Assert.assertTrue(bos.toString().contains("1 0 0 RC")); | |||
} | |||
} |
@@ -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, false, false); | |||
EmbeddingMode.AUTO, EncodingMode.AUTO, true, useAdvanced, resolver, false, false, true); | |||
FontMetrics font = fontLoader.getFont(); | |||
registerFont(font, "F" + fontKey++, fontName); | |||
return this; |