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