]> git.ipfire.org Git - thirdparty/cups.git/blame - filter/texttops.c
Merge changes from CUPS 1.5.1-r9875.
[thirdparty/cups.git] / filter / texttops.c
CommitLineData
ef416fc2 1/*
b19ccc9e 2 * "$Id: texttops.c 7720 2008-07-11 22:46:21Z mike $"
ef416fc2 3 *
71e16022 4 * Text to PostScript filter for CUPS.
ef416fc2 5 *
71e16022 6 * Copyright 2007-2010 by Apple Inc.
f7deaa1a 7 * Copyright 1993-2007 by Easy Software Products.
ef416fc2 8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
19 * main() - Main entry for text to PostScript filter.
20 * WriteEpilogue() - Write the PostScript file epilogue.
21 * WritePage() - Write a page of text.
22 * WriteProlog() - Write the PostScript file prolog with options.
23 * write_line() - Write a row of text.
24 * write_string() - Write a string of text.
25 */
26
27/*
28 * Include necessary headers...
29 */
30
31#include "textcommon.h"
71e16022 32#include <cups/language-private.h>
ef416fc2 33
34
35/*
36 * Globals...
37 */
38
39char *Glyphs[65536]; /* PostScript glyphs for Unicode */
40int NumFonts; /* Number of fonts to use */
41char *Fonts[256][4]; /* Fonts to use */
42unsigned short Chars[65536]; /* 0xffcc (ff = font, cc = char) */
43unsigned short Codes[65536]; /* Unicode glyph mapping to fonts */
44int Widths[256]; /* Widths of each font */
45int Directions[256];/* Text directions for each font */
46
47
48/*
49 * Local functions...
50 */
51
52static void write_line(int row, lchar_t *line);
53static void write_string(int col, int row, int len, lchar_t *s);
54static void write_text(const char *s);
55
56
57/*
58 * 'main()' - Main entry for text to PostScript filter.
59 */
60
61int /* O - Exit status */
62main(int argc, /* I - Number of command-line arguments */
63 char *argv[]) /* I - Command-line arguments */
64{
65 return (TextMain("texttops", argc, argv));
66}
67
68
69/*
70 * 'WriteEpilogue()' - Write the PostScript file epilogue.
71 */
72
73void
74WriteEpilogue(void)
75{
76 puts("%%Trailer");
77 printf("%%%%Pages: %d\n", NumPages);
78 puts("%%EOF");
79
80 free(Page[0]);
81 free(Page);
82}
83
84
85/*
86 * 'WritePage()' - Write a page of text.
87 */
88
89void
90WritePage(void)
91{
92 int line; /* Current line */
93
94
95 NumPages ++;
96 printf("%%%%Page: %d %d\n", NumPages, NumPages);
97
98 puts("gsave");
99
100 if (PrettyPrint)
101 printf("%d H\n", NumPages);
102
103 for (line = 0; line < SizeLines; line ++)
104 write_line(line, Page[line]);
105
106 puts("grestore");
107 puts("showpage");
108
109 memset(Page[0], 0, sizeof(lchar_t) * SizeColumns * SizeLines);
110}
111
112
113/*
114 * 'WriteProlog()' - Write the PostScript file prolog with options.
115 */
116
117void
118WriteProlog(const char *title, /* I - Title of job */
119 const char *user, /* I - Username */
120 const char *classification, /* I - Classification */
121 const char *label, /* I - Page label */
122 ppd_file_t *ppd) /* I - PPD file info */
123{
124 int i, j, k; /* Looping vars */
125 char *charset; /* Character set string */
126 char filename[1024]; /* Glyph filenames */
127 FILE *fp; /* Glyph files */
128 const char *datadir; /* CUPS_DATADIR environment variable */
129 char line[1024], /* Line from file */
130 *lineptr, /* Pointer into line */
131 *valptr; /* Pointer to value in line */
132 int ch, unicode; /* Character values */
133 int start, end; /* Start and end values for range */
134 char glyph[64]; /* Glyph name */
135 time_t curtime; /* Current time */
136 struct tm *curtm; /* Current date */
137 char curdate[255]; /* Current date (text format) */
138 int num_fonts; /* Number of unique fonts */
139 char *fonts[1024]; /* Unique fonts */
140 static char *names[] = /* Font names */
141 {
142 "cupsNormal",
143 "cupsBold",
144 "cupsItalic"
145 };
146
147
148 /*
149 * Get the data directory...
150 */
151
152 if ((datadir = getenv("CUPS_DATADIR")) == NULL)
153 datadir = CUPS_DATADIR;
154
155 /*
156 * Adjust margins as necessary...
157 */
158
159 if (classification || label)
160 {
161 /*
162 * Leave room for labels...
163 */
164
165 PageBottom += 36;
166 PageTop -= 36;
167 }
168
169 /*
170 * Allocate memory for the page...
171 */
172
173 SizeColumns = (PageRight - PageLeft) / 72.0 * CharsPerInch;
174 SizeLines = (PageTop - PageBottom) / 72.0 * LinesPerInch;
175
61cf44e2
MS
176 if (SizeColumns <= 0 || SizeColumns > 32767 ||
177 SizeLines <= 0 || SizeLines > 32767)
178 {
0837b7e8
MS
179 _cupsLangPrintFilter(stderr, "ERROR",
180 _("Unable to print %dx%d text page."),
181 SizeColumns, SizeLines);
c7017ecc
MS
182 exit(1);
183 }
184
185 if ((Page = calloc(sizeof(lchar_t *), SizeLines)) == NULL)
186 {
0837b7e8
MS
187 _cupsLangPrintFilter(stderr, "ERROR",
188 _("Unable to print %dx%d text page."),
189 SizeColumns, SizeLines);
c7017ecc
MS
190 exit(1);
191 }
192
193 if ((Page[0] = calloc(sizeof(lchar_t), SizeColumns * SizeLines)) == NULL)
194 {
0837b7e8
MS
195 _cupsLangPrintFilter(stderr, "ERROR",
196 _("Unable to print %dx%d text page."),
197 SizeColumns, SizeLines);
61cf44e2
MS
198 exit(1);
199 }
200
ef416fc2 201 for (i = 1; i < SizeLines; i ++)
202 Page[i] = Page[0] + i * SizeColumns;
203
204 if (PageColumns > 1)
205 {
206 ColumnGutter = CharsPerInch / 2;
207 ColumnWidth = (SizeColumns - ColumnGutter * (PageColumns - 1)) /
208 PageColumns;
209 }
210 else
211 ColumnWidth = SizeColumns;
212
61cf44e2
MS
213 if (ColumnWidth <= 0)
214 {
0837b7e8
MS
215 _cupsLangPrintFilter(stderr, "ERROR",
216 _("Unable to print %d text columns."), PageColumns);
61cf44e2
MS
217 exit(1);
218 }
219
ef416fc2 220 /*
221 * Output the DSC header...
222 */
223
224 curtime = time(NULL);
225 curtm = localtime(&curtime);
226 strftime(curdate, sizeof(curdate), "%c", curtm);
227
228 puts("%!PS-Adobe-3.0");
229 printf("%%%%BoundingBox: 0 0 %.0f %.0f\n", PageWidth, PageLength);
230 printf("%%cupsRotation: %d\n", (Orientation & 3) * 90);
231 puts("%%Creator: texttops/" CUPS_SVERSION);
232 printf("%%%%CreationDate: %s\n", curdate);
2abf387c 233 WriteTextComment("Title", title);
234 WriteTextComment("For", user);
ef416fc2 235 puts("%%Pages: (atend)");
236
237 /*
238 * Initialize globals...
239 */
240
241 NumFonts = 0;
242 memset(Fonts, 0, sizeof(Fonts));
243 memset(Glyphs, 0, sizeof(Glyphs));
244 memset(Chars, 0, sizeof(Chars));
245 memset(Codes, 0, sizeof(Codes));
246
247 /*
248 * Load the PostScript glyph names and the corresponding character
249 * set definition...
250 */
251
252 snprintf(filename, sizeof(filename), "%s/data/psglyphs", datadir);
253
254 if ((fp = fopen(filename, "r")) != NULL)
255 {
256 while (fscanf(fp, "%x%63s", &unicode, glyph) == 2)
257 Glyphs[unicode] = strdup(glyph);
258
259 fclose(fp);
260 }
261 else
262 {
229681c1 263 _cupsLangPrintError("ERROR", _("Unable to open psglyphs"));
ef416fc2 264 exit(1);
265 }
266
267 /*
268 * Get the output character set...
269 */
270
271 charset = getenv("CHARSET");
272 if (charset != NULL && strcmp(charset, "us-ascii") != 0)
273 {
274 snprintf(filename, sizeof(filename), "%s/charsets/%s", datadir, charset);
275
276 if ((fp = fopen(filename, "r")) == NULL)
277 {
278 /*
279 * Can't open charset file!
280 */
281
229681c1 282 _cupsLangPrintError("ERROR", _("Unable to open charset file"));
ef416fc2 283 exit(1);
284 }
285
286 /*
287 * Opened charset file; now see if this is really a charset file...
288 */
289
290 if (fgets(line, sizeof(line), fp) == NULL)
291 {
292 /*
293 * Bad/empty charset file!
294 */
295
296 fclose(fp);
0837b7e8
MS
297 _cupsLangPrintFilter(stderr, "ERROR", _("Bad charset file \"%s\"."),
298 filename);
ef416fc2 299 exit(1);
300 }
301
302 if (strncmp(line, "charset", 7) != 0)
303 {
304 /*
305 * Bad format/not a charset file!
306 */
307
308 fclose(fp);
0837b7e8
MS
309 _cupsLangPrintFilter(stderr, "ERROR", _("Bad charset file \"%s\"."),
310 filename);
ef416fc2 311 exit(1);
312 }
313
314 /*
315 * See if this is an 8-bit or UTF-8 character set file...
316 */
317
318 line[strlen(line) - 1] = '\0'; /* Drop \n */
319 for (lineptr = line + 7; isspace(*lineptr & 255); lineptr ++); /* Skip whitespace */
320
e6013cfa 321 if (strcmp(lineptr, "utf8") == 0)
ef416fc2 322 {
323 /*
324 * UTF-8 (Unicode) text...
325 */
326
ef416fc2 327 NumFonts = 0;
328
329 while (fgets(line, sizeof(line), fp) != NULL)
330 {
331 /*
332 * Skip comment and blank lines...
333 */
334
335 if (line[0] == '#' || line[0] == '\n')
336 continue;
337
338 /*
339 * Read the font descriptions that should look like:
340 *
341 * start end direction width normal [bold italic bold-italic]
342 */
343
344 lineptr = line;
345
346 start = strtol(lineptr, &lineptr, 16);
347 end = strtol(lineptr, &lineptr, 16);
348
349 while (isspace(*lineptr & 255))
350 lineptr ++;
351
352 valptr = lineptr;
353
354 while (!isspace(*lineptr & 255) && *lineptr)
355 lineptr ++;
356
357 if (!*lineptr)
358 {
359 /*
360 * Can't have a font without all required values...
361 */
362
0837b7e8
MS
363 _cupsLangPrintFilter(stderr, "ERROR",
364 _("Bad font description line: %s"), valptr);
ef416fc2 365 fclose(fp);
366 exit(1);
367 }
368
369 *lineptr++ = '\0';
370
371 if (strcmp(valptr, "ltor") == 0)
372 Directions[NumFonts] = 1;
373 else if (strcmp(valptr, "rtol") == 0)
374 Directions[NumFonts] = -1;
375 else
376 {
0837b7e8
MS
377 _cupsLangPrintFilter(stderr, "ERROR", _("Bad text direction: %s"),
378 valptr);
ef416fc2 379 fclose(fp);
380 exit(1);
381 }
382
383 /*
384 * Got the direction, now get the width...
385 */
386
387 while (isspace(*lineptr & 255))
388 lineptr ++;
389
390 valptr = lineptr;
391
392 while (!isspace(*lineptr & 255) && *lineptr)
393 lineptr ++;
394
395 if (!*lineptr)
396 {
397 /*
398 * Can't have a font without all required values...
399 */
400
0837b7e8
MS
401 _cupsLangPrintFilter(stderr, "ERROR",
402 _("Bad font description line: %s"), valptr);
ef416fc2 403 fclose(fp);
404 exit(1);
405 }
406
407 *lineptr++ = '\0';
408
409 if (strcmp(valptr, "single") == 0)
410 Widths[NumFonts] = 1;
411 else if (strcmp(valptr, "double") == 0)
412 Widths[NumFonts] = 2;
413 else
414 {
0837b7e8
MS
415 _cupsLangPrintFilter(stderr, "ERROR",
416 _("Bad text width: %s"), valptr);
ef416fc2 417 fclose(fp);
418 exit(1);
419 }
420
421 /*
422 * Get the fonts...
423 */
424
425 for (i = 0; *lineptr && i < 4; i ++)
426 {
427 while (isspace(*lineptr & 255))
428 lineptr ++;
429
430 valptr = lineptr;
431
432 while (!isspace(*lineptr & 255) && *lineptr)
433 lineptr ++;
434
435 if (*lineptr)
436 *lineptr++ = '\0';
437
438 if (lineptr > valptr)
439 Fonts[NumFonts][i] = strdup(valptr);
440 }
441
442 /*
443 * Fill in remaining fonts as needed...
444 */
445
446 for (j = i; j < 4; j ++)
447 Fonts[NumFonts][j] = strdup(Fonts[NumFonts][0]);
448
449 /*
450 * Define the character mappings...
451 */
452
453 for (i = start, j = NumFonts * 256; i <= end; i ++, j ++)
454 {
455 Chars[i] = j;
456 Codes[j] = i;
457 }
458
459 /*
460 * Move to the next font, stopping if needed...
461 */
462
463 NumFonts ++;
464 if (NumFonts >= 256)
465 break;
466 }
467
468 fclose(fp);
469 }
470 else
471 {
0837b7e8 472 _cupsLangPrintFilter(stderr, "ERROR", _("Bad charset type: %s"), lineptr);
ef416fc2 473 fclose(fp);
474 exit(1);
475 }
476 }
477 else
478 {
479 /*
480 * Standard ASCII output just uses Courier, Courier-Bold, and
481 * possibly Courier-Oblique.
482 */
483
484 NumFonts = 1;
485
486 Fonts[0][ATTR_NORMAL] = strdup("Courier");
487 Fonts[0][ATTR_BOLD] = strdup("Courier-Bold");
488 Fonts[0][ATTR_ITALIC] = strdup("Courier-Oblique");
489 Fonts[0][ATTR_BOLDITALIC] = strdup("Courier-BoldOblique");
490
491 Widths[0] = 1;
492 Directions[0] = 1;
493
494 /*
495 * Define US-ASCII characters...
496 */
497
498 for (i = 32; i < 127; i ++)
499 {
500 Chars[i] = i;
501 Codes[i] = i;
502 }
503 }
504
505 /*
506 * Generate a list of unique fonts to use...
507 */
508
509 for (i = 0, num_fonts = 0; i < NumFonts; i ++)
510 for (j = PrettyPrint ? 2 : 1; j >= 0; j --)
511 {
512 for (k = 0; k < num_fonts; k ++)
513 if (strcmp(Fonts[i][j], fonts[k]) == 0)
514 break;
515
516 if (k >= num_fonts)
517 {
518 /*
519 * Add new font...
520 */
521
522 fonts[num_fonts] = Fonts[i][j];
523 num_fonts ++;
524 }
525 }
526
527 /*
528 * List the fonts that will be used...
529 */
530
531 for (i = 0; i < num_fonts; i ++)
532 if (i == 0)
533 printf("%%%%DocumentNeededResources: font %s\n", fonts[i]);
534 else
535 printf("%%%%+ font %s\n", fonts[i]);
536
537 puts("%%DocumentSuppliedResources: procset texttops 1.1 0");
538
539 for (i = 0; i < num_fonts; i ++)
540 {
541 if (ppd != NULL)
542 {
543 fprintf(stderr, "DEBUG: ppd->num_fonts = %d\n", ppd->num_fonts);
544
545 for (j = 0; j < ppd->num_fonts; j ++)
546 {
547 fprintf(stderr, "DEBUG: ppd->fonts[%d] = %s\n", j, ppd->fonts[j]);
548
549 if (strcmp(fonts[i], ppd->fonts[j]) == 0)
550 break;
551 }
552 }
553 else
554 j = 0;
555
556 if ((ppd != NULL && j >= ppd->num_fonts) ||
557 strncmp(fonts[i], "Courier", 7) == 0 ||
558 strcmp(fonts[i], "Symbol") == 0)
559 {
560 /*
561 * Need to embed this font...
562 */
563
564 printf("%%%%+ font %s\n", fonts[i]);
565 }
566 }
567
568 puts("%%EndComments");
569
570 puts("%%BeginProlog");
571
572 /*
573 * Download any missing fonts...
574 */
575
576 for (i = 0; i < num_fonts; i ++)
577 {
578 if (ppd != NULL)
579 {
580 for (j = 0; j < ppd->num_fonts; j ++)
581 if (strcmp(fonts[i], ppd->fonts[j]) == 0)
582 break;
583 }
584 else
585 j = 0;
586
587 if ((ppd != NULL && j >= ppd->num_fonts) ||
588 strncmp(fonts[i], "Courier", 7) == 0 ||
589 strcmp(fonts[i], "Symbol") == 0)
590 {
591 /*
592 * Need to embed this font...
593 */
594
595 printf("%%%%BeginResource: font %s\n", fonts[i]);
596
597 /**** MRS: Need to use CUPS_FONTPATH env var! ****/
598 /**** Also look for Fontmap file or name.pfa, name.pfb... ****/
599 snprintf(filename, sizeof(filename), "%s/fonts/%s", datadir, fonts[i]);
600 if ((fp = fopen(filename, "rb")) != NULL)
601 {
602 while ((j = fread(line, 1, sizeof(line), fp)) > 0)
603 fwrite(line, 1, j, stdout);
604
605 fclose(fp);
606 }
607
608 puts("\n%%EndResource");
609 }
610 }
611
612 /*
613 * Write the encoding array(s)...
614 */
615
616 puts("% character encoding(s)");
617
618 for (i = 0; i < NumFonts; i ++)
619 {
620 printf("/cupsEncoding%02x [\n", i);
621
622 for (ch = 0; ch < 256; ch ++)
623 {
624 if (Glyphs[Codes[i * 256 + ch]])
625 printf("/%s", Glyphs[Codes[i * 256 + ch]]);
626 else if (Codes[i * 256 + ch] > 255)
627 printf("/uni%04X", Codes[i * 256 + ch]);
628 else
629 printf("/.notdef");
630
631 if ((ch & 7) == 7)
632 putchar('\n');
633 }
634
635 puts("] def");
636 }
637
638 /*
639 * Create the fonts...
640 */
641
642 if (NumFonts == 1)
643 {
644 /*
645 * Just reencode the named fonts...
646 */
647
648 puts("% Reencode fonts");
649
650 for (i = PrettyPrint ? 2 : 1; i >= 0; i --)
651 {
652 printf("/%s findfont\n", Fonts[0][i]);
653 puts("dup length 1 add dict begin\n"
654 " { 1 index /FID ne { def } { pop pop } ifelse } forall\n"
655 " /Encoding cupsEncoding00 def\n"
656 " currentdict\n"
657 "end");
658 printf("/%s exch definefont pop\n", names[i]);
659 }
660 }
661 else
662 {
663 /*
664 * Construct composite fonts... Start by reencoding the base fonts...
665 */
666
667 puts("% Reencode base fonts");
668
f7deaa1a 669 for (i = PrettyPrint ? 2 : 1; i >= 0; i --)
ef416fc2 670 for (j = 0; j < NumFonts; j ++)
671 {
672 printf("/%s findfont\n", Fonts[j][i]);
673 printf("dup length 1 add dict begin\n"
674 " { 1 index /FID ne { def } { pop pop } ifelse } forall\n"
675 " /Encoding cupsEncoding%02x def\n"
676 " currentdict\n"
677 "end\n", j);
678 printf("/%s%02x exch definefont /%s%02x exch def\n", names[i], j,
679 names[i], j);
680 }
681
682 /*
683 * Then merge them into composite fonts...
684 */
685
686 puts("% Create composite fonts...");
687
f7deaa1a 688 for (i = PrettyPrint ? 2 : 1; i >= 0; i --)
ef416fc2 689 {
690 puts("8 dict begin");
691 puts("/FontType 0 def/FontMatrix[1.0 0 0 1.0 0 0]def/FMapType 2 def/Encoding[");
692 for (j = 0; j < NumFonts; j ++)
693 if (j == (NumFonts - 1))
694 printf("%d", j);
695 else if ((j & 15) == 15)
696 printf("%d\n", j);
697 else
698 printf("%d ", j);
699 puts("]def/FDepVector[");
700 for (j = 0; j < NumFonts; j ++)
701 if (j == (NumFonts - 1))
702 printf("%s%02x", names[i], j);
703 else if ((j & 3) == 3)
704 printf("%s%02x\n", names[i], j);
705 else
706 printf("%s%02x ", names[i], j);
707 puts("]def currentdict end");
708 printf("/%s exch definefont pop\n", names[i]);
709 }
710 }
711
712 /*
713 * Output the texttops procset...
714 */
715
716 puts("%%BeginResource: procset texttops 1.1 0");
717
718 puts("% Define fonts");
719
720 printf("/FN /cupsNormal findfont [%.3f 0 0 %.3f 0 0] makefont def\n",
721 120.0 / CharsPerInch, 68.0 / LinesPerInch);
722 printf("/FB /cupsBold findfont [%.3f 0 0 %.3f 0 0] makefont def\n",
723 120.0 / CharsPerInch, 68.0 / LinesPerInch);
724 if (PrettyPrint)
725 printf("/FI /cupsItalic findfont [%.3f 0 0 %.3f 0 0] makefont def\n",
726 120.0 / CharsPerInch, 68.0 / LinesPerInch);
727
728 puts("% Common procedures");
729
730 puts("/N { FN setfont moveto } bind def");
731 puts("/B { FB setfont moveto } bind def");
732 printf("/U { gsave 0.5 setlinewidth 0 %.3f rmoveto "
733 "0 rlineto stroke grestore } bind def\n", -6.8 / LinesPerInch);
734
735 if (PrettyPrint)
736 {
737 if (ColorDevice)
738 {
739 puts("/S { 0.0 setgray show } bind def");
740 puts("/r { 0.5 0.0 0.0 setrgbcolor show } bind def");
741 puts("/g { 0.0 0.5 0.0 setrgbcolor show } bind def");
742 puts("/b { 0.0 0.0 0.5 setrgbcolor show } bind def");
743 }
744 else
745 {
746 puts("/S { 0.0 setgray show } bind def");
747 puts("/r { 0.2 setgray show } bind def");
748 puts("/g { 0.2 setgray show } bind def");
749 puts("/b { 0.2 setgray show } bind def");
750 }
751
752 puts("/I { FI setfont moveto } bind def");
753
754 puts("/n {");
755 puts("\t20 string cvs % convert page number to string");
756 if (NumFonts > 1)
757 {
758 /*
759 * Convert a number to double-byte chars...
760 */
761
762 puts("\tdup length % get length");
763 puts("\tdup 2 mul string /P exch def % P = string twice as long");
764 puts("\t0 1 2 index 1 sub { % loop through each character in the page number");
765 puts("\t\tdup 3 index exch get % get character N from the page number");
766 puts("\t\texch 2 mul dup % compute offset in P");
767 puts("\t\tP exch 0 put % font 0");
768 puts("\t\t1 add P exch 2 index put % character");
769 puts("\t\tpop % discard character");
770 puts("\t} for % do for loop");
771 puts("\tpop pop % discard string and length");
772 puts("\tP % put string on stack");
773 }
774 puts("} bind def");
775
776 printf("/T");
777 write_text(title);
778 puts("def");
779
780 printf("/D");
781 write_text(curdate);
782 puts("def");
783
784 puts("/H {");
785 puts("\tgsave");
786 puts("\t0.9 setgray");
787
788 if (Duplex)
789 {
790 puts("\tdup 2 mod 0 eq {");
791 printf("\t\t%.3f %.3f translate } {\n",
792 PageWidth - PageRight, PageTop + 72.0f / LinesPerInch);
793 printf("\t\t%.3f %.3f translate } ifelse\n",
794 PageLeft, PageTop + 72.0f / LinesPerInch);
795 }
796 else
797 printf("\t%.3f %.3f translate\n",
798 PageLeft, PageTop + 72.0f / LinesPerInch);
799
800 printf("\t0 0 %.3f %.3f rectfill\n", PageRight - PageLeft,
801 144.0f / LinesPerInch);
802
803 puts("\tFB setfont");
804 puts("\t0 setgray");
805
806 if (Duplex)
807 {
808 puts("\tdup 2 mod 0 eq {");
809 printf("\t\tT stringwidth pop neg %.3f add %.3f } {\n",
810 PageRight - PageLeft - 36.0f / LinesPerInch,
811 (0.5f + 0.157f) * 72.0f / LinesPerInch);
812 printf("\t\t%.3f %.3f } ifelse\n", 36.0f / LinesPerInch,
813 (0.5f + 0.157f) * 72.0f / LinesPerInch);
814 }
815 else
816 printf("\t%.3f %.3f\n", 36.0f / LinesPerInch,
817 (0.5f + 0.157f) * 72.0f / LinesPerInch);
818
819 puts("\tmoveto T show");
820
821 printf("\tD dup stringwidth pop neg 2 div %.3f add %.3f\n",
822 (PageRight - PageLeft) * 0.5,
823 (0.5f + 0.157f) * 72.0f / LinesPerInch);
824 puts("\tmoveto show");
825
826 if (Duplex)
827 {
828 puts("\tdup n exch 2 mod 0 eq {");
829 printf("\t\t%.3f %.3f } {\n", 36.0f / LinesPerInch,
830 (0.5f + 0.157f) * 72.0f / LinesPerInch);
831 printf("\t\tdup stringwidth pop neg %.3f add %.3f } ifelse\n",
832 PageRight - PageLeft - 36.0f / LinesPerInch,
833 (0.5f + 0.157f) * 72.0f / LinesPerInch);
834 }
835 else
836 printf("\tn dup stringwidth pop neg %.3f add %.3f\n",
837 PageRight - PageLeft - 36.0f / LinesPerInch,
838 (0.5f + 0.157f) * 72.0f / LinesPerInch);
839
840 puts("\tmoveto show");
841 puts("\tgrestore");
842 puts("} bind def");
843 }
844 else
845 puts("/S { show } bind def");
846
847 puts("%%EndResource");
848
849 puts("%%EndProlog");
850}
851
852
853/*
854 * 'write_line()' - Write a row of text.
855 */
856
857static void
858write_line(int row, /* I - Row number (0 to N) */
859 lchar_t *line) /* I - Line to print */
860{
861 int i; /* Looping var */
862 int col; /* Current column */
863 int attr; /* Current attribute */
864 int font, /* Font to use */
865 lastfont, /* Last font */
866 mono; /* Monospaced? */
867 lchar_t *start; /* First character in sequence */
868
869
1f0275e3 870 for (col = 0; col < SizeColumns;)
ef416fc2 871 {
872 while (col < SizeColumns && (line->ch == ' ' || line->ch == 0))
873 {
874 col ++;
875 line ++;
876 }
877
878 if (col >= SizeColumns)
879 break;
880
881 if (NumFonts == 1)
882 {
883 /*
884 * All characters in a single font - assume monospaced...
885 */
886
887 attr = line->attr;
888 start = line;
889
890 while (col < SizeColumns && line->ch != 0 && attr == line->attr)
891 {
892 col ++;
893 line ++;
894 }
895
896 write_string(col - (line - start), row, line - start, start);
897 }
898 else
899 {
900 /*
901 * Multiple fonts; break up based on the font...
902 */
903
904 attr = line->attr;
905 start = line;
906 lastfont = Chars[line->ch] / 256;
907 mono = strncmp(Fonts[lastfont][0], "Courier", 7) == 0;
908 col ++;
909 line ++;
910
911 if (mono)
912 {
913 while (col < SizeColumns && line->ch != 0 && attr == line->attr)
914 {
915 font = Chars[line->ch] / 256;
916 if (strncmp(Fonts[font][0], "Courier", 7) != 0 ||
917 font != lastfont)
918 break;
919
920 col ++;
921 line ++;
922 }
923 }
924
925 if (Directions[lastfont] > 0)
926 write_string(col - (line - start), row, line - start, start);
927 else
928 {
929 /*
930 * Do right-to-left text...
931 */
932
933 while (col < SizeColumns && line->ch != 0 && attr == line->attr)
934 {
935 if (Directions[Chars[line->ch] / 256] > 0 &&
936 !ispunct(line->ch & 255) && !isspace(line->ch & 255))
937 break;
938
939 col ++;
940 line ++;
941 }
942
943 for (i = 1; start < line; i ++, start ++)
944 if (!isspace(start->ch & 255))
945 write_string(col - i, row, 1, start);
946 }
947 }
948 }
949}
950
951
952/*
953 * 'write_string()' - Write a string of text.
954 */
955
956static void
957write_string(int col, /* I - Start column */
958 int row, /* I - Row */
959 int len, /* I - Number of characters */
960 lchar_t *s) /* I - String to print */
961{
962 int ch; /* Current character */
963 float x, y; /* Position of text */
964 unsigned attr; /* Character attributes */
965
966
967 /*
968 * Position the text and set the font...
969 */
970
971 if (Duplex && (NumPages & 1) == 0)
972 {
973 x = PageWidth - PageRight;
974 y = PageTop;
975 }
976 else
977 {
978 x = PageLeft;
979 y = PageTop;
980 }
981
982 x += (float)col * 72.0f / (float)CharsPerInch;
983 y -= (float)(row + 0.843) * 72.0f / (float)LinesPerInch;
984
985 attr = s->attr;
986
987 if (attr & ATTR_RAISED)
988 y += 36.0 / (float)LinesPerInch;
989 else if (attr & ATTR_LOWERED)
990 y -= 36.0 / (float)LinesPerInch;
991
992 if (x == (int)x)
993 printf("%.0f ", x);
994 else
995 printf("%.3f ", x);
996
997 if (y == (int)y)
998 printf("%.0f ", y);
999 else
1000 printf("%.3f ", y);
1001
1002 if (attr & ATTR_BOLD)
1003 putchar('B');
1004 else if (attr & ATTR_ITALIC)
1005 putchar('I');
1006 else
1007 putchar('N');
1008
1009 if (attr & ATTR_UNDERLINE)
1010 printf(" %.3f U", (float)len * 72.0 / (float)CharsPerInch);
1011
1012 if (NumFonts > 1)
1013 {
1014 /*
1015 * Write a hex string...
1016 */
1017
1018 putchar('<');
1019
1020 while (len > 0)
1021 {
1022 printf("%04x", Chars[s->ch]);
1023
1024 len --;
1025 s ++;
1026 }
1027
1028 putchar('>');
1029 }
1030 else
1031 {
1032 /*
1033 * Write a quoted string...
1034 */
1035
1036 putchar('(');
1037
1038 while (len > 0)
1039 {
1040 ch = Chars[s->ch];
1041
1042 if (ch < 32 || ch > 126)
1043 {
1044 /*
1045 * Quote 8-bit and control characters...
1046 */
1047
1048 printf("\\%03o", ch);
1049 }
1050 else
1051 {
1052 /*
1053 * Quote the parenthesis and backslash as needed...
1054 */
1055
1056 if (ch == '(' || ch == ')' || ch == '\\')
1057 putchar('\\');
1058
1059 putchar(ch);
1060 }
1061
1062 len --;
1063 s ++;
1064 }
1065
1066 putchar(')');
1067 }
1068
1069 if (PrettyPrint)
1070 {
1071 if (attr & ATTR_RED)
1072 puts("r");
1073 else if (attr & ATTR_GREEN)
1074 puts("g");
1075 else if (attr & ATTR_BLUE)
1076 puts("b");
1077 else
1078 puts("S");
1079 }
1080 else
1081 puts("S");
1082}
1083
1084
1085/*
1086 * 'write_text()' - Write a text string, quoting/encoding as needed.
1087 */
1088
1089static void
1090write_text(const char *s) /* I - String to write */
1091{
1092 int ch; /* Actual character value (UTF8) */
1093 const unsigned char *utf8; /* UTF8 text */
1094
1095
1096 if (NumFonts > 1)
1097 {
1098 /*
1099 * 8/8 encoding...
1100 */
1101
1102 putchar('<');
1103
1104 utf8 = (const unsigned char *)s;
1105
1106 while (*utf8)
1107 {
e6013cfa 1108 if (*utf8 < 0xc0)
ef416fc2 1109 ch = *utf8 ++;
1110 else if ((*utf8 & 0xe0) == 0xc0)
1111 {
1112 /*
1113 * Two byte character...
1114 */
1115
1116 ch = ((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f);
1117 utf8 += 2;
1118 }
1119 else
1120 {
1121 /*
1122 * Three byte character...
1123 */
1124
1125 ch = ((((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f)) << 6) |
1126 (utf8[2] & 0x3f);
1127 utf8 += 3;
1128 }
1129
1130 printf("%04x", Chars[ch]);
1131 }
1132
1133 putchar('>');
1134 }
1135 else
1136 {
1137 /*
1138 * Standard 8-bit encoding...
1139 */
1140
1141 putchar('(');
1142
1143 while (*s)
1144 {
1145 if (*s < 32 || *s > 126)
1146 printf("\\%03o", *s);
1147 else
1148 {
1149 if (*s == '(' || *s == ')' || *s == '\\')
1150 putchar('\\');
1151
1152 putchar(*s);
1153 }
1154
1155 s ++;
1156 }
1157
1158 putchar(')');
1159 }
1160}
1161
1162
1163/*
b19ccc9e 1164 * End of "$Id: texttops.c 7720 2008-07-11 22:46:21Z mike $".
ef416fc2 1165 */