Showing posts with label fop. Show all posts
Showing posts with label fop. Show all posts

2008-09-18

FOP and fonts, the story goes on

Now I've a better understanding on how FOP handles fonts and how to get its precious informations about font list, duplicates, and failures. In the PrintRendererConfigurator, the #buildFontListFromConfiguration static method does (almost) all the job. It takes following input parameters:
  • A Configuration object with a <renderer> as root element.
  • A URL (as a String) to resolve relative font URLs with. Null is supported.
  • A FontResolver, which can be a DefaultFontResolver.
  • A boolean set to true if an exception should be thrown if an error is found.
  • A FontCache instance or null if caching is disabled.
As it is a static method, #buildFontListFromConfiguration can be called from everywhere with a fresh FontCache instance. The latter is useful as it gathers failed fonts. A fresh cache instance is needed, because cached data may survive the JVM. The cache saves itself in a ~/.fop/fop-fonts.cache file, holding font descriptions. Font descriptions seem to be only invalidated when FOP attempts to load a font for "real" (at rendering time). So when hitting the cache in a test program, it sometimes returned font descriptions that shouldn't have been there. The FontResolver requires a FOUserAgent which is created from a FopFactory. The FopFactory itself is created from a Configuration which contains the <renderer> elements, so there should be some instance reuse here. I've found a private method somewhere which logs font duplicates but I can't find it back to see if there was any hook around (didn't seem so). Anyways it will be cleaner to sort out font triplets with the same value. Printing the font triplets on the console, I noted they take the right font name, whatever the font file is. Adios, proprietary font naming convention!

Opening access to FOP configuration?

As I'm getting closer and closer to support multiple font directories comes the problem of how to define them. It seems logical to extend current convention, passing several paths to the novelang.fonts.dir VM argument, separated by platform's path separator. On Un*x it would look like:
-Dnovelang.fonts.dirs=my-fonts:other/fonts
But the path separator highlights that font definition becomes system-dependant (it's a semicolon on Windows). And anyways defining the fonts in the command line is unlogical as fonts are part of the rendering. So I'm thinking on embedding font directories names as XSLT metadata (this idea was already mentioned). I explored the possibility to embed the whole FOP configuration itself, which is XML, also. But opening direct access to FOP configuration would let the opportunity to do weird things:
  • Font cache configuration.
  • Default page settings. This makes only sense when configuration is accessed by multiple XSLT.
  • Title of the PDF document. The probable need to get this title from a source document (like the Book) would make this approach redundant.
On the other hand, stuff like hyphenation directories, ICC profiles and target resolution for bitmap images make sense. But one good reason to let Novelang keep hands on everything passed to FOP is to ensure that every directory is a subdirectory of current project, therefore preventing security threats. So we could have something like:
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fo="http://www.w3.org/1999/XSL/Format"
    xmlns:n="http://novelang.org/book-xml/1.0"
    xmlns:nmeta="http://novelang.org/meta-xsl/1.0"
>
  <nmeta:fop version="1.0" >
    <target-resolution>72</target-resolution>
    <renderers>
      <renderer mime="application/pdf" >
        <fonts>
          <directory recursive="true" >
            my/fonts
          </directory>
        </fonts>
        <output-profile>
          profiles/EuropeISOCoatedFOGRA27.icc
        </output-profile>
        <filterList>
          <value>null</value>
        </filterList>
        <filterList type="image" >
          <value>flate</value>
          <value>ascii-85</value>
        </filterList>
      </renderer>
    </renderers>
  </nmeta:fop>

    ...

</xsl:stylesheet>

This really looks like FOP configuration (see FOP documentation for details), but what's not shown is all forbidden stuff. So we end up with the best of the two worlds.

2008-09-16

FOP and fonts

FOP makes me feel dumb because it is great. In a previous post I already mentioned that FOP's font handling was better than I thought first, so I could have been wrong forcing a custom font naming. As a matter of fact, FOP holds a list of fonts inside the FontCache of its FontFactory. Novelang instantiates the FontFactory so it has full hands on it. Using a Java debugger, let's look at what a FontCache contains.
fontMap = {java.util.HashMap}
  [0] = {java.util.HashMap$Entry} 
    key: java.lang.String
        "file:/…/fonts/URWGothicL-BookObli.ttf"
    value: org.apache.fop.fonts.CachedFontInfo
      lastModified = 1042610200000
      metricsFile = {java.lang.String}
          "file:/…/.fop-font-metrics-14045 \
               .temp/URWGothicL-BookObli.xml"
      embedFile = {java.lang.String}
          "file:/…/fonts/URWGothicL-BookObli.ttf"
      kerning = true
      fontTriplets = {java.util.ArrayList} 
        [0] = {org.apache.fop.fonts.FontTriplet} 
          name = {java.lang.String} 
              "URWGothicL-BookObli"
          style = {java.lang.String} "normal"
          weight = 400
          priority = 0
          key = {java.lang.String}
              "URWGothicL-BookObli,normal,400"
        […]
  […]
failedFontMap = {java.util.HashMap}
Sweet! Here is everything I need:
  • Font name.
  • Font style, "italic" or "normal".
  • Weight. Not just "normal" or "bold" but "light" and "extra-bold".
  • Priority for dealing with duplicates
  • A list of fonts which could not be read (failedFontMap).
The moderately bad news is, fontMap and failedFontMap are private fields but I see no reason to not use dirty reflexion here. The example above is biased as it was created from a Novelang-generated font list, so I'll have to investigate a bit more to see how Fop deals with:
  • Failed fonts.
  • Font name different from font file name.
  • Multiple directories (including nested ones).
To sum up, FOP provides all I need to make Novelang code cleaner and bring following enhancements:
  • Multiple directories.
  • List of failed fonts.
  • Warning in case of duplicates.
  • Fonts sorted by font name.
  • Throw away temporary .fop-font-metrics directory.
  • Cache of font descriptions handled by FOP itself (this is the meaning of the lastModified field in the FontCache).
  • Support more font types. By letting FOP do its job we let its FontFileFinder recognize following font files: *.ttf for True Type, *.pfb for Type One. The *.otf suffix also appears and those fonts may be treated as TTF,