1 /*
2 * Text to PDF filter for the Common UNIX Printing System (CUPS).
3 *
4 * Copyright 2008,2012 by Tobias Hoffmann.
5 * Copyright 2007 by Apple Inc.
6 * Copyright 1993-2007 by Easy Software Products.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file.
12 *
13 * Contents:
14 *
15 * main() - Main entry for text to PDF filter.
16 * WriteEpilogue() - Write the PDF file epilogue.
17 * WritePage() - Write a page of text.
18 * WriteProlog() - Write the PDF file prolog with options.
19 * write_line() - Write a row of text.
20 * write_string() - Write a string of text.
21 */
22
23 /*
24 * Include necessary headers...
25 */
26
27 #include "textcommon.h"
28 #include "pdfutils.h"
29 #include "fontembed/embed.h"
30 #include <assert.h>
31 #include "fontembed/sfnt.h"
32 #include <fontconfig/fontconfig.h>
33
34 /*
35 * Globals...
36 */
37
38 #ifdef CUPS_1_4 /* CUPS 1.4.x or newer: only UTF8 is supported */
39 int UTF8 = 1; /* Use UTF-8 encoding? */
40 #endif /* CUPS_1_4 */
41
42 EMB_PARAMS *font_load(const char *font, int fontwidth);
43
font_load(const char * font,int fontwidth)44 EMB_PARAMS *font_load(const char *font, int fontwidth)
45 {
46 OTF_FILE *otf;
47
48 FcPattern *pattern;
49 FcFontSet *candidates;
50 FcChar8 *fontname = NULL;
51 FcResult result;
52 int i;
53
54 if ( (font[0]=='/')||(font[0]=='.') ) {
55 candidates = NULL;
56 fontname=(FcChar8 *)strdup(font);
57 } else {
58 FcInit ();
59 pattern = FcNameParse ((const FcChar8 *)font);
60 FcPatternAddInteger (pattern, FC_SPACING, FC_MONO); // guide fc, in case substitution becomes necessary
61 FcConfigSubstitute (0, pattern, FcMatchPattern);
62 FcDefaultSubstitute (pattern);
63
64 /* Receive a sorted list of fonts matching our pattern */
65 candidates = FcFontSort (0, pattern, FcFalse, 0, &result);
66 FcPatternDestroy (pattern);
67
68 if (candidates) {
69 /* In the list of fonts returned by FcFontSort()
70 find the first one that is both in TrueType format and monospaced */
71 for (i = 0; i < candidates->nfont; i++) {
72 FcChar8 *fontformat=NULL; // TODO? or just try?
73 int spacing=0; // sane default, as FC_MONO == 100
74 FcPatternGetString (candidates->fonts[i], FC_FONTFORMAT, 0, &fontformat);
75 FcPatternGetInteger (candidates->fonts[i], FC_SPACING, 0, &spacing);
76
77 if ( (fontformat)&&((spacing == FC_MONO) || (fontwidth == 2)) ) { // check for monospace or double width fonts
78 if (strcmp((const char *)fontformat, "TrueType") == 0) {
79 fontname = FcPatternFormat (candidates->fonts[i], (const FcChar8 *)"%{file|cescape}/%{index}");
80 break;
81 } else if (strcmp((const char *)fontformat, "CFF") == 0) {
82 fontname = FcPatternFormat (candidates->fonts[i], (const FcChar8 *)"%{file|cescape}"); // TTC only possible with non-cff glyphs!
83 break;
84 }
85 }
86 }
87 FcFontSetDestroy (candidates);
88 }
89 }
90
91 if (!fontname) {
92 // TODO: try /usr/share/fonts/*/*/%s.ttf
93 fprintf(stderr,"No viable font found\n");
94 return NULL;
95 }
96
97 otf = otf_load((const char *)fontname);
98 free(fontname);
99 if (!otf) {
100 return NULL;
101 }
102
103 FONTFILE *ff=fontfile_open_sfnt(otf);
104 assert(ff);
105 EMB_PARAMS *emb=emb_new(ff,
106 EMB_DEST_PDF16,
107 EMB_C_FORCE_MULTIBYTE|
108 EMB_C_TAKE_FONTFILE);
109 assert(emb);
110 assert(emb->plan&EMB_A_MULTIBYTE);
111 return emb;
112 }
113
font_std(const char * name)114 EMB_PARAMS *font_std(const char *name)
115 {
116 FONTFILE *ff=fontfile_open_std(name);
117 assert(ff);
118 EMB_PARAMS *emb=emb_new(ff,
119 EMB_DEST_PDF16,
120 EMB_C_TAKE_FONTFILE);
121 assert(emb);
122 return emb;
123 }
124
125 /*
126 * Globals...
127 */
128
129 int NumFonts; /* Number of fonts to use */
130 EMB_PARAMS *Fonts[256][4]; /* Fonts to use */
131 unsigned short Chars[256]; /* Input char to unicode */
132 unsigned char Codes[65536]; /* Unicode glyph mapping to font */
133 int Widths[256]; /* Widths of each font */
134 int Directions[256];/* Text directions for each font */
135 pdfOut *pdf;
136 int FontResource; /* Object number of font resource dictionary */
137 float FontScaleX,FontScaleY; /* The font matrix */
138 lchar_t *Title,*Date; /* The title and date strings */
139
140 /*
141 * Local functions...
142 */
143
144 static void write_line(int row, lchar_t *line);
145 static void write_string(int col, int row, int len, lchar_t *s);
146 static lchar_t *make_wide(const char *buf);
147 static void write_font_str(float x,float y,int fontid, lchar_t *str, int len);
148 static void write_pretty_header();
149
150
151 /*
152 * 'main()' - Main entry for text to PDF filter.
153 */
154
155 int /* O - Exit status */
main(int argc,char * argv[])156 main(int argc, /* I - Number of command-line arguments */
157 char *argv[]) /* I - Command-line arguments */
158 {
159 return (TextMain("texttopdf", argc, argv));
160 }
161
162
163 /*
164 * 'WriteEpilogue()' - Write the PDF file epilogue.
165 */
166
167 void
WriteEpilogue(void)168 WriteEpilogue(void)
169 {
170 static char *names[] = /* Font names */
171 { "FN","FB","FI","FBI" };
172 int i,j;
173
174 // embed fonts
175 for (i = PrettyPrint ? 3 : 1; i >= 0; i --) {
176 for (j = 0; j < NumFonts; j ++)
177 {
178 EMB_PARAMS *emb=Fonts[j][i];
179 if (emb->font->fobj) { // already embedded
180 continue;
181 }
182 if ( (!emb->subset)||(bits_used(emb->subset,emb->font->sfnt->numGlyphs)) ) {
183 emb->font->fobj=pdfOut_write_font(pdf,emb);
184 assert(emb->font->fobj);
185 }
186 }
187 }
188
189 /*
190 * Create the global fontdict
191 */
192
193 // now fix FontResource
194 pdf->xref[FontResource-1]=pdf->filepos;
195 pdfOut_printf(pdf,"%d 0 obj\n"
196 "<<\n",
197 FontResource);
198
199 for (i = PrettyPrint ? 3 : 1; i >= 0; i --) {
200 for (j = 0; j < NumFonts; j ++) {
201 EMB_PARAMS *emb=Fonts[j][i];
202 if (emb->font->fobj) { // used
203 pdfOut_printf(pdf," /%s%02x %d 0 R\n",names[i],j,emb->font->fobj);
204 }
205 }
206 }
207
208 pdfOut_printf(pdf,">>\n"
209 "endobj\n");
210
211 pdfOut_finish_pdf(pdf);
212
213 pdfOut_free(pdf);
214 }
215
216 /*
217 * {{{ 'WritePage()' - Write a page of text.
218 */
219
220 void
WritePage(void)221 WritePage(void)
222 {
223 int line; /* Current line */
224
225 int content=pdfOut_add_xref(pdf);
226 pdfOut_printf(pdf,"%d 0 obj\n"
227 "<</Length %d 0 R\n"
228 ">>\n"
229 "stream\n"
230 "q\n"
231 ,content,content+1);
232 long size=-(pdf->filepos-2);
233
234 NumPages ++;
235 if (PrettyPrint)
236 write_pretty_header(pdf);
237
238 for (line = 0; line < SizeLines; line ++)
239 write_line(line, Page[line]);
240
241 size+=pdf->filepos+2;
242 pdfOut_printf(pdf,"Q\n"
243 "endstream\n"
244 "endobj\n");
245
246 int len_obj=pdfOut_add_xref(pdf);
247 assert(len_obj==content+1);
248 pdfOut_printf(pdf,"%d 0 obj\n"
249 "%ld\n"
250 "endobj\n",
251 len_obj,size);
252
253 int obj=pdfOut_add_xref(pdf);
254 pdfOut_printf(pdf,"%d 0 obj\n"
255 "<</Type/Page\n"
256 " /Parent 1 0 R\n"
257 " /MediaBox [0 0 %.0f %.0f]\n"
258 " /Contents %d 0 R\n"
259 " /Resources << /Font %d 0 R >>\n"
260 ">>\n"
261 "endobj\n",
262 obj,PageWidth,PageLength,content,FontResource);
263 pdfOut_add_page(pdf,obj);
264
265 memset(Page[0], 0, sizeof(lchar_t) * SizeColumns * SizeLines);
266 }
267 // }}}
268
269 /*
270 * {{{'WriteProlog()' - Write the PDF file prolog with options.
271 */
272
273 void
WriteProlog(const char * title,const char * user,const char * classification,const char * label,ppd_file_t * ppd)274 WriteProlog(const char *title, /* I - Title of job */
275 const char *user, /* I - Username */
276 const char *classification, /* I - Classification */
277 const char *label, /* I - Page label */
278 ppd_file_t *ppd) /* I - PPD file info */
279 {
280 int i, j, k; /* Looping vars */
281 char *charset; /* Character set string */
282 char filename[1024]; /* Glyph filenames */
283 FILE *fp; /* Glyph files */
284 const char *datadir; /* CUPS_DATADIR environment variable */
285 char line[1024], /* Line from file */
286 *lineptr, /* Pointer into line */
287 *valptr; /* Pointer to value in line */
288 #ifndef CUPS_1_4 /* CUPS 1.4.x or newer: support for non-utf8 removed */
289 int ch, unicode; /* Character values */
290 #endif
291 int start, end; /* Start and end values for range */
292 time_t curtime; /* Current time */
293 struct tm *curtm; /* Current date */
294 char curdate[255]; /* Current date (text format) */
295 int num_fonts=0; /* Number of unique fonts */
296 EMB_PARAMS *fonts[1024]; /* Unique fonts */
297 char *fontnames[1024]; /* Unique fonts */
298 #if 0
299 static char *names[] = /* Font names */
300 {
301 "FN","FB","FI"
302 /*
303 "cupsNormal",
304 "cupsBold",
305 "cupsItalic"
306 */
307 };
308 #endif
309
310
311 /*
312 * Get the data directory...
313 */
314
315 if ((datadir = getenv("CUPS_DATADIR")) == NULL)
316 datadir = CUPS_DATADIR;
317
318 /*
319 * Adjust margins as necessary...
320 */
321
322 if (classification || label)
323 {
324 /*
325 * Leave room for labels...
326 */
327
328 PageBottom += 36;
329 PageTop -= 36;
330 }
331
332 if (PageColumns > 1)
333 {
334 ColumnGutter = CharsPerInch / 2;
335 ColumnWidth = (SizeColumns - ColumnGutter * (PageColumns - 1)) /
336 PageColumns;
337 }
338 else
339 ColumnWidth = SizeColumns;
340
341 /*
342 * {{{ Output the PDF header...
343 */
344
345 assert(!pdf);
346 pdf=pdfOut_new();
347 assert(pdf);
348
349 pdfOut_begin_pdf(pdf);
350 pdfOut_printf(pdf,"%%cupsRotation: %d\n", (Orientation & 3) * 90); // TODO?
351
352 pdfOut_add_kv(pdf,"Creator","texttopdf/" PACKAGE_VERSION);
353
354 curtime = time(NULL);
355 curtm = localtime(&curtime);
356 strftime(curdate, sizeof(curdate), "%c", curtm);
357
358 pdfOut_add_kv(pdf,"CreationDate",pdfOut_to_pdfdate(curtm));
359 pdfOut_add_kv(pdf,"Title",title);
360 pdfOut_add_kv(pdf,"Author",user); // was(PostScript): /For
361 // }}}
362
363 /*
364 * {{{ Initialize globals...
365 */
366
367 NumFonts = 0;
368 memset(Fonts, 0, sizeof(Fonts));
369 memset(Chars, 0, sizeof(Chars));
370 memset(Codes, 0, sizeof(Codes));
371 // }}}
372
373 /*
374 * Get the output character set...
375 */
376
377 charset = getenv("CHARSET");
378 if (charset != NULL && strcmp(charset, "us-ascii") != 0) // {{{
379 {
380 snprintf(filename, sizeof(filename), "%s/charsets/pdf.%s", datadir, charset);
381
382 if ((fp = fopen(filename, "r")) == NULL)
383 {
384 /*
385 * Can't open charset file!
386 */
387
388 fprintf(stderr, "ERROR: Unable to open %s: %s\n", filename,
389 strerror(errno));
390 exit(1);
391 }
392
393 /*
394 * Opened charset file; now see if this is really a charset file...
395 */
396
397 if (fgets(line, sizeof(line), fp) == NULL)
398 {
399 /*
400 * Bad/empty charset file!
401 */
402
403 fclose(fp);
404 fprintf(stderr, "ERROR: Bad charset file %s\n", filename);
405 exit(1);
406 }
407
408 if (strncmp(line, "charset", 7) != 0)
409 {
410 /*
411 * Bad format/not a charset file!
412 */
413
414 fclose(fp);
415 fprintf(stderr, "ERROR: Bad charset file %s\n", filename);
416 exit(1);
417 }
418
419 /*
420 * See if this is an 8-bit or UTF-8 character set file...
421 */
422
423 line[strlen(line) - 1] = '\0'; /* Drop \n */
424 for (lineptr = line + 7; isspace(*lineptr & 255); lineptr ++); /* Skip whitespace */
425
426 #ifndef CUPS_1_4 /* CUPS 1.4.x or newer: support for non-utf8 removed */
427 if (strcmp(lineptr, "8bit") == 0) // {{{
428 {
429 /*
430 * 8-bit text...
431 */
432
433 UTF8 = 0;
434 NumFonts = 0;
435
436 /*
437 * Read the font description(s)...
438 */
439
440 while (fgets(line, sizeof(line), fp) != NULL)
441 {
442 /*
443 * Skip comment and blank lines...
444 */
445
446 if (line[0] == '#' || line[0] == '\n')
447 continue;
448
449 /*
450 * Read the font descriptions that should look like:
451 *
452 * first last direction width normal [bold italic bold-italic]
453 */
454
455 lineptr = line;
456
457 start = strtol(lineptr, &lineptr, 16);
458 end = strtol(lineptr, &lineptr, 16);
459
460 while (isspace(*lineptr & 255))
461 lineptr ++;
462
463 if (!*lineptr)
464 break; /* Must be a font mapping */
465
466 valptr = lineptr;
467
468 while (!isspace(*lineptr & 255) && *lineptr)
469 lineptr ++;
470
471 if (!*lineptr)
472 {
473 /*
474 * Can't have a font without all required values...
475 */
476
477 fprintf(stderr, "ERROR: Bad font description line: %s\n", valptr);
478 fclose(fp);
479 exit(1);
480 }
481
482 *lineptr++ = '\0';
483
484 if (strcmp(valptr, "ltor") == 0)
485 Directions[NumFonts] = 1;
486 else if (strcmp(valptr, "rtol") == 0)
487 Directions[NumFonts] = -1;
488 else
489 {
490 fprintf(stderr, "ERROR: Bad text direction %s\n", valptr);
491 fclose(fp);
492 exit(1);
493 }
494
495 /*
496 * Got the direction, now get the width...
497 */
498
499 while (isspace(*lineptr & 255))
500 lineptr ++;
501
502 valptr = lineptr;
503
504 while (!isspace(*lineptr & 255) && *lineptr)
505 lineptr ++;
506
507 if (!*lineptr)
508 {
509 /*
510 * Can't have a font without all required values...
511 */
512
513 fprintf(stderr, "ERROR: Bad font description line: %s\n", valptr);
514 fclose(fp);
515 exit(1);
516 }
517
518 *lineptr++ = '\0';
519
520 if (strcmp(valptr, "single") == 0)
521 Widths[NumFonts] = 1;
522 else if (strcmp(valptr, "double") == 0)
523 Widths[NumFonts] = 2;
524 else
525 {
526 fprintf(stderr, "ERROR: Bad text width %s\n", valptr);
527 fclose(fp);
528 exit(1);
529 }
530
531 /*
532 * Get the fonts...
533 */
534
535 for (i = 0; *lineptr && i < 4; i ++)
536 {
537 while (isspace(*lineptr & 255))
538 lineptr ++;
539
540 valptr = lineptr;
541
542 while (!isspace(*lineptr & 255) && *lineptr)
543 lineptr ++;
544
545 if (*lineptr)
546 *lineptr++ = '\0';
547
548 if (lineptr > valptr) {
549 // search for duplicates
550 for (k = 0; k < num_fonts; k ++)
551 if (strcmp(valptr, fontnames[k]) == 0) {
552 Fonts[NumFonts][i] = fonts[k];
553 break;
554 }
555
556 if (k==num_fonts) { // not found
557 fonts[num_fonts] = Fonts[NumFonts][i] = font_load(valptr, Widths[NumFonts]);
558 if (!fonts[num_fonts]) { // font missing/corrupt, replace by first
559 fprintf(stderr,"WARNING: Ignored bad font \"%s\"\n",valptr);
560 break;
561 }
562 fontnames[num_fonts++] = strdup(valptr);
563 }
564 }
565 }
566
567 /* ignore complete range, when the first font is not available */
568 if (i==0) {
569 continue;
570 }
571
572 /*
573 * Fill in remaining fonts as needed...
574 */
575
576 for (j = i; j < 4; j ++)
577 Fonts[NumFonts][j] = Fonts[NumFonts][0];
578
579 /*
580 * Define the character mappings...
581 */
582
583 for (i = start; i <= end; i ++)
584 Codes[i] = NumFonts;
585
586 NumFonts ++;
587 }
588
589 /*
590 * Read encoding lines...
591 */
592
593 do
594 {
595 /*
596 * Skip comment and blank lines...
597 */
598
599 if (line[0] == '#' || line[0] == '\n')
600 continue;
601
602 /*
603 * Grab the character and unicode glyph number.
604 */
605
606 if (sscanf(line, "%x%x", &ch, &unicode) == 2 && ch < 256)
607 Chars[ch] = unicode;
608 }
609 while (fgets(line, sizeof(line), fp) != NULL);
610
611 fclose(fp);
612 } else // }}}
613 #endif
614 if (strcmp(lineptr, "utf8") == 0) { // {{{
615 /*
616 * UTF-8 (Unicode) text...
617 */
618
619 UTF8 = 1;
620
621 /*
622 * Read the font descriptions...
623 */
624
625 NumFonts = 0;
626
627 while (fgets(line, sizeof(line), fp) != NULL)
628 {
629 /*
630 * Skip comment and blank lines...
631 */
632
633 if (line[0] == '#' || line[0] == '\n')
634 continue;
635
636 /*
637 * Read the font descriptions that should look like:
638 *
639 * start end direction width normal [bold italic bold-italic]
640 */
641
642 lineptr = line;
643
644 start = strtol(lineptr, &lineptr, 16);
645 end = strtol(lineptr, &lineptr, 16);
646
647 while (isspace(*lineptr & 255))
648 lineptr ++;
649
650 valptr = lineptr;
651
652 while (!isspace(*lineptr & 255) && *lineptr)
653 lineptr ++;
654
655 if (!*lineptr)
656 {
657 /*
658 * Can't have a font without all required values...
659 */
660
661 fprintf(stderr, "ERROR: Bad font description line: %s\n", valptr);
662 fclose(fp);
663 exit(1);
664 }
665
666 *lineptr++ = '\0';
667
668 if (strcmp(valptr, "ltor") == 0)
669 Directions[NumFonts] = 1;
670 else if (strcmp(valptr, "rtol") == 0)
671 Directions[NumFonts] = -1;
672 else
673 {
674 fprintf(stderr, "ERROR: Bad text direction %s\n", valptr);
675 fclose(fp);
676 exit(1);
677 }
678
679 /*
680 * Got the direction, now get the width...
681 */
682
683 while (isspace(*lineptr & 255))
684 lineptr ++;
685
686 valptr = lineptr;
687
688 while (!isspace(*lineptr & 255) && *lineptr)
689 lineptr ++;
690
691 if (!*lineptr)
692 {
693 /*
694 * Can't have a font without all required values...
695 */
696
697 fprintf(stderr, "ERROR: Bad font description line: %s\n", valptr);
698 fclose(fp);
699 exit(1);
700 }
701
702 *lineptr++ = '\0';
703
704 if (strcmp(valptr, "single") == 0)
705 Widths[NumFonts] = 1;
706 else if (strcmp(valptr, "double") == 0)
707 Widths[NumFonts] = 2;
708 else
709 {
710 fprintf(stderr, "ERROR: Bad text width %s\n", valptr);
711 fclose(fp);
712 exit(1);
713 }
714
715 /*
716 * Get the fonts...
717 */
718
719 for (i = 0; *lineptr && i < 4; i ++)
720 {
721 while (isspace(*lineptr & 255))
722 lineptr ++;
723
724 valptr = lineptr;
725
726 while (!isspace(*lineptr & 255) && *lineptr)
727 lineptr ++;
728
729 if (*lineptr)
730 *lineptr++ = '\0';
731
732 if (lineptr > valptr) {
733 // search for duplicates
734 for (k = 0; k < num_fonts; k ++)
735 if (strcmp(valptr, fontnames[k]) == 0) {
736 Fonts[NumFonts][i] = fonts[k];
737 break;
738 }
739
740 if (k==num_fonts) { // not found
741 fonts[num_fonts] = Fonts[NumFonts][i] = font_load(valptr, Widths[NumFonts]);
742 if (!fonts[num_fonts]) { // font missing/corrupt, replace by first
743 fprintf(stderr,"WARNING: Ignored bad font \"%s\"\n",valptr);
744 break;
745 }
746 fontnames[num_fonts++] = strdup(valptr);
747 }
748 }
749 }
750
751 /* ignore complete range, when the first font is not available */
752 if (i==0) {
753 continue;
754 }
755
756 /*
757 * Fill in remaining fonts as needed...
758 */
759
760 for (j = i; j < 4; j ++)
761 Fonts[NumFonts][j] = Fonts[NumFonts][0];
762
763 /*
764 * Define the character mappings...
765 */
766
767 for (i = start; i <= end; i ++)
768 {
769 Codes[i] = NumFonts;
770 }
771
772 /*
773 * Move to the next font, stopping if needed...
774 */
775
776 NumFonts ++;
777 if (NumFonts >= 256)
778 break;
779 }
780
781 fclose(fp);
782 } // }}}
783 else // {{{
784 {
785 fprintf(stderr, "ERROR: Bad charset type %s\n", lineptr);
786 fclose(fp);
787 exit(1);
788 } // }}}
789 } // }}}
790 else // {{{ Standard ASCII
791 {
792 /*
793 * Standard ASCII output just uses Courier, Courier-Bold, and
794 * possibly Courier-Oblique.
795 */
796
797 NumFonts = 1;
798
799 Fonts[0][ATTR_NORMAL] = font_std("Courier");
800 Fonts[0][ATTR_BOLD] = font_std("Courier-Bold");
801 Fonts[0][ATTR_ITALIC] = font_std("Courier-Oblique");
802 Fonts[0][ATTR_BOLDITALIC] = font_std("Courier-BoldOblique");
803
804 Widths[0] = 1;
805 Directions[0] = 1;
806
807 /*
808 * Define US-ASCII characters...
809 */
810
811 for (i = 32; i < 127; i ++)
812 {
813 Chars[i] = i;
814 Codes[i] = NumFonts-1;
815 }
816 }
817 // }}}
818
819 if (NumFonts==0) {
820 fprintf(stderr, "ERROR: No usable font available\n");
821 exit(1);
822 }
823
824 FontScaleX=120.0 / CharsPerInch;
825 FontScaleY=68.0 / LinesPerInch;
826
827 // allocate now, for pages to use. will be fixed in epilogue
828 FontResource=pdfOut_add_xref(pdf);
829
830 if (PrettyPrint)
831 {
832 Date=make_wide(curdate);
833 Title=make_wide(title);
834 }
835 }
836 // }}}
837
838 /*
839 * {{{ 'write_line()' - Write a row of text.
840 */
841
842 static void
write_line(int row,lchar_t * line)843 write_line(int row, /* I - Row number (0 to N) */
844 lchar_t *line) /* I - Line to print */
845 {
846 int i; /* Looping var */
847 int col,xcol,xwid; /* Current column */
848 int attr; /* Current attribute */
849 int font, /* Font to use */
850 lastfont, /* Last font */
851 mono; /* Monospaced? */
852 lchar_t *start; /* First character in sequence */
853
854
855 xcol=0;
856 for (col = 0, start = line; col < SizeColumns;)
857 {
858 while (col < SizeColumns && (line->ch == ' ' || line->ch == 0))
859 {
860 col ++;
861 xcol ++;
862 line ++;
863 }
864
865 if (col >= SizeColumns)
866 break;
867
868 if (NumFonts == 1)
869 {
870 /*
871 * All characters in a single font - assume monospaced and single width...
872 */
873
874 attr = line->attr;
875 start = line;
876
877 while (col < SizeColumns && line->ch != 0 && attr == line->attr)
878 {
879 col ++;
880 line ++;
881 }
882
883 write_string(col - (line - start), row, line - start, start);
884 }
885 else
886 {
887 /*
888 * Multiple fonts; break up based on the font...
889 */
890
891 attr = line->attr;
892 start = line;
893 xwid = 0;
894 if (UTF8) {
895 lastfont = Codes[line->ch];
896 } else {
897 lastfont = Codes[Chars[line->ch]];
898 }
899 // mono = strncmp(Fonts[lastfont][0], "Courier", 7) == 0;
900 mono=1; // TODO
901
902 col ++;
903 xwid += Widths[lastfont];
904 line ++;
905
906 if (mono)
907 {
908 while (col < SizeColumns && line->ch != 0 && attr == line->attr)
909 {
910 if (UTF8) {
911 font = Codes[line->ch];
912 } else {
913 font = Codes[Chars[line->ch]];
914 }
915 if (/*strncmp(Fonts[font][0], "Courier", 7) != 0 ||*/ // TODO
916 font != lastfont)
917 break;
918
919 col ++;
920 xwid += Widths[lastfont];
921 line ++;
922 }
923 }
924
925 if (Directions[lastfont] > 0) {
926 write_string(xcol, row, line - start, start);
927 xcol += xwid;
928 }
929 else
930 {
931 /*
932 * Do right-to-left text... ; assume no font change without direction change
933 */
934
935 while (col < SizeColumns && line->ch != 0 && attr == line->attr)
936 {
937 if (UTF8) {
938 font = Codes[line->ch];
939 } else {
940 font = Codes[Chars[line->ch]];
941 }
942 if (Directions[font] > 0 &&
943 !ispunct(line->ch & 255) && !isspace(line->ch & 255))
944 break;
945
946 col ++;
947 xwid += Widths[lastfont];
948 line ++;
949 }
950
951 for (i = 1; start < line; i ++, start ++)
952 if (!isspace(start->ch & 255)) {
953 xwid-=Widths[lastfont];
954 write_string(xcol + xwid, row, 1, start);
955 } else {
956 xwid--;
957 }
958 }
959 }
960 }
961 }
962 // }}}
963
make_wide(const char * buf)964 static lchar_t *make_wide(const char *buf) // {{{ - convert to lchar_t
965 {
966 const unsigned char *utf8; /* UTF8 text */
967 lchar_t *ret,*out;
968
969 // this is enough, utf8 chars will only require less space
970 out=ret=malloc((strlen(buf)+1)*sizeof(lchar_t));
971
972 utf8 = (const unsigned char *)buf;
973 while (*utf8)
974 {
975 out->attr=0;
976
977 if (*utf8 < 0xc0 || !UTF8)
978 out->ch = *utf8 ++;
979 else if ((*utf8 & 0xe0) == 0xc0)
980 {
981 /*
982 * Two byte character...
983 */
984
985 out->ch = ((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f);
986 utf8 += 2;
987 }
988 else
989 {
990 /*
991 * Three byte character...
992 */
993
994 out->ch = ((((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f)) << 6) |
995 (utf8[2] & 0x3f);
996 utf8 += 3;
997 }
998
999 out++;
1000 }
1001 out->ch=out->attr=0;
1002 return ret;
1003 }
1004 // }}}
1005
1006 /*
1007 * {{{ 'write_string()' - Write a string of text.
1008 */
1009
1010 static void
write_string(int col,int row,int len,lchar_t * s)1011 write_string(int col, /* I - Start column */
1012 int row, /* I - Row */
1013 int len, /* I - Number of characters */
1014 lchar_t *s) /* I - String to print */
1015 {
1016 float x, y; /* Position of text */
1017 unsigned attr; /* Character attributes */
1018
1019
1020 /*
1021 * Position the text and set the font...
1022 */
1023
1024 if (Duplex && (NumPages & 1) == 0)
1025 {
1026 x = PageWidth - PageRight;
1027 y = PageTop;
1028 }
1029 else
1030 {
1031 x = PageLeft;
1032 y = PageTop;
1033 }
1034
1035 x += (float)col * 72.0f / (float)CharsPerInch;
1036 y -= (float)(row + 0.843) * 72.0f / (float)LinesPerInch;
1037
1038 attr = s->attr;
1039
1040 if (attr & ATTR_RAISED)
1041 y += 36.0 / (float)LinesPerInch;
1042 else if (attr & ATTR_LOWERED)
1043 y -= 36.0 / (float)LinesPerInch;
1044
1045 if (attr & ATTR_UNDERLINE)
1046 pdfOut_printf(pdf,"q 0.5 w 0 g %.3f %.3f m %.3f %.3f l S Q ",
1047 x, y - 6.8 / LinesPerInch,
1048 x + (float)len * 72.0 / (float)CharsPerInch,
1049 y - 6.8 / LinesPerInch);
1050
1051 if (PrettyPrint)
1052 {
1053 if (ColorDevice) {
1054 if (attr & ATTR_RED)
1055 pdfOut_printf(pdf,"0.5 0 0 rg\n");
1056 else if (attr & ATTR_GREEN)
1057 pdfOut_printf(pdf,"0 0.5 0 rg\n");
1058 else if (attr & ATTR_BLUE)
1059 pdfOut_printf(pdf,"0 0 0.5 rg\n");
1060 else
1061 pdfOut_printf(pdf,"0 g\n");
1062 } else {
1063 if ( (attr & ATTR_RED)||(attr & ATTR_GREEN)||(attr & ATTR_BLUE) )
1064 pdfOut_printf(pdf,"0.2 g\n");
1065 else
1066 pdfOut_printf(pdf,"0 g\n");
1067 }
1068 }
1069 else
1070 pdfOut_printf(pdf,"0 g\n");
1071
1072 write_font_str(x,y,attr & ATTR_FONT,s,len);
1073 }
1074 // }}}
1075
1076 // {{{ show >len characters from >str, using the right font(s) at >x,>y
write_font_str(float x,float y,int fontid,lchar_t * str,int len)1077 static void write_font_str(float x,float y,int fontid, lchar_t *str, int len)
1078 {
1079 unsigned short ch; /* Current character */
1080 static char *names[] = /* Font names */
1081 { "FN","FB","FI","FBI" };
1082
1083 if (len==-1) {
1084 for (len=0;str[len].ch;len++);
1085 }
1086 pdfOut_printf(pdf,"BT\n");
1087
1088 if (x == (int)x)
1089 pdfOut_printf(pdf," %.0f ", x);
1090 else
1091 pdfOut_printf(pdf," %.3f ", x);
1092
1093 if (y == (int)y)
1094 pdfOut_printf(pdf,"%.0f Td\n", y);
1095 else
1096 pdfOut_printf(pdf,"%.3f Td\n", y);
1097
1098 int lastfont,font;
1099
1100 // split on font boundary
1101 while (len > 0)
1102 {
1103 /*
1104 * Write a hex string...
1105 */
1106 if (UTF8) {
1107 lastfont=Codes[str->ch];
1108 } else {
1109 lastfont=Codes[Chars[str->ch]];
1110 }
1111 EMB_PARAMS *emb=Fonts[lastfont][fontid];
1112 OTF_FILE *otf=emb->font->sfnt;
1113
1114 if (otf) { // TODO?
1115 pdfOut_printf(pdf," %.3f Tz\n",
1116 FontScaleX*600.0/(otf_get_width(otf,4)*1000.0/otf->unitsPerEm)*100.0/FontScaleY); // TODO?
1117 // gid==4 is usually '!', the char after space. We just need "the" width for the monospaced font. gid==0 is bad, and space might also be bad.
1118 } else {
1119 pdfOut_printf(pdf," %.3f Tz\n",
1120 FontScaleX*100.0/FontScaleY); // TODO?
1121 }
1122
1123 pdfOut_printf(pdf," /%s%02x %.3f Tf <",
1124 names[fontid],lastfont,FontScaleY);
1125
1126 while (len > 0)
1127 {
1128 if (UTF8) {
1129 ch=str->ch;
1130 } else {
1131 ch=Chars[str->ch];
1132 }
1133
1134 font = Codes[ch];
1135 if (lastfont != font) { // only possible, when not used via write_string (e.g. utf-8filename.txt in prettyprint)
1136 break;
1137 }
1138 if (otf) { // TODO
1139 const unsigned short gid=emb_get(emb,ch);
1140 pdfOut_printf(pdf,"%04x", gid);
1141 } else { // std 14 font with 7-bit us-ascii uses single byte encoding, TODO
1142 pdfOut_printf(pdf,"%02x",ch);
1143 }
1144
1145 len --;
1146 str ++;
1147 }
1148
1149 pdfOut_printf(pdf,"> Tj\n");
1150 }
1151 pdfOut_printf(pdf,"ET\n");
1152 }
1153 // }}}
1154
stringwidth_x(lchar_t * str)1155 static float stringwidth_x(lchar_t *str)
1156 {
1157 int len;
1158
1159 for (len=0;str[len].ch;len++);
1160
1161 return (float)len * 72.0 / (float)CharsPerInch;
1162 }
1163
write_pretty_header()1164 static void write_pretty_header() // {{{
1165 {
1166 float x,y;
1167 pdfOut_printf(pdf,"q\n"
1168 "0.9 g\n");
1169
1170 if (Duplex && (NumPages & 1) == 0) {
1171 x = PageWidth - PageRight;
1172 y = PageTop + 72.0f / LinesPerInch;
1173 } else {
1174 x = PageLeft;
1175 y = PageTop + 72.0f / LinesPerInch;
1176 }
1177
1178 pdfOut_printf(pdf,"1 0 0 1 %.3f %.3f cm\n",x,y); // translate
1179 pdfOut_printf(pdf,"0 0 %.3f %.3f re f\n",
1180 PageRight - PageLeft, 144.0f / LinesPerInch);
1181 pdfOut_printf(pdf,"0 g 0 G\n");
1182
1183 if (Duplex && (NumPages & 1) == 0) {
1184 x = PageRight - PageLeft - 36.0f / LinesPerInch - stringwidth_x(Title);
1185 y = (0.5f + 0.157f) * 72.0f / LinesPerInch;
1186 } else {
1187 x = 36.0f / LinesPerInch;
1188 y = (0.5f + 0.157f) * 72.0f / LinesPerInch;
1189 }
1190 write_font_str(x,y,ATTR_BOLD,Title,-1);
1191
1192 x = (-stringwidth_x(Date) + PageRight - PageLeft) * 0.5;
1193 write_font_str(x,y,ATTR_BOLD,Date,-1);
1194
1195 // convert pagenumber to string
1196 char tmp[20];
1197 tmp[19]=0;
1198 snprintf(tmp,19,"%d",NumPages);
1199 lchar_t *pagestr=make_wide(tmp);
1200
1201 if (Duplex && (NumPages & 1) == 0) {
1202 x = 36.0f / LinesPerInch;
1203 } else {
1204 x = PageRight - PageLeft - 36.0f / LinesPerInch - stringwidth_x(pagestr);
1205 }
1206 write_font_str(x,y,ATTR_BOLD,pagestr,-1);
1207 free(pagestr);
1208
1209 pdfOut_printf(pdf,"Q\n");
1210 }
1211 // }}}
1212
1213