/* * Text to PDF filter for the Common UNIX Printing System (CUPS). * * Copyright 2008,2012 by Tobias Hoffmann. * Copyright 2007 by Apple Inc. * Copyright 1993-2007 by Easy Software Products. * * These coded instructions, statements, and computer programs are the * property of Apple Inc. and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. * * Contents: * * main() - Main entry for text to PDF filter. * WriteEpilogue() - Write the PDF file epilogue. * WritePage() - Write a page of text. * WriteProlog() - Write the PDF file prolog with options. * write_line() - Write a row of text. * write_string() - Write a string of text. */ /* * Include necessary headers... */ #include "textcommon.h" #include "pdfutils.h" #include "fontembed/embed.h" #include #include "fontembed/sfnt.h" #include /* * Globals... */ #ifdef CUPS_1_4 /* CUPS 1.4.x or newer: only UTF8 is supported */ int UTF8 = 1; /* Use UTF-8 encoding? */ #endif /* CUPS_1_4 */ EMB_PARAMS *font_load(const char *font, int fontwidth); EMB_PARAMS *font_load(const char *font, int fontwidth) { OTF_FILE *otf; FcPattern *pattern; FcFontSet *candidates; FcChar8 *fontname = NULL; FcResult result; int i; if ( (font[0]=='/')||(font[0]=='.') ) { candidates = NULL; fontname=(FcChar8 *)strdup(font); } else { FcInit (); pattern = FcNameParse ((const FcChar8 *)font); FcPatternAddInteger (pattern, FC_SPACING, FC_MONO); // guide fc, in case substitution becomes necessary FcConfigSubstitute (0, pattern, FcMatchPattern); FcDefaultSubstitute (pattern); /* Receive a sorted list of fonts matching our pattern */ candidates = FcFontSort (0, pattern, FcFalse, 0, &result); FcPatternDestroy (pattern); if (candidates) { /* In the list of fonts returned by FcFontSort() find the first one that is both in TrueType format and monospaced */ for (i = 0; i < candidates->nfont; i++) { FcChar8 *fontformat=NULL; // TODO? or just try? int spacing=0; // sane default, as FC_MONO == 100 FcPatternGetString (candidates->fonts[i], FC_FONTFORMAT, 0, &fontformat); FcPatternGetInteger (candidates->fonts[i], FC_SPACING, 0, &spacing); if ( (fontformat)&&((spacing == FC_MONO) || (fontwidth == 2)) ) { // check for monospace or double width fonts if (strcmp((const char *)fontformat, "TrueType") == 0) { fontname = FcPatternFormat (candidates->fonts[i], (const FcChar8 *)"%{file|cescape}/%{index}"); break; } else if (strcmp((const char *)fontformat, "CFF") == 0) { fontname = FcPatternFormat (candidates->fonts[i], (const FcChar8 *)"%{file|cescape}"); // TTC only possible with non-cff glyphs! break; } } } FcFontSetDestroy (candidates); } } if (!fontname) { // TODO: try /usr/share/fonts/*/*/%s.ttf fprintf(stderr,"No viable font found\n"); return NULL; } otf = otf_load((const char *)fontname); free(fontname); if (!otf) { return NULL; } FONTFILE *ff=fontfile_open_sfnt(otf); assert(ff); EMB_PARAMS *emb=emb_new(ff, EMB_DEST_PDF16, EMB_C_FORCE_MULTIBYTE| EMB_C_TAKE_FONTFILE); assert(emb); assert(emb->plan&EMB_A_MULTIBYTE); return emb; } EMB_PARAMS *font_std(const char *name) { FONTFILE *ff=fontfile_open_std(name); assert(ff); EMB_PARAMS *emb=emb_new(ff, EMB_DEST_PDF16, EMB_C_TAKE_FONTFILE); assert(emb); return emb; } /* * Globals... */ int NumFonts; /* Number of fonts to use */ EMB_PARAMS *Fonts[256][4]; /* Fonts to use */ unsigned short Chars[256]; /* Input char to unicode */ unsigned char Codes[65536]; /* Unicode glyph mapping to font */ int Widths[256]; /* Widths of each font */ int Directions[256];/* Text directions for each font */ pdfOut *pdf; int FontResource; /* Object number of font resource dictionary */ float FontScaleX,FontScaleY; /* The font matrix */ lchar_t *Title,*Date; /* The title and date strings */ /* * Local functions... */ static void write_line(int row, lchar_t *line); static void write_string(int col, int row, int len, lchar_t *s); static lchar_t *make_wide(const char *buf); static void write_font_str(float x,float y,int fontid, lchar_t *str, int len); static void write_pretty_header(); /* * 'main()' - Main entry for text to PDF filter. */ int /* O - Exit status */ main(int argc, /* I - Number of command-line arguments */ char *argv[]) /* I - Command-line arguments */ { return (TextMain("texttopdf", argc, argv)); } /* * 'WriteEpilogue()' - Write the PDF file epilogue. */ void WriteEpilogue(void) { static char *names[] = /* Font names */ { "FN","FB","FI","FBI" }; int i,j; // embed fonts for (i = PrettyPrint ? 3 : 1; i >= 0; i --) { for (j = 0; j < NumFonts; j ++) { EMB_PARAMS *emb=Fonts[j][i]; if (emb->font->fobj) { // already embedded continue; } if ( (!emb->subset)||(bits_used(emb->subset,emb->font->sfnt->numGlyphs)) ) { emb->font->fobj=pdfOut_write_font(pdf,emb); assert(emb->font->fobj); } } } /* * Create the global fontdict */ // now fix FontResource pdf->xref[FontResource-1]=pdf->filepos; pdfOut_printf(pdf,"%d 0 obj\n" "<<\n", FontResource); for (i = PrettyPrint ? 3 : 1; i >= 0; i --) { for (j = 0; j < NumFonts; j ++) { EMB_PARAMS *emb=Fonts[j][i]; if (emb->font->fobj) { // used pdfOut_printf(pdf," /%s%02x %d 0 R\n",names[i],j,emb->font->fobj); } } } pdfOut_printf(pdf,">>\n" "endobj\n"); pdfOut_finish_pdf(pdf); pdfOut_free(pdf); } /* * {{{ 'WritePage()' - Write a page of text. */ void WritePage(void) { int line; /* Current line */ int content=pdfOut_add_xref(pdf); pdfOut_printf(pdf,"%d 0 obj\n" "<>\n" "stream\n" "q\n" ,content,content+1); long size=-(pdf->filepos-2); NumPages ++; if (PrettyPrint) write_pretty_header(pdf); for (line = 0; line < SizeLines; line ++) write_line(line, Page[line]); size+=pdf->filepos+2; pdfOut_printf(pdf,"Q\n" "endstream\n" "endobj\n"); int len_obj=pdfOut_add_xref(pdf); assert(len_obj==content+1); pdfOut_printf(pdf,"%d 0 obj\n" "%ld\n" "endobj\n", len_obj,size); int obj=pdfOut_add_xref(pdf); pdfOut_printf(pdf,"%d 0 obj\n" "<>\n" ">>\n" "endobj\n", obj,PageWidth,PageLength,content,FontResource); pdfOut_add_page(pdf,obj); memset(Page[0], 0, sizeof(lchar_t) * SizeColumns * SizeLines); } // }}} /* * {{{'WriteProlog()' - Write the PDF file prolog with options. */ void WriteProlog(const char *title, /* I - Title of job */ const char *user, /* I - Username */ const char *classification, /* I - Classification */ const char *label, /* I - Page label */ ppd_file_t *ppd) /* I - PPD file info */ { int i, j, k; /* Looping vars */ char *charset; /* Character set string */ char filename[1024]; /* Glyph filenames */ FILE *fp; /* Glyph files */ const char *datadir; /* CUPS_DATADIR environment variable */ char line[1024], /* Line from file */ *lineptr, /* Pointer into line */ *valptr; /* Pointer to value in line */ #ifndef CUPS_1_4 /* CUPS 1.4.x or newer: support for non-utf8 removed */ int ch, unicode; /* Character values */ #endif int start, end; /* Start and end values for range */ time_t curtime; /* Current time */ struct tm *curtm; /* Current date */ char curdate[255]; /* Current date (text format) */ int num_fonts=0; /* Number of unique fonts */ EMB_PARAMS *fonts[1024]; /* Unique fonts */ char *fontnames[1024]; /* Unique fonts */ #if 0 static char *names[] = /* Font names */ { "FN","FB","FI" /* "cupsNormal", "cupsBold", "cupsItalic" */ }; #endif /* * Get the data directory... */ if ((datadir = getenv("CUPS_DATADIR")) == NULL) datadir = CUPS_DATADIR; /* * Adjust margins as necessary... */ if (classification || label) { /* * Leave room for labels... */ PageBottom += 36; PageTop -= 36; } if (PageColumns > 1) { ColumnGutter = CharsPerInch / 2; ColumnWidth = (SizeColumns - ColumnGutter * (PageColumns - 1)) / PageColumns; } else ColumnWidth = SizeColumns; /* * {{{ Output the PDF header... */ assert(!pdf); pdf=pdfOut_new(); assert(pdf); pdfOut_begin_pdf(pdf); pdfOut_printf(pdf,"%%cupsRotation: %d\n", (Orientation & 3) * 90); // TODO? pdfOut_add_kv(pdf,"Creator","texttopdf/" PACKAGE_VERSION); curtime = time(NULL); curtm = localtime(&curtime); strftime(curdate, sizeof(curdate), "%c", curtm); pdfOut_add_kv(pdf,"CreationDate",pdfOut_to_pdfdate(curtm)); pdfOut_add_kv(pdf,"Title",title); pdfOut_add_kv(pdf,"Author",user); // was(PostScript): /For // }}} /* * {{{ Initialize globals... */ NumFonts = 0; memset(Fonts, 0, sizeof(Fonts)); memset(Chars, 0, sizeof(Chars)); memset(Codes, 0, sizeof(Codes)); // }}} /* * Get the output character set... */ charset = getenv("CHARSET"); if (charset != NULL && strcmp(charset, "us-ascii") != 0) // {{{ { snprintf(filename, sizeof(filename), "%s/charsets/pdf.%s", datadir, charset); if ((fp = fopen(filename, "r")) == NULL) { /* * Can't open charset file! */ fprintf(stderr, "ERROR: Unable to open %s: %s\n", filename, strerror(errno)); exit(1); } /* * Opened charset file; now see if this is really a charset file... */ if (fgets(line, sizeof(line), fp) == NULL) { /* * Bad/empty charset file! */ fclose(fp); fprintf(stderr, "ERROR: Bad charset file %s\n", filename); exit(1); } if (strncmp(line, "charset", 7) != 0) { /* * Bad format/not a charset file! */ fclose(fp); fprintf(stderr, "ERROR: Bad charset file %s\n", filename); exit(1); } /* * See if this is an 8-bit or UTF-8 character set file... */ line[strlen(line) - 1] = '\0'; /* Drop \n */ for (lineptr = line + 7; isspace(*lineptr & 255); lineptr ++); /* Skip whitespace */ #ifndef CUPS_1_4 /* CUPS 1.4.x or newer: support for non-utf8 removed */ if (strcmp(lineptr, "8bit") == 0) // {{{ { /* * 8-bit text... */ UTF8 = 0; NumFonts = 0; /* * Read the font description(s)... */ while (fgets(line, sizeof(line), fp) != NULL) { /* * Skip comment and blank lines... */ if (line[0] == '#' || line[0] == '\n') continue; /* * Read the font descriptions that should look like: * * first last direction width normal [bold italic bold-italic] */ lineptr = line; start = strtol(lineptr, &lineptr, 16); end = strtol(lineptr, &lineptr, 16); while (isspace(*lineptr & 255)) lineptr ++; if (!*lineptr) break; /* Must be a font mapping */ valptr = lineptr; while (!isspace(*lineptr & 255) && *lineptr) lineptr ++; if (!*lineptr) { /* * Can't have a font without all required values... */ fprintf(stderr, "ERROR: Bad font description line: %s\n", valptr); fclose(fp); exit(1); } *lineptr++ = '\0'; if (strcmp(valptr, "ltor") == 0) Directions[NumFonts] = 1; else if (strcmp(valptr, "rtol") == 0) Directions[NumFonts] = -1; else { fprintf(stderr, "ERROR: Bad text direction %s\n", valptr); fclose(fp); exit(1); } /* * Got the direction, now get the width... */ while (isspace(*lineptr & 255)) lineptr ++; valptr = lineptr; while (!isspace(*lineptr & 255) && *lineptr) lineptr ++; if (!*lineptr) { /* * Can't have a font without all required values... */ fprintf(stderr, "ERROR: Bad font description line: %s\n", valptr); fclose(fp); exit(1); } *lineptr++ = '\0'; if (strcmp(valptr, "single") == 0) Widths[NumFonts] = 1; else if (strcmp(valptr, "double") == 0) Widths[NumFonts] = 2; else { fprintf(stderr, "ERROR: Bad text width %s\n", valptr); fclose(fp); exit(1); } /* * Get the fonts... */ for (i = 0; *lineptr && i < 4; i ++) { while (isspace(*lineptr & 255)) lineptr ++; valptr = lineptr; while (!isspace(*lineptr & 255) && *lineptr) lineptr ++; if (*lineptr) *lineptr++ = '\0'; if (lineptr > valptr) { // search for duplicates for (k = 0; k < num_fonts; k ++) if (strcmp(valptr, fontnames[k]) == 0) { Fonts[NumFonts][i] = fonts[k]; break; } if (k==num_fonts) { // not found fonts[num_fonts] = Fonts[NumFonts][i] = font_load(valptr, Widths[NumFonts]); if (!fonts[num_fonts]) { // font missing/corrupt, replace by first fprintf(stderr,"WARNING: Ignored bad font \"%s\"\n",valptr); break; } fontnames[num_fonts++] = strdup(valptr); } } } /* ignore complete range, when the first font is not available */ if (i==0) { continue; } /* * Fill in remaining fonts as needed... */ for (j = i; j < 4; j ++) Fonts[NumFonts][j] = Fonts[NumFonts][0]; /* * Define the character mappings... */ for (i = start; i <= end; i ++) Codes[i] = NumFonts; NumFonts ++; } /* * Read encoding lines... */ do { /* * Skip comment and blank lines... */ if (line[0] == '#' || line[0] == '\n') continue; /* * Grab the character and unicode glyph number. */ if (sscanf(line, "%x%x", &ch, &unicode) == 2 && ch < 256) Chars[ch] = unicode; } while (fgets(line, sizeof(line), fp) != NULL); fclose(fp); } else // }}} #endif if (strcmp(lineptr, "utf8") == 0) { // {{{ /* * UTF-8 (Unicode) text... */ UTF8 = 1; /* * Read the font descriptions... */ NumFonts = 0; while (fgets(line, sizeof(line), fp) != NULL) { /* * Skip comment and blank lines... */ if (line[0] == '#' || line[0] == '\n') continue; /* * Read the font descriptions that should look like: * * start end direction width normal [bold italic bold-italic] */ lineptr = line; start = strtol(lineptr, &lineptr, 16); end = strtol(lineptr, &lineptr, 16); while (isspace(*lineptr & 255)) lineptr ++; valptr = lineptr; while (!isspace(*lineptr & 255) && *lineptr) lineptr ++; if (!*lineptr) { /* * Can't have a font without all required values... */ fprintf(stderr, "ERROR: Bad font description line: %s\n", valptr); fclose(fp); exit(1); } *lineptr++ = '\0'; if (strcmp(valptr, "ltor") == 0) Directions[NumFonts] = 1; else if (strcmp(valptr, "rtol") == 0) Directions[NumFonts] = -1; else { fprintf(stderr, "ERROR: Bad text direction %s\n", valptr); fclose(fp); exit(1); } /* * Got the direction, now get the width... */ while (isspace(*lineptr & 255)) lineptr ++; valptr = lineptr; while (!isspace(*lineptr & 255) && *lineptr) lineptr ++; if (!*lineptr) { /* * Can't have a font without all required values... */ fprintf(stderr, "ERROR: Bad font description line: %s\n", valptr); fclose(fp); exit(1); } *lineptr++ = '\0'; if (strcmp(valptr, "single") == 0) Widths[NumFonts] = 1; else if (strcmp(valptr, "double") == 0) Widths[NumFonts] = 2; else { fprintf(stderr, "ERROR: Bad text width %s\n", valptr); fclose(fp); exit(1); } /* * Get the fonts... */ for (i = 0; *lineptr && i < 4; i ++) { while (isspace(*lineptr & 255)) lineptr ++; valptr = lineptr; while (!isspace(*lineptr & 255) && *lineptr) lineptr ++; if (*lineptr) *lineptr++ = '\0'; if (lineptr > valptr) { // search for duplicates for (k = 0; k < num_fonts; k ++) if (strcmp(valptr, fontnames[k]) == 0) { Fonts[NumFonts][i] = fonts[k]; break; } if (k==num_fonts) { // not found fonts[num_fonts] = Fonts[NumFonts][i] = font_load(valptr, Widths[NumFonts]); if (!fonts[num_fonts]) { // font missing/corrupt, replace by first fprintf(stderr,"WARNING: Ignored bad font \"%s\"\n",valptr); break; } fontnames[num_fonts++] = strdup(valptr); } } } /* ignore complete range, when the first font is not available */ if (i==0) { continue; } /* * Fill in remaining fonts as needed... */ for (j = i; j < 4; j ++) Fonts[NumFonts][j] = Fonts[NumFonts][0]; /* * Define the character mappings... */ for (i = start; i <= end; i ++) { Codes[i] = NumFonts; } /* * Move to the next font, stopping if needed... */ NumFonts ++; if (NumFonts >= 256) break; } fclose(fp); } // }}} else // {{{ { fprintf(stderr, "ERROR: Bad charset type %s\n", lineptr); fclose(fp); exit(1); } // }}} } // }}} else // {{{ Standard ASCII { /* * Standard ASCII output just uses Courier, Courier-Bold, and * possibly Courier-Oblique. */ NumFonts = 1; Fonts[0][ATTR_NORMAL] = font_std("Courier"); Fonts[0][ATTR_BOLD] = font_std("Courier-Bold"); Fonts[0][ATTR_ITALIC] = font_std("Courier-Oblique"); Fonts[0][ATTR_BOLDITALIC] = font_std("Courier-BoldOblique"); Widths[0] = 1; Directions[0] = 1; /* * Define US-ASCII characters... */ for (i = 32; i < 127; i ++) { Chars[i] = i; Codes[i] = NumFonts-1; } } // }}} if (NumFonts==0) { fprintf(stderr, "ERROR: No usable font available\n"); exit(1); } FontScaleX=120.0 / CharsPerInch; FontScaleY=68.0 / LinesPerInch; // allocate now, for pages to use. will be fixed in epilogue FontResource=pdfOut_add_xref(pdf); if (PrettyPrint) { Date=make_wide(curdate); Title=make_wide(title); } } // }}} /* * {{{ 'write_line()' - Write a row of text. */ static void write_line(int row, /* I - Row number (0 to N) */ lchar_t *line) /* I - Line to print */ { int i; /* Looping var */ int col,xcol,xwid; /* Current column */ int attr; /* Current attribute */ int font, /* Font to use */ lastfont, /* Last font */ mono; /* Monospaced? */ lchar_t *start; /* First character in sequence */ xcol=0; for (col = 0, start = line; col < SizeColumns;) { while (col < SizeColumns && (line->ch == ' ' || line->ch == 0)) { col ++; xcol ++; line ++; } if (col >= SizeColumns) break; if (NumFonts == 1) { /* * All characters in a single font - assume monospaced and single width... */ attr = line->attr; start = line; while (col < SizeColumns && line->ch != 0 && attr == line->attr) { col ++; line ++; } write_string(col - (line - start), row, line - start, start); } else { /* * Multiple fonts; break up based on the font... */ attr = line->attr; start = line; xwid = 0; if (UTF8) { lastfont = Codes[line->ch]; } else { lastfont = Codes[Chars[line->ch]]; } // mono = strncmp(Fonts[lastfont][0], "Courier", 7) == 0; mono=1; // TODO col ++; xwid += Widths[lastfont]; line ++; if (mono) { while (col < SizeColumns && line->ch != 0 && attr == line->attr) { if (UTF8) { font = Codes[line->ch]; } else { font = Codes[Chars[line->ch]]; } if (/*strncmp(Fonts[font][0], "Courier", 7) != 0 ||*/ // TODO font != lastfont) break; col ++; xwid += Widths[lastfont]; line ++; } } if (Directions[lastfont] > 0) { write_string(xcol, row, line - start, start); xcol += xwid; } else { /* * Do right-to-left text... ; assume no font change without direction change */ while (col < SizeColumns && line->ch != 0 && attr == line->attr) { if (UTF8) { font = Codes[line->ch]; } else { font = Codes[Chars[line->ch]]; } if (Directions[font] > 0 && !ispunct(line->ch & 255) && !isspace(line->ch & 255)) break; col ++; xwid += Widths[lastfont]; line ++; } for (i = 1; start < line; i ++, start ++) if (!isspace(start->ch & 255)) { xwid-=Widths[lastfont]; write_string(xcol + xwid, row, 1, start); } else { xwid--; } } } } } // }}} static lchar_t *make_wide(const char *buf) // {{{ - convert to lchar_t { const unsigned char *utf8; /* UTF8 text */ lchar_t *ret,*out; // this is enough, utf8 chars will only require less space out=ret=malloc((strlen(buf)+1)*sizeof(lchar_t)); utf8 = (const unsigned char *)buf; while (*utf8) { out->attr=0; if (*utf8 < 0xc0 || !UTF8) out->ch = *utf8 ++; else if ((*utf8 & 0xe0) == 0xc0) { /* * Two byte character... */ out->ch = ((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f); utf8 += 2; } else { /* * Three byte character... */ out->ch = ((((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f)) << 6) | (utf8[2] & 0x3f); utf8 += 3; } out++; } out->ch=out->attr=0; return ret; } // }}} /* * {{{ 'write_string()' - Write a string of text. */ static void write_string(int col, /* I - Start column */ int row, /* I - Row */ int len, /* I - Number of characters */ lchar_t *s) /* I - String to print */ { float x, y; /* Position of text */ unsigned attr; /* Character attributes */ /* * Position the text and set the font... */ if (Duplex && (NumPages & 1) == 0) { x = PageWidth - PageRight; y = PageTop; } else { x = PageLeft; y = PageTop; } x += (float)col * 72.0f / (float)CharsPerInch; y -= (float)(row + 0.843) * 72.0f / (float)LinesPerInch; attr = s->attr; if (attr & ATTR_RAISED) y += 36.0 / (float)LinesPerInch; else if (attr & ATTR_LOWERED) y -= 36.0 / (float)LinesPerInch; if (attr & ATTR_UNDERLINE) pdfOut_printf(pdf,"q 0.5 w 0 g %.3f %.3f m %.3f %.3f l S Q ", x, y - 6.8 / LinesPerInch, x + (float)len * 72.0 / (float)CharsPerInch, y - 6.8 / LinesPerInch); if (PrettyPrint) { if (ColorDevice) { if (attr & ATTR_RED) pdfOut_printf(pdf,"0.5 0 0 rg\n"); else if (attr & ATTR_GREEN) pdfOut_printf(pdf,"0 0.5 0 rg\n"); else if (attr & ATTR_BLUE) pdfOut_printf(pdf,"0 0 0.5 rg\n"); else pdfOut_printf(pdf,"0 g\n"); } else { if ( (attr & ATTR_RED)||(attr & ATTR_GREEN)||(attr & ATTR_BLUE) ) pdfOut_printf(pdf,"0.2 g\n"); else pdfOut_printf(pdf,"0 g\n"); } } else pdfOut_printf(pdf,"0 g\n"); write_font_str(x,y,attr & ATTR_FONT,s,len); } // }}} // {{{ show >len characters from >str, using the right font(s) at >x,>y static void write_font_str(float x,float y,int fontid, lchar_t *str, int len) { unsigned short ch; /* Current character */ static char *names[] = /* Font names */ { "FN","FB","FI","FBI" }; if (len==-1) { for (len=0;str[len].ch;len++); } pdfOut_printf(pdf,"BT\n"); if (x == (int)x) pdfOut_printf(pdf," %.0f ", x); else pdfOut_printf(pdf," %.3f ", x); if (y == (int)y) pdfOut_printf(pdf,"%.0f Td\n", y); else pdfOut_printf(pdf,"%.3f Td\n", y); int lastfont,font; // split on font boundary while (len > 0) { /* * Write a hex string... */ if (UTF8) { lastfont=Codes[str->ch]; } else { lastfont=Codes[Chars[str->ch]]; } EMB_PARAMS *emb=Fonts[lastfont][fontid]; OTF_FILE *otf=emb->font->sfnt; if (otf) { // TODO? pdfOut_printf(pdf," %.3f Tz\n", FontScaleX*600.0/(otf_get_width(otf,4)*1000.0/otf->unitsPerEm)*100.0/FontScaleY); // TODO? // 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. } else { pdfOut_printf(pdf," %.3f Tz\n", FontScaleX*100.0/FontScaleY); // TODO? } pdfOut_printf(pdf," /%s%02x %.3f Tf <", names[fontid],lastfont,FontScaleY); while (len > 0) { if (UTF8) { ch=str->ch; } else { ch=Chars[str->ch]; } font = Codes[ch]; if (lastfont != font) { // only possible, when not used via write_string (e.g. utf-8filename.txt in prettyprint) break; } if (otf) { // TODO const unsigned short gid=emb_get(emb,ch); pdfOut_printf(pdf,"%04x", gid); } else { // std 14 font with 7-bit us-ascii uses single byte encoding, TODO pdfOut_printf(pdf,"%02x",ch); } len --; str ++; } pdfOut_printf(pdf,"> Tj\n"); } pdfOut_printf(pdf,"ET\n"); } // }}} static float stringwidth_x(lchar_t *str) { int len; for (len=0;str[len].ch;len++); return (float)len * 72.0 / (float)CharsPerInch; } static void write_pretty_header() // {{{ { float x,y; pdfOut_printf(pdf,"q\n" "0.9 g\n"); if (Duplex && (NumPages & 1) == 0) { x = PageWidth - PageRight; y = PageTop + 72.0f / LinesPerInch; } else { x = PageLeft; y = PageTop + 72.0f / LinesPerInch; } pdfOut_printf(pdf,"1 0 0 1 %.3f %.3f cm\n",x,y); // translate pdfOut_printf(pdf,"0 0 %.3f %.3f re f\n", PageRight - PageLeft, 144.0f / LinesPerInch); pdfOut_printf(pdf,"0 g 0 G\n"); if (Duplex && (NumPages & 1) == 0) { x = PageRight - PageLeft - 36.0f / LinesPerInch - stringwidth_x(Title); y = (0.5f + 0.157f) * 72.0f / LinesPerInch; } else { x = 36.0f / LinesPerInch; y = (0.5f + 0.157f) * 72.0f / LinesPerInch; } write_font_str(x,y,ATTR_BOLD,Title,-1); x = (-stringwidth_x(Date) + PageRight - PageLeft) * 0.5; write_font_str(x,y,ATTR_BOLD,Date,-1); // convert pagenumber to string char tmp[20]; tmp[19]=0; snprintf(tmp,19,"%d",NumPages); lchar_t *pagestr=make_wide(tmp); if (Duplex && (NumPages & 1) == 0) { x = 36.0f / LinesPerInch; } else { x = PageRight - PageLeft - 36.0f / LinesPerInch - stringwidth_x(pagestr); } write_font_str(x,y,ATTR_BOLD,pagestr,-1); free(pagestr); pdfOut_printf(pdf,"Q\n"); } // }}}