package org.apache.fop.events;
+import java.util.HashSet;
+import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
private Log log;
private boolean skipFatal;
+ private final Set<String> loggedMessages = new HashSet<String>();
+
/**
* Creates an instance logging to the default log category of this class.
*/
if (severity == EventSeverity.INFO) {
log.info(msg);
} else if (severity == EventSeverity.WARN) {
- log.warn(msg);
+ // we want to prevent logging of duplicate messages in situations where they are likely
+ // to occur; for instance, warning related to layout do not repeat (since line number
+ // will be different) and as such we do not try to filter them here; on the other hand,
+ // font related warnings are very likely to repeat and we try to filter them out here;
+ // the same may happen with missing images (but not implemented yet).
+ String eventGroupID = event.getEventGroupID();
+ if (eventGroupID.equals("org.apache.fop.fonts.FontEventProducer")) {
+ if (!loggedMessages.contains(msg)) {
+ loggedMessages.add(msg);
+ log.warn(msg);
+ }
+ } else {
+ log.warn(msg);
+ }
} else if (severity == EventSeverity.ERROR) {
if (event.getParam("e") != null) {
log.error(msg, (Throwable)event.getParam("e"));
assert false;
}
}
-
}
getEventProducer().fontDirectoryNotFound(source, dir);
}
+ /** {@inheritDoc} */
+ public void svgTextStrokedAsShapes(Object source, String fontFamily) {
+ getEventProducer().svgTextStrokedAsShapes(source, fontFamily);
+ }
+
}
* @param dir the directory in the config file
*/
void fontDirectoryNotFound(Object source, String dir);
+
+ /**
+ * The SVG text will be stroked as shapes.
+ * @param source the event source
+ * @param fontFamily the family name of the font that is being stroked
+ */
+ void svgTextStrokedAsShapes(Object source, String fontFamily);
}
/**
* An error occurred trying to find the font directory specified in the config file.
- * @param source the event sourece
+ * @param source the event source
* @param dir the directory in the config file
* @event.severity WARN
*/
void fontDirectoryNotFound(Object source, String dir);
+ /**
+ * The SVG text will be stroked as shapes.
+ * @param source the event source
+ * @param fontFamily the family name of the font that is being stroked
+ * @event.severity WARN
+ */
+ void svgTextStrokedAsShapes(Object source, String fontFamily);
}
<message key="fontSubstituted">Font "{requested}" not found. Substituting with "{effective}".</message>
<message key="fontLoadingErrorAtAutoDetection">Unable to load font file: {fontURL}.[ Reason: {e}]</message>
<message key="glyphNotAvailable">Glyph "{ch}" (0x{ch,hex}[, {ch,glyph-name}]) not available in font "{fontName}".</message>
- <message key="fontDirectoryNotFound">'{dir}' does not exist or is not a directory.</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>
</catalogue>
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
/** look up a font-name to get a font (that implements FontMetrics at least) */
private Map<String, Typeface> fonts = null; //(String = font key)
- /**
- * a collection of missing fonts; used to make sure the user gets
- * a warning for a missing font only once (not every time the font is used)
- */
- private Set<FontTriplet> loggedFontKeys = null;
-
/** Cache for Font instances. */
private Map<FontTriplet, Map<Integer, Font>> fontInstanceCache = null;
return fontTriplets;
}
- private Set<FontTriplet> getLoggedFontKeys() {
- if (loggedFontKeys == null) {
- loggedFontKeys = new HashSet<FontTriplet>();
+ private void notifyFontReplacement(FontTriplet replacedKey, FontTriplet newKey) {
+ if (this.eventListener != null) {
+ this.eventListener.fontSubstituted(this, replacedKey, newKey);
}
- return loggedFontKeys;
}
- private void notifyFontReplacement(FontTriplet replacedKey, FontTriplet newKey) {
- if (!getLoggedFontKeys().contains(replacedKey)) {
- getLoggedFontKeys().add(replacedKey);
- if (this.eventListener != null) {
- this.eventListener.fontSubstituted(this, replacedKey, newKey);
- } else {
- log.warn("Font '" + replacedKey + "' not found. "
- + "Substituting with '" + newKey + "'.");
- }
+ /**
+ * Notify listeners that the SVG text for the given font will be stroked as shapes.
+ * @param fontFamily a SVG font family
+ */
+ public void notifyStrokingSVGTextAsShapes(String fontFamily) {
+ if (this.eventListener != null) {
+ this.eventListener.svgTextStrokedAsShapes(this, fontFamily);
}
}
import java.util.Map.Entry;
import java.util.Set;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
import org.apache.batik.bridge.SVGFontFamily;
import org.apache.batik.gvt.font.GVTFont;
import org.apache.batik.gvt.font.GVTFontFamily;
import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
}
if (gvtFonts != null) {
+ boolean haveInstanceOfSVGFontFamily = false;
for (GVTFontFamily fam : gvtFonts) {
if (fam instanceof SVGFontFamily) {
- return null; //Let Batik paint this text!
+ haveInstanceOfSVGFontFamily = true;
}
String fontFamily = fam.getFamilyName();
if (fontInfo.hasFont(fontFamily, style, weight)) {
firstFontFamily = fontFamily;
}
}
+ // SVG fonts are embedded fonts in the SVG document and are rarely used; however if they
+ // are used but the fonts also exists in the system and are known to FOP then FOP should
+ // use them; then the decision whether Batik should stroke the text should be made after
+ // no matching fonts are found
+ if (fonts.isEmpty() && haveInstanceOfSVGFontFamily) {
+ fontInfo.notifyStrokingSVGTextAsShapes(firstFontFamily);
+ return null; // Let Batik paint this text!
+ }
}
if (fonts.isEmpty()) {
if (firstFontFamily == null) {
//ignore
}
+ public void svgTextStrokedAsShapes(Object source, String fontFamily) {
+ // ignore
+ }
+
};
FontListGenerator listGenerator = new FontListGenerator();
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
+ <action context="Code" dev="MH" type="fix" fixes-bug="52849" due-to="Luis Bernardo">
+ Fixed bug that caused a configured and installed SVG font to stroked,
+ also added an event indicating when fonts are stroked.
+ </action>
<action context="Code" dev="PH" type="fix">
Fix of a bug introduced when merging ImproveAccessibility.
</action>
MimeConstants.MIME_PDF);
}
+ @Test
+ public void testSVGFontStrokedAsShapes() throws FOPException, TransformerException, IOException,
+ SAXException {
+ // svg-fonts.fo embeds two fonts; one that is present in the system and the other is not; the
+ // missing font is stroked as shapes while the fonts that exists is stroked as text
+ InputStream inStream = getClass().getResourceAsStream("svg-fonts.fo");
+ eventsTests.doTest(inStream, null, FontEventProducer.class.getName() + ".svgTextStrokedAsShapes",
+ MimeConstants.MIME_PDF);
+ }
+
}
--- /dev/null
+<?xml version="1.0" standalone="no"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="page">
+ <fo:region-body />
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="page">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>
+ <fo:instream-foreign-object>
+ <svg:svg width="250" height="50">
+ <svg:font horiz-adv-x="1000">
+ <svg:font-face font-family="Missing" units-per-em="1000" underline-position="-100"
+ underline-thickness="50" />
+ <svg:glyph unicode="A" horiz-adv-x="686" d="M162,186l362,0l78,-186l84,0l-308,708l-70,0l-308,-708l84,0M343,624l153,-372l-307,0z" />
+ <svg:glyph unicode="C" horiz-adv-x="704" d="M620,154C567,72 491,48 417,48C235,48 126,191 126,354C126,517 235,660 417,660C492,660 571,613 599,567l63,47C600,693 505,726 417,726C206,726 48,569 48,354C48,139 206,-18 417,-18C534,-18 632,39 679,112z" />
+ <svg:glyph unicode="F" horiz-adv-x="556" d="M168,335l330,0l0,66l-330,0l0,241l355,0l0,66l-427,0l0,-708l72,0z" />
+ <svg:glyph unicode="G" horiz-adv-x="778" d="M673,631C610,694 529,726 417,726C206,726 48,569 48,354C48,139 206,-18 417,-18C503,-18 606,7 685,54l0,347l-241,0l0,-66l169,0l0,-237C560,68 490,48 417,48C235,48 126,191 126,354C126,517 235,660 417,660C504,660 571,629 619,578z" />
+ </svg:font>
+ <svg:font horiz-adv-x="1000">
+ <!-- this is not Helvetica but it is here to increase coverage and show the code takes expected path -->
+ <svg:font-face font-family="Helvetica" units-per-em="1000" underline-position="-100"
+ underline-thickness="50" />
+ <svg:glyph unicode="A" horiz-adv-x="686" d="M162,186l362,0l78,-186l84,0l-308,708l-70,0l-308,-708l84,0M343,624l153,-372l-307,0z" />
+ <svg:glyph unicode="C" horiz-adv-x="704" d="M620,154C567,72 491,48 417,48C235,48 126,191 126,354C126,517 235,660 417,660C492,660 571,613 599,567l63,47C600,693 505,726 417,726C206,726 48,569 48,354C48,139 206,-18 417,-18C534,-18 632,39 679,112z" />
+ <svg:glyph unicode="F" horiz-adv-x="556" d="M168,335l330,0l0,66l-330,0l0,241l355,0l0,66l-427,0l0,-708l72,0z" />
+ <svg:glyph unicode="G" horiz-adv-x="778" d="M673,631C610,694 529,726 417,726C206,726 48,569 48,354C48,139 206,-18 417,-18C503,-18 606,7 685,54l0,347l-241,0l0,-66l169,0l0,-237C560,68 490,48 417,48C235,48 126,191 126,354C126,517 235,660 417,660C504,660 571,629 619,578z" />
+ </svg:font>
+ <svg:text x="20" y="20" font-family="Missing" font-size="12">ACFG</svg:text>
+ <svg:text x="20" y="40" font-family="Helvetica" font-size="12">ACFG</svg:text>
+ </svg:svg>
+ </fo:instream-foreign-object>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>