New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FreeType rasterization doesn't work, and has been made mandatory #685

Closed
mskala opened this Issue Aug 26, 2013 · 54 comments

Comments

Projects
None yet
8 participants
@mskala
Member

mskala commented Aug 26, 2013

This is a priority issue for me because it's blocking the Tsukurimashou 0.8 release, planned for tomorrow. Encountered in the latest git master version of FontForge, under 64-bit Linux.

Same problem I reported two years ago to the mailing list (http://sourceforge.net/mailarchive/forum.php?thread_name=20111125.100615.444195158.wl%40gnu.org&forum_name=fontforge-devel ): when FontForge uses FreeType to generate bitmaps, it sometimes produces complete garbage. The bitmaps, if saved to BDF, are mostly empty, with just a few containing large irregular black areas. If I generate a bitmap strike in the GUI and then try to look at it in the bitmap window, it sometimes segfaults, suggesting in-memory data structures contain garbage.

The file I posted for that bug report is no longer online, but I've posted a fresh one at https://github.com/fontforge/debugfonts/blob/master/jieub-bt-ps.sfd

In the GUI, there is an option during bitmap generation to use FreeType or not; but native scripting doesn't offer such a choice and will always attempt to use FreeType.

Werner Lemberg found and fixed an unrelated bug in FreeType in response to my report (see the mailing list thread) - apparently FreeType couldn't read BDF files with large indices. But FontForge does not use FreeType for BDF files; that bug is unrelated to mine and only happened to also be noticeable from the test case I provided. He disclaimed responsibility for my actual bug, which is specific to the case where FontForge generates a bitmap using FreeType.

In 2011, a workaround for me was to build FontForge without FreeType. But pull #157 made that impossible.

At this point it looks to me like my best option may be to dig into the FontForge source and modify the hardcoded "use FreeType" setting to "no," because I need this to work now. But in the longer term, we should figure out what's actually wrong with the FreeType rasterization and fix it, or convince Werner that it is his business to do so.

@lemzwerg

This comment has been minimized.

Member

lemzwerg commented Aug 26, 2013

Same problem I reported two years ago to the mailing list
(http://sourceforge.net/mailarchive/forum.php?thread_name=20111125.100615.444195158.wl%40gnu.org&forum_name=fontforge-devel):
when FontForge uses FreeType to generate bitmaps, it sometimes
produces complete garbage. The bitmaps, if saved to BDF, are mostly
empty, with just a few containing large irregular black areas. If I
generate a bitmap strike in the GUI and then try to look at it in
the bitmap window, it sometimes segfaults, suggesting in-memory data
structures contain garbage.

If you can provide a backtrace, this would be very helpful, since it
helps identify a not directly related display bug in FontForge.

[...] But in the longer term, we should figure out what's actually
wrong with the FreeType rasterization and fix it, or convince Werner
that it is his business to do so.

If you can give me data related to FreeType to work on, I'm
certainly fixing any issue. However, I don't have time and enough
knowledge, unfortunately, to read FontForge's code in detail.

I still have doubts whether this is a FreeType issue: Currently, there
are no known FreeType rendering problems. I rather suspect that
FontForge's code to generate the BDF data from the rendered bitmaps is
buggy.

Werner
@davelab6

This comment has been minimized.

Member

davelab6 commented Aug 26, 2013

Would a --configure flag be useful? So that you have to intentionally say you dont have FreeType, and if you don't say that, the build fails?

@mskala

This comment has been minimized.

Member

mskala commented Aug 26, 2013

I'll look into this after my own software release, which takes priority, but as a quick note: since my goal is "I want rasterization to work," not "I want to build without FreeType," and allowing a no-FreeType build as an option at all even if it's deliberately made hard to use would cancel out the maintainability benefits of #157, no, I don't think it would be a good idea to try to add back the compile-time option of building without FreeType.

Oh, I should mention - hacking the code to hardcode the same option that the GUI exposes, in the "no" position, did solve my immediate problem, unblocking the software release. That's why this gets bumped down my priority list.

@mskala

This comment has been minimized.

Member

mskala commented Aug 26, 2013

Here's a backtrace for the segfault. Running on 64-bit Linux with the latest Git master sources of FontForge; load the SFD file linked above, create a 100-pixel bitmap strike with the (default) "use FreeType" option selected, open a bitmap window on the uppercase "B" glyph, segfault.

#0  CVDrawSplineSetOutlineOnly (cv=0x7fffffff73d0, pixmap=0x801a20, set=
    0x7de590, fg=65280, dopoints=0, clip=0x7fffffff73a0, strokeFillMode=
    sfm_stroke) at charview.c:1306
#1  0x00007ffff7ad191f in CVDrawSplineSetSpecialized (cv=0x7fffffff73d0, 
    pixmap=0x801a20, set=0x7de590, fg=65280, dopoints=0, clip=0x7fffffff73a0, 
    strokeFillMode=sfm_stroke) at charview.c:1436
#2  0x00007ffff7ad0b50 in CVDrawSplineSet (cv=0x7fffffff73d0, pixmap=0x801a20, 
    set=0x7de590, fg=65280, dopoints=0, clip=0x7fffffff73a0) at charview.c:1278
#3  0x00007ffff7aac975 in BVExpose (bv=0x13dc4ac0, pixmap=0x801a20, event=
    0x7fffffffae00) at bitmapview.c:765
#4  0x00007ffff7aaf86c in v_e_h (gw=0x801a20, event=0x7fffffffae00)
    at bitmapview.c:1396
#5  0x00007ffff70a27b0 in _GWidget_Container_eh (gw=0x15057670, event=
    0x7fffffffae00) at gcontainer.c:269
#6  0x00007ffff711b41f in dispatchEvent (gdisp=0x695d70, event=0x7fffffffaed0)
    at gxdraw.c:3493
#7  0x00007ffff711b723 in GXDrawProcessPendingEvents (gdisp=0x695d70)
    at gxdraw.c:3563
#8  0x00007ffff70a7ac1 in GDrawProcessPendingEvents (gdisp=0x695d70)
    at gdraw.c:777
#9  0x00007ffff71275ce in _GHVBoxFitWindow (g=0x1505c7b0, center=0)
    at ghvbox.c:677
#10 0x00007ffff71276ca in GHVBoxFitWindow (g=0x1505c7b0) at ghvbox.c:691
#11 0x00007ffff7b4de56 in BVMakeLayers (bv=0x13dc4ac0) at cvpalettes.c:3569
#12 0x00007ffff7b50196 in BVPaletteCheck (bv=0x13dc4ac0) at cvpalettes.c:4037
#13 0x00007ffff7b50328 in BVPaletteActivate (bv=0x13dc4ac0)
    at cvpalettes.c:4067
#14 0x00007ffff7aafb31 in bv_e_h (gw=0x13db5840, event=0x7fffffffbbb0)
    at bitmapview.c:1484
#15 0x00007ffff70a3139 in _GWidget_Container_eh (gw=0x13db5840, event=
    0x7fffffffbbb0) at gcontainer.c:393
#16 0x00007ffff70a45c0 in _GWidget_TopLevel_eh (gw=0x13db5840, event=
    0x7fffffffbbb0) at gcontainer.c:734
#17 0x00007ffff711b41f in dispatchEvent (gdisp=0x695d70, event=0x7fffffffbc80)
    at gxdraw.c:3493
#18 0x00007ffff711b865 in GXDrawEventLoop (gd=0x695d70) at gxdraw.c:3592
#19 0x00007ffff70a7b24 in GDrawEventLoop (gdisp=0x695d70) at gdraw.c:786
#20 0x00007ffff7cd563d in fontforge_main (argc=2, argv=0x7fffffffdfa8)
    at startui.c:1259
#21 0x0000000000400924 in main (argc=2, argv=0x7fffffffdfa8) at main.c:31
@davelab6

This comment has been minimized.

Member

davelab6 commented Aug 26, 2013

So this issue is really two tasks?

@mskala

This comment has been minimized.

Member

mskala commented Aug 26, 2013

I think the main issue is to fix the rasterization. The bad rasterization seems like it may be separate from the crash, because I can reproduce incorrect rasterization without causing a crash. (For instance, by saving a BDF file without opening a bitmap window, as in my first report.) Note that the incorrect rasterization is visible in the bitmap window when it doesn't crash (opening "B" as described above crashes immediately, but opening other glyphs does not always crash), and indeed the incorrect bitmap is visible in the corner of the bitmap window even in the "open B" case described above. So the bitmaps are incorrect very soon after they come out of FreeType, though it remains possible that they could be correct when they come out of FreeType, with FontForge corrupting them very soon after that point.

I suspect fixing the rasterization might have the effect of fixing the crash too, depending on how the data gets to be corrupted. The crash looks like a consequence of trouble upstream. But we haven't ruled out that the crash may represent an additional issue. The rasterization is really the issue I intended to report.

If the equivalent Python command doesn't already have an option for switching FreeType on and off, then adding an option for that (not just in native scripting) would make sense too.

@davelab6

This comment has been minimized.

Member

davelab6 commented Aug 26, 2013

So, 4?

  • Add a native scripting option that matches the GUI option
  • Add a python scripting option that matches the GUI option
  • Fix the generation of bitmaps with freetype
  • Fix the crash if it still happens after the above is fixed
@lemzwerg

This comment has been minimized.

Member

lemzwerg commented Aug 26, 2013

So the bitmaps are incorrect very soon after they come out of
FreeType, though it remains possible that they could be correct
when they come out of FreeType, with FontForge corrupting them
very soon after that point.

It should be straightforward to immediately send the bitmaps created
by FreeType to a series of debugging log files (one glyph per file,
where image bits can be represented with `*' or something similar).
Doing so it can be checked easily whether FreeType produces buggy
output.

Werner
@mskala

This comment has been minimized.

Member

mskala commented Aug 26, 2013

Yes, I think those four points cover it.

@mskala

This comment has been minimized.

Member

mskala commented Sep 9, 2013

mskala@3989899 makes it not segfault when opening a bitmap window, but there are obviously other things wrong too. It appears that what comes out of CVLayer() and goes into activelayer is garbage: a very large negative number, which causes trouble when it's used as an array index. Still not sure why, because I don't know the data structure and am not sure what that number is really supposed to represent.

@mskala

This comment has been minimized.

Member

mskala commented Sep 13, 2013

On further investigation: I was able to reproduce the crash without using FreeType, so it appears that that is at least partially independent of the bad bitmaps. The bad bitmaps seem to be deterministic but sensitive: they don't occur if I regenerate the glyphs one at a time, nor if I regenerate "selected" glyphs when only a few are selected, but they occur if I generate bitmaps for the entire font all at once, or "selected" with all or a large number of glyphs selected. Moreover, the bad bitmaps become worse (affecting a larger fraction of the glyphs in the font) when more glyphs are being rasterized; the problem first becomes noticeable at about 5000-8000 glyphs (this font has about 13000).

The code that calls FreeType is very complicated, but it appears that as part of creating a FreeType context, FontForge is actually creating a temporary Postscript font and telling FreeType to read that. Maybe the problem is only showing up when that temporary font is large. But I would expect it to be just as large when few glyphs are selected (there'd just be fewer operations done on the same large temporary font), So maybe there is (also or instead) some issue that crops up when FontForge does too many operations on the same FreeType context.

A workaround for my purposes is to rewrite my script that does rasterization to do it in 1000-glyph blocks, instead of the entire font in one go. I'll look at the temporary-file generation stuff and see if I can find anything obvious wrong with it, but unless others besides myself are willing to attempt reproducing this, I'm unlikely to want to pursue it further.

@davelab6

This comment has been minimized.

Member

davelab6 commented Sep 13, 2013

Perhaps the best fix is to check if there are more than 1000 glyphs being processed and if so, alert the user that only the first 1000 are processed, and just process them?

@mskala

This comment has been minimized.

Member

mskala commented Sep 13, 2013

What would you think of a word processor that notified you - when you tried to print, with no hint of such a limitation before that moment - "Only the first 100 pages of your document will be printed."?

@davelab6

This comment has been minimized.

Member

davelab6 commented Sep 14, 2013

Have you used a libre word processor? ;)

@davelab6

This comment has been minimized.

Member

davelab6 commented Sep 18, 2013

@mskala okay, lets fix this now :) You said,

when FontForge uses FreeType to generate bitmaps, it sometimes produces complete garbage. The bitmaps, if saved to BDF, are mostly empty, with just a few containing large irregular black areas. If I generate a bitmap strike in the GUI and then try to look at it in the bitmap window, it sometimes segfaults, suggesting in-memory data structures contain garbage. The file I posted for that bug report is no longer online, but I've posted a fresh one at the same URL, http://ansuz.sooke.bc.ca/temporary/jieub-bt-ps.sfd.bz2.

Please list the exact steps to reproduce the problem with this file, so @monkeyiq knows how to do it.

@mskala

This comment has been minimized.

Member

mskala commented Sep 18, 2013

  1. download and decompress the file from http://ansuz.sooke.bc.ca/temporary/jieub-bt-ps.sfd.bz2
  2. load it in FontForge
  3. open the "Element"/"Bitmap Strikes Available..." dialogue
  4. enter "100" in the Pixel Sizes box. "Use FreeType" and "Create Rasterized Strikes" should be checked by default.
  5. click "OK"
  6. choose "View"/"100 pixel bitmap."

Some notes:

  • Other files besides this one fail, but only other large files. I've only seen it on Korean fonts with over 13000 glyphs. However, I also have Korean fonts with just as many glyphs as this one, generated by the same workflow, that don't fail. I suspect something to do with file size (in bytes or similar), because the files that fail seem to have in common both a large number of glyphs, and more complicated glyphs than the ones that don't fail.
  • This problem occurs when bitmaps are created by any command: not only in the GUI but also in native scripting, and not only with "Bitmap Strikes Available..." but also with "Regenerate Bitmaps..."
  • However, it only occurs when Freetype is used.
  • The problem occurs when rasterizing the entire font, or only selected glyphs if a significant fraction of the font is selected. It does not occur when rasterizing just the current glyph from a glyph view, or if only a few are selected. (I haven't tried the new on-the-fly regeneration patch, but since that is working on a single glyph at a time, I wouldn't expect the problem to occur there.) Furthermore, the fraction of bitmaps that are corrupted seems to increase when a larger number are selected.
  • The bad bitmaps are visible when bitmaps are viewed by any means, including the whole-font "View" option mentioned above, opening a bitmap window, and generating a bitmap font file and viewing it with other software.
  • It occurs consistently on two 64-bit Linux machines and a 32-bit Linux machine, with different versions of Freetype and FontForge, spanning more than two years. Consistently means not only the bitmaps are corrupted every time, but the same bitmaps are corrupted with identical-looking results every time.
@ultrasquid

This comment has been minimized.

ultrasquid commented Sep 18, 2013

Would it be possible to break up such large fonts into smaller batches for
processing bitmaps, then concatenate the results when done? Would fonts
that make use of references trip up that sort of thing?

On Wed, Sep 18, 2013 at 8:02 AM, Matthew Skala notifications@github.comwrote:

  1. download and decompress the file from
    http://ansuz.sooke.bc.ca/temporary/jieub-bt-ps.sfd.bz2
  2. load it in FontForge
  3. open the "Element"/"Bitmap Strikes Available..." dialogue
  4. enter "100" in the Pixel Sizes box. "Use FreeType" and "Create
    Rasterized Strikes" should be checked by default.
  5. click "OK"
  6. choose "View"/"100 pixel bitmap."

Some notes:

  • Other files besides this one fail, but only other large files.
    I've only seen it on Korean fonts with over 13000 glyphs. However, I also
    have Korean fonts with just as many glyphs as this one, generated by the
    same workflow, that don't fail. I suspect something to do with file size
    (in bytes or similar), because the files that fail seem to have in common
    both a large number of glyphs, and more complicated glyphs than the ones
    that don't fail.
  • This problem occurs when bitmaps are created by any command: not
    only in the GUI but also in native scripting, and not only with "Bitmap
    Strikes Available..." but also with "Regenerate Bitmaps..."
  • However, it only occurs when Freetype is used.
  • The problem occurs when rasterizing the entire font, or only
    selected glyphs if a significant fraction of the font is selected. It does
    not occur when rasterizing just the current glyph from a glyph view, or if
    only a few are selected. (I haven't tried the new on-the-fly regeneration
    patch, but since that is working on a single glyph at a time, I wouldn't
    expect the problem to occur there.) Furthermore, the fraction of bitmaps
    that are corrupted seems to increase when a larger number are selected.
  • The bad bitmaps are visible when bitmaps are viewed by any means,
    including the whole-font "View" option mentioned above, opening a bitmap
    window, and generating a bitmap font file and viewing it with other
    software.
  • It occurs consistently on two 64-bit Linux machines and a 32-bit
    Linux machine, with different versions of Freetype and FontForge, spanning
    more than two years. Consistently means not only the bitmaps are corrupted
    every time, but the same bitmaps are corrupted with identical-looking
    results every time.


Reply to this email directly or view it on GitHubhttps://github.com//issues/685#issuecomment-24671039
.

Jason Pagura
zimbach at gmail dot com

@mskala

This comment has been minimized.

Member

mskala commented Sep 18, 2013

It's not necessary to separate the pieces completely - I've written a script to select and rasterize a thousand glyphs at a time until the whole font has been rasterized, and it seems to work. But I don't think that depending on such a workaround is a good idea when we don't know why the bitmaps are getting corrupted in the first place. See http://thecodelesscode.com/case/60 .

@ultrasquid

This comment has been minimized.

ultrasquid commented Sep 18, 2013

What it sounds like to me (and keep in mind I'm no systems analyst; just
going by what I understand of what you're saying, and it's going to sound
like a rehash of everything up to now) that the problem is with a memory
overflow of some kind, probably because of a flaw in Freetype not handling
this memory situation properly. Short of stuffing your computer with more
RAM modules, or getting the Freetype people involved, your workaround
script may be the most sensible solution.

I think it would a benefit to all to include your script with FontForge, at
least until a more concrete solution is arrived at.

On Wed, Sep 18, 2013 at 12:02 PM, Matthew Skala notifications@github.comwrote:

It's not necessary to separate the pieces completely - I've written a
script to select and rasterize a thousand glyphs at a time until the whole
font has been rasterized, and it seems to work. But I don't think that
depending on such a workaround is a good idea when we don't know why the
bitmaps are getting corrupted in the first place. See
http://thecodelesscode.com/case/60 .


Reply to this email directly or view it on GitHubhttps://github.com//issues/685#issuecomment-24690455
.

Jason Pagura
zimbach at gmail dot com

@mskala

This comment has been minimized.

Member

mskala commented Sep 18, 2013

My script in the form I wrote it is https://github.com/mskala/Tsukurimashou/blob/master/pe/bdf.pe but that includes some code to do other things specific to my project, and some debugging commands, and probably isn't too useful outside its original context. The relevant part is as follows:

# find blocks of 1000 glyphs
nblocks=0;
x=0;
blockstart=Array(100);
blockstart[0]=-1;
blockend=Array(100);
SelectWorthOutputting();
foreach
  if (x%1000 == 0)
    nblocks=nblocks+1;
  endif
  x=x+1;
  if (blockstart[nblocks-1] == -1)
    blockstart[nblocks-1]=GlyphInfo("Encoding");
    blockstart[nblocks]=-1;
  endif
  blockend[nblocks-1]=GlyphInfo("Encoding");
endloop

# make bitmaps, one block at a time
BitmapsAvail([100],0);
x=0;
while (x<nblocks)
  Select(blockstart[x],blockend[x]);
  BitmapsRegen([100]);
  x=x+1;
endloop

I don't mind relicensing that as appropriate.

@mskala

This comment has been minimized.

Member

mskala commented Sep 18, 2013

On other points: I don't think physical memory is really an issue because it happens the same on both large and small systems, and running out of memory as such would have more catastrophic effects. Also, for all I say this is a "large" font, it's in the range of tens of megabytes, not gigabytes. A machine big enough to run FontForge at all should not have trouble with a chunk of data that size. But I agree it looks like there is some kind of limited-size thing (maybe a buffer?) overflowing.

I am very suspicious of the design which builds a Postscript font internally on the fly and then expects Freetype to read it. Building a Postscript font is complicated, and if the code that does this is written especially for the interface to Freetype and isn't just calling FontForge's normal Postscript generator, then it may not be debugged as thoroughly as the normal Postscript generator. Alternately, if Freetype has trouble reading a very large Postscript file, that could be a Freetype bug. I don't think @lemzwerg and any other Freetype maintainers will be interested unless there is clear evidence of correct data going into Freetype and incorrect data coming out, without passing through FontForge between the measurement points. Any FontForge code between the "correct data" and "incorrect data" points could potentially be the cause of the problem instead of Freetype.

Thus I suggest that it would be a good idea to get a copy of the temporary Postscript font as an actual file that can be examined independently of FontForge. If it's good, it'll be a good test case for Freetype. If it's bad, it may shed some light on what's wrong with FontForge's interface. I can't promise to extract this myself but I'll at least look into it.

I also would feel a lot better if anyone other than me would try to actually reproduce this bug.

@lemzwerg

This comment has been minimized.

Member

lemzwerg commented Sep 19, 2013

What it sounds like to me (and keep in mind I'm no systems analyst;
just going by what I understand of what you're saying, and it's
going to sound like a rehash of everything up to now) that the
problem is with a memory overflow of some kind, probably because of
a flaw in Freetype not handling this memory situation properly.

What about using valgrind or running FontForge (with optimization
flags switched off) within gdb? I'm quite sure that this gives more
hints where the problem is.

I think it would a benefit to all to include your script with
FontForge, at least until a more concrete solution is arrived at.

My personal opinion is that this is the wrong solution.

Werner
@mskala

This comment has been minimized.

Member

mskala commented Sep 19, 2013

What about using valgrind or running FontForge (with optimization flags switched off) within gdb? I'm quite sure that this gives more hints where the problem is.

Running in valgrind caused me to find #735/#736, but didn't flag any errors I could relate to this problem. Running in gdb allowed me to locate the segfault concealed by mskala@3989899 but it's not clear that that is related either. More tracing with gdb led to the detailed information I gave above.

@lemzwerg

This comment has been minimized.

Member

lemzwerg commented Sep 19, 2013

Next try: Please compile FreeType with debugging output. This is, get
a source code bundle and uncomment the lines

/* #define FT_DEBUG_LEVEL_ERROR */
/* #define FT_DEBUG_LEVEL_TRACE */

in FreeType's include/freetype/config/ftoption.h, then compile and
install the library with

./configure
make
make install

and do the same with FontForge.

With the new binary, say

FT2_DEBUG=any:7 fontforge ... &> fontforge.log

and analyze this (probably very huge) log file.

@davelab6

This comment has been minimized.

Member

davelab6 commented Jul 20, 2014

@mskala is this still an issue?

@mskala

This comment has been minimized.

Member

mskala commented Jul 20, 2014

Still an issue as of b0bf164 , which is the current head.

@davelab6 davelab6 added this to the 2014 08 milestone Jul 20, 2014

@mskala

This comment has been minimized.

Member

mskala commented Feb 27, 2015

Yes, it still fails with the current git master revision.

@frank-trampe

This comment has been minimized.

Contributor

frank-trampe commented Feb 27, 2015

Same backtrace?

@mskala

This comment has been minimized.

Member

mskala commented Feb 27, 2015

I'm not sure where you want a backtrace from. The segfault has been fixed since the merge of d556150 in September 2013, but it wasn't the original problem. The original problem, which remains, is that FontForge produces corrupt bitmaps without crashing.

@adrientetar

This comment has been minimized.

Member

adrientetar commented Feb 28, 2015

Does FreeType provide any benefit in the generation of bitmap? If not we could just disable it by default with a separate config variable on the code path that generates the bitmaps.

@adrientetar

This comment has been minimized.

Member

adrientetar commented Feb 28, 2015

Is there any other issue that prevents you from using the mainline FontForge? I see you have 19 of them opened.

@mskala

This comment has been minimized.

Member

mskala commented Feb 28, 2015

I'm not currently entirely prevented from using mainline FontForge, and I do use mainline FontForge for GUI font viewing. I use FontAnvil for scripting Tsukurimashou builds, and I'm unlikely to switch back. The reason has more to do with strategic direction than specific bugs: FontForge's priority is GUI and Python, both of which have negative, not just zero, value in my project's use case, so FontForge isn't the right tool for that job. Both FontForge and FontAnvil share this bug (#685), and I'm using the 1000 glyphs at a time workaround described above.

I'm not sure that disabling Freetype rasterization is really the best thing to do. When Freetype works, it does produce nicer-looking results.

@frank-trampe

This comment has been minimized.

Contributor

frank-trampe commented Feb 28, 2015

This may be feasible. What we can do is find where FontForge is calling FreeType functions in this workflow and then check the arguments and their referenced data against the specifications provided by the FreeType documentation. Any volunteers?

@mskala

This comment has been minimized.

Member

mskala commented Mar 3, 2015

When FontForge is going to do bitmap generation using Freetype, it creates a temporary Postscript file containing just the glyphs that will be rasterized, writes that to disk in a file created by tmpfile(), mmap()s it, and then calls Freetype to read the resulting chunk of memory. I modified my copy to use a known filename instead of tmpfile(), and captured the resulting Postscript font file. It looks okay to me. I'm now suspicious of Freetype's handing of large files, because the problem specifically appears when FontForge invokes FreeType on a large mmap()ped file, and not when processing identical font data in a smaller file. I did this on a Mac, by the way, so we can add that to the list of systems that show the same failure.

Is there any code other than FontForge that generates bitmaps using Freetype from an mmap()ped Postscript file, so we could compare against it?

Has anyone other than me attempted to reproduce this bug?

@frank-trampe

This comment has been minimized.

Contributor

frank-trampe commented Mar 3, 2015

@lemzwerg, any ideas?

@lemzwerg

This comment has been minimized.

Member

lemzwerg commented Mar 3, 2015

Unfortunately no. There are no known problem in this area. BTW, what exactly is `large' for the mmap()ed file?

Do you get a good results if you build FreeType with configure option --disable-mmap?

@mskala

This comment has been minimized.

Member

mskala commented Mar 3, 2015

My test file is 13 megabytes. I have seen it happen on slightly smaller files; I'm not sure what is the smallest. The amount of corruption (percentage of the glyphs that get corrupted) seems to increase with filesize.

FreeType 2.5.5 compiled with --disable-mmap makes no difference. I am not convinced that that option really has any effect on FreeType compilation. Recursive grep finds only a couple of calls to mmap() in the entire FreeType code base, they do not seem to be conditional on any configuration setting, and although the configure script parses this option, it doesn't seem to record the value anywhere. It looks like a do-nothing option to me. Anyway, even if --disable-mmap has some effect on FreeType by disabling the mmap() calls through a non-obvious mechanism, I don't think that FreeType is calling mmap() in the failing code path. Rather FontForge calls mmap() and then passes the resulting chunk of memory into FT_New_Memory_Face. It's not obvious to me why FontForge needs to use mmap() for this; why not just allocate a chunk of memory and read the file into it, or better yet not have a file at all? I wonder if there is some weirdness involving the use of mmap() on a file from tmpfile(), but the fact that this has shown up identically on both MacOS and Linux makes operating system weirdness less likely. I will try changing FontForge to read the file into plain ordinary non-mmap()ped memory at some point, but not tonight. It's close to midnight here in Copenhagen and I should be asleep.

Has anyone other than me attempted to reproduce this bug?

@lemzwerg

This comment has been minimized.

Member

lemzwerg commented Mar 4, 2015

There is an effect! If you use --disable-mmap, FreeType gets built using the source file src/base/ftsystem.c instead of builds/unix/ftsystem.c, and only the latter contains calls to mmap. The relevant code can be found in builds/unix/configure.raw.

Have you tried to render the temporary PS font with one of the FreeType demo programs? For example, ftview has a command line option -p to simulate memory mapping.

@mskala

This comment has been minimized.

Member

mskala commented Mar 4, 2015

The font appears corrupted when loaded with ftview, with or without the -p option. It may well really be corrupted. FontForge loads it with no trouble, provided one does not use FreeType in FontForge to rasterize it. kfontview displays the file with corruption, but I think it's using FreeType. I don't seem to have a viewer handy that can read .pfb files without using FreeType. XeLaTeX, when told to use this file as the font for a document, gives the message "Error 11 (driver return code) generating output" and generates an empty (zero-length) PDF file. I think that means the XeTeX PDF generator, which is a separate process, is segfaulting. So for the moment I'm guessing that FontForge may be writing a bad file.

The font file is at http://ansuz.sooke.bc.ca/temporary/bmtmp.pfb - that particular version created by FontAnvil because it's what I have on this Mac, but the relevant code is the same and the one from FontForge shows the same behaviour.

@lemzwerg

This comment has been minimized.

Member

lemzwerg commented Mar 4, 2015

Thanks for the PFB file. It is indeed invalid: There are 107791 subroutines! Within Type 1 charstrings, all numbers must be in the range -32000 <= x <= 32000 (except arguments to the div operator, which in turn must produce a result in this range). In particular, FreeType can't handle larger values, since it stores all stack values in 16.16 format – or rather, it converts large integer values to the 16.16 format (with a warning), which is of course complete nonsense if the value is used as a subroutine index, thus the distorted rasterization.

Theoretically, I could change FreeType to allow larger values, however, this would be a major rewrite of the Type 1 parser, and I strongly doubt whether this is of any value given that it is against the specification.

Even if you restrict yourself to 1000 glyphs per run, there is no guarantee in current FontForge that it doesn't create a font with more than 32000 subroutines...

In summary: FontForge's current route using a PFB for rasterization might lead to invalid results. No idea how complicated it is to rewrite the stuff so that FontForge tries to split the data into smaller sub-PFBs.

@mskala

This comment has been minimized.

Member

mskala commented Mar 4, 2015

I wonder why FontForge is generating so many subroutines. There are only about 14000 glyphs in the font, so that's an average of more than seven subroutines per glyph. Most likely, a lot of them are duplicates.

I don't know what the right answer is. FreeType seems to need a font file (i.e. a sequence of bytes in Postscript or some other standard format) in order to rasterize at all. FontForge handles that in a very messy way by saving the font into a Postscript file (in principle, on disk) and then loading it with FreeType. With some minor internal changes, FontForge could and probably should write the temporary file into an in-memory buffer and have FreeType read that, instead of really going through the OS's file interface. Doing it in smaller batches of glyphs would be a more significant internal change.

@mskala

This comment has been minimized.

Member

mskala commented Mar 4, 2015

Okay, on further examination of the font, it looks like those subroutines are associated with hint replacement. There is some duplication, but even with the duplicates removed, there are almost 82000 distinct hint-replacement subroutines. And the standard as I understand it requires each of these to really be a subroutine; it's not possible to do hint replacement in a fully standard way with the data stored in any other form. But maybe it's not necessary to do hint replacement at all, and maybe FreeType can do hint replacement with the data stored in a way that is not fully downward-compatible with the oldest Postscript interpreters. Failing those possibilities, it may be possible to merge very similar hint-replacement subroutines with very little loss of information until there are few enough to fit in the file format.

@lemzwerg

This comment has been minimized.

Member

lemzwerg commented Mar 4, 2015

Maybe FontForge can create an intermediate CFF instead of a PFB?

@mskala

This comment has been minimized.

Member

mskala commented Mar 4, 2015

That looks like it may well be possible. I'll investigate it. Would CFF make this a non-issue because of being able to represent larger numbers?

@lemzwerg

This comment has been minimized.

Member

lemzwerg commented Mar 5, 2015

It's a non-issue in CFF because you don't need hint replacement subroutines at all. Instead, there is the hintmask operator (and cntrmask, for which there is no equivalent in Type 1 fonts).

The availability of large integers is another bonus but not important here, I guess.

@mskala

This comment has been minimized.

Member

mskala commented Mar 5, 2015

I'm not sure that FontForge decides how many subroutines it needs at the time it saves a file; it looks to me like there may be an array of subroutines generated at some earlier point, which are then dumped out in a fairly dumb way at file-generation time - so that it might still attempt to write a large number of subroutines even if they aren't needed in CFF. But maybe that array is only "extra" subroutines and wouldn't apply to the hint-replacement ones. Anyway, I'll look into it. Thanks for the assistance.

@KrasnayaPloshchad

This comment has been minimized.

KrasnayaPloshchad commented Mar 24, 2015

I found Wikipedia article explained FreeType’s subpixel rendering implementation is currently disabled by default to avoid Microsoft's patent about a specific color filtering algorithm for use in LCD display subpixel rendering.

@lemzwerg

This comment has been minimized.

Member

lemzwerg commented Mar 25, 2015

While the statement itself is true, it's not clear to me what the last comment wants to tell us...

@mskala

This comment has been minimized.

Member

mskala commented Mar 25, 2015

Maybe an attempt to help based on reading the title, and not the content, of this issue?

Anyway, I've gone through the code and found that in principle, making FontForge use CFF for its temporary file would work. It wouldn't be completely trivial because when FontForge goes to write its PostScript temporary file, it passes a parameter into a function saying "use Postscript"; and just changing that to say "use CFF" instead will cause the CFF write to silently fail, at which point FontForge will fall back to using Postscript again but without any hints - which succeeds, but doesn't process the hints. To get it right, correct rendering with hints, it needs a little more work to make it really write CFF. But that should be possible.

I got distracted looking at FontForge's handling of temporary files. Freetype requires a file image, that is, a sequence of bytes in the format that would exist in a font file on disk, but Freetype doesn't require that that actually be a file on disk. FontForge actually writes a file on disk (which we can hope the OS will keep in cache instead of REALLY on disk), then mmap()s it, and passes the block of memory to Freetype. That's pretty silly. I would really like to change FontForge to just write its data into memory. But that's not trivial because FontForge splits its font-writing code over many different functions, all passing FILE *s around, and it does a fair bit of seeking, opening and closing additional temporary files, and so. This is a common pattern throughout FontForge's code base. I wish it wasn't written that way.

Some parts of FontForge's file-writing code actually already include an abstraction layer to allow it to write through a filter (for compression, for example) instead of passing around a FILE * directly, but this is applied inconsistently.

At the very least, FontForge could read its temporary file back into memory before invoking Freetype, and have one less ball in the air at that point. There's no sensible reason for mmap() to be involved. Fixing the situation really properly means doing at least one of:

  • Expanding the scope of FontForge's current file-writing abstraction layer to cover all file writing
  • Writing a new abstraction layer for "things that behave sort of like files but can be stored in RAM" and having FontForge use pointers to such things everywhere it currently uses FILE *s
  • Using fopencookie(), which only works on Linux, or some equivalent specific to some other system, to allow FontForge to continue using FILE *s while having them not really be files
  • Rewriting it all in C++

At the moment I'm leaning toward writing a wrapper for tmpfile() which will use fopencookie() on Linux and fall back to the system tmpfile() everywhere else. That would at least improve matters for Linux users, and would make things no worse on other systems.

@ghlawrence2000

This comment has been minimized.

ghlawrence2000 commented Jul 5, 2015

Sorry, was a freetype problem I just posted in great detail before i realised this was fontforge area

@mskala

This comment has been minimized.

Member

mskala commented Jul 6, 2015

FWIW, this is now fixed in FontAnvil, by following @lemzwerg's suggestion of making the temporary font be CFF instead of PS. I don't have a clean patch that could be applied to FontForge, because the code in question in FontAnvil has been remodelled to use a FontAnvil-specific file abstraction layer and read directly out of a temporary memory buffer instead of using mmap(). However, it probably wouldn't be hard to do a simpler change in FontForge and just make it use CFF format while keeping the FontForge file-on-disk architecture. I'm not volunteering, because my own problem is solved and the absence of any reproduction of this bug by anyone else suggests that it may not really be an issue for many FontForge users.

jtanx added a commit to jtanx/fontforge that referenced this issue Jul 11, 2015

@jtanx jtanx closed this in f468b1c Jul 12, 2015

mskala added a commit that referenced this issue Jul 12, 2015

Merge pull request #2420 from jtanx/ftraster
Use CFF over PFB as intermediary for FT Rasterisation. Fixes #685
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment