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