[cairo] Serious concerns about cairo
spitzak at d2.com
Mon Sep 25 15:25:54 PDT 2006
The biggest problem I have with Cairo is the fact that the pen and font
is changed by the current transform and the lack of a "hairline" mode.
The result is that Cairo transforms, except for translation, are
completely useless to me, and I suspect they are useless to any program
trying to do anything other than preview a printout scaled to a window.
Fixing this *IS* a change to the API, but considering that Carl also
suggested this, I feel it is possible.
An alternative is to remove transforms completely from Cairo. In the
current state they are similar to the "toy font api" in that they cannot
be used by real programs. Maybe scrapping all the transforms, except for
the ability to specify the matrix for patterns, would make a more
I. Proposed fix for the transforms:
* The current font (it's transformation), the current pen, and the
current dash pattern, are "frozen" in device space at the moment they
are specified. Changing the CTM does not change these settings. This is
already true of the clip path, the source pattern, the destination surface.
* Font metrics that return rectangles (such as bounding boxes, offsets
to parts of the image, and the horizontal/vertical advance pairs) return
a rectangle with the same *area* as if the font was not rotated. This
appears to be the most useful value. Rotating the CTM after specifying
the font is not recommended, so we don't care about preventing
intersections, and any other solution results in huge boxes.
* Requesting things like the font and pen inverse-transforms the device
coordinates to the ctm. This matches how font metrics are returned.
* Add an api to specify the pen as a 2x2 matrix, used to transform a
.5-radius circle to get the pen. This is so that a get-pen api can work.
* Dashed lines are done by inverse-transforming the path to pen space,
converting the path to dashes using the simpler unit circle for the pen,
and then transforming the resulting path back and filling it. Thus
dashes will scale with the size and shape of the pen.
* There is a "dash size" variable, and the dash pattern is multiplied by
this before use to make the dash. This is for back compatability. The
older set-pen that takes a single number r for the pen sets the pen
matrix to [r 0 0 r]*ctm and sets the dash size to 1/r. Add an api to
directly get/set the "dash size".
II. Proposed fix for "hairline" mode:
Currently any software that wants to draw anything like this cannot use
Cairo's transforms. Also the thick-or-blurry lines by default really
annoy new users of Cairo. And this would eliminate probably 90% of the
requests for "turn off antialiasing".
* Hairline mode completely replaces the stroke algorithm with different
code when turned on.
* Setting the line width to zero (or setting the pen to a degenerate
matrix) puts Cairo into "hairline" mode. Setting the pen to a non-zero
size (including negative) turns off hairline mode.
* If the CTM is degernate, then setting a non-zero pen does *not* result
in hairline mode, but instead in an invisible pen (stroke is a no-op).
Setting a zero width pen *does* result in hairline mode.
* Horizontal and vertical strokes are an odd number of 100% opaque
pixels wide, and centered over the antialiased pixels produced by
filling the same path. This odd number is a constant that depends on the
back end, it is 1 for most screens, but may be higher for
* Diagonal strokes produce a stroke the correct width and position so
that they will join nicely to a horizontal or vertical line that would
be joined to their ends. The *are* antialiased.
* Line caps and joins are ignored. Instead something is done so that
right-angles are filled opaque out to a sharp corner, and horizontal and
vertical edges end such that drawing another line at right angles from
that point to produce a short opaque corner. All other joins and caps
can do whatever it is easiest for the algorithim that produces this to
* Dash patterns act as if one dash unit equals one unit in the ctm at
the time the pen was set.
* A new cairo context defaults to hairline mode.
I have a simple requirement for any text api:
* To draw a UTF-8 string, there is an api that takes a pointer to the
string, it's length, and NOTHING ELSE, and the result is the text
appears on the output surface. Any structure or context or anything else
other than the cairo_t is by my definition, NOT "fun or easy to use",
and thus contrary to Cairo's design goals.
It sounds like the only solution is that Cairo should contain a layout
engine, possibly by doing the work necessary to extract if from Pango.
The "toy api" needs to call this, just like it is doing on the Mac and
Windows anyway. Text is formatted into an infinitly-wide rectangle to
the right of this point (IE if the backend decides to do bidi and draw
right-to-left, the last character is put to the right of this point).
Ideally all other api to the layout engine would be cleaned up. The
pango context would be inserted into the cairo context so the user only
has to keep one pointer. In keeping with cairo's style, remove any
structures from the api, instead you would do something like
"cairo_set_layoutfoo(args)" and then draw text, rather than having an
api that takes a Cairo_LayoutFoo* as an argument.
Setting the font must be done with an api that takes a single string. If
system-specific calls are used to select the font in a more complex way,
this must result in the current font name returning this information
such that if sent to the set-font api results in exactly the same font.
If sent to other backends it should never produce an error, but select a
similar font, or a random font if the other backend has no idea. It is a
vital requirement that the user's font settings be easily written to a
database as a single string.
Setting the font should result in as many glyphs as possible rendering.
This means it really is a "font set", but all fonts except for the first
one can be automatically generated by Cairo, with no controls provided
(except maybe config files). When the user picks the starwars font, they
are probably interested in the letters a-z looking like star wars, and
if they draw Chinese, it is ok if the chinese glyhps appear in a way
that does not look like starwars. This is infinetly better than drawing
When the user picks the "symbol" font, it means they expect the unicode
indexes 0-255 to produce greek glyphs, it does NOT mean "the symbol
glyphs are now available". If a Symbol font exists then the symbol
glyphs are ALWAYS available no matter what font you select.
There should be a call to return a set of font names that work. This
does not have to be the entire set of strings that the font-set can take
(which may be infinite in size) but should be a reasonable set.
As for the data itself, I would recommend that only UTF-8 and arrays of
32-bit unicode be accepted. If the back end does font sets, it should be
possible to completely hide "glyphs indexes".
More information about the cairo