1/*
2
3Copyright (c) 1991  X Consortium
4
5Permission is hereby granted, free of charge, to any person obtaining
6a copy of this software and associated documentation files (the
7"Software"), to deal in the Software without restriction, including
8without limitation the rights to use, copy, modify, merge, publish,
9distribute, sublicense, and/or sell copies of the Software, and to
10permit persons to whom the Software is furnished to do so, subject to
11the following conditions:
12
13The above copyright notice and this permission notice shall be included
14in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the name of the X Consortium shall
25not be used in advertising or otherwise to promote the sale, use or
26other dealings in this Software without prior written authorization
27from the X Consortium.
28
29*/
30
31/*
32 * parse.c
33 *
34 * parse dvi input
35 */
36
37#ifdef HAVE_CONFIG_H
38# include "config.h"
39#endif
40
41#include <X11/Xos.h>
42#include <X11/IntrinsicP.h>
43#include <X11/StringDefs.h>
44#include <stdio.h>
45#include <ctype.h>
46#include "DviP.h"
47
48static void ParseDrawFunction(DviWidget dw, char *buf);
49static void ParseDeviceControl(DviWidget dw);
50static void PutCharacters(DviWidget dw, unsigned char *src, int len);
51static void push_env(DviWidget dw);
52static void pop_env(DviWidget dw);
53
54#ifdef USE_XFT
55static int
56charWidth(DviWidget dw, XftFont *font, char c)
57{
58    XGlyphInfo extents;
59
60    XftTextExtents8(XtDisplay(dw), font, (unsigned char *) &c, 1, &extents);
61    return extents.xOff;
62}
63#else
64#define charWidth(dw,fi,c) (\
65    (fi)->per_char ?\
66	(fi)->per_char[(c) - (fi)->min_char_or_byte2].width\
67    :\
68	(fi)->max_bounds.width\
69)
70#endif
71
72int
73ParseInput(DviWidget dw)
74{
75    char Buffer[BUFSIZ];
76
77    /*
78     * make sure some state exists
79     */
80
81    if (!dw->dvi.state)
82        push_env(dw);
83    for (;;) {
84        int c;
85
86        switch (DviGetC(dw, &c)) {
87        case '\n':
88            break;
89        case ' ':              /* when input is text */
90        case 0:                /* occasional noise creeps in */
91            break;
92        case '{':              /* push down current environment */
93            push_env(dw);
94            break;
95        case '}':
96            pop_env(dw);
97            break;
98            /*
99             * two motion digits plus a character
100             */
101        case '0':
102        case '1':
103        case '2':
104        case '3':
105        case '4':
106        case '5':
107        case '6':
108        case '7':
109        case '8':
110        case '9':
111        {
112            int otherc;
113            HorizontalMove(dw, (c - '0') * 10 + DviGetC(dw, &otherc) - '0');
114        }
115            /* fall through */
116        case 'c':              /* single ascii character */
117            (void) DviGetC(dw, &c);
118            if (c == ' ')
119                break;
120            else {
121                unsigned char tc = c;
122                PutCharacters(dw, &tc, 1);
123            }
124            break;
125        case 'C':
126            GetWord(dw, Buffer, BUFSIZ);
127            {
128                DviCharNameMap *map;
129                int prevFont;
130
131                c = -1;
132                map = QueryFontMap(dw, dw->dvi.state->font_number);
133                if (map) {
134                    c = DviCharIndex(map, Buffer);
135                    if (c == -1) {
136                        unsigned char *ligature = DviCharIsLigature(map, Buffer);
137                        if (ligature) {
138                            int l = strlen((char *) ligature);
139                            PutCharacters(dw, ligature, l);
140                            break;
141                        }
142                    }
143                }
144                prevFont = -1;
145                if (c == -1) {
146                    for (int i = 1; (map = QueryFontMap(dw, i)); i++) {
147                        if (map->special) {
148                            if ((c = DviCharIndex(map, Buffer)) != -1) {
149                                prevFont = dw->dvi.state->font_number;
150                                dw->dvi.state->font_number = i;
151                                break;
152                            }
153                        }
154                    }
155                }
156                if (c != -1) {
157                    unsigned char tc = c;
158                    PutCharacters(dw, &tc, 1);
159                }
160                if (prevFont != -1)
161                    dw->dvi.state->font_number = prevFont;
162            }
163            break;
164        case 'D':              /* draw function */
165            GetLine(dw, Buffer, BUFSIZ);
166            ParseDrawFunction(dw, Buffer);
167            break;
168        case 's':              /* ignore fractional sizes */
169        {
170            int n = GetNumber(dw);
171            if (!dw->dvi.size_scale) {
172                static const int guesses[] = { 1, 4, 100, 1000, 1 };
173                int i;
174
175                for (i = 0; i < 4; i++) {
176                    if (8 <= n / guesses[i] && n / guesses[i] <= 24) {
177                        break;
178                    }
179                }
180                dw->dvi.size_scale = guesses[i];
181            }
182            dw->dvi.state->font_size = n;
183            dw->dvi.state->line_width = n * (dw->dvi.device_resolution /
184                                             (720 * dw->dvi.size_scale));
185        }
186            break;
187        case 'f':
188        {
189            int n = GetNumber(dw);
190            dw->dvi.state->font_number = n;
191        }
192            break;
193        case 'H':              /* absolute horizontal motion */
194        {
195            int k = GetNumber(dw);
196            HorizontalGoto(dw, k);
197        }
198            break;
199        case 'h':              /* relative horizontal motion */
200        {
201            int k = GetNumber(dw);
202            HorizontalMove(dw, k);
203        }
204            break;
205        case 'w':              /* word space */
206            break;
207        case 'V':
208        {
209            int n = GetNumber(dw);
210            VerticalGoto(dw, n);
211        }
212            break;
213        case 'v':
214        {
215            int n = GetNumber(dw);
216            VerticalMove(dw, n);
217        }
218            break;
219        case 'P':              /* new spread */
220            break;
221        case 'p':              /* new page */
222        {
223            int NextPage;
224
225            (void) GetNumber(dw);
226            NextPage = dw->dvi.current_page + 1;
227            RememberPagePosition(dw, NextPage);
228            FlushCharCache(dw);
229            return (NextPage);
230        }
231        case 'n':              /* end of line */
232            GetNumber(dw);
233            GetNumber(dw);
234            HorizontalGoto(dw, 0);
235            break;
236        case '#':              /* comment */
237        case 'F':              /* file info */
238            GetLine(dw, NULL, 0);
239            break;
240        case 't':              /* text */
241            GetLine(dw, Buffer, BUFSIZ);
242            PutCharacters(dw, (unsigned char *) Buffer, strlen(Buffer));
243            dw->dvi.state->x = ToDevice(dw, dw->dvi.cache.x);
244            break;
245        case 'x':              /* device control */
246            ParseDeviceControl(dw);
247            break;
248        case EOF:
249            dw->dvi.last_page = dw->dvi.current_page;
250            FlushCharCache(dw);
251            return dw->dvi.current_page;
252        default:
253            GetLine(dw, Buffer, BUFSIZ);
254            fprintf(stderr, "Unknown command %s\n", Buffer);
255            break;
256        }
257    }
258}
259
260static void
261push_env(DviWidget dw)
262{
263    DviState *new = (DviState *) XtMalloc(sizeof(*new));
264
265    if (dw->dvi.state)
266        *new = *(dw->dvi.state);
267    else {
268        new->font_size = 10 * dw->dvi.size_scale;
269        new->font_number = 1;
270        new->line_style = 0;
271        new->line_width = 10;
272        new->x = 0;
273        new->y = 0;
274    }
275    new->next = dw->dvi.state;
276    dw->dvi.state = new;
277}
278
279static void
280pop_env(DviWidget dw)
281{
282    DviState *old = dw->dvi.state;
283    dw->dvi.state = old->next;
284    XtFree((char *) old);
285}
286
287static void
288InitTypesetter(DviWidget dw)
289{
290    while (dw->dvi.state)
291        pop_env(dw);
292    dw->dvi.size_scale = dw->dvi.size_scale_set;
293    push_env(dw);
294    FlushCharCache(dw);
295}
296
297static void
298SetFont(DviWidget dw)
299{
300    dw->dvi.cache.font_size = dw->dvi.state->font_size;
301    dw->dvi.cache.font_number = dw->dvi.state->font_number;
302    dw->dvi.cache.font = QueryFont(dw,
303                                   dw->dvi.cache.font_number,
304                                   dw->dvi.cache.font_size);
305}
306
307static void
308PutCharacters(DviWidget dw, unsigned char *src, int len)
309{
310    char *dst;
311
312    int xx = ToX(dw, dw->dvi.state->x);
313    int yx = ToX(dw, dw->dvi.state->y);
314    int fy = FontSizeInPixels(dw, dw->dvi.state->font_size);
315    int fx = fy * len;
316
317    /*
318     * quick and dirty extents calculation:
319     */
320    if (yx + fy >= dw->dvi.extents.y1 &&
321        yx - fy <= dw->dvi.extents.y2 &&
322        xx + fx >= dw->dvi.extents.x1 && xx - fx <= dw->dvi.extents.x2) {
323#ifdef USE_XFT
324        XftFont *font;
325        DviTextItem *text;
326#else
327        register XFontStruct *font;
328        register XTextItem *text;
329#endif
330
331        if (!dw->dvi.display_enable)
332            return;
333
334        if (yx != dw->dvi.cache.y ||
335            dw->dvi.cache.char_index + len > DVI_CHAR_CACHE_SIZE)
336            FlushCharCache(dw);
337        /*
338         * load a new font, if the current block is not empty,
339         * step to the next.
340         */
341        if (dw->dvi.cache.font_size != dw->dvi.state->font_size ||
342            dw->dvi.cache.font_number != dw->dvi.state->font_number) {
343            SetFont(dw);
344            if (dw->dvi.cache.cache[dw->dvi.cache.index].nchars != 0) {
345                ++dw->dvi.cache.index;
346                if (dw->dvi.cache.index >= dw->dvi.cache.max)
347                    FlushCharCache(dw);
348                dw->dvi.cache.cache[dw->dvi.cache.index].nchars = 0;
349            }
350        }
351        if (xx != dw->dvi.cache.x) {
352            if (dw->dvi.cache.cache[dw->dvi.cache.index].nchars != 0) {
353                ++dw->dvi.cache.index;
354                if (dw->dvi.cache.index >= dw->dvi.cache.max)
355                    FlushCharCache(dw);
356                dw->dvi.cache.cache[dw->dvi.cache.index].nchars = 0;
357            }
358        }
359        if (!dw->dvi.cache.font)
360            SetFont(dw);
361        text = &dw->dvi.cache.cache[dw->dvi.cache.index];
362        font = dw->dvi.cache.font;
363        dst = &dw->dvi.cache.char_cache[dw->dvi.cache.char_index];
364        if (text->nchars == 0) {
365            text->chars = dst;
366#ifdef USE_XFT
367            text->x = xx;
368#else
369            text->delta = xx - dw->dvi.cache.x;
370#endif
371#ifdef USE_XFT
372            text->font = font;
373#endif
374            if (font != dw->dvi.font) {
375#ifndef USE_XFT
376                text->font = font->fid;
377#endif
378                dw->dvi.font = font;
379            }
380#ifndef USE_XFT
381            else
382                text->font = None;
383#endif
384            dw->dvi.cache.x = xx;
385        }
386        dw->dvi.cache.char_index += len;
387        text->nchars += len;
388        while (len--) {
389            int c = *src++;
390            *dst++ = c;
391            if (font)
392                dw->dvi.cache.x += charWidth(dw, font, c);
393        }
394    }
395}
396
397static void
398ParseDrawFunction(DviWidget dw, char *buf)
399{
400    int n, m, n1, m1;
401
402    SetGCForDraw(dw);
403    switch (buf[0]) {
404    case 'l':                  /* draw a line */
405        sscanf(buf + 1, "%d %d", &n, &m);
406        DrawLine(dw, n, m);
407        break;
408    case 'c':                  /* circle */
409        sscanf(buf + 1, "%d", &n);
410        DrawCircle(dw, n);
411        break;
412    case 'e':                  /* ellipse */
413        sscanf(buf + 1, "%d %d", &m, &n);
414        DrawEllipse(dw, m, n);
415        break;
416    case 'a':                  /* arc */
417        sscanf(buf + 1, "%d %d %d %d", &n, &m, &n1, &m1);
418        DrawArc(dw, n, m, n1, m1);
419        break;
420    case '~':                  /* wiggly line */
421        DrawSpline(dw, buf + 1, 1);
422        break;
423    case 't':                  /* line width */
424        sscanf(buf + 1, "%d", &n);
425        dw->dvi.state->line_width = n;
426        break;
427    case 's':                  /* line style */
428        sscanf(buf + 1, "%d", &n);
429        /* XXX */
430        break;
431    default:
432        /* warning("unknown drawing function %s", buf); */
433        break;
434    }
435}
436
437extern int LastPage, CurrentPage;
438
439static void
440ParseDeviceControl(DviWidget dw)
441{                               /* Parse the x commands */
442    char str[20];
443    int c;
444
445    GetWord(dw, str, 20);
446    switch (str[0]) {           /* crude for now */
447    case 'T':                  /* output device */
448        GetWord(dw, str, 20);
449        break;
450    case 'i':                  /* initialize */
451        InitTypesetter(dw);
452        break;
453    case 't':                  /* trailer */
454        break;
455    case 'p':                  /* pause -- can restart */
456        break;
457    case 's':                  /* stop */
458        return;
459    case 'r':                  /* resolution when prepared */
460        SetDeviceResolution(dw, GetNumber(dw));
461        break;
462    case 'f':                  /* font used */
463    {
464        int n = GetNumber(dw);
465        char str1[50];
466
467        GetWord(dw, str, 20);
468        GetLine(dw, str1, 50);
469        SetFontPosition(dw, n, str, str1);
470    }
471        break;
472    case 'H':                  /* char height */
473        break;
474    case 'S':                  /* slant */
475        break;
476    }
477    while (DviGetC(dw, &c) != '\n')     /* skip rest of input line */
478        if (c == EOF)
479            return;
480}
481