// PLplot pstex (Postscript/LaTeX) device driver. // #include "plDevs.h" #ifdef PLD_pstex #include "plplotP.h" #include "drivers.h" #include "ps.h" // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_pstex = "pstex:Combined Postscript/LaTeX files:0:pstex:41:pstex\n"; //-------------------------------------------------------------------------- // plD_init_pstex() // // Initialize device. //-------------------------------------------------------------------------- static void parse_str( const char *str, char *dest ); static void proc_str( PLStream *pls, EscText *args ); static int color = 1; static DrvOpt pstex_options[] = { { "color", DRV_INT, &color, "Color Postscript/LaTeX (color=1|0)" }, { NULL, DRV_INT, NULL, NULL} }; void plD_dispatch_init_pstex( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Combined Postscript/LaTeX files"; pdt->pl_DevName = "pstex"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 41; pdt->pl_init = (plD_init_fp) plD_init_pstex; pdt->pl_line = (plD_line_fp) plD_line_ps; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_ps; pdt->pl_eop = (plD_eop_fp) plD_eop_ps; pdt->pl_bop = (plD_bop_fp) plD_bop_pstex; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_pstex; pdt->pl_state = (plD_state_fp) plD_state_ps; pdt->pl_esc = (plD_esc_fp) plD_esc_pstex; } void plD_init_pstex( PLStream *pls ) { char *ofile; size_t len; PSDev *dev; FILE *fp; plParseDrvOpts( pstex_options ); if ( color ) plD_init_psc( pls ); // init color postscript driver else plD_init_ps( pls ); // init monochrome postscript driver dev = (PSDev *) pls->dev; pls->dev_text = 1; // want to draw text pls->dev_unicode = 0; // don't want unicode // open latex output file len = strlen( pls->FileName ) + 3; ofile = (char *) malloc( sizeof ( char ) * len ); snprintf( ofile, len, "%s_t", pls->FileName ); fp = fopen( ofile, "w" ); free( ofile ); dev->fp = fp; fprintf( fp, "\\begin{picture}(0,0)(0,0)%%\n" ); fprintf( fp, "\\includegraphics[scale=1.,clip]{%s}%%\n", pls->FileName ); fprintf( fp, "\\end{picture}%%\n" ); // fprintf(fp,"\\setlength{\\unitlength}{%fbp}%%\n", 72./25.4/pls->xpmm); fprintf( fp, "\\setlength{\\unitlength}{%fbp}%%\n", 1.0 / ENLARGE ); fprintf( fp, "\\begingroup\\makeatletter\\ifx\\SetFigFont\\undefined%%\n" ); fprintf( fp, "\\gdef\\SetFigFont#1#2#3#4#5{%%\n" ); fprintf( fp, "\\reset@font\\fontsize{#1}{#2pt}%%\n" ); fprintf( fp, "\\fontfamily{#3}\\fontseries{#4}\\fontshape{#5}%%\n" ); fprintf( fp, "\\selectfont}%%\n" ); fprintf( fp, "\\fi\\endgroup%%\n" ); dev->cur_pos = ftell( fp ); fprintf( fp, "\\begin{picture}(xxxxxx,xxxxxx)(xxxxxx,xxxxxx)%%\n" ); } void plD_esc_pstex( PLStream *pls, PLINT op, void *ptr ) { switch ( op ) { case PLESC_HAS_TEXT: proc_str( pls, ptr ); break; default: plD_esc_ps( pls, op, ptr ); } } void plD_bop_pstex( PLStream *pls ) { plD_bop_ps( pls ); plGetFam( pls ); } void plD_tidy_pstex( PLStream *pls ) { PSDev *dev = (PSDev *) pls->dev; PLFLT scale; FILE *fp; plD_tidy_ps( pls ); scale = pls->xpmm * 25.4 / 72.; fp = dev->fp; fprintf( fp, "\\end{picture}\n" ); fseek( fp, dev->cur_pos, SEEK_SET ); fprintf( fp, "\\begin{picture}(%d,%d)(%d,%d)%%\n%%", ROUND( ( dev->urx - dev->llx ) * scale ), ROUND( ( dev->ury - dev->lly ) * scale ), ROUND( ( dev->llx - XOFFSET ) * scale ), ROUND( ( dev->lly - YOFFSET ) * scale ) ); plCloseFile( pls ); } void proc_str( PLStream *pls, EscText *args ) { PLFLT *t = args->xform; PLFLT a1, alpha, ft_ht, angle; char cptr[256], jst, ref; PSDev *dev = (PSDev *) pls->dev; PLINT clxmin, clxmax, clymin, clymax; FILE *fp; fp = dev->fp; // font height ft_ht = 1.6 /*!*/ * pls->chrht * 72.0 / 25.4; /* ft_ht in points. ht is in mm */ // calculate baseline text angle angle = ( (PLFLT) ( ORIENTATION - 1 ) + pls->diorot ) * 90.; a1 = acos( t[0] ) * 180. / PI; if ( t[2] > 0. ) alpha = a1 - angle - 90.; else alpha = 360. - a1 - angle - 90.; // parse string for format (escape) characters parse_str( args->string, cptr ); // // Reference point (center baseline of string, not latex character reference point). // If base = 0, it is aligned with the center of the text box // If base = 1, it is aligned with the baseline of the text box // If base = 2, it is aligned with the top of the text box // Currently plplot only uses base=0 // if ( args->base == 2 ) // not supported by plplot ref = 't'; else if ( args->base == 1 ) ref = 'b'; else ref = 'c'; // // Text justification. Left, center and right justification, which // are the more common options, are supported; variable justification is // only approximate, based on plplot computation of it's string lenght // if ( args->just == 0.5 ) jst = 'c'; else if ( args->just == 1. ) jst = 'r'; else { jst = 'l'; args->x = args->refx; // use hints provided by plplot args->y = args->refy; } // apply transformations difilt( &args->x, &args->y, 1, &clxmin, &clxmax, &clymin, &clymax ); // check clip limits. For now, only the reference point of the string is checked; // but the the whole string should be checked -- using a postscript construct // such as gsave/clip/grestore. This method can also be applied to the xfig and // pstex drivers. Zoom side effect: the font size must be adjusted! if ( args->x < clxmin || args->x > clxmax || args->y < clymin || args->y > clymax ) return; plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &( args->x ), &( args->y ) ); #ifdef DEBUG fprintf( fp, "\\put(%d,%d){\\circle{10}}\n", args->x, args->y ); #endif fprintf( fp, "\\put(%d,%d){\\rotatebox{%.1f}{\\makebox(0,0)[%c%c]{\\SetFigFont{%.1f}{12}", args->x, args->y, alpha, jst, ref, ft_ht ); // // font family, serie and shape. Currently not supported by plplot // // Use current font instead: // 1: Normal font (latex document default font) // 2: Roman font // 3: Italic font (most probably latex slanted) // 4: Script font (latex sans serif) // switch ( pls->cfont ) { case ( 1 ): fprintf( fp, "{\\familydefault}" ); break; case ( 2 ): fprintf( fp, "{\\rmdefault}" ); break; case ( 3 ): fprintf( fp, "{\\itdefault}" ); break; case ( 4 ): fprintf( fp, "{\\sfdefault}" ); break; default: fprintf( fp, "{\\familydefault}" ); } fprintf( fp, "{\\mddefault}{\\updefault}\n" ); // font color. if ( color ) fprintf( fp, "\\special{ps: %.3f %.3f %.3f setrgbcolor}{", pls->curcolor.r / 255., pls->curcolor.g / 255., pls->curcolor.b / 255. ); else fprintf( fp, "\\special{ps: 0 0 0 setrgbcolor}{" ); fprintf( fp, "%% Your text follows:\n" ); fprintf( fp, "%s\n", cptr ); fprintf( fp, "}}}}" ); // // keep ps driver happy -- needed for background and orientation. // arghhh! can't calculate it, as I only have the string reference // point, not its extent! // Quick (and final?) *hack*, ASSUME that no more than a char height // extents after/before the string reference point. // dev->llx = MIN( dev->llx, args->x - ft_ht * 25.4 / 72. * pls->xpmm ); dev->lly = MIN( dev->lly, args->y - ft_ht * 25.4 / 72. * pls->ypmm ); dev->urx = MAX( dev->urx, args->x + ft_ht * 25.4 / 72. * pls->xpmm ); dev->ury = MAX( dev->ury, args->y + ft_ht * 25.4 / 72. * pls->ypmm ); } void parse_str( const char *str, char *dest ) { int n, opened = 0, raised = 0, overline = 0, underline = 0, fontset = 0, math = 0; char *tp = dest, c, esc; char greek[] = "abgGdDezyhHiklLmncCopPrsStuUfFxqQwW"; char *mathgreek[] = { "alpha", "beta", "gamma", "Gamma", "delta", "Delta", "epsilon", "zeta", "eta", "theta", "Theta", "iota", "kappa", "lambda", "Lambda", "mu", "nu", "xi", "Xi", "o", "pi", "Pi", "rho", "sigma", "Sigma","tau", "upsilon", "Upsilon", "phi", "Phi", "chi", "psi", "Psi", "omega", "Omega" }; plgesc( &esc ); while ( *str ) { if ( *str != esc ) { *tp++ = *str++; continue; } str++; switch ( *str++ ) { case 'u': // up one level if ( raised < 0 ) { *tp++ = '}'; opened--; } else { n = sprintf( tp, "\\raisebox{%.2fex}{", 0.6 ); tp += n; opened++; } raised++; break; case 'd': // down one level if ( raised > 0 ) { *tp++ = '}'; opened--; } else { n = sprintf( tp, "\\raisebox{%.2fex}{", -0.6 ); tp += n; opened++; } raised--; break; case 'b': // backspace n = sprintf( tp, "\\hspace{-1em}" ); tp += n; break; case '+': // toggles overline mode. Side effect, enter math mode. if ( overline ) { if ( --math ) *tp++ = '}'; else { n = sprintf( tp, "}$" ); tp += n; } overline--; opened--; } else { if ( !math ) *tp++ = '$'; n = sprintf( tp, "\\overline{" ); tp += n; overline++; opened++; math++; } break; case '-': // toggles underline mode. Side effect, enter math mode. if ( underline ) { if ( --math ) *tp++ = '}'; else { n = sprintf( tp, "}$" ); tp += n; } underline--; opened--; } else { if ( !math ) *tp++ = '$'; n = sprintf( tp, "\\underline{" ); tp += n; underline++; opened++; math++; } break; case 'g': // greek letter corresponding to roman letter x c = *str++; n = plP_strpos( greek, c ); if ( n != -1 ) { if ( !math ) *tp++ = '$'; *tp++ = '\\'; strcpy( tp, mathgreek[n] ); if ( isupper( c ) ) *tp = toupper( *tp ); tp += strlen( mathgreek[n] ); if ( !math ) *tp++ = '$'; } else *tp++ = c; break; case '(': // Hershey symbol number (nnn) (any number of digits) FIXME ??? plwarn( "'g(...)' text escape sequence not processed." ); while ( *str++ != ')' ) ; break; case 'f': // switch font switch ( *str++ ) { case 'n': // Normal while ( fontset-- ) { *tp++ = '}'; opened--; } if ( math ) { *tp++ = '$'; math = 0; } n = sprintf( tp, "\\normalfont " ); tp += n; break; case 'r': // Roman if ( math ) n = sprintf( tp, "\\mathrm{" ); else n = sprintf( tp, "\\textrm{" ); tp += n; opened++; fontset++; break; case 'i': // Italic if ( math ) n = sprintf( tp, "\\mathit{" ); else n = sprintf( tp, "\\textit{" ); tp += n; opened++; fontset++; break; case 's': // Script. Don't, use sans serif if ( math ) n = sprintf( tp, "\\mathsf{" ); else n = sprintf( tp, "\\textsf{" ); tp += n; opened++; fontset++; break; } default: if ( *str == esc ) *tp++ = esc; } } while ( opened-- ) *tp++ = '}'; *tp = '\0'; } #else int pldummy_pstex() { return 0; } #endif // PLD_pstexdev