PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/v5_9_7/drivers/pdf.c

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