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