1 | /* $Id$ $Revision$ */ |
2 | /* vim:set shiftwidth=4 ts=8: */ |
3 | |
4 | /************************************************************************* |
5 | * Copyright (c) 2011 AT&T Intellectual Property |
6 | * All rights reserved. This program and the accompanying materials |
7 | * are made available under the terms of the Eclipse Public License v1.0 |
8 | * which accompanies this distribution, and is available at |
9 | * http://www.eclipse.org/legal/epl-v10.html |
10 | * |
11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
12 | *************************************************************************/ |
13 | |
14 | #include "config.h" |
15 | |
16 | #include <stdarg.h> |
17 | #include <stdlib.h> |
18 | #include <string.h> |
19 | #include <inttypes.h> |
20 | |
21 | #include "macros.h" |
22 | #include "const.h" |
23 | |
24 | #include "gvplugin_render.h" |
25 | #include "gvplugin_device.h" |
26 | #include "gvio.h" |
27 | #include "memory.h" |
28 | |
29 | typedef enum { FORMAT_VML, FORMAT_VMLZ, } format_type; |
30 | |
31 | unsigned int graphHeight,graphWidth; |
32 | |
33 | #ifndef HAVE_STRCASECMP |
34 | extern int strcasecmp(const char *s1, const char *s2); |
35 | #endif |
36 | |
37 | /* this is a direct copy fromlib/common/labels.c */ |
38 | static int xml_isentity(char *s) |
39 | { |
40 | s++; /* already known to be '&' */ |
41 | if (*s == '#') { |
42 | s++; |
43 | if (*s == 'x' || *s == 'X') { |
44 | s++; |
45 | while ((*s >= '0' && *s <= '9') |
46 | || (*s >= 'a' && *s <= 'f') |
47 | || (*s >= 'A' && *s <= 'F')) |
48 | s++; |
49 | } else { |
50 | while (*s >= '0' && *s <= '9') |
51 | s++; |
52 | } |
53 | } else { |
54 | while ((*s >= 'a' && *s <= 'z') |
55 | || (*s >= 'A' && *s <= 'Z')) |
56 | s++; |
57 | } |
58 | if (*s == ';') |
59 | return 1; |
60 | return 0; |
61 | } |
62 | |
63 | static void vml_bzptarray(GVJ_t * job, pointf * A, int n) |
64 | { |
65 | int i; |
66 | char *c; |
67 | |
68 | c = "m " ; /* first point */ |
69 | for (i = 0; i < n; i++) { |
70 | /* integers only in path! */ |
71 | gvprintf(job, "%s%.0f,%.0f " , c, A[i].x, graphHeight-A[i].y); |
72 | if (i == 0) |
73 | c = "c " ; /* second point */ |
74 | else |
75 | c = "" ; /* remaining points */ |
76 | } |
77 | gvputs(job, "\"" ); |
78 | } |
79 | |
80 | static void vml_print_color(GVJ_t * job, gvcolor_t color) |
81 | { |
82 | switch (color.type) { |
83 | case COLOR_STRING: |
84 | gvputs(job, color.u.string); |
85 | break; |
86 | case RGBA_BYTE: |
87 | if (color.u.rgba[3] == 0) /* transparent */ |
88 | gvputs(job, "none" ); |
89 | else |
90 | gvprintf(job, "#%02x%02x%02x" , |
91 | color.u.rgba[0], color.u.rgba[1], color.u.rgba[2]); |
92 | break; |
93 | default: |
94 | assert(0); /* internal error */ |
95 | } |
96 | } |
97 | |
98 | static void vml_grstroke(GVJ_t * job, int filled) |
99 | { |
100 | obj_state_t *obj = job->obj; |
101 | |
102 | gvputs(job, "<v:stroke color=\"" ); |
103 | vml_print_color(job, obj->pencolor); |
104 | if (obj->penwidth != PENWIDTH_NORMAL) |
105 | gvprintf(job, "\" weight=\"%.0fpt" , obj->penwidth); |
106 | if (obj->pen == PEN_DASHED) { |
107 | gvputs(job, "\" dashstyle=\"dash" ); |
108 | } else if (obj->pen == PEN_DOTTED) { |
109 | gvputs(job, "\" dashstyle=\"dot" ); |
110 | } |
111 | gvputs(job, "\" />" ); |
112 | } |
113 | |
114 | |
115 | static void vml_grfill(GVJ_t * job, int filled) |
116 | { |
117 | obj_state_t *obj = job->obj; |
118 | |
119 | if (filled){ |
120 | gvputs(job, " filled=\"true\" fillcolor=\"" ); |
121 | vml_print_color(job, obj->fillcolor); |
122 | gvputs(job, "\" " ); |
123 | }else{ |
124 | gvputs(job, " filled=\"false\" " ); |
125 | } |
126 | } |
127 | |
128 | /* html_string is a modified version of xml_string */ |
129 | char *html_string(char *s) |
130 | { |
131 | static char *buf = NULL; |
132 | static int bufsize = 0; |
133 | char *p, *sub, *prev = NULL; |
134 | int len, pos = 0; |
135 | int temp,cnt,remaining=0; |
136 | char workstr[16]; |
137 | uint64_t charnum=0; |
138 | unsigned char byte; |
139 | unsigned char mask; |
140 | |
141 | |
142 | if (!buf) { |
143 | bufsize = 64; |
144 | buf = gmalloc(bufsize); |
145 | } |
146 | p = buf; |
147 | while (s && *s) { |
148 | if (pos > (bufsize - 8)) { |
149 | bufsize *= 2; |
150 | buf = grealloc(buf, bufsize); |
151 | p = buf + pos; |
152 | } |
153 | /* escape '&' only if not part of a legal entity sequence */ |
154 | if (*s == '&' && !(xml_isentity(s))) { |
155 | sub = "&" ; |
156 | len = 5; |
157 | } |
158 | /* '<' '>' are safe to substitute even if string is already UTF-8 coded |
159 | * since UTF-8 strings won't contain '<' or '>' */ |
160 | else if (*s == '<') { |
161 | sub = "<" ; |
162 | len = 4; |
163 | } |
164 | else if (*s == '>') { |
165 | sub = ">" ; |
166 | len = 4; |
167 | } |
168 | else if (*s == '-') { /* can't be used in xml comment strings */ |
169 | sub = "-" ; |
170 | len = 5; |
171 | } |
172 | else if (*s == ' ' && prev && *prev == ' ') { |
173 | /* substitute 2nd and subsequent spaces with required_spaces */ |
174 | sub = " " ; /* inkscape doesn't recognise */ |
175 | len = 6; |
176 | } |
177 | else if (*s == '"') { |
178 | sub = """ ; |
179 | len = 6; |
180 | } |
181 | else if (*s == '\'') { |
182 | sub = "'" ; |
183 | len = 5; |
184 | } |
185 | else if ((unsigned char)*s > 127) { |
186 | byte=(unsigned char)*s; |
187 | cnt=0; |
188 | for (mask=127; mask < byte; mask=mask >>1){ |
189 | cnt++; |
190 | byte=byte & mask; |
191 | } |
192 | if (cnt>1){ |
193 | charnum=byte; |
194 | remaining=cnt-1; |
195 | }else{ |
196 | charnum=charnum<<6; |
197 | charnum+=byte; |
198 | remaining--; |
199 | } |
200 | if (remaining>0){ |
201 | s++; |
202 | continue; |
203 | } |
204 | /* we will build the html value right-to-left |
205 | * (least significant-to-most) */ |
206 | workstr[15]=';'; |
207 | sub=&workstr[14]; |
208 | len=3; /* &# + ; */ |
209 | do { |
210 | temp=charnum%10; |
211 | *(sub--)=(char)((int)'0'+ temp); |
212 | charnum/=10; |
213 | len++; |
214 | if (len>12){ /* 12 is arbitrary, but clearly in error */ |
215 | fprintf(stderr, "Error during conversion to \"UTF-8\". Quiting.\n" ); |
216 | exit(1); |
217 | } |
218 | } while (charnum>0); |
219 | *(sub--)='#'; |
220 | *(sub)='&'; |
221 | } |
222 | else { |
223 | sub = s; |
224 | len = 1; |
225 | } |
226 | while (len--) { |
227 | *p++ = *sub++; |
228 | pos++; |
229 | } |
230 | prev = s; |
231 | s++; |
232 | } |
233 | *p = '\0'; |
234 | return buf; |
235 | } |
236 | static void (GVJ_t * job, char *str) |
237 | { |
238 | gvputs(job, " <!-- " ); |
239 | gvputs(job, html_string(str)); |
240 | gvputs(job, " -->\n" ); |
241 | } |
242 | static void vml_begin_job(GVJ_t * job) |
243 | { |
244 | gvputs(job, "<HTML>\n" ); |
245 | gvputs(job, "\n<!-- Generated by " ); |
246 | gvputs(job, html_string(job->common->info[0])); |
247 | gvputs(job, " version " ); |
248 | gvputs(job, html_string(job->common->info[1])); |
249 | gvputs(job, " (" ); |
250 | gvputs(job, html_string(job->common->info[2])); |
251 | gvputs(job, ")\n-->\n" ); |
252 | } |
253 | |
254 | static void vml_begin_graph(GVJ_t * job) |
255 | { |
256 | obj_state_t *obj = job->obj; |
257 | char *name; |
258 | |
259 | graphHeight =(int)(job->bb.UR.y - job->bb.LL.y); |
260 | graphWidth =(int)(job->bb.UR.x - job->bb.LL.x); |
261 | |
262 | gvputs(job, "<HEAD>" ); |
263 | gvputs(job, "<META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n" ); |
264 | |
265 | |
266 | name = agnameof(obj->u.g); |
267 | if (name[0]) { |
268 | gvputs(job, "<TITLE>" ); |
269 | gvputs(job, html_string(name)); |
270 | gvputs(job, "</TITLE>" ); |
271 | } |
272 | gvprintf(job, "<!-- Pages: %d -->\n" , job->pagesArraySize.x * job->pagesArraySize.y); |
273 | |
274 | /* the next chunk and all the "DIV" stuff is not required, |
275 | * but it helps with non-IE browsers */ |
276 | gvputs(job, " <SCRIPT LANGUAGE='Javascript'>\n" ); |
277 | gvputs(job, " function browsercheck()\n" ); |
278 | gvputs(job, " {\n" ); |
279 | gvputs(job, " var ua = window.navigator.userAgent\n" ); |
280 | gvputs(job, " var msie = ua.indexOf ( 'MSIE ' )\n" ); |
281 | gvputs(job, " var ievers;\n" ); |
282 | gvputs(job, " var item;\n" ); |
283 | gvputs(job, " var VMLyes=new Array('_VML1_','_VML2_');\n" ); |
284 | gvputs(job, " var VMLno=new Array('_notVML1_','_notVML2_');\n" ); |
285 | gvputs(job, " if ( msie > 0 ){ // If Internet Explorer, return version number\n" ); |
286 | gvputs(job, " ievers= parseInt (ua.substring (msie+5, ua.indexOf ('.', msie )))\n" ); |
287 | gvputs(job, " }\n" ); |
288 | gvputs(job, " if (ievers>=5){\n" ); |
289 | gvputs(job, " for (x in VMLyes){\n" ); |
290 | gvputs(job, " item = document.getElementById(VMLyes[x]);\n" ); |
291 | gvputs(job, " if (item) {\n" ); |
292 | gvputs(job, " item.style.visibility='visible';\n" ); |
293 | gvputs(job, " }\n" ); |
294 | gvputs(job, " }\n" ); |
295 | gvputs(job, " for (x in VMLno){\n" ); |
296 | gvputs(job, " item = document.getElementById(VMLno[x]);\n" ); |
297 | gvputs(job, " if (item) {\n" ); |
298 | gvputs(job, " item.style.visibility='hidden';\n" ); |
299 | gvputs(job, " }\n" ); |
300 | gvputs(job, " }\n" ); |
301 | gvputs(job, " }else{\n" ); |
302 | gvputs(job, " for (x in VMLyes){\n" ); |
303 | gvputs(job, " item = document.getElementById(VMLyes[x]);\n" ); |
304 | gvputs(job, " if (item) {\n" ); |
305 | gvputs(job, " item.style.visibility='hidden';\n" ); |
306 | gvputs(job, " }\n" ); |
307 | gvputs(job, " }\n" ); |
308 | gvputs(job, " for (x in VMLno){\n" ); |
309 | gvputs(job, " item = document.getElementById(VMLno[x]);\n" ); |
310 | gvputs(job, " if (item) {\n" ); |
311 | gvputs(job, " item.style.visibility='visible';\n" ); |
312 | gvputs(job, " }\n" ); |
313 | gvputs(job, " }\n" ); |
314 | gvputs(job, " }\n" ); |
315 | gvputs(job, " }\n" ); |
316 | gvputs(job, " </SCRIPT>\n" ); |
317 | |
318 | gvputs(job, "</HEAD>" ); |
319 | gvputs(job, "<BODY onload='browsercheck();'>\n" ); |
320 | /* add 10pt pad to the bottom of the graph */ |
321 | gvputs(job, "<DIV id='_VML1_' style=\"position:relative; display:inline; visibility:hidden" ); |
322 | gvprintf(job, " width: %dpt; height: %dpt\">\n" , graphWidth, 10+graphHeight); |
323 | gvputs(job, "<STYLE>\n" ); |
324 | gvputs(job, "v\\:* { behavior: url(#default#VML);display:inline-block}\n" ); |
325 | gvputs(job, "</STYLE>\n" ); |
326 | gvputs(job, "<xml:namespace ns=\"urn:schemas-microsoft-com:vml\" prefix=\"v\" />\n" ); |
327 | |
328 | gvputs(job, " <v:group style=\"position:relative; " ); |
329 | gvprintf(job, " width: %dpt; height: %dpt\"" , graphWidth, graphHeight); |
330 | gvprintf(job, " coordorigin=\"0,0\" coordsize=\"%d,%d\" >" , graphWidth, graphHeight); |
331 | } |
332 | |
333 | static void vml_end_graph(GVJ_t * job) |
334 | { |
335 | gvputs(job, "</v:group>\n" ); |
336 | gvputs(job, "</DIV>\n" ); |
337 | /* add 10pt pad to the bottom of the graph */ |
338 | gvputs(job, "<DIV id='_VML2_' style=\"position:relative;visibility:hidden\">\n" ); |
339 | gvputs(job, "<!-- insert any other html content here -->\n" ); |
340 | gvputs(job, "</DIV>\n" ); |
341 | gvputs(job, "<DIV id='_notVML1_' style=\"position:relative;\">\n" ); |
342 | gvputs(job, "<!-- this should only display on NON-IE browsers -->\n" ); |
343 | gvputs(job, "<H2>Sorry, this diagram will only display correctly on Internet Explorer 5 (and up) browsers.</H2>\n" ); |
344 | gvputs(job, "</DIV>\n" ); |
345 | gvputs(job, "<DIV id='_notVML2_' style=\"position:relative;\">\n" ); |
346 | gvputs(job, "<!-- insert any other NON-IE html content here -->\n" ); |
347 | gvputs(job, "</DIV>\n" ); |
348 | |
349 | gvputs(job, "</BODY>\n</HTML>\n" ); |
350 | } |
351 | |
352 | static void |
353 | vml_begin_anchor(GVJ_t * job, char *href, char *tooltip, char *target, char *id) |
354 | { |
355 | gvputs(job, "<a" ); |
356 | if (href && href[0]) |
357 | gvprintf(job, " href=\"%s\"" , html_string(href)); |
358 | if (tooltip && tooltip[0]) |
359 | gvprintf(job, " title=\"%s\"" , html_string(tooltip)); |
360 | if (target && target[0]) |
361 | gvprintf(job, " target=\"%s\"" , html_string(target)); |
362 | gvputs(job, ">\n" ); |
363 | } |
364 | |
365 | static void vml_end_anchor(GVJ_t * job) |
366 | { |
367 | gvputs(job, "</a>\n" ); |
368 | } |
369 | |
370 | static void vml_textspan(GVJ_t * job, pointf p, textspan_t * span) |
371 | { |
372 | pointf p1,p2; |
373 | obj_state_t *obj = job->obj; |
374 | PostscriptAlias *pA; |
375 | |
376 | switch (span->just) { |
377 | case 'l': |
378 | p1.x=p.x; |
379 | break; |
380 | case 'r': |
381 | p1.x=p.x-span->size.x; |
382 | break; |
383 | default: |
384 | case 'n': |
385 | p1.x=p.x-(span->size.x/2); |
386 | break; |
387 | } |
388 | p2.x=p1.x+span->size.x; |
389 | if (span->size.y < span->font->size){ |
390 | span->size.y = 1 + (1.1*span->font->size); |
391 | } |
392 | |
393 | p1.x-=8; /* vml textbox margin fudge factor */ |
394 | p2.x+=8; /* vml textbox margin fudge factor */ |
395 | p2.y=graphHeight-(p.y); |
396 | p1.y=(p2.y-span->size.y); |
397 | /* text "y" was too high |
398 | * Graphviz uses "baseline", VML seems to use bottom of descenders - so we fudge a little |
399 | * (heuristics - based on eyeballs) */ |
400 | if (span->font->size <12.){ /* see graphs/directed/arrows.gv */ |
401 | p1.y+=1.4+span->font->size/5; /* adjust by approx. descender */ |
402 | p2.y+=1.4+span->font->size/5; /* adjust by approx. descender */ |
403 | }else{ |
404 | p1.y+=2+span->font->size/5; /* adjust by approx. descender */ |
405 | p2.y+=2+span->font->size/5; /* adjust by approx. descender */ |
406 | } |
407 | |
408 | gvprintf(job, "<v:rect style=\"position:absolute; " ); |
409 | gvprintf(job, " left: %.2f; top: %.2f;" , p1.x, p1.y); |
410 | gvprintf(job, " width: %.2f; height: %.2f\"" , p2.x-p1.x, p2.y-p1.y); |
411 | gvputs(job, " stroked=\"false\" filled=\"false\">\n" ); |
412 | gvputs(job, "<v:textbox inset=\"0,0,0,0\" style=\"position:absolute; v-text-wrapping:'false';padding:'0';" ); |
413 | |
414 | pA = span->font->postscript_alias; |
415 | if (pA) { |
416 | gvprintf(job, "font-family: '%s';" , pA->family); |
417 | if (pA->weight) |
418 | gvprintf(job, "font-weight: %s;" , pA->weight); |
419 | if (pA->stretch) |
420 | gvprintf(job, "font-stretch: %s;" , pA->stretch); |
421 | if (pA->style) |
422 | gvprintf(job, "font-style: %s;" , pA->style); |
423 | } |
424 | else { |
425 | gvprintf(job, "font-family: \'%s\';" , span->font->name); |
426 | } |
427 | gvprintf(job, " font-size: %.2fpt;" , span->font->size); |
428 | switch (obj->pencolor.type) { |
429 | case COLOR_STRING: |
430 | if (strcasecmp(obj->pencolor.u.string, "black" )) |
431 | gvprintf(job, "color:%s;" , obj->pencolor.u.string); |
432 | break; |
433 | case RGBA_BYTE: |
434 | gvprintf(job, "color:#%02x%02x%02x;" , |
435 | obj->pencolor.u.rgba[0], obj->pencolor.u.rgba[1], obj->pencolor.u.rgba[2]); |
436 | break; |
437 | default: |
438 | assert(0); /* internal error */ |
439 | } |
440 | gvputs(job, "\"><center>" ); |
441 | gvputs(job, html_string(span->str)); |
442 | gvputs(job, "</center></v:textbox>\n" ); |
443 | gvputs(job, "</v:rect>\n" ); |
444 | } |
445 | |
446 | static void vml_ellipse(GVJ_t * job, pointf * A, int filled) |
447 | { |
448 | double dx, dy, left, right, top, bottom; |
449 | |
450 | /* A[] contains 2 points: the center and corner. */ |
451 | gvputs(job, " <v:oval style=\"position:absolute;" ); |
452 | |
453 | dx=A[1].x-A[0].x; |
454 | dy=A[1].y-A[0].y; |
455 | |
456 | top=graphHeight-(A[0].y+dy); |
457 | bottom=top+dy+dy; |
458 | left=A[0].x - dx; |
459 | right=A[1].x; |
460 | gvprintf(job, " left: %.2f; top: %.2f;" ,left, top); |
461 | gvprintf(job, " width: %.2f; height: %.2f\"" , 2*dx, 2*dy); |
462 | |
463 | vml_grfill(job, filled); |
464 | gvputs(job, " >" ); |
465 | vml_grstroke(job, filled); |
466 | gvputs(job, "</v:oval>\n" ); |
467 | } |
468 | |
469 | static void |
470 | vml_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start, |
471 | int arrow_at_end, int filled) |
472 | { |
473 | gvputs(job, " <v:shape style=\"position:absolute; " ); |
474 | gvprintf(job, " width: %d; height: %d\"" , graphWidth, graphHeight); |
475 | |
476 | vml_grfill(job, filled); |
477 | gvputs(job, " >" ); |
478 | vml_grstroke(job, filled); |
479 | gvputs(job, "<v:path v=\"" ); |
480 | vml_bzptarray(job, A, n); |
481 | gvputs(job, "/></v:shape>\n" ); |
482 | } |
483 | |
484 | static void vml_polygon(GVJ_t * job, pointf * A, int n, int filled) |
485 | { |
486 | int i; |
487 | double px,py; |
488 | |
489 | gvputs(job, " <v:shape style=\"position:absolute; " ); |
490 | gvprintf(job, " width: %d; height: %d\"" , graphWidth, graphHeight); |
491 | vml_grfill(job, filled); |
492 | gvputs(job, " >" ); |
493 | vml_grstroke(job, filled); |
494 | |
495 | gvputs(job, "<v:path v=\"" ); |
496 | for (i = 0; i < n; i++) |
497 | { |
498 | px=A[i].x; |
499 | py= (graphHeight-A[i].y); |
500 | if (i==0){ |
501 | gvputs(job, "m " ); |
502 | } |
503 | /* integers only in path */ |
504 | gvprintf(job, "%.0f %.0f " , px, py); |
505 | if (i==0) gvputs(job, "l " ); |
506 | if (i==n-1) gvputs(job, "x e \"/>" ); |
507 | } |
508 | gvputs(job, "</v:shape>\n" ); |
509 | } |
510 | |
511 | static void vml_polyline(GVJ_t * job, pointf * A, int n) |
512 | { |
513 | int i; |
514 | |
515 | gvputs(job, " <v:shape style=\"position:absolute; " ); |
516 | gvprintf(job, " width: %d; height: %d\" filled=\"false\">" , graphWidth, graphHeight); |
517 | gvputs(job, "<v:path v=\"" ); |
518 | for (i = 0; i < n; i++) |
519 | { |
520 | if (i==0) gvputs(job, " m " ); |
521 | gvprintf(job, "%.0f,%.0f " , A[i].x, graphHeight-A[i].y); |
522 | if (i==0) gvputs(job, " l " ); |
523 | if (i==n-1) gvputs(job, " e " ); /* no x here for polyline */ |
524 | } |
525 | gvputs(job, "\"/>" ); |
526 | vml_grstroke(job, 0); /* no fill here for polyline */ |
527 | gvputs(job, "</v:shape>\n" ); |
528 | } |
529 | |
530 | /* color names from |
531 | http://msdn.microsoft.com/en-us/library/bb250525(VS.85).aspx#t.color |
532 | */ |
533 | /* NB. List must be LANG_C sorted */ |
534 | static char *vml_knowncolors[] = { |
535 | "aqua" , "black" , "blue" , "fuchsia" , |
536 | "gray" , "green" , "lime" , "maroon" , |
537 | "navy" , "olive" , "purple" , "red" , |
538 | "silver" , "teal" , "white" , "yellow" |
539 | }; |
540 | |
541 | gvrender_engine_t vml_engine = { |
542 | vml_begin_job, |
543 | 0, /* vml_end_job */ |
544 | vml_begin_graph, |
545 | vml_end_graph, |
546 | 0, /* vml_begin_layer */ |
547 | 0, /* vml_end_layer */ |
548 | 0, /* vml_begin_page */ |
549 | 0, /* vml_end_page */ |
550 | 0, /* vml_begin_cluster */ |
551 | 0, /* vml_end_cluster */ |
552 | 0, /* vml_begin_nodes */ |
553 | 0, /* vml_end_nodes */ |
554 | 0, /* vml_begin_edges */ |
555 | 0, /* vml_end_edges */ |
556 | 0, /* vml_begin_node */ |
557 | 0, /* vml_end_node */ |
558 | 0, /* vml_begin_edge */ |
559 | 0, /* vml_end_edge */ |
560 | vml_begin_anchor, |
561 | vml_end_anchor, |
562 | 0, /* vml_begin_label */ |
563 | 0, /* vml_end_label */ |
564 | vml_textspan, |
565 | 0, /* vml_resolve_color */ |
566 | vml_ellipse, |
567 | vml_polygon, |
568 | vml_bezier, |
569 | vml_polyline, |
570 | vml_comment, |
571 | 0, /* vml_library_shape */ |
572 | }; |
573 | |
574 | gvrender_features_t render_features_vml = { |
575 | GVRENDER_Y_GOES_DOWN |
576 | | GVRENDER_DOES_TRANSFORM |
577 | | GVRENDER_DOES_LABELS |
578 | | GVRENDER_DOES_MAPS |
579 | | GVRENDER_DOES_TARGETS |
580 | | GVRENDER_DOES_TOOLTIPS, /* flags */ |
581 | 0., /* default pad - graph units */ |
582 | vml_knowncolors, /* knowncolors */ |
583 | sizeof(vml_knowncolors) / sizeof(char *), /* sizeof knowncolors */ |
584 | RGBA_BYTE, /* color_type */ |
585 | }; |
586 | |
587 | gvdevice_features_t device_features_vml = { |
588 | GVDEVICE_DOES_TRUECOLOR, /* flags */ |
589 | {0.,0.}, /* default margin - points */ |
590 | {0.,0.}, /* default page width, height - points */ |
591 | {96.,96.}, /* default dpi */ |
592 | }; |
593 | |
594 | gvdevice_features_t device_features_vmlz = { |
595 | GVDEVICE_DOES_TRUECOLOR |
596 | | GVDEVICE_COMPRESSED_FORMAT, /* flags */ |
597 | {0.,0.}, /* default margin - points */ |
598 | {0.,0.}, /* default page width, height - points */ |
599 | {96.,96.}, /* default dpi */ |
600 | }; |
601 | |
602 | gvplugin_installed_t gvrender_vml_types[] = { |
603 | {FORMAT_VML, "vml" , 1, &vml_engine, &render_features_vml}, |
604 | {0, NULL, 0, NULL, NULL} |
605 | }; |
606 | |
607 | gvplugin_installed_t gvdevice_vml_types[] = { |
608 | {FORMAT_VML, "vml:vml" , 1, NULL, &device_features_vml}, |
609 | #if HAVE_LIBZ |
610 | {FORMAT_VMLZ, "vmlz:vml" , 1, NULL, &device_features_vmlz}, |
611 | #endif |
612 | {0, NULL, 0, NULL, NULL} |
613 | }; |
614 | |
615 | |