psgeom.c revision e6bced22
1/************************************************************
2 Copyright (c) 1995 by Silicon Graphics Computer Systems, Inc.
3
4 Permission to use, copy, modify, and distribute this
5 software and its documentation for any purpose and without
6 fee is hereby granted, provided that the above copyright
7 notice appear in all copies and that both that copyright
8 notice and this permission notice appear in supporting
9 documentation, and that the name of Silicon Graphics not be
10 used in advertising or publicity pertaining to distribution
11 of the software without specific prior written permission.
12 Silicon Graphics makes no representation about the suitability
13 of this software for any purpose. It is provided "as is"
14 without any express or implied warranty.
15
16 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23 THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
25 ********************************************************/
26
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif
30
31#define	XK_TECHNICAL
32#define	XK_PUBLISHING
33#define	XK_KATAKANA
34#include <stdio.h>
35#include <ctype.h>
36#include <X11/X.h>
37#include <X11/Xlib.h>
38#include <X11/XKBlib.h>
39#include <X11/extensions/XKBgeom.h>
40#include <X11/extensions/XKM.h>
41#include <X11/extensions/XKBfile.h>
42#include <X11/keysym.h>
43#include <X11/Xutil.h>
44
45#if defined(sgi)
46#include <malloc.h>
47#endif
48
49#include <stdlib.h>
50
51#include "utils.h"
52#include "xkbprint.h"
53#include "isokeys.h"
54
55#define	FONT_NONE	-1
56#define	FONT_TEXT	0
57#define	FONT_LATIN1	1
58#define	FONT_SYMBOL	2
59#define	FONT_ISOCAPS	3
60#define	FONT_MOUSECAPS	4
61
62typedef struct {
63    Display *           dpy;
64    XkbDescPtr          xkb;
65    XkbGeometryPtr      geom;
66    int                 totalKB;
67    int                 kbPerPage;
68    int                 black;
69    int                 white;
70    int                 color;
71    int                 font;
72    int                 fontSize;
73    int                 nPages;
74    int                 x1, y1;
75    int                 x2, y2;
76    XKBPrintArgs *      args;
77} PSState;
78
79#define	G1L1		0
80#define	G1L2		1
81#define	G2L1		2
82#define	G2L2		3
83#define	CENTER		4
84
85#define	G1L1_MASK	(1<<G1L1)
86#define	G1L2_MASK	(1<<G1L2)
87#define	G2L1_MASK	(1<<G2L1)
88#define	G2L2_MASK	(1<<G2L2)
89#define	CENTER_MASK	(1<<CENTER)
90
91#define	LABEL_MASK	(0x1f)
92#define	GXL1_MASK	(G1L1_MASK|G2L1_MASK)
93#define	GXL2_MASK	(G1L2_MASK|G2L2_MASK)
94#define	G1LX_MASK	(G1L1_MASK|G1L2_MASK)
95#define	G2LX_MASK	(G2L1_MASK|G2L2_MASK)
96#define	GXLX_MASK	(0x0f)
97
98#define	NLABELS		5
99#define	LABEL_LEN	30
100
101#define	SZ_AUTO		0
102#define	SZ_TINY		1
103#define	SZ_SMALL	2
104#define	SZ_MEDIUM	3
105#define	SZ_LARGE	4
106#define	SZ_XLARGE	5
107
108typedef struct {
109    unsigned    present;
110    Bool        alpha[2];
111    char        label[NLABELS][LABEL_LEN];
112    int         font[NLABELS];
113    int         size[NLABELS];
114} KeyTop;
115
116#define	DFLT_LABEL_FONT "Helvetica-Narrow-Bold"
117#define DFLT_LABEL_FONT_SIZE 10
118
119/***====================================================================***/
120
121typedef struct _PSFontDef {
122    const char *name;
123    const char **def;
124} PSFontDef;
125
126static PSFontDef internalFonts[] = {
127    { "IsoKeyCaps", &IsoKeyCaps }
128};
129static int nInternalFonts = (sizeof(internalFonts) / sizeof(PSFontDef));
130
131static void
132ListInternalFonts(FILE *out, int first, int indent)
133{
134    register int i, n, nThisLine;
135
136    for (n = 0; n < first; n++) {
137        putc(' ', out);
138    }
139
140    for (nThisLine = i = 0; i < nInternalFonts; i++) {
141        if (nThisLine == 4) {
142            fprintf(out, ",\n");
143            for (n = 0; n < indent; n++) {
144                putc(' ', out);
145            }
146            nThisLine = 0;
147        }
148        if (nThisLine == 0)
149            fprintf(out, "%s", internalFonts[i].name);
150        else
151            fprintf(out, ", %s", internalFonts[i].name);
152        nThisLine++;
153    }
154    if (nThisLine != 0)
155        fprintf(out, "\n");
156    return;
157}
158
159static Bool
160PSIncludeFont(FILE *out, const char *font)
161{
162    const char **pstr;
163    register int i;
164
165    pstr = NULL;
166    for (i = 0; (i < nInternalFonts) && (pstr == NULL); i++) {
167        if (uStringEqual(internalFonts[i].name, font))
168            pstr = internalFonts[i].def;
169    }
170    if (pstr != NULL) {
171        fprintf(out, "%%%%BeginFont: %s\n", font);
172        fprintf(out, "%s", *pstr);
173        fprintf(out, "%%%%EndFont\n");
174        return True;
175    }
176    return False;
177}
178
179Bool
180DumpInternalFont(FILE *out, const char *fontName)
181{
182    if (strcmp(fontName, "IsoKeyCaps") != 0) {
183        uError("No internal font named \"%s\"\n", fontName);
184        uAction("No font dumped\n");
185        fprintf(stderr, "Current internal fonts are: ");
186        ListInternalFonts(stderr, 0, 8);
187        return False;
188    }
189    PSIncludeFont(out, fontName);
190    return True;
191}
192
193/***====================================================================***/
194
195static void
196PSColorDef(FILE *out, PSState *state, XkbColorPtr color)
197{
198    int tmp;
199
200    fprintf(out, "/C%03d ", color->pixel);
201    if (uStrCaseEqual(color->spec, "black")) {
202        state->black = color->pixel;
203        fprintf(out, "{ 0 setgray } def      %% %s\n", color->spec);
204    }
205    else if (uStrCaseEqual(color->spec, "white")) {
206        state->white = color->pixel;
207        fprintf(out, "{ 1 setgray } def      %% %s\n", color->spec);
208    }
209    else if ((sscanf(color->spec, "grey%d", &tmp) == 1) ||
210             (sscanf(color->spec, "gray%d", &tmp) == 1) ||
211             (sscanf(color->spec, "Grey%d", &tmp) == 1) ||
212             (sscanf(color->spec, "Gray%d", &tmp) == 1)) {
213        fprintf(out, "{ %f setgray } def	    %% %s\n",
214                1.0 - (((float) tmp) / 100.0), color->spec);
215    }
216    else if ((tmp = (uStrCaseEqual(color->spec, "red") * 100)) ||
217             (sscanf(color->spec, "red%d", &tmp) == 1)) {
218        fprintf(out, "{ %f 0 0 setrgbcolor } def %% %s\n",
219                (((float) tmp) / 100.0), color->spec);
220    }
221    else if ((tmp = (uStrCaseEqual(color->spec, "green") * 100)) ||
222             (sscanf(color->spec, "green%d", &tmp) == 1)) {
223        fprintf(out, "{ 0 %f 0 setrgbcolor } def %% %s\n",
224                (((float) tmp) / 100.0), color->spec);
225    }
226    else if ((tmp = (uStrCaseEqual(color->spec, "blue") * 100)) ||
227             (sscanf(color->spec, "blue%d", &tmp) == 1)) {
228        fprintf(out, "{ 0 0 %f setrgbcolor } def %% %s\n",
229                (((float) tmp) / 100.0), color->spec);
230    }
231    else
232        fprintf(out, "{ 0.9 setgray       } def %% BOGUS! %s\n", color->spec);
233}
234
235static void
236PSSetColor(FILE *out, PSState *state, int color)
237{
238    if ((state->args->wantColor) && (state->color != color)) {
239        fprintf(out, "C%03d %% set color\n", color);
240        state->color = color;
241    }
242    return;
243}
244
245static void
246PSGSave(FILE *out, PSState *state)
247{
248    fprintf(out, "gsave\n");
249    return;
250}
251
252static void
253PSGRestore(FILE *out, PSState *state)
254{
255    fprintf(out, "grestore\n");
256    state->color = -1;
257    state->font = FONT_NONE;
258    state->fontSize = -1;
259    return;
260}
261
262static void
263PSShapeDef(FILE *out, PSState *state, XkbShapePtr shape)
264{
265    int o, p;
266    XkbOutlinePtr ol;
267
268    {
269        char *a = XkbAtomGetString(state->dpy, shape->name);
270        fprintf(out, "/%s {\n", a);
271        XFree(a);
272    }
273    fprintf(out, "	gsave translate rotate /SOLID exch def\n");
274    for (o = 0, ol = shape->outlines; o < shape->num_outlines; o++, ol++) {
275        XkbPointPtr pt;
276
277        if ((shape->num_outlines > 1) && (ol == shape->approx))
278            continue;
279        pt = ol->points;
280        fprintf(out, "%%	Outline %d\n", o + 1);
281        if (ol->num_points == 1) {
282            if (ol->corner_radius < 1) {
283                fprintf(out, "	  0   0 moveto\n");
284                fprintf(out, "	%3d   0 lineto\n", pt->x);
285                fprintf(out, "	%3d %3d lineto\n", pt->x, pt->y);
286                fprintf(out, "	  0 %3d lineto\n", pt->y);
287                fprintf(out, "	  0   0 lineto\n");
288                fprintf(out, "	SOLID { fill } { stroke } ifelse\n");
289            }
290            else {
291                fprintf(out, "	mark\n");
292                fprintf(out, "	%3d   0 moveto\n", ol->corner_radius);
293                fprintf(out, "	%3d   0 %3d %3d %3d arcto\n", pt->x, pt->x,
294                        pt->y, ol->corner_radius);
295                fprintf(out, "	%3d %3d   0 %3d %3d arcto\n", pt->x, pt->y,
296                        pt->y, ol->corner_radius);
297                fprintf(out, "	  0 %3d   0   0 %3d arcto\n", pt->y,
298                        ol->corner_radius);
299                fprintf(out, "     0   0 %3d   0 %3d arcto\n", pt->x,
300                        ol->corner_radius);
301                fprintf(out, "	SOLID { fill } { stroke } ifelse\n");
302                fprintf(out, "	cleartomark\n");
303            }
304        }
305        else if (ol->num_points == 2) {
306            if (ol->corner_radius < 1) {
307                fprintf(out, "	%3d %3d moveto\n", pt[0].x, pt[0].y);
308                fprintf(out, "	%3d %3d lineto\n", pt[1].x, pt[0].y);
309                fprintf(out, "	%3d %3d lineto\n", pt[1].x, pt[1].y);
310                fprintf(out, "	%3d %3d lineto\n", pt[0].x, pt[1].y);
311                fprintf(out, "	%3d %3d lineto\n", pt[0].x, pt[0].y);
312                fprintf(out, "	SOLID { fill } { stroke } ifelse\n");
313            }
314            else {
315                fprintf(out, "	mark\n");
316                fprintf(out, "	%3d %3d moveto\n", pt[0].x + ol->corner_radius,
317                        pt[0].y);
318                fprintf(out, "	%3d %3d %3d %3d %3d arcto\n", pt[1].x, pt[0].y,
319                        pt[1].x, pt[1].y, ol->corner_radius);
320                fprintf(out, "	%3d %3d %3d %3d %3d arcto\n", pt[1].x, pt[1].y,
321                        pt[0].x, pt[1].y, ol->corner_radius);
322                fprintf(out, "	%3d %3d %3d %3d %3d arcto\n", pt[0].x, pt[1].y,
323                        pt[0].x, pt[0].y, ol->corner_radius);
324                fprintf(out, "   %3d %3d %3d %3d %3d arcto\n", pt[0].x, pt[0].y,
325                        pt[1].x, pt[0].y, ol->corner_radius);
326                fprintf(out, "	SOLID { fill } { stroke } ifelse\n");
327                fprintf(out, "	cleartomark\n");
328            }
329        }
330        else {
331            if (ol->corner_radius < 1) {
332                fprintf(out, "	%3d %3d moveto\n", pt->x, pt->y);
333                pt++;
334                for (p = 1; p < ol->num_points; p++, pt++) {
335                    fprintf(out, "	%3d %3d lineto\n", pt->x, pt->y);
336                }
337                if ((pt->x != ol->points[0].x) || (pt->y != ol->points[0].y))
338                    fprintf(out, "	closepath\n");
339                fprintf(out, "	SOLID { fill } { stroke } ifelse\n");
340            }
341            else {
342                XkbPointPtr last;
343
344                last = &pt[ol->num_points - 1];
345                if ((last->x == pt->x) && (last->y == pt->y))
346                    last--;
347                fprintf(out, "	mark\n");
348                fprintf(out, "	%% Determine tangent point of first corner\n");
349                fprintf(out, "	%3d %3d moveto %d %d %d %d %d arcto\n",
350                        last->x, last->y,
351                        pt[0].x, pt[0].y, pt[1].x, pt[1].y, ol->corner_radius);
352                fprintf(out, "	/TY exch def /TX exch def pop pop newpath\n");
353                fprintf(out, "	%% Now draw the shape\n");
354                fprintf(out, "	TX TY moveto\n");
355                for (p = 1; p < ol->num_points; p++) {
356                    if (p < (ol->num_points - 1))
357                        last = &pt[p + 1];
358                    else
359                        last = &pt[0];
360                    fprintf(out, "	%3d %3d %3d %3d %3d arcto\n",
361                            pt[p].x, pt[p].y,
362                            last->x, last->y, ol->corner_radius);
363                }
364                last = &pt[ol->num_points - 1];
365                if ((last->x != pt->x) || (last->y != pt->y)) {
366                    fprintf(out, "	%3d %3d %3d %3d %3d arcto\n",
367                            pt[0].x, pt[0].y,
368                            pt[1].x, pt[1].y, ol->corner_radius);
369                }
370                fprintf(out, "	SOLID { fill } { stroke } ifelse\n");
371                fprintf(out, "	cleartomark\n");
372            }
373        }
374    }
375    fprintf(out, "	grestore\n");
376    fprintf(out, "} def\n");
377    return;
378}
379
380/***====================================================================***/
381
382typedef struct {
383    char *      foundry;
384    char *      face;
385    char *      weight;
386    char *      slant;
387    char *      setWidth;
388    char *      variant;
389    int         pixelSize;
390    int         ptSize;
391    int         resX;
392    int         resY;
393    char *      spacing;
394    int         avgWidth;
395    char *      encoding;
396} FontStuff;
397
398static void
399ClearFontStuff(FontStuff *stuff)
400{
401    if (stuff && stuff->foundry)
402        free(stuff->foundry);
403    bzero(stuff, sizeof(FontStuff));
404    return;
405}
406
407static Bool
408CrackXLFDName(const char *name, FontStuff *stuff)
409{
410    char *tmp;
411
412    if ((name == NULL) || (stuff == NULL))
413        return False;
414    if (name[0] == '-')
415        tmp = strdup(&name[1]);
416    else
417        tmp = strdup(name);
418    if (tmp == NULL)
419        return False;
420    stuff->foundry = tmp;
421
422    if ((tmp = strchr(tmp, '-')) == NULL)
423        goto BAILOUT;
424    else
425        *tmp++ = '\0';
426    stuff->face = tmp;
427
428    if ((tmp = strchr(tmp, '-')) == NULL)
429        goto BAILOUT;
430    else
431        *tmp++ = '\0';
432    stuff->weight = tmp;
433
434    if ((tmp = strchr(tmp, '-')) == NULL)
435        goto BAILOUT;
436    else
437        *tmp++ = '\0';
438    stuff->slant = tmp;
439
440    if ((tmp = strchr(tmp, '-')) == NULL)
441        goto BAILOUT;
442    else
443        *tmp++ = '\0';
444    stuff->setWidth = tmp;
445
446    if ((tmp = strchr(tmp, '-')) == NULL)
447        goto BAILOUT;
448    else
449        *tmp++ = '\0';
450    stuff->variant = tmp;
451
452    if ((tmp = strchr(tmp, '-')) == NULL)
453        goto BAILOUT;
454    else
455        *tmp++ = '\0';
456    if (*tmp == '*')
457        stuff->pixelSize = 0;
458    else if (sscanf(tmp, "%i", &stuff->pixelSize) != 1)
459        goto BAILOUT;
460
461    if ((tmp = strchr(tmp, '-')) == NULL)
462        goto BAILOUT;
463    else
464        *tmp++ = '\0';
465    if (*tmp == '*')
466        stuff->ptSize = 0;
467    else if (sscanf(tmp, "%i", &stuff->ptSize) != 1)
468        goto BAILOUT;
469
470    if ((tmp = strchr(tmp, '-')) == NULL)
471        goto BAILOUT;
472    else
473        *tmp++ = '\0';
474    if (*tmp == '*')
475        stuff->resX = 0;
476    else if (sscanf(tmp, "%i", &stuff->resX) != 1)
477        goto BAILOUT;
478
479    if ((tmp = strchr(tmp, '-')) == NULL)
480        goto BAILOUT;
481    else
482        *tmp++ = '\0';
483    if (*tmp == '*')
484        stuff->resY = 0;
485    else if (sscanf(tmp, "%i", &stuff->resY) != 1)
486        goto BAILOUT;
487
488    if ((tmp = strchr(tmp, '-')) == NULL)
489        goto BAILOUT;
490    else
491        *tmp++ = '\0';
492    stuff->spacing = tmp;
493
494    if ((tmp = strchr(tmp, '-')) == NULL)
495        goto BAILOUT;
496    else
497        *tmp++ = '\0';
498    if (*tmp == '*')
499        stuff->avgWidth = 0;
500    else if (sscanf(tmp, "%i", &stuff->avgWidth) != 1)
501        goto BAILOUT;
502
503    if ((tmp = strchr(tmp, '-')) == NULL)
504        goto BAILOUT;
505    else
506        *tmp++ = '\0';
507    stuff->encoding = tmp;
508    return True;
509 BAILOUT:
510    ClearFontStuff(stuff);
511    return False;
512}
513
514static void
515PSSetUpForLatin1(FILE *out, PSState *state)
516{
517    fprintf(out, "%s",
518            "save\n"
519            "/ISOLatin1Encoding where {pop save true}{false} ifelse\n"
520            "/ISOLatin1Encoding [\n"
521            "   /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
522            "   /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
523            "   /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
524            "   /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
525            "   /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
526            "   /.notdef /.notdef /space /exclam /quotedbl /numbersign\n"
527            "   /dollar /percent /ampersand /quoteright /parenleft\n"
528            "   /parenright /asterisk /plus /comma /minus /period\n"
529            "   /slash /zero /one /two /three /four /five /six /seven\n"
530            "   /eight /nine /colon /semicolon /less /equal /greater\n"
531            "   /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M\n"
532            "   /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft\n"
533            "   /backslash /bracketright /asciicircum /underscore\n"
534            "   /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m\n"
535            "   /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft\n"
536            "   /bar /braceright /asciitilde /guilsinglright /fraction\n"
537            "   /florin /quotesingle /quotedblleft /guilsinglleft /fi\n"
538            "   /fl /endash /dagger /daggerdbl /bullet /quotesinglbase\n"
539            "   /quotedblbase /quotedblright /ellipsis /trademark\n"
540            "   /perthousand /grave /scaron /circumflex /Scaron /tilde\n"
541            "   /breve /zcaron /dotaccent /dotlessi /Zcaron /ring\n"
542            "   /hungarumlaut /ogonek /caron /emdash /space /exclamdown\n"
543            "   /cent /sterling /currency /yen /brokenbar /section\n"
544            "   /dieresis /copyright /ordfeminine /guillemotleft\n"
545            "   /logicalnot /hyphen /registered /macron /degree\n"
546            "   /plusminus /twosuperior /threesuperior /acute /mu\n"
547            "   /paragraph /periodcentered /cedilla /onesuperior\n"
548            "   /ordmasculine /guillemotright /onequarter /onehalf\n"
549            "   /threequarters /questiondown /Agrave /Aacute\n"
550            "   /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n"
551            "   /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute\n"
552            "   /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute\n"
553            "   /Ocircumflex /Otilde /Odieresis /multiply /Oslash\n"
554            "   /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn\n"
555            "   /germandbls /agrave /aacute /acircumflex /atilde\n"
556            "   /adieresis /aring /ae /ccedilla /egrave /eacute\n"
557            "   /ecircumflex /edieresis /igrave /iacute /icircumflex\n"
558            "   /idieresis /eth /ntilde /ograve /oacute /ocircumflex\n"
559            "   /otilde /odieresis /divide /oslash /ugrave /uacute\n"
560            "   /ucircumflex /udieresis /yacute /thorn /ydieresis\n"
561            "] def {restore} if\n"
562            "/reencodeISO-1 {\n"
563            "    dup length dict begin\n"
564            "        {1 index /FID ne {def}{pop pop} ifelse} forall\n"
565            "        /Encoding ISOLatin1Encoding def\n"
566            "        currentdict\n"
567            "    end\n"
568            "} def\n"
569        );
570}
571
572static void
573PSReencodeLatin1Font(FILE *out, const char *font)
574{
575    fprintf(out, "/%s findfont reencodeISO-1\n", font);
576    fprintf(out, "	/%s-8859-1 exch definefont pop\n", font);
577    return;
578}
579
580static void
581PSSetUpFonts(FILE *out, const char *textFont, int size)
582{
583    fprintf(out, "/F%d { /%s findfont exch scalefont setfont } def\n",
584            FONT_TEXT, textFont);
585    fprintf(out, "/F%d { /%s-8859-1 findfont exch scalefont setfont } def\n",
586            FONT_LATIN1, textFont);
587    fprintf(out, "/F%d { /%s findfont exch scalefont setfont } def\n",
588            FONT_SYMBOL, "Symbol");
589    fprintf(out, "/F%d { /%s findfont exch scalefont setfont } def\n",
590            FONT_ISOCAPS, "IsoKeyCaps");
591    return;
592}
593
594static void
595PSSetFont(FILE *out, PSState *state, int font, int size, int pts)
596{
597    if ((state->font != font) || (state->fontSize != size)) {
598        fprintf(out, "%d %sF%d\n", size, (pts ? "pts " : ""), font);
599        state->font = font;
600        state->fontSize = size;
601    }
602    return;
603}
604
605static void
606PSProlog(FILE *out, PSState *state)
607{
608    register int i;
609
610    if (!state->args->wantEPS) {
611        fprintf(out,
612                "%%!PS-Adobe-2.0\n"
613                "%%%%Creator: xkbprint\n");
614        if (state->geom->name != None) {
615            char *a = XkbAtomGetString(state->dpy, state->geom->name);
616            fprintf(out, "%%%%Title: %s\n", a);
617            XFree(a);
618        }
619        fprintf(out,
620                "%%%%BoundingBox: (atend)\n"
621                "%%%%Pages: 1\n"
622                "%%%%PageOrder: Ascend\n"
623                "%%%%DocumentFonts: (atend)\n"
624                "%%%%DocumentData: Clean7Bit\n"
625                "%%%%Orientation: Landscape\n"
626                "%%%%EndComments\n"
627                "%%%%BeginProlog\n"
628                "%% Resolution is 1/10mm -- need pt sizes for fonts\n"
629                "clippath pathbbox\n"
630                "    /ury exch def /urx exch def\n"
631                "    /llx exch def /lly exch def\n"
632                "    newpath\n"
633                "/devwidth  urx llx sub def\n"
634                "/devheight ury lly sub def\n");
635    }
636    else {
637        int w, h;
638        int pw, ph;
639
640        w = (((state->geom->width_mm * 72) / 254) * 11) / 10;
641        h = (((state->geom->height_mm * 72) / 254) * 11) / 10;
642        if (state->kbPerPage > 1)
643            h *= (state->kbPerPage + 1);
644
645        if (w <= h) {
646            pw = 7.5 * 72;
647            ph = 10 * 72;
648        }
649        else {
650            pw = 10 * 72;
651            ph = 7.5 * 72;
652        }
653        while ((w > pw) || (h > ph)) {
654            w = (w * 9) / 10;
655            h = (h * 9) / 10;
656        }
657
658        fprintf(out, "%%!PS-Adobe-2.0 EPSF-2.0\n");
659        fprintf(out, "%%%%BoundingBox: 0 0 %d %d\n", w, h);
660        fprintf(out, "%%%%Creator: xkbprint\n");
661        if (state->geom->name != None) {
662            char *a = XkbAtomGetString(state->dpy, state->geom->name);
663            fprintf(out, "%%%%Title: %s\n", a);
664            XFree(a);
665        }
666        fprintf(out, "%%%%Pages: 1\n");
667        fprintf(out, "%%%%EndComments\n");
668        fprintf(out, "%%%%BeginProlog\n");
669        fprintf(out, "/ury 0 def /urx 0 def\n");
670        fprintf(out, "/llx %d def /lly %d def\n", w, h);
671        fprintf(out, "/devwidth %d def /devheight %d def\n", w, h);
672    }
673    fprintf(out, "/kbdwidth %d def\n", state->geom->width_mm);
674    fprintf(out, "/kbdheight %d def\n", state->geom->height_mm);
675    fprintf(out, "%s",
676            "/pts { 254 mul 72 div } def\n"
677            "/mm10 { 72 mul 254 div } def\n"
678            "/landscape? {\n"
679            "	devheight devwidth gt {\n"
680            "		/pwidth devheight def /pheight devwidth def\n"
681            "		0 devheight translate\n"
682            "		-90 rotate\n"
683            "	} {\n"
684            "		/pwidth devwidth def /pheight devheight def\n"
685            "	} ifelse\n"
686            "	0 pheight translate\n"
687            "	1 -1 scale\n"
688            "} def\n"
689            "/centeroffset {\n"
690            "    /S     exch def\n"
691            "    /HEIGHT exch def\n"
692            "    /WIDTH exch def\n"
693            "    S stringwidth /SH exch def /SW exch def\n"
694            "    WIDTH SW sub 2 div\n"
695            "    HEIGHT SH sub 2 div\n"
696            "} def\n");
697    PSSetUpForLatin1(out, state);
698    PSReencodeLatin1Font(out, DFLT_LABEL_FONT);
699    if (state->args->wantColor) {
700        XkbGeometryPtr geom = state->geom;
701
702        for (i = 0; i < geom->num_colors; i++) {
703            PSColorDef(out, state, &geom->colors[i]);
704        }
705        if (state->black < 0) {
706            XkbColorPtr color;
707
708            if (!(color = XkbAddGeomColor(geom, "black", geom->num_colors)))
709                uFatalError("Couldn't allocate black color!\n");
710            PSColorDef(out, state, color);
711        }
712        if (state->white < 0) {
713            XkbColorPtr color;
714
715            if (!(color = XkbAddGeomColor(geom, "white", geom->num_colors)))
716                uFatalError("Couldn't allocate white color!\n");
717            PSColorDef(out, state, color);
718        }
719    }
720    for (i = 0; i < state->geom->num_shapes; i++) {
721        PSShapeDef(out, state, &state->geom->shapes[i]);
722    }
723    if (state->args->label == LABEL_SYMBOLS) {
724        PSIncludeFont(out, "IsoKeyCaps");
725    }
726    PSSetUpFonts(out, DFLT_LABEL_FONT, DFLT_LABEL_FONT_SIZE);
727    fprintf(out, "%%%%EndProlog\n");
728    return;
729}
730
731static void
732PSFileTrailer(FILE *out, PSState *state)
733{
734    fprintf(out, "restore\n");
735    if (!state->args->wantEPS)
736        fprintf(out, "%%%%Trailer\n");
737    fprintf(out, "%%%%EOF\n");
738    return;
739}
740
741static void
742PSPageSetup(FILE *out, PSState *state, Bool drawBorder)
743{
744    XkbGeometryPtr geom;
745
746    geom = state->geom;
747    if (state->kbPerPage == 1) {
748        fprintf(out, "%%%%Page: %d %d\n", state->nPages + 1, state->nPages + 1);
749        fprintf(out, "%%%%BeginPageSetup\n");
750    }
751    else if ((state->nPages & 1) == 0) {        /* even page */
752        int realPage;
753
754        realPage = state->nPages / 2 + 1;
755        fprintf(out, "%%%%Page: %d %d\n", realPage, realPage);
756        fprintf(out, "%%%%BeginPageSetup\n");
757        fprintf(out, "%% Keyboard %d\n", state->nPages + 1);
758        if (state->nPages == 0) {
759            fprintf(out,
760                    "/realwidth devwidth def\n"
761                    "/realheight devheight def\n"
762                    "/devheight realheight 3 div def\n");
763        }
764        fprintf(out, "0 devheight dup 2 div add translate\n");
765    }
766    else {
767        fprintf(out, "%% Keyboard %d\n", state->nPages + 1);
768    }
769    fprintf(out, "save\n");
770    fprintf(out, "landscape?\n");
771    if (state->args->scaleToFit) {
772        fprintf(out,
773                "%% Scale keyboard to fit on the page\n"
774                "/kbdscale pwidth 72 sub kbdwidth div def\n"
775                "/kbdscalewidth kbdwidth kbdscale mul def\n"
776                "/kbdscaleheight kbdheight kbdscale mul def\n"
777                "/kbx 36 def\n"
778                "/kby pheight kbdscaleheight sub 2 div def\n");
779        PSGSave(out, state);
780        fprintf(out,
781                "kbx kby translate\n"
782                "kbdscale kbdscale scale\n");
783    }
784    else {
785        fprintf(out,
786                "%% Draw keyboard full size\n"
787                "/kbdscale 1 def\n"
788                "/kbdscalewidth kbdwidth mm10 def\n"
789                "/kbdscaleheight kbdheight mm10 def\n"
790                "/kbx pwidth kbdscalewidth sub 2 div def\n"
791                "/kby pheight kbdscaleheight sub 2 div def\n");
792        PSGSave(out, state);
793        fprintf(out,
794                "kbx kby translate\n"
795                "72 254 div dup scale\n");
796    }
797    if (drawBorder) {
798        if (state->args->wantColor) {
799            PSSetColor(out, state, geom->base_color->pixel);
800            fprintf(out, "  0   0 moveto\n");
801            fprintf(out, "%3d   0 lineto\n", geom->width_mm);
802            fprintf(out, "%3d %3d lineto\n", geom->width_mm, geom->height_mm);
803            fprintf(out, "  0 %3d lineto\n", geom->height_mm);
804            fprintf(out, "closepath fill\n");
805        }
806        PSSetColor(out, state, state->black);
807        fprintf(out, "  0   0 moveto\n");
808        fprintf(out, "%3d   0 lineto\n", geom->width_mm);
809        fprintf(out, "%3d %3d lineto\n", geom->width_mm, geom->height_mm);
810        fprintf(out, "  0 %3d lineto\n", geom->height_mm);
811        fprintf(out, "closepath stroke\n");
812    }
813    fprintf(out, "%%%%EndPageSetup\n");
814    return;
815}
816
817static void
818PSPageTrailer(FILE *out, PSState *state)
819{
820    char *name;
821    XkbDescPtr xkb;
822    XkbGeometryPtr geom;
823    XkbPropertyPtr prop;
824    int p, baseline;
825
826    xkb = state->xkb;
827    geom = state->geom;
828    if (state->args->grid > 0) {
829        fprintf(out, "%% Draw a %dmm grid\n", state->args->grid);
830        fprintf(out, "0 setlinewidth\n");
831        fprintf(out, "0.25 setgray\n");
832        fprintf(out, " 0 %d %d {\n", state->args->grid * 10, geom->width_mm);
833        fprintf(out, "    /GX exch def\n");
834        fprintf(out, "    GX 0 moveto GX %d lineto stroke\n", geom->height_mm);
835        fprintf(out, "} for\n");
836        fprintf(out, " 0 %d %d {\n", state->args->grid * 10, geom->height_mm);
837        fprintf(out, "    /GY exch def\n");
838        fprintf(out, "    0 GY moveto %d GY lineto stroke\n", geom->width_mm);
839        fprintf(out, "} for\n");
840    }
841    PSGRestore(out, state);
842    name = NULL;
843    for (p = 0, prop = geom->properties; p < geom->num_properties; p++, prop++) {
844        if ((prop->value != NULL) && (uStrCaseEqual(prop->name, "description"))) {
845            name = prop->value;
846            break;
847        }
848    }
849    if ((!state->args->wantEPS) &&
850        ((state->kbPerPage == 1) || ((state->nPages & 1) == 1) ||
851         (state->nPages == state->totalKB))) {
852        char *a1 = NULL;
853
854        if ((name == NULL) && (geom->name != None))
855            name = a1 = XkbAtomGetString(state->dpy, geom->name);
856
857        baseline = 16;
858        if ((name != NULL) || (state->args->label == LABEL_SYMBOLS)) {
859            PSSetColor(out, state, state->black);
860            PSSetFont(out, state, FONT_LATIN1, 14, False);
861        }
862        if (state->args->label == LABEL_SYMBOLS) {
863            char buf[40], *lbuf;
864            const char *sName = NULL;
865            char *a2 = NULL;
866            Atom sAtom;
867
868            if (state->args->nLabelGroups == 1)
869                snprintf(buf, sizeof(buf), "Group %d",
870                         state->args->baseLabelGroup + 1);
871            else
872                snprintf(buf, sizeof(buf), "Groups %d-%d",
873                         state->args->baseLabelGroup + 1,
874                         state->args->baseLabelGroup +
875                         state->args->nLabelGroups);
876            fprintf(out, "kbx kbdscalewidth 0 (%s) centeroffset pop add\n",
877                    buf);
878            fprintf(out, "    kby kbdscaleheight add %d add\n", baseline);
879            fprintf(out, "    moveto\n");
880            fprintf(out, "1 -1 scale (%s) show 1 -1 scale\n", buf);
881            baseline += 16;
882
883            if (xkb->names != NULL)
884                sAtom = xkb->names->symbols;
885            else
886                sAtom = None;
887            if (sAtom != None)
888                sName = a2 = XkbAtomGetString(state->dpy, sAtom);
889            if (sName == NULL)
890                sName = "(unknown)";
891
892            if (asprintf(&lbuf, "Layout: %s", sName) == -1) {
893                uFatalError("Can't allocate memory for string\n");
894            }
895            fprintf(out, "kbx kbdscalewidth 0 (%s) centeroffset pop add\n",
896                    lbuf);
897            fprintf(out, "    kby kbdscaleheight add %d add\n", baseline);
898            fprintf(out, "    moveto\n");
899            fprintf(out, "1 -1 scale (%s) show 1 -1 scale\n", lbuf);
900            baseline += 16;
901            free(lbuf);
902            XFree(a2);
903        }
904        if (name != NULL) {
905            fprintf(out, "kbx kbdscalewidth 0 (%s) centeroffset pop add\n",
906                    name);
907            fprintf(out, "    kby kbdscaleheight add %d add\n", baseline);
908            fprintf(out, "    moveto\n");
909            fprintf(out, "1 -1 scale (%s) show 1 -1 scale\n", name);
910            baseline += 16;
911            name = NULL;
912            XFree(a1);
913        }
914        if (state->args->label == LABEL_KEYCODE) {
915            const char *sName = NULL;
916            char *lbuf;
917            char *a3 = NULL;
918            Atom sAtom;
919
920            if (xkb->names != NULL)
921                sAtom = xkb->names->keycodes;
922            else
923                sAtom = None;
924            if (sAtom != None)
925                sName = a3 = XkbAtomGetString(state->dpy, sAtom);
926            if (sName == NULL)
927                sName = "(unknown)";
928
929            if (asprintf(&lbuf, "Keycodes: %s", sName) == -1) {
930                uFatalError("Can't allocate memory for string\n");
931            }
932            fprintf(out, "kbx kbdscalewidth 0 (%s) centeroffset pop add\n",
933                    lbuf);
934            fprintf(out, "    kby kbdscaleheight add %d add\n", baseline);
935            fprintf(out, "    moveto\n");
936            fprintf(out, "1 -1 scale (%s) show 1 -1 scale\n", lbuf);
937            baseline += 16;
938            free(lbuf);
939            XFree(a3);
940        }
941        if (state->args->copies > 1) {
942            for (p = 1; p < state->args->copies; p++)
943                fprintf(out, "copypage\n");
944        }
945        fprintf(out, "showpage\n");
946        fprintf(out, "restore\n");
947        fprintf(out, "%% Done with keyboard/page %d\n", state->nPages + 1);
948    }
949    else {
950        if ((!state->args->wantEPS) && (state->args->label == LABEL_SYMBOLS)) {
951            char buf[40];
952
953            baseline = 16;
954            PSSetColor(out, state, state->black);
955            PSSetFont(out, state, FONT_LATIN1, 14, False);
956            if (state->args->nLabelGroups == 1)
957                snprintf(buf, sizeof(buf), "Group %d",
958                         state->args->baseLabelGroup + 1);
959            else
960                snprintf(buf, sizeof(buf), "Groups %d-%d",
961                         state->args->baseLabelGroup + 1,
962                         state->args->baseLabelGroup +
963                         state->args->nLabelGroups + 1);
964            fprintf(out, "kbx kbdscalewidth 0 (%s) centeroffset pop add\n",
965                    buf);
966            fprintf(out, "    kby kbdscaleheight add %d add\n", baseline);
967            fprintf(out, "    moveto\n");
968            fprintf(out, "1 -1 scale (%s) show 1 -1 scale\n", buf);
969            baseline += 16;
970        }
971        fprintf(out, "restore\n");
972        fprintf(out, "%% Done with keyboard %d\n", state->nPages + 1);
973        fprintf(out, "0 devheight -1 mul translate %% next keyboard\n");
974    }
975    state->nPages++;
976    state->color = state->black;
977    state->font = -1;
978    return;
979}
980
981static void
982PSDoodad(FILE *out, PSState *state, XkbDoodadPtr doodad)
983{
984    XkbDescPtr xkb;
985    const char *name, *dname;
986    char *a1 = NULL, *a2 = NULL;
987    int sz, leading;
988
989    xkb = state->xkb;
990    if (doodad->any.name != None)
991        dname = a1 = XkbAtomGetString(xkb->dpy, doodad->any.name);
992    else
993        dname = "NoName";
994    switch (doodad->any.type) {
995    case XkbOutlineDoodad:
996    case XkbSolidDoodad:
997        name = a2 = XkbAtomGetString(xkb->dpy,
998                                XkbShapeDoodadShape(xkb->geom,
999                                                    &doodad->shape)->name);
1000        if (state->args->wantColor) {
1001            PSSetColor(out, state, doodad->shape.color_ndx);
1002            if (doodad->any.type != XkbOutlineDoodad) {
1003                fprintf(out, "true %d %d %d %s %% Doodad %s\n",
1004                        doodad->shape.angle,
1005                        doodad->shape.left, doodad->shape.top, name, dname);
1006                PSSetColor(out, state, state->black);
1007            }
1008            fprintf(out, "false %d %d %d %s %% Doodad %s\n",
1009                    doodad->shape.angle,
1010                    doodad->shape.left, doodad->shape.top, name, dname);
1011        }
1012        else {
1013            fprintf(out, "false %d %d %d %s %% Doodad %s\n",
1014                    doodad->shape.angle,
1015                    doodad->shape.left, doodad->shape.top, name, dname);
1016        }
1017        name = NULL;
1018        XFree(a2);
1019        break;
1020    case XkbTextDoodad:
1021        fprintf(out, "%% Doodad %s\n", dname);
1022        PSSetColor(out, state, doodad->text.color_ndx);
1023        PSGSave(out, state);
1024        fprintf(out, "%d %d translate\n", doodad->text.left, doodad->text.top);
1025        if (doodad->text.angle != 0)
1026            fprintf(out, "%s rotate\n",
1027                    XkbGeomFPText(doodad->text.angle, XkbMessage));
1028        sz = 14;
1029        if (doodad->text.font) {
1030            FontStuff stuff;
1031
1032            if (CrackXLFDName(doodad->text.font, &stuff)) {
1033                if (stuff.ptSize > 0)
1034                    sz = stuff.ptSize / 10;
1035                ClearFontStuff(&stuff);
1036            }
1037        }
1038        PSSetFont(out, state, FONT_LATIN1, sz, True);
1039        leading = (sz * 12) / 10;
1040        if (strchr(doodad->text.text, '\n') == NULL) {
1041            fprintf(out, "0 %d pts moveto 1 -1 scale\n", (leading * 8) / 10);
1042            fprintf(out, "(%s) show\n", doodad->text.text);
1043        }
1044        else {
1045            char *tmp, *buf, *end;
1046            int offset = (leading * 8 / 10);
1047
1048            tmp = buf = strdup(doodad->text.text);
1049            while (tmp != NULL) {
1050                end = strchr(tmp, '\n');
1051                if (end != NULL)
1052                    *end++ = '\0';
1053                fprintf(out, "0 %d pts moveto 1 -1 scale\n", offset);
1054                fprintf(out, "(%s) show 1 -1 scale\n", tmp);
1055                offset += leading;
1056                tmp = end;
1057            }
1058            free(buf);
1059        }
1060        PSGRestore(out, state);
1061        break;
1062    case XkbIndicatorDoodad:
1063        name = a2 = XkbAtomGetString(xkb->dpy,
1064                                XkbIndicatorDoodadShape(xkb->geom,
1065                                                        &doodad->indicator)->
1066                                name);
1067        if (state->args->wantColor) {
1068            PSSetColor(out, state, doodad->indicator.off_color_ndx);
1069            fprintf(out, "true 0 %d %d %s %% Doodad %s\n",
1070                    doodad->indicator.left, doodad->indicator.top, name, dname);
1071            PSSetColor(out, state, state->black);
1072        }
1073        fprintf(out, "false 0 %d %d %s %% Doodad %s\n",
1074                doodad->indicator.left, doodad->indicator.top, name, dname);
1075        name = NULL;
1076        XFree(a2);
1077        break;
1078    case XkbLogoDoodad:
1079        name = a2 = XkbAtomGetString(xkb->dpy,
1080                                XkbLogoDoodadShape(xkb->geom,
1081                                                   &doodad->logo)->name);
1082        if (state->args->wantColor)
1083            PSSetColor(out, state, doodad->shape.color_ndx);
1084        fprintf(out, "false %d %d %d %s %% Doodad %s\n",
1085                doodad->shape.angle,
1086                doodad->shape.left, doodad->shape.top, name, dname);
1087        name = NULL;
1088        XFree(a2);
1089        break;
1090    }
1091    XFree(a1);
1092    return;
1093}
1094
1095/***====================================================================***/
1096
1097static Bool
1098PSKeycapsSymbol(KeySym sym, unsigned char *buf,
1099                int *font_rtrn, int *sz_rtrn, PSState *state)
1100{
1101    if (state->args->wantSymbols == NO_SYMBOLS)
1102        return False;
1103
1104    if (font_rtrn != NULL)
1105        *font_rtrn = FONT_ISOCAPS;
1106    if (sz_rtrn != NULL)
1107        *sz_rtrn = SZ_LARGE;
1108    buf[1] = '\0';
1109    switch (sym) {
1110    case XK_Shift_L:
1111    case XK_Shift_R:
1112        buf[0] = XKC_ISO_Shift;
1113        return True;
1114    case XK_Shift_Lock:
1115        buf[0] = XKC_ISO_Shift_Lock;
1116        return True;
1117    case XK_ISO_Lock:
1118        buf[0] = XKC_ISO_Caps_Lock;
1119        return True;
1120    case XK_BackSpace:
1121        buf[0] = XKC_ISO_Backspace;
1122        return True;
1123    case XK_Return:
1124        buf[0] = XKC_ISO_Return;
1125        return True;
1126    case XK_Up:
1127    case XK_KP_Up:
1128        buf[0] = XKC_ISO_Up;
1129        return True;
1130    case XK_Down:
1131    case XK_KP_Down:
1132        buf[0] = XKC_ISO_Down;
1133        return True;
1134    case XK_Left:
1135    case XK_KP_Left:
1136        buf[0] = XKC_ISO_Left;
1137        return True;
1138    case XK_Right:
1139    case XK_KP_Right:
1140        buf[0] = XKC_ISO_Right;
1141        return True;
1142    case XK_Tab:
1143        buf[0] = XKC_ISO_Tab;
1144        return True;
1145    case XK_ISO_Left_Tab:
1146        buf[0] = XKC_ISO_Left_Tab;
1147        return True;
1148    }
1149    if (state->args->wantSymbols != ALL_SYMBOLS)
1150        return False;
1151    switch (sym) {
1152    case XK_Caps_Lock:
1153        buf[0] = XKC_ISO_Caps_Lock;
1154        return True;
1155    case XK_Num_Lock:
1156        buf[0] = XKC_ISO_Num_Lock;
1157        return True;
1158    case XK_ISO_Level3_Shift:
1159        buf[0] = XKC_ISO_Level3_Shift;
1160        return True;
1161    case XK_ISO_Level3_Lock:
1162        buf[0] = XKC_ISO_Level3_Lock;
1163        return True;
1164    case XK_ISO_Next_Group:
1165    case XK_ISO_Group_Shift:
1166        buf[0] = XKC_ISO_Next_Group;
1167        return True;
1168    case XK_ISO_Next_Group_Lock:
1169        buf[0] = XKC_ISO_Next_Group_Lock;
1170        return True;
1171    case XK_space:
1172        buf[0] = XKC_ISO_Space;
1173        return True;
1174    case XK_nobreakspace:
1175        buf[0] = XKC_ISO_No_Break_Space;
1176        return True;
1177    case XK_Insert:
1178        buf[0] = XKC_ISO_Insert;
1179        return True;
1180    case XK_ISO_Continuous_Underline:
1181        buf[0] = XKC_ISO_Continuous_Underline;
1182        return True;
1183    case XK_ISO_Discontinuous_Underline:
1184        buf[0] = XKC_ISO_Discontinuous_Underline;
1185        return True;
1186    case XK_ISO_Emphasize:
1187        buf[0] = XKC_ISO_Emphasize;
1188        return True;
1189    case XK_Multi_key:
1190        buf[0] = XKC_ISO_Compose;
1191        return True;
1192    case XK_ISO_Center_Object:
1193        buf[0] = XKC_ISO_Center_Object;
1194        return True;
1195    case XK_Delete:
1196        buf[0] = XKC_ISO_Delete;
1197        return True;
1198    case XK_Clear:
1199        buf[0] = XKC_ISO_Clear_Screen;
1200        return True;
1201    case XK_Scroll_Lock:
1202        buf[0] = XKC_ISO_Scroll_Lock;
1203        return True;
1204    case XK_Help:
1205        buf[0] = XKC_ISO_Help;
1206        return True;
1207    case XK_Print:
1208        buf[0] = XKC_ISO_Print_Screen;
1209        return True;
1210    case XK_ISO_Enter:
1211        buf[0] = XKC_ISO_Enter;
1212        return True;
1213    case XK_Alt_L:
1214    case XK_Alt_R:
1215        buf[0] = XKC_ISO_Alt;
1216        return True;
1217    case XK_Control_L:
1218    case XK_Control_R:
1219        buf[0] = XKC_ISO_Control;
1220        return True;
1221    case XK_Pause:
1222        buf[0] = XKC_ISO_Pause;
1223        return True;
1224    case XK_Break:
1225        buf[0] = XKC_ISO_Break;
1226        return True;
1227    case XK_Escape:
1228        buf[0] = XKC_ISO_Escape;
1229        return True;
1230    case XK_Undo:
1231        buf[0] = XKC_ISO_Undo;
1232        return True;
1233    case XK_ISO_Fast_Cursor_Up:
1234        buf[0] = XKC_ISO_Fast_Cursor_Up;
1235        return True;
1236    case XK_ISO_Fast_Cursor_Down:
1237        buf[0] = XKC_ISO_Fast_Cursor_Down;
1238        return True;
1239    case XK_ISO_Fast_Cursor_Left:
1240        buf[0] = XKC_ISO_Fast_Cursor_Left;
1241        return True;
1242    case XK_ISO_Fast_Cursor_Right:
1243        buf[0] = XKC_ISO_Fast_Cursor_Right;
1244        return True;
1245    case XK_Home:
1246        buf[0] = XKC_ISO_Home;
1247        return True;
1248    case XK_End:
1249        buf[0] = XKC_ISO_End;
1250        return True;
1251    case XK_Page_Up:
1252        buf[0] = XKC_ISO_Page_Up;
1253        return True;
1254    case XK_Page_Down:
1255        buf[0] = XKC_ISO_Page_Down;
1256        return True;
1257    case XK_ISO_Move_Line_Up:
1258        buf[0] = XKC_ISO_Move_Line_Up;
1259        return True;
1260    case XK_ISO_Move_Line_Down:
1261        buf[0] = XKC_ISO_Move_Line_Down;
1262        return True;
1263    case XK_ISO_Partial_Line_Up:
1264        buf[0] = XKC_ISO_Partial_Line_Up;
1265        return True;
1266    case XK_ISO_Partial_Line_Down:
1267        buf[0] = XKC_ISO_Partial_Line_Down;
1268        return True;
1269    case XK_ISO_Partial_Space_Left:
1270        buf[0] = XKC_ISO_Partial_Space_Left;
1271        return True;
1272    case XK_ISO_Partial_Space_Right:
1273        buf[0] = XKC_ISO_Partial_Space_Right;
1274        return True;
1275    case XK_ISO_Set_Margin_Left:
1276        buf[0] = XKC_ISO_Set_Margin_Left;
1277        return True;
1278    case XK_ISO_Set_Margin_Right:
1279        buf[0] = XKC_ISO_Set_Margin_Right;
1280        return True;
1281    case XK_ISO_Release_Margin_Left:
1282        buf[0] = XKC_ISO_Release_Margin_Left;
1283        return True;
1284    case XK_ISO_Release_Margin_Right:
1285        buf[0] = XKC_ISO_Release_Margin_Right;
1286        return True;
1287    case XK_ISO_Release_Both_Margins:
1288        buf[0] = XKC_ISO_Release_Both_Margins;
1289        return True;
1290    case XK_ISO_Prev_Group:
1291        buf[0] = XKC_ISO_Prev_Group;
1292        return True;
1293    case XK_ISO_Prev_Group_Lock:
1294        buf[0] = XKC_ISO_Prev_Group_Lock;
1295        return True;
1296    }
1297    return False;
1298}
1299
1300static Bool
1301PSNonLatin1Symbol(KeySym sym, unsigned char *buf,
1302                  int *font_rtrn, int *sz_rtrn, PSState *state)
1303{
1304    if (state->args->wantSymbols == NO_SYMBOLS)
1305        return False;
1306
1307    if (font_rtrn != NULL)
1308        *font_rtrn = FONT_TEXT;
1309    if (sz_rtrn != NULL)
1310        *sz_rtrn = SZ_LARGE;
1311    buf[1] = '\0';
1312    switch (sym) {
1313    case XK_breve:
1314        buf[0] = 0xC6;
1315        return True;
1316    case XK_abovedot:
1317        buf[0] = 0xC7;
1318        return True;
1319    case XK_doubleacute:
1320        buf[0] = 0xCD;
1321        return True;
1322    case XK_ogonek:
1323        buf[0] = 0xCE;
1324        return True;
1325    case XK_caron:
1326        buf[0] = 0xCF;
1327        return True;
1328    case XK_Lstroke:
1329        buf[0] = 0xE8;
1330        return True;
1331    case XK_idotless:
1332        buf[0] = 0xF5;
1333        return True;
1334    case XK_lstroke:
1335        buf[0] = 0xF8;
1336        return True;
1337    }
1338    if (font_rtrn != NULL)
1339        *font_rtrn = FONT_SYMBOL;
1340    if (sz_rtrn != NULL)
1341        *sz_rtrn = SZ_MEDIUM;
1342    if ((sym & (~0xffUL)) == 0x700) {
1343        switch (sym) {
1344            /* Greek symbol */
1345        case XK_Greek_ALPHA:
1346            buf[0] = 0x41;
1347            return True;
1348        case XK_Greek_BETA:
1349            buf[0] = 0x42;
1350            return True;
1351        case XK_Greek_CHI:
1352            buf[0] = 0x43;
1353            return True;
1354        case XK_Greek_DELTA:
1355            buf[0] = 0x44;
1356            return True;
1357        case XK_Greek_EPSILON:
1358            buf[0] = 0x45;
1359            return True;
1360        case XK_Greek_PHI:
1361            buf[0] = 0x46;
1362            return True;
1363        case XK_Greek_GAMMA:
1364            buf[0] = 0x47;
1365            return True;
1366        case XK_Greek_ETA:
1367            buf[0] = 0x48;
1368            return True;
1369        case XK_Greek_IOTA:
1370            buf[0] = 0x49;
1371            return True;
1372        case XK_Greek_KAPPA:
1373            buf[0] = 0x4B;
1374            return True;
1375        case XK_Greek_LAMDA:
1376            buf[0] = 0x4C;
1377            return True;
1378        case XK_Greek_MU:
1379            buf[0] = 0x4D;
1380            return True;
1381        case XK_Greek_NU:
1382            buf[0] = 0x4E;
1383            return True;
1384        case XK_Greek_OMICRON:
1385            buf[0] = 0x4F;
1386            return True;
1387        case XK_Greek_PI:
1388            buf[0] = 0x50;
1389            return True;
1390        case XK_Greek_THETA:
1391            buf[0] = 0x51;
1392            return True;
1393        case XK_Greek_RHO:
1394            buf[0] = 0x52;
1395            return True;
1396        case XK_Greek_SIGMA:
1397            buf[0] = 0x53;
1398            return True;
1399        case XK_Greek_TAU:
1400            buf[0] = 0x54;
1401            return True;
1402        case XK_Greek_UPSILON:
1403            buf[0] = 0x55;
1404            return True;
1405        case XK_Greek_OMEGA:
1406            buf[0] = 0x57;
1407            return True;
1408        case XK_Greek_XI:
1409            buf[0] = 0x58;
1410            return True;
1411        case XK_Greek_PSI:
1412            buf[0] = 0x59;
1413            return True;
1414        case XK_Greek_ZETA:
1415            buf[0] = 0x5A;
1416            return True;
1417
1418        case XK_Greek_alpha:
1419            buf[0] = 0x61;
1420            return True;
1421        case XK_Greek_beta:
1422            buf[0] = 0x62;
1423            return True;
1424        case XK_Greek_chi:
1425            buf[0] = 0x63;
1426            return True;
1427        case XK_Greek_delta:
1428            buf[0] = 0x64;
1429            return True;
1430        case XK_Greek_epsilon:
1431            buf[0] = 0x65;
1432            return True;
1433        case XK_Greek_phi:
1434            buf[0] = 0x66;
1435            return True;
1436        case XK_Greek_gamma:
1437            buf[0] = 0x67;
1438            return True;
1439        case XK_Greek_eta:
1440            buf[0] = 0x68;
1441            return True;
1442        case XK_Greek_iota:
1443            buf[0] = 0x69;
1444            return True;
1445        case XK_Greek_kappa:
1446            buf[0] = 0x6B;
1447            return True;
1448        case XK_Greek_lamda:
1449            buf[0] = 0x6C;
1450            return True;
1451        case XK_Greek_mu:
1452            buf[0] = 0x6D;
1453            return True;
1454        case XK_Greek_nu:
1455            buf[0] = 0x6E;
1456            return True;
1457        case XK_Greek_omicron:
1458            buf[0] = 0x6F;
1459            return True;
1460        case XK_Greek_pi:
1461            buf[0] = 0x70;
1462            return True;
1463        case XK_Greek_theta:
1464            buf[0] = 0x71;
1465            return True;
1466        case XK_Greek_rho:
1467            buf[0] = 0x72;
1468            return True;
1469        case XK_Greek_sigma:
1470            buf[0] = 0x73;
1471            return True;
1472        case XK_Greek_tau:
1473            buf[0] = 0x74;
1474            return True;
1475        case XK_Greek_upsilon:
1476            buf[0] = 0x75;
1477            return True;
1478        case XK_Greek_omega:
1479            buf[0] = 0x77;
1480            return True;
1481        case XK_Greek_xi:
1482            buf[0] = 0x78;
1483            return True;
1484        case XK_Greek_psi:
1485            buf[0] = 0x79;
1486            return True;
1487        case XK_Greek_zeta:
1488            buf[0] = 0x7A;
1489            return True;
1490        }
1491    }
1492    switch (sym) {
1493    case XK_leftarrow:
1494        buf[0] = 0xAC;
1495        return True;
1496    case XK_uparrow:
1497        buf[0] = 0xAD;
1498        return True;
1499    case XK_rightarrow:
1500        buf[0] = 0xAE;
1501        return True;
1502    case XK_downarrow:
1503        buf[0] = 0xAF;
1504        return True;
1505    case XK_horizconnector:
1506        buf[0] = 0xBE;
1507        return True;
1508    case XK_trademark:
1509        buf[0] = 0xE4;
1510        return True;
1511    }
1512    return False;
1513}
1514
1515static KeySym
1516CheckSymbolAlias(KeySym sym, PSState *state)
1517{
1518    if (XkbKSIsKeypad(sym)) {
1519        if ((sym >= XK_KP_0) && (sym <= XK_KP_9))
1520            sym = (sym - XK_KP_0) + XK_0;
1521        else
1522            switch (sym) {
1523            case XK_KP_Space:
1524                return XK_space;
1525            case XK_KP_Tab:
1526                return XK_Tab;
1527            case XK_KP_Enter:
1528                return XK_Return;
1529            case XK_KP_F1:
1530                return XK_F1;
1531            case XK_KP_F2:
1532                return XK_F2;
1533            case XK_KP_F3:
1534                return XK_F3;
1535            case XK_KP_F4:
1536                return XK_F4;
1537            case XK_KP_Home:
1538                return XK_Home;
1539            case XK_KP_Left:
1540                return XK_Left;
1541            case XK_KP_Up:
1542                return XK_Up;
1543            case XK_KP_Right:
1544                return XK_Right;
1545            case XK_KP_Down:
1546                return XK_Down;
1547            case XK_KP_Page_Up:
1548                return XK_Page_Up;
1549            case XK_KP_Page_Down:
1550                return XK_Page_Down;
1551            case XK_KP_End:
1552                return XK_End;
1553            case XK_KP_Begin:
1554                return XK_Begin;
1555            case XK_KP_Insert:
1556                return XK_Insert;
1557            case XK_KP_Delete:
1558                return XK_Delete;
1559            case XK_KP_Equal:
1560                return XK_equal;
1561            case XK_KP_Multiply:
1562                return XK_asterisk;
1563            case XK_KP_Add:
1564                return XK_plus;
1565            case XK_KP_Subtract:
1566                return XK_minus;
1567            case XK_KP_Divide:
1568                return XK_slash;
1569            }
1570    }
1571    else if (XkbKSIsDeadKey(sym)) {
1572        switch (sym) {
1573        case XK_dead_grave:
1574            sym = XK_grave;
1575            break;
1576        case XK_dead_acute:
1577            sym = XK_acute;
1578            break;
1579        case XK_dead_circumflex:
1580            sym = XK_asciicircum;
1581            break;
1582        case XK_dead_tilde:
1583            sym = XK_asciitilde;
1584            break;
1585        case XK_dead_macron:
1586            sym = XK_macron;
1587            break;
1588        case XK_dead_breve:
1589            sym = XK_breve;
1590            break;
1591        case XK_dead_abovedot:
1592            sym = XK_abovedot;
1593            break;
1594        case XK_dead_diaeresis:
1595            sym = XK_diaeresis;
1596            break;
1597        case XK_dead_abovering:
1598            sym = XK_degree;
1599            break;
1600        case XK_dead_doubleacute:
1601            sym = XK_doubleacute;
1602            break;
1603        case XK_dead_caron:
1604            sym = XK_caron;
1605            break;
1606        case XK_dead_cedilla:
1607            sym = XK_cedilla;
1608            break;
1609        case XK_dead_ogonek:
1610            sym = XK_ogonek;
1611            break;
1612        case XK_dead_iota:
1613            sym = XK_Greek_iota;
1614            break;
1615        case XK_dead_voiced_sound:
1616            sym = XK_voicedsound;
1617            break;
1618        case XK_dead_semivoiced_sound:
1619            sym = XK_semivoicedsound;
1620            break;
1621        }
1622    }
1623    return sym;
1624}
1625
1626static Bool
1627FindKeysymsByName(XkbDescPtr xkb, char *name, PSState *state, KeyTop *top)
1628{
1629    static unsigned char buf[30];
1630    int kc;
1631    KeySym sym, *syms, topSyms[NLABELS];
1632    int level, group;
1633    int eG, nG, gI, l, g;
1634
1635    bzero(top, sizeof(KeyTop));
1636    kc = XkbFindKeycodeByName(xkb, name, True);
1637    if (state->args != NULL) {
1638        level = state->args->labelLevel;
1639        group = state->args->baseLabelGroup;
1640    }
1641    else
1642        level = group = 0;
1643    syms = XkbKeySymsPtr(xkb, kc);
1644    eG = group;
1645    nG = XkbKeyNumGroups(xkb, kc);
1646    gI = XkbKeyGroupInfo(xkb, kc);
1647    if ((state->args->wantDiffs) && (eG >= XkbKeyNumGroups(xkb, kc)))
1648        return False;           /* XXX was a return with no value */
1649    if (nG == 0) {
1650        return False;
1651    }
1652    else if (nG == 1) {
1653        eG = 0;
1654    }
1655    else if (eG >= XkbKeyNumGroups(xkb, kc)) {
1656        switch (XkbOutOfRangeGroupAction(gI)) {
1657        default:
1658            eG %= nG;
1659            break;
1660        case XkbClampIntoRange:
1661            eG = nG - 1;
1662            break;
1663        case XkbRedirectIntoRange:
1664            eG = XkbOutOfRangeGroupNumber(gI);
1665            if (eG >= nG)
1666                eG = 0;
1667            break;
1668        }
1669    }
1670    for (g = 0; g < state->args->nLabelGroups; g++) {
1671        if ((eG + g) >= nG)
1672            continue;
1673        for (l = 0; l < 2; l++) {
1674            int font, sz;
1675
1676            if (level + l >= XkbKeyGroupWidth(xkb, kc, (eG + g)))
1677                continue;
1678            sym = syms[((eG + g) * XkbKeyGroupsWidth(xkb, kc)) + (level + l)];
1679
1680            if (state->args->wantSymbols != NO_SYMBOLS)
1681                sym = CheckSymbolAlias(sym, state);
1682            topSyms[(g * 2) + l] = sym;
1683
1684            if (PSKeycapsSymbol(sym, buf, &font, &sz, state)) {
1685                top->font[(g * 2) + l] = font;
1686                top->size[(g * 2) + l] = sz;
1687            }
1688            else if (((sym & (~0xffUL)) == 0) && isprint(sym) && (!isspace(sym))) {
1689                if (sym == '(')
1690                    snprintf((char *) buf, sizeof(buf), "\\(");
1691                else if (sym == ')')
1692                    snprintf((char *) buf, sizeof(buf), "\\)");
1693                else if (sym == '\\')
1694                    snprintf((char *) buf, sizeof(buf), "\\\\");
1695                else
1696                    snprintf((char *) buf, sizeof(buf), "%c", (char) sym);
1697                top->font[(g * 2) + l] = FONT_LATIN1;
1698                top->size[(g * 2) + l] = SZ_MEDIUM;
1699                switch (buf[0]) {
1700                case '.':
1701                case ':':
1702                case ',':
1703                case ';':
1704                case '\'':
1705                case '"':
1706                case '`':
1707                case '~':
1708                case '^':
1709                case 0250:
1710                case 0270:
1711                case 0267:
1712                case 0260:
1713                case 0252:
1714                case 0272:
1715                case 0271:
1716                case 0262:
1717                case 0263:
1718                case 0264:
1719                case 0255:
1720                case 0254:
1721                case 0257:
1722                    top->size[(g * 2) + l] = SZ_LARGE;
1723                    break;
1724                }
1725            }
1726            else if (PSNonLatin1Symbol(sym, buf, &font, &sz, state)) {
1727                top->font[(g * 2) + l] = font;
1728                top->size[(g * 2) + l] = sz;
1729            }
1730            else {
1731                char *tmp;
1732
1733                tmp = XKeysymToString(sym);
1734                if (tmp != NULL)
1735                    strcpy((char *) buf, tmp);
1736                else
1737                    snprintf((char *) buf, sizeof(buf), "(%ld)", sym);
1738                top->font[(g * 2) + l] = FONT_LATIN1;
1739                if (strlen((char *) buf) < 9)
1740                    top->size[(g * 2) + l] = SZ_SMALL;
1741                else
1742                    top->size[(g * 2) + l] = SZ_TINY;
1743            }
1744            top->present |= (1 << ((g * 2) + l));
1745            strncpy(top->label[(g * 2) + l], (char *) buf, LABEL_LEN - 1);
1746            top->label[(g * 2) + l][LABEL_LEN - 1] = '\0';
1747        }
1748        if (((g == 0) && (top->present & G1LX_MASK) == G1LX_MASK) ||
1749            ((g == 1) && (top->present & G2LX_MASK) == G2LX_MASK)) {
1750            KeySym lower, upper;
1751
1752            XConvertCase(topSyms[(g * 2)], &lower, &upper);
1753            if ((topSyms[(g * 2)] == lower) && (topSyms[(g * 2) + 1] == upper)) {
1754                top->alpha[g] = True;
1755            }
1756        }
1757    }
1758    return True;
1759}
1760
1761static void
1762PSDrawLabel(FILE *out, const char *label, int x, int y, int w, int h)
1763{
1764    fprintf(out, "%d %d (%s) centeroffset\n", w, h, label);
1765    fprintf(out, "%d add exch\n", y);
1766    fprintf(out, "%d add exch moveto\n", x);
1767    fprintf(out, "1 -1 scale (%s) show 1 -1 scale\n", label);
1768    return;
1769}
1770
1771#define	TOP_ROW		0
1772#define	BOTTOM_ROW	1
1773#define	CENTER_ROW	2
1774
1775#define	LEFT_COL	0
1776#define	RIGHT_COL	1
1777#define	CENTER_COL	2
1778
1779static void
1780PSLabelKey(FILE *out, PSState *state, KeyTop *top, int x, int y,
1781           XkbBoundsPtr bounds, int kc, int btm)
1782{
1783    int w, h, i;
1784    int row_y[3];
1785    int col_x[3];
1786    int row_h[3];
1787    int col_w[3];
1788    Bool present[NLABELS];
1789    int sym_row[NLABELS];
1790    int sym_col[NLABELS];
1791
1792    w = XkbBoundsWidth(bounds);
1793    h = XkbBoundsHeight(bounds);
1794    row_y[TOP_ROW] = y + bounds->y1 + (h / 10);
1795    row_y[BOTTOM_ROW] = y + bounds->y1 + (h / 2) + (h / 10);
1796    row_y[CENTER_ROW] = y + bounds->y1 + (h / 10);
1797    row_h[TOP_ROW] = h / 2;
1798    row_h[BOTTOM_ROW] = h / 2;
1799    row_h[CENTER_ROW] = h;
1800
1801    col_x[LEFT_COL] = x + bounds->x1;
1802    col_x[RIGHT_COL] = x + bounds->x1 + w / 2;
1803    col_x[CENTER_COL] = x + bounds->x1;
1804    col_w[LEFT_COL] = w / 2;
1805    col_w[RIGHT_COL] = w / 2;
1806    col_w[CENTER_COL] = w;
1807
1808    present[G1L1] = False;
1809    sym_row[G1L1] = BOTTOM_ROW;
1810    sym_col[G1L1] = LEFT_COL;
1811
1812    present[G1L2] = False;
1813    sym_row[G1L2] = TOP_ROW;
1814    sym_col[G1L2] = LEFT_COL;
1815
1816    present[G2L1] = False;
1817    sym_row[G2L1] = BOTTOM_ROW;
1818    sym_col[G2L1] = RIGHT_COL;
1819
1820    present[G2L2] = False;
1821    sym_row[G2L2] = TOP_ROW;
1822    sym_col[G2L2] = RIGHT_COL;
1823
1824    present[CENTER] = False;
1825    sym_row[CENTER] = CENTER_ROW;
1826    sym_col[CENTER] = CENTER_COL;
1827
1828    if (top->present & CENTER_MASK) {
1829        present[CENTER] = True;
1830    }
1831    else
1832        switch (top->present & GXLX_MASK) {
1833        case G1L1_MASK:
1834            present[G1L1] = True;
1835            sym_row[G1L1] = CENTER_ROW;
1836            sym_col[G1L1] = CENTER_COL;
1837            break;
1838        case G1LX_MASK:
1839            present[G1L2] = True;
1840            if (!top->alpha[0]) {
1841                present[G1L1] = True;
1842                if ((strlen(top->label[G1L1]) > 1) &&
1843                    (top->label[G1L1][0] != '\\'))
1844                    sym_col[G1L1] = CENTER_COL;
1845                if ((strlen(top->label[G1L2]) > 1) &&
1846                    (top->label[G1L1][0] != '\\'))
1847                    sym_col[G1L2] = CENTER_COL;
1848            }
1849            break;
1850        default:
1851            if ((top->present & G1LX_MASK) == G1LX_MASK) {
1852                present[G1L2] = True;
1853                if (!top->alpha[0])
1854                    present[G1L1] = True;
1855            }
1856            else if ((top->present & G1LX_MASK) == G1L1_MASK) {
1857                present[G1L1] = True;
1858            }
1859            else if ((top->present & G1LX_MASK) == G1L2_MASK) {
1860                present[G1L2] = True;
1861            }
1862            if ((top->present & G2LX_MASK) == G2LX_MASK) {
1863                present[G2L2] = True;
1864                if (!top->alpha[1])
1865                    present[G2L1] = True;
1866            }
1867            else if ((top->present & G2LX_MASK) == G2L1_MASK) {
1868                present[G2L1] = True;
1869            }
1870            else if ((top->present & G2LX_MASK) == G2L2_MASK) {
1871                present[G2L2] = True;
1872            }
1873            break;
1874        case 0:
1875            return;
1876        }
1877    for (i = 0; i < NLABELS; i++) {
1878        if (present[i]) {
1879            int size;
1880
1881            if (top->size[i] == SZ_AUTO) {
1882                size_t len = strlen(top->label[i]);
1883                if (len == 1) {
1884                    if (top->font[i] == FONT_ISOCAPS)
1885                        size = 18;
1886                    else
1887                        size = 14;
1888                }
1889                else if (len < 10)
1890                    size = 12;
1891                else
1892                    size = 10;
1893            }
1894            else if (top->size[i] == SZ_TINY)
1895                size = 10;
1896            else if (top->size[i] == SZ_SMALL)
1897                size = 12;
1898            else if (top->size[i] == SZ_LARGE)
1899                size = 18;
1900            else if (top->size[i] == SZ_XLARGE)
1901                size = 24;
1902            else
1903                size = 14;
1904            PSSetFont(out, state, top->font[i], size, True);
1905            PSDrawLabel(out, top->label[i], col_x[sym_col[i]],
1906                        row_y[sym_row[i]], col_w[sym_col[i]],
1907                        row_h[sym_row[i]]);
1908        }
1909    }
1910    if (state->args->wantKeycodes) {
1911        char keycode[10];
1912
1913        snprintf(keycode, sizeof(keycode), "%d", kc);
1914        PSSetFont(out, state, FONT_LATIN1, 8, True);
1915        PSDrawLabel(out, keycode, x + bounds->x1, y + btm - 5, w, 0);
1916    }
1917    return;
1918}
1919
1920static void
1921PSSection(FILE *out, PSState *state, XkbSectionPtr section)
1922{
1923    int r, offset;
1924    XkbRowPtr row;
1925    Display *dpy;
1926    XkbDescPtr xkb;
1927
1928    xkb = state->xkb;
1929    dpy = xkb->dpy;
1930    {
1931        const char *section_name;
1932        char *atom_name = NULL;
1933
1934        if (section->name != None)
1935            section_name = atom_name = XkbAtomGetString(dpy, section->name);
1936        else
1937            section_name = "NoName";
1938        fprintf(out, "%% Begin Section '%s'\n", section_name);
1939        XFree(atom_name);
1940    }
1941    PSGSave(out, state);
1942    fprintf(out, "%d %d translate\n", section->left, section->top);
1943    if (section->angle != 0)
1944        fprintf(out, "%s rotate\n", XkbGeomFPText(section->angle, XkbMessage));
1945    if (section->doodads) {
1946        XkbDrawablePtr first, draw;
1947
1948        first = draw = XkbGetOrderedDrawables(NULL, section);
1949        while (draw) {
1950            if (draw->type == XkbDW_Section)
1951                PSSection(out, state, draw->u.section);
1952            else
1953                PSDoodad(out, state, draw->u.doodad);
1954            draw = draw->next;
1955        }
1956        XkbFreeOrderedDrawables(first);
1957    }
1958    for (r = 0, row = section->rows; r < section->num_rows; r++, row++) {
1959        int k;
1960        XkbKeyPtr key;
1961
1962        if (row->vertical)
1963            offset = row->top;
1964        else
1965            offset = row->left;
1966        fprintf(out, "%% Begin %s %d\n", row->vertical ? "column" : "row",
1967                r + 1);
1968        for (k = 0, key = row->keys; k < row->num_keys; k++, key++) {
1969            XkbShapePtr shape = XkbKeyShape(xkb->geom, key);
1970            char *shape_name  = XkbAtomGetString(dpy, shape->name);
1971
1972            offset += key->gap;
1973            if (row->vertical) {
1974                if (state->args->wantColor) {
1975                    if (key->color_ndx != state->white) {
1976                        PSSetColor(out, state, key->color_ndx);
1977                        fprintf(out, "true 0 %d %d %s %% %s\n",
1978                                row->left, offset, shape_name,
1979                                XkbKeyNameText(key->name.name, XkbMessage));
1980                    }
1981                    PSSetColor(out, state, state->black);
1982                }
1983                fprintf(out, "false 0 %d %d %s %% %s\n", row->left, offset,
1984                        shape_name,
1985                        XkbKeyNameText(key->name.name, XkbMessage));
1986                offset += shape->bounds.y2;
1987            }
1988            else {
1989                if (state->args->wantColor) {
1990                    if (key->color_ndx != state->white) {
1991                        PSSetColor(out, state, key->color_ndx);
1992                        fprintf(out, "true 0 %d %d %s %% %s\n", offset,
1993                                row->top, shape_name,
1994                                XkbKeyNameText(key->name.name, XkbMessage));
1995                    }
1996                    PSSetColor(out, state, state->black);
1997                }
1998                fprintf(out, "false 0 %d %d %s %% %s\n", offset, row->top,
1999                        shape_name,
2000                        XkbKeyNameText(key->name.name, XkbMessage));
2001                offset += shape->bounds.x2;
2002            }
2003            XFree(shape_name);
2004        }
2005    }
2006    for (r = 0, row = section->rows; r < section->num_rows; r++, row++) {
2007        int k, kc = 0;
2008        XkbKeyPtr key;
2009        XkbShapePtr shape;
2010        XkbBoundsRec bounds;
2011
2012        if (state->args->label == LABEL_NONE)
2013            break;
2014        if (row->vertical)
2015            offset = row->top;
2016        else
2017            offset = row->left;
2018        fprintf(out, "%% Begin %s %d labels\n",
2019                row->vertical ? "column" : "row", r + 1);
2020        PSSetColor(out, state, xkb->geom->label_color->pixel);
2021        PSSetFont(out, state, FONT_LATIN1, 12, True);
2022        for (k = 0, key = row->keys; k < row->num_keys; k++, key++) {
2023            char *name, *name2, buf[30], buf2[30];
2024            int x, y;
2025            KeyTop top;
2026
2027            shape = XkbKeyShape(xkb->geom, key);
2028            XkbComputeShapeTop(shape, &bounds);
2029            offset += key->gap;
2030            name = name2 = NULL;
2031            if (state->args->label == LABEL_SYMBOLS) {
2032                if (!FindKeysymsByName(xkb, key->name.name, state, &top)) {
2033                    fprintf(out, "%% No label for %s\n",
2034                            XkbKeyNameText(key->name.name, XkbMessage));
2035                }
2036            }
2037            else {
2038                char *olKey;
2039
2040                if (section->num_overlays > 0)
2041                    olKey = XkbFindOverlayForKey(xkb->geom, section,
2042                                                 key->name.name);
2043                else
2044                    olKey = NULL;
2045
2046                if (state->args->label == LABEL_KEYNAME) {
2047                    name = XkbKeyNameText(key->name.name, XkbMessage);
2048                    if (olKey)
2049                        name2 = XkbKeyNameText(olKey, XkbMessage);
2050                }
2051                else if (state->args->label == LABEL_KEYCODE) {
2052                    name = buf;
2053                    snprintf(name, sizeof(buf), "%d",
2054                            XkbFindKeycodeByName(xkb, key->name.name, True));
2055                    if (olKey) {
2056                        name2 = buf2;
2057                        snprintf(name2, sizeof(buf2), "%d",
2058                                 XkbFindKeycodeByName(xkb, olKey, True));
2059                    }
2060                }
2061                bzero(&top, sizeof(KeyTop));
2062                if (name2 != NULL) {
2063                    top.present |= G1LX_MASK;
2064                    strncpy(top.label[G1L1], name, LABEL_LEN - 1);
2065                    top.label[G1L1][LABEL_LEN - 1] = '\0';
2066                    strncpy(top.label[G1L2], name2, LABEL_LEN - 1);
2067                    top.label[G1L2][LABEL_LEN - 1] = '\0';
2068                }
2069                else if (name != NULL) {
2070                    top.present |= CENTER_MASK;
2071                    strncpy(top.label[CENTER], name, LABEL_LEN - 1);
2072                    top.label[CENTER][LABEL_LEN - 1] = '\0';
2073                }
2074                else {
2075                    fprintf(out, "%% No label for %s\n",
2076                            XkbKeyNameText(key->name.name, XkbMessage));
2077                }
2078            }
2079            if (row->vertical) {
2080                x = row->left;
2081                y = offset;
2082                offset += shape->bounds.y2;
2083            }
2084            else {
2085                x = offset;
2086                y = row->top;
2087                offset += shape->bounds.x2;
2088            }
2089            name = key->name.name;
2090            fprintf(out, "%% %s\n", XkbKeyNameText(name, XkbMessage));
2091            if (state->args->wantKeycodes)
2092                kc = XkbFindKeycodeByName(xkb, key->name.name, True);
2093            PSLabelKey(out, state, &top, x, y, &bounds, kc, shape->bounds.y2);
2094        }
2095    }
2096    PSGRestore(out, state);
2097    return;
2098}
2099
2100Bool
2101GeometryToPostScript(FILE *out, XkbFileInfo *pResult, XKBPrintArgs *args)
2102{
2103    XkbDrawablePtr first, draw;
2104    PSState state;
2105    Bool dfltBorder;
2106    int i;
2107
2108    if ((!pResult) || (!pResult->xkb) || (!pResult->xkb->geom))
2109        return False;
2110    state.xkb = pResult->xkb;
2111    state.dpy = pResult->xkb->dpy;
2112    state.geom = pResult->xkb->geom;
2113    state.color = state.black = state.white = -1;
2114    state.font = -1;
2115    state.nPages = 0;
2116    state.totalKB = 1;
2117    state.kbPerPage = 1;
2118    state.x1 = state.y1 = state.x2 = state.y2 = 0;
2119    state.args = args;
2120
2121    if ((args->label == LABEL_SYMBOLS) && (pResult->xkb->ctrls)) {
2122        if (args->nTotalGroups == 0)
2123            state.totalKB =
2124                pResult->xkb->ctrls->num_groups / args->nLabelGroups;
2125        else
2126            state.totalKB = args->nTotalGroups;
2127        if (state.totalKB < 1)
2128            state.totalKB = 1;
2129        else if (state.totalKB > 1)
2130            state.kbPerPage = 2;
2131    }
2132    if (args->nKBPerPage != 0)
2133        state.kbPerPage = args->nKBPerPage;
2134
2135    PSProlog(out, &state);
2136    first = XkbGetOrderedDrawables(state.geom, NULL);
2137
2138    for (draw = first, dfltBorder = True; draw != NULL; draw = draw->next) {
2139        if ((draw->type != XkbDW_Section) &&
2140            ((draw->u.doodad->any.type == XkbOutlineDoodad) ||
2141             (draw->u.doodad->any.type == XkbSolidDoodad))) {
2142            char *name;
2143
2144            name = XkbAtomGetString(state.dpy, draw->u.doodad->any.name);
2145            if ((name != NULL) && (uStrCaseEqual(name, "edges"))) {
2146                dfltBorder = False;
2147                XFree(name);
2148                break;
2149            }
2150            XFree(name);
2151        }
2152    }
2153    for (i = 0; i < state.totalKB; i++) {
2154        PSPageSetup(out, &state, dfltBorder);
2155        for (draw = first; draw != NULL; draw = draw->next) {
2156            if (draw->type == XkbDW_Section)
2157                PSSection(out, &state, draw->u.section);
2158            else {
2159                PSDoodad(out, &state, draw->u.doodad);
2160            }
2161        }
2162        PSPageTrailer(out, &state);
2163        state.args->baseLabelGroup += state.args->nLabelGroups;
2164    }
2165    XkbFreeOrderedDrawables(first);
2166    PSFileTrailer(out, &state);
2167    return True;
2168}
2169