xref: /plan9/sys/src/cmd/aux/antiword/pdf.c (revision 25b329d522281a8cdd35da0dcc08c3fc621059a9)
1 /*
2  * pdf.c
3  * Copyright (C) 2003-2005 A.J. van Os; Released under GNU GPL
4  *
5  * Description:
6  * Functions to deal with the Adobe Portable Document Format (pdf)
7  *
8  */
9 
10 #include <stdarg.h>
11 #include <string.h>
12 #include "version.h"
13 #include "antiword.h"
14 
15 
16 /* Constants for the file positions */
17 #define INITIAL_LOCATION_SIZE	20
18 #define INITIAL_PAGEOBJECT_SIZE	 5
19 #if defined(DEBUG)
20 #define EXTENSION_ARRAY_SIZE	10
21 #else
22 #define EXTENSION_ARRAY_SIZE	30
23 #endif /* DEBUG */
24 
25 /* The character set */
26 static encoding_type	eEncoding = encoding_neutral;
27 /* Current creator for a PDF header */
28 static const char	*szProducer = NULL;
29 /* The height and width of a PDF page (in DrawUnits) */
30 static long		lPageHeight = LONG_MAX;
31 static long		lPageWidth = LONG_MAX;
32 /* The height of the footer on the current page (in DrawUnits) */
33 static long		lFooterHeight = 0;
34 /* Inside a footer (to prevent an infinite loop when the footer is too big) */
35 static BOOL		bInFtrSpace = FALSE;
36 /* Current font information */
37 static drawfile_fontref	tFontRefCurr = (drawfile_fontref)-1;
38 static USHORT		usFontSizeCurr = 0;
39 static int		iFontColorCurr = -1;
40 /* Current vertical position information */
41 static long		lYtopCurr = -1;
42 /* Image counter */
43 static int		iImageCount = 0;
44 /* Section index */
45 static int		iSectionIndex = 0;
46 /* Are we on the first page of the section? */
47 static BOOL		bFirstInSection = TRUE;
48 /* File positions */
49 static long		lFilePosition = 0;
50 static long		*alLocation = NULL;
51 static size_t		tLocations = 0;
52 static int		iMaxLocationNumber = 0;
53 /* File position at the start of a page */
54 static long		lStreamStart = -1;
55 /* Page objects */
56 static int		*aiPageObject = NULL;
57 static int		iPageCount = 0;
58 static size_t		tMaxPageObjects = 0;
59 /* Current object number */
60 /* 1 = root; 2 = info; 3 = pages; 4 = encoding; 5-16 = fonts; 17 = resources */
61 static int		iObjectNumberCurr = 17;
62 
63 static void		vMoveTo(diagram_type *, long);
64 
65 static const struct {
66 	const char	*szPDFname;
67 	const char	*szPSname;
68 } atFontname[] = {
69 	{ "Courier",			FONT_MONOSPACED_PLAIN },
70 	{ "Courier-Bold",		FONT_MONOSPACED_BOLD },
71 	{ "Courier-Oblique",		FONT_MONOSPACED_ITALIC },
72 	{ "Courier-BoldOblique",	FONT_MONOSPACED_BOLDITALIC },
73 	{ "Helvetica",			FONT_SANS_SERIF_PLAIN },
74 	{ "Helvetica-Bold",		FONT_SANS_SERIF_BOLD },
75 	{ "Helvetica-Oblique",		FONT_SANS_SERIF_ITALIC },
76 	{ "Helvetica-BoldOblique",	FONT_SANS_SERIF_BOLDITALIC },
77 	{ "Times-Roman",		FONT_SERIF_PLAIN },
78 	{ "Times-Bold",			FONT_SERIF_BOLD },
79 	{ "Times-Italic",		FONT_SERIF_ITALIC },
80 	{ "Times-BoldItalic",		FONT_SERIF_BOLDITALIC },
81 };
82 
83 static const char *iso_8859_1[] = {
84 "128 /Euro",
85 "140 /ellipsis /trademark /perthousand /bullet",
86 "    /quoteleft /quoteright /guilsinglleft /guilsinglright",
87 "    /quotedblleft /quotedblright /quotedblbase /endash /emdash",
88 "    /minus /OE /oe /dagger /daggerdbl /fi /fl",
89 "160 /space /exclamdown /cent /sterling /currency",
90 "    /yen /brokenbar /section /dieresis /copyright",
91 "    /ordfeminine /guillemotleft /logicalnot /hyphen /registered",
92 "    /macron /degree /plusminus /twosuperior /threesuperior",
93 "    /acute /mu /paragraph /periodcentered /cedilla",
94 "    /onesuperior /ordmasculine /guillemotright /onequarter",
95 "    /onehalf /threequarters /questiondown /Agrave /Aacute",
96 "    /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla",
97 "    /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute",
98 "    /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute",
99 "    /Ocircumflex /Otilde /Odieresis /multiply /Oslash",
100 "    /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn",
101 "    /germandbls /agrave /aacute /acircumflex /atilde",
102 "    /adieresis /aring /ae /ccedilla /egrave /eacute",
103 "    /ecircumflex /edieresis /igrave /iacute /icircumflex",
104 "    /idieresis /eth /ntilde /ograve /oacute /ocircumflex",
105 "    /otilde /odieresis /divide /oslash /ugrave /uacute",
106 "    /ucircumflex /udieresis /yacute /thorn /ydieresis",
107 };
108 
109 static const char *iso_8859_2[] = {
110 "160 /space /Aogonek /breve /Lslash /currency /Lcaron",
111 "    /Sacute /section /dieresis /Scaron /Scommaaccent",
112 "    /Tcaron /Zacute /hyphen /Zcaron /Zdotaccent /degree",
113 "    /aogonek /ogonek /lslash /acute /lcaron /sacute",
114 "    /caron /cedilla /scaron /scommaaccent /tcaron",
115 "    /zacute /hungarumlaut /zcaron /zdotaccent /Racute",
116 "    /Aacute /Acircumflex /Abreve /Adieresis /Lacute",
117 "    /Cacute /Ccedilla /Ccaron /Eacute /Eogonek",
118 "    /Edieresis /Ecaron /Iacute /Icircumflex /Dcaron",
119 "    /.notdef /Nacute /Ncaron /Oacute /Ocircumflex",
120 "    /Ohungarumlaut /Odieresis /multiply /Rcaron /Uring",
121 "    /Uacute /Uhungarumlaut /Udieresis /Yacute /Tcommaaccent",
122 "    /germandbls /racute /aacute /acircumflex /abreve",
123 "    /adieresis /lacute /cacute /ccedilla /ccaron /eacute",
124 "    /eogonek /edieresis /ecaron /iacute /icircumflex",
125 "    /dcaron /.notdef /nacute /ncaron /oacute /ocircumflex",
126 "    /ohungarumlaut /odieresis /divide /rcaron /uring",
127 "    /uacute /uhungarumlaut /udieresis /yacute /tcommaaccent",
128 "    /dotaccent",
129 };
130 
131 
132 /*
133  * tGetFontIndex - get the font index
134  */
135 static size_t
tGetFontIndex(drawfile_fontref tFontRef)136 tGetFontIndex(drawfile_fontref tFontRef)
137 {
138 	const char	*szFontname;
139 	size_t		tIndex;
140 
141 	/* Get the font name */
142 	szFontname = szGetFontname(tFontRef);
143 	fail(szFontname == NULL);
144 	if (szFontname == NULL) {
145 		return 0;
146 	}
147 
148 	/* Find the name in the table */
149 	for (tIndex = 0; tIndex < elementsof(atFontname); tIndex++) {
150 		if (STRCEQ(atFontname[tIndex].szPSname, szFontname)) {
151 			return tIndex;
152 		}
153 	}
154 	/* Not found */
155 	DBG_DEC(tFontRef);
156 	DBG_MSG(szFontname);
157 	return 0;
158 } /* end of tGetFontIndex */
159 
160 /*
161  * vSetLocation - store the location of objects
162  */
163 static void
vSetLocation(int iLocationNumber)164 vSetLocation(int iLocationNumber)
165 {
166 	fail(iLocationNumber <= 0);
167 
168 	if ((size_t)iLocationNumber >= tLocations) {
169 		/* Extend and set to zero */
170 		tLocations += EXTENSION_ARRAY_SIZE;
171 		alLocation = xrealloc(alLocation, tLocations * sizeof(long));
172 		memset(alLocation + tLocations - EXTENSION_ARRAY_SIZE,
173 			0,
174 			EXTENSION_ARRAY_SIZE * sizeof(long));
175 		DBG_DEC(tLocations);
176 	}
177 	if (iLocationNumber > iMaxLocationNumber) {
178 		iMaxLocationNumber = iLocationNumber;
179 	}
180 
181 	DBG_DEC_C((size_t)iLocationNumber >= tLocations, iLocationNumber);
182 	DBG_DEC_C((size_t)iLocationNumber >= tLocations, tLocations);
183 	fail((size_t)iLocationNumber >= tLocations);
184 
185 	alLocation[iLocationNumber] = lFilePosition;
186 } /* end of vSetLocation */
187 
188 /*
189  * vFillNextPageObject - fil the next page object with the current object number
190  */
191 static void
vFillNextPageObject(void)192 vFillNextPageObject(void)
193 {
194 	iPageCount++;
195 	if ((size_t)iPageCount >= tMaxPageObjects) {
196 		/* Extend the array */
197 		tMaxPageObjects += EXTENSION_ARRAY_SIZE;
198 		aiPageObject = xrealloc(aiPageObject,
199 					tMaxPageObjects * sizeof(int));
200 		DBG_DEC(tMaxPageObjects);
201 	}
202 	aiPageObject[iPageCount] = iObjectNumberCurr;
203 } /* end of vFillNextPageObject */
204 
205 /*
206  * vFPprintf - printf and update the fileposition
207  *
208  * called with arguments like fprintf(3)
209  */
210 static void
vFPprintf(FILE * pOutFile,const char * szFormat,...)211 vFPprintf(FILE *pOutFile, const char *szFormat, ...)
212 {
213 	va_list	tArg;
214 
215 	va_start(tArg, szFormat);
216 	lFilePosition += vfprintf(pOutFile, szFormat, tArg);
217 	va_end(tArg);
218 } /* end of vFPprintf */
219 
220 /*
221  * vCreateInfoDictionary - create the document information dictionary
222  */
223 void
vCreateInfoDictionary(diagram_type * pDiag,int iWordVersion)224 vCreateInfoDictionary(diagram_type *pDiag, int iWordVersion)
225 {
226 	FILE	*pOutFile;
227 	const char	*szTitle, *szAuthor, *szSubject, *szCreator;
228 	const char	*szCreationDate, *szModDate;
229 
230 	fail(pDiag == NULL);
231 	fail(pDiag->pOutFile == NULL);
232 	fail(iWordVersion < 0);
233 	fail(szProducer == NULL || szProducer[0] == '\0');
234 
235 	szTitle = szGetTitle();
236 	szAuthor = szGetAuthor();
237 	szSubject = szGetSubject();
238 	szCreationDate = szGetCreationDate();
239 	szModDate = szGetModDate();
240 
241 	switch (iWordVersion) {
242 	case 0: szCreator = "Word for DOS"; break;
243 	case 1: szCreator = "WinWord 1.x"; break;
244 	case 2: szCreator = "WinWord 2.0"; break;
245 	case 4: szCreator = "MacWord 4"; break;
246 	case 5: szCreator = "MacWord 5"; break;
247 	case 6: szCreator = "Word 6"; break;
248 	case 7: szCreator = "Word 7/95"; break;
249 	case 8: szCreator = "Word 97 or later"; break;
250 	default: szCreator = NULL; break;
251 	}
252 
253 	pOutFile = pDiag->pOutFile;
254 
255 	vSetLocation(2);
256 	vFPprintf(pOutFile, "2 0 obj\n");
257 	vFPprintf(pOutFile, "<<\n");
258 	if (szTitle != NULL && szTitle[0] != '\0') {
259 		vFPprintf(pOutFile, "/Title (%s)\n", szTitle);
260 	}
261 	if (szAuthor != NULL && szAuthor[0] != '\0') {
262 		vFPprintf(pOutFile, "/Author (%s)\n", szAuthor);
263 	}
264 	if (szSubject != NULL && szSubject[0] != '\0') {
265 		vFPprintf(pOutFile, "/Subject (%s)\n", szSubject);
266 	}
267 	if (szCreator != NULL && szCreator[0] != '\0') {
268 		vFPprintf(pOutFile, "/Creator (%s)\n", szCreator);
269 	}
270 	vFPprintf(pOutFile, "/Producer (%s %s)\n", szProducer, VERSIONSTRING);
271 	if (szCreationDate != NULL && szCreationDate[0] != '\0') {
272 		vFPprintf(pOutFile, "/CreationDate (%s)\n", szCreationDate);
273 	}
274 	if (szModDate != NULL && szModDate[0] != '\0') {
275 		vFPprintf(pOutFile, "/ModDate (%s)\n", szModDate);
276 	}
277 	vFPprintf(pOutFile, ">>\n");
278 	vFPprintf(pOutFile, "endobj\n");
279 } /* end of vCreateInfoDictionary */
280 
281 /*
282  * vAddHdrFtr - add a header or footer
283  */
284 static void
vAddHdrFtr(diagram_type * pDiag,const hdrftr_block_type * pHdrFtrInfo)285 vAddHdrFtr(diagram_type *pDiag, const hdrftr_block_type *pHdrFtrInfo)
286 {
287 	output_type	*pStart, *pPrev, *pNext;
288 
289 	fail(pDiag == NULL);
290 	fail(pHdrFtrInfo == NULL);
291 
292 	vStartOfParagraphPDF(pDiag, 0);
293 	pStart = pHdrFtrInfo->pText;
294 	while (pStart != NULL) {
295 		pNext = pStart;
296 		while (pNext != NULL &&
297 		       (pNext->tNextFree != 1 ||
298 		        (pNext->szStorage[0] != PAR_END &&
299 		         pNext->szStorage[0] != HARD_RETURN))) {
300 			pNext = pNext->pNext;
301 		}
302 		if (pNext == NULL) {
303 			if (bOutputContainsText(pStart)) {
304 				vAlign2Window(pDiag, pStart,
305 					lChar2MilliPoints(DEFAULT_SCREEN_WIDTH),
306 					ALIGNMENT_LEFT);
307 			} else {
308 				vMove2NextLinePDF(pDiag, pStart->usFontSize);
309 			}
310 			break;
311 		}
312 		fail(pNext->tNextFree != 1);
313 		fail(pNext->szStorage[0] != PAR_END &&
314 			pNext->szStorage[0] != HARD_RETURN);
315 
316 		if (pStart != pNext) {
317 			/* There is something to print */
318 			pPrev = pNext->pPrev;
319 			fail(pPrev->pNext != pNext);
320 			/* Cut the chain */
321 			pPrev->pNext = NULL;
322 			if (bOutputContainsText(pStart)) {
323 				/* Print it */
324 				vAlign2Window(pDiag, pStart,
325 					lChar2MilliPoints(DEFAULT_SCREEN_WIDTH),
326 					ALIGNMENT_LEFT);
327 			} else {
328 				/* Just an empty line */
329 				vMove2NextLinePDF(pDiag, pStart->usFontSize);
330 			}
331 			/* Repair the chain */
332 			pPrev->pNext = pNext;
333 		}
334 		if (pNext->szStorage[0] == PAR_END) {
335 			vEndOfParagraphPDF(pDiag, pNext->usFontSize,
336 					(long)pNext->usFontSize * 200);
337 		}
338 		pStart = pNext->pNext;
339 	}
340 } /* end of vAddHdrFtr */
341 
342 /*
343  * vAddHeader - add a page header
344  */
345 static void
vAddHeader(diagram_type * pDiag)346 vAddHeader(diagram_type *pDiag)
347 {
348 	const hdrftr_block_type *pHdrInfo;
349 	const hdrftr_block_type *pFtrInfo;
350 
351 	fail(pDiag == NULL);
352 
353 	NO_DBG_MSG("vAddHeader");
354 
355 	pHdrInfo = pGetHdrFtrInfo(iSectionIndex, TRUE,
356 					odd(iPageCount), bFirstInSection);
357 	pFtrInfo = pGetHdrFtrInfo(iSectionIndex, FALSE,
358 					odd(iPageCount), bFirstInSection);
359 	/* Set the height of the footer of this page */
360 	lFooterHeight = pFtrInfo == NULL ? 0 : pFtrInfo->lHeight;
361 	fail(lFooterHeight < 0);
362 
363 	if (pHdrInfo == NULL ||
364 	    pHdrInfo->pText == NULL ||
365 	    pHdrInfo->lHeight <= 0) {
366 		fail(pHdrInfo != NULL && pHdrInfo->lHeight < 0);
367 		fail(pHdrInfo != NULL &&
368 			pHdrInfo->pText != NULL &&
369 			pHdrInfo->lHeight == 0);
370 		return;
371 	}
372 
373 	vAddHdrFtr(pDiag, pHdrInfo);
374 
375 	DBG_DEC_C(pHdrInfo->lHeight !=
376 		lPageHeight - PS_TOP_MARGIN - pDiag->lYtop,
377 		pHdrInfo->lHeight);
378 	DBG_DEC_C(pHdrInfo->lHeight !=
379 		lPageHeight - PS_TOP_MARGIN - pDiag->lYtop,
380 		lPageHeight - PS_TOP_MARGIN - pDiag->lYtop);
381 } /* end of vAddHeader */
382 
383 /*
384  * vAddFooter - add a page footer
385  */
386 static void
vAddFooter(diagram_type * pDiag)387 vAddFooter(diagram_type *pDiag)
388 {
389 	const hdrftr_block_type *pFtrInfo;
390 
391 	fail(pDiag == NULL);
392 
393 	NO_DBG_MSG("vAddFooter");
394 
395 	pFtrInfo = pGetHdrFtrInfo(iSectionIndex, FALSE,
396 					odd(iPageCount), bFirstInSection);
397 	bFirstInSection = FALSE;
398 	if (pFtrInfo == NULL ||
399 	    pFtrInfo->pText == NULL ||
400 	    pFtrInfo->lHeight <= 0) {
401 		fail(pFtrInfo != NULL && pFtrInfo->lHeight < 0);
402 		fail(pFtrInfo != NULL &&
403 			pFtrInfo->pText != NULL &&
404 			pFtrInfo->lHeight == 0);
405 		return;
406 	}
407 
408 	bInFtrSpace = TRUE;
409 
410 	DBG_DEC_C(pFtrInfo->lHeight != lFooterHeight, pFtrInfo->lHeight);
411 	DBG_DEC_C(pFtrInfo->lHeight != lFooterHeight, lFooterHeight);
412 	DBG_DEC_C(pDiag->lYtop < lFooterHeight + PS_BOTTOM_MARGIN,
413 			pDiag->lYtop);
414 	DBG_DEC_C(pDiag->lYtop < lFooterHeight + PS_BOTTOM_MARGIN,
415 			lFooterHeight + PS_BOTTOM_MARGIN);
416 
417 	if (pDiag->lYtop > lFooterHeight + PS_BOTTOM_MARGIN) {
418 		/* Move down to the start of the footer */
419 		pDiag->lYtop = lFooterHeight + PS_BOTTOM_MARGIN;
420 		vMoveTo(pDiag, 0);
421 	} else if (pDiag->lYtop < lFooterHeight + PS_BOTTOM_MARGIN / 2) {
422 		DBG_FIXME();
423 		/*
424 		 * Move up to the start of the footer, to prevent moving
425 		 * of the bottom edge of the paper
426 		 */
427 		pDiag->lYtop = lFooterHeight + PS_BOTTOM_MARGIN;
428 		vMoveTo(pDiag, 0);
429 	}
430 
431 	DBG_FLT_C(pDiag->lYtop < lFooterHeight + PS_BOTTOM_MARGIN,
432 	dDrawUnits2Points(lFooterHeight + PS_BOTTOM_MARGIN - pDiag->lYtop));
433 
434 	vAddHdrFtr(pDiag, pFtrInfo);
435 	bInFtrSpace = FALSE;
436 } /* end of vAddFooter */
437 
438 /*
439  * vEndPageObject - end the current page object
440  */
441 static void
vEndPageObject(FILE * pOutFile)442 vEndPageObject(FILE *pOutFile)
443 {
444 	long	lStreamEnd;
445 
446 	if (lStreamStart < 0) {
447 		/* There is no current page object */
448 		return;
449 	}
450 
451 	vFPprintf(pOutFile, "ET\n");
452 	lStreamEnd = lFilePosition;
453 	vFPprintf(pOutFile, "endstream\n");
454 	vFPprintf(pOutFile, "endobj\n");
455 
456 	iObjectNumberCurr++;
457 	vSetLocation(iObjectNumberCurr);
458 	vFPprintf(pOutFile, "%d 0 obj\n", iObjectNumberCurr);
459 	vFPprintf(pOutFile, "%lu\n", lStreamEnd - lStreamStart);
460 	vFPprintf(pOutFile, "endobj\n");
461 } /* end of vEndPageObject */
462 
463 /*
464  * vMove2NextPage - move to the start of the next page
465  */
466 static void
vMove2NextPage(diagram_type * pDiag,BOOL bNewSection)467 vMove2NextPage(diagram_type *pDiag, BOOL bNewSection)
468 {
469 	FILE	*pOutFile;
470 
471 	fail(pDiag == NULL);
472 	fail(pDiag->pOutFile == NULL);
473 
474 	pOutFile = pDiag->pOutFile;
475 
476 	vAddFooter(pDiag);
477 	/* End the old page object */
478 	vEndPageObject(pOutFile);
479 	if (bNewSection) {
480 		iSectionIndex++;
481 		bFirstInSection = TRUE;
482 	}
483 
484 	/* Start the new page object */
485 	iObjectNumberCurr++;
486 	vSetLocation(iObjectNumberCurr);
487 	vFillNextPageObject();
488 	vFPprintf(pOutFile, "%d 0 obj\n", iObjectNumberCurr);
489 	vFPprintf(pOutFile, "<<\n");
490 	vFPprintf(pOutFile, "/Type /Page\n");
491 	vFPprintf(pOutFile, "/Parent 3 0 R\n");
492 	vFPprintf(pOutFile, "/Resources 17 0 R\n");
493 	vFPprintf(pOutFile, "/Contents %d 0 R\n", iObjectNumberCurr + 1);
494 	vFPprintf(pOutFile, ">>\n");
495 	vFPprintf(pOutFile, "endobj\n");
496 
497 	/* Start the new text object */
498 	iObjectNumberCurr++;
499 	vSetLocation(iObjectNumberCurr);
500 	vFPprintf(pOutFile, "%d 0 obj\n", iObjectNumberCurr);
501 	vFPprintf(pOutFile, "<<\n");
502 	vFPprintf(pOutFile, "/Length %d 0 R\n", iObjectNumberCurr + 1);
503 	vFPprintf(pOutFile, ">>\n");
504 	vFPprintf(pOutFile, "stream\n");
505 	lStreamStart = lFilePosition;
506 	vFPprintf(pOutFile, "BT\n");
507 
508 	/* Set variables to their start of page values */
509 	pDiag->lYtop = lPageHeight - PS_TOP_MARGIN;
510 	tFontRefCurr = (drawfile_fontref)-1;
511 	usFontSizeCurr = 0;
512 	iFontColorCurr = -1;
513 	lYtopCurr = -1;
514 	vAddHeader(pDiag);
515 } /* end of vMove2NextPage */
516 
517 /*
518  * vMoveTo - move to the specified X,Y coordinates
519  *
520  * Move the current position of the specified diagram to its X,Y coordinates,
521  * start on a new page if needed
522  */
523 static void
vMoveTo(diagram_type * pDiag,long lLastVerticalMovement)524 vMoveTo(diagram_type *pDiag, long lLastVerticalMovement)
525 {
526 	fail(pDiag == NULL);
527 	fail(pDiag->pOutFile == NULL);
528 
529 	if (pDiag->lYtop <= lFooterHeight + PS_BOTTOM_MARGIN && !bInFtrSpace) {
530 		vMove2NextPage(pDiag, FALSE);
531 		/* Repeat the last vertical movement on the new page */
532 		pDiag->lYtop -= lLastVerticalMovement;
533 	}
534 
535 	fail(pDiag->lYtop < lFooterHeight + PS_BOTTOM_MARGIN && !bInFtrSpace);
536 	DBG_DEC_C(pDiag->lYtop < PS_BOTTOM_MARGIN, pDiag->lYtop);
537 	fail(pDiag->lYtop < PS_BOTTOM_MARGIN / 3);
538 
539 	if (pDiag->lYtop != lYtopCurr) {
540 		vFPprintf(pDiag->pOutFile, "1 0 0 1 %.2f %.2f Tm\n",
541 			dDrawUnits2Points(pDiag->lXleft + PS_LEFT_MARGIN),
542 			dDrawUnits2Points(pDiag->lYtop));
543 		lYtopCurr = pDiag->lYtop;
544 	}
545 } /* end of vMoveTo */
546 
547 /*
548  * vProloguePDF - set options and perform the PDF initialization
549  */
550 void
vProloguePDF(diagram_type * pDiag,const char * szTask,const options_type * pOptions)551 vProloguePDF(diagram_type *pDiag,
552 	const char *szTask, const options_type *pOptions)
553 {
554 	FILE	*pOutFile;
555 
556 	fail(pDiag == NULL);
557 	fail(pDiag->pOutFile == NULL);
558 	fail(pOptions == NULL);
559 
560 	pOutFile = pDiag->pOutFile;
561 
562 	eEncoding = pOptions->eEncoding;
563 
564 	/* Create an empty location array */
565 	tLocations = INITIAL_LOCATION_SIZE;
566 	alLocation = xcalloc(tLocations, sizeof(long));
567 
568 	/* Create an empty pageobject array */
569 	tMaxPageObjects = INITIAL_PAGEOBJECT_SIZE;
570 	aiPageObject = xcalloc(tMaxPageObjects, sizeof(int));
571 
572 	if (pOptions->iPageHeight == INT_MAX) {
573 		lPageHeight = LONG_MAX;
574 	} else {
575 		lPageHeight = lPoints2DrawUnits(pOptions->iPageHeight);
576 	}
577 	DBG_DEC(lPageHeight);
578 	if (pOptions->iPageWidth == INT_MAX) {
579 		lPageWidth = LONG_MAX;
580 	} else {
581 		lPageWidth = lPoints2DrawUnits(pOptions->iPageWidth);
582 	}
583 	DBG_DEC(lPageWidth);
584 	lFooterHeight = 0;
585 	bInFtrSpace = FALSE;
586 
587 	tFontRefCurr = (drawfile_fontref)-1;
588 	usFontSizeCurr = 0;
589 	iFontColorCurr = -1;
590 	lYtopCurr = -1;
591 	iPageCount = 0;
592 	iImageCount = 0;
593 	iSectionIndex = 0;
594 	bFirstInSection = TRUE;
595 	lFilePosition = 0;
596 	iMaxLocationNumber = 0;
597 	lStreamStart = -1;
598 	iObjectNumberCurr = 17;
599 	pDiag->lXleft = 0;
600 	pDiag->lYtop = 0;
601 
602 	szProducer = szTask;
603 
604 	vFPprintf(pOutFile, "%%PDF-1.3\n");
605 	vFPprintf(pOutFile, "%%%c%c%c%c\n", 0xe2, 0xe3, 0xcf, 0xd3);
606 
607 	/* Root catalog */
608 	vSetLocation(1);
609 	vFPprintf(pOutFile, "1 0 obj\n");
610 	vFPprintf(pOutFile, "<<\n");
611 	vFPprintf(pOutFile, "/Type /Catalog\n");
612 	vFPprintf(pOutFile, "/Pages 3 0 R\n");
613 	vFPprintf(pOutFile, ">>\n");
614 	vFPprintf(pOutFile, "endobj\n");
615 } /* end of vProloguePDF */
616 
617 /*
618  * vEpiloguePDF - clean up after everything is done
619  */
620 void
vEpiloguePDF(diagram_type * pDiag)621 vEpiloguePDF(diagram_type *pDiag)
622 {
623 	FILE	*pOutFile;
624 	long	lXref;
625 	int	iIndex;
626 
627 	fail(pDiag == NULL);
628 	fail(pDiag->pOutFile == NULL);
629 
630 	pOutFile = pDiag->pOutFile;
631 
632 	vAddFooter(pDiag);
633 	/* End the old page object */
634 	vEndPageObject(pOutFile);
635 
636 	vSetLocation(3);
637 	vFPprintf(pOutFile, "3 0 obj\n");
638 	vFPprintf(pOutFile, "<<\n");
639 	vFPprintf(pOutFile, "/Type /Pages\n");
640 	vFPprintf(pOutFile, "/Count %d\n", iPageCount);
641 	vFPprintf(pOutFile, "/MediaBox [ 0 0 %.0f %.0f ]\n",
642 			dDrawUnits2Points(lPageWidth),
643 			dDrawUnits2Points(lPageHeight));
644 	vFPprintf(pOutFile, "/Kids [ ");
645 	for (iIndex = 1; iIndex <= iPageCount; iIndex++) {
646 		vFPprintf(pOutFile, "\t%d 0 R\n", aiPageObject[iIndex]);
647 	}
648 	vFPprintf(pOutFile, "]\n");
649 	vFPprintf(pOutFile, ">>\n");
650 	vFPprintf(pOutFile, "endobj\n");
651 
652 	lXref = lFilePosition;
653 
654 	vFPprintf(pOutFile, "xref\n");
655 	vFPprintf(pOutFile, "0 %d\n", iMaxLocationNumber + 1);
656 	vFPprintf(pOutFile, "0000000000 65535 f \n");
657 	for (iIndex = 1; iIndex <= iMaxLocationNumber; iIndex++) {
658 		vFPprintf(pOutFile, "%.10ld 00000 n \n", alLocation[iIndex]);
659 	}
660 
661 	vFPprintf(pOutFile, "trailer\n");
662 	vFPprintf(pOutFile, "<<\n");
663 	vFPprintf(pOutFile, "/Size %d\n", iMaxLocationNumber + 1);
664 	vFPprintf(pOutFile, "/Root 1 0 R\n");
665 	vFPprintf(pOutFile, "/Info 2 0 R\n");
666 	vFPprintf(pOutFile, ">>\n");
667 
668 	vFPprintf(pOutFile, "startxref\n");
669 	vFPprintf(pOutFile, "%ld\n", lXref);
670 	vFPprintf(pOutFile, "%%%%EOF\n");
671 
672 	szProducer = NULL;
673 	aiPageObject = xfree(aiPageObject);
674 	alLocation = xfree(alLocation);
675 } /* end of vEpiloguePDF */
676 
677 /*
678  * vPrintPalette - print a pdf color space (palette)
679  */
680 static void
vPrintPalette(FILE * pOutFile,const imagedata_type * pImg)681 vPrintPalette(FILE *pOutFile, const imagedata_type *pImg)
682 {
683 	int	iIndex;
684 
685 	fail(pOutFile == NULL);
686 	fail(pImg == NULL);
687 	fail(pImg->iColorsUsed < 2);
688 	fail(pImg->iColorsUsed > 256);
689 
690 	vFPprintf(pOutFile, "\t/ColorSpace [ /Indexed\n");
691 	vFPprintf(pOutFile, "\t/Device%s %d\n",
692 		pImg->bColorImage ? "RGB" : "Gray", pImg->iColorsUsed - 1);
693 	vFPprintf(pOutFile, "<");
694 	for (iIndex = 0; iIndex < pImg->iColorsUsed; iIndex++) {
695 		vFPprintf(pOutFile, "%02x",
696 				(unsigned int)pImg->aucPalette[iIndex][0]);
697 		if (pImg->bColorImage) {
698 			vFPprintf(pOutFile, "%02x%02x",
699 				(unsigned int)pImg->aucPalette[iIndex][1],
700 				(unsigned int)pImg->aucPalette[iIndex][2]);
701 		}
702 		if (iIndex % 8 == 7) {
703 			vFPprintf(pOutFile, "\n");
704 		} else {
705 			vFPprintf(pOutFile, " ");
706 		}
707 	}
708 	vFPprintf(pOutFile, "> ]\n");
709 } /* end of vPrintPalette */
710 
711 /*
712  * vImageProloguePDF - perform the image initialization
713  */
714 void
vImageProloguePDF(diagram_type * pDiag,const imagedata_type * pImg)715 vImageProloguePDF(diagram_type *pDiag, const imagedata_type *pImg)
716 {
717 	FILE	*pOutFile;
718 
719 	fail(pDiag == NULL);
720 	fail(pDiag->pOutFile == NULL);
721 	fail(pImg == NULL);
722 
723 	if (pImg->iVerSizeScaled <= 0 || pImg->iHorSizeScaled <= 0) {
724 		return;
725 	}
726 
727 	iImageCount++;
728 
729 	DBG_DEC_C(pDiag->lXleft != 0, pDiag->lXleft);
730 
731 	pDiag->lYtop -= lPoints2DrawUnits(pImg->iVerSizeScaled);
732 	vMoveTo(pDiag, lPoints2DrawUnits(pImg->iVerSizeScaled));
733 
734 	pOutFile = pDiag->pOutFile;
735 
736 	vFPprintf(pOutFile, "ET\n");
737 	vFPprintf(pOutFile, "q %% Image %03d\n", iImageCount);
738 	if (pImg->eImageType == imagetype_is_dib) {
739 		/* Scanning from left to right and bottom to top */
740 		vFPprintf(pOutFile, "%d 0 0 %d %.2f %.2f cm\n",
741 			pImg->iHorSizeScaled, -pImg->iVerSizeScaled,
742 			dDrawUnits2Points(pDiag->lXleft + PS_LEFT_MARGIN),
743 			dDrawUnits2Points(pDiag->lYtop) + pImg->iVerSizeScaled);
744 	} else {
745 		/* Scanning from left to right and top to bottom */
746 		vFPprintf(pOutFile, "%d 0 0 %d %.2f %.2f cm\n",
747 			pImg->iHorSizeScaled, pImg->iVerSizeScaled,
748 			dDrawUnits2Points(pDiag->lXleft + PS_LEFT_MARGIN),
749 			dDrawUnits2Points(pDiag->lYtop));
750 	}
751 	vFPprintf(pOutFile, "BI\n");
752 	vFPprintf(pOutFile, "\t/Width %d\n", pImg->iWidth);
753 	vFPprintf(pOutFile, "\t/Height %d\n", pImg->iHeight);
754 	switch (pImg->eImageType) {
755 	case imagetype_is_jpeg:
756 		switch (pImg->iComponents) {
757 		case 1:
758 			vFPprintf(pOutFile, "\t/ColorSpace /DeviceGray\n");
759 			break;
760 		case 3:
761 			vFPprintf(pOutFile, "\t/ColorSpace /DeviceRGB\n");
762 			break;
763 		case 4:
764 			vFPprintf(pOutFile, "\t/ColorSpace /DeviceCMYK\n");
765 			if (pImg->bAdobe) {
766 				/*
767 				 * Adobe-conforming CMYK file
768 				 * applying workaround for color inversion
769 				 */
770 				vFPprintf(pOutFile,
771 					"\t/Decode [1 0 1 0 1 0 1 0]\n");
772 			}
773 			break;
774 		default:
775 			DBG_DEC(pImg->iComponents);
776 			break;
777 		}
778 		vFPprintf(pOutFile, "\t/BitsPerComponent 8\n");
779 		vFPprintf(pOutFile,
780 			"\t/Filter [ /ASCII85Decode /DCTDecode ]\n");
781 		break;
782 	case imagetype_is_png:
783 		if (pImg->iComponents == 3 || pImg->iComponents == 4) {
784 			vFPprintf(pOutFile, "\t/ColorSpace /DeviceRGB\n");
785 			vFPprintf(pOutFile, "\t/BitsPerComponent 8\n");
786 		} else if (pImg->iColorsUsed > 0) {
787 			vPrintPalette(pOutFile, pImg);
788 			fail(pImg->uiBitsPerComponent > 8);
789 			vFPprintf(pOutFile, "\t/BitsPerComponent %u\n",
790 					pImg->uiBitsPerComponent);
791 		} else {
792 			vFPprintf(pOutFile, "\t/ColorSpace /DeviceGray\n");
793 			vFPprintf(pOutFile, "\t/BitsPerComponent 8\n");
794 		}
795 		vFPprintf(pOutFile,
796 			"\t/Filter [ /ASCII85Decode /FlateDecode ]\n");
797 		vFPprintf(pOutFile, "\t/DecodeParms [ null <<\n");
798 		vFPprintf(pOutFile, "\t\t/Predictor 10\n");
799 		vFPprintf(pOutFile, "\t\t/Colors %d\n", pImg->iComponents);
800 		vFPprintf(pOutFile, "\t\t/BitsPerComponent %u\n",
801 						pImg->uiBitsPerComponent);
802 		vFPprintf(pOutFile, "\t\t/Columns %d\n", pImg->iWidth);
803 		vFPprintf(pOutFile, "\t\t>> ]\n");
804 		break;
805 	case imagetype_is_dib:
806 		if (pImg->uiBitsPerComponent <= 8) {
807 			vPrintPalette(pOutFile, pImg);
808 		} else {
809 			vFPprintf(pOutFile, "\t/ColorSpace /DeviceRGB\n");
810 		}
811 		vFPprintf(pOutFile, "\t/BitsPerComponent 8\n");
812 		vFPprintf(pOutFile, "\t/Filter /ASCII85Decode\n");
813 		break;
814 	default:
815 		vFPprintf(pOutFile, "\t/ColorSpace /Device%s\n",
816 			pImg->bColorImage ? "RGB" : "Gray");
817 		vFPprintf(pOutFile, "\t/BitsPerComponent 8\n");
818 		vFPprintf(pOutFile, "\t/Filter /ASCIIHexDecode\n");
819 		break;
820 	}
821 	vFPprintf(pOutFile, "ID\n");
822 } /* end of vImageProloguePDF */
823 
824 /*
825  * vImageEpiloguePDF - clean up after the image
826  */
827 void
vImageEpiloguePDF(diagram_type * pDiag)828 vImageEpiloguePDF(diagram_type *pDiag)
829 {
830 	FILE	*pOutFile;
831 
832 	fail(pDiag == NULL);
833 	fail(pDiag->pOutFile == NULL);
834 
835 	pOutFile = pDiag->pOutFile;
836 
837 	/* Correction for the image bytes */
838 	lFilePosition = ftell(pOutFile);
839 
840 	vFPprintf(pOutFile, "EI\n");
841 	vFPprintf(pOutFile, "Q\n");
842 	vFPprintf(pOutFile, "BT\n");
843 
844 	pDiag->lXleft = 0;
845 } /* end of vImageEpiloguePDF */
846 
847 /*
848  * bAddDummyImagePDF - add a dummy image
849  *
850  * return TRUE when successful, otherwise FALSE
851  */
852 BOOL
bAddDummyImagePDF(diagram_type * pDiag,const imagedata_type * pImg)853 bAddDummyImagePDF(diagram_type *pDiag, const imagedata_type *pImg)
854 {
855 	FILE	*pOutFile;
856 
857 	fail(pDiag == NULL);
858 	fail(pDiag->pOutFile == NULL);
859 	fail(pImg == NULL);
860 
861 	if (pImg->iVerSizeScaled <= 0 || pImg->iHorSizeScaled <= 0) {
862 		return FALSE;
863 	}
864 
865 	iImageCount++;
866 
867 	DBG_DEC_C(pDiag->lXleft != 0, pDiag->lXleft);
868 
869 	pDiag->lYtop -= lPoints2DrawUnits(pImg->iVerSizeScaled);
870 	vMoveTo(pDiag, lPoints2DrawUnits(pImg->iVerSizeScaled));
871 
872 	pOutFile = pDiag->pOutFile;
873 
874 	vFPprintf(pOutFile, "ET\n");
875 	vFPprintf(pOutFile, "q %% Image %03d\n", iImageCount);
876 	vFPprintf(pOutFile, "\t1.0 w\n");
877 	vFPprintf(pOutFile, "\t0.3 G\n");
878 	vFPprintf(pOutFile, "\t%.2f %.2f %d %d re\n",
879 			dDrawUnits2Points(pDiag->lXleft + PS_LEFT_MARGIN),
880 			dDrawUnits2Points(pDiag->lYtop),
881 			pImg->iHorSizeScaled,
882 			pImg->iVerSizeScaled);
883 	vFPprintf(pOutFile, "\tS\n");
884 	vFPprintf(pOutFile, "Q\n");
885 	vFPprintf(pOutFile, "BT\n");
886 
887 	pDiag->lXleft = 0;
888 
889 	return TRUE;
890 } /* end of bAddDummyImagePDF */
891 
892 /*
893  * vAddFontsPDF - add the font information
894  */
895 void
vAddFontsPDF(diagram_type * pDiag)896 vAddFontsPDF(diagram_type *pDiag)
897 {
898 	FILE	*pOutFile;
899 	size_t	tIndex;
900 
901 	fail(pDiag == NULL);
902 	fail(pDiag->pOutFile == NULL);
903 
904 	pOutFile = pDiag->pOutFile;
905 
906 	/* The font encoding */
907 	vSetLocation(4);
908 	vFPprintf(pOutFile, "4 0 obj\n");
909 	vFPprintf(pOutFile, "<<\n");
910 	vFPprintf(pOutFile, "/Type /Encoding\n");
911 	vFPprintf(pOutFile, "/BaseEncoding /StandardEncoding\n");
912 	vFPprintf(pOutFile, "/Differences [\n");
913 	switch (eEncoding) {
914 	case encoding_latin_1:
915 		for (tIndex = 0;
916 		     tIndex < elementsof(iso_8859_1);
917 		     tIndex++) {
918 			vFPprintf(pOutFile, "%s\n", iso_8859_1[tIndex]);
919 		}
920 		break;
921 	case encoding_latin_2:
922 		for (tIndex = 0;
923 		     tIndex < elementsof(iso_8859_2);
924 		     tIndex++) {
925 			vFPprintf(pOutFile, "%s\n", iso_8859_2[tIndex]);
926 		}
927 		break;
928 	case encoding_cyrillic:
929 		werr(1,
930 		"The combination PDF and Cyrillic is not supported");
931 		break;
932 	case encoding_utf_8:
933 		werr(1,
934 		"The combination PDF and UTF-8 is not supported");
935 		break;
936 	default:
937 		DBG_DEC(eEncoding);
938 		break;
939 	}
940 	vFPprintf(pOutFile, "]\n");
941 	vFPprintf(pOutFile, ">>\n");
942 	vFPprintf(pOutFile, "endobj\n");
943 
944 	/* Twelve of the standard type 1 fonts */
945 	for (tIndex = 0; tIndex < 12; tIndex++) {
946 		vSetLocation(5 + tIndex);
947 		vFPprintf(pOutFile, "%u 0 obj\n", 5 + tIndex);
948 		vFPprintf(pOutFile, "<<\n");
949 		vFPprintf(pOutFile, "/Type /Font\n");
950 		vFPprintf(pOutFile, "/Subtype /Type1\n");
951 		vFPprintf(pOutFile, "/Name /F%u\n", 1 + tIndex);
952 		vFPprintf(pOutFile, "/BaseFont /%s\n",
953 						atFontname[tIndex].szPDFname);
954 		vFPprintf(pOutFile, "/Encoding 4 0 R\n");
955 		vFPprintf(pOutFile, ">>\n");
956 		vFPprintf(pOutFile, "endobj\n");
957 	}
958 
959 	/* The Resources */
960 	vSetLocation(17);
961 	vFPprintf(pOutFile, "17 0 obj\n");
962 	vFPprintf(pOutFile, "<<\n");
963 	vFPprintf(pOutFile, "/ProcSet [ /PDF /Text ]\n");
964 	vFPprintf(pOutFile, "/Font <<\n");
965 	for (tIndex = 0; tIndex < 12; tIndex++) {
966 		vFPprintf(pOutFile, "\t/F%u %u 0 R\n", 1 + tIndex, 5 + tIndex);
967 	}
968 	vFPprintf(pOutFile, "\t>>\n");
969 	vFPprintf(pOutFile, ">>\n");
970 	vFPprintf(pOutFile, "endobj\n");
971 	vAddHeader(pDiag);
972 } /* end of vAddFontsPDF */
973 
974 /*
975  * vPrintPDF - print a PDF string
976  */
977 static void
vPrintPDF(FILE * pFile,const char * szString,size_t tStringLength,USHORT usFontstyle)978 vPrintPDF(FILE *pFile, const char *szString, size_t tStringLength,
979 	USHORT usFontstyle)
980 {
981 	const UCHAR	*aucBytes;
982 	double	dMove;
983 	size_t	tCount;
984 
985 	fail(szString == NULL);
986 
987 	if (szString == NULL || szString[0] == '\0' || tStringLength == 0) {
988 		return;
989 	}
990 	DBG_DEC_C(usFontSizeCurr < MIN_FONT_SIZE, usFontSizeCurr);
991 
992 	dMove = 0.0;
993 
994 	/* Up for superscript */
995 	if (bIsSuperscript(usFontstyle) && usFontSizeCurr != 0) {
996 		dMove = (double)((usFontSizeCurr + 1) / 2) * 0.375;
997 		vFPprintf(pFile, "%.2f Ts\n", dMove);
998 	}
999 
1000 	/* Down for subscript */
1001 	if (bIsSubscript(usFontstyle) && usFontSizeCurr != 0) {
1002 		dMove = (double)usFontSizeCurr * 0.125;
1003 		vFPprintf(pFile, "%.2f Ts\n", -dMove);
1004 	}
1005 
1006 	/* Generate and print the PDF output */
1007 	aucBytes = (UCHAR *)szString;
1008 	vFPprintf(pFile, "(");
1009 	for (tCount = 0; tCount < tStringLength ; tCount++) {
1010 		switch (aucBytes[tCount]) {
1011 		case '(':
1012 		case ')':
1013 		case '\\':
1014 			vFPprintf(pFile, "\\%c", szString[tCount]);
1015 			break;
1016 		default:
1017 			if (aucBytes[tCount] < 0x20 ||
1018 			    aucBytes[tCount] == 0x7f ||
1019 			    (aucBytes[tCount] >= 0x81 &&
1020 			     aucBytes[tCount] < 0x8c)) {
1021 				DBG_HEX(aucBytes[tCount]);
1022 				vFPprintf(pFile, " ");
1023 			} else if (aucBytes[tCount] >= 0x80) {
1024 				vFPprintf(pFile, "\\%03o",
1025 						(UINT)aucBytes[tCount]);
1026 			} else {
1027 				vFPprintf(pFile, "%c", szString[tCount]);
1028 			}
1029 			break;
1030 		}
1031 	}
1032 	vFPprintf(pFile, ") Tj\n");
1033 
1034 	/* Undo the superscript/subscript move */
1035 	if (dMove != 0.0) {
1036 		vFPprintf(pFile, "0 Ts\n");
1037 	}
1038 } /* end of vPrintPDF */
1039 
1040 /*
1041  * vSetColor - move to the specified color
1042  */
1043 static void
vSetColor(FILE * pFile,UCHAR ucFontColor)1044 vSetColor(FILE *pFile, UCHAR ucFontColor)
1045 {
1046 	ULONG	ulTmp, ulRed, ulGreen, ulBlue;
1047 
1048 	ulTmp = ulColor2Color(ucFontColor);
1049 	ulRed   = (ulTmp & 0x0000ff00) >> 8;
1050 	ulGreen = (ulTmp & 0x00ff0000) >> 16;
1051 	ulBlue  = (ulTmp & 0xff000000) >> 24;
1052 	vFPprintf(pFile, "%.3f %.3f %.3f rg\n",
1053 			ulRed / 255.0, ulGreen / 255.0, ulBlue / 255.0);
1054 } /* end of vSetColor */
1055 
1056 /*
1057  * vMove2NextLinePDF - move to the next line
1058  */
1059 void
vMove2NextLinePDF(diagram_type * pDiag,USHORT usFontSize)1060 vMove2NextLinePDF(diagram_type *pDiag, USHORT usFontSize)
1061 {
1062 	fail(pDiag == NULL);
1063 	fail(usFontSize < MIN_FONT_SIZE || usFontSize > MAX_FONT_SIZE);
1064 
1065 	pDiag->lYtop -= lComputeLeading(usFontSize);
1066 } /* end of vMove2NextLinePDF */
1067 
1068 /*
1069  * vSubstringPDF - print a sub string
1070  */
1071 void
vSubstringPDF(diagram_type * pDiag,char * szString,size_t tStringLength,long lStringWidth,UCHAR ucFontColor,USHORT usFontstyle,drawfile_fontref tFontRef,USHORT usFontSize,USHORT usMaxFontSize)1072 vSubstringPDF(diagram_type *pDiag,
1073 	char *szString, size_t tStringLength, long lStringWidth,
1074 	UCHAR ucFontColor, USHORT usFontstyle, drawfile_fontref tFontRef,
1075 	USHORT usFontSize, USHORT usMaxFontSize)
1076 {
1077 	size_t	tFontIndex;
1078 
1079 	fail(pDiag == NULL || szString == NULL);
1080 	fail(pDiag->pOutFile == NULL);
1081 	fail(pDiag->lXleft < 0);
1082 	fail(tStringLength != strlen(szString));
1083 	fail(usFontSize < MIN_FONT_SIZE || usFontSize > MAX_FONT_SIZE);
1084 	fail(usMaxFontSize < MIN_FONT_SIZE || usMaxFontSize > MAX_FONT_SIZE);
1085 	fail(usFontSize > usMaxFontSize);
1086 
1087 	if (szString[0] == '\0' || tStringLength == 0) {
1088 		return;
1089 	}
1090 
1091 	vMoveTo(pDiag, lComputeLeading(usMaxFontSize));
1092 	if (tFontRef != tFontRefCurr || usFontSize != usFontSizeCurr) {
1093 		tFontIndex = tGetFontIndex(tFontRef);
1094 		vFPprintf(pDiag->pOutFile, "/F%u %.1f Tf\n",
1095 			 1 + tFontIndex, (double)usFontSize / 2.0);
1096 		tFontRefCurr = tFontRef;
1097 		usFontSizeCurr = usFontSize;
1098 	}
1099 	if ((int)ucFontColor != iFontColorCurr) {
1100 		vSetColor(pDiag->pOutFile, ucFontColor);
1101 		iFontColorCurr = (int)ucFontColor;
1102 	}
1103 	vPrintPDF(pDiag->pOutFile, szString, tStringLength, usFontstyle);
1104 	pDiag->lXleft += lStringWidth;
1105 } /* end of vSubstringPDF */
1106 
1107 /*
1108  * Create an start of paragraph by moving the y-top mark
1109  */
1110 void
vStartOfParagraphPDF(diagram_type * pDiag,long lBeforeIndentation)1111 vStartOfParagraphPDF(diagram_type *pDiag, long lBeforeIndentation)
1112 {
1113 	fail(pDiag == NULL);
1114 	fail(lBeforeIndentation < 0);
1115 
1116 	pDiag->lXleft = 0;
1117 	pDiag->lYtop -= lMilliPoints2DrawUnits(lBeforeIndentation);
1118 } /* end of vStartOfParagraphPDF */
1119 
1120 /*
1121  * Create an end of paragraph by moving the y-top mark
1122  */
1123 void
vEndOfParagraphPDF(diagram_type * pDiag,USHORT usFontSize,long lAfterIndentation)1124 vEndOfParagraphPDF(diagram_type *pDiag,
1125 	USHORT usFontSize, long lAfterIndentation)
1126 {
1127 	fail(pDiag == NULL);
1128 	fail(pDiag->pOutFile == NULL);
1129 	fail(usFontSize < MIN_FONT_SIZE || usFontSize > MAX_FONT_SIZE);
1130 	fail(lAfterIndentation < 0);
1131 
1132 	if (pDiag->lXleft > 0) {
1133 		/* To the start of the line */
1134 		vMove2NextLinePDF(pDiag, usFontSize);
1135 	}
1136 
1137 	pDiag->lXleft = 0;
1138 	pDiag->lYtop -= lMilliPoints2DrawUnits(lAfterIndentation);
1139 } /* end of vEndOfParagraphPDF */
1140 
1141 /*
1142  * Create an end of page
1143  */
1144 void
vEndOfPagePDF(diagram_type * pDiag,BOOL bNewSection)1145 vEndOfPagePDF(diagram_type *pDiag, BOOL bNewSection)
1146 {
1147 	vMove2NextPage(pDiag, bNewSection);
1148 } /* end of vEndOfPagePDF */
1149