• Main Page
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

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 *, char * );
00085 static void svg_open_end( SVG * );
00086 static void svg_attr_value( SVG *, char *, char * );
00087 static void svg_attr_values( SVG *, char *, char *, ... );
00088 static void svg_close( SVG *, char * );
00089 static void svg_general( SVG *, 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 = 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     SVG *aStream;
00358 
00359     aStream = pls->dev;
00360     if ( svg_family_check( pls ) )
00361     {
00362         return;
00363     }
00364     plCloseFile( pls );
00365 }
00366 
00367 //--------------------------------------------------------------------------
00368 // plD_state_svg()
00369 //
00370 // Handle change in PLStream state (color, pen width, fill attribute, etc).
00371 //
00372 // Nothing is done here because these attributes are aquired from
00373 // PLStream for each element that is drawn.
00374 //--------------------------------------------------------------------------
00375 
00376 void plD_state_svg( PLStream *pls, PLINT op )
00377 {
00378 }
00379 
00380 //--------------------------------------------------------------------------
00381 // svg_esc()
00382 //
00383 // Escape function.
00384 //--------------------------------------------------------------------------
00385 
00386 void plD_esc_svg( PLStream *pls, PLINT op, void *ptr )
00387 {
00388     if ( svg_family_check( pls ) )
00389     {
00390         return;
00391     }
00392     switch ( op )
00393     {
00394     case PLESC_FILL:      // fill polygon
00395         poly_line( pls, pls->dev_x, pls->dev_y, pls->dev_npts, 1 );
00396         break;
00397     case PLESC_GRADIENT:      // render gradient inside polygon
00398         gradient( pls, pls->dev_x, pls->dev_y, pls->dev_npts );
00399         break;
00400     case PLESC_HAS_TEXT:  // render text
00401         proc_str( pls, (EscText *) ptr );
00402         break;
00403     }
00404 }
00405 
00406 //--------------------------------------------------------------------------
00407 // poly_line()
00408 //
00409 // Handles drawing filled and unfilled polygons
00410 //--------------------------------------------------------------------------
00411 
00412 void poly_line( PLStream *pls, short *xa, short *ya, PLINT npts, short fill )
00413 {
00414     int i;
00415     SVG *aStream;
00416 
00417     aStream = pls->dev;
00418 
00419     svg_open( aStream, "polyline" );
00420     if ( fill )
00421     {
00422         // Two adjacent regions will put non-zero width boundary strokes on top
00423         // of each other on their common boundary.  Thus, a stroke on the boundary
00424         // of a filled region is generally a bad idea when the fill is partially
00425         // opaque because the partial opacity of the two boundary strokes which
00426         // are on top of each other will mutually interfere and produce a
00427         // bad-looking result.  On the other hand, for completely opaque fills
00428         // a boundary stroke is a good idea since if it is of sufficient width
00429         // it will keep the background from leaking through at the anti-aliased
00430         // edges of filled regions that have a common boundary with other
00431         // filled regions.
00432         if ( pls->curcolor.a < 0.99 )
00433         {
00434             svg_attr_value( aStream, "stroke", "none" );
00435         }
00436         else
00437         {
00438             svg_stroke_width( pls );
00439             svg_stroke_color( pls );
00440         }
00441         svg_fill_color( pls );
00442         if ( pls->dev_eofill )
00443             svg_attr_value( aStream, "fill-rule", "evenodd" );
00444         else
00445             svg_attr_value( aStream, "fill-rule", "nonzero" );
00446     }
00447     else
00448     {
00449         svg_stroke_width( pls );
00450         svg_stroke_color( pls );
00451         svg_attr_value( aStream, "fill", "none" );
00452     }
00453     //svg_attr_value(aStream, "shape-rendering", "crispEdges");
00454     svg_indent( aStream );
00455     fprintf( aStream->svgFile, "points=\"" );
00456     for ( i = 0; i < npts; i++ )
00457     {
00458         fprintf( aStream->svgFile, "%.2f,%.2f ", (double) xa[i] / aStream->scale, (double) ya[i] / aStream->scale );
00459         if ( ( ( i + 1 ) % 10 ) == 0 )
00460         {
00461             fprintf( aStream->svgFile, "\n" );
00462             svg_indent( aStream );
00463         }
00464     }
00465     fprintf( aStream->svgFile, "\"/>\n" );
00466     aStream->svgIndent -= 2;
00467 }
00468 
00469 //--------------------------------------------------------------------------
00470 // gradient()
00471 //
00472 // Draws gradient
00473 //--------------------------------------------------------------------------
00474 
00475 void gradient( PLStream *pls, short *xa, short *ya, PLINT npts )
00476 {
00477     int  i;
00478     // 27 should be the maximum needed below, but be generous.
00479     char buffer[50];
00480     SVG  *aStream;
00481 
00482     aStream = pls->dev;
00483 
00484     svg_open( aStream, "g>" );
00485     svg_open( aStream, "defs>" );
00486     svg_open( aStream, "linearGradient" );
00487     // Allows ~2^31 unique gradient id's, gradient_index incremented below.
00488     sprintf( buffer, "MyGradient%010d", aStream->gradient_index );
00489     svg_attr_value( aStream, "id", buffer );
00490     svg_attr_value( aStream, "gradientUnits", "userSpaceOnUse" );
00491     sprintf( buffer, "%.2f", pls->xgradient[0] / aStream->scale );
00492     svg_attr_value( aStream, "x1", buffer );
00493     sprintf( buffer, "%.2f", pls->ygradient[0] / aStream->scale );
00494     svg_attr_value( aStream, "y1", buffer );
00495     sprintf( buffer, "%.2f", pls->xgradient[1] / aStream->scale );
00496     svg_attr_value( aStream, "x2", buffer );
00497     sprintf( buffer, "%.2f", pls->ygradient[1] / aStream->scale );
00498     svg_attr_value( aStream, "y2", buffer );
00499     svg_general( aStream, ">\n" );
00500 
00501     for ( i = 0; i < pls->ncol1; i++ )
00502     {
00503         svg_indent( aStream );
00504         fprintf( aStream->svgFile, "<stop offset=\"%.3f\" ",
00505             (double) i / (double) ( pls->ncol1 - 1 ) );
00506         fprintf( aStream->svgFile, "stop-color=\"#" );
00507         write_hex( aStream->svgFile, pls->cmap1[i].r );
00508         write_hex( aStream->svgFile, pls->cmap1[i].g );
00509         write_hex( aStream->svgFile, pls->cmap1[i].b );
00510         fprintf( aStream->svgFile, "\" " );
00511         fprintf( aStream->svgFile, "stop-opacity=\"%.3f\"/>\n", pls->cmap1[i].a );
00512     }
00513 
00514     svg_close( aStream, "linearGradient" );
00515     svg_close( aStream, "defs" );
00516     svg_open( aStream, "polyline" );
00517     sprintf( buffer, "url(#MyGradient%010d)", aStream->gradient_index++ );
00518     svg_attr_value( aStream, "fill", buffer );
00519     svg_indent( aStream );
00520     fprintf( aStream->svgFile, "points=\"" );
00521     for ( i = 0; i < npts; i++ )
00522     {
00523         fprintf( aStream->svgFile, "%.2f,%.2f ", (double) xa[i] / aStream->scale, (double) ya[i] / aStream->scale );
00524         if ( ( ( i + 1 ) % 10 ) == 0 )
00525         {
00526             fprintf( aStream->svgFile, "\n" );
00527             svg_indent( aStream );
00528         }
00529     }
00530     fprintf( aStream->svgFile, "\"/>\n" );
00531     aStream->svgIndent -= 2;
00532     svg_close( aStream, "g" );
00533 }
00534 
00535 //--------------------------------------------------------------------------
00536 // proc_str()
00537 //
00538 // Processes strings for display.
00539 //
00540 // NOTE:
00541 //
00542 // (1) This was tested on Firefox and Camino where it seemed to display
00543 // text properly. However, it isn't obvious to me that these browsers
00544 // conform to the specification. Basically the issue is that some of
00545 // the text properties (i.e. dy) that you specify inside a tspan element
00546 // remain in force until the end of the text element. It would seem to
00547 // me that they should only apply inside the tspan tag. To get around
00548 // this, and because it was easier anyway, I used what is essentially
00549 // a list of tspan tags rather than a tree of tspan tags. Perhaps
00550 // better described as a tree with one branch?
00551 //
00552 // (2) To deal with the some whitespace annoyances, the entire text
00553 // element must be written on a single line. If there are lots of
00554 // format characters then this line might end up being too long
00555 // for some SVG implementations.
00556 //
00557 // (3) Text placement is not ideal. Vertical offset seems to be
00558 // particularly troublesome.
00559 //
00560 // (4) See additional notes in specify_font re. to sans / serif
00561 //
00562 //--------------------------------------------------------------------------
00563 
00564 void proc_str( PLStream *pls, EscText *args )
00565 {
00566     char         plplot_esc;
00567     short static which_clip = 0;
00568     short        i;
00569     short        totalTags = 1;
00570     short        ucs4Len   = args->unicode_array_len;
00571     double       ftHt, scaled_offset, scaled_ftHt;
00572     PLUNICODE    fci;
00573     PLINT        rcx[4], rcy[4];
00574     PLFLT        rotation, shear, stride, cos_rot, sin_rot, sin_shear, cos_shear;
00575     PLFLT        t[4];
00576     int          glyph_size, sum_glyph_size;
00577     short        if_write;
00578     //   PLFLT *t = args->xform;
00579     PLUNICODE    *ucs4 = args->unicode_array;
00580     SVG          *aStream;
00581     PLFLT        old_sscale, sscale, old_soffset, soffset, old_dup, dup;
00582     PLINT        level;
00583 
00584     // check that we got unicode
00585     if ( ucs4Len == 0 )
00586     {
00587         printf( "Non unicode string passed to SVG driver, ignoring\n" );
00588         return;
00589     }
00590 
00591     // get plplot escape character and the current font
00592     plgesc( &plplot_esc );
00593     plgfci( &fci );
00594 
00595     // determine the font height in points.
00596     ftHt = FONT_SIZE_RATIO * pls->chrht * POINTS_PER_INCH / 25.4;
00597 
00598     // Setup & apply text clipping area if desired
00599     aStream = (SVG *) pls->dev;
00600     if ( aStream->textClipping )
00601     {
00602         svg_open( aStream, "clipPath" );
00603         svg_attr_values( aStream, "id", "text-clipping%d", which_clip );
00604         svg_general( aStream, ">\n" );
00605 
00606         // Use PLplot core routine difilt_clip to appropriately
00607         // transform the coordinates of the clipping rectangle
00608         difilt_clip( rcx, rcy );
00609 
00610         // Output a polygon to represent the clipping region.
00611         svg_open( aStream, "polygon" );
00612         svg_attr_values( aStream,
00613             "points",
00614             "%f,%f %f,%f %f,%f %f,%f",
00615             ( (PLFLT) rcx[0] ) / aStream->scale,
00616             ( (PLFLT) rcy[0] ) / aStream->scale,
00617             ( (PLFLT) rcx[1] ) / aStream->scale,
00618             ( (PLFLT) rcy[1] ) / aStream->scale,
00619             ( (PLFLT) rcx[2] ) / aStream->scale,
00620             ( (PLFLT) rcy[2] ) / aStream->scale,
00621             ( (PLFLT) rcx[3] ) / aStream->scale,
00622             ( (PLFLT) rcy[3] ) / aStream->scale );
00623         svg_open_end( aStream );
00624 
00625         svg_close( aStream, "clipPath" );
00626         svg_open( aStream, "g" );
00627         svg_attr_values( aStream, "clip-path", "url(#text-clipping%d)", which_clip );
00628         svg_general( aStream, ">\n" );
00629 
00630         which_clip++;
00631     }
00632 
00633     // This draws the clipping region on the screen which can
00634     // be very helpful for debugging.
00635 
00636     //
00637     // svg_open(aStream, "polygon");
00638     // svg_attr_values(aStream,
00639     //              "points",
00640     //              "%f,%f %f,%f %f,%f %f,%f",
00641     //              ((PLFLT)rcx[0])/aStream->scale,
00642     //              ((PLFLT)rcy[0])/aStream->scale,
00643     //              ((PLFLT)rcx[1])/aStream->scale,
00644     //              ((PLFLT)rcy[1])/aStream->scale,
00645     //              ((PLFLT)rcx[2])/aStream->scale,
00646     //              ((PLFLT)rcy[2])/aStream->scale,
00647     //              ((PLFLT)rcx[3])/aStream->scale,
00648     //              ((PLFLT)rcy[3])/aStream->scale);
00649     // svg_stroke_width(pls);
00650     // svg_stroke_color(pls);
00651     // svg_attr_value(aStream, "fill", "none");
00652     // svg_open_end(aStream);
00653     //
00654 
00655     // Calculate the tranformation matrix for SVG based on the
00656     // transformation matrix provided by PLplot.
00657     plRotationShear( args->xform, &rotation, &shear, &stride );
00658     // N.B. Experimentally, I (AWI) have found the svg rotation angle is
00659     // the negative of the libcairo rotation angle, and the svg shear angle
00660     // is pi minus the libcairo shear angle.
00661     rotation -= pls->diorot * PI / 2.0;
00662     cos_rot   = cos( rotation );
00663     sin_rot   = -sin( rotation );
00664     sin_shear = sin( shear );
00665     cos_shear = -cos( shear );
00666     t[0]      = cos_rot * stride;
00667     t[1]      = -sin_rot * stride;
00668     t[2]      = cos_rot * sin_shear + sin_rot * cos_shear;
00669     t[3]      = -sin_rot * sin_shear + cos_rot * cos_shear;
00670 
00671     // Apply coordinate transform for text display.
00672     // The transformation also defines the location of the text in x and y.
00673     svg_open( aStream, "g" );
00674     svg_attr_values( aStream, "transform", "matrix(%f %f %f %f %f %f)", t[0], t[1], t[2], t[3], (double) ( args->x / aStream->scale ), (double) ( args->y / aStream->scale ) );
00675     svg_general( aStream, ">\n" );
00676 
00677     svg_open( aStream, "g" );
00678     svg_attr_values( aStream, "transform", "matrix(1.0 0.0 0.0 1.0 0.0 %f)", FONT_SHIFT_RATIO * 0.5 * ftHt + FONT_SHIFT_OFFSET );
00679     svg_general( aStream, ">\n" );
00680 
00681     //--------------
00682     // open text tag
00683     // --------------
00684 
00685     svg_open( aStream, "text" );
00686 
00687     svg_attr_value( aStream, "dominant-baseline", "no-change" );
00688 
00689     // set font color
00690     svg_fill_color( pls );
00691 
00692     // white space preserving mode
00693     svg_attr_value( aStream, "xml:space", "preserve" );
00694 
00695     // set the font size
00696     svg_attr_values( aStream, "font-size", "%d", (int) ftHt );
00697 
00698     //----------------------------------------------------------
00699     // Write the text with formatting
00700     // We just keep stacking up tspan tags, then close them all
00701     // after we have written out all of the text.
00702     // ----------------------------------------------------------
00703 
00704     // For if_write = 0, we write nothing and instead accumulate the
00705     // sum_glyph_size from the fontsize of the individual glyphs which
00706     // is then used to figure out the initial x position from text-anchor and
00707     // args->just that is used to write out the SVG xml for if_write = 1.
00708 
00709     glyph_size     = (int) ftHt;
00710     sum_glyph_size = 0;
00711     if_write       = 0;
00712     while ( if_write < 2 )
00713     {
00714         if ( if_write == 1 )
00715         {
00716             //printf("number of characters = %f\n", sum_glyph_size/ftHt);
00717             // The above coordinate transform defines the _raw_ x position of the
00718             // text without justification so this attribute value depends on
00719             // text-anchor and args->just*sum_glyph_size
00720             // N.B. sum_glyph_size calculation only correct for monospaced fonts
00721             // so generally sum_glyph_size will be overestimated by various amounts
00722             // depending on what glyphs are to be rendered, the font, etc.  However,
00723             // this correction is differential respect to the end points or the
00724             // middle so you should be okay so long as you don't deviate too far
00725             // from those anchor points.
00726             if ( args->just < 0.33 )
00727             {
00728                 svg_attr_value( aStream, "text-anchor", "start" ); // left justification
00729                 svg_attr_values( aStream, "x", "%f", (double) ( -args->just * sum_glyph_size ) );
00730             }
00731             else if ( args->just > 0.66 )
00732             {
00733                 svg_attr_value( aStream, "text-anchor", "end" ); // right justification
00734                 svg_attr_values( aStream, "x", "%f", (double) ( ( 1. - args->just ) * sum_glyph_size ) );
00735             }
00736             else
00737             {
00738                 svg_attr_value( aStream, "text-anchor", "middle" ); // center
00739                 svg_attr_values( aStream, "x", "%f", (double) ( ( 0.5 - args->just ) * sum_glyph_size ) );
00740             }
00741 
00742             // The text goes at zero in y since the above
00743             // coordinate transform defines the y position of the text
00744             svg_attr_value( aStream, "y", "0" );
00745             fprintf( aStream->svgFile, ">" );
00746 
00747             // specify the initial font
00748             specify_font( aStream->svgFile, fci );
00749         }
00750         i           = 0;
00751         scaled_ftHt = ftHt;
00752         level       = 0;
00753         dup         = 0.;
00754         while ( i < ucs4Len )
00755         {
00756             if ( ucs4[i] < PL_FCI_MARK )                 // not a font change
00757             {
00758                 if ( ucs4[i] != (PLUNICODE) plplot_esc ) // a character to display
00759                 {
00760                     if ( if_write )
00761                     {
00762                         write_unicode( aStream->svgFile, ucs4[i] );
00763                     }
00764                     else
00765                     {
00766                         sum_glyph_size += glyph_size;
00767                     }
00768                     i++;
00769                     continue;
00770                 }
00771                 i++;
00772                 if ( ucs4[i] == (PLUNICODE) plplot_esc ) // a escape character to display
00773                 {
00774                     if ( if_write )
00775                     {
00776                         write_unicode( aStream->svgFile, ucs4[i] );
00777                     }
00778                     else
00779                     {
00780                         sum_glyph_size += glyph_size;
00781                     }
00782                     i++;
00783                     continue;
00784                 }
00785                 else
00786                 {
00787                     // super/subscript logic follows that in plstr routine (plsym.c)
00788                     // for Hershey fonts. Factor of FONT_SHIFT_RATIO*0.80 is empirical
00789                     // adjustment.
00790                     if ( ucs4[i] == (PLUNICODE) 'u' ) // Superscript
00791                     {
00792                         plP_script_scale( TRUE, &level,
00793                             &old_sscale, &sscale, &old_soffset, &soffset );
00794                         // The correction for the difference in magnitude
00795                         // between the baseline and middle coordinate systems
00796                         // for superscripts should be
00797                         // 0.5*(base font size - superscript/subscript font size).
00798                         old_dup = dup;
00799                         dup     = 0.5 * ( 1.0 - sscale );
00800                         if ( level <= 0 )
00801                         {
00802                             scaled_offset = FONT_SHIFT_RATIO * ftHt * ( 0.80 * ( soffset - old_soffset ) - ( dup - old_dup ) );
00803                         }
00804                         else
00805                         {
00806                             scaled_offset = -FONT_SHIFT_RATIO * ftHt * ( 0.80 * ( soffset - old_soffset ) + ( dup - old_dup ) );
00807                         }
00808                         scaled_ftHt = sscale * ftHt;
00809                         if ( if_write )
00810                         {
00811                             totalTags++;
00812                             fprintf( aStream->svgFile, "<tspan dy=\"%f\" font-size=\"%d\">", scaled_offset, (int) scaled_ftHt );
00813                         }
00814                         else
00815                         {
00816                             glyph_size = (int) scaled_ftHt;
00817                         }
00818                     }
00819                     if ( ucs4[i] == (PLUNICODE) 'd' ) // Subscript
00820                     {
00821                         plP_script_scale( FALSE, &level,
00822                             &old_sscale, &sscale, &old_soffset, &soffset );
00823                         // The correction for the difference in magnitude
00824                         // between the baseline and middle coordinate systems
00825                         // for superscripts should be
00826                         // 0.5*(base font size - superscript/subscript font size).
00827                         old_dup = dup;
00828                         dup     = 0.5 * ( 1.0 - sscale );
00829                         if ( level < 0 )
00830                         {
00831                             scaled_offset = FONT_SHIFT_RATIO * ftHt * ( 0.80 * ( soffset - old_soffset ) - ( dup - old_dup ) );
00832                         }
00833                         else
00834                         {
00835                             scaled_offset = -FONT_SHIFT_RATIO * ftHt * ( 0.80 * ( soffset - old_soffset ) + ( dup - old_dup ) );
00836                         }
00837                         scaled_ftHt = sscale * ftHt;
00838                         if ( if_write )
00839                         {
00840                             totalTags++;
00841                             fprintf( aStream->svgFile, "<tspan dy=\"%f\" font-size=\"%d\">", scaled_offset, (int) scaled_ftHt );
00842                         }
00843                         else
00844                         {
00845                             glyph_size = (int) scaled_ftHt;
00846                         }
00847                     }
00848                     i++;
00849                 }
00850             }
00851             else // a font change
00852             {
00853                 if ( if_write )
00854                 {
00855                     specify_font( aStream->svgFile, ucs4[i] );
00856                     totalTags++;
00857                 }
00858                 i++;
00859             }
00860         }
00861         if_write++;
00862     }
00863 
00864     //----------------------------------------------
00865     // close out all the tspan tags and the text tag
00866     // ----------------------------------------------
00867 
00868     for ( i = 0; i < totalTags; i++ )
00869     {
00870         fprintf( aStream->svgFile, "</tspan>" );
00871     }
00872     // The following commented out (by AWI) because it is a bad idea to
00873     // put line ends in the middle of a text tag.  This was the key to
00874     // all the text rendering issues we had.
00875     //fprintf(svgFile,"\n");
00876     // For the same reason use fprintf and svgIndent -= 2;
00877     // to close the text tag rather than svg_close("text"); since
00878     // we don't want indentation spaces entering the text.
00879     // svg_close("text");
00880     fprintf( aStream->svgFile, "</text>\n" );
00881     aStream->svgIndent -= 2;
00882     svg_close( aStream, "g" );
00883     svg_close( aStream, "g" );
00884     if ( aStream->textClipping )
00885     {
00886         svg_close( aStream, "g" );
00887     }
00888 }
00889 
00890 //--------------------------------------------------------------------------
00891 // svg_open ()
00892 //
00893 // Used to open a new XML expression, sets the indent level appropriately
00894 //--------------------------------------------------------------------------
00895 
00896 void svg_open( SVG *aStream, char *tag )
00897 {
00898     svg_indent( aStream );
00899     fprintf( aStream->svgFile, "<%s\n", tag );
00900     aStream->svgIndent += 2;
00901 }
00902 
00903 //--------------------------------------------------------------------------
00904 // svg_open_end ()
00905 //
00906 // Used to end the opening of a new XML expression i.e. add
00907 // the final ">".
00908 //--------------------------------------------------------------------------
00909 
00910 void svg_open_end( SVG *aStream )
00911 {
00912     svg_indent( aStream );
00913     fprintf( aStream->svgFile, "/>\n" );
00914     aStream->svgIndent -= 2;
00915 }
00916 
00917 //--------------------------------------------------------------------------
00918 // svg_attr_value ()
00919 //
00920 // Prints two strings to svgFile as a XML attribute value pair
00921 // i.e. foo="bar"
00922 //--------------------------------------------------------------------------
00923 
00924 void svg_attr_value( SVG *aStream, char *attribute, char *value )
00925 {
00926     svg_indent( aStream );
00927     fprintf( aStream->svgFile, "%s=\"%s\"\n", attribute, value );
00928 }
00929 
00930 //--------------------------------------------------------------------------
00931 // svg_attr_values ()
00932 //
00933 // Prints a string and a bunch of numbers / strings as a XML attribute
00934 // value pair i.e. foo="0 10 1.0 5.3 bar"
00935 //
00936 // This function is derived from an example in
00937 // "The C Programming Language" by Kernighan and Ritchie.
00938 //
00939 //--------------------------------------------------------------------------
00940 
00941 void svg_attr_values( SVG *aStream, char *attribute, char *format, ... )
00942 {
00943     va_list ap;
00944     char    *p, *sval;
00945     int     ival;
00946     double  dval;
00947 
00948     svg_indent( aStream );
00949     fprintf( aStream->svgFile, "%s=\"", attribute );
00950     va_start( ap, format );
00951     for ( p = format; *p; p++ )
00952     {
00953         if ( *p != '%' )
00954         {
00955             fprintf( aStream->svgFile, "%c", *p );
00956             continue;
00957         }
00958         switch ( *++p )
00959         {
00960         case 'd':
00961             ival = va_arg( ap, int );
00962             fprintf( aStream->svgFile, "%d", ival );
00963             break;
00964         case 'f':
00965             dval = va_arg( ap, double );
00966             fprintf( aStream->svgFile, "%f", dval );
00967             break;
00968         case 'r':
00969             // r is non-standard, but use it here to format rounded value
00970             dval = va_arg( ap, double );
00971             fprintf( aStream->svgFile, "%.2f", dval );
00972             break;
00973         case 's':
00974             sval = va_arg( ap, char * );
00975             fprintf( aStream->svgFile, "%s", sval );
00976             break;
00977         default:
00978             fprintf( aStream->svgFile, "%c", *p );
00979             break;
00980         }
00981     }
00982     fprintf( aStream->svgFile, "\"\n" );
00983     va_end( ap );
00984 }
00985 
00986 //--------------------------------------------------------------------------
00987 // svg_close ()
00988 //
00989 // Used to close a XML expression, sets the indent level appropriately
00990 //--------------------------------------------------------------------------
00991 
00992 void svg_close( SVG *aStream, char *tag )
00993 {
00994     aStream->svgIndent -= 2;
00995     svg_indent( aStream );
00996     if ( strlen( tag ) > 0 )
00997     {
00998         fprintf( aStream->svgFile, "</%s>\n", tag );
00999     }
01000     else
01001     {
01002         fprintf( aStream->svgFile, "/>\n" );
01003     }
01004 }
01005 
01006 //--------------------------------------------------------------------------
01007 // svg_general ()
01008 //
01009 // Used to print any text into the svgFile
01010 //--------------------------------------------------------------------------
01011 
01012 void svg_general( SVG *aStream, char *text )
01013 {
01014     svg_indent( aStream );
01015     fprintf( aStream->svgFile, "%s", text );
01016 }
01017 
01018 //--------------------------------------------------------------------------
01019 // svg_indent ()
01020 //
01021 // Indents properly based on the current indent level
01022 //--------------------------------------------------------------------------
01023 
01024 void svg_indent( SVG *aStream )
01025 {
01026     short i;
01027     for ( i = 0; i < aStream->svgIndent; i++ )
01028     {
01029         fprintf( aStream->svgFile, " " );
01030     }
01031 }
01032 
01033 //--------------------------------------------------------------------------
01034 // svg_stroke_width ()
01035 //
01036 // sets the stroke width based on the current width
01037 // N.B. a stroke width of 0 in SVG means no stroke is painted so
01038 // we make sure the minimum value is 1.
01039 //--------------------------------------------------------------------------
01040 
01041 void svg_stroke_width( PLStream *pls )
01042 {
01043     SVG *aStream;
01044 
01045     aStream = pls->dev;
01046     svg_indent( aStream );
01047     fprintf( aStream->svgFile, "stroke-width=\"%d\"\n", MAX( 1, pls->width ) );
01048 }
01049 
01050 //--------------------------------------------------------------------------
01051 // svg_stroke_color ()
01052 //
01053 // sets the stroke color based on the current color
01054 //--------------------------------------------------------------------------
01055 
01056 void svg_stroke_color( PLStream *pls )
01057 {
01058     SVG *aStream;
01059 
01060     aStream = pls->dev;
01061     svg_indent( aStream );
01062     fprintf( aStream->svgFile, "stroke=\"#" );
01063     write_hex( aStream->svgFile, pls->curcolor.r );
01064     write_hex( aStream->svgFile, pls->curcolor.g );
01065     write_hex( aStream->svgFile, pls->curcolor.b );
01066     fprintf( aStream->svgFile, "\"\n" );
01067     svg_indent( aStream );
01068     fprintf( aStream->svgFile, "stroke-opacity=\"%f\"\n", pls->curcolor.a );
01069 }
01070 
01071 //--------------------------------------------------------------------------
01072 // svg_fill_color ()
01073 //
01074 // sets the fill color based on the current color
01075 //--------------------------------------------------------------------------
01076 
01077 void svg_fill_color( PLStream *pls )
01078 {
01079     SVG *aStream;
01080 
01081     aStream = pls->dev;
01082     svg_indent( aStream );
01083     fprintf( aStream->svgFile, "fill=\"#" );
01084     write_hex( aStream->svgFile, pls->curcolor.r );
01085     write_hex( aStream->svgFile, pls->curcolor.g );
01086     write_hex( aStream->svgFile, pls->curcolor.b );
01087     fprintf( aStream->svgFile, "\"\n" );
01088     svg_indent( aStream );
01089     fprintf( aStream->svgFile, "fill-opacity=\"%f\"\n", pls->curcolor.a );
01090 }
01091 
01092 //--------------------------------------------------------------------------
01093 // svg_fill_background_color ()
01094 //
01095 // sets the background fill color based on the current background color
01096 //--------------------------------------------------------------------------
01097 
01098 void svg_fill_background_color( PLStream *pls )
01099 {
01100     SVG *aStream;
01101 
01102     aStream = pls->dev;
01103     svg_indent( aStream );
01104     fprintf( aStream->svgFile, "fill=\"#" );
01105     write_hex( aStream->svgFile, pls->cmap0[0].r );
01106     write_hex( aStream->svgFile, pls->cmap0[0].g );
01107     write_hex( aStream->svgFile, pls->cmap0[0].b );
01108     fprintf( aStream->svgFile, "\"\n" );
01109     svg_indent( aStream );
01110     fprintf( aStream->svgFile, "fill-opacity=\"%f\"\n", pls->cmap0[0].a );
01111 }
01112 
01113 //--------------------------------------------------------------------------
01114 // svg_family_check ()
01115 //
01116 // support function to help supress more than one page if family file
01117 // output not specified by the user  (e.g., with the -fam command-line option).
01118 //--------------------------------------------------------------------------
01119 
01120 int svg_family_check( PLStream *pls )
01121 {
01122     if ( pls->family || pls->page == 1 )
01123     {
01124         return 0;
01125     }
01126     else
01127     {
01128         if ( !already_warned )
01129         {
01130             already_warned = 1;
01131             plwarn( "All pages after the first skipped because family file output not specified.\n" );
01132         }
01133         return 1;
01134     }
01135 }
01136 
01137 //--------------------------------------------------------------------------
01138 // write_hex ()
01139 //
01140 // writes a unsigned char as an appropriately formatted hex value
01141 //--------------------------------------------------------------------------
01142 
01143 void write_hex( FILE *svgFile, unsigned char val )
01144 {
01145     if ( val < 16 )
01146     {
01147         fprintf( svgFile, "0%X", val );
01148     }
01149     else
01150     {
01151         fprintf( svgFile, "%X", val );
01152     }
01153 }
01154 
01155 //--------------------------------------------------------------------------
01156 // write_unicode ()
01157 //
01158 // writes a unicode character, appropriately formatted (i.e. &#xNNN)
01159 // with invalid xml characters replaced by ' '.
01160 //--------------------------------------------------------------------------
01161 
01162 void write_unicode( FILE *svgFile, PLUNICODE ucs4_char )
01163 {
01164     if ( ucs4_char >= ' ' || ucs4_char == '\t' || ucs4_char == '\n' || ucs4_char == '\r' )
01165         fprintf( svgFile, "&#x%x;", ucs4_char );
01166     else
01167         fprintf( svgFile, "&#x%x;", ' ' );
01168 }
01169 
01170 //--------------------------------------------------------------------------
01171 // specify_font ()
01172 //
01173 // Note:
01174 // We don't actually specify a font, just the fonts properties.
01175 // The hope is that this will give the display program the freedom
01176 // to choose the font with the glyphs that it needs to display
01177 // the text.
01178 //
01179 // Known Issues:
01180 // (1) On OS-X 10.4 with Firefox and Camino the "serif" font-family
01181 // looks more like the "italic" font-style.
01182 //
01183 //--------------------------------------------------------------------------
01184 
01185 void specify_font( FILE *svgFile, PLUNICODE ucs4_char )
01186 {
01187     fprintf( svgFile, "<tspan " );
01188 
01189     // sans, serif, mono, script, symbol
01190 
01191     if ( ( ucs4_char & 0x00F ) == 0x000 )
01192     {
01193         fprintf( svgFile, "font-family=\"sans-serif\" " );
01194     }
01195     else if ( ( ucs4_char & 0x00F ) == 0x001 )
01196     {
01197         fprintf( svgFile, "font-family=\"serif\" " );
01198     }
01199     else if ( ( ucs4_char & 0x00F ) == 0x002 )
01200     {
01201         fprintf( svgFile, "font-family=\"mono-space\" " );
01202     }
01203     else if ( ( ucs4_char & 0x00F ) == 0x003 )
01204     {
01205         fprintf( svgFile, "font-family=\"cursive\" " );
01206     }
01207     else if ( ( ucs4_char & 0x00F ) == 0x004 )
01208     {
01209         // this should be symbol, but that doesn't seem to be available
01210         fprintf( svgFile, "font-family=\"sans-serif\" " );
01211     }
01212 
01213     // normal, italic, oblique
01214 
01215     if ( ( ucs4_char & 0x0F0 ) == 0x000 )
01216     {
01217         fprintf( svgFile, "font-style=\"normal\" " );
01218     }
01219     else if ( ( ucs4_char & 0x0F0 ) == 0x010 )
01220     {
01221         fprintf( svgFile, "font-style=\"italic\" " );
01222     }
01223     else if ( ( ucs4_char & 0x0F0 ) == 0x020 )
01224     {
01225         fprintf( svgFile, "font-style=\"oblique\" " );
01226     }
01227 
01228     // normal, bold
01229 
01230     if ( ( ucs4_char & 0xF00 ) == 0x000 )
01231     {
01232         fprintf( svgFile, "font-weight=\"normal\">" );
01233     }
01234     else if ( ( ucs4_char & 0xF00 ) == 0x100 )
01235     {
01236         fprintf( svgFile, "font-weight=\"bold\">" );
01237     }
01238 }

Generated on Wed Oct 12 2011 20:42:23 for PLplot by  doxygen 1.7.1