Improving GD's way of handling missing glyphs in fonts (currently, ttftext/ftbbox have a bug-by-design)
[email protected] ("PmI") Sat, 13 Feb 2010 13:07:50 +0100
| Newsgroups | php.gd.devel |
|---|---|
| Organization | nihongoresources.com |
| Message-ID | <[email protected]> |
Hi,
to just start with the problem: currently, GD fails silently when instructed
to write text to an image, because it will substitute missing characters in a
font with a box shape, rather than indicating it can only render the text if
it effectively rewrites the text, thus not writing what the programmer wants
GD to write.
I know that GD relies on FreeType for its textual aid (in current
incarnations of GD, FreeType 2.x), and this means that GD has access to, and
probably should make accessible in its own API, the FT_GET_CHAR_INDEX
function. This function returns '0' when a character is not supported, or any
other number when it is (in line with the opentype specification).
So to make GD behave properly, I recommend two extensions on the way GD
handles writing text to images, and one new function to be added to GD:
1) an extension of the "imageftbbox" function, adding an optional int
constant (say: FAIL_ON_SUBSTITUTION) so that it returns "false" when this is
passed as argument for a text that cannot be rendered without substitutions.
2) an extension of the "imagefttext" function, adding the same optional int
constant to the function's parameter list to indicate whether it is allowed
to substitute, or whether it should fail on substitution (returning false).
3) a new function "imagettftextsupported(string $fontlocation, string $text)"
(or an equally indicative name) that can be used to determine whether a font
will be able to render the given text. This should be a relatively easy
function, as all it has to do is essentially:
$chars = preg_split("//u",$text);
foreach($chars as $char) {
if(FREETYPE2->FT_GET_CHAR_INDEX($fontlocation, uniord($char))==0) {
// very simple shortcutting, since a single substitution is enough
// to indicate that a text render will not yield the desired text.
return false; }}
return true;
(preg split on //u, since FreeType expects unicode character codes, uniord is
not a standard php function but can be found relatively easily, such as at
http://www.php.net/manual/en/function.ord.php#77905)
With the availability of this "does it exist?" function, a programmer can
structure their code in a way that lets them deal with the eventuality that
GD cannot do what the want with the fonts and text it's been given.
(Also, note that the extensions keep the functions backward compatible.
Nothing breaks, but programmar control is greatly improved)
---
To illustrate a use case, I'll use the one I'm currently faced with:
I am writing a script for a webpage that shows what CJK characters look like
in various writing styles (print form, hand written, caligraphic, etc). For
each of these styles, there are different fonts available, but not every CJK
character is supported by each font. In order to generate proper images, GD
has to know which character is supported by which font, so that it does not
generate an image with substitution boxes, rather than actual characters.
To deal with this problem for now without leaving PHP, I had to write my own
PHP TTF parser (which is relatively easy, as long as it's a real opentype
with truetype outline font, and not a font that has a .ttf extension but is
actually a type 1 font). This parser lets me determine whether or not a font
supports a particular character by performing the same check that FreeType
does in its FT_GET_CHAR_INDEX function. If the result is 0, I know the
character cannot be rendered by a font, and so it is passed over when drawing
a character's writing styles onto the image. This works, of course, but
ideally I would simply rely on GD for this functionality, so that I could
write something like the following:
$char = 0x1234; // some unicode character
if(imagettftextsupported($myfont, $char))
{
$bounds = imageftbbox($pointsize, 0, $myfont, $char);
$xpox = ... // something intelligent based on $bounds
$ypos = ... // idem ditto
imagettftext($im, $pointsie, 0, $xpos, $ypos, $fontcolor, $myfont, $char);
}
or, alternatively,
$char = 0x1234;
if(($bounds = imageftbbox($pointsize, 0, $myfont, $char,
FAIL_ON_SUBSTITUTION))!==false) {
$xpox = ...
$ypos = ...
imagettftext($im, $pointsie, 0, $xpos, $ypos, $fontcolor, $myfont, $char);
}
Offering the FAIL_ON_SUBSTITUTION behaviour would be my minimum requirement
for a next GD, so that its text function is not longer bugged -by-design:
since right now technically the ttftext and ftbbox functions fail to do what
they promise (writing a text with boxes has GD making a decision it should
not be making without the programmer instructing it to), their behaviour is
according to spec, and in violation of their conceptual promise =)
The availability of the function to determine whether a text will require
substitutions, in addition to the error catching in the box/text functions,
would offer programmers much more control than they would otherwise have, and
since it would probably be needed to improve the box/text functions anyway,
there's every reason to include it in GD's PHP API.
I'd be happy to discuss the finer points if someone thingks there was
something lacking in this problem description.
- Mike "Pomax" Kamermans
nihongoresources.com