PLplot  5.9.9
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
pdf.c
Go to the documentation of this file.
1 // $Id: pdf.c 12401 2013-07-01 04:22:35Z airwin $
2 //
3 // PLplot driver for PDF based on the haru library http://www.libharu.org.
4 //
5 // Copyright (C) 2006, 2008 Werner Smekal
6 //
7 // This file is part of PLplot.
8 //
9 // PLplot is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU Library General Public License as published
11 // by the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // PLplot is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU Library General Public License for more details.
18 //
19 // You should have received a copy of the GNU Library General Public License
20 // along with PLplot; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 //
24 
25 // TODO:
26 // - page orientation
27 // - text clipping
28 //
29 
30 //--------------------------------------------------------------------------
31 // Header files, defines and local variables
32 //--------------------------------------------------------------------------
33 #include "plDevs.h"
34 
35 #ifdef PLD_pdf
36 
37 #include <stdarg.h>
38 #include <math.h>
39 #include <setjmp.h>
40 
41 #include "hpdf/hpdf.h"
42 
43 // PLplot header files
44 #define DEBUG
45 #define NEED_PLDEBUG
46 #include "plplotP.h"
47 #include "drivers.h"
48 #include "plunicode-type1.h"
49 #include "plfci-type1.h"
50 
51 // Workaround for caseless string comparison
52 #ifndef WIN32
53  #define stricmp strcasecmp
54  #define strnicmp strncasecmp
55 #endif
56 
57 // constants
58 
59 // We define a virtual page and scale it down to the
60 // paper size chosen by the user (A4 is default).
61 //
62 
63 // Default dimensions of the canvas (in inches) and DPI
64 #define CANVAS_WIDTH ( 50.0 )
65 #define CANVAS_HEIGHT ( 37.5 )
66 #define DEVICE_PIXELS_PER_INCH ( 72 )
67 
68 // mm per inch
69 #define MM_PER_INCH ( 25.4 )
70 
71 // pixels per mm
72 #define DEVICE_PIXELS_PER_MM ( DEVICE_PIXELS_PER_INCH / MM_PER_INCH )
73 
74 // maximum string length for own font handling
75 #define MAX_STRING_LEN 1000
76 
77 // container for device specific data
78 typedef struct
79 {
80  HPDF_Doc pdf;
81  HPDF_Page page;
82  HPDF_PageSizes pageSize;
83  FILE *pdfFile;
84  PLFLT scalex, scaley;
85 
86  // font variables
87  HPDF_Font m_font;
88  int nlookup, if_symbol_font;
89  const Unicode_to_Type1_table *lookup;
90  HPDF_REAL fontSize;
91  HPDF_REAL fontScale;
92  HPDF_REAL textWidth, textHeight;
93  HPDF_REAL yOffset;
94  HPDF_REAL textRed, textGreen, textBlue;
95 } pdfdev;
96 
97 // local variables
98 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_pdf = "pdf:Portable Document Format PDF:1:pdf:58:pdf\n";
99 static jmp_buf env;
100 
101 //--------------------------------------------------------------------------
102 // function declarations
103 //--------------------------------------------------------------------------
104 
105 // General
106 static short desired_offset( short, double );
107 static void poly_line( PLStream *pls, short *xa, short *ya, PLINT npts, short fill );
108 
109 // String processing
110 static void process_string( PLStream *, EscText * );
111 
112 // PLplot interface functions
114 void plD_init_pdf( PLStream * );
115 void plD_line_pdf( PLStream *, short, short, short, short );
116 void plD_polyline_pdf( PLStream *, short *, short *, PLINT );
117 void plD_eop_pdf( PLStream * );
118 void plD_bop_pdf( PLStream * );
119 void plD_tidy_pdf( PLStream * );
120 void plD_state_pdf( PLStream *, PLINT );
121 void plD_esc_pdf( PLStream *, PLINT, void * );
122 
123 //--------------------------------------------------------------------------
124 // error_handler( HPDF_STATUS error_no, HPDF_STATUS detail_no,
125 // void *user_data )
126 //
127 // Error handler for haru library.
128 //--------------------------------------------------------------------------
129 #ifdef HPDF_DLL
130 void __stdcall
131 #else
132 void
133 #endif
134 error_handler( HPDF_STATUS error_no, HPDF_STATUS detail_no, void *user_data )
135 {
136  // invoke longjmp() when an error has occurred
137  printf( "ERROR: error_no=%04X, detail_no=%d\n", (unsigned int) error_no, (int) detail_no );
138  longjmp( env, 1 );
139 }
140 
141 
142 //--------------------------------------------------------------------------
143 // plD_dispatch_init_pdf( PLDispatchTable *pdt )
144 //
145 // Initialize device dispatch table.
146 //--------------------------------------------------------------------------
148 {
149 #ifndef ENABLE_DYNDRIVERS
150  pdt->pl_MenuStr = "Portable Document Format PDF";
151  pdt->pl_DevName = "pdf";
152 #endif
154  pdt->pl_seq = 58;
155  pdt->pl_init = (plD_init_fp) plD_init_pdf;
156  pdt->pl_line = (plD_line_fp) plD_line_pdf;
157  pdt->pl_polyline = (plD_polyline_fp) plD_polyline_pdf;
158  pdt->pl_eop = (plD_eop_fp) plD_eop_pdf;
159  pdt->pl_bop = (plD_bop_fp) plD_bop_pdf;
160  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_pdf;
161  pdt->pl_state = (plD_state_fp) plD_state_pdf;
162  pdt->pl_esc = (plD_esc_fp) plD_esc_pdf;
163 }
164 
165 
166 // driver specific options
167 static PLINT text = 1;
168 static PLINT compress = 1;
169 static PLINT hrshsym = 1;
170 static PLINT color = 1;
171 static char * pageSize = NULL;
172 
173 DrvOpt pdf_options[] = {
174  { "text", DRV_INT, &text, "Use own text routines (text=0|1)" },
175  { "color", DRV_INT, &color, "Use color (color=0|1)" },
176  { "compress", DRV_INT, &compress, "Compress pdf output (compress=0|1)" },
177  { "hrshsym", DRV_INT, &hrshsym, "Use Hershey symbol set (hrshsym=0|1)" },
178  { "pagesize", DRV_STR, &pageSize, "Set page size (pagesize=A4|letter|A3|A5)" },
179  { NULL, DRV_INT, NULL, NULL }
180 };
181 
182 
183 //--------------------------------------------------------------------------
184 // plD_init_pdf( PLStream *pls )
185 //
186 // Initialize device.
187 //--------------------------------------------------------------------------
188 void plD_init_pdf( PLStream *pls )
189 {
190  pdfdev* dev;
191 
192  // allocate memory for the device storage
193  dev = (pdfdev *) calloc( 1, sizeof ( pdfdev ) );
194  if ( dev == NULL )
195  plexit( "Insufficient memory\n" );
196  pls->dev = (void *) dev;
197 
198  // Check for and set up driver options
199  plParseDrvOpts( pdf_options );
200 
201  pls->termin = 0; // not an interactive device
202  if ( color )
203  pls->color = 1; // supports color
204  else
205  pls->color = 0; // monochrome
206  pls->width = 1;
207  pls->bytecnt = 0;
208 
209  if ( text )
210  {
211  pls->dev_text = 1; // handles text
212  pls->dev_unicode = 1; // wants text as unicode
213  if ( hrshsym )
214  pls->dev_hrshsym = 1;
215  }
216 
217  pls->page = 0;
218  pls->dev_fill0 = 1; // supports hardware solid fills
219  pls->dev_fill1 = 0; // Use PLplot core fallback for pattern fills
220 
221  pls->graphx = GRAPHICS_MODE;
222 
223  if ( !pls->colorset )
224  pls->color = 1;
225 
226  // Set the (virtual) page size. The geometry option is
227  // neglected. Page sizes are set with the pagesize option.
228  plspage( DEVICE_PIXELS_PER_INCH, DEVICE_PIXELS_PER_INCH,
229  (PLINT) ( CANVAS_WIDTH * DEVICE_PIXELS_PER_INCH ), (PLINT) ( CANVAS_HEIGHT * DEVICE_PIXELS_PER_INCH ), 0, 0 );
230 
231  // Set up physical limits of plotting device (in drawing units)
232  plP_setphy( 0, (PLINT) ( CANVAS_WIDTH * DEVICE_PIXELS_PER_INCH ),
233  0, (PLINT) ( CANVAS_HEIGHT * DEVICE_PIXELS_PER_INCH ) );
234 
235  // Set the number of pixels per mm
236  plP_setpxl( (PLFLT) DEVICE_PIXELS_PER_MM, (PLFLT) DEVICE_PIXELS_PER_MM );
237 
238  // If portrait mode is specified, then set up an additional rotation
239  // transformation with aspect ratio allowed to adjust via freeaspect.
240  // Default orientation is landscape (ORIENTATION == 3 or 90 deg rotation
241  // counter-clockwise from portrait). (Legacy PLplot used seascape
242  // which was equivalent to ORIENTATION == 1 or 90 deg clockwise rotation
243  // from portrait.)
244  if ( pls->portrait )
245  {
246  plsdiori( (PLFLT) ( 4 - ORIENTATION ) );
247  pls->freeaspect = 1;
248  }
249 
250  // Initialize family file info
251  plFamInit( pls );
252 
253  // Prompt for a file name if not already set
254  plOpenFile( pls );
255  dev->pdfFile = pls->OutFile;
256 
257  dev->pdf = HPDF_New( error_handler, NULL );
258  if ( !dev->pdf )
259  plexit( "ERROR: cannot create pdf object.\n" );
260 
261  if ( compress )
262  HPDF_SetCompressionMode( dev->pdf, HPDF_COMP_ALL );
263 
264  // determine size of pdf page - A4 is default
265  dev->pageSize = HPDF_PAGE_SIZE_EOF;
266  if ( pageSize == NULL )
267  dev->pageSize = HPDF_PAGE_SIZE_A4;
268  else if ( !stricmp( pageSize, "letter" ) )
269  dev->pageSize = HPDF_PAGE_SIZE_LETTER;
270  else if ( !stricmp( pageSize, "A3" ) )
271  dev->pageSize = HPDF_PAGE_SIZE_A3;
272  else if ( !stricmp( pageSize, "A4" ) )
273  dev->pageSize = HPDF_PAGE_SIZE_A4;
274  else if ( !stricmp( pageSize, "A5" ) )
275  dev->pageSize = HPDF_PAGE_SIZE_A5;
276 
277  if ( dev->pageSize == HPDF_PAGE_SIZE_EOF )
278  plexit( "ERROR: Unknown page size. Allowed strings are: letter, A3, A4, A5.\n" );
279 
280  if ( setjmp( env ) )
281  {
282  // HPDF_Free segfaults after error so skip this nicety.
283  //HPDF_Free( dev->pdf );
284  // can't call plexit because that appears to be circular via
285  // what happens with plend. Therefore, print out an error message
286  // and exit.
287  fprintf( stderr, "ERROR in haru library\n" );
288  exit( 1 );
289  }
290 }
291 
292 //--------------------------------------------------------------------------
293 // plD_bop_pdf( PLStream *pls )
294 //
295 // Set up for the next page.
296 //--------------------------------------------------------------------------
297 void plD_bop_pdf( PLStream *pls )
298 {
299  pdfdev * dev = (pdfdev *) pls->dev;
300  HPDF_REAL width, height;
301 
302  pls->page++;
303 
304  // add page and set size (default is A4)
305  dev->page = HPDF_AddPage( dev->pdf );
306  if ( pls->portrait )
307  HPDF_Page_SetSize( dev->page, dev->pageSize, HPDF_PAGE_PORTRAIT );
308  else
309  HPDF_Page_SetSize( dev->page, dev->pageSize, HPDF_PAGE_LANDSCAPE );
310 
311  // Determine scaling parameters.
312  width = HPDF_Page_GetWidth( dev->page ); // in pixels/dots
313  height = HPDF_Page_GetHeight( dev->page ); // in pixels/dots
314  dev->scalex = (PLFLT) ( width / ( CANVAS_WIDTH * DEVICE_PIXELS_PER_INCH ) );
315  dev->scaley = (PLFLT) ( height / ( CANVAS_HEIGHT * DEVICE_PIXELS_PER_INCH ) );
316  HPDF_Page_Concat( dev->page, (HPDF_REAL) ( dev->scalex ), 0, 0, (HPDF_REAL) ( dev->scaley ), 0, 0 );
317 
318  // Set the background by drawing a rectangle that is the size of
319  // of the canvas and filling it with the background color.
320  HPDF_Page_SetRGBFill( dev->page, (HPDF_REAL) ( pls->cmap0[0].r / 255.0 ),
321  (HPDF_REAL) ( pls->cmap0[0].g / 255.0 ), (HPDF_REAL) ( pls->cmap0[0].b / 255.0 ) );
322  width /= (HPDF_REAL) ( dev->scalex );
323  height /= (HPDF_REAL) ( dev->scaley );
324  HPDF_Page_MoveTo( dev->page, (HPDF_REAL) 0.0, (HPDF_REAL) 0.0 );
325  HPDF_Page_LineTo( dev->page, width, (HPDF_REAL) 0.0 );
326  HPDF_Page_LineTo( dev->page, width, (HPDF_REAL) height );
327  HPDF_Page_LineTo( dev->page, 0.0, (HPDF_REAL) height );
328  HPDF_Page_Fill( dev->page );
329 }
330 
331 
332 //--------------------------------------------------------------------------
333 // pdf_line()
334 //
335 // Draw a line in the current color from (x1,y1) to (x2,y2).
336 //--------------------------------------------------------------------------
337 void plD_line_pdf( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
338 {
339  short xa[2], ya[2];
340 
341  xa[0] = x1a; xa[1] = x2a;
342  ya[0] = y1a; ya[1] = y2a;
343 
344  poly_line( pls, xa, ya, 2, 0 );
345 }
346 
347 
348 //--------------------------------------------------------------------------
349 // pdf_polyline()
350 //
351 // Draw a polyline in the current color.
352 //--------------------------------------------------------------------------
353 void plD_polyline_pdf( PLStream *pls, short *xa, short *ya, PLINT npts )
354 {
355  poly_line( pls, xa, ya, npts, 0 );
356 }
357 
358 
359 //--------------------------------------------------------------------------
360 // pdf_eop()
361 //
362 // End of page
363 //--------------------------------------------------------------------------
364 void plD_eop_pdf( PLStream *pls )
365 {
366  // nothing to be done here
367 }
368 
369 
370 //--------------------------------------------------------------------------
371 // pdf_tidy()
372 //
373 // Close graphics file or otherwise clean up.
374 //--------------------------------------------------------------------------
375 void plD_tidy_pdf( PLStream *pls )
376 {
377  pdfdev* dev = (pdfdev *) pls->dev;
378 
379  // save the document to a stream
380  HPDF_SaveToStream( dev->pdf );
381 
382  // rewind the stream.
383  HPDF_ResetStream( dev->pdf );
384 
385  // get the data from the stream and output it to stdout.
386  for (;; )
387  {
388  HPDF_BYTE buf[4096]; // TODO: not good
389  HPDF_UINT32 size = 4096;
390  HPDF_STATUS ret = HPDF_ReadFromStream( dev->pdf, buf, &size );
391 
392  if ( size == 0 )
393  break;
394 
395  if ( fwrite( buf, size, 1, dev->pdfFile ) != 1 )
396  plexit( "ERROR: Cannot write to file!" );
397  }
398 
399  plCloseFile( pls );
400 
401  // cleanup
402  HPDF_Free( dev->pdf );
403 }
404 
405 
406 //--------------------------------------------------------------------------
407 // plD_state_pdf()
408 //
409 // Handle change in PLStream state (color, pen width, fill attribute, etc).
410 //
411 // Nothing is done here because these attributes are aquired from
412 // PLStream for each element that is drawn.
413 //--------------------------------------------------------------------------
414 void plD_state_pdf( PLStream *pls, PLINT op )
415 {
416  // Nothing to be done here.
417 }
418 
419 
420 //--------------------------------------------------------------------------
421 // pdf_esc()
422 //
423 // Escape function.
424 //--------------------------------------------------------------------------
425 void plD_esc_pdf( PLStream *pls, PLINT op, void *ptr )
426 {
427  switch ( op )
428  {
429  case PLESC_FILL: // fill polygon
430  poly_line( pls, pls->dev_x, pls->dev_y, pls->dev_npts, 1 );
431  break;
432  case PLESC_HAS_TEXT: // render text
433  process_string( pls, (EscText *) ptr );
434  break;
435  }
436 }
437 
438 
439 //--------------------------------------------------------------------------
440 // poly_line()
441 //
442 // Handles drawing filled and unfilled polygons
443 //--------------------------------------------------------------------------
444 void poly_line( PLStream *pls, short *xa, short *ya, PLINT npts, short fill )
445 {
446  pdfdev* dev = (pdfdev *) pls->dev;
447  PLINT i;
448 
449  HPDF_Page_SetLineWidth( dev->page, (HPDF_REAL) ( pls->width ) );
450  HPDF_Page_SetLineCap( dev->page, HPDF_ROUND_END );
451  HPDF_Page_SetLineJoin( dev->page, HPDF_ROUND_JOIN );
452  HPDF_Page_SetRGBStroke( dev->page, (HPDF_REAL) ( pls->curcolor.r / 255.0 ),
453  (HPDF_REAL) ( pls->curcolor.g / 255.0 ), (HPDF_REAL) ( pls->curcolor.b / 255.0 ) );
454  HPDF_Page_SetRGBFill( dev->page, (HPDF_REAL) ( pls->curcolor.r / 255.0 ),
455  (HPDF_REAL) ( pls->curcolor.g / 255.0 ), (HPDF_REAL) ( pls->curcolor.b / 255.0 ) );
456 
457  HPDF_Page_MoveTo( dev->page, (HPDF_REAL) xa[0], (HPDF_REAL) ya[0] );
458  for ( i = 1; i < npts; i++ )
459  HPDF_Page_LineTo( dev->page, (HPDF_REAL) xa[i], (HPDF_REAL) ya[i] );
460 
461  if ( fill == 1 )
462  {
463  if ( pls->dev_eofill )
464  HPDF_Page_EofillStroke( dev->page );
465  else
466  HPDF_Page_FillStroke( dev->page );
467  }
468  else
469  {
470  HPDF_Page_Stroke( dev->page );
471  }
472 }
473 
474 
475 //--------------------------------------------------------------------------
476 // unsigned char plunicode2type1 (const PLUNICODE index,
477 // const Unicode_to_Type1_table lookup[], const int number_of_entries)
478 //
479 // Function takes an input unicode index, looks through the lookup
480 // table (which must be sorted by PLUNICODE Unicode), then returns the
481 // corresponding Type1 code in the lookup table. If the Unicode index
482 // is not present the returned value is 32 (which is normally a blank
483 // for Type 1 fonts).
484 //--------------------------------------------------------------------------
485 static unsigned char plunicode2type1( const PLUNICODE index,
486  const Unicode_to_Type1_table lookup[],
487  const int nlookup )
488 {
489  int jlo = -1, jmid, jhi = nlookup;
490 
491  while ( jhi - jlo > 1 )
492  {
493  // Note that although jlo or jhi can be just outside valid
494  // range (see initialization above) because of while condition
495  // jlo < jmid < jhi and jmid must be in valid range.
496  //
497  jmid = ( jlo + jhi ) / 2;
498  if ( index > lookup[jmid].Unicode )
499  jlo = jmid;
500  else if ( index < lookup[jmid].Unicode )
501  jhi = jmid;
502  else
503  // We have found it!
504  // index == lookup[jmid].Unicode
505  //
506  return ( lookup[jmid].Type1 );
507  }
508  // jlo is invalid or it is valid and index > lookup[jlo].Unicode.
509  // jhi is invalid or it is valid and index < lookup[jhi].Unicode.
510  // All these conditions together imply index cannot be found in lookup.
511  // Mark with ' ' (which is normally the index for blank in type 1 fonts).
512  //
513  return ( ' ' );
514 }
515 
516 
517 //--------------------------------------------------------------------------
518 // PSDrawTextToCanvas( pdfdev* dev, unsigned char* type1_string, short drawText )
519 //
520 // This function determines the extent of the string and does
521 // the actual drawing to the page if drawText is true.
522 //--------------------------------------------------------------------------
523 void PSDrawTextToCanvas( pdfdev* dev, unsigned char* type1_string, short drawText )
524 {
525  HPDF_REAL th;
526 
527  // write text to page
528  if ( drawText )
529  {
530  HPDF_Page_BeginText( dev->page );
531  HPDF_Page_SetTextRenderingMode( dev->page, HPDF_FILL );
532  HPDF_Page_SetRGBFill( dev->page, dev->textRed, dev->textGreen, dev->textBlue );
533  HPDF_Page_MoveTextPos( dev->page, dev->textWidth, dev->yOffset );
534  HPDF_Page_ShowText( dev->page, (char *) type1_string ); // TODO: this conversion must be wrong
535  HPDF_Page_EndText( dev->page );
536  }
537 
538  // determine text width and height
539  dev->textWidth += HPDF_Page_TextWidth( dev->page, (char *) type1_string ); // TODO: this conversion must be wrong
540  th = (HPDF_REAL) ( HPDF_Font_GetCapHeight( dev->m_font ) * dev->fontSize * dev->fontScale / 1000.0 );
541  dev->textHeight = dev->textHeight > ( th + dev->yOffset ) ? dev->textHeight : ( th + dev->yOffset );
542 
543  // clear string
544  memset( type1_string, '\0', MAX_STRING_LEN );
545 }
546 
547 
548 //--------------------------------------------------------------------------
549 // PSSetFont( pdfdev* dev, PLUNICODE fci )
550 //
551 // Sets the font.
552 //--------------------------------------------------------------------------
553 void PSSetFont( pdfdev* dev, PLUNICODE fci )
554 {
555  char *font;
556 
557  // fci = 0 is a special value indicating the Type 1 Symbol font
558  // is desired. This value cannot be confused with a normal FCI value
559  // because it doesn't have the PL_FCI_MARK.
560  if ( fci == 0 )
561  {
562  font = "Symbol";
564  dev->lookup = unicode_to_symbol_lookup_table;
565  dev->if_symbol_font = 1;
566  }
567  else
568  {
569  // convert the fci to Base14/Type1 font information
572  dev->lookup = unicode_to_standard_lookup_table;
573  dev->if_symbol_font = 0;
574  }
575 
576  if ( !( dev->m_font = HPDF_GetFont( dev->pdf, font, NULL ) ) )
577  plexit( "ERROR: Couldn't open font\n" );
578  //pldebug( "PSSetFont", "HPDF requested font size = %f\n", dev->fontSize * dev->fontScale );
579  HPDF_Page_SetFontAndSize( dev->page, dev->m_font, dev->fontSize * dev->fontScale );
580 }
581 
582 // 0.8 should mimic the offset of first superscript/subscript level
583 // implemented in plstr (plsym.c) for Hershey fonts. However, when
584 // comparing with -dev xwin and -dev xcairo results changing this
585 // factor to 0.6 appears to offset the centers of the letters
586 // appropriately while 0.8 gives much poorer agreement with the
587 // other devices.
588 # define RISE_FACTOR 0.6
589 
590 //--------------------------------------------------------------------------
591 // PSDrawText( pdfdev* dev, PLUNICODE* ucs4, int ucs4Len, short drawText )
592 //
593 // This function is called twice, first to determine the extent of the
594 // text written to the page and then a second time to actually draw
595 // the text.
596 //--------------------------------------------------------------------------
597 void PSDrawText( pdfdev* dev, PLUNICODE* ucs4, int ucs4Len, short drawText )
598 {
599  int i, s;
600  unsigned char type1_string[MAX_STRING_LEN];
601  char plplotEsc;
602  PLUNICODE fci;
603  int last_chance = 0;
604  PLFLT old_sscale, sscale, old_soffset, soffset, dup;
605  PLINT level = 0;
606 
607  memset( type1_string, '\0', MAX_STRING_LEN );
608 
609  // Get PLplot escape character
610  plgesc( &plplotEsc );
611 
612  // Get the current font
613  dev->fontScale = 1.0;
614  dev->yOffset = 0.0;
615  plgfci( &fci );
616  PSSetFont( dev, fci );
617  dev->textWidth = 0;
618  dev->textHeight = 0;
619 
620  i = 0; s = 0;
621  while ( i < ucs4Len )
622  {
623  if ( ucs4[i] < PL_FCI_MARK ) // not a font change
624  {
625  if ( ucs4[i] != (PLUNICODE) plplotEsc ) // a character to display
626  {
627  type1_string[s] = plunicode2type1( ucs4[i], dev->lookup, dev->nlookup );
628  if ( ucs4[i] != ' ' && type1_string[s] == ' ' )
629  {
630  // failed lookup
631  if ( !dev->if_symbol_font )
632  {
633  // failed standard font lookup. Try "last chance"
634  // symbol font instead.
635  type1_string[s] = '\0';
636  PSDrawTextToCanvas( dev, type1_string, drawText );
637  s = 0;
638  last_chance = 1;
639  PSSetFont( dev, 0 );
640  continue;
641  }
642  else if ( !last_chance )
643  {
644  // failed symbol font lookup that is not right
645  // after a failed standard font lookup (i.e.,
646  // last_change = 0). Try standard fonts lookup instead.
647  type1_string[s] = '\0';
648  PSDrawTextToCanvas( dev, type1_string, drawText );
649  s = 0;
650  last_chance = 0;
651  PSSetFont( dev, fci );
652  continue;
653  }
654  else
655  {
656  // failed "last_chance" symbol font lookup that
657  // has occurred right after a failed standard
658  // fonts lookup. Just accept blank result and
659  // move on using standard fonts.
660  PSDrawTextToCanvas( dev, type1_string, drawText );
661  s = 0;
662  last_chance = 0;
663  PSSetFont( dev, fci );
664  i++;
665  continue;
666  }
667  }
668  else
669  {
670  // font lookup succeeded.
671  s++;
672  i++;
673  last_chance = 0;
674  continue;
675  }
676  }
677  i++;
678  if ( ucs4[i] == (PLUNICODE) plplotEsc ) // a escape character to display
679  {
680  type1_string[s] = plunicode2type1( ucs4[i], dev->lookup, dev->nlookup );
681  if ( ucs4[i] != ' ' && type1_string[s] == ' ' )
682  {
683  // failed lookup
684  if ( !dev->if_symbol_font )
685  {
686  // failed standard font lookup. Try "last chance"
687  // symbol font instead.
688  type1_string[s] = '\0';
689  PSDrawTextToCanvas( dev, type1_string, drawText );
690  s = 0;
691  last_chance = 1;
692  PSSetFont( dev, 0 );
693  continue;
694  }
695  else if ( !last_chance )
696  {
697  // failed symbol font lookup that is not right
698  // after a failed standard font lookup (i.e.,
699  // last_change = 0). Try standard fonts lookup instead.
700  type1_string[s] = '\0';
701  PSDrawTextToCanvas( dev, type1_string, drawText );
702  s = 0;
703  last_chance = 0;
704  PSSetFont( dev, fci );
705  continue;
706  }
707  else
708  {
709  // failed "last_chance" symbol font lookup that
710  // has occurred right after a failed standard
711  // fonts lookup. Just accept blank result and
712  // move on using standard fonts.
713  PSDrawTextToCanvas( dev, type1_string, drawText );
714  s = 0;
715  last_chance = 0;
716  PSSetFont( dev, fci );
717  i++;
718  continue;
719  }
720  }
721  else
722  {
723  // font lookup succeeded.
724  s++;
725  i++;
726  last_chance = 0;
727  continue;
728  }
729  }
730  else
731  {
732  if ( ucs4[i] == (PLUNICODE) 'u' ) // Superscript
733  {
734  // draw string so far
735  PSDrawTextToCanvas( dev, type1_string, drawText );
736  s = 0;
737 
738  plP_script_scale( TRUE, &level,
739  &old_sscale, &sscale, &old_soffset, &soffset );
740  // The correction for the difference in magnitude
741  // between the baseline and middle coordinate systems
742  // for superscripts should be
743  // 0.5*(base font size - superscript/subscript font size).
744  dup = 0.5 * ( 1.0 - sscale );
745  dev->fontScale = sscale;
746  PSSetFont( dev, fci );
747  dev->yOffset = dev->fontSize * ( soffset * RISE_FACTOR + dup );
748  }
749  if ( ucs4[i] == (PLUNICODE) 'd' ) // Subscript
750  {
751  // draw string so far
752  PSDrawTextToCanvas( dev, type1_string, drawText );
753  s = 0;
754 
755  plP_script_scale( FALSE, &level,
756  &old_sscale, &sscale, &old_soffset, &soffset );
757  // The correction for the difference in magnitude
758  // between the baseline and middle coordinate systems
759  // for subcripts should be
760  // 0.5*(base font size - superscript/subscript font size).
761  dup = -0.5 * ( 1.0 - sscale );
762  dev->fontScale = sscale;
763  PSSetFont( dev, fci );
764  dev->yOffset = -dev->fontSize * ( soffset * RISE_FACTOR + dup );
765  }
766  if ( ucs4[i] == (PLUNICODE) '-' ) // underline
767  { // draw string so far
768  PSDrawTextToCanvas( dev, type1_string, drawText );
769  s = 0;
770 
771  // dev->underlined = !dev->underlined;
772  PSSetFont( dev, fci );
773  }
774  if ( ucs4[i] == (PLUNICODE) '+' ) // overline
775  { // not implemented yet
776  }
777  i++;
778  }
779  }
780  else // a font change
781  {
782  // draw string so far
783  PSDrawTextToCanvas( dev, type1_string, drawText );
784  s = 0;
785 
786  // get new font
787  fci = ucs4[i];
788  PSSetFont( dev, fci );
789  i++;
790  }
791  }
792 
793  PSDrawTextToCanvas( dev, type1_string, drawText );
794 }
795 
796 
797 //--------------------------------------------------------------------------
798 // process_string( PLStream* pls, EscText* args )
799 //
800 // Handles the output of the text on the page.
801 //--------------------------------------------------------------------------
802 void process_string( PLStream* pls, EscText* args )
803 {
804  pdfdev * dev = (pdfdev *) pls->dev;
805  PLFLT rotation, shear, stride;
806  HPDF_REAL cos_rot, sin_rot, cos_shear, sin_shear;
807 
808  // Check that we got unicode, warning message and return if not
809  if ( args->unicode_array_len == 0 )
810  {
811  printf( "Non unicode string passed to a pdf driver, ignoring\n" );
812  return;
813  }
814 
815  // Check that unicode string isn't longer then the max we allow
816  if ( args->unicode_array_len >= MAX_STRING_LEN )
817  {
818  printf( "Sorry, the pdf drivers only handles strings of length < %d\n", MAX_STRING_LEN );
819  return;
820  }
821 
822  // Calculate the font size (in pixels)
823  dev->fontSize = (HPDF_REAL) ( pls->chrht * DEVICE_PIXELS_PER_INCH / 25.4 * 1.6 );
824 
825  // text color
826  dev->textRed = (HPDF_REAL) ( pls->curcolor.r / 255.0 );
827  dev->textGreen = (HPDF_REAL) ( pls->curcolor.g / 255.0 );
828  dev->textBlue = (HPDF_REAL) ( pls->curcolor.b / 255.0 );
829 
830  // calculate transformation matrix (rotation and shear of text)
831  plRotationShear( args->xform, &rotation, &shear, &stride );
832  rotation -= pls->diorot * M_PI / 2.0;
833  cos_rot = (HPDF_REAL) cos( rotation );
834  sin_rot = (HPDF_REAL) sin( rotation );
835  cos_shear = (HPDF_REAL) cos( shear );
836  sin_shear = (HPDF_REAL) sin( shear );
837 
838  // calculate text extend -> stored in dev->textWidth and dev->textHeight
839  PSDrawText( dev, args->unicode_array, args->unicode_array_len, 0 );
840 
841  // apply transformation matrix and draw text
842  HPDF_Page_GSave( dev->page );
843  HPDF_Page_Concat( dev->page, cos_rot, sin_rot,
844  -cos_rot * sin_shear - sin_rot * cos_shear,
845  -sin_rot * sin_shear + cos_rot * cos_shear,
846  (HPDF_REAL) ( args->x ), (HPDF_REAL) ( args->y ) );
847  HPDF_Page_Concat( dev->page, (HPDF_REAL) 1.0, (HPDF_REAL) 0.0, (HPDF_REAL) 0.0, (HPDF_REAL) 1.0,
848  (HPDF_REAL) ( -args->just * dev->textWidth ), (HPDF_REAL) ( -0.5 * dev->textHeight ) );
849  PSDrawText( dev, args->unicode_array, args->unicode_array_len, 1 );
850  HPDF_Page_GRestore( dev->page );
851 }
852 
853 #else
854 
855 //--------------------------------------------------------------------------
856 // pldummy_pdf()
857 //
858 // Dummy function if driver should not be available.
859 //--------------------------------------------------------------------------
861 {
862  return 0;
863 }
864 
865 #endif // PLD_pdf
866