[cairo] Serious concerns about cairo

Bill Spitzak 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 
consistent api.

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 
high-resolution devices.

* 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 
result in.

* 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.

III. Fonts:

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 mailing list