diff options
author | Simon Steiner <ssteiner@apache.org> | 2021-01-11 15:00:20 +0000 |
---|---|---|
committer | Simon Steiner <ssteiner@apache.org> | 2021-01-11 15:00:20 +0000 |
commit | a5fa25cf3e6fc98267fe5eaefedf466516c0bc9f (patch) | |
tree | a4c893ea8b018b6d4e56894f117cf505f42b151c /fop-core/src/main | |
parent | 1f2a414bb711508e158f9704cb6b5320cfd848e3 (diff) | |
download | xmlgraphics-fop-a5fa25cf3e6fc98267fe5eaefedf466516c0bc9f.tar.gz xmlgraphics-fop-a5fa25cf3e6fc98267fe5eaefedf466516c0bc9f.zip |
FOP-2994: Support OTF/TTF SVG fonts
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1885366 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'fop-core/src/main')
21 files changed, 275 insertions, 64 deletions
diff --git a/fop-core/src/main/java/org/apache/fop/apps/FOUserAgent.java b/fop-core/src/main/java/org/apache/fop/apps/FOUserAgent.java index ffb92b027..159efe4aa 100644 --- a/fop-core/src/main/java/org/apache/fop/apps/FOUserAgent.java +++ b/fop-core/src/main/java/org/apache/fop/apps/FOUserAgent.java @@ -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; diff --git a/fop-core/src/main/java/org/apache/fop/apps/FopConfParser.java b/fop-core/src/main/java/org/apache/fop/apps/FopConfParser.java index f125dc6a3..642b76f57 100644 --- a/fop-core/src/main/java/org/apache/fop/apps/FopConfParser.java +++ b/fop-core/src/main/java/org/apache/fop/apps/FopConfParser.java @@ -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) diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java b/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java index 4b319f1c1..0607f998e 100644 --- a/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java @@ -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; } diff --git a/fop-core/src/main/java/org/apache/fop/fonts/CustomFont.java b/fop-core/src/main/java/org/apache/fop/fonts/CustomFont.java index 2b659f512..cd026e162 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/CustomFont.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/CustomFont.java @@ -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; + } } 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 a1663756c..2b67dbd2e 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 @@ -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; + } } } 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 c8d1c00e2..6e8c493cd 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 @@ -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); 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 186f098fd..27a0a4db2 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 @@ -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(); 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 06907f403..1e3d4092f 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 @@ -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(); } 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 7b2d46584..753cece8c 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 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; diff --git a/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java b/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java index 3fd780d4e..4f1263ebe 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java @@ -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); + } } 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 e14bb0123..9e8d9e5db 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 @@ -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); } 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 0634957de..895b798f7 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 @@ -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); diff --git a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OFTableName.java b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OFTableName.java index f6264129a..29d0f78ee 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/truetype/OFTableName.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/truetype/OFTableName.java @@ -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) { 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 5076e3064..0d10129f0 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 @@ -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 diff --git a/fop-core/src/main/java/org/apache/fop/fonts/truetype/SVGGlyphData.java b/fop-core/src/main/java/org/apache/fop/fonts/truetype/SVGGlyphData.java new file mode 100644 index 000000000..e46e50074 --- /dev/null +++ b/fop-core/src/main/java/org/apache/fop/fonts/truetype/SVGGlyphData.java @@ -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(); + } +} diff --git a/fop-core/src/main/java/org/apache/fop/render/afp/AFPFontConfig.java b/fop-core/src/main/java/org/apache/fop/render/afp/AFPFontConfig.java index 0967bd1f8..eb6c4a77f 100644 --- a/fop-core/src/main/java/org/apache/fop/render/afp/AFPFontConfig.java +++ b/fop-core/src/main/java/org/apache/fop/render/afp/AFPFontConfig.java @@ -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, diff --git a/fop-core/src/main/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/fop-core/src/main/java/org/apache/fop/render/intermediate/AbstractIFPainter.java index 45996a2f4..44620ff61 100644 --- a/fop-core/src/main/java/org/apache/fop/render/intermediate/AbstractIFPainter.java +++ b/fop-core/src/main/java/org/apache/fop/render/intermediate/AbstractIFPainter.java @@ -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); + } + } } 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 e494ed65c..d019f352a 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 @@ -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); } diff --git a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java index 985e587cb..cae8973ac 100644 --- a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java @@ -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 { diff --git a/fop-core/src/main/java/org/apache/fop/render/ps/PSImageHandlerSVG.java b/fop-core/src/main/java/org/apache/fop/render/ps/PSImageHandlerSVG.java index b034fa79e..9c0a90286 100644 --- a/fop-core/src/main/java/org/apache/fop/render/ps/PSImageHandlerSVG.java +++ b/fop-core/src/main/java/org/apache/fop/render/ps/PSImageHandlerSVG.java @@ -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()); } 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 34f57e42d..a7f0f455f 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 @@ -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); } |