Bug 46374 - Fonts not found for SVG texts (when not in C:\WINNT\Fonts)
Summary: Fonts not found for SVG texts (when not in C:\WINNT\Fonts)
Status: NEEDINFO
Alias: None
Product: Batik - Now in Jira
Classification: Unclassified
Component: SVGGraphics2D (show other bugs)
Version: 1.7
Hardware: PC Windows 2000
: P2 normal
Target Milestone: ---
Assignee: Batik Developer's Mailing list
URL:
Keywords:
Depends on:
Blocks: 46371
  Show dependency tree
 
Reported: 2008-12-10 03:27 UTC by M.H.
Modified: 2012-01-23 14:59 UTC (History)
1 user (show)



Attachments
Sample test case that should work. (1.64 KB, text/plain)
2009-05-13 21:24 UTC, Alexis Andre
Details
patch with proposal of fix for adding external fonts (87.21 KB, patch)
2012-01-23 14:54 UTC, Hugo de Almeida Cocharro
Details | Diff
icon for font preference page (1.35 KB, image/png)
2012-01-23 14:57 UTC, Hugo de Almeida Cocharro
Details
icon for font preference page (1.29 KB, image/png)
2012-01-23 14:57 UTC, Hugo de Almeida Cocharro
Details

Note You need to log in before you can comment on or make changes to this bug.
Description M.H. 2008-12-10 03:27:50 UTC
As posted as a FOP bug 46371 (https://issues.apache.org/bugzilla/show_bug.cgi?id=46371), this is rather a Batik issue:

Fonts used in SVG images need to be available in C:\WINNT\Fonts (or: OS font system). Due to font license restrictions, some fonts only are allowed to be available for our Java application and not OS wide. Therefore, I copied the TTF fonts in a custom directory which works well with Apache FOP. I tried to set a custom font configuration for the Java application via

java -Dsun.awt.fontconfig ...

and this basically works in a test application as I am able to create a Font object with the correct family name and sub name. But Batik seems to ignore this.
Comment 1 Thomas Deweese 2008-12-10 05:48:44 UTC
It is true that currently Batik does not have a way for someone to
provide arbitrary fonts to Batik.  However we do support CSS @font-face
to reference arbitrary TrueType fonts via URL.  See the following SVG
file for examples of how to accomplish this:

http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/samples/tests/spec/fonts/fontFace.svg?view=markup

A  lso if you successfuly added the font to the AWT font list then Batik
should see and use it.  The Code Batik uses to know about 'system fonts'
(where system here means the JVM) is:

        GraphicsEnvironment env;
        env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        String[] fontNames = env.getAvailableFontFamilyNames();

   If the fontNames array includes your font then Batik _will_
use it.  It's possible the name of the font Java see's isn't 
what you think it is which could cause problems as well.
Comment 2 Alexis Andre 2009-05-13 21:24:57 UTC
Created attachment 23660 [details]
Sample test case that should work.
Comment 3 Alexis Andre 2009-05-13 21:29:05 UTC
I am trying to embed a non-system font to a SVG file using the SVGGraphics2D class. 

Batik actually embeds a font, but a standard Java font one I think. The attachment gives a sample that should work but does not.
Comment 4 Alexis Andre 2009-05-13 23:47:10 UTC
I found at least one issue with font embedding. 

The way Batik is storing the font usage information is storing the wrong font name, so it generates the glyphs using a default Dialog font when the desired font is not available system-wide.

In svggen.SVGFont.java:

############
private static Font createCommonSizeFont(Font font) {
   Map attributes = new HashMap(font.getAttributes());
   attributes.put(TextAttribute.SIZE, new Float(COMMON_FONT_SIZE));
   // Remove Transform from font otherwise it will be applied twice.
   attributes.remove(TextAttribute.TRANSFORM);
   return new Font(attributes);
}
############

The last line that is calling the Font constructor actually fails silently if the "font" object is not a system font. This is called when creating the map of which characters are used by which font, so the String used to store the font family and weight correspond to the Dialog font used as a fallback.

A simple way to solve this problem would be to use the deriveFont method instead:

############
private static Font createCommonSizeFont(Font font) {
   Map attributes = new HashMap(font.getAttributes());
   attributes.put(TextAttribute.SIZE, new Float(COMMON_FONT_SIZE));
   // Remove Transform from font otherwise it will be applied twice.
   attributes.remove(TextAttribute.TRANSFORM);
   return font.deriveFont(attributes);
}
############

(or maybe just a 
############
private static Font createCommonSizeFont(Font font) {
   return font.deriveFont(new Float(COMMON_FONT_SIZE));
}
############
but I am not sure about the transform you are removing)

Since any generated SVG file contains the non-system font name and family, but uses the default shapes (see the previous attachment), I guess the fonts are stored in a different place, but the toSVG method is calling anyway createCommonSizeFont before generating the glyphs.

Why I am not commiting a patch is because I have not found yet where the xmlwriter is calling the toSVG method of SVGFont that is actually writing the glyph shapes. I am wondering when the method is actually called (non system fonts might be destroyed between the drawString call and the stream call).

I hope this helps.
Comment 5 Cameron McCormack 2009-05-14 18:40:23 UTC
Hi Alexis.

(In reply to comment #4)
> In svggen.SVGFont.java:
> 
> ############
> private static Font createCommonSizeFont(Font font) {
>    Map attributes = new HashMap(font.getAttributes());
>    attributes.put(TextAttribute.SIZE, new Float(COMMON_FONT_SIZE));
>    // Remove Transform from font otherwise it will be applied twice.
>    attributes.remove(TextAttribute.TRANSFORM);
>    return new Font(attributes);
> }
> ############
> 
> The last line that is calling the Font constructor actually fails silently if
> the "font" object is not a system font. This is called when creating the map of
> which characters are used by which font, so the String used to store the font
> family and weight correspond to the Dialog font used as a fallback.

Makes sense.

> A simple way to solve this problem would be to use the deriveFont method
> instead:
> 
> ############
> private static Font createCommonSizeFont(Font font) {
>    Map attributes = new HashMap(font.getAttributes());
>    attributes.put(TextAttribute.SIZE, new Float(COMMON_FONT_SIZE));
>    // Remove Transform from font otherwise it will be applied twice.
>    attributes.remove(TextAttribute.TRANSFORM);
>    return font.deriveFont(attributes);
> }
> ############
> 
> (or maybe just a 
> ############
> private static Font createCommonSizeFont(Font font) {
>    return font.deriveFont(new Float(COMMON_FONT_SIZE));
> }
> ############
> but I am not sure about the transform you are removing)

When using deriveFont(), all of the existing attributes are kept from the original Font, and only those present in the passed-in Map are overwritten.  So to remove the TRANSFORM attribute, you need to provide a map with a [TRANSFORM, null] entry in it:

  private static Font createCommonSizeFont(Font font) {
      Map attributes = new HashMap();
      attributes.put(TextAttribute.SIZE, new Float(COMMON_FONT_SIZE));
      attributes.put(TextAttribute.TRANSFORM, null);
      return font.deriveFont(attributes);
  }

> Since any generated SVG file contains the non-system font name and family, but
> uses the default shapes (see the previous attachment), I guess the fonts are
> stored in a different place, but the toSVG method is calling anyway
> createCommonSizeFont before generating the glyphs.
> 
> Why I am not commiting a patch is because I have not found yet where the
> xmlwriter is calling the toSVG method of SVGFont that is actually writing the
> glyph shapes. I am wondering when the method is actually called (non system
> fonts might be destroyed between the drawString call and the stream call).

The glyph shapes are extracted with the

  gv.getGlyphOutline(0)

calls that are in SVGFont.toSVG().  Since they are called on a GlyphVector that was obtained from the common size font, that'd be why the wrong shapes were written out in the SVG.


I've just committed the above change as r774981.  It makes your test case work, Alexis.


M.H., are you able to test if this allows FOP to work with other fonts?
Comment 6 Hugo de Almeida Cocharro 2012-01-23 14:54:32 UTC
Created attachment 28192 [details]
patch with proposal of fix for adding external fonts

Attached a patch with a proposal for allowing external fonts to be used by batik.
It adds 2 extra methods to the useragent. UserAgentAdapter implements the default behavior.

added some extra changes to squiggle, allowing you to test this more easily, this is for me not necessary but nice to have.
* added fonts preferences page
** allow for configuring the default css font. (not yet happy about the layouting)
** allow for configuring extra font directories
*** these extra font directories are first checked before fallbacking to fonts known to the AWT graphics environment.
Comment 7 Hugo de Almeida Cocharro 2012-01-23 14:57:25 UTC
Created attachment 28193 [details]
icon for font preference page
Comment 8 Hugo de Almeida Cocharro 2012-01-23 14:57:44 UTC
Created attachment 28194 [details]
icon for font preference page