%!
/show { {pop pop} exch kshow } def
/ashow {
3 1 roll
{ ax ay
rmoveto
pop pop
}
dup 4 1 roll 0 2 getinterval astore pop
exch
kshow
} def
/widthshow {
4 1 roll
{ cx cy char
4 index eq { rmoveto }{ pop pop } ifelse
pop pop
}
dup 5 1 roll 0 3 getinterval astore pop
exch
kshow
} def
/awidthshow {
6 1 roll
{ cx cy char ax sy
rmoveto
4 index eq { rmoveto }{ pop pop } ifelse
pop pop
}
dup 7 1 roll 0 5 getinterval astore pop
exch
kshow
} def
100 500 moveto
/Courier 20 selectfont
0 10 32 5 -5 (Is This a Staircase?) awidthshow
(landing) show
I got a simple prototype working to load a Cairo font face using a
fontconfig pattern. It doesn't actually make any calls to FreeType,
but of course I had to read all the documentation for fontconfig
before discovering this, and it still needs FreeType in the compiler
search path.
But I'm not at all sure how patch this into the postscript mechanism.
As a first hack I tried just making a fonttype object whose payload
is a cairo_scaled_font*. findfont returns the face scaled to 1-pt.
scalefont extracts the cairo_font_face_t* from the cairo_scaled_font*
scales it and returns. And show just nul-terminates the string
and calls cairo_show_text. But it isn't working. I think it might
be better to defer calling cairo_scaled_font_create until setfont,
and findfont and scalefont just set the matrix somewhere (maybe
in the fontconfig pattern?).
520(0)12:35 AM:proto 0> make font CFLAGS='-I/usr/include/cairo -I/usr/
include/fontconfig -I/usr/include/freetype2' LDLIBS='-lcairo -lX11'
cc -I/usr/include/cairo -I/usr/include/fontconfig -I/usr/include/
freetype2 font.c -lcairo -lX11 -o font
521(0)12:35 AM:proto 0> font
522(0)12:36 AM:proto 0> cat font.c
#include <cairo.h>
#include <X11/Xlib.h>
#include <cairo-xlib.h>
#include <cairo-ft.h>
#include <fontconfig.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
enum { DEBUG = 0, WID = 24, HGT = 23, SCALE = 10 };
enum { BIG, LITTLE } endian = LITTLE;
struct state {
int width, height;
Display *dis;
int scr, depth;
Visual *vis;
XSetWindowAttributes attr;
int attrmask;
Window win;
cairo_t *cr;
cairo_surface_t *surface;
} sta;
int wopen(struct state *st, int wid, int hgt) {
st->width = wid * SCALE;
st->height = hgt * SCALE;
st->dis = XOpenDisplay(NULL);
st->scr = DefaultScreen(st->dis);
st->depth = DefaultDepth(st->dis, st->scr);
st->vis = DefaultVisual(st->dis, st->scr);
st->attr.background_pixel = WhitePixel(st->dis, st->scr);
st->attr.border_pixel = BlackPixel(st->dis, st->scr);
st->attr.event_mask = ExposureMask | StructureNotifyMask |
ButtonPressMask;
st->attrmask = CWColormap | CWBackPixel | CWBorderPixel |
CWEventMask;
st->win = XCreateWindow(st->dis, RootWindow(st->dis, st->scr),
200, 10, /* pos */
st->width, st->height, 5, /* width height border */
st->depth,
InputOutput,
st->vis,
st->attrmask, &st->attr);
XMapWindow(st->dis, st->win);
st->surface = cairo_xlib_surface_create(st->dis, st->win,
st->vis, st->width, st->height);
st->cr = cairo_create(st->surface);
}
int errseq = 0;
cairo_font_face_t *loadfont (struct state *st, char *name) {
cairo_status_t status;
FcPattern *pat;
cairo_font_face_t *face;
cairo_font_options_t *fopt;
/*
pat = FcPatternBuild (0, //FC_FILE, FcTypeString, "n022003l.pfb",
FC_FAMILY, FcTypeString, "Nimbus Mono L",
FC_STYLE, FcTypeString, "Regular", (char *) 0);
*/
//pat = FcPatternBuild (0, FC_FAMILY, FcTypeString, "Courier",
(char *) 0);
//pat = FcPatternBuild (0, FC_FT_FACE, FcTypeString, "Times",
(char *) 0);
pat = FcPatternBuild (0, FC_FT_FACE, FcTypeString, name, (char *)
0);
if ((++errseq, status = cairo_status(st->cr)))
fprintf(stderr, "%d Cairo error: %s\n", errseq,
cairo_status_to_string(status));
fopt = cairo_font_options_create();
if ((++errseq, status = cairo_font_options_status(fopt)))
fprintf(stderr, "%d Cairo font opt error: %s\n", errseq,
cairo_status_to_string(status));
if (FcConfigSubstitute(NULL/*conf*/, pat, FcMatchFont) != FcTrue)
fprintf(stderr, "FcConfigSubstitute failed\n");
cairo_ft_font_options_substitute(fopt, pat);
FcDefaultSubstitute(pat);
FcResult res;
pat = FcFontMatch(NULL/*conf*/, pat, &res);
if (res,0) { /* always gives Unknown value */
fprintf(stderr, "FcFontMatch returned result: %s\n",
res == FcResultMatch ? "FcResultMatch" :
res == FcResultNoMatch ? "FcResultNoMatch" :
res == FcResultTypeMismatch ? "FcResultTypeMismatch" :
res == FcResultNoId ? "FcResultNoId" :
res == FcResultOutOfMemory ? "FcResultOutOfMemory" :
"Unknown value");
}
if ((++errseq, status = cairo_status(st->cr)))
fprintf(stderr, "%d Cairo error: %s\n", errseq,
cairo_status_to_string(status));
face = cairo_ft_font_face_create_for_pattern(pat);
if ((++errseq, status = cairo_font_face_status(face))) //4
fprintf(stderr, "%d Cairo face error: %s\n", errseq,
cairo_status_to_string(status));
return face;
}
cairo_scaled_font_t *scalefont(struct state *st, cairo_font_face_t
*face, double sf) {
cairo_status_t status;
cairo_font_options_t *fopt;
cairo_matrix_t fmat, ctm;
cairo_scaled_font_t *font;
fopt = cairo_font_options_create();
if ((++errseq, status = cairo_font_options_status(fopt)))
fprintf(stderr, "%d Cairo font opt error: %s\n", errseq,
cairo_status_to_string(status));
cairo_matrix_init(&fmat, sf, 0, 0, sf, 0, 0);
cairo_get_matrix(st->cr, &ctm);
if ((++errseq, status = cairo_status(st->cr)))
fprintf(stderr, "%d Cairo error: %s\n", errseq,
cairo_status_to_string(status));
font = cairo_scaled_font_create(face, &fmat, &ctm, fopt);
if ((++errseq, status = cairo_font_options_status(fopt)))
fprintf(stderr, "%d Cairo font opt error: %s\n", errseq,
cairo_status_to_string(status));
if ((++errseq, status = cairo_scaled_font_status(font))) //7
fprintf(stderr, "%d Cairo font error: %s\n", errseq,
cairo_status_to_string(status));
if ((++errseq, status = cairo_status(st->cr)))
fprintf(stderr, "%d Cairo error: %s\n", errseq,
cairo_status_to_string(status));
return font;
}
enum { toy = 0 };
int main(void) {
//FcConfig *conf;
//FT_Library library;
cairo_status_t status;
cairo_font_face_t *face;
cairo_scaled_font_t *font;
wopen(&sta,WID,HGT);
/* it appears Cairo does indeed initialize everything */
//if (FcInit() != FcTrue) fprintf(stderr, "FcInit() returned
FcFalse\n");
//conf = FcInitLoadConfigAndFonts();
//if (FcInit() != FcTrue) fprintf(stderr, "FcInit() returned
FcFalse\n");
//if (FT_Init_FreeType(&library) != 0) fprintf(stderr, "failed to
Init FreeType\n");
if (toy) {
cairo_select_font_face(sta.cr, "Courier",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(sta.cr, 20);
} else {
face = loadfont(&sta, "Times");
font = scalefont(&sta, face, 20);
cairo_set_scaled_font(sta.cr, font);
if ((++errseq, status = cairo_status(sta.cr))) //9
fprintf(stderr, "%d Cairo error: %s\n", errseq,
cairo_status_to_string(status));
//cairo_font_options_destroy(fopt);
}
cairo_move_to(sta.cr, 20, 20);
cairo_set_source_rgb(sta.cr, 0, 0, 0);
if ((++errseq, status = cairo_status(sta.cr))) //10
fprintf(stderr, "%d Cairo error: %s\n", errseq,
cairo_status_to_string(status));
cairo_show_text(sta.cr, "show this text");
cairo_surface_flush(sta.surface);
if ((++errseq, status = cairo_status(sta.cr))) //11
fprintf(stderr, "%d Cairo error: %s\n", errseq,
cairo_status_to_string(status));
XFlush(sta.dis);
sleep(10);
}
Alright, I think I'm making some progress. I had an inspiration about
the gray transfer function; and that provided the key [ :) ] to
defering
the scaled_font_create. For the gray transfer, I stash the proc
object
in malloc'ed memory and attach it to the user data of the cairo
context
(which serves as the postscript 'graphics state'). Then setgray gets
redefined as
/setgray { currenttransfer exec setgray } bind def
Along the way I noticed that while pondering how save and restore
should 'implicitly' perform gsave/grestore, I had completely
overlooked
the fact that I should just explicitly call them. So if you can't
restore without grestore, it should be impossible to try to execute
an invalid proc array. (I hope I've thought this all the way through,
but it's that nagging doubt that leaves me with a nagging doubt.)
So for version 2 of the font kludge.
I stuff the cairo_font_face into the font object with a matrix
attached to the font_face_user_data. So setfont performs the final
step of creating the scaled font and installing it into the cairo
context.
Right now, scalefont just attaches a new matrix of the specified
point
size but it should concatenate with the old matrix first.
But despite this, the kludge allows the following program
to execute successfully:
591(1)05:13 AM:xpost2 0> !x
xpost
190 opcodes
219 definitions in systemdict
mcp launching executive
Xpost Version x2vC
PS>/Times findfont
loading font Times
PS<$>20 scalefont
PS<$>setfont
PS>72 72 moveto
PS>(Hello World!) show
showing (Hello World!)
Now it needs to evolve into a better design. Before I waste any time
or energy on polish and style.
Of course there should be a FontDirectory and findfont should look
there first; falling back to the fonconfig stuff. I'll hit the Type 1
manual to see what all should be in the dicts. I anticipate a
difficult
time extracting and converting all this data into a dictionary.
And then back out again.
I'm not sure when I started rambling. 2009? :)
Thank you for your patience. We'll get there.
Long live NeWS!
> Now it needs to evolve into a better design. Before I waste any time
> or energy on polish and style.
>
> Of course there should be a FontDirectory and findfont should look
> there first; falling back to the fonconfig stuff. I'll hit the Type 1
> manual to see what all should be in the dicts. I anticipate a
> difficult
> time extracting and converting all this data into a dictionary.
> And then back out again.
Well, I didn't do any of this, yet. But I have filled out the
font kludge with scalefont makefont charpath and stringwidth.
So two big tasks I have to tackle are the whole font as dictionary
thing, and performing a char-by-char incremental show so I can
implement kshow and finally, the whole *show suite on top of that.
The last one involves accessing the FreeType api and doing crazy
unicode conversions and extractions (it seems iconv is the tool
for that).
So I uploaded this thing, and before announcing it, I ran it one
more time and hit the weirdest bug yet. An off-by-one in dict dict
copy which manifested as a rangecheck error in get while trying
to get data out of an empty array. The array came from a dict
which was a (nearly complete) copy of another dict which was a
copy of another dict. I had to beef-up the debugging instrumentation
in the program itself so I could turn on/off ridiculous detail
by interrupting in gdb and changing the DEBUGxxx variable.
I suppose the bugs only get weirder don't they?
I've redesigned part of the font stuff.
findfont is now a procedure which checks FontDirectory (initially
empty)
and falls back to creating and installing a new dictionary
with the cairo_font_face_t* in the fonttype object stored
under the key /Private.
scalefont and makefont are procedures.
setfont creates a cairo_scaled_font_t using the face
and the FontMatrix, and stashes the dictionary in the
cairo_scaled_font_user_data before calling
cairo_set_scaled_font.
currentfont calls cairo_get_scaled_font and extracts
the dictionary from the user_data.
This should finally give the correct semantics for these.
Specifically, you can now have 2 (or more) copies of the
same font at different scales; at the same time!