PageRenderTime 61ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/tags/v5_9_8/drivers/pdf.c

#
C | 866 lines | 505 code | 113 blank | 248 comment | 73 complexity | ce859fbb268733df52f3ddb1f23e97d6 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 11804 2011-07-10 17:27:00Z 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. // 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. {
  386. if ( pls->dev_eofill )
  387. HPDF_Page_EofillStroke( dev->page );
  388. else
  389. HPDF_Page_FillStroke( dev->page );
  390. }
  391. else
  392. {
  393. HPDF_Page_Stroke( dev->page );
  394. }
  395. }
  396. //--------------------------------------------------------------------------
  397. // unsigned char plunicode2type1 (const PLUNICODE index,
  398. // const Unicode_to_Type1_table lookup[], const int number_of_entries)
  399. //
  400. // Function takes an input unicode index, looks through the lookup
  401. // table (which must be sorted by PLUNICODE Unicode), then returns the
  402. // corresponding Type1 code in the lookup table. If the Unicode index
  403. // is not present the returned value is 32 (which is normally a blank
  404. // for Type 1 fonts).
  405. //--------------------------------------------------------------------------
  406. static unsigned char plunicode2type1( const PLUNICODE index,
  407. const Unicode_to_Type1_table lookup[],
  408. const int nlookup )
  409. {
  410. int jlo = -1, jmid, jhi = nlookup;
  411. while ( jhi - jlo > 1 )
  412. {
  413. // Note that although jlo or jhi can be just outside valid
  414. // range (see initialization above) because of while condition
  415. // jlo < jmid < jhi and jmid must be in valid range.
  416. //
  417. jmid = ( jlo + jhi ) / 2;
  418. if ( index > lookup[jmid].Unicode )
  419. jlo = jmid;
  420. else if ( index < lookup[jmid].Unicode )
  421. jhi = jmid;
  422. else
  423. // We have found it!
  424. // index == lookup[jmid].Unicode
  425. //
  426. return ( lookup[jmid].Type1 );
  427. }
  428. // jlo is invalid or it is valid and index > lookup[jlo].Unicode.
  429. // jhi is invalid or it is valid and index < lookup[jhi].Unicode.
  430. // All these conditions together imply index cannot be found in lookup.
  431. // Mark with ' ' (which is normally the index for blank in type 1 fonts).
  432. //
  433. return ( ' ' );
  434. }
  435. //--------------------------------------------------------------------------
  436. // PSDrawTextToCanvas( pdfdev* dev, unsigned char* type1_string, short drawText )
  437. //
  438. // This function determines the extent of the string and does
  439. // the actual drawing to the page if drawText is true.
  440. //--------------------------------------------------------------------------
  441. void PSDrawTextToCanvas( pdfdev* dev, unsigned char* type1_string, short drawText )
  442. {
  443. HPDF_REAL th;
  444. // write text to page
  445. if ( drawText )
  446. {
  447. HPDF_Page_BeginText( dev->page );
  448. HPDF_Page_SetTextRenderingMode( dev->page, HPDF_FILL );
  449. HPDF_Page_SetRGBFill( dev->page, dev->textRed, dev->textGreen, dev->textBlue );
  450. HPDF_Page_MoveTextPos( dev->page, dev->textWidth, dev->yOffset );
  451. HPDF_Page_ShowText( dev->page, (char *) type1_string ); // TODO: this conversion must be wrong
  452. HPDF_Page_EndText( dev->page );
  453. }
  454. // determine text width and height
  455. dev->textWidth += HPDF_Page_TextWidth( dev->page, (char *) type1_string ); // TODO: this conversion must be wrong
  456. th = (HPDF_REAL) ( HPDF_Font_GetCapHeight( dev->m_font ) * dev->fontSize * dev->fontScale / 1000.0 );
  457. dev->textHeight = dev->textHeight > ( th + dev->yOffset ) ? dev->textHeight : ( th + dev->yOffset );
  458. // clear string
  459. memset( type1_string, '\0', MAX_STRING_LEN );
  460. }
  461. //--------------------------------------------------------------------------
  462. // PSSetFont( pdfdev* dev, PLUNICODE fci )
  463. //
  464. // Sets the font.
  465. //--------------------------------------------------------------------------
  466. void PSSetFont( pdfdev* dev, PLUNICODE fci )
  467. {
  468. char *font;
  469. // fci = 0 is a special value indicating the Type 1 Symbol font
  470. // is desired. This value cannot be confused with a normal FCI value
  471. // because it doesn't have the PL_FCI_MARK.
  472. if ( fci == 0 )
  473. {
  474. font = "Symbol";
  475. dev->nlookup = number_of_entries_in_unicode_to_symbol_table;
  476. dev->lookup = unicode_to_symbol_lookup_table;
  477. dev->if_symbol_font = 1;
  478. }
  479. else
  480. {
  481. // convert the fci to Base14/Type1 font information
  482. font = plP_FCI2FontName( fci, Type1Lookup, N_Type1Lookup );
  483. dev->nlookup = number_of_entries_in_unicode_to_standard_table;
  484. dev->lookup = unicode_to_standard_lookup_table;
  485. dev->if_symbol_font = 0;
  486. }
  487. if ( !( dev->m_font = HPDF_GetFont( dev->pdf, font, NULL ) ) )
  488. plexit( "ERROR: Couldn't open font\n" );
  489. //pldebug( "PSSetFont", "HPDF requested font size = %f\n", dev->fontSize * dev->fontScale );
  490. HPDF_Page_SetFontAndSize( dev->page, dev->m_font, dev->fontSize * dev->fontScale );
  491. }
  492. // 0.8 should mimic the offset of first superscript/subscript level
  493. // implemented in plstr (plsym.c) for Hershey fonts. However, when
  494. // comparing with -dev xwin and -dev xcairo results changing this
  495. // factor to 0.6 appears to offset the centers of the letters
  496. // appropriately while 0.8 gives much poorer agreement with the
  497. // other devices.
  498. # define RISE_FACTOR 0.6
  499. //--------------------------------------------------------------------------
  500. // PSDrawText( pdfdev* dev, PLUNICODE* ucs4, int ucs4Len, short drawText )
  501. //
  502. // This function is called twice, first to determine the extent of the
  503. // text written to the page and then a second time to actually draw
  504. // the text.
  505. //--------------------------------------------------------------------------
  506. void PSDrawText( pdfdev* dev, PLUNICODE* ucs4, int ucs4Len, short drawText )
  507. {
  508. int i, s;
  509. unsigned char type1_string[MAX_STRING_LEN];
  510. char plplotEsc;
  511. PLUNICODE fci;
  512. int last_chance = 0;
  513. PLFLT old_sscale, sscale, old_soffset, soffset, dup;
  514. PLINT level = 0;
  515. memset( type1_string, '\0', MAX_STRING_LEN );
  516. // Get PLplot escape character
  517. plgesc( &plplotEsc );
  518. // Get the current font
  519. dev->fontScale = 1.0;
  520. dev->yOffset = 0.0;
  521. plgfci( &fci );
  522. PSSetFont( dev, fci );
  523. dev->textWidth = 0;
  524. dev->textHeight = 0;
  525. i = 0; s = 0;
  526. while ( i < ucs4Len )
  527. {
  528. if ( ucs4[i] < PL_FCI_MARK ) // not a font change
  529. {
  530. if ( ucs4[i] != (PLUNICODE) plplotEsc ) // a character to display
  531. {
  532. type1_string[s] = plunicode2type1( ucs4[i], dev->lookup, dev->nlookup );
  533. if ( ucs4[i] != ' ' && type1_string[s] == ' ' )
  534. {
  535. // failed lookup
  536. if ( !dev->if_symbol_font )
  537. {
  538. // failed standard font lookup. Try "last chance"
  539. // symbol font instead.
  540. type1_string[s] = '\0';
  541. PSDrawTextToCanvas( dev, type1_string, drawText );
  542. s = 0;
  543. last_chance = 1;
  544. PSSetFont( dev, 0 );
  545. continue;
  546. }
  547. else if ( !last_chance )
  548. {
  549. // failed symbol font lookup that is not right
  550. // after a failed standard font lookup (i.e.,
  551. // last_change = 0). Try standard fonts lookup instead.
  552. type1_string[s] = '\0';
  553. PSDrawTextToCanvas( dev, type1_string, drawText );
  554. s = 0;
  555. last_chance = 0;
  556. PSSetFont( dev, fci );
  557. continue;
  558. }
  559. else
  560. {
  561. // failed "last_chance" symbol font lookup that
  562. // has occurred right after a failed standard
  563. // fonts lookup. Just accept blank result and
  564. // move on using standard fonts.
  565. PSDrawTextToCanvas( dev, type1_string, drawText );
  566. s = 0;
  567. last_chance = 0;
  568. PSSetFont( dev, fci );
  569. i++;
  570. continue;
  571. }
  572. }
  573. else
  574. {
  575. // font lookup succeeded.
  576. s++;
  577. i++;
  578. last_chance = 0;
  579. continue;
  580. }
  581. }
  582. i++;
  583. if ( ucs4[i] == (PLUNICODE) plplotEsc ) // a escape character to display
  584. {
  585. type1_string[s] = plunicode2type1( ucs4[i], dev->lookup, dev->nlookup );
  586. if ( ucs4[i] != ' ' && type1_string[s] == ' ' )
  587. {
  588. // failed lookup
  589. if ( !dev->if_symbol_font )
  590. {
  591. // failed standard font lookup. Try "last chance"
  592. // symbol font instead.
  593. type1_string[s] = '\0';
  594. PSDrawTextToCanvas( dev, type1_string, drawText );
  595. s = 0;
  596. last_chance = 1;
  597. PSSetFont( dev, 0 );
  598. continue;
  599. }
  600. else if ( !last_chance )
  601. {
  602. // failed symbol font lookup that is not right
  603. // after a failed standard font lookup (i.e.,
  604. // last_change = 0). Try standard fonts lookup instead.
  605. type1_string[s] = '\0';
  606. PSDrawTextToCanvas( dev, type1_string, drawText );
  607. s = 0;
  608. last_chance = 0;
  609. PSSetFont( dev, fci );
  610. continue;
  611. }
  612. else
  613. {
  614. // failed "last_chance" symbol font lookup that
  615. // has occurred right after a failed standard
  616. // fonts lookup. Just accept blank result and
  617. // move on using standard fonts.
  618. PSDrawTextToCanvas( dev, type1_string, drawText );
  619. s = 0;
  620. last_chance = 0;
  621. PSSetFont( dev, fci );
  622. i++;
  623. continue;
  624. }
  625. }
  626. else
  627. {
  628. // font lookup succeeded.
  629. s++;
  630. i++;
  631. last_chance = 0;
  632. continue;
  633. }
  634. }
  635. else
  636. {
  637. if ( ucs4[i] == (PLUNICODE) 'u' ) // Superscript
  638. {
  639. // draw string so far
  640. PSDrawTextToCanvas( dev, type1_string, drawText );
  641. s = 0;
  642. plP_script_scale( TRUE, &level,
  643. &old_sscale, &sscale, &old_soffset, &soffset );
  644. // The correction for the difference in magnitude
  645. // between the baseline and middle coordinate systems
  646. // for superscripts should be
  647. // 0.5*(base font size - superscript/subscript font size).
  648. dup = 0.5 * ( 1.0 - sscale );
  649. dev->fontScale = sscale;
  650. PSSetFont( dev, fci );
  651. dev->yOffset = dev->fontSize * ( soffset * RISE_FACTOR + dup );
  652. }
  653. if ( ucs4[i] == (PLUNICODE) 'd' ) // Subscript
  654. {
  655. // draw string so far
  656. PSDrawTextToCanvas( dev, type1_string, drawText );
  657. s = 0;
  658. plP_script_scale( FALSE, &level,
  659. &old_sscale, &sscale, &old_soffset, &soffset );
  660. // The correction for the difference in magnitude
  661. // between the baseline and middle coordinate systems
  662. // for subcripts should be
  663. // 0.5*(base font size - superscript/subscript font size).
  664. dup = -0.5 * ( 1.0 - sscale );
  665. dev->fontScale = sscale;
  666. PSSetFont( dev, fci );
  667. dev->yOffset = -dev->fontSize * ( soffset * RISE_FACTOR + dup );
  668. }
  669. if ( ucs4[i] == (PLUNICODE) '-' ) // underline
  670. { // draw string so far
  671. PSDrawTextToCanvas( dev, type1_string, drawText );
  672. s = 0;
  673. // dev->underlined = !dev->underlined;
  674. PSSetFont( dev, fci );
  675. }
  676. if ( ucs4[i] == (PLUNICODE) '+' ) // overline
  677. { // not implemented yet
  678. }
  679. i++;
  680. }
  681. }
  682. else // a font change
  683. {
  684. // draw string so far
  685. PSDrawTextToCanvas( dev, type1_string, drawText );
  686. s = 0;
  687. // get new font
  688. fci = ucs4[i];
  689. PSSetFont( dev, fci );
  690. i++;
  691. }
  692. }
  693. PSDrawTextToCanvas( dev, type1_string, drawText );
  694. }
  695. //--------------------------------------------------------------------------
  696. // process_string( PLStream* pls, EscText* args )
  697. //
  698. // Handles the output of the text on the page.
  699. //--------------------------------------------------------------------------
  700. void process_string( PLStream* pls, EscText* args )
  701. {
  702. pdfdev * dev = (pdfdev *) pls->dev;
  703. PLFLT rotation, shear, stride;
  704. HPDF_REAL cos_rot, sin_rot, cos_shear, sin_shear;
  705. // Check that we got unicode, warning message and return if not
  706. if ( args->unicode_array_len == 0 )
  707. {
  708. printf( "Non unicode string passed to a pdf driver, ignoring\n" );
  709. return;
  710. }
  711. // Check that unicode string isn't longer then the max we allow
  712. if ( args->unicode_array_len >= MAX_STRING_LEN )
  713. {
  714. printf( "Sorry, the pdf drivers only handles strings of length < %d\n", MAX_STRING_LEN );
  715. return;
  716. }
  717. // Calculate the font size (in pixels)
  718. dev->fontSize = (HPDF_REAL) ( pls->chrht * DEVICE_PIXELS_PER_INCH / 25.4 * 1.6 );
  719. // text color
  720. dev->textRed = (HPDF_REAL) ( pls->curcolor.r / 255.0 );
  721. dev->textGreen = (HPDF_REAL) ( pls->curcolor.g / 255.0 );
  722. dev->textBlue = (HPDF_REAL) ( pls->curcolor.b / 255.0 );
  723. // calculate transformation matrix (rotation and shear of text)
  724. plRotationShear( args->xform, &rotation, &shear, &stride );
  725. rotation -= pls->diorot * M_PI / 2.0;
  726. cos_rot = (HPDF_REAL) cos( rotation );
  727. sin_rot = (HPDF_REAL) sin( rotation );
  728. cos_shear = (HPDF_REAL) cos( shear );
  729. sin_shear = (HPDF_REAL) sin( shear );
  730. // calculate text extend -> stored in dev->textWidth and dev->textHeight
  731. PSDrawText( dev, args->unicode_array, args->unicode_array_len, 0 );
  732. // apply transformation matrix and draw text
  733. HPDF_Page_GSave( dev->page );
  734. HPDF_Page_Concat( dev->page, cos_rot, sin_rot,
  735. -cos_rot * sin_shear - sin_rot * cos_shear,
  736. -sin_rot * sin_shear + cos_rot * cos_shear,
  737. (HPDF_REAL) ( args->x ), (HPDF_REAL) ( args->y ) );
  738. HPDF_Page_Concat( dev->page, (HPDF_REAL) 1.0, (HPDF_REAL) 0.0, (HPDF_REAL) 0.0, (HPDF_REAL) 1.0,
  739. (HPDF_REAL) ( -args->just * dev->textWidth ), (HPDF_REAL) ( -0.5 * dev->textHeight ) );
  740. PSDrawText( dev, args->unicode_array, args->unicode_array_len, 1 );
  741. HPDF_Page_GRestore( dev->page );
  742. }
  743. #else
  744. //--------------------------------------------------------------------------
  745. // pldummy_pdf()
  746. //
  747. // Dummy function if driver should not be available.
  748. //--------------------------------------------------------------------------
  749. int pldummy_pdf()
  750. {
  751. return 0;
  752. }
  753. #endif // PLD_pdf