PLplot  5.10.0
svg.c
Go to the documentation of this file.
00001 // November 8, 2006
00002 //
00003 // PLplot driver for SVG 1.1 (http://www.w3.org/Graphics/SVG/)
00004 //
00005 // Copyright (C) 2006 Hazen Babcock
00006 //
00007 // This file is part of PLplot.
00008 //
00009 // PLplot is free software; you can redistribute it and/or modify
00010 // it under the terms of the GNU Library General Public License as published
00011 // by the Free Software Foundation; either version 2 of the License, or
00012 // (at your option) any later version.
00013 //
00014 // PLplot is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 // GNU Library General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU Library General Public License
00020 // along with PLplot; if not, write to the Free Software
00021 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00022 //
00023 //
00024 
00025 //---------------------------------------------
00026 // Header files, defines and local variables
00027 // ---------------------------------------------
00028 
00029 #include <stdarg.h>
00030 #include <math.h>
00031 
00032 // PLplot header files
00033 
00034 #include "plplotP.h"
00035 #include "drivers.h"
00036 
00037 // constants
00038 
00039 #define SVG_Default_X      720
00040 #define SVG_Default_Y      540
00041 
00042 #define POINTS_PER_INCH    72
00043 
00044 #define MAX_STRING_LEN     1000
00045 
00046 // This has been generated empirically by looking carefully at results from
00047 // examples 1 and 2.
00048 
00049 #define FONT_SIZE_RATIO      1.34
00050 #define FONT_SHIFT_RATIO     0.705
00051 #define FONT_SHIFT_OFFSET    0.5
00052 
00053 // local variables
00054 
00055 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_svg = "svg:Scalable Vector Graphics (SVG 1.1):1:svg:57:svg\n";
00056 
00057 static int    already_warned = 0;
00058 
00059 static int    text_clipping = 1;
00060 static DrvOpt svg_options[] = { { "text_clipping", DRV_INT, &text_clipping, "Use text clipping (text_clipping=0|1)" } };
00061 
00062 typedef struct
00063 {
00064     short textClipping;
00065     int   canvasXSize;
00066     int   canvasYSize;
00067     PLFLT scale;
00068     int   svgIndent;
00069     FILE  *svgFile;
00070     int   gradient_index;
00071     //  char curColor[7];
00072 } SVG;
00073 
00074 // font stuff
00075 
00076 // Debugging extras
00077 
00078 //-----------------------------------------------
00079 // function declarations
00080 // -----------------------------------------------
00081 
00082 // Functions for writing XML SVG tags to a file
00083 
00084 static void svg_open( SVG *, const char * );
00085 static void svg_open_end( SVG * );
00086 static void svg_attr_value( SVG *, const char *, const char * );
00087 static void svg_attr_values( SVG *, const char *, const char *, ... );
00088 static void svg_close( SVG *, const char * );
00089 static void svg_general( SVG *, const char * );
00090 static void svg_indent( SVG * );
00091 static void svg_stroke_width( PLStream * );
00092 static void svg_stroke_color( PLStream * );
00093 static void svg_fill_color( PLStream * );
00094 static void svg_fill_background_color( PLStream * );
00095 static int svg_family_check( PLStream * );
00096 
00097 
00098 // General
00099 
00100 static void poly_line( PLStream *, short *, short *, PLINT, short );
00101 static void gradient( PLStream *, short *, short *, PLINT );
00102 static void write_hex( FILE *, unsigned char );
00103 static void write_unicode( FILE *, PLUNICODE );
00104 static void specify_font( FILE *, PLUNICODE );
00105 
00106 // String processing
00107 
00108 static void proc_str( PLStream *, EscText * );
00109 
00110 // PLplot interface functions
00111 
00112 void plD_dispatch_init_svg( PLDispatchTable *pdt );
00113 void plD_init_svg( PLStream * );
00114 void plD_line_svg( PLStream *, short, short, short, short );
00115 void plD_polyline_svg( PLStream *, short *, short *, PLINT );
00116 void plD_eop_svg( PLStream * );
00117 void plD_bop_svg( PLStream * );
00118 void plD_tidy_svg( PLStream * );
00119 void plD_state_svg( PLStream *, PLINT );
00120 void plD_esc_svg( PLStream *, PLINT, void * );
00121 
00122 //--------------------------------------------------------------------------
00123 // dispatch_init_init()
00124 //
00125 // Initialize device dispatch table
00126 //--------------------------------------------------------------------------
00127 
00128 void plD_dispatch_init_svg( PLDispatchTable *pdt )
00129 {
00130 #ifndef ENABLE_DYNDRIVERS
00131     pdt->pl_MenuStr = "Scalable Vector Graphics (SVG 1.1)";
00132     pdt->pl_DevName = "svg";
00133 #endif
00134     pdt->pl_type     = plDevType_FileOriented;
00135     pdt->pl_seq      = 57;
00136     pdt->pl_init     = (plD_init_fp) plD_init_svg;
00137     pdt->pl_line     = (plD_line_fp) plD_line_svg;
00138     pdt->pl_polyline = (plD_polyline_fp) plD_polyline_svg;
00139     pdt->pl_eop      = (plD_eop_fp) plD_eop_svg;
00140     pdt->pl_bop      = (plD_bop_fp) plD_bop_svg;
00141     pdt->pl_tidy     = (plD_tidy_fp) plD_tidy_svg;
00142     pdt->pl_state    = (plD_state_fp) plD_state_svg;
00143     pdt->pl_esc      = (plD_esc_fp) plD_esc_svg;
00144 }
00145 
00146 //--------------------------------------------------------------------------
00147 // svg_init()
00148 //
00149 // Initialize device
00150 //--------------------------------------------------------------------------
00151 
00152 void plD_init_svg( PLStream *pls )
00153 {
00154     SVG *aStream;
00155 
00156     pls->termin  = 0;                   // not an interactive device
00157     pls->color   = 1;                   // supports color
00158     pls->width   = 1;
00159     pls->verbose = 1;
00160     pls->bytecnt = 0;
00161     //pls->debug = 1;
00162     pls->dev_text     = 1;      // handles text
00163     pls->dev_unicode  = 1;      // wants text as unicode
00164     pls->page         = 0;
00165     pls->dev_fill0    = 1;      // driver generates solid fills
00166     pls->dev_fill1    = 0;      // Use PLplot core fallback for pattern fills
00167     pls->dev_gradient = 1;      // driver renders gradient
00168 
00169     pls->graphx = GRAPHICS_MODE;
00170 
00171     if ( !pls->colorset )
00172         pls->color = 1;
00173 
00174     // Initialize family file info
00175     plFamInit( pls );
00176 
00177     // Prompt for a file name if not already set
00178     plOpenFile( pls );
00179 // Allocate and initialize device-specific data
00180 
00181     if ( pls->dev != NULL )
00182         free( (void *) pls->dev );
00183 
00184     pls->dev = calloc( 1, (size_t) sizeof ( SVG ) );
00185     if ( pls->dev == NULL )
00186         plexit( "plD_init_svg: Out of memory." );
00187 
00188     aStream = (SVG *) pls->dev;
00189 
00190     // Set the bounds for plotting in points (unit of 1/72 of an inch).  Default is SVG_Default_X points x SVG_Default_Y points unless otherwise specified by plspage or -geometry option.
00191 
00192     if ( pls->xlength <= 0 || pls->ylength <= 0 )
00193     {
00194         aStream->canvasXSize = SVG_Default_X;
00195         aStream->canvasYSize = SVG_Default_Y;
00196     }
00197     else
00198     {
00199         aStream->canvasXSize = pls->xlength;
00200         aStream->canvasYSize = pls->ylength;
00201     }
00202     // Calculate ratio of (larger) internal PLplot coordinates to external
00203     // coordinates used for svg file.
00204     if ( aStream->canvasXSize > aStream->canvasYSize )
00205         aStream->scale = (PLFLT) ( PIXELS_X - 1 ) / (PLFLT) aStream->canvasXSize;
00206     else
00207         aStream->scale = (PLFLT) PIXELS_Y / (PLFLT) aStream->canvasYSize;
00208     plP_setphy( (PLINT) 0, (PLINT) ( aStream->scale * aStream->canvasXSize ), (PLINT) 0, (PLINT) ( aStream->scale * aStream->canvasYSize ) ); // Scaled points.
00209 
00210     plP_setpxl( aStream->scale * POINTS_PER_INCH / 25.4, aStream->scale * POINTS_PER_INCH / 25.4 );                                           // Scaled points/mm.
00211 
00212     aStream->svgFile = pls->OutFile;
00213 
00214     // Handle the text clipping option
00215     plParseDrvOpts( svg_options );
00216 
00217     // Turn on text clipping if the user desires this
00218     if ( text_clipping )
00219     {
00220         aStream->textClipping = 1;
00221     }
00222     aStream->textClipping = (short) text_clipping;
00223 
00224     aStream->svgIndent      = 0;
00225     aStream->gradient_index = 0;
00226     svg_general( aStream, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
00227     svg_general( aStream, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n" );
00228     svg_general( aStream, "        \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n" );
00229 }
00230 
00231 //--------------------------------------------------------------------------
00232 // svg_bop()
00233 //
00234 // Set up for the next page.
00235 //--------------------------------------------------------------------------
00236 
00237 void plD_bop_svg( PLStream *pls )
00238 {
00239     SVG *aStream;
00240 
00241     // Plot familying stuff. Not really understood, just copying gd.c
00242     plGetFam( pls );
00243 // n.b. pls->dev can change because of an indirect call to plD_init_svg
00244 // from plGetFam if familying is enabled.  Thus, wait to define aStream until
00245 // now.
00246     aStream = pls->dev;
00247 
00248     pls->famadv = 1;
00249     pls->page++;
00250     if ( svg_family_check( pls ) )
00251     {
00252         return;
00253     }
00254 
00255     // write opening svg tag for the new page
00256 
00257     svg_open( aStream, "svg" );
00258     svg_attr_value( aStream, "xmlns", "http://www.w3.org/2000/svg" );
00259     svg_attr_value( aStream, "xmlns:xlink", "http://www.w3.org/1999/xlink" );
00260     svg_attr_value( aStream, "version", "1.1" );
00261     // svg_attr_values("width", "%dcm", (int)((double)canvasXSize/POINTS_PER_INCH * 2.54));
00262     // svg_attr_values("height", "%dcm", (int)((double)canvasYSize/POINTS_PER_INCH * 2.54));
00263     svg_attr_values( aStream, "width", "%dpt", aStream->canvasXSize );
00264     svg_attr_values( aStream, "height", "%dpt", aStream->canvasYSize );
00265     svg_attr_values( aStream, "viewBox", "%d %d %d %d", 0, 0, aStream->canvasXSize, aStream->canvasYSize );
00266     svg_general( aStream, ">\n" );
00267 
00268     // set the background by drawing a rectangle that is the size of
00269     // of the canvas and filling it with the background color.
00270 
00271     svg_open( aStream, "rect" );
00272     svg_attr_values( aStream, "x", "%d", 0 );
00273     svg_attr_values( aStream, "y", "%d", 0 );
00274     svg_attr_values( aStream, "width", "%d", aStream->canvasXSize );
00275     svg_attr_values( aStream, "height", "%d", aStream->canvasYSize );
00276     svg_attr_value( aStream, "stroke", "none" );
00277     svg_fill_background_color( pls );
00278     svg_open_end( aStream );
00279 
00280     // invert the coordinate system so that PLplot graphs appear right side up
00281 
00282     svg_open( aStream, "g" );
00283     svg_attr_values( aStream, "transform", "matrix(1 0 0 -1 0 %d)", aStream->canvasYSize );
00284     svg_general( aStream, ">\n" );
00285 }
00286 
00287 //--------------------------------------------------------------------------
00288 // svg_line()
00289 //
00290 // Draw a line in the current color from (x1,y1) to (x2,y2).
00291 //--------------------------------------------------------------------------
00292 
00293 void plD_line_svg( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
00294 {
00295     SVG *aStream;
00296 
00297     aStream = pls->dev;
00298 
00299     if ( svg_family_check( pls ) )
00300     {
00301         return;
00302     }
00303     svg_open( aStream, "polyline" );
00304     svg_stroke_width( pls );
00305     svg_stroke_color( pls );
00306     svg_attr_value( aStream, "fill", "none" );
00307     // svg_attr_value(aStream, "shape-rendering", "crispEdges");
00308     svg_attr_values( aStream, "points", "%r,%r %r,%r", (double) x1a / aStream->scale, (double) y1a / aStream->scale, (double) x2a / aStream->scale, (double) y2a / aStream->scale );
00309     svg_open_end( aStream );
00310 }
00311 
00312 //--------------------------------------------------------------------------
00313 // svg_polyline()
00314 //
00315 // Draw a polyline in the current color.
00316 //--------------------------------------------------------------------------
00317 
00318 void plD_polyline_svg( PLStream *pls, short *xa, short *ya, PLINT npts )
00319 {
00320     if ( svg_family_check( pls ) )
00321     {
00322         return;
00323     }
00324     poly_line( pls, xa, ya, npts, 0 );
00325 }
00326 
00327 //--------------------------------------------------------------------------
00328 // svg_eop()
00329 //
00330 // End of page
00331 //--------------------------------------------------------------------------
00332 
00333 void plD_eop_svg( PLStream *pls )
00334 {
00335     SVG *aStream;
00336 
00337     aStream = pls->dev;
00338 
00339     if ( svg_family_check( pls ) )
00340     {
00341         return;
00342     }
00343     // write the closing svg tag
00344 
00345     svg_close( aStream, "g" );
00346     svg_close( aStream, "svg" );
00347 }
00348 
00349 //--------------------------------------------------------------------------
00350 // svg_tidy()
00351 //
00352 // Close graphics file or otherwise clean up.
00353 //--------------------------------------------------------------------------
00354 
00355 void plD_tidy_svg( PLStream *pls )
00356 {
00357     if ( svg_family_check( pls ) )
00358     {
00359         return;
00360     }
00361     plCloseFile( pls );
00362 }
00363 
00364 //--------------------------------------------------------------------------
00365 // plD_state_svg()
00366 //
00367 // Handle change in PLStream state (color, pen width, fill attribute, etc).
00368 //
00369 // Nothing is done here because these attributes are aquired from
00370 // PLStream for each element that is drawn.
00371 //--------------------------------------------------------------------------
00372 
00373 void plD_state_svg( PLStream *PL_UNUSED( pls ), PLINT PL_UNUSED( op ) )
00374 {
00375 }
00376 
00377 //--------------------------------------------------------------------------
00378 // svg_esc()
00379 //
00380 // Escape function.
00381 //--------------------------------------------------------------------------
00382 
00383 void plD_esc_svg( PLStream *pls, PLINT op, void *ptr )
00384 {
00385     if ( svg_family_check( pls ) )
00386     {
00387         return;
00388     }
00389     switch ( op )
00390     {
00391     case PLESC_FILL:      // fill polygon
00392         poly_line( pls, pls->dev_x, pls->dev_y, pls->dev_npts, 1 );
00393         break;
00394     case PLESC_GRADIENT:      // render gradient inside polygon
00395         gradient( pls, pls->dev_x, pls->dev_y, pls->dev_npts );
00396         break;
00397     case PLESC_HAS_TEXT:  // render text
00398         proc_str( pls, (EscText *) ptr );
00399         break;
00400     }
00401 }
00402 
00403 //--------------------------------------------------------------------------
00404 // poly_line()
00405 //
00406 // Handles drawing filled and unfilled polygons
00407 //--------------------------------------------------------------------------
00408 
00409 void poly_line( PLStream *pls, short *xa, short *ya, PLINT npts, short fill )
00410 {
00411     int i;
00412     SVG *aStream;
00413 
00414     aStream = pls->dev;
00415 
00416     svg_open( aStream, "polyline" );
00417     if ( fill )
00418     {
00419         // Two adjacent regions will put non-zero width boundary strokes on top
00420         // of each other on their common boundary.  Thus, a stroke on the boundary
00421         // of a filled region is generally a bad idea when the fill is partially
00422         // opaque because the partial opacity of the two boundary strokes which
00423         // are on top of each other will mutually interfere and produce a
00424         // bad-looking result.  On the other hand, for completely opaque fills
00425         // a boundary stroke is a good idea since if it is of sufficient width
00426         // it will keep the background from leaking through at the anti-aliased
00427         // edges of filled regions that have a common boundary with other
00428         // filled regions.
00429         if ( pls->curcolor.a < 0.99 )
00430         {
00431             svg_attr_value( aStream, "stroke", "none" );
00432         }
00433         else
00434         {
00435             svg_stroke_width( pls );
00436             svg_stroke_color( pls );
00437         }
00438         svg_fill_color( pls );
00439         if ( pls->dev_eofill )
00440             svg_attr_value( aStream, "fill-rule", "evenodd" );
00441         else
00442             svg_attr_value( aStream, "fill-rule", "nonzero" );
00443     }
00444     else
00445     {
00446         svg_stroke_width( pls );
00447         svg_stroke_color( pls );
00448         svg_attr_value( aStream, "fill", "none" );
00449     }
00450     //svg_attr_value(aStream, "shape-rendering", "crispEdges");
00451     svg_indent( aStream );
00452     fprintf( aStream->svgFile, "points=\"" );
00453     for ( i = 0; i < npts; i++ )
00454     {
00455         fprintf( aStream->svgFile, "%.2f,%.2f ", (double) xa[i] / aStream->scale, (double) ya[i] / aStream->scale );
00456         if ( ( ( i + 1 ) % 10 ) == 0 )
00457         {
00458             fprintf( aStream->svgFile, "\n" );
00459             svg_indent( aStream );
00460         }
00461     }
00462     fprintf( aStream->svgFile, "\"/>\n" );
00463     aStream->svgIndent -= 2;
00464 }
00465 
00466 //--------------------------------------------------------------------------
00467 // gradient()
00468 //
00469 // Draws gradient
00470 //--------------------------------------------------------------------------
00471 
00472 void gradient( PLStream *pls, short *xa, short *ya, PLINT npts )
00473 {
00474     int  i;
00475     // 27 should be the maximum needed below, but be generous.
00476     char buffer[50];
00477     SVG  *aStream;
00478 
00479     aStream = pls->dev;
00480 
00481     svg_open( aStream, "g>" );
00482     svg_open( aStream, "defs>" );
00483     svg_open( aStream, "linearGradient" );
00484     // Allows ~2^31 unique gradient id's, gradient_index incremented below.
00485     sprintf( buffer, "MyGradient%010d", aStream->gradient_index );
00486     svg_attr_value( aStream, "id", buffer );
00487     svg_attr_value( aStream, "gradientUnits", "userSpaceOnUse" );
00488     sprintf( buffer, "%.2f", pls->xgradient[0] / aStream->scale );
00489     svg_attr_value( aStream, "x1", buffer );
00490     sprintf( buffer, "%.2f", pls->ygradient[0] / aStream->scale );
00491     svg_attr_value( aStream, "y1", buffer );
00492     sprintf( buffer, "%.2f", pls->xgradient[1] / aStream->scale );
00493     svg_attr_value( aStream, "x2", buffer );
00494     sprintf( buffer, "%.2f", pls->ygradient[1] / aStream->scale );
00495     svg_attr_value( aStream, "y2", buffer );
00496     svg_general( aStream, ">\n" );
00497 
00498     for ( i = 0; i < pls->ncol1; i++ )
00499     {
00500         svg_indent( aStream );
00501         fprintf( aStream->svgFile, "<stop offset=\"%.3f\" ",
00502             (double) i / (double) ( pls->ncol1 - 1 ) );
00503         fprintf( aStream->svgFile, "stop-color=\"#" );
00504         write_hex( aStream->svgFile, pls->cmap1[i].r );
00505         write_hex( aStream->svgFile, pls->cmap1[i].g );
00506         write_hex( aStream->svgFile, pls->cmap1[i].b );
00507         fprintf( aStream->svgFile, "\" " );
00508         fprintf( aStream->svgFile, "stop-opacity=\"%.3f\"/>\n", pls->cmap1[i].a );
00509     }
00510 
00511     svg_close( aStream, "linearGradient" );
00512     svg_close( aStream, "defs" );
00513     svg_open( aStream, "polyline" );
00514     sprintf( buffer, "url(#MyGradient%010d)", aStream->gradient_index++ );
00515     svg_attr_value( aStream, "fill", buffer );
00516     svg_indent( aStream );
00517     fprintf( aStream->svgFile, "points=\"" );
00518     for ( i = 0; i < npts; i++ )
00519     {
00520         fprintf( aStream->svgFile, "%.2f,%.2f ", (double) xa[i] / aStream->scale, (double) ya[i] / aStream->scale );
00521         if ( ( ( i + 1 ) % 10 ) == 0 )
00522         {
00523             fprintf( aStream->svgFile, "\n" );
00524             svg_indent( aStream );
00525         }
00526     }
00527     fprintf( aStream->svgFile, "\"/>\n" );
00528     aStream->svgIndent -= 2;
00529     svg_close( aStream, "g" );
00530 }
00531 
00532 //--------------------------------------------------------------------------
00533 // proc_str()
00534 //
00535 // Processes strings for display.
00536 //
00537 // NOTE:
00538 //
00539 // (1) This was tested on Firefox and Camino where it seemed to display
00540 // text properly. However, it isn't obvious to me that these browsers
00541 // conform to the specification. Basically the issue is that some of
00542 // the text properties (i.e. dy) that you specify inside a tspan element
00543 // remain in force until the end of the text element. It would seem to
00544 // me that they should only apply inside the tspan tag. To get around
00545 // this, and because it was easier anyway, I used what is essentially
00546 // a list of tspan tags rather than a tree of tspan tags. Perhaps
00547 // better described as a tree with one branch?
00548 //
00549 // (2) To deal with the some whitespace annoyances, the entire text
00550 // element must be written on a single line. If there are lots of
00551 // format characters then this line might end up being too long
00552 // for some SVG implementations.
00553 //
00554 // (3) Text placement is not ideal. Vertical offset seems to be
00555 // particularly troublesome.
00556 //
00557 // (4) See additional notes in specify_font re. to sans / serif
00558 //
00559 //--------------------------------------------------------------------------
00560 
00561 void proc_str( PLStream *pls, EscText *args )
00562 {
00563     char         plplot_esc;
00564     static short which_clip = 0;
00565     short        i;
00566     short        totalTags = 1;
00567     short        ucs4Len   = (short) args->unicode_array_len;
00568     double       ftHt, scaled_offset, scaled_ftHt;
00569     PLUNICODE    fci;
00570     PLINT        rcx[4], rcy[4];
00571     static PLINT prev_rcx[4], prev_rcy[4];
00572     PLFLT        rotation, shear, stride, cos_rot, sin_rot, sin_shear, cos_shear;
00573     PLFLT        t[4];
00574     int          glyph_size, sum_glyph_size;
00575     short        if_write;
00576     //   PLFLT *t = args->xform;
00577     PLUNICODE    *ucs4 = args->unicode_array;
00578     SVG          *aStream;
00579     PLFLT        old_sscale, sscale, old_soffset, soffset, old_dup, ddup;
00580     PLINT        level;
00581     PLINT        same_clip;
00582 
00583     // check that we got unicode
00584     if ( ucs4Len == 0 )
00585     {
00586         printf( "Non unicode string passed to SVG driver, ignoring\n" );
00587         return;
00588     }
00589 
00590     // get plplot escape character and the current font
00591     plgesc( &plplot_esc );
00592     plgfci( &fci );
00593 
00594     // determine the font height in points.
00595     ftHt = FONT_SIZE_RATIO * pls->chrht * POINTS_PER_INCH / 25.4;
00596 
00597     // Setup & apply text clipping area if desired
00598     aStream = (SVG *) pls->dev;
00599     if ( aStream->textClipping )
00600     {
00601         // Use PLplot core routine difilt_clip to appropriately
00602         // transform the coordinates of the clipping rectangle
00603         difilt_clip( rcx, rcy );
00604         same_clip = TRUE;
00605         if ( which_clip == 0 )
00606         {
00607             same_clip = FALSE;
00608         }
00609         else
00610         {
00611             for ( i = 0; i < 4; i++ )
00612             {
00613                 if ( rcx[i] != prev_rcx[i] ||
00614                      rcy[i] != prev_rcy[i] )
00615                     same_clip = FALSE;
00616             }
00617         }
00618         if ( !same_clip )
00619         {
00620             svg_open( aStream, "clipPath" );
00621             svg_attr_values( aStream, "id", "text-clipping%d", which_clip );
00622             svg_general( aStream, ">\n" );
00623 
00624             // Output a polygon to represent the clipping region.
00625             svg_open( aStream, "polygon" );
00626             svg_attr_values( aStream,
00627                 "points",
00628                 "%f,%f %f,%f %f,%f %f,%f",
00629                 ( (PLFLT) rcx[0] ) / aStream->scale,
00630                 ( (PLFLT) rcy[0] ) / aStream->scale,
00631                 ( (PLFLT) rcx[1] ) / aStream->scale,
00632                 ( (PLFLT) rcy[1] ) / aStream->scale,
00633                 ( (PLFLT) rcx[2] ) / aStream->scale,
00634                 ( (PLFLT) rcy[2] ) / aStream->scale,
00635                 ( (PLFLT) rcx[3] ) / aStream->scale,
00636                 ( (PLFLT) rcy[3] ) / aStream->scale );
00637             svg_open_end( aStream );
00638 
00639             svg_close( aStream, "clipPath" );
00640             for ( i = 0; i < 4; i++ )
00641             {
00642                 prev_rcx[i] = rcx[i];
00643                 prev_rcy[i] = rcy[i];
00644             }
00645             which_clip++;
00646         }
00647         svg_open( aStream, "g" );
00648         svg_attr_values( aStream, "clip-path",
00649             "url(#text-clipping%d)", which_clip - 1 );
00650         svg_general( aStream, ">\n" );
00651     }
00652 
00653     // This draws the clipping region on the screen which can
00654     // be very helpful for debugging.
00655 
00656     //
00657     // svg_open(aStream, "polygon");
00658     // svg_attr_values(aStream,
00659     //              "points",
00660     //              "%f,%f %f,%f %f,%f %f,%f",
00661     //              ((PLFLT)rcx[0])/aStream->scale,
00662     //              ((PLFLT)rcy[0])/aStream->scale,
00663     //              ((PLFLT)rcx[1])/aStream->scale,
00664     //              ((PLFLT)rcy[1])/aStream->scale,
00665     //              ((PLFLT)rcx[2])/aStream->scale,
00666     //              ((PLFLT)rcy[2])/aStream->scale,
00667     //              ((PLFLT)rcx[3])/aStream->scale,
00668     //              ((PLFLT)rcy[3])/aStream->scale);
00669     // svg_stroke_width(pls);
00670     // svg_stroke_color(pls);
00671     // svg_attr_value(aStream, "fill", "none");
00672     // svg_open_end(aStream);
00673     //
00674 
00675     // Calculate the tranformation matrix for SVG based on the
00676     // transformation matrix provided by PLplot.
00677     plRotationShear( args->xform, &rotation, &shear, &stride );
00678     // N.B. Experimentally, I (AWI) have found the svg rotation angle is
00679     // the negative of the libcairo rotation angle, and the svg shear angle
00680     // is pi minus the libcairo shear angle.
00681     rotation -= pls->diorot * PI / 2.0;
00682     cos_rot   = cos( rotation );
00683     sin_rot   = -sin( rotation );
00684     sin_shear = sin( shear );
00685     cos_shear = -cos( shear );
00686     t[0]      = cos_rot * stride;
00687     t[1]      = -sin_rot * stride;
00688     t[2]      = cos_rot * sin_shear + sin_rot * cos_shear;
00689     t[3]      = -sin_rot * sin_shear + cos_rot * cos_shear;
00690 
00691     //--------------
00692     // open text tag
00693     // --------------
00694 
00695     svg_open( aStream, "text" );
00696 
00697     svg_attr_value( aStream, "dominant-baseline", "no-change" );
00698 
00699     // set font color
00700     svg_fill_color( pls );
00701 
00702     // white space preserving mode
00703     svg_attr_value( aStream, "xml:space", "preserve" );
00704 
00705     // set the font size
00706     svg_attr_values( aStream, "font-size", "%d", (int) ftHt );
00707 
00708     // Apply coordinate transform for text display.
00709     // The transformation also defines the location of the text in x and y.
00710     svg_attr_values( aStream, "transform", "matrix(%f %f %f %f %f %f)",
00711         t[0], t[1], t[2], t[3],
00712         (double) ( args->x / aStream->scale ),
00713         (double) ( args->y / aStream->scale ) );
00714 
00715 
00716     //----------------------------------------------------------
00717     // Write the text with formatting
00718     // We just keep stacking up tspan tags, then close them all
00719     // after we have written out all of the text.
00720     // ----------------------------------------------------------
00721 
00722     // For if_write = 0, we write nothing and instead accumulate the
00723     // sum_glyph_size from the fontsize of the individual glyphs which
00724     // is then used to figure out the initial x position from text-anchor and
00725     // args->just that is used to write out the SVG xml for if_write = 1.
00726 
00727     glyph_size     = (int) ftHt;
00728     sum_glyph_size = 0;
00729     if_write       = 0;
00730     while ( if_write < 2 )
00731     {
00732         if ( if_write == 1 )
00733         {
00734             //printf("number of characters = %f\n", sum_glyph_size/ftHt);
00735             // The above coordinate transform defines the _raw_ x position of the
00736             // text without justification so this attribute value depends on
00737             // text-anchor and args->just*sum_glyph_size
00738             // N.B. sum_glyph_size calculation only correct for monospaced fonts
00739             // so generally sum_glyph_size will be overestimated by various amounts
00740             // depending on what glyphs are to be rendered, the font, etc.  However,
00741             // this correction is differential respect to the end points or the
00742             // middle so you should be okay so long as you don't deviate too far
00743             // from those anchor points.
00744             if ( args->just < 0.33 )
00745             {
00746                 svg_attr_value( aStream, "text-anchor", "start" ); // left justification
00747                 svg_attr_values( aStream, "x", "%f", (double) ( -args->just * sum_glyph_size ) );
00748             }
00749             else if ( args->just > 0.66 )
00750             {
00751                 svg_attr_value( aStream, "text-anchor", "end" ); // right justification
00752                 svg_attr_values( aStream, "x", "%f", (double) ( ( 1. - args->just ) * sum_glyph_size ) );
00753             }
00754             else
00755             {
00756                 svg_attr_value( aStream, "text-anchor", "middle" ); // center
00757                 svg_attr_values( aStream, "x", "%f", (double) ( ( 0.5 - args->just ) * sum_glyph_size ) );
00758             }
00759 
00760             // The text goes at zero in y since the above
00761             // coordinate transform defines the y position of the text
00762             svg_attr_values( aStream, "y", "%f",
00763                 FONT_SHIFT_RATIO * 0.5 * ftHt +
00764                 FONT_SHIFT_OFFSET );
00765 
00766             fprintf( aStream->svgFile, ">" );
00767 
00768             // specify the initial font
00769             specify_font( aStream->svgFile, fci );
00770         }
00771         i           = 0;
00772         scaled_ftHt = ftHt;
00773         level       = 0;
00774         ddup        = 0.;
00775         while ( i < ucs4Len )
00776         {
00777             if ( ucs4[i] < PL_FCI_MARK )                 // not a font change
00778             {
00779                 if ( ucs4[i] != (PLUNICODE) plplot_esc ) // a character to display
00780                 {
00781                     if ( if_write )
00782                     {
00783                         write_unicode( aStream->svgFile, ucs4[i] );
00784                     }
00785                     else
00786                     {
00787                         sum_glyph_size += glyph_size;
00788                     }
00789                     i++;
00790                     continue;
00791                 }
00792                 i++;
00793                 if ( ucs4[i] == (PLUNICODE) plplot_esc ) // a escape character to display
00794                 {
00795                     if ( if_write )
00796                     {
00797                         write_unicode( aStream->svgFile, ucs4[i] );
00798                     }
00799                     else
00800                     {
00801                         sum_glyph_size += glyph_size;
00802                     }
00803                     i++;
00804                     continue;
00805                 }
00806                 else
00807                 {
00808                     // super/subscript logic follows that in plstr routine (plsym.c)
00809                     // for Hershey fonts. Factor of FONT_SHIFT_RATIO*0.80 is empirical
00810                     // adjustment.
00811                     if ( ucs4[i] == (PLUNICODE) 'u' ) // Superscript
00812                     {
00813                         plP_script_scale( TRUE, &level,
00814                             &old_sscale, &sscale, &old_soffset, &soffset );
00815                         // The correction for the difference in magnitude
00816                         // between the baseline and middle coordinate systems
00817                         // for superscripts should be
00818                         // 0.5*(base font size - superscript/subscript font size).
00819                         old_dup = ddup;
00820                         ddup    = 0.5 * ( 1.0 - sscale );
00821                         if ( level <= 0 )
00822                         {
00823                             scaled_offset = FONT_SHIFT_RATIO * ftHt * ( 0.80 * ( soffset - old_soffset ) - ( ddup - old_dup ) );
00824                         }
00825                         else
00826                         {
00827                             scaled_offset = -FONT_SHIFT_RATIO * ftHt * ( 0.80 * ( soffset - old_soffset ) + ( ddup - old_dup ) );
00828                         }
00829                         scaled_ftHt = sscale * ftHt;
00830                         if ( if_write )
00831                         {
00832                             totalTags++;
00833                             fprintf( aStream->svgFile, "<tspan dy=\"%f\" font-size=\"%d\">", scaled_offset, (int) scaled_ftHt );
00834                         }
00835                         else
00836                         {
00837                             glyph_size = (int) scaled_ftHt;
00838                         }
00839                     }
00840                     if ( ucs4[i] == (PLUNICODE) 'd' ) // Subscript
00841                     {
00842                         plP_script_scale( FALSE, &level,
00843                             &old_sscale, &sscale, &old_soffset, &soffset );
00844                         // The correction for the difference in magnitude
00845                         // between the baseline and middle coordinate systems
00846                         // for superscripts should be
00847                         // 0.5*(base font size - superscript/subscript font size).
00848                         old_dup = ddup;
00849                         ddup    = 0.5 * ( 1.0 - sscale );
00850                         if ( level < 0 )
00851                         {
00852                             scaled_offset = FONT_SHIFT_RATIO * ftHt * ( 0.80 * ( soffset - old_soffset ) - ( ddup - old_dup ) );
00853                         }
00854                         else
00855                         {
00856                             scaled_offset = -FONT_SHIFT_RATIO * ftHt * ( 0.80 * ( soffset - old_soffset ) + ( ddup - old_dup ) );
00857                         }
00858                         scaled_ftHt = sscale * ftHt;
00859                         if ( if_write )
00860                         {
00861                             totalTags++;
00862                             fprintf( aStream->svgFile, "<tspan dy=\"%f\" font-size=\"%d\">", scaled_offset, (int) scaled_ftHt );
00863                         }
00864                         else
00865                         {
00866                             glyph_size = (int) scaled_ftHt;
00867                         }
00868                     }
00869                     i++;
00870                 }
00871             }
00872             else // a font change
00873             {
00874                 if ( if_write )
00875                 {
00876                     specify_font( aStream->svgFile, ucs4[i] );
00877                     totalTags++;
00878                 }
00879                 i++;
00880             }
00881         }
00882         if_write++;
00883     }
00884 
00885     //----------------------------------------------
00886     // close out all the tspan tags and the text tag
00887     // ----------------------------------------------
00888 
00889     for ( i = 0; i < totalTags; i++ )
00890     {
00891         fprintf( aStream->svgFile, "</tspan>" );
00892     }
00893     // The following commented out (by AWI) because it is a bad idea to
00894     // put line ends in the middle of a text tag.  This was the key to
00895     // all the text rendering issues we had.
00896     //fprintf(svgFile,"\n");
00897     // For the same reason use fprintf and svgIndent -= 2;
00898     // to close the text tag rather than svg_close("text"); since
00899     // we don't want indentation spaces entering the text.
00900     // svg_close("text");
00901     fprintf( aStream->svgFile, "</text>\n" );
00902     aStream->svgIndent -= 2;
00903     if ( aStream->textClipping )
00904     {
00905         svg_close( aStream, "g" );
00906     }
00907 }
00908 
00909 //--------------------------------------------------------------------------
00910 // svg_open ()
00911 //
00912 // Used to open a new XML expression, sets the indent level appropriately
00913 //--------------------------------------------------------------------------
00914 
00915 void svg_open( SVG *aStream, const char *tag )
00916 {
00917     svg_indent( aStream );
00918     fprintf( aStream->svgFile, "<%s\n", tag );
00919     aStream->svgIndent += 2;
00920 }
00921 
00922 //--------------------------------------------------------------------------
00923 // svg_open_end ()
00924 //
00925 // Used to end the opening of a new XML expression i.e. add
00926 // the final ">".
00927 //--------------------------------------------------------------------------
00928 
00929 void svg_open_end( SVG *aStream )
00930 {
00931     svg_indent( aStream );
00932     fprintf( aStream->svgFile, "/>\n" );
00933     aStream->svgIndent -= 2;
00934 }
00935 
00936 //--------------------------------------------------------------------------
00937 // svg_attr_value ()
00938 //
00939 // Prints two strings to svgFile as a XML attribute value pair
00940 // i.e. foo="bar"
00941 //--------------------------------------------------------------------------
00942 
00943 void svg_attr_value( SVG *aStream, const char *attribute, const char *value )
00944 {
00945     svg_indent( aStream );
00946     fprintf( aStream->svgFile, "%s=\"%s\"\n", attribute, value );
00947 }
00948 
00949 //--------------------------------------------------------------------------
00950 // svg_attr_values ()
00951 //
00952 // Prints a string and a bunch of numbers / strings as a XML attribute
00953 // value pair i.e. foo="0 10 1.0 5.3 bar"
00954 //
00955 // This function is derived from an example in
00956 // "The C Programming Language" by Kernighan and Ritchie.
00957 //
00958 //--------------------------------------------------------------------------
00959 
00960 void svg_attr_values( SVG *aStream, const char *attribute, const char *format, ... )
00961 {
00962     va_list    ap;
00963     const char *p, *sval;
00964     int        ival;
00965     double     dval;
00966 
00967     svg_indent( aStream );
00968     fprintf( aStream->svgFile, "%s=\"", attribute );
00969     va_start( ap, format );
00970     for ( p = format; *p; p++ )
00971     {
00972         if ( *p != '%' )
00973         {
00974             fprintf( aStream->svgFile, "%c", *p );
00975             continue;
00976         }
00977         switch ( *++p )
00978         {
00979         case 'd':
00980             ival = va_arg( ap, int );
00981             fprintf( aStream->svgFile, "%d", ival );
00982             break;
00983         case 'f':
00984             dval = va_arg( ap, double );
00985             fprintf( aStream->svgFile, "%f", dval );
00986             break;
00987         case 'r':
00988             // r is non-standard, but use it here to format rounded value
00989             dval = va_arg( ap, double );
00990             fprintf( aStream->svgFile, "%.2f", dval );
00991             break;
00992         case 's':
00993             sval = va_arg( ap, char * );
00994             fprintf( aStream->svgFile, "%s", sval );
00995             break;
00996         default:
00997             fprintf( aStream->svgFile, "%c", *p );
00998             break;
00999         }
01000     }
01001     fprintf( aStream->svgFile, "\"\n" );
01002     va_end( ap );
01003 }
01004 
01005 //--------------------------------------------------------------------------
01006 // svg_close ()
01007 //
01008 // Used to close a XML expression, sets the indent level appropriately
01009 //--------------------------------------------------------------------------
01010 
01011 void svg_close( SVG *aStream, const char *tag )
01012 {
01013     aStream->svgIndent -= 2;
01014     svg_indent( aStream );
01015     if ( strlen( tag ) > 0 )
01016     {
01017         fprintf( aStream->svgFile, "</%s>\n", tag );
01018     }
01019     else
01020     {
01021         fprintf( aStream->svgFile, "/>\n" );
01022     }
01023 }
01024 
01025 //--------------------------------------------------------------------------
01026 // svg_general ()
01027 //
01028 // Used to print any text into the svgFile
01029 //--------------------------------------------------------------------------
01030 
01031 void svg_general( SVG *aStream, const char *text )
01032 {
01033     svg_indent( aStream );
01034     fprintf( aStream->svgFile, "%s", text );
01035 }
01036 
01037 //--------------------------------------------------------------------------
01038 // svg_indent ()
01039 //
01040 // Indents properly based on the current indent level
01041 //--------------------------------------------------------------------------
01042 
01043 void svg_indent( SVG *aStream )
01044 {
01045     short i;
01046     for ( i = 0; i < aStream->svgIndent; i++ )
01047     {
01048         fprintf( aStream->svgFile, " " );
01049     }
01050 }
01051 
01052 //--------------------------------------------------------------------------
01053 // svg_stroke_width ()
01054 //
01055 // sets the stroke width based on the current width
01056 //--------------------------------------------------------------------------
01057 
01058 void svg_stroke_width( PLStream *pls )
01059 {
01060     SVG *aStream;
01061 
01062     aStream = pls->dev;
01063     svg_indent( aStream );
01064     fprintf( aStream->svgFile, "stroke-width=\"%e\"\n", pls->width );
01065 }
01066 
01067 //--------------------------------------------------------------------------
01068 // svg_stroke_color ()
01069 //
01070 // sets the stroke color based on the current color
01071 //--------------------------------------------------------------------------
01072 
01073 void svg_stroke_color( PLStream *pls )
01074 {
01075     SVG *aStream;
01076 
01077     aStream = pls->dev;
01078     svg_indent( aStream );
01079     fprintf( aStream->svgFile, "stroke=\"#" );
01080     write_hex( aStream->svgFile, pls->curcolor.r );
01081     write_hex( aStream->svgFile, pls->curcolor.g );
01082     write_hex( aStream->svgFile, pls->curcolor.b );
01083     fprintf( aStream->svgFile, "\"\n" );
01084     svg_indent( aStream );
01085     fprintf( aStream->svgFile, "stroke-opacity=\"%f\"\n", pls->curcolor.a );
01086 }
01087 
01088 //--------------------------------------------------------------------------
01089 // svg_fill_color ()
01090 //
01091 // sets the fill color based on the current color
01092 //--------------------------------------------------------------------------
01093 
01094 void svg_fill_color( PLStream *pls )
01095 {
01096     SVG *aStream;
01097 
01098     aStream = pls->dev;
01099     svg_indent( aStream );
01100     fprintf( aStream->svgFile, "fill=\"#" );
01101     write_hex( aStream->svgFile, pls->curcolor.r );
01102     write_hex( aStream->svgFile, pls->curcolor.g );
01103     write_hex( aStream->svgFile, pls->curcolor.b );
01104     fprintf( aStream->svgFile, "\"\n" );
01105     svg_indent( aStream );
01106     fprintf( aStream->svgFile, "fill-opacity=\"%f\"\n", pls->curcolor.a );
01107 }
01108 
01109 //--------------------------------------------------------------------------
01110 // svg_fill_background_color ()
01111 //
01112 // sets the background fill color based on the current background color
01113 //--------------------------------------------------------------------------
01114 
01115 void svg_fill_background_color( PLStream *pls )
01116 {
01117     SVG *aStream;
01118 
01119     aStream = pls->dev;
01120     svg_indent( aStream );
01121     fprintf( aStream->svgFile, "fill=\"#" );
01122     write_hex( aStream->svgFile, pls->cmap0[0].r );
01123     write_hex( aStream->svgFile, pls->cmap0[0].g );
01124     write_hex( aStream->svgFile, pls->cmap0[0].b );
01125     fprintf( aStream->svgFile, "\"\n" );
01126     svg_indent( aStream );
01127     fprintf( aStream->svgFile, "fill-opacity=\"%f\"\n", pls->cmap0[0].a );
01128 }
01129 
01130 //--------------------------------------------------------------------------
01131 // svg_family_check ()
01132 //
01133 // support function to help supress more than one page if family file
01134 // output not specified by the user  (e.g., with the -fam command-line option).
01135 //--------------------------------------------------------------------------
01136 
01137 int svg_family_check( PLStream *pls )
01138 {
01139     if ( pls->family || pls->page == 1 )
01140     {
01141         return 0;
01142     }
01143     else
01144     {
01145         if ( !already_warned )
01146         {
01147             already_warned = 1;
01148             plwarn( "All pages after the first skipped because family file output not specified.\n" );
01149         }
01150         return 1;
01151     }
01152 }
01153 
01154 //--------------------------------------------------------------------------
01155 // write_hex ()
01156 //
01157 // writes a unsigned char as an appropriately formatted hex value
01158 //--------------------------------------------------------------------------
01159 
01160 void write_hex( FILE *svgFile, unsigned char val )
01161 {
01162     if ( val < 16 )
01163     {
01164         fprintf( svgFile, "0%X", val );
01165     }
01166     else
01167     {
01168         fprintf( svgFile, "%X", val );
01169     }
01170 }
01171 
01172 //--------------------------------------------------------------------------
01173 // write_unicode ()
01174 //
01175 // writes a unicode character, appropriately formatted (i.e. &#xNNN)
01176 // with invalid xml characters replaced by ' '.
01177 //--------------------------------------------------------------------------
01178 
01179 void write_unicode( FILE *svgFile, PLUNICODE ucs4_char )
01180 {
01181     if ( ucs4_char >= ' ' || ucs4_char == '\t' || ucs4_char == '\n' || ucs4_char == '\r' )
01182         fprintf( svgFile, "&#x%x;", ucs4_char );
01183     else
01184         fprintf( svgFile, "&#x%x;", ' ' );
01185 }
01186 
01187 //--------------------------------------------------------------------------
01188 // specify_font ()
01189 //
01190 // Note:
01191 // We don't actually specify a font, just the fonts properties.
01192 // The hope is that this will give the display program the freedom
01193 // to choose the font with the glyphs that it needs to display
01194 // the text.
01195 //
01196 // Known Issues:
01197 // (1) On OS-X 10.4 with Firefox and Camino the "serif" font-family
01198 // looks more like the "italic" font-style.
01199 //
01200 //--------------------------------------------------------------------------
01201 
01202 void specify_font( FILE *svgFile, PLUNICODE ucs4_char )
01203 {
01204     fprintf( svgFile, "<tspan " );
01205 
01206     // sans, serif, mono, script, symbol
01207 
01208     if ( ( ucs4_char & 0x00F ) == 0x000 )
01209     {
01210         fprintf( svgFile, "font-family=\"sans-serif\" " );
01211     }
01212     else if ( ( ucs4_char & 0x00F ) == 0x001 )
01213     {
01214         fprintf( svgFile, "font-family=\"serif\" " );
01215     }
01216     else if ( ( ucs4_char & 0x00F ) == 0x002 )
01217     {
01218         fprintf( svgFile, "font-family=\"mono-space\" " );
01219     }
01220     else if ( ( ucs4_char & 0x00F ) == 0x003 )
01221     {
01222         fprintf( svgFile, "font-family=\"cursive\" " );
01223     }
01224     else if ( ( ucs4_char & 0x00F ) == 0x004 )
01225     {
01226         // this should be symbol, but that doesn't seem to be available
01227         fprintf( svgFile, "font-family=\"sans-serif\" " );
01228     }
01229 
01230     // normal, italic, oblique
01231 
01232     if ( ( ucs4_char & 0x0F0 ) == 0x000 )
01233     {
01234         fprintf( svgFile, "font-style=\"normal\" " );
01235     }
01236     else if ( ( ucs4_char & 0x0F0 ) == 0x010 )
01237     {
01238         fprintf( svgFile, "font-style=\"italic\" " );
01239     }
01240     else if ( ( ucs4_char & 0x0F0 ) == 0x020 )
01241     {
01242         fprintf( svgFile, "font-style=\"oblique\" " );
01243     }
01244 
01245     // normal, bold
01246 
01247     if ( ( ucs4_char & 0xF00 ) == 0x000 )
01248     {
01249         fprintf( svgFile, "font-weight=\"normal\">" );
01250     }
01251     else if ( ( ucs4_char & 0xF00 ) == 0x100 )
01252     {
01253         fprintf( svgFile, "font-weight=\"bold\">" );
01254     }
01255 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines