gvsig-raster / libjni-potrace / trunk / libjni-potrace / resources / potrace-1.8 / src / backend_svg.c @ 1780
History | View | Annotate | Download (8.04 KB)
1 |
/* Copyright (C) 2001-2007 Peter Selinger.
|
---|---|
2 |
This file is part of Potrace. It is free software and it is covered
|
3 |
by the GNU General Public License. See the file COPYING for details. */
|
4 |
|
5 |
/* $Id: backend_svg.c 147 2007-04-09 00:44:09Z selinger $ */
|
6 |
|
7 |
/* The SVG backend of Potrace. */
|
8 |
|
9 |
#include <stdio.h> |
10 |
#include <stdarg.h> |
11 |
#include <string.h> |
12 |
#include <math.h> |
13 |
|
14 |
#include "potracelib.h" |
15 |
#include "curve.h" |
16 |
#include "main.h" |
17 |
#include "backend_svg.h" |
18 |
#include "lists.h" |
19 |
#include "auxiliary.h" |
20 |
|
21 |
#ifdef HAVE_CONFIG_H
|
22 |
#include "config.h" |
23 |
#endif
|
24 |
|
25 |
/* ---------------------------------------------------------------------- */
|
26 |
/* path-drawing auxiliary functions */
|
27 |
|
28 |
/* coordinate quantization */
|
29 |
static inline point_t unit(dpoint_t p) { |
30 |
point_t q; |
31 |
|
32 |
q.x = (long)(floor(p.x*info.unit+.5)); |
33 |
q.y = (long)(floor(p.y*info.unit+.5)); |
34 |
return q;
|
35 |
} |
36 |
|
37 |
static point_t cur;
|
38 |
static char lastop = 0; |
39 |
static int column = 0; |
40 |
static int newline = 1; |
41 |
|
42 |
static void shiptoken(FILE *fout, char *token) { |
43 |
int c = strlen(token);
|
44 |
if (!newline && column+c+1 > 75) { |
45 |
fprintf(fout, "\n");
|
46 |
column = 0;
|
47 |
newline = 1;
|
48 |
} else if (!newline) { |
49 |
fprintf(fout, " ");
|
50 |
column++; |
51 |
} |
52 |
fprintf(fout, "%s", token);
|
53 |
column += c; |
54 |
newline = 0;
|
55 |
} |
56 |
|
57 |
static void ship(FILE *fout, char *fmt, ...) { |
58 |
va_list args; |
59 |
static char buf[4096]; /* static string limit is okay here because |
60 |
we only use constant format strings - for
|
61 |
the same reason, it is okay to use
|
62 |
vsprintf instead of vsnprintf below. */
|
63 |
char *p, *q;
|
64 |
|
65 |
va_start(args, fmt); |
66 |
vsprintf(buf, fmt, args); |
67 |
buf[4095] = 0; |
68 |
va_end(args); |
69 |
|
70 |
p = buf; |
71 |
while ((q = strchr(p, ' ')) != NULL) { |
72 |
*q = 0;
|
73 |
shiptoken(fout, p); |
74 |
p = q+1;
|
75 |
} |
76 |
shiptoken(fout, p); |
77 |
} |
78 |
|
79 |
static void svg_moveto(FILE *fout, dpoint_t p) { |
80 |
cur = unit(p); |
81 |
|
82 |
ship(fout, "M%ld %ld", cur.x, cur.y);
|
83 |
lastop = 'M';
|
84 |
} |
85 |
|
86 |
static void svg_rmoveto(FILE *fout, dpoint_t p) { |
87 |
point_t q; |
88 |
|
89 |
q = unit(p); |
90 |
ship(fout, "m%ld %ld", q.x-cur.x, q.y-cur.y);
|
91 |
cur = q; |
92 |
lastop = 'm';
|
93 |
} |
94 |
|
95 |
static void svg_lineto(FILE *fout, dpoint_t p) { |
96 |
point_t q; |
97 |
|
98 |
q = unit(p); |
99 |
|
100 |
if (lastop != 'l') { |
101 |
ship(fout, "l%ld %ld", q.x-cur.x, q.y-cur.y);
|
102 |
} else {
|
103 |
ship(fout, "%ld %ld", q.x-cur.x, q.y-cur.y);
|
104 |
} |
105 |
cur = q; |
106 |
lastop = 'l';
|
107 |
} |
108 |
|
109 |
static void svg_curveto(FILE *fout, dpoint_t p1, dpoint_t p2, dpoint_t p3) { |
110 |
point_t q1, q2, q3; |
111 |
|
112 |
q1 = unit(p1); |
113 |
q2 = unit(p2); |
114 |
q3 = unit(p3); |
115 |
|
116 |
if (lastop != 'c') { |
117 |
ship(fout, "c%ld %ld %ld %ld %ld %ld", q1.x-cur.x, q1.y-cur.y, q2.x-cur.x, q2.y-cur.y, q3.x-cur.x, q3.y-cur.y);
|
118 |
} else {
|
119 |
ship(fout, "%ld %ld %ld %ld %ld %ld", q1.x-cur.x, q1.y-cur.y, q2.x-cur.x, q2.y-cur.y, q3.x-cur.x, q3.y-cur.y);
|
120 |
} |
121 |
cur = q3; |
122 |
lastop = 'c';
|
123 |
} |
124 |
|
125 |
/* ---------------------------------------------------------------------- */
|
126 |
/* functions for converting a path to an SVG path element */
|
127 |
|
128 |
/* Explicit encoding. If abs is set, move to first coordinate
|
129 |
absolutely. */
|
130 |
static int svg_path(FILE *fout, potrace_curve_t *curve, int abs) { |
131 |
int i;
|
132 |
dpoint_t *c; |
133 |
int m = curve->n;
|
134 |
|
135 |
c = curve->c[m-1];
|
136 |
if (abs) {
|
137 |
svg_moveto(fout, c[2]);
|
138 |
} else {
|
139 |
svg_rmoveto(fout, c[2]);
|
140 |
} |
141 |
|
142 |
for (i=0; i<m; i++) { |
143 |
c = curve->c[i]; |
144 |
switch (curve->tag[i]) {
|
145 |
case POTRACE_CORNER:
|
146 |
svg_lineto(fout, c[1]);
|
147 |
svg_lineto(fout, c[2]);
|
148 |
break;
|
149 |
case POTRACE_CURVETO:
|
150 |
svg_curveto(fout, c[0], c[1], c[2]); |
151 |
break;
|
152 |
} |
153 |
} |
154 |
newline = 1;
|
155 |
shiptoken(fout, "z");
|
156 |
return 0; |
157 |
} |
158 |
|
159 |
/* produce a jaggy path - for debugging. If abs is set, move to first
|
160 |
coordinate absolutely. If abs is not set, move to first coordinate
|
161 |
relatively, and traverse path in the opposite direction. */
|
162 |
static int svg_jaggy_path(FILE *fout, point_t *pt, int n, int abs) { |
163 |
int i;
|
164 |
point_t cur, prev; |
165 |
|
166 |
if (abs) {
|
167 |
cur = prev = pt[n-1];
|
168 |
svg_moveto(fout, dpoint(cur)); |
169 |
for (i=0; i<n; i++) { |
170 |
if (pt[i].x != cur.x && pt[i].y != cur.y) {
|
171 |
cur = prev; |
172 |
svg_lineto(fout, dpoint(cur)); |
173 |
} |
174 |
prev = pt[i]; |
175 |
} |
176 |
svg_lineto(fout, dpoint(pt[n-1]));
|
177 |
} else {
|
178 |
cur = prev = pt[0];
|
179 |
svg_rmoveto(fout, dpoint(cur)); |
180 |
for (i=n-1; i>=0; i--) { |
181 |
if (pt[i].x != cur.x && pt[i].y != cur.y) {
|
182 |
cur = prev; |
183 |
svg_lineto(fout, dpoint(cur)); |
184 |
} |
185 |
prev = pt[i]; |
186 |
} |
187 |
svg_lineto(fout, dpoint(pt[0]));
|
188 |
} |
189 |
newline = 1;
|
190 |
shiptoken(fout, "z");
|
191 |
return 0; |
192 |
} |
193 |
|
194 |
static void write_paths_opaque(FILE *fout, potrace_path_t *tree) { |
195 |
potrace_path_t *p, *q; |
196 |
int c;
|
197 |
|
198 |
for (p=tree; p; p=p->sibling) {
|
199 |
if (info.group) {
|
200 |
fprintf(fout, "<g>\n");
|
201 |
fprintf(fout, "<g>\n");
|
202 |
} |
203 |
c = fprintf(fout, "<path fill=\"#%06x\" stroke=\"none\" d=\"", info.color);
|
204 |
column = c; |
205 |
newline = 1;
|
206 |
lastop = 0;
|
207 |
if (info.debug == 1) { |
208 |
svg_jaggy_path(fout, p->priv->pt, p->priv->len, 1);
|
209 |
} else {
|
210 |
svg_path(fout, &p->curve, 1);
|
211 |
} |
212 |
fprintf(fout, "\"/>\n");
|
213 |
for (q=p->childlist; q; q=q->sibling) {
|
214 |
c = fprintf(fout, "<path fill=\"#%06x\" stroke=\"none\" d=\"", info.fillcolor);
|
215 |
column = c; |
216 |
newline = 1;
|
217 |
lastop = 0;
|
218 |
if (info.debug == 1) { |
219 |
svg_jaggy_path(fout, q->priv->pt, q->priv->len, 1);
|
220 |
} else {
|
221 |
svg_path(fout, &q->curve, 1);
|
222 |
} |
223 |
fprintf(fout, "\"/>\n");
|
224 |
} |
225 |
if (info.group) {
|
226 |
fprintf(fout, "</g>\n");
|
227 |
} |
228 |
for (q=p->childlist; q; q=q->sibling) {
|
229 |
write_paths_opaque(fout, q->childlist); |
230 |
} |
231 |
if (info.group) {
|
232 |
fprintf(fout, "</g>\n");
|
233 |
} |
234 |
} |
235 |
} |
236 |
|
237 |
static void write_paths_transparent(FILE *fout, potrace_path_t *tree) { |
238 |
potrace_path_t *p, *q; |
239 |
int c;
|
240 |
|
241 |
for (p=tree; p; p=p->sibling) {
|
242 |
if (info.group) {
|
243 |
fprintf(fout, "<g>\n");
|
244 |
} |
245 |
c = fprintf(fout, "<path d=\"");
|
246 |
column = c; |
247 |
newline = 1;
|
248 |
lastop = 0;
|
249 |
if (info.debug == 1) { |
250 |
svg_jaggy_path(fout, p->priv->pt, p->priv->len, 1);
|
251 |
} else {
|
252 |
svg_path(fout, &p->curve, 1);
|
253 |
} |
254 |
for (q=p->childlist; q; q=q->sibling) {
|
255 |
if (info.debug == 1) { |
256 |
svg_jaggy_path(fout, q->priv->pt, q->priv->len, 0);
|
257 |
} else {
|
258 |
svg_path(fout, &q->curve, 0);
|
259 |
} |
260 |
} |
261 |
fprintf(fout, "\"/>\n");
|
262 |
for (q=p->childlist; q; q=q->sibling) {
|
263 |
write_paths_transparent(fout, q->childlist); |
264 |
} |
265 |
if (info.group) {
|
266 |
fprintf(fout, "</g>\n");
|
267 |
} |
268 |
} |
269 |
} |
270 |
|
271 |
/* ---------------------------------------------------------------------- */
|
272 |
/* Backend. */
|
273 |
|
274 |
/* public interface for SVG */
|
275 |
int page_svg(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo) {
|
276 |
|
277 |
int bboxx = (int)ceil(imginfo->trans.bb[0]+imginfo->lmar+imginfo->rmar); |
278 |
int bboxy = (int)ceil(imginfo->trans.bb[1]+imginfo->tmar+imginfo->bmar); |
279 |
double origx = imginfo->trans.orig[0] + imginfo->lmar; |
280 |
double origy = bboxy - imginfo->trans.orig[1] - imginfo->bmar; |
281 |
double scalex = imginfo->width / imginfo->pixwidth / info.unit;
|
282 |
double scaley = -imginfo->height / imginfo->pixheight / info.unit;
|
283 |
|
284 |
/* header */
|
285 |
fprintf(fout, "<?xml version=\"1.0\" standalone=\"no\"?>\n");
|
286 |
fprintf(fout, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
|
287 |
fprintf(fout, " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
|
288 |
|
289 |
/* set bounding box and namespace */
|
290 |
fprintf(fout, "<svg version=\"1.0\" xmlns=\"http://www.w3.org/2000/svg\"\n");
|
291 |
fprintf(fout, " width=\"%dpt\" height=\"%dpt\" viewBox=\"0 0 %d %d\"\n",
|
292 |
bboxx, bboxy, bboxx, bboxy); |
293 |
fprintf(fout, " preserveAspectRatio=\"xMidYMid meet\">\n");
|
294 |
|
295 |
/* metadata: creator */
|
296 |
fprintf(fout, "<metadata>\n");
|
297 |
fprintf(fout, "Created by "POTRACE" "VERSION", written by Peter Selinger 2001-2007\n"); |
298 |
fprintf(fout, "</metadata>\n");
|
299 |
|
300 |
/* use a "group" tag to establish coordinate system and style */
|
301 |
fprintf(fout, "<g transform=\"");
|
302 |
if (origx != 0 || origy != 0) { |
303 |
fprintf(fout, "translate(%.0f,%.0f) ", origx, origy);
|
304 |
} |
305 |
if (info.angle != 0) { |
306 |
fprintf(fout, "rotate(%.2f) ", -info.angle);
|
307 |
} |
308 |
fprintf(fout, "scale(%f,%f)", scalex, scaley);
|
309 |
fprintf(fout, "\"\n");
|
310 |
fprintf(fout, "fill=\"#%06x\" stroke=\"none\">\n", info.color);
|
311 |
|
312 |
if (info.opaque) {
|
313 |
write_paths_opaque(fout, plist); |
314 |
} else {
|
315 |
write_paths_transparent(fout, plist); |
316 |
} |
317 |
|
318 |
/* write footer */
|
319 |
fprintf(fout, "</g>\n");
|
320 |
fprintf(fout, "</svg>\n");
|
321 |
fflush(fout); |
322 |
|
323 |
return 0; |
324 |
} |
325 |
|