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