1 /*
2  * << H a r u --free pdf library >> -- PdfContents.cpp
3  *
4  * Copyright (c) 1999-2003 Takeshi Kanno <takeshi_kanno@est.hi-ho.ne.jp>
5  *
6  * Permission to use, copy, modify, distribute and sell this software
7  * and its documentation for any purpose is hereby granted without fee,
8  * provided that the above copyright notice appear in all copies and
9  * that both that copyright notice and this permission notice appear
10  * in supporting documentation.
11  * It is provided "as is" without express or implied warranty.
12  *
13  */
14 
15 #include <assert.h>
16 #include "libharu.h"
17 
18 /*----- PdfContents class ----------------------------------------------------*/
19 
PdfContents(PdfPage * page)20 PdfContents::PdfContents(PdfPage *page)
21         : PdfStream(page->GetXref())
22 {
23     /* initialize attributes to the default */
24     fPage = page;
25     fFontMgr = fPage->FontMgr();
26     fXObjectMgr = fPage->XObjectMgr();
27 
28     fWordSpace = PDF_DEF_WORDSPACE;
29     fCharSpace = PDF_DEF_CHARSPACE;
30     fFontSize = PDF_DEF_FONTSIZE;
31     fHScalling = PDF_DEF_HSCALING;
32     fTextLeading = PDF_DEF_LEADING;
33     fRenderingMode = PDF_DEF_RENDERING_MODE;
34     fTextRaise = PDF_DEF_RAISE;
35     fLineWidth = PDF_DEF_LINEWIDTH;
36     fLineCap = PDF_DEF_LINECAP;
37     fLineJoin = PDF_DEF_LINEJOIN;
38     fMiterLimit = PDF_DEF_MITERLIMIT;
39     fDashOn = 0;
40     fDashOff = 0;
41     fDashPhase = 0;
42     fFlatness = 0;
43 
44     fCurPoint.x = 0;
45     fCurPoint.y = 0;
46     fTextPoint.x = 0;
47     fTextPoint.y = 0;
48     fMatrix = PdfTextMatrix(1, 0, 0, 1, 0, 0);
49     fGMode = PDF_GMODE_PAGE_DESCRIPTION;
50 
51     fRGBFill.red = 0;
52     fRGBFill.green = 0;
53     fRGBFill.blue = 0;
54     fRGBStroke.red = 0;
55     fRGBStroke.green = 0;
56     fRGBStroke.blue = 0;
57     fGrayFill = 0;
58     fGrayStroke = 0;
59 }
60 
61 void
Init()62 PdfContents::Init()
63 {
64     GetXref()->AddObject(this);
65     fPage->AddElement("Contents", this);
66 
67     PdfNumber *length = new PdfNumber(0);
68     GetXref()->AddObject(length);
69     AddElement("Length", length);
70 }
71 
72 /* graphics mode checking macro */
73 #define GMODE_ERROR(method_name)  { \
74     throw PdfException(PDF_RUNTIME_ERROR, "ERROR: %s invalid graphics " \
75             "mode(%d).", method_name, (int)fGMode); \
76 }
77 
78 /* font checking macro */
79 
80 
81 
82 double
TextWidth(const char * text,int * numchars,int * numwords)83 PdfContents::TextWidth(const char* text, int* numchars, int* numwords)
84 {
85     if (fFont == NULL)
86         throw PdfException(PDF_RUNTIME_ERROR, "ERROR: PdfContents::TextWidth "
87                 "current font has not been set.");
88 
89     pdf_text_width pw = fFont->TextWidth(text);
90 
91     double w1 = pw.width * fFontSize / 1000;
92     double w2 = (pw.numchars - 1) * fCharSpace;
93     double w3 = (pw.numwords - 1) * fWordSpace;
94 
95     PDF_DEBUG_PRINT(("PdfContents::TextWidth pw.width=%d, pw.numchars=%d, "
96                "pw.numwords=%d\n", pw.width, pw.numchars, pw.numwords));
97 
98     if (numchars != NULL)
99         *numchars = pw.numchars;
100 
101     if (numwords != NULL)
102         *numwords = pw.numwords;
103 
104     return w1 + w2 + w3;
105 }
106 
107 void
CharWidths(const char * chars,double * widths)108 PdfContents::CharWidths(const char* chars, double* widths)
109 {
110     assert(chars);
111     assert(widths);
112 
113     if (fFont == NULL)
114         throw PdfException(PDF_RUNTIME_ERROR, "ERROR: PdfContents::CharWidths "
115                 "current font has not been set.");
116 
117     int len = strlen(chars);
118     unsigned int* w = new unsigned int[len];
119     unsigned int* pw = w;
120     double* pf = widths;
121 
122     fFont->TextWidths(chars, w);
123     for (int i = 0; i < len; i++) {
124         *pf = *pw * fFontSize / 1000;
125         pf++;
126         pw++;
127     }
128 
129     delete[] w;
130 }
131 
132 unsigned int
MeasureText(const char * text,double width,double * realwidth)133 PdfContents::MeasureText(const char* text, double width, double* realwidth)
134 {
135     assert(text);
136     assert(width > 0);
137 
138     if (fFont == NULL)
139         throw PdfException(PDF_RUNTIME_ERROR, "ERROR: "
140                 "PdfContents::MeasureText current font has not been set.");
141 
142     return fFont->MeasureText(text, width, fFontSize, fCharSpace,
143             fWordSpace, realwidth);
144 }
145 
146 void
TextOut(double x,double y,const char * text)147 PdfContents::TextOut(double x, double y, const char* text)
148 {
149     BeginText();
150     MoveTextPos(x, y);
151     ShowText(text);
152     EndText();
153 }
154 
155 void
TextRect(const char * text,pdf_rect rect,int max_len)156 PdfContents::TextRect(const char* text, pdf_rect rect, int max_len)
157 {
158     char *tmp;
159     double w = rect.right - rect.left;
160     int len = strlen(text);
161     if (len == 0)
162         return;
163 
164     /* copy the text to temporary buffer */
165     tmp = new char[len + 1];
166     int j = 0;
167     for (int i = 0; i < len; i++) {
168         if (text[i] != 0x0a) {
169             tmp[j] = text[i];
170             j++;
171         }
172     }
173     len = j;
174 
175     /* replace \n charactor to 0x00 */
176     for (int k = 0; k < len; k++)
177         if (tmp[k] == 0x0d)
178             tmp[k] = 0x00;
179 
180     try {
181         char* buf = new char[max_len + 1];
182 
183         try {
184             BeginText();
185             double ascent = Font()->Ascent() * FontSize() / 1000;
186             MoveTextPos(rect.left, rect.top - ascent);
187 
188             char* pstr = tmp;
189             char* end = tmp + len;
190 
191             while (pstr < end) {
192                 if (*pstr == 0x00)
193                     MoveToNextLine();
194                 else {
195                     /* calcurate how many charactor of the text can be
196                      * included in a line
197                      */
198                     int cnt = MeasureText(pstr, w);
199                     if (cnt <= max_len) {
200                         memcpy(buf, pstr, cnt);
201                         buf[cnt] = 0x00;
202                     } else {
203                         memcpy(buf, pstr, max_len);
204                         buf[max_len] = 0x00;
205                     }
206                     ShowText(buf);
207                     MoveToNextLine();
208 
209                     if ((TextPos()).y < rect.bottom + TextLeading())
210                         break;
211                     pstr += cnt;
212                 }
213             }
214 
215             EndText();
216         } catch (...) {
217             delete[] buf;
218             throw;
219         }
220         delete[] buf;
221     } catch (...) {
222         delete[] tmp;
223         throw;
224     }
225     delete[] tmp;
226 }
227 
228 /*----- General graphics state -------------------------------------*/
229 
230 void
SetLineWidth(double linewidth)231 PdfContents::SetLineWidth(double linewidth)
232 {
233     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION && fGMode != PDF_GMODE_TEXT_OBJECT)
234         GMODE_ERROR("SetLineWidth")
235 
236     if (linewidth < 0)
237         throw PdfException(PDF_ERR_OUT_OF_RANGE,
238                 "ERROR: SetLineWidth invalid value(%d).", (int)linewidth);
239 
240     *GetStream() << linewidth
241              << " w\012";
242     fLineWidth = linewidth;
243     return;
244 }
245 
246 void
SetLineCap(pdf_line_cap_style linecap)247 PdfContents::SetLineCap(pdf_line_cap_style linecap)
248 {
249     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION && fGMode != PDF_GMODE_TEXT_OBJECT)
250         GMODE_ERROR("SetLineCap")
251 
252     *GetStream() << (unsigned int)linecap
253              << " J\012";
254     fLineCap = linecap;
255     return;
256 }
257 
258 void
SetLineJoin(pdf_line_join_style linejoin)259 PdfContents::SetLineJoin(pdf_line_join_style linejoin)
260 {
261     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION && fGMode != PDF_GMODE_TEXT_OBJECT)
262         GMODE_ERROR("SetLineJoin")
263 
264     *GetStream() << (unsigned int)linejoin
265              << " j\012";
266     fLineJoin = linejoin;
267     return;
268 }
269 
270 void
SetMiterLimit(double miterlimit)271 PdfContents::SetMiterLimit(double miterlimit)
272 {
273     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION && fGMode != PDF_GMODE_TEXT_OBJECT)
274         GMODE_ERROR("SetMiterLimit")
275 
276     if (miterlimit < 1)
277         throw PdfException(PDF_ERR_OUT_OF_RANGE,
278             "ERROR: PdfContents::SetLineJoin (%d)"
279             ")--linejoin must be more than or equal to 1.", (int)miterlimit);
280 
281     *GetStream() << miterlimit
282              << " M\012";
283     fMiterLimit = miterlimit;
284     return;
285 }
286 
287 void
SetDash(unsigned int on,unsigned int off,unsigned int phase)288 PdfContents::SetDash(unsigned int on, unsigned int off, unsigned int phase)
289 {
290     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION && fGMode != PDF_GMODE_TEXT_OBJECT)
291         GMODE_ERROR("SetDash")
292 
293     fDashOn = on;
294     if (on == off || on == 0)
295         fDashOff = 0;
296     else
297         fDashOff = off;
298     fDashPhase = phase;
299 
300     *GetStream() << '[';
301     if (fDashOn > 0) {
302         *GetStream() << fDashOn;
303         if (fDashOff > 0) {
304             *GetStream() << ' ' << fDashOff;
305         }
306     }
307     *GetStream() << ']'
308              << fDashPhase
309              << " d\012";
310     return;
311 }
312 
313 void
SetFlat(unsigned int flatness)314 PdfContents::SetFlat(unsigned int flatness)
315 {
316     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION && fGMode != PDF_GMODE_TEXT_OBJECT)
317         GMODE_ERROR("SetFlat")
318 
319     if (flatness > 100)
320         throw PdfException(PDF_ERR_OUT_OF_RANGE,
321             "ERROR: PdfContents::SetFlat flatness must "
322             "be between 0 to 100.(%d).\n", flatness);
323 
324     fFlatness = flatness;
325     *GetStream() << flatness
326              << " i\012";
327     return;
328 }
329 
330 /*----- Special graphics state -------------------------------------*/
331 
332 void
GSave()333 PdfContents::GSave()
334 {
335     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION)
336         GMODE_ERROR("GSave")
337 
338     *GetStream() << "q\012";
339     return;
340 }
341 
342 void
GRestore()343 PdfContents::GRestore()
344 {
345     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION)
346         GMODE_ERROR("GRestore")
347 
348     *GetStream() << "Q\012";
349     return;
350 }
351 
352 void
Concat(double a,double b,double c,double d,double e,double f)353 PdfContents::Concat(double a, double b, double c, double d, double e, double f)
354 {
355     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION)
356         GMODE_ERROR("Concat")
357 
358     *GetStream() << a << ' '
359              << b << ' '
360              << c << ' '
361              << d << ' '
362              << e << ' '
363              << f << " cm\012";
364     return;
365 }
366 
367 /*----- Path construction ------------------------------------------*/
368 
369 void
MoveTo(double x,double y)370 PdfContents::MoveTo(double x, double y)
371 {
372     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION && fGMode != PDF_GMODE_PATH_OBJECT)
373         GMODE_ERROR("MoveTo")
374 
375     *GetStream() << x << ' '
376              << y << " m\012";
377     fCurPoint.x = x;
378     fCurPoint.y = y;
379     fStartPoint.x = x;
380     fStartPoint.y = y;
381     fGMode = PDF_GMODE_PATH_OBJECT;
382     return;
383 }
384 
385 void
LineTo(double x,double y)386 PdfContents::LineTo(double x, double y)
387 {
388     if (fGMode != PDF_GMODE_PATH_OBJECT)
389         GMODE_ERROR("LineTo")
390 
391     *GetStream() << x << ' '
392              << y << " l\012";
393     fCurPoint.x = x;
394     fCurPoint.y = y;
395     return;
396 }
397 
398 void
CurveTo(double x1,double y1,double x2,double y2,double x3,double y3)399 PdfContents::CurveTo(double x1, double y1, double x2,
400         double y2, double x3, double y3)
401 {
402     if (fGMode != PDF_GMODE_PATH_OBJECT)
403         GMODE_ERROR("CurveTo")
404 
405     *GetStream() << x1 << ' '
406              << y1 << ' '
407              << x2 << ' '
408              << y2 << ' '
409              << x3 << ' '
410              << y3 << " c\012";
411     fCurPoint.x = x3;
412     fCurPoint.y = y3;
413     return;
414 }
415 
416 void
CurveTo2(double x2,double y2,double x3,double y3)417 PdfContents::CurveTo2(double x2, double y2, double x3, double y3)
418 {
419     if (fGMode != PDF_GMODE_PATH_OBJECT)
420         GMODE_ERROR("CurveTo2")
421 
422     *GetStream() << x2 << ' '
423              << y2 << ' '
424              << x3 << ' '
425              << y3 << " v\012";
426     fCurPoint.x = x3;
427     fCurPoint.y = y3;
428     return;
429 }
430 
431 void
CurveTo3(double x1,double y1,double x3,double y3)432 PdfContents::CurveTo3(double x1, double y1, double x3,
433                 double y3)
434 {
435     if (fGMode != PDF_GMODE_PATH_OBJECT)
436         GMODE_ERROR("CurveTo3")
437 
438     *GetStream() << x1 << ' '
439              << y1 << ' '
440              << x3 << ' '
441              << y3 << " y\012";
442     fCurPoint.x = x3;
443     fCurPoint.y = y3;
444     return;
445 }
446 
447 void
ClosePath()448 PdfContents::ClosePath()
449 {
450     if (fGMode != PDF_GMODE_PATH_OBJECT)
451         GMODE_ERROR("ClosePath")
452 
453     *GetStream() << "h\012";
454     fCurPoint = fStartPoint;
455     return;
456 }
457 
458 void
Rectangle(double x,double y,double width,double height)459 PdfContents::Rectangle(double x, double y, double width,
460                 double height)
461 {
462     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION && fGMode != PDF_GMODE_PATH_OBJECT)
463         GMODE_ERROR("Rectangle")
464 
465     *GetStream() << x << ' '
466              << y << ' '
467              << width << ' '
468              << height << " re\012";
469     fCurPoint.x = x;
470     fCurPoint.y = y;
471     fGMode = PDF_GMODE_PATH_OBJECT;
472     return;
473 }
474 
475 /*----- Path painting ----------------------------------------------*/
476 
477 void
Stroke()478 PdfContents::Stroke()
479 {
480     if (fGMode != PDF_GMODE_PATH_OBJECT && fGMode != PDF_GMODE_CLIPPING_PATH)
481         GMODE_ERROR("Stroke")
482 
483     *GetStream() << "S\012";
484     fGMode = PDF_GMODE_PAGE_DESCRIPTION;
485     return;
486 }
487 
488 void
ClosePathStroke()489 PdfContents::ClosePathStroke()
490 {
491     if (fGMode != PDF_GMODE_PATH_OBJECT && fGMode != PDF_GMODE_CLIPPING_PATH)
492         GMODE_ERROR("ClosePathStroke")
493 
494     *GetStream() << "s\012";
495     fGMode = PDF_GMODE_PAGE_DESCRIPTION;
496     return;
497 }
498 
499 void
Fill()500 PdfContents::Fill()
501 {
502     if (fGMode != PDF_GMODE_PATH_OBJECT && fGMode != PDF_GMODE_CLIPPING_PATH)
503         GMODE_ERROR("Fill")
504 
505     *GetStream() << "f\012";
506     fGMode = PDF_GMODE_PAGE_DESCRIPTION;
507     return;
508 }
509 
510 void
Eofill()511 PdfContents::Eofill()
512 {
513     if (fGMode != PDF_GMODE_PATH_OBJECT && fGMode != PDF_GMODE_CLIPPING_PATH)
514         GMODE_ERROR("Eofill")
515 
516     *GetStream() << "f*\012";
517     fGMode = PDF_GMODE_PAGE_DESCRIPTION;
518     return;
519 }
520 
521 void
FillStroke()522 PdfContents::FillStroke()
523 {
524     if (fGMode != PDF_GMODE_PATH_OBJECT && fGMode != PDF_GMODE_CLIPPING_PATH)
525         GMODE_ERROR("FillStroke")
526 
527     *GetStream() << "B\012";
528     fGMode = PDF_GMODE_PAGE_DESCRIPTION;
529     return;
530 }
531 
532 void
EofillStroke()533 PdfContents::EofillStroke()
534 {
535     if (fGMode != PDF_GMODE_PATH_OBJECT && fGMode != PDF_GMODE_CLIPPING_PATH)
536         GMODE_ERROR("EofillStroke")
537 
538     *GetStream() << "B*\012";
539     fGMode = PDF_GMODE_PAGE_DESCRIPTION;
540     return;
541 }
542 
543 void
ClosePathFillStroke()544 PdfContents::ClosePathFillStroke()
545 {
546     if (fGMode != PDF_GMODE_PATH_OBJECT && fGMode != PDF_GMODE_CLIPPING_PATH)
547         GMODE_ERROR("ClosePathFillStroke")
548 
549     *GetStream() << "b\012";
550     fGMode = PDF_GMODE_PAGE_DESCRIPTION;
551     return;
552 }
553 
554 void
ClosePathEofillStroke()555 PdfContents::ClosePathEofillStroke()
556 {
557     if (fGMode != PDF_GMODE_PATH_OBJECT && fGMode != PDF_GMODE_CLIPPING_PATH)
558         GMODE_ERROR("ClosePathEofillStroke")
559 
560     *GetStream() << "b*\012";
561     fGMode = PDF_GMODE_PAGE_DESCRIPTION;
562     return;
563 }
564 
565 void
EndPath()566 PdfContents::EndPath()
567 {
568     if (fGMode != PDF_GMODE_PATH_OBJECT && fGMode != PDF_GMODE_CLIPPING_PATH)
569         GMODE_ERROR("EndPath")
570 
571     *GetStream() << "n\012";
572     fCurPoint.x = 0;  //
573     fCurPoint.y = 0;  //
574     fGMode = PDF_GMODE_PAGE_DESCRIPTION;
575     return;
576 }
577 
578 /*----- Clipping paths ---------------------------------------------*/
579 
580 void
Clip()581 PdfContents::Clip()
582 {
583     if (fGMode != PDF_GMODE_PATH_OBJECT)
584         GMODE_ERROR("Clip")
585 
586     *GetStream() << "W\012";
587     fGMode = PDF_GMODE_CLIPPING_PATH;
588     return;
589 }
590 
591 void
EoClip()592 PdfContents::EoClip()
593 {
594     if (fGMode != PDF_GMODE_PATH_OBJECT)
595         GMODE_ERROR("Eolip")
596 
597     *GetStream() << "W*\012";
598     fGMode = PDF_GMODE_CLIPPING_PATH;
599     return;
600 }
601 
602 /*----- Text object ------------------------------------------------*/
603 
604 void
BeginText()605 PdfContents::BeginText()
606 {
607     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION)
608         GMODE_ERROR("BeginText")
609 
610     fPage->AddProcSet(PDF_PROCSET_TEXT);
611 
612     *GetStream() << "BT\012";
613     fTextPoint.x = 0;
614     fTextPoint.y = 0;
615     fMatrix = PdfTextMatrix(1, 0, 0, 1, 0, 0);
616 
617     fGMode = PDF_GMODE_TEXT_OBJECT;
618     return;
619 }
620 
621 void
EndText()622 PdfContents::EndText()
623 {
624     if (fGMode != PDF_GMODE_TEXT_OBJECT)
625         GMODE_ERROR("EndText")
626 
627     *GetStream() << "ET\012";
628 
629     fGMode = PDF_GMODE_PAGE_DESCRIPTION;
630     return;
631 }
632 
633 /*----- Text state -------------------------------------------------*/
634 
635 void
SetCharSpace(double value)636 PdfContents::SetCharSpace(double value)
637 {
638     if (fGMode != PDF_GMODE_TEXT_OBJECT && fGMode != PDF_GMODE_PAGE_DESCRIPTION)
639         GMODE_ERROR("SetCharSpace")
640 
641     if (value < PDF_MIN_CHARSPACE || value > PDF_MAX_CHARSPACE)
642         throw PdfException(PDF_ERR_OUT_OF_RANGE,
643             "ERROR: SetCharSpace out of range(%f)", value);
644 
645     *GetStream() << value << " "
646              << "Tc\012";
647     fCharSpace = value;
648     return;
649 }
650 
651 void
SetWordSpace(double value)652 PdfContents::SetWordSpace(double value)
653 {
654     if (fGMode != PDF_GMODE_TEXT_OBJECT && fGMode != PDF_GMODE_PAGE_DESCRIPTION)
655         GMODE_ERROR("SetWordSpace")
656 
657     if (value < PDF_MIN_WORDSPACE || value > PDF_MAX_WORDSPACE)
658         throw PdfException(PDF_ERR_OUT_OF_RANGE,
659             "ERROR: SetWordSpace out of range(%f)", value);
660 
661     *GetStream() << value << " "
662              << "Tw\012";
663     fWordSpace = value;
664     return;
665 }
666 
667 void
SetHorizontalScalling(double value)668 PdfContents::SetHorizontalScalling(double value)
669 {
670     if (fGMode != PDF_GMODE_TEXT_OBJECT && fGMode != PDF_GMODE_PAGE_DESCRIPTION)
671         GMODE_ERROR("SetHorizontalScalling")
672 
673     if (value < PDF_MIN_HORIZONTALSCALING ||
674         value > PDF_MAX_HORIZONTALSCALING) {
675         throw PdfException(PDF_ERR_OUT_OF_RANGE,
676                 "ERROR: SetHorizontalScalling out of range(%f)", value);
677     }
678 
679     *GetStream() << value << " "
680              << "Tz\012";
681     fHScalling = value;
682     return;
683 }
684 
685 void
SetTextLeading(double value)686 PdfContents::SetTextLeading(double value)
687 {
688     if (fGMode != PDF_GMODE_TEXT_OBJECT && fGMode != PDF_GMODE_PAGE_DESCRIPTION)
689         GMODE_ERROR("SetTextLeading")
690 
691     *GetStream() << value << " "
692              << "TL\012";
693     fTextLeading = value;
694     return;
695 }
696 
697 void
SetFontAndSize(const char * fontname,double size)698 PdfContents::SetFontAndSize(const char* fontname, double size)
699 {
700     assert(fFontMgr);
701 
702     PdfFont* font = fFontMgr->GetFont(fontname);
703     if (font == NULL) {
704         throw PdfException(PDF_RUNTIME_ERROR,
705             "ERROR: font[%s] is not found.", fontname);
706     }
707 
708     SetFontAndSize(font, size);
709 }
710 
711 void
SetFontAndSize(PdfFont * font,double size)712 PdfContents::SetFontAndSize(PdfFont* font, double size)
713 {
714     if (font == NULL)
715         throw PdfException(PDF_RUNTIME_ERROR,
716                 "ERROR: Invalid paramator[font].");
717     if (size <= 0)
718         throw PdfException(PDF_RUNTIME_ERROR,
719                 "ERROR: Invalid font size[%f].", size);
720 
721     fFont = font;
722     const char* local_fontname = fPage->GetFontName(font);
723     if (local_fontname != NULL)
724         WriteEscapeName(GetStream(), local_fontname);
725     else {
726         char tmp_fontname[20];
727 
728 #ifdef __WIN32__
729         _snprintf(tmp_fontname, 20, "F%d", (int)fPage->CountFonts());
730 #else
731         snprintf(tmp_fontname, 20, "F%d", (int)fPage->CountFonts());
732 #endif
733 
734         fPage->AddFont(font, tmp_fontname);
735         WriteEscapeName(GetStream(), tmp_fontname);
736     }
737 
738     *GetStream() << " "
739              << size
740              << " Tf\012";
741 
742     fFontSize = size;
743     return;
744 }
745 
746 void
SetTextRenderingMode(pdf_text_rendering_mode mode)747 PdfContents::SetTextRenderingMode(pdf_text_rendering_mode mode)
748 {
749     if (fGMode != PDF_GMODE_TEXT_OBJECT && fGMode != PDF_GMODE_PAGE_DESCRIPTION)
750         GMODE_ERROR("SetTextRenderingMode")
751 
752     *GetStream() << (int)mode
753              << " Tr\012";
754     fRenderingMode = mode;
755     return;
756 }
757 
758 void
SetTextRaise(double value)759 PdfContents::SetTextRaise(double value)
760 {
761     if (fGMode != PDF_GMODE_TEXT_OBJECT && fGMode != PDF_GMODE_PAGE_DESCRIPTION)
762         GMODE_ERROR("SetTextRaise")
763 
764     *GetStream() << value
765              << " Ts\012";
766     fTextRaise = value;
767     return;
768 }
769 
770 /*--- Text positioning -----------------------------------------------------*/
771 
772 void
MoveTextPos(double tx,double ty)773 PdfContents::MoveTextPos(double tx, double ty)
774 {
775     if (fGMode != PDF_GMODE_TEXT_OBJECT)
776         GMODE_ERROR("MoveTextPos")
777 
778     *GetStream() << tx << " "
779              << ty
780              << " Td\012";
781     fMatrix.x += tx;
782     fMatrix.y += ty;
783     fTextPoint.x = fMatrix.x;
784     fTextPoint.y += ty;
785     return;
786 }
787 
788 void
MoveTextPos2(double tx,double ty)789 PdfContents::MoveTextPos2(double tx, double ty)
790 {
791     if (fGMode != PDF_GMODE_TEXT_OBJECT)
792         GMODE_ERROR("MoveTextPos2")
793 
794     *GetStream() << tx << " "
795              << ty
796              << " TD\012";
797     fMatrix.x += tx;
798     fMatrix.y += ty;
799     fTextPoint.x = fMatrix.x;
800     fTextPoint.y += ty;
801     fTextLeading = -ty;
802     return;
803 }
804 
805 void
SetTextMatrix(double a,double b,double c,double d,double x,double y)806 PdfContents::SetTextMatrix(double a, double b, double c,
807                         double d, double x, double y)
808 {
809     if (fGMode != PDF_GMODE_TEXT_OBJECT)
810         GMODE_ERROR("SetTextMatrix")
811 
812     *GetStream() << a << " "
813              << b << " "
814              << c << " "
815              << d << " "
816              << x << " "
817              << y
818              << " Tm\012";
819     fMatrix.a = a;
820     fMatrix.b = b;
821     fMatrix.c = c;
822     fMatrix.d = d;
823     fMatrix.x = x;
824     fMatrix.y = y;
825     fTextPoint.x = fMatrix.x;
826     fTextPoint.y = fMatrix.y;
827     return;
828 }
829 
830 void
MoveToNextLine()831 PdfContents::MoveToNextLine()
832 {
833     if (fGMode != PDF_GMODE_TEXT_OBJECT)
834         GMODE_ERROR("MoveToNextLine")
835 
836     *GetStream() << "T*\012";
837 
838     /* calculate the reference point of text */
839     fMatrix.x += fTextLeading * fMatrix.b;
840     fMatrix.y -= fTextLeading * fMatrix.a;
841 
842     /* set text point to the start of new line. */
843     fTextPoint.x = fMatrix.x;
844     fTextPoint.y = fMatrix.y;
845 
846     PDF_DEBUG_PRINT(("PdfContents::MoveToNextLine -- x=%f, y=%f\n"
847                 , fTextPoint.x, fTextPoint.y));
848 
849     return;
850 }
851 
852 /*--- Text showing ---------------------------------------------------------*/
853 void
ShowText(const char * text)854 PdfContents::ShowText(const char* text)
855 {
856     if (fGMode != PDF_GMODE_TEXT_OBJECT)
857         GMODE_ERROR("ShowText")
858 
859     if (text == NULL || strlen(text) < 1)
860         return;
861 
862     WriteEscapeText(GetStream(), text);
863     *GetStream() << " Tj\012";
864 
865     /* set text point to the end of this line */
866     double w = TextWidth(text);
867 
868     if (fFont->WritingMode() == PDF_WMODE_HORIZONTAL) {
869         fTextPoint.x += w * fMatrix.a;
870         fTextPoint.y += w * fMatrix.b;
871     } else {
872         fTextPoint.y -= w * fMatrix.a;
873         fTextPoint.x -= w * fMatrix.b;
874     }
875     return;
876 }
877 
878 void
ShowTextNextLine(const char * text)879 PdfContents::ShowTextNextLine(const char* text)
880 {
881     if (fGMode != PDF_GMODE_TEXT_OBJECT)
882         GMODE_ERROR("ShowTextNextLine")
883 
884     if (text == NULL || strlen(text) < 1)
885         return;
886 
887     /* calculate the reference point of text. */
888     fMatrix.x += fTextLeading * fMatrix.b;
889     fMatrix.y -= fTextLeading * fMatrix.a;
890 
891     /* set text point to the start of new line. */
892     fTextPoint.x = fMatrix.x;
893     fTextPoint.y = fMatrix.y;
894 
895     WriteEscapeText(GetStream(), text);
896     *GetStream() << " '\012";
897 
898     /* set text point to the end of this line */
899     double w = TextWidth(text);
900 
901     if (fFont->WritingMode() == PDF_WMODE_HORIZONTAL) {
902         fTextPoint.x += w * fMatrix.a;
903         fTextPoint.y += w * fMatrix.b;
904     } else {
905         fTextPoint.y -= w * fMatrix.a;
906         fTextPoint.x -= w * fMatrix.b;
907     }
908 
909     return;
910 }
911 
912 void
ShowTextNextLine(double aw,double ac,const char * text)913 PdfContents::ShowTextNextLine(double aw, double ac,
914         const char* text)
915 {
916     if (fGMode != PDF_GMODE_TEXT_OBJECT)
917         GMODE_ERROR("ShowTextNextLine")
918 
919     if (text == NULL || strlen(text) < 1)
920         return;
921 
922     fWordSpace = aw;
923     fCharSpace = ac;
924 
925     /* calculate the reference point of text */
926     fMatrix.x += fTextLeading * fMatrix.b;
927     fMatrix.y -= fTextLeading * fMatrix.a;
928 
929     /* set text point to the start of new line. */
930     fTextPoint.x = fMatrix.x;
931     fTextPoint.y = fMatrix.y;
932 
933     *GetStream() << aw << " "
934              << ac << " ";
935     WriteEscapeText(GetStream(), text);
936     *GetStream() << " \"\012";
937 
938     /* set text point to the end of this line */
939     double w = TextWidth(text);
940 
941     if (fFont->WritingMode() == PDF_WMODE_HORIZONTAL) {
942         fTextPoint.x += w * fMatrix.a;
943         fTextPoint.y += w * fMatrix.b;
944     } else {
945         fTextPoint.y -= w * fMatrix.a;
946         fTextPoint.x -= w * fMatrix.b;
947     }
948 
949     return;
950 }
951 
952 /*--- Color showing --------------------------------------------------------*/
953 
954 void
SetGrayFill(double gray)955 PdfContents::SetGrayFill(double gray)
956 {
957     if (fGMode != PDF_GMODE_TEXT_OBJECT && fGMode != PDF_GMODE_PAGE_DESCRIPTION)
958         GMODE_ERROR("SetGrayFill")
959 
960     if (gray < 0 || gray > 1)
961         throw PdfException(PDF_ERR_OUT_OF_RANGE,
962             "ERROR: SetGrayFill out of range(%f)", gray);
963 
964     *GetStream() << gray
965              << " g\012";
966 
967     fGrayFill = gray;
968 
969     return;
970 }
971 
972 void
SetGrayStroke(double gray)973 PdfContents::SetGrayStroke(double gray)
974 {
975     if (fGMode != PDF_GMODE_TEXT_OBJECT && fGMode != PDF_GMODE_PAGE_DESCRIPTION)
976         GMODE_ERROR("SetGrayStroke")
977 
978     if (gray < 0 || gray > 1)
979         throw PdfException(PDF_ERR_OUT_OF_RANGE,
980             "ERROR: SetGrayStroke out of range(%f).", gray);
981 
982     *GetStream() << gray
983              << " G\012";
984 
985     fGrayStroke = gray;
986 
987     return;
988 }
989 
990 void
SetRGBFill(int r,int g,int b)991 PdfContents::SetRGBFill(int r,  int g, int b)
992 {
993     SetRGBFill((double)r / 255, (double)g / 255, (double)b / 255);
994 }
995 
996 void
SetRGBFill(double r,double g,double b)997 PdfContents::SetRGBFill(double r, double g, double b)
998 {
999     if (fGMode != PDF_GMODE_TEXT_OBJECT && fGMode != PDF_GMODE_PAGE_DESCRIPTION)
1000         GMODE_ERROR("SetRGBFill")
1001 
1002     if (r < 0 || r > 1 ||
1003         g < 0 || g > 1 ||
1004         b < 0 || b > 1) {
1005         throw PdfException(PDF_ERR_OUT_OF_RANGE,
1006             "ERROR: SetRGBFill out of range(%f:%f:%f).", r, g, b);
1007     }
1008 
1009     fRGBFill.red = r;
1010     fRGBFill.green = g;
1011     fRGBFill.blue = b;
1012 
1013     *GetStream() << r
1014              << " "
1015              << g
1016              << " "
1017              << b
1018              << " rg\012";
1019 
1020     return;
1021 }
1022 
1023 void
SetRGBStroke(int r,int g,int b)1024 PdfContents::SetRGBStroke(int r, int g, int b)
1025 {
1026     SetRGBStroke((double)r / 255, (double)g / 255, (double)b / 255);
1027 }
1028 
1029 void
SetRGBStroke(double r,double g,double b)1030 PdfContents::SetRGBStroke(double r, double g, double b)
1031 {
1032     if (fGMode != PDF_GMODE_TEXT_OBJECT && fGMode != PDF_GMODE_PAGE_DESCRIPTION)
1033         GMODE_ERROR("SetRGBStroke")
1034 
1035     if (r < 0 || r > 1 ||
1036         g < 0 || g > 1 ||
1037         b < 0 || b > 1) {
1038         throw PdfException(PDF_ERR_OUT_OF_RANGE,
1039                 "ERROR: SetRGBStroke out of range(%f:%f:%f).", r, g, b);
1040     }
1041 
1042     fRGBStroke.red = r;
1043     fRGBStroke.green = g;
1044     fRGBStroke.blue = b;
1045 
1046     *GetStream() << r
1047              << " "
1048              << g
1049              << " "
1050              << b
1051              << " RG\012";
1052 
1053     return;
1054 }
1055 
1056 void
SetCMYKFill(double c,double m,double y,double k)1057 PdfContents::SetCMYKFill(double c, double m, double y, double k)
1058 {
1059     if (fGMode != PDF_GMODE_TEXT_OBJECT && fGMode != PDF_GMODE_PAGE_DESCRIPTION)
1060         GMODE_ERROR("SetCMYKFill")
1061 
1062     if (c < 0 || c > 1 ||
1063         m < 0 || m > 1 ||
1064         y < 0 || y > 1 ||
1065         k < 0 || k > 1) {
1066         throw PdfException(PDF_ERR_OUT_OF_RANGE,
1067             "ERROR: SetCYMKFill out of range(%f:%f:%f:%f).", c, m, y, k);
1068     }
1069 
1070     *GetStream() << c
1071              << " "
1072              << m
1073              << " "
1074              << y
1075              << " "
1076              << k
1077              << " k\012";
1078 
1079     return;
1080 }
1081 
1082 void
SetCMYKStroke(double c,double m,double y,double k)1083 PdfContents::SetCMYKStroke(double c, double m, double y, double k)
1084 {
1085     if (fGMode != PDF_GMODE_TEXT_OBJECT && fGMode != PDF_GMODE_PAGE_DESCRIPTION)
1086         GMODE_ERROR("SetCMYKStroke")
1087 
1088     if (c < 0 || c > 1 ||
1089         m < 0 || m > 1 ||
1090         y < 0 || y > 1 ||
1091         k < 0 || k > 1) {
1092         throw PdfException(PDF_ERR_OUT_OF_RANGE,
1093             "ERROR: SetCMYKStroke out of range(%f:%f:%f:%f).", c, m, y, k);
1094     }
1095 
1096     *GetStream() << c
1097              << " "
1098              << m
1099              << " "
1100              << y
1101              << " "
1102              << k
1103              << " K\012";
1104 
1105     return;
1106 }
1107 
1108 /*----------------------------------------------------------------------------*/
1109 /*------ XObjects ------------------------------------------------------------*/
1110 
1111 void
ExecuteXObject(const char * name)1112 PdfContents::ExecuteXObject(const char *name)
1113 {
1114     assert(fXObjectMgr);
1115 
1116     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION)
1117         GMODE_ERROR("ExecuteXObject")
1118 
1119     /* find x-object from XObjectManager by name */
1120     PdfXObject* obj = fXObjectMgr->GetXObject(name);
1121     if (obj == NULL)
1122         throw PdfException(PDF_RUNTIME_ERROR,
1123             "ERROR: XObject[%s] is not found.", name);
1124 
1125     ExecuteXObject(obj);
1126 }
1127 
1128 void
ExecuteXObject(PdfXObject * obj)1129 PdfContents::ExecuteXObject(PdfXObject* obj)
1130 {
1131     assert(fXObjectMgr);
1132 
1133     if (fGMode != PDF_GMODE_PAGE_DESCRIPTION)
1134         GMODE_ERROR("ExecuteXObject");
1135 
1136     /* check the xobject is valid or not. */
1137     if (obj == NULL || obj->Name() == NULL)
1138         throw PdfException(PDF_RUNTIME_ERROR,
1139                 "ERROR: Invalid XObject.");
1140 
1141     /* get the local-name which is used in page-object */
1142     const char* local_name = fPage->GetXObjectName(obj);
1143 
1144     if (local_name != NULL)
1145         WriteEscapeName(GetStream(), local_name);
1146     else {
1147         fPage->AddXObject(obj, obj->Name());
1148         WriteEscapeName(GetStream(), obj->Name());
1149     }
1150 
1151     *GetStream() << " Do\012";
1152 }
1153 
1154 /*----------------------------------------------------------------------------*/
1155 
1156