Description
I took a bit of time to understand how font handling works in this library and I want to discuss some improvements.
Context
To give you a bit of context I was trying to make tests for PS canvas work (see this branch). As a reminder, this code uses the PS extension itself based on pslib.
The blocking point is that PS canvas can't find the font. There are several reasons to that:
- First of all by default this driver uses the Helvetica font which is proprietary and is not installed on my system (other drivers use Time New Roman which can be installed simply from MS Core Fonts).
- Even if I try to change for another font, I need to convert it to AFM format.
- Even with that, to use a font, one has to call ps_findfont which ultimately call PS_findfont which by default looks only in the current directory so one has to play with the
SearchPath
parameter.
I think this was working on Windows since Helvetica is bundled on such systems and the code was written to find it (see below).
I also took some time on that because I want to port the PDF canvas to FPDF which also uses a different font format (a PHP file to be created by some internal tools from the TTF file).
How fonts are handled?
Fonts can become rather complicated (formats, terminology, conventions, etc). AFAICS the user of the library has 2 options:
- specify simple font properties (name, size, color, etc)
- specify the font file
This is a rather good mix of simplicity/flexibility. The driver is in charge of finding and using the correct font according to this (if possible).
From an implementation point of view, the tricky point is to find font files. Here is what I found:
- There is a constant called
IMAGE_CANVAS_SYSTEM_FONT_PATH
which points to "system" fonts. - There is also a local directory in
Image/Canvas/Fonts
which can hold fonts (a kind of local font path). - To find a font file, one has to call the method
Image_Canvas_Tool::fontMap
. This method will look intoImage/Canvas/Fonts
to translate a font name to a TTF file based on a text file (IIUC it can handle several files per font). If it can't find the file, it will look inIMAGE_CANVAS_SYSTEM_FONT_PATH
and inImage/Canvas/Fonts
(again). In theory, this method can look for other formats.
Problems
The experience with PS summarize the problems:
- Default values are not consistent/realistic
- Different drivers use different formats for the font and need some internal calls to manage the fonts: for instance, GD use a number to specify standard fonts but can also load a TTF file, pslib need to call
ps_findfont
to load a file, etc. This is not always handled correctly.
Here is a more detailed list of problems:
- The code to set
IMAGE_CANVAS_SYSTEM_FONT_PATH
is windows specific (based on$_SERVER['SystemRoot']
). This explain why I think that PS tests used to work on windows. As it is a constant, the user can't tune it. - The translation mechanism in
Image_Canvas_Tool::fontMap
is interesting there is no help to use it (i.e. installing fonts in it). As an aside, the current text files contains values but not the corresponding fonts. - In theory,
Image_Canvas_Tool::fontMap
can look for other formats but the call is buried inImage_Canvas::setFont
so not easy to overload.
Solutions
Here is what I propose:
- Use Time New Roman as the default font everywhere.
- The base class
Image_Canvas
should not try to find the font file (i.e. don't callImage_Canvas_Tool::fontMap
). This is a responsibility of the drivers (hence, they can use the same logic to find the format they need). - The code to define
IMAGE_CANVAS_SYSTEM_FONT_PATH
could be extended to test reasonable values for Linux and MacOS and give the user some way to add paths. - Provide ways to install fonts in
Image/Canvas/Fonts
and query installed fonts in the local font path with the translation mechanism. This way, the user can add fonts. Also if a driver needs a specific format, it can add the font here. As a bonus, add a method to simply install MS Core Fonts (to ease tests).
I think that those solutions are not too hard to implement.
Any idea ?
Activity