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