Annotation of mandoc/term.c, Revision 1.139
1.139 ! schwarze 1: /* $Id: term.c,v 1.138 2010/05/24 21:34:16 schwarze Exp $ */
1.1 kristaps 2: /*
1.75 kristaps 3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
1.74 kristaps 6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 8: *
1.74 kristaps 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 16: */
1.128 kristaps 17: #ifdef HAVE_CONFIG_H
18: #include "config.h"
19: #endif
20:
1.126 kristaps 21: #include <sys/types.h>
22:
1.1 kristaps 23: #include <assert.h>
1.122 kristaps 24: #include <ctype.h>
1.22 kristaps 25: #include <stdio.h>
1.1 kristaps 26: #include <stdlib.h>
27: #include <string.h>
1.113 kristaps 28: #include <time.h>
1.1 kristaps 29:
1.137 kristaps 30: #include "mandoc.h"
1.101 kristaps 31: #include "chars.h"
1.107 kristaps 32: #include "out.h"
1.71 kristaps 33: #include "term.h"
34: #include "man.h"
35: #include "mdoc.h"
1.105 kristaps 36: #include "main.h"
1.1 kristaps 37:
1.134 joerg 38: static struct termp *term_alloc(enum termenc, size_t);
1.71 kristaps 39: static void term_free(struct termp *);
1.125 kristaps 40: static void spec(struct termp *, const char *, size_t);
41: static void res(struct termp *, const char *, size_t);
42: static void buffera(struct termp *, const char *, size_t);
43: static void bufferc(struct termp *, char);
44: static void adjbuf(struct termp *p, size_t);
45: static void encode(struct termp *, const char *, size_t);
1.1 kristaps 46:
47:
1.71 kristaps 48: void *
1.134 joerg 49: ascii_alloc(size_t width)
1.10 kristaps 50: {
1.1 kristaps 51:
1.134 joerg 52: return(term_alloc(TERMENC_ASCII, width));
1.1 kristaps 53: }
54:
55:
1.99 kristaps 56: void
1.71 kristaps 57: terminal_free(void *arg)
1.11 kristaps 58: {
59:
1.71 kristaps 60: term_free((struct termp *)arg);
1.11 kristaps 61: }
62:
63:
1.71 kristaps 64: static void
65: term_free(struct termp *p)
1.14 kristaps 66: {
67:
1.71 kristaps 68: if (p->buf)
69: free(p->buf);
1.102 kristaps 70: if (p->symtab)
1.101 kristaps 71: chars_free(p->symtab);
1.14 kristaps 72:
1.71 kristaps 73: free(p);
1.14 kristaps 74: }
75:
76:
1.71 kristaps 77: static struct termp *
1.134 joerg 78: term_alloc(enum termenc enc, size_t width)
1.14 kristaps 79: {
1.71 kristaps 80: struct termp *p;
1.14 kristaps 81:
1.117 kristaps 82: p = calloc(1, sizeof(struct termp));
83: if (NULL == p) {
1.120 kristaps 84: perror(NULL);
1.117 kristaps 85: exit(EXIT_FAILURE);
86: }
1.138 schwarze 87: p->tabwidth = 5;
1.71 kristaps 88: p->enc = enc;
1.134 joerg 89: /* Enforce some lower boundary. */
90: if (width < 60)
91: width = 60;
92: p->defrmargin = width - 2;
1.71 kristaps 93: return(p);
1.14 kristaps 94: }
95:
96:
1.71 kristaps 97: /*
98: * Flush a line of text. A "line" is loosely defined as being something
99: * that should be followed by a newline, regardless of whether it's
100: * broken apart by newlines getting there. A line can also be a
1.130 kristaps 101: * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does
102: * not have a trailing newline.
1.71 kristaps 103: *
1.130 kristaps 104: * The following flags may be specified:
1.71 kristaps 105: *
106: * - TERMP_NOLPAD: when beginning to write the line, don't left-pad the
107: * offset value. This is useful when doing columnar lists where the
108: * prior column has right-padded.
109: *
110: * - TERMP_NOBREAK: this is the most important and is used when making
111: * columns. In short: don't print a newline and instead pad to the
112: * right margin. Used in conjunction with TERMP_NOLPAD.
113: *
1.91 kristaps 114: * - TERMP_TWOSPACE: when padding, make sure there are at least two
115: * space characters of padding. Otherwise, rather break the line.
116: *
1.84 kristaps 117: * - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and
118: * the line is overrun, and don't pad-right if it's underrun.
119: *
120: * - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when
121: * overruning, instead save the position and continue at that point
122: * when the next invocation.
1.71 kristaps 123: *
124: * In-line line breaking:
125: *
126: * If TERMP_NOBREAK is specified and the line overruns the right
127: * margin, it will break and pad-right to the right margin after
128: * writing. If maxrmargin is violated, it will break and continue
1.114 kristaps 129: * writing from the right-margin, which will lead to the above scenario
130: * upon exit. Otherwise, the line will break at the right margin.
1.71 kristaps 131: */
132: void
133: term_flushln(struct termp *p)
1.53 kristaps 134: {
1.114 kristaps 135: int i; /* current input position in p->buf */
136: size_t vis; /* current visual position on output */
137: size_t vbl; /* number of blanks to prepend to output */
1.136 schwarze 138: size_t vend; /* end of word visual position on output */
1.114 kristaps 139: size_t bp; /* visual right border position */
140: int j; /* temporary loop index */
141: size_t maxvis, mmax;
1.53 kristaps 142:
1.71 kristaps 143: /*
144: * First, establish the maximum columns of "visible" content.
145: * This is usually the difference between the right-margin and
146: * an indentation, but can be, for tagged lists or columns, a
1.115 kristaps 147: * small set of values.
1.71 kristaps 148: */
1.53 kristaps 149:
1.71 kristaps 150: assert(p->offset < p->rmargin);
1.92 kristaps 151:
1.129 kristaps 152: maxvis = (int)(p->rmargin - p->offset) - p->overstep < 0 ?
1.119 kristaps 153: /* LINTED */
1.129 kristaps 154: 0 : p->rmargin - p->offset - p->overstep;
155: mmax = (int)(p->maxrmargin - p->offset) - p->overstep < 0 ?
1.119 kristaps 156: /* LINTED */
1.129 kristaps 157: 0 : p->maxrmargin - p->offset - p->overstep;
1.92 kristaps 158:
1.71 kristaps 159: bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
1.115 kristaps 160:
1.136 schwarze 161: /*
162: * Indent the first line of a paragraph.
163: */
164: vbl = p->flags & TERMP_NOLPAD ? 0 : p->offset;
165:
1.115 kristaps 166: /*
167: * FIXME: if bp is zero, we still output the first word before
168: * breaking the line.
169: */
170:
1.136 schwarze 171: vis = vend = i = 0;
172: while (i < (int)p->col) {
1.71 kristaps 173:
174: /*
1.138 schwarze 175: * Handle literal tab characters.
176: */
177: for (j = i; j < (int)p->col; j++) {
178: if ('\t' != p->buf[j])
179: break;
180: vend = (vis/p->tabwidth+1)*p->tabwidth;
181: vbl += vend - vis;
182: vis = vend;
183: }
184:
185: /*
1.71 kristaps 186: * Count up visible word characters. Control sequences
187: * (starting with the CSI) aren't counted. A space
188: * generates a non-printing word, which is valid (the
189: * space is printed according to regular spacing rules).
190: */
191:
192: /* LINTED */
1.138 schwarze 193: for ( ; j < (int)p->col; j++) {
194: if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j])
1.71 kristaps 195: break;
1.130 kristaps 196: if (8 == p->buf[j])
1.136 schwarze 197: vend--;
1.71 kristaps 198: else
1.136 schwarze 199: vend++;
1.71 kristaps 200: }
1.53 kristaps 201:
1.71 kristaps 202: /*
1.81 kristaps 203: * Find out whether we would exceed the right margin.
1.136 schwarze 204: * If so, break to the next line.
1.81 kristaps 205: */
1.136 schwarze 206: if (vend > bp && vis > 0) {
207: vend -= vis;
1.81 kristaps 208: putchar('\n');
209: if (TERMP_NOBREAK & p->flags) {
1.139 ! schwarze 210: p->viscol = p->rmargin;
1.81 kristaps 211: for (j = 0; j < (int)p->rmargin; j++)
212: putchar(' ');
1.136 schwarze 213: vend += p->rmargin - p->offset;
1.81 kristaps 214: } else {
1.139 ! schwarze 215: p->viscol = 0;
1.136 schwarze 216: vbl = p->offset;
1.81 kristaps 217: }
1.130 kristaps 218:
1.129 kristaps 219: /* Remove the p->overstep width. */
1.130 kristaps 220:
1.112 kristaps 221: bp += (int)/* LINTED */
1.129 kristaps 222: p->overstep;
223: p->overstep = 0;
1.71 kristaps 224: }
1.53 kristaps 225:
1.138 schwarze 226: /*
227: * Skip leading tabs, they were handled above.
228: */
229: while (i < (int)p->col && '\t' == p->buf[i])
230: i++;
231:
1.130 kristaps 232: /* Write out the [remaining] word. */
1.136 schwarze 233: for ( ; i < (int)p->col; i++) {
1.138 schwarze 234: if ('\t' == p->buf[i])
235: break;
1.136 schwarze 236: if (' ' == p->buf[i]) {
237: while (' ' == p->buf[i]) {
238: vbl++;
239: i++;
240: }
1.71 kristaps 241: break;
1.136 schwarze 242: }
243: if (ASCII_NBRSP == p->buf[i]) {
244: vbl++;
245: continue;
246: }
1.130 kristaps 247:
1.136 schwarze 248: /*
249: * Now we definitely know there will be
250: * printable characters to output,
251: * so write preceding white space now.
252: */
253: if (vbl) {
254: for (j = 0; j < (int)vbl; j++)
255: putchar(' ');
1.139 ! schwarze 256: p->viscol += vbl;
1.136 schwarze 257: vbl = 0;
258: }
259: putchar(p->buf[i]);
1.139 ! schwarze 260: p->viscol += 1;
1.136 schwarze 261: }
262: vend += vbl;
263: vis = vend;
1.71 kristaps 264: }
1.111 kristaps 265:
1.91 kristaps 266: p->col = 0;
1.129 kristaps 267: p->overstep = 0;
1.15 kristaps 268:
1.91 kristaps 269: if ( ! (TERMP_NOBREAK & p->flags)) {
1.139 ! schwarze 270: p->viscol = 0;
1.91 kristaps 271: putchar('\n');
1.15 kristaps 272: return;
1.71 kristaps 273: }
1.15 kristaps 274:
1.91 kristaps 275: if (TERMP_HANG & p->flags) {
276: /* We need one blank after the tag. */
1.129 kristaps 277: p->overstep = /* LINTED */
1.92 kristaps 278: vis - maxvis + 1;
1.91 kristaps 279:
280: /*
281: * Behave exactly the same way as groff:
1.92 kristaps 282: * If we have overstepped the margin, temporarily move
283: * it to the right and flag the rest of the line to be
284: * shorter.
1.91 kristaps 285: * If we landed right at the margin, be happy.
1.92 kristaps 286: * If we are one step before the margin, temporarily
287: * move it one step LEFT and flag the rest of the line
288: * to be longer.
1.91 kristaps 289: */
1.129 kristaps 290: if (p->overstep >= -1) {
291: assert((int)maxvis + p->overstep >= 0);
1.92 kristaps 292: /* LINTED */
1.129 kristaps 293: maxvis += p->overstep;
1.92 kristaps 294: } else
1.129 kristaps 295: p->overstep = 0;
1.91 kristaps 296:
297: } else if (TERMP_DANGLE & p->flags)
298: return;
1.15 kristaps 299:
1.92 kristaps 300: /* Right-pad. */
301: if (maxvis > vis + /* LINTED */
1.139 ! schwarze 302: ((TERMP_TWOSPACE & p->flags) ? 1 : 0)) {
! 303: p->viscol += maxvis - vis;
1.91 kristaps 304: for ( ; vis < maxvis; vis++)
305: putchar(' ');
1.139 ! schwarze 306: } else { /* ...or newline break. */
1.71 kristaps 307: putchar('\n');
1.139 ! schwarze 308: p->viscol = p->rmargin;
1.91 kristaps 309: for (i = 0; i < (int)p->rmargin; i++)
310: putchar(' ');
311: }
1.15 kristaps 312: }
313:
314:
1.71 kristaps 315: /*
316: * A newline only breaks an existing line; it won't assert vertical
317: * space. All data in the output buffer is flushed prior to the newline
318: * assertion.
319: */
320: void
321: term_newln(struct termp *p)
1.15 kristaps 322: {
323:
1.71 kristaps 324: p->flags |= TERMP_NOSPACE;
1.139 ! schwarze 325: if (0 == p->col && 0 == p->viscol) {
1.71 kristaps 326: p->flags &= ~TERMP_NOLPAD;
1.15 kristaps 327: return;
1.16 kristaps 328: }
1.71 kristaps 329: term_flushln(p);
330: p->flags &= ~TERMP_NOLPAD;
1.16 kristaps 331: }
332:
333:
1.71 kristaps 334: /*
335: * Asserts a vertical space (a full, empty line-break between lines).
336: * Note that if used twice, this will cause two blank spaces and so on.
337: * All data in the output buffer is flushed prior to the newline
338: * assertion.
339: */
340: void
341: term_vspace(struct termp *p)
1.16 kristaps 342: {
343:
1.62 kristaps 344: term_newln(p);
1.139 ! schwarze 345: p->viscol = 0;
1.71 kristaps 346: putchar('\n');
1.16 kristaps 347: }
348:
349:
1.71 kristaps 350: static void
1.125 kristaps 351: spec(struct termp *p, const char *word, size_t len)
1.17 kristaps 352: {
1.71 kristaps 353: const char *rhs;
354: size_t sz;
1.17 kristaps 355:
1.101 kristaps 356: rhs = chars_a2ascii(p->symtab, word, len, &sz);
1.125 kristaps 357: if (rhs)
358: encode(p, rhs, sz);
1.94 kristaps 359: }
360:
361:
362: static void
1.125 kristaps 363: res(struct termp *p, const char *word, size_t len)
1.94 kristaps 364: {
365: const char *rhs;
366: size_t sz;
367:
1.101 kristaps 368: rhs = chars_a2res(p->symtab, word, len, &sz);
1.125 kristaps 369: if (rhs)
370: encode(p, rhs, sz);
371: }
372:
373:
374: void
375: term_fontlast(struct termp *p)
376: {
377: enum termfont f;
378:
379: f = p->fontl;
380: p->fontl = p->fontq[p->fonti];
381: p->fontq[p->fonti] = f;
382: }
383:
384:
385: void
386: term_fontrepl(struct termp *p, enum termfont f)
387: {
388:
389: p->fontl = p->fontq[p->fonti];
390: p->fontq[p->fonti] = f;
391: }
392:
393:
394: void
395: term_fontpush(struct termp *p, enum termfont f)
396: {
397:
398: assert(p->fonti + 1 < 10);
399: p->fontl = p->fontq[p->fonti];
400: p->fontq[++p->fonti] = f;
401: }
402:
403:
404: const void *
405: term_fontq(struct termp *p)
406: {
407:
408: return(&p->fontq[p->fonti]);
409: }
410:
411:
412: enum termfont
413: term_fonttop(struct termp *p)
414: {
415:
416: return(p->fontq[p->fonti]);
417: }
418:
419:
420: void
421: term_fontpopq(struct termp *p, const void *key)
422: {
423:
424: while (p->fonti >= 0 && key != &p->fontq[p->fonti])
425: p->fonti--;
426: assert(p->fonti >= 0);
427: }
1.94 kristaps 428:
1.125 kristaps 429:
430: void
431: term_fontpop(struct termp *p)
432: {
433:
434: assert(p->fonti);
435: p->fonti--;
1.17 kristaps 436: }
437:
438:
1.71 kristaps 439: /*
440: * Handle pwords, partial words, which may be either a single word or a
441: * phrase that cannot be broken down (such as a literal string). This
442: * handles word styling.
443: */
1.86 kristaps 444: void
445: term_word(struct termp *p, const char *word)
1.65 kristaps 446: {
1.124 kristaps 447: const char *sv, *seq;
1.125 kristaps 448: int sz;
1.124 kristaps 449: size_t ssz;
450: enum roffdeco deco;
1.71 kristaps 451:
1.100 kristaps 452: sv = word;
453:
1.123 kristaps 454: if (word[0] && '\0' == word[1])
1.100 kristaps 455: switch (word[0]) {
456: case('.'):
457: /* FALLTHROUGH */
458: case(','):
459: /* FALLTHROUGH */
460: case(';'):
461: /* FALLTHROUGH */
462: case(':'):
463: /* FALLTHROUGH */
464: case('?'):
465: /* FALLTHROUGH */
466: case('!'):
467: /* FALLTHROUGH */
468: case(')'):
469: /* FALLTHROUGH */
470: case(']'):
471: if ( ! (TERMP_IGNDELIM & p->flags))
472: p->flags |= TERMP_NOSPACE;
473: break;
474: default:
475: break;
476: }
1.65 kristaps 477:
1.133 kristaps 478: if ( ! (TERMP_NOSPACE & p->flags)) {
1.125 kristaps 479: bufferc(p, ' ');
1.133 kristaps 480: if (TERMP_SENTENCE & p->flags)
481: bufferc(p, ' ');
482: }
1.65 kristaps 483:
1.71 kristaps 484: if ( ! (p->flags & TERMP_NONOSPACE))
485: p->flags &= ~TERMP_NOSPACE;
1.133 kristaps 486:
487: p->flags &= ~TERMP_SENTENCE;
1.65 kristaps 488:
1.125 kristaps 489: /* FIXME: use strcspn. */
1.124 kristaps 490:
491: while (*word) {
492: if ('\\' != *word) {
1.125 kristaps 493: encode(p, word, 1);
1.124 kristaps 494: word++;
495: continue;
496: }
497:
498: seq = ++word;
499: sz = a2roffdeco(&deco, &seq, &ssz);
500:
501: switch (deco) {
502: case (DECO_RESERVED):
1.125 kristaps 503: res(p, seq, ssz);
1.124 kristaps 504: break;
505: case (DECO_SPECIAL):
1.125 kristaps 506: spec(p, seq, ssz);
1.124 kristaps 507: break;
508: case (DECO_BOLD):
1.125 kristaps 509: term_fontrepl(p, TERMFONT_BOLD);
1.124 kristaps 510: break;
511: case (DECO_ITALIC):
1.125 kristaps 512: term_fontrepl(p, TERMFONT_UNDER);
1.124 kristaps 513: break;
514: case (DECO_ROMAN):
1.125 kristaps 515: term_fontrepl(p, TERMFONT_NONE);
1.124 kristaps 516: break;
517: case (DECO_PREVIOUS):
1.125 kristaps 518: term_fontlast(p);
1.124 kristaps 519: break;
520: default:
521: break;
522: }
1.127 kristaps 523:
1.124 kristaps 524: word += sz;
1.127 kristaps 525: if (DECO_NOSPACE == deco && '\0' == *word)
526: p->flags |= TERMP_NOSPACE;
1.124 kristaps 527: }
1.65 kristaps 528:
1.131 kristaps 529: /*
530: * Note that we don't process the pipe: the parser sees it as
531: * punctuation, but we don't in terms of typography.
532: */
1.100 kristaps 533: if (sv[0] && 0 == sv[1])
534: switch (sv[0]) {
535: case('('):
536: /* FALLTHROUGH */
537: case('['):
538: p->flags |= TERMP_NOSPACE;
539: break;
540: default:
541: break;
542: }
1.65 kristaps 543: }
544:
545:
1.71 kristaps 546: static void
1.125 kristaps 547: adjbuf(struct termp *p, size_t sz)
1.51 kristaps 548: {
549:
1.125 kristaps 550: if (0 == p->maxcols)
551: p->maxcols = 1024;
552: while (sz >= p->maxcols)
553: p->maxcols <<= 2;
554:
555: p->buf = realloc(p->buf, p->maxcols);
556: if (NULL == p->buf) {
557: perror(NULL);
558: exit(EXIT_FAILURE);
1.71 kristaps 559: }
1.51 kristaps 560: }
561:
1.79 kristaps 562:
563: static void
1.125 kristaps 564: buffera(struct termp *p, const char *word, size_t sz)
1.79 kristaps 565: {
1.125 kristaps 566:
567: if (p->col + sz >= p->maxcols)
568: adjbuf(p, p->col + sz);
569:
1.126 kristaps 570: memcpy(&p->buf[(int)p->col], word, sz);
1.125 kristaps 571: p->col += sz;
572: }
573:
574:
575: static void
576: bufferc(struct termp *p, char c)
577: {
578:
579: if (p->col + 1 >= p->maxcols)
580: adjbuf(p, p->col + 1);
581:
1.126 kristaps 582: p->buf[(int)p->col++] = c;
1.125 kristaps 583: }
584:
585:
586: static void
587: encode(struct termp *p, const char *word, size_t sz)
588: {
589: enum termfont f;
590: int i;
591:
592: /*
593: * Encode and buffer a string of characters. If the current
594: * font mode is unset, buffer directly, else encode then buffer
595: * character by character.
596: */
597:
598: if (TERMFONT_NONE == (f = term_fonttop(p))) {
599: buffera(p, word, sz);
600: return;
601: }
602:
603: for (i = 0; i < (int)sz; i++) {
604: if ( ! isgraph((u_char)word[i])) {
605: bufferc(p, word[i]);
606: continue;
1.79 kristaps 607: }
1.125 kristaps 608:
609: if (TERMFONT_UNDER == f)
610: bufferc(p, '_');
611: else
612: bufferc(p, word[i]);
613:
614: bufferc(p, 8);
615: bufferc(p, word[i]);
1.79 kristaps 616: }
617: }
1.106 kristaps 618:
619:
1.107 kristaps 620: size_t
621: term_vspan(const struct roffsu *su)
1.106 kristaps 622: {
623: double r;
624:
1.107 kristaps 625: switch (su->unit) {
1.106 kristaps 626: case (SCALE_CM):
1.107 kristaps 627: r = su->scale * 2;
1.106 kristaps 628: break;
629: case (SCALE_IN):
1.107 kristaps 630: r = su->scale * 6;
1.106 kristaps 631: break;
632: case (SCALE_PC):
1.107 kristaps 633: r = su->scale;
1.106 kristaps 634: break;
635: case (SCALE_PT):
1.107 kristaps 636: r = su->scale / 8;
1.106 kristaps 637: break;
638: case (SCALE_MM):
1.107 kristaps 639: r = su->scale / 1000;
1.106 kristaps 640: break;
641: case (SCALE_VS):
1.107 kristaps 642: r = su->scale;
1.106 kristaps 643: break;
644: default:
1.107 kristaps 645: r = su->scale - 1;
1.106 kristaps 646: break;
647: }
648:
649: if (r < 0.0)
650: r = 0.0;
1.107 kristaps 651: return(/* LINTED */(size_t)
1.106 kristaps 652: r);
653: }
654:
655:
1.107 kristaps 656: size_t
657: term_hspan(const struct roffsu *su)
1.106 kristaps 658: {
659: double r;
660:
1.108 kristaps 661: /* XXX: CM, IN, and PT are approximations. */
662:
1.107 kristaps 663: switch (su->unit) {
1.106 kristaps 664: case (SCALE_CM):
1.108 kristaps 665: r = 4 * su->scale;
1.106 kristaps 666: break;
667: case (SCALE_IN):
1.108 kristaps 668: /* XXX: this is an approximation. */
669: r = 10 * su->scale;
1.106 kristaps 670: break;
671: case (SCALE_PC):
1.108 kristaps 672: r = (10 * su->scale) / 6;
1.106 kristaps 673: break;
674: case (SCALE_PT):
1.108 kristaps 675: r = (10 * su->scale) / 72;
1.106 kristaps 676: break;
677: case (SCALE_MM):
1.107 kristaps 678: r = su->scale / 1000; /* FIXME: double-check. */
1.106 kristaps 679: break;
680: case (SCALE_VS):
1.107 kristaps 681: r = su->scale * 2 - 1; /* FIXME: double-check. */
1.106 kristaps 682: break;
683: default:
1.107 kristaps 684: r = su->scale;
1.106 kristaps 685: break;
686: }
687:
688: if (r < 0.0)
689: r = 0.0;
1.107 kristaps 690: return((size_t)/* LINTED */
1.106 kristaps 691: r);
692: }
693:
694:
CVSweb