3

We use BIRT since version 2 (currently using 4.2.2) and have always been plagued by the PDF (itext?) fonts register time.

org.eclipse.birt.report.engine.layout.pdf.font.FontMappingManagerFactory$2 run
INFO: register fonts in c:/windows/fonts cost:17803ms

This process only occurs the first time the render is used. Subsequent renders are not problematic.

The problem seems to be the time wasted when accessing ALL the system connected DRIVES.

Editing the fontsConfig.xml in org.eclipse.birt.report.engine.fonts plugin, reducing the search paths does not resolve the issue. ALL connected drives are accessed by BIRT.

<font-paths>
    <path path="/windows/fonts" />
</font-paths>

Is there a simple solution for this without having to render a "dummy" report to initialize BIRT in the background??

marcolopes
  • 9,232
  • 14
  • 54
  • 65
  • any luck with this problem? In my case it takes 53s ! – paul Feb 26 '14 at 13:46
  • Yes. I test BIRT on a daily basis, and i noticed that some users do not have this issue, even on the same S.O. (differences from Linux / MAC / Windows are expected!). The time is proportional to the number of drives in your system, even if you configure the fontsConfig.xml on org.eclipse.birt.report.engine.fonts plugin. Try BIRT in a system with only ONE drive connected and give me your feedback. – marcolopes Feb 27 '14 at 19:09
  • very interesting! I have 15 drives (including network shares). It's probably about time to drop some of them! I'll get a colleage with fewer drives to test the warmup time and I'll get back to you. – paul Feb 28 '14 at 07:06
  • 1
    Well, 15 drives certainly would explain 52sec! (i get 20 to 23s when all my 6 external drives are on...) – marcolopes Mar 01 '14 at 19:37
  • Opened a bug at Bugzilla: https://bugs.eclipse.org/bugs/show_bug.cgi?id=431511 – marcolopes Mar 29 '14 at 17:17
  • 1
    I have reduced the attached drive count to 9 and it is better. It corresponds to the times you mentioned. My colleague claims his start up time is very short (2-3) seconds but I haven't been able to verify that. I have put the initialisation in a 'warm-up' thread so that Birt gets started before it is actually needed. – paul Mar 31 '14 at 04:38
  • I do that too (initialize BIRT so it gets started before it is actually needed), but i just do that for the engine (with a memory created EMPTY design), but the difference is negligible. Initializing with a PDF render is out of the question (it will "stall" the application start-up) unless done in background (which will lead to other problems). – marcolopes Apr 01 '14 at 10:03

2 Answers2

3

This isn't necessarily a solution, but potentially another option as a workaround that I came up with after we noticed the same issue and did some investigating. It's also (IMO) better than generating a dummy report.

On startup (or at some other point depending on your needs), make the following call:

FontMappingManagerFactory.getInstance().getFontMappingManager(format, Locale.getDefault());

Why?

BIRT uses com.lowagie.text.FontFactory (iText) to register the fonts. Calls to that class are made from org.eclipse.birt.report.engine.layout.pdf.font.FontMappingManagerFactory which also spits out the log entries given in the question.

Within FontMappingManagerFactory we can see where the log entries are coming from:

private static void registerFontPath( final String fontPath )
{
    AccessController.doPrivileged( new PrivilegedAction<Object>( ) {

        public Object run( )
        {
            long start = System.currentTimeMillis( );
            File file = new File( fontPath );
            if ( file.exists( ) )
            {
                if ( file.isDirectory( ) )
                {
                    FontFactory.registerDirectory( fontPath );
                }
                else
                {
                    FontFactory.register( fontPath );
                }
            }
            long end = System.currentTimeMillis( );
            logger.info( "register fonts in " + fontPath + " cost:"
                    + ( end - start ) + "ms" ); // <-- Here!
            return null;
        }
    } );
}

Working backwards, we see that registerFontPath(String) is called by loadFontMappingConfig(URL), etc etc resulting in the following call hierarchy:

getFontMappingManager(String, Locale)
`-- createFontMappingManager(String, Locale)
    `-- loadFontMappingConfig(String)
        `-- loadFontMappingConfig(URL)
            `-- registerFontPath(String)

And getFontMappingManager(String, Locale) is the public method we can call. More importantly, however, is that the method also caches the FontMappingManager that gets created:

public synchronized FontMappingManager getFontMappingManager(
        String format, Locale locale )
{
    HashMap managers = (HashMap) cachedManagers.get( format );
    if ( managers == null )
    {
        managers = new HashMap( );
        cachedManagers.put( format, managers );
    }
    FontMappingManager manager = (FontMappingManager) managers.get( locale );
    if ( manager == null )
    {
        manager = createFontMappingManager( format, locale );
        managers.put( locale, manager );
    }
    return manager;
}

As a result, when you're ready to go generate your PDF, it will already be in the cache and BIRT won't have to go call down to the FontFactory and re-register the fonts.

But what about the format String?

This bit is some speculation, but I think the valid options are the OUTPUT_FORMAT_XXX Strings in IRenderOption. For our purposes I debugged to see that we want the String to be pdf. Considering that's also conveniently the desired output format, I assume IRenderOption.OUTPUT_FORMAT_PDF is the route to go.

If you're ultimately creating both PDFs and HTML files, it appears that you could make the call twice (once with IRenderOption.OUTPUT_FORMAT_PDF and once with IRenderOption.OUTPUT_FORMAT_HTML) and only the font config files which are different will be considered (ie. you won't be reading from c:/windows/fonts twice).


All that said, take this with a grain of salt. I believe this is completely safe, since the purpose of getFontMappingManager(String, Locale) is to get an object for accessing available fonts, etc., and it conveniently caches the result. However, if that were to change in the future you may end up with a tricky-to-find bug on your hands.

Community
  • 1
  • 1
avojak
  • 2,342
  • 2
  • 26
  • 32
  • impeccable!! Works great! And very nice explanation (as it should be!). I just don't understand the need for a "locale" :\ I use the default one, but i wonder where is birt getting the locale latter on to access FontMappingManager manager – marcolopes May 29 '17 at 22:35
0

I would suggest that you can modify the fontsConfig.xml and remove the fonts that you no longer need. Also remove the drives that you dont want birt to check for fonts.

Saurabh Singhal
  • 271
  • 1
  • 5
  • 18
  • As i say on the question: Editing the fontsConfig.xml in org.eclipse.birt.report.engine.fonts plugin, reducing the search paths does not resolve the issue. ALL connected drives are accessed by BIRT. – marcolopes Mar 06 '14 at 20:19