2
* Text to PDF filter for the Common UNIX Printing System (CUPS).
4
* Copyright 2008,2012 by Tobias Hoffmann.
5
* Copyright 2007 by Apple Inc.
6
* Copyright 1993-2007 by Easy Software Products.
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 "LICENSE.txt"
11
* which should have been included with this file. If this file is
12
* file is missing or damaged, see the license at "http://www.cups.org/".
14
* This file is subject to the Apple OS-Developed Software exception.
18
* main() - Main entry for text to PDF filter.
19
* WriteEpilogue() - Write the PDF file epilogue.
20
* WritePage() - Write a page of text.
21
* WriteProlog() - Write the PDF file prolog with options.
22
* write_line() - Write a row of text.
23
* write_string() - Write a string of text.
27
* Include necessary headers...
30
#include "textcommon.h"
32
#include "fontembed/embed.h"
34
#include "fontembed/sfnt.h"
35
#include <fontconfig/fontconfig.h>
41
#ifdef CUPS_1_4 /* CUPS 1.4.x or newer: only UTF8 is supported */
42
int UTF8 = 1; /* Use UTF-8 encoding? */
45
EMB_PARAMS *font_load(const char *font);
47
EMB_PARAMS *font_load(const char *font)
52
FcFontSet *candidates;
53
FcChar8 *fontname = NULL;
56
if ( (font[0]=='/')||(font[0]=='.') ) {
58
fontname=(FcChar8 *)strdup(font);
61
pattern = FcNameParse ((const FcChar8 *)font);
62
FcPatternAddInteger (pattern, FC_SPACING, FC_MONO); // guide fc, in case substitution becomes necessary
63
FcConfigSubstitute (0, pattern, FcMatchPattern);
64
FcDefaultSubstitute (pattern);
66
/* Receive a sorted list of fonts matching our pattern */
67
candidates = FcFontSort (0, pattern, FcTrue, 0, 0);
68
FcPatternDestroy (pattern);
70
/* In the list of fonts returned by FcFontSort()
71
find the first one that is both in TrueType format and monospaced */
72
for (i = 0; i < candidates->nfont; i++) {
73
FcChar8 *fontformat=NULL; // TODO? or just try?
74
int spacing=0; // sane default, as FC_MONO == 100
75
FcPatternGetString (candidates->fonts[i], FC_FONTFORMAT, 0, &fontformat);
76
FcPatternGetInteger (candidates->fonts[i], FC_SPACING, 0, &spacing);
78
if ( (fontformat)&&(spacing == FC_MONO) ) {
79
if (strcmp((const char *)fontformat, "TrueType") == 0) {
80
fontname = FcPatternFormat (candidates->fonts[i], (const FcChar8 *)"%{file|cescape}/%{index}");
82
} else if (strcmp((const char *)fontformat, "CFF") == 0) {
83
fontname = FcPatternFormat (candidates->fonts[i], (const FcChar8 *)"%{file|cescape}"); // TTC only possible with non-cff glyphs!
88
FcFontSetDestroy (candidates);
92
// TODO: try /usr/share/fonts/*/*/%s.ttf
93
fprintf(stderr,"No viable font found\n");
97
otf = otf_load((const char *)fontname);
103
FONTFILE *ff=fontfile_open_sfnt(otf);
105
EMB_PARAMS *emb=emb_new(ff,
107
EMB_C_FORCE_MULTIBYTE|
108
EMB_C_TAKE_FONTFILE);
110
assert(emb->plan&EMB_A_MULTIBYTE);
114
EMB_PARAMS *font_std(const char *name)
116
FONTFILE *ff=fontfile_open_std(name);
118
EMB_PARAMS *emb=emb_new(ff,
120
EMB_C_TAKE_FONTFILE);
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 */
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 */
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();
152
* 'main()' - Main entry for text to PDF filter.
155
int /* O - Exit status */
156
main(int argc, /* I - Number of command-line arguments */
157
char *argv[]) /* I - Command-line arguments */
159
return (TextMain("texttopdf", argc, argv));
164
* 'WriteEpilogue()' - Write the PDF file epilogue.
170
static char *names[] = /* Font names */
178
for (i = PrettyPrint ? 2 : 1; i >= 0; i --) {
179
for (j = 0; j < NumFonts; j ++)
181
EMB_PARAMS *emb=Fonts[j][i];
182
if (emb->font->fobj) { // already embedded
185
if ( (!emb->subset)||(bits_used(emb->subset,emb->font->sfnt->numGlyphs)) ) {
186
emb->font->fobj=pdfOut_write_font(pdf,emb);
187
assert(emb->font->fobj);
193
* Create the global fontdict
196
// now fix FontResource
197
pdf->xref[FontResource-1]=pdf->filepos;
198
pdfOut_printf(pdf,"%d 0 obj\n"
202
for (i = PrettyPrint ? 2 : 1; i >= 0; i --) {
203
for (j = 0; j < NumFonts; j ++) {
204
EMB_PARAMS *emb=Fonts[j][i];
205
if (emb->font->fobj) { // used
206
pdfOut_printf(pdf," /%s%02x %d 0 R\n",names[i],j,emb->font->fobj);
211
pdfOut_printf(pdf,">>\n"
214
pdfOut_finish_pdf(pdf);
220
* {{{ 'WritePage()' - Write a page of text.
226
int line; /* Current line */
228
int content=pdfOut_add_xref(pdf);
229
pdfOut_printf(pdf,"%d 0 obj\n"
235
long size=-(pdf->filepos-2);
239
write_pretty_header(pdf);
241
for (line = 0; line < SizeLines; line ++)
242
write_line(line, Page[line]);
244
size+=pdf->filepos+2;
245
pdfOut_printf(pdf,"Q\n"
249
int len_obj=pdfOut_add_xref(pdf);
250
assert(len_obj==content+1);
251
pdfOut_printf(pdf,"%d 0 obj\n"
256
int obj=pdfOut_add_xref(pdf);
257
pdfOut_printf(pdf,"%d 0 obj\n"
260
" /MediaBox [0 0 %.0f %.0f]\n"
261
" /Contents %d 0 R\n"
262
" /Resources << /Font %d 0 R >>\n"
265
obj,PageWidth,PageLength,content,FontResource);
266
pdfOut_add_page(pdf,obj);
268
memset(Page[0], 0, sizeof(lchar_t) * SizeColumns * SizeLines);
273
* {{{'WriteProlog()' - Write the PDF file prolog with options.
277
WriteProlog(const char *title, /* I - Title of job */
278
const char *user, /* I - Username */
279
const char *classification, /* I - Classification */
280
const char *label, /* I - Page label */
281
ppd_file_t *ppd) /* I - PPD file info */
283
int i, j, k; /* Looping vars */
284
char *charset; /* Character set string */
285
char filename[1024]; /* Glyph filenames */
286
FILE *fp; /* Glyph files */
287
const char *datadir; /* CUPS_DATADIR environment variable */
288
char line[1024], /* Line from file */
289
*lineptr, /* Pointer into line */
290
*valptr; /* Pointer to value in line */
291
#ifndef CUPS_1_4 /* CUPS 1.4.x or newer: support for non-utf8 removed */
292
int ch, unicode; /* Character values */
294
int start, end; /* Start and end values for range */
295
time_t curtime; /* Current time */
296
struct tm *curtm; /* Current date */
297
char curdate[255]; /* Current date (text format) */
298
int num_fonts=0; /* Number of unique fonts */
299
EMB_PARAMS *fonts[1024]; /* Unique fonts */
300
char *fontnames[1024]; /* Unique fonts */
302
static char *names[] = /* Font names */
315
* Get the data directory...
318
if ((datadir = getenv("CUPS_DATADIR")) == NULL)
319
datadir = CUPS_DATADIR;
322
* Adjust margins as necessary...
325
if (classification || label)
328
* Leave room for labels...
336
* Allocate memory for the page...
339
SizeColumns = (PageRight - PageLeft) / 72.0 * CharsPerInch;
340
SizeLines = (PageTop - PageBottom) / 72.0 * LinesPerInch;
342
Page = calloc(sizeof(lchar_t *), SizeLines);
343
Page[0] = calloc(sizeof(lchar_t), SizeColumns * SizeLines);
344
for (i = 1; i < SizeLines; i ++)
345
Page[i] = Page[0] + i * SizeColumns;
349
ColumnGutter = CharsPerInch / 2;
350
ColumnWidth = (SizeColumns - ColumnGutter * (PageColumns - 1)) /
354
ColumnWidth = SizeColumns;
357
* {{{ Output the PDF header...
364
pdfOut_begin_pdf(pdf);
365
pdfOut_printf(pdf,"%%cupsRotation: %d\n", (Orientation & 3) * 90); // TODO?
367
pdfOut_add_kv(pdf,"Creator","texttopdf/" CUPSFILTERS_SVERSION);
369
curtime = time(NULL);
370
curtm = localtime(&curtime);
371
strftime(curdate, sizeof(curdate), "%c", curtm);
373
pdfOut_add_kv(pdf,"CreationDate",pdfOut_to_pdfdate(curtm));
374
pdfOut_add_kv(pdf,"Title",title);
375
pdfOut_add_kv(pdf,"Author",user); // was(PostScript): /For
379
* {{{ Initialize globals...
383
memset(Fonts, 0, sizeof(Fonts));
384
memset(Chars, 0, sizeof(Chars));
385
memset(Codes, 0, sizeof(Codes));
389
* Get the output character set...
392
charset = getenv("CHARSET");
393
if (charset != NULL && strcmp(charset, "us-ascii") != 0) // {{{
395
snprintf(filename, sizeof(filename), "%s/charsets/pdf.%s", datadir, charset);
397
if ((fp = fopen(filename, "r")) == NULL)
400
* Can't open charset file!
403
fprintf(stderr, "ERROR: Unable to open %s: %s\n", filename,
409
* Opened charset file; now see if this is really a charset file...
412
if (fgets(line, sizeof(line), fp) == NULL)
415
* Bad/empty charset file!
419
fprintf(stderr, "ERROR: Bad charset file %s\n", filename);
423
if (strncmp(line, "charset", 7) != 0)
426
* Bad format/not a charset file!
430
fprintf(stderr, "ERROR: Bad charset file %s\n", filename);
435
* See if this is an 8-bit or UTF-8 character set file...
438
line[strlen(line) - 1] = '\0'; /* Drop \n */
439
for (lineptr = line + 7; isspace(*lineptr & 255); lineptr ++); /* Skip whitespace */
441
#ifndef CUPS_1_4 /* CUPS 1.4.x or newer: support for non-utf8 removed */
442
if (strcmp(lineptr, "8bit") == 0) // {{{
452
* Read the font description(s)...
455
while (fgets(line, sizeof(line), fp) != NULL)
458
* Skip comment and blank lines...
461
if (line[0] == '#' || line[0] == '\n')
465
* Read the font descriptions that should look like:
467
* first last direction width normal [bold italic bold-italic]
472
start = strtol(lineptr, &lineptr, 16);
473
end = strtol(lineptr, &lineptr, 16);
475
while (isspace(*lineptr & 255))
479
break; /* Must be a font mapping */
483
while (!isspace(*lineptr & 255) && *lineptr)
489
* Can't have a font without all required values...
492
fprintf(stderr, "ERROR: Bad font description line: %s\n", valptr);
499
if (strcmp(valptr, "ltor") == 0)
500
Directions[NumFonts] = 1;
501
else if (strcmp(valptr, "rtol") == 0)
502
Directions[NumFonts] = -1;
505
fprintf(stderr, "ERROR: Bad text direction %s\n", valptr);
511
* Got the direction, now get the width...
514
while (isspace(*lineptr & 255))
519
while (!isspace(*lineptr & 255) && *lineptr)
525
* Can't have a font without all required values...
528
fprintf(stderr, "ERROR: Bad font description line: %s\n", valptr);
535
if (strcmp(valptr, "single") == 0)
536
Widths[NumFonts] = 1;
537
else if (strcmp(valptr, "double") == 0)
538
Widths[NumFonts] = 2;
541
fprintf(stderr, "ERROR: Bad text width %s\n", valptr);
550
for (i = 0; *lineptr && i < 4; i ++)
552
while (isspace(*lineptr & 255))
557
while (!isspace(*lineptr & 255) && *lineptr)
563
if (lineptr > valptr) {
564
// search for duplicates
565
for (k = 0; k < num_fonts; k ++)
566
if (strcmp(valptr, fontnames[k]) == 0) {
567
Fonts[NumFonts][i] = fonts[k];
571
if (k==num_fonts) { // not found
572
fonts[num_fonts] = Fonts[NumFonts][i] = font_load(valptr);
573
if (!fonts[num_fonts]) { // font missing/corrupt, replace by first
574
fprintf(stderr,"WARNING: Ignored bad font \"%s\"\n",valptr);
577
fontnames[num_fonts++] = strdup(valptr);
582
/* ignore complete range, when the first font is not available */
588
* Fill in remaining fonts as needed...
591
for (j = i; j < 4; j ++)
592
Fonts[NumFonts][j] = Fonts[NumFonts][0];
595
* Define the character mappings...
598
for (i = start; i <= end; i ++)
605
* Read encoding lines...
611
* Skip comment and blank lines...
614
if (line[0] == '#' || line[0] == '\n')
618
* Grab the character and unicode glyph number.
621
if (sscanf(line, "%x%x", &ch, &unicode) == 2 && ch < 256)
624
while (fgets(line, sizeof(line), fp) != NULL);
629
if (strcmp(lineptr, "utf8") == 0) { // {{{
631
* UTF-8 (Unicode) text...
637
* Read the font descriptions...
642
while (fgets(line, sizeof(line), fp) != NULL)
645
* Skip comment and blank lines...
648
if (line[0] == '#' || line[0] == '\n')
652
* Read the font descriptions that should look like:
654
* start end direction width normal [bold italic bold-italic]
659
start = strtol(lineptr, &lineptr, 16);
660
end = strtol(lineptr, &lineptr, 16);
662
while (isspace(*lineptr & 255))
667
while (!isspace(*lineptr & 255) && *lineptr)
673
* Can't have a font without all required values...
676
fprintf(stderr, "ERROR: Bad font description line: %s\n", valptr);
683
if (strcmp(valptr, "ltor") == 0)
684
Directions[NumFonts] = 1;
685
else if (strcmp(valptr, "rtol") == 0)
686
Directions[NumFonts] = -1;
689
fprintf(stderr, "ERROR: Bad text direction %s\n", valptr);
695
* Got the direction, now get the width...
698
while (isspace(*lineptr & 255))
703
while (!isspace(*lineptr & 255) && *lineptr)
709
* Can't have a font without all required values...
712
fprintf(stderr, "ERROR: Bad font description line: %s\n", valptr);
719
if (strcmp(valptr, "single") == 0)
720
Widths[NumFonts] = 1;
721
else if (strcmp(valptr, "double") == 0)
722
Widths[NumFonts] = 2;
725
fprintf(stderr, "ERROR: Bad text width %s\n", valptr);
734
for (i = 0; *lineptr && i < 4; i ++)
736
while (isspace(*lineptr & 255))
741
while (!isspace(*lineptr & 255) && *lineptr)
747
if (lineptr > valptr) {
748
// search for duplicates
749
for (k = 0; k < num_fonts; k ++)
750
if (strcmp(valptr, fontnames[k]) == 0) {
751
Fonts[NumFonts][i] = fonts[k];
755
if (k==num_fonts) { // not found
756
fonts[num_fonts] = Fonts[NumFonts][i] = font_load(valptr);
757
if (!fonts[num_fonts]) { // font missing/corrupt, replace by first
758
fprintf(stderr,"WARNING: Ignored bad font \"%s\"\n",valptr);
761
fontnames[num_fonts++] = strdup(valptr);
766
/* ignore complete range, when the first font is not available */
772
* Fill in remaining fonts as needed...
775
for (j = i; j < 4; j ++)
776
Fonts[NumFonts][j] = Fonts[NumFonts][0];
779
* Define the character mappings...
782
for (i = start; i <= end; i ++)
788
* Move to the next font, stopping if needed...
800
fprintf(stderr, "ERROR: Bad charset type %s\n", lineptr);
805
else // {{{ Standard ASCII
808
* Standard ASCII output just uses Courier, Courier-Bold, and
809
* possibly Courier-Oblique.
814
Fonts[0][ATTR_NORMAL] = font_std("Courier");
815
Fonts[0][ATTR_BOLD] = font_std("Courier-Bold");
816
Fonts[0][ATTR_ITALIC] = font_std("Courier-Oblique");
817
Fonts[0][ATTR_BOLDITALIC] = font_std("Courier-BoldOblique");
823
* Define US-ASCII characters...
826
for (i = 32; i < 127; i ++)
829
Codes[i] = NumFonts-1;
835
fprintf(stderr, "ERROR: No usable font available\n");
839
FontScaleX=120.0 / CharsPerInch;
840
FontScaleY=68.0 / LinesPerInch;
842
// allocate now, for pages to use. will be fixed in epilogue
843
FontResource=pdfOut_add_xref(pdf);
847
Date=make_wide(curdate);
848
Title=make_wide(title);
854
* {{{ 'write_line()' - Write a row of text.
858
write_line(int row, /* I - Row number (0 to N) */
859
lchar_t *line) /* I - Line to print */
861
int i; /* Looping var */
862
int col,xcol,xwid; /* 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 */
871
for (col = 0, start = line; col < SizeColumns;)
873
while (col < SizeColumns && (line->ch == ' ' || line->ch == 0))
880
if (col >= SizeColumns)
886
* All characters in a single font - assume monospaced and single width...
892
while (col < SizeColumns && line->ch != 0 && attr == line->attr)
898
write_string(col - (line - start), row, line - start, start);
903
* Multiple fonts; break up based on the font...
910
lastfont = Codes[line->ch];
912
lastfont = Codes[Chars[line->ch]];
914
// mono = strncmp(Fonts[lastfont][0], "Courier", 7) == 0;
918
xwid += Widths[lastfont];
923
while (col < SizeColumns && line->ch != 0 && attr == line->attr)
926
font = Codes[line->ch];
928
font = Codes[Chars[line->ch]];
930
if (/*strncmp(Fonts[font][0], "Courier", 7) != 0 ||*/ // TODO
935
xwid += Widths[lastfont];
940
if (Directions[lastfont] > 0) {
941
write_string(xcol, row, line - start, start);
947
* Do right-to-left text... ; assume no font change without direction change
950
while (col < SizeColumns && line->ch != 0 && attr == line->attr)
953
font = Codes[line->ch];
955
font = Codes[Chars[line->ch]];
957
if (Directions[font] > 0 &&
958
!ispunct(line->ch & 255) && !isspace(line->ch & 255))
962
xwid += Widths[lastfont];
966
for (i = 1; start < line; i ++, start ++)
967
if (!isspace(start->ch & 255)) {
968
xwid-=Widths[lastfont];
969
write_string(xcol + xwid, row, 1, start);
979
static lchar_t *make_wide(const char *buf) // {{{ - convert to lchar_t
981
const unsigned char *utf8; /* UTF8 text */
984
// this is enough, utf8 chars will only require less space
985
out=ret=malloc((strlen(buf)+1)*sizeof(lchar_t));
987
utf8 = (const unsigned char *)buf;
992
if (*utf8 < 0xc0 || !UTF8)
994
else if ((*utf8 & 0xe0) == 0xc0)
997
* Two byte character...
1000
out->ch = ((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f);
1006
* Three byte character...
1009
out->ch = ((((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f)) << 6) |
1016
out->ch=out->attr=0;
1022
* {{{ 'write_string()' - Write a string of text.
1026
write_string(int col, /* I - Start column */
1027
int row, /* I - Row */
1028
int len, /* I - Number of characters */
1029
lchar_t *s) /* I - String to print */
1031
float x, y; /* Position of text */
1032
unsigned attr; /* Character attributes */
1036
* Position the text and set the font...
1039
if (Duplex && (NumPages & 1) == 0)
1041
x = PageWidth - PageRight;
1050
x += (float)col * 72.0f / (float)CharsPerInch;
1051
y -= (float)(row + 0.843) * 72.0f / (float)LinesPerInch;
1055
if (attr & ATTR_RAISED)
1056
y += 36.0 / (float)LinesPerInch;
1057
else if (attr & ATTR_LOWERED)
1058
y -= 36.0 / (float)LinesPerInch;
1060
if (attr & ATTR_UNDERLINE)
1061
pdfOut_printf(pdf,"q 0.5 w 0 g %.3f %.3f m %.3f %.3f l S Q ",
1062
x, y - 6.8 / LinesPerInch,
1063
x + (float)len * 72.0 / (float)CharsPerInch,
1064
y - 6.8 / LinesPerInch);
1069
if (attr & ATTR_RED)
1070
pdfOut_printf(pdf,"0.5 0 0 rg\n");
1071
else if (attr & ATTR_GREEN)
1072
pdfOut_printf(pdf,"0 0.5 0 rg\n");
1073
else if (attr & ATTR_BLUE)
1074
pdfOut_printf(pdf,"0 0 0.5 rg\n");
1076
pdfOut_printf(pdf,"0 g\n");
1078
if ( (attr & ATTR_RED)||(attr & ATTR_GREEN)||(attr & ATTR_BLUE) )
1079
pdfOut_printf(pdf,"0.2 g\n");
1081
pdfOut_printf(pdf,"0 g\n");
1085
pdfOut_printf(pdf,"0 g\n");
1087
write_font_str(x,y,attr & ATTR_FONT,s,len);
1091
// {{{ show >len characters from >str, using the right font(s) at >x,>y
1092
static void write_font_str(float x,float y,int fontid, lchar_t *str, int len)
1094
unsigned short ch; /* Current character */
1095
static char *names[] = /* Font names */
1099
for (len=0;str[len].ch;len++);
1101
pdfOut_printf(pdf,"BT\n");
1104
pdfOut_printf(pdf," %.0f ", x);
1106
pdfOut_printf(pdf," %.3f ", x);
1109
pdfOut_printf(pdf,"%.0f Td\n", y);
1111
pdfOut_printf(pdf,"%.3f Td\n", y);
1115
// split on font boundary
1119
* Write a hex string...
1122
lastfont=Codes[str->ch];
1124
lastfont=Codes[Chars[str->ch]];
1126
EMB_PARAMS *emb=Fonts[lastfont][fontid];
1127
OTF_FILE *otf=emb->font->sfnt;
1130
pdfOut_printf(pdf," %.3f Tz\n",
1131
FontScaleX*600.0/(otf_get_width(otf,4)*1000.0/otf->unitsPerEm)*100.0/FontScaleY); // TODO?
1132
// 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.
1134
pdfOut_printf(pdf," %.3f Tz\n",
1135
FontScaleX*100.0/FontScaleY); // TODO?
1138
pdfOut_printf(pdf," /%s%02x %.3f Tf <",
1139
names[fontid],lastfont,FontScaleY);
1150
if (lastfont != font) {
1151
assert(0); // should never happen; TODO
1155
const unsigned short gid=emb_get(emb,ch);
1156
pdfOut_printf(pdf,"%04x", gid);
1157
} else { // std 14 font with 7-bit us-ascii uses single byte encoding, TODO
1158
pdfOut_printf(pdf,"%02x",ch);
1165
pdfOut_printf(pdf,"> Tj\n");
1167
pdfOut_printf(pdf,"ET\n");
1171
static float stringwidth_x(lchar_t *str)
1175
for (len=0;str[len].ch;len++);
1177
return (float)len * 72.0 / (float)CharsPerInch;
1180
static void write_pretty_header() // {{{
1183
pdfOut_printf(pdf,"q\n"
1186
if (Duplex && (NumPages & 1) == 0) {
1187
x = PageWidth - PageRight;
1188
y = PageTop + 72.0f / LinesPerInch;
1191
y = PageTop + 72.0f / LinesPerInch;
1194
pdfOut_printf(pdf,"1 0 0 1 %.3f %.3f cm\n",x,y); // translate
1195
pdfOut_printf(pdf,"0 0 %.3f %.3f re f\n",
1196
PageRight - PageLeft, 144.0f / LinesPerInch);
1197
pdfOut_printf(pdf,"0 g 0 G\n");
1199
if (Duplex && (NumPages & 1) == 0) {
1200
x = PageRight - PageLeft - 36.0f / LinesPerInch - stringwidth_x(Title);
1201
y = (0.5f + 0.157f) * 72.0f / LinesPerInch;
1203
x = 36.0f / LinesPerInch;
1204
y = (0.5f + 0.157f) * 72.0f / LinesPerInch;
1206
write_font_str(x,y,ATTR_BOLD,Title,-1);
1208
x = (-stringwidth_x(Date) + PageRight - PageLeft) * 0.5;
1209
write_font_str(x,y,ATTR_BOLD,Date,-1);
1211
// convert pagenumber to string
1214
snprintf(tmp,19,"%d",NumPages);
1215
lchar_t *pagestr=make_wide(tmp);
1217
if (Duplex && (NumPages & 1) == 0) {
1218
x = 36.0f / LinesPerInch;
1220
x = PageRight - PageLeft - 36.0f / LinesPerInch - stringwidth_x(pagestr);
1222
write_font_str(x,y,ATTR_BOLD,pagestr,-1);
1224
pdfOut_printf(pdf,"Q\n");