1 /*****************************************************************************/
2 /* LibreDWG - free implementation of the DWG file format */
3 /* */
4 /* Copyright (C) 2009-2020 Free Software Foundation, Inc. */
5 /* Copyright (C) 2010 Thien-Thi Nguyen */
6 /* */
7 /* This library is free software, licensed under the terms of the GNU */
8 /* General Public License as published by the Free Software Foundation, */
9 /* either version 3 of the License, or (at your option) any later version. */
10 /* You should have received a copy of the GNU General Public License */
11 /* along with this program. If not, see <http://www.gnu.org/licenses/>. */
12 /*****************************************************************************/
13
14 /*
15 * dwg2SVG.c: convert a DWG to SVG
16 * written by Felipe Corrêa da Silva Sances
17 * modified by Rodrigo Rodrigues da Silva
18 * modified by Thien-Thi Nguyen
19 * modified by Reini Urban
20 *
21 * TODO: all entities: 3DSOLID, SHAPE, ARC_DIMENSION, ATTRIB, DIMENSION*,
22 * *SURFACE, GEOPOSITIONMARKER/CAMERA/LIGHT, HATCH, HELIX,
23 * IMAGE/WIPEOUT/UNDERLAY, LEADER, MESH, MINSERT, MLINE, MTEXT, MULTILEADER,
24 * OLE2FRAME, OLEFRAME, POLYLINE_3D, POLYLINE_MESH, POLYLINE_PFACE, RAY, XLINE,
25 * SPLINE, TABLE, TOLERANCE, VIEWPORT?
26 * common_entity_data: ltype, ltype_scale.
27 * PLINE: widths, bulges.
28 */
29
30 #include "../src/config.h"
31 #include <stdio.h>
32 #include <stdlib.h>
33 #ifdef HAVE_STRCASESTR
34 # undef __DARWIN_C_LEVEL
35 # define __DARWIN_C_LEVEL __DARWIN_C_FULL
36 # ifndef __USE_GNU
37 # define __USE_GNU
38 # endif
39 # ifndef __BSD_VISIBLE
40 # define __BSD_VISIBLE 1
41 # endif
42 #endif
43 #include <string.h>
44 #include <unistd.h>
45 #include <math.h>
46 #include <getopt.h>
47 #ifdef HAVE_VALGRIND_VALGRIND_H
48 # include <valgrind/valgrind.h>
49 #endif
50
51 #include <dwg.h>
52 #include <dwg_api.h>
53 #include "bits.h"
54 #include "common.h"
55 #include "escape.h"
56 #include "geom.h"
57 #include "suffix.inc"
58
59 static int opts = 0;
60 static int mspace = 0; // only mspace, even when pspace is defined
61 Dwg_Data g_dwg;
62 double model_xmin, model_ymin, model_xmax, model_ymax;
63 double page_width, page_height, scale;
64
65 static void output_SVG (Dwg_Data *dwg);
66
67 static int
usage(void)68 usage (void)
69 {
70 printf ("\nUsage: dwg2SVG [-v[0-9]] DWGFILE >SVGFILE\n");
71 return 1;
72 }
73 static int
opt_version(void)74 opt_version (void)
75 {
76 printf ("dwg2SVG %s\n", PACKAGE_VERSION);
77 return 0;
78 }
79 static int
help(void)80 help (void)
81 {
82 printf ("\nUsage: dwg2SVG [OPTION]... DWGFILE >SVGFILE\n");
83 printf ("Converts some 2D elements of the DWG to a SVG.\n"
84 "\n");
85 #ifdef HAVE_GETOPT_LONG
86 printf (" -v[0-9], --verbose [0-9] verbosity\n");
87 printf (" --mspace only model-space, no paper-space\n");
88 printf (" --force-free force free\n");
89 printf (" --help display this help and exit\n");
90 printf (" --version output version information and exit\n"
91 "\n");
92 #else
93 printf (" -v[0-9] verbosity\n");
94 printf (" -m only model-space, no paper-space\n");
95 printf (" -h display this help and exit\n");
96 printf (" -i output version information and exit\n"
97 "\n");
98 #endif
99 printf ("GNU LibreDWG online manual: "
100 "<https://www.gnu.org/software/libredwg/>\n");
101 return 0;
102 }
103
104 static double
transform_X(double x)105 transform_X (double x)
106 {
107 return x - model_xmin;
108 }
109
110 static double
transform_Y(double y)111 transform_Y (double y)
112 {
113 return page_height - (y - model_ymin);
114 }
115
116 static bool
isnan_2BD(BITCODE_2BD pt)117 isnan_2BD (BITCODE_2BD pt)
118 {
119 return isnan (pt.x) || isnan (pt.y);
120 }
121
122 static bool
isnan_2pt(dwg_point_2d pt)123 isnan_2pt (dwg_point_2d pt)
124 {
125 return isnan (pt.x) || isnan (pt.y);
126 }
127
128 static bool
isnan_3BD(BITCODE_3BD pt)129 isnan_3BD (BITCODE_3BD pt)
130 {
131 return isnan (pt.x) || isnan (pt.y) || isnan (pt.z);
132 }
133
134 static bool
entity_invisible(Dwg_Object * obj)135 entity_invisible (Dwg_Object *obj)
136 {
137 BITCODE_BS invisible = obj->tio.entity->invisible;
138 Dwg_Object *layer;
139 Dwg_Object_LAYER *_obj;
140 if (invisible)
141 return true;
142
143 if (!obj->tio.entity->layer || !obj->tio.entity->layer->obj)
144 return false;
145 layer = obj->tio.entity->layer->obj;
146 if (layer->fixedtype != DWG_TYPE_LAYER)
147 return false;
148 _obj = layer->tio.object->tio.LAYER;
149 // pre-r13 it is set if the layer color is negative
150 return _obj->on == 0 ? true : false;
151 }
152
153 static double
entity_lweight(Dwg_Object_Entity * ent)154 entity_lweight (Dwg_Object_Entity *ent)
155 {
156 // TODO: resolve BYLAYER 256, see above.
157 // stroke-width:%0.1fpx. 100th of a mm
158 int lw = dxf_cvt_lweight (ent->linewt);
159 return lw < 0 ? 0.1 : (double)(lw * 0.001);
160 }
161
162 static char *
entity_color(Dwg_Object_Entity * ent)163 entity_color (Dwg_Object_Entity *ent)
164 {
165 // TODO: alpha?
166 if (ent->color.index >= 8 && ent->color.index < 256)
167 {
168 const Dwg_RGB_Palette *palette = dwg_rgb_palette ();
169 const Dwg_RGB_Palette *rgb = &palette[ent->color.index];
170 char *s = malloc (8);
171 sprintf (s, "#%02x%02x%02x", rgb->r, rgb->g, rgb->b);
172 return s;
173 }
174 else if (ent->color.flag & 0x80 && !(ent->color.flag & 0x40))
175 {
176 char *s = malloc (8);
177 sprintf (s, "#%06x", ent->color.rgb & 0x00ffffff);
178 return s;
179 }
180 else
181 switch (ent->color.index)
182 {
183 case 1:
184 return (char*)"red";
185 case 2:
186 return (char*)"yellow";
187 case 3:
188 return (char*)"green";
189 case 4:
190 return (char*)"cyan";
191 case 5:
192 return (char*)"blue";
193 case 6:
194 return (char*)"magenta";
195 case 7:
196 return (char*)"white";
197 case 0: // ByBlock
198 case 256: // ByLayer
199 default:
200 return (char*)"black";
201 }
202 }
203
204 static void
common_entity(Dwg_Object_Entity * ent)205 common_entity (Dwg_Object_Entity *ent)
206 {
207 double lweight;
208 char* color;
209 lweight = entity_lweight (ent);
210 color = entity_color (ent);
211 printf (" style=\"fill:none;stroke:%s;stroke-width:%.1fpx\" />\n",
212 color, lweight);
213 if (*color == '#')
214 free (color);
215 }
216
217 // TODO: MTEXT
218 static void
output_TEXT(Dwg_Object * obj)219 output_TEXT (Dwg_Object *obj)
220 {
221 Dwg_Data *dwg = obj->parent;
222 Dwg_Entity_TEXT *text = obj->tio.entity->tio.TEXT;
223 char *escaped;
224 const char *fontfamily;
225 BITCODE_H style_ref = text->style;
226 Dwg_Object *o = style_ref ? dwg_ref_object_silent (dwg, style_ref) : NULL;
227 Dwg_Object_STYLE *style = o ? o->tio.object->tio.STYLE : NULL;
228 BITCODE_2DPOINT pt;
229
230 if (!text->text_value || entity_invisible (obj))
231 return;
232 if (isnan_2BD (text->ins_pt) || isnan_3BD (text->extrusion))
233 return;
234 if (dwg->header.version >= R_2007)
235 escaped = htmlwescape ((BITCODE_TU)text->text_value);
236 else
237 escaped = htmlescape (text->text_value, (int)dwg->header.codepage);
238
239 if (style && o->fixedtype == DWG_TYPE_STYLE && style->font_file
240 && *style->font_file
241 #ifdef HAVE_STRCASESTR
242 && strcasestr (style->font_file, ".ttf")
243 #else
244 && (strstr (style->font_file, ".ttf")
245 || strstr (style->font_file, ".TTF"))
246 #endif
247 )
248 {
249 #ifdef HAVE_STRCASESTR
250 if (strcasestr (style->font_file, "Arial"))
251 #else
252 if ((strstr (style->font_file, "arial"))
253 || strstr (style->font_file, "Arial"))
254 #endif
255 {
256 fontfamily = "Arial";
257 }
258 else
259 fontfamily = "Verdana";
260 }
261 else
262 fontfamily = "Courier";
263
264 transform_OCS_2d (&pt, text->ins_pt, text->extrusion);
265 printf ("\t<text id=\"dwg-object-%d\" x=\"%f\" y=\"%f\" "
266 "font-family=\"%s\" font-size=\"%f\" fill=\"%s\">%s</text>\n",
267 obj->index, transform_X (pt.x), transform_Y (pt.y), fontfamily,
268 text->height /* fontsize */, entity_color (obj->tio.entity),
269 escaped ? escaped : "");
270 free (escaped);
271 }
272
273 static void
output_LINE(Dwg_Object * obj)274 output_LINE (Dwg_Object *obj)
275 {
276 Dwg_Entity_LINE *line = obj->tio.entity->tio.LINE;
277 BITCODE_3DPOINT start, end;
278
279 if (isnan_3BD (line->start) || isnan_3BD (line->end)
280 || isnan_3BD (line->extrusion) || entity_invisible (obj))
281 return;
282 transform_OCS (&start, line->start, line->extrusion);
283 transform_OCS (&end, line->end, line->extrusion);
284 printf ("\t<!-- line-%d -->\n", obj->index);
285 printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f L %f,%f\"\n\t",
286 obj->index, transform_X (start.x), transform_Y (start.y),
287 transform_X (end.x), transform_Y (end.y));
288 common_entity (obj->tio.entity);
289 }
290
291 static void
output_XLINE(Dwg_Object * obj)292 output_XLINE (Dwg_Object *obj)
293 {
294 Dwg_Entity_XLINE *xline = obj->tio.entity->tio.XLINE;
295 BITCODE_3DPOINT invvec;
296 static BITCODE_3DPOINT box[2];
297 int sign[3];
298 double txmin, txmax, tymin, tymax, tzmin, tzmax;
299
300 if (isnan_3BD (xline->point) || isnan_3BD (xline->vector)
301 || entity_invisible (obj))
302 return;
303
304 invvec.x = 1.0 / xline->vector.x;
305 invvec.y = 1.0 / xline->vector.y;
306 invvec.z = 1.0 / xline->vector.z;
307 sign[0] = (invvec.x < 0.0);
308 sign[1] = (invvec.y < 0.0);
309 sign[2] = (invvec.z < 0.0);
310 box[0].x = model_xmin;
311 box[0].y = model_ymin;
312 box[1].x = model_xmax;
313 box[1].y = model_ymin;
314 printf ("\t<!-- xline-%d -->\n", obj->index);
315
316 // untested!
317 /* intersect xline with model_xmin, model_ymin, model_xmax, model_ymax */
318 txmin = (box[sign[0]].x - xline->point.x) * invvec.x;
319 txmax = (box[1-sign[0]].x - xline->point.x) * invvec.x;
320 tymin = (box[sign[1]].x - xline->point.y) * invvec.y;
321 tymax = (box[1-sign[1]].x - xline->point.y) * invvec.y;
322 if ((txmin > tymax) || (tymin > txmax))
323 return;
324 if (tymin > txmin)
325 txmin = tymin;
326 if (tymax > txmax)
327 txmax = tymax;
328 tzmin = (box[sign[0]].z - xline->point.z) * invvec.z;
329 tzmax = (box[1-sign[0]].z - xline->point.z) * invvec.z;
330 if ((txmin > tzmax) || (tzmin > txmax))
331 return;
332
333 printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f L %f,%f\"\n\t",
334 obj->index, txmin, tymin, txmax, tymax);
335 common_entity (obj->tio.entity);
336 }
337
338 static void
output_RAY(Dwg_Object * obj)339 output_RAY (Dwg_Object *obj)
340 {
341 Dwg_Entity_XLINE *xline = obj->tio.entity->tio.RAY;
342 BITCODE_3DPOINT point, invvec;
343 static BITCODE_3DPOINT box[2];
344 int sign[3];
345 double txmin, txmax, tymin, tymax, tzmin, tzmax;
346
347 if (isnan_3BD (xline->point) || isnan_3BD (xline->vector)
348 || entity_invisible (obj))
349 return;
350
351 invvec.x = 1.0 / xline->vector.x;
352 invvec.y = 1.0 / xline->vector.y;
353 invvec.z = 1.0 / xline->vector.z;
354 sign[0] = (invvec.x < 0.0);
355 sign[1] = (invvec.y < 0.0);
356 sign[2] = (invvec.z < 0.0);
357 box[0].x = model_xmin;
358 box[0].y = model_ymin;
359 box[1].x = model_xmax;
360 box[1].y = model_ymin;
361 printf ("\t<!-- ray-%d -->\n", obj->index);
362
363 // untested!
364 /* intersect ray from point with box (model_xmin, model_ymin, model_xmax, model_ymax) */
365 txmin = (box[sign[0]].x - xline->point.x) * invvec.x;
366 txmax = (box[1-sign[0]].x - xline->point.x) * invvec.x;
367 tymin = (box[sign[1]].x - xline->point.y) * invvec.y;
368 tymax = (box[1-sign[1]].x - xline->point.y) * invvec.y;
369 if ((txmin > tymax) || (tymin > txmax))
370 return;
371 if (tymin > txmin)
372 txmin = tymin;
373 if (tymax > txmax)
374 txmax = tymax;
375 point.x = (xline->point.x > txmax) ? txmax : xline->point.x;
376 if (point.x < txmin)
377 point.x = txmin;
378 point.y = (xline->point.y > tymax) ? tymax : xline->point.y;
379 if (point.y < tymin)
380 point.y = tymin;
381
382 tzmin = (box[sign[0]].z - xline->point.z) * invvec.z;
383 tzmax = (box[1-sign[0]].z - xline->point.z) * invvec.z;
384 if ((txmin > tzmax) || (tzmin > txmax))
385 return;
386
387 printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f L %f,%f\"\n\t",
388 obj->index, point.x, point.y, txmax, tymax);
389 common_entity (obj->tio.entity);
390 }
391
392 static void
output_CIRCLE(Dwg_Object * obj)393 output_CIRCLE (Dwg_Object *obj)
394 {
395 Dwg_Entity_CIRCLE *circle = obj->tio.entity->tio.CIRCLE;
396 BITCODE_3DPOINT center;
397
398 if (isnan_3BD (circle->center) || isnan_3BD (circle->extrusion)
399 || isnan (circle->radius) || entity_invisible (obj))
400 return;
401 transform_OCS (¢er, circle->center, circle->extrusion);
402 printf ("\t<!-- circle-%d -->\n", obj->index);
403 printf ("\t<circle id=\"dwg-object-%d\" cx=\"%f\" cy=\"%f\" r=\"%f\"\n\t",
404 obj->index, transform_X (center.x), transform_Y (center.y),
405 circle->radius);
406 common_entity (obj->tio.entity);
407 }
408
409 // CIRCLE with radius 0.1
410 static void
output_POINT(Dwg_Object * obj)411 output_POINT (Dwg_Object *obj)
412 {
413 Dwg_Entity_POINT *point = obj->tio.entity->tio.POINT;
414 BITCODE_3DPOINT pt, pt1;
415
416 pt.x = point->x;
417 pt.y = point->y;
418 pt.z = point->z;
419 if (isnan_3BD (pt) || isnan_3BD (point->extrusion) || entity_invisible (obj))
420 return;
421 transform_OCS (&pt1, pt, point->extrusion);
422 printf ("\t<!-- point-%d -->\n", obj->index);
423 printf ("\t<circle id=\"dwg-object-%d\" cx=\"%f\" cy=\"%f\" r=\"0.1\"\n\t",
424 obj->index, transform_X (pt1.x), transform_Y (pt1.y));
425 common_entity (obj->tio.entity);
426 }
427
428 static void
output_ARC(Dwg_Object * obj)429 output_ARC (Dwg_Object *obj)
430 {
431 Dwg_Entity_ARC *arc = obj->tio.entity->tio.ARC;
432 BITCODE_3DPOINT center;
433 double x_start, y_start, x_end, y_end;
434 int large_arc;
435
436 if (isnan_3BD (arc->center) || isnan_3BD (arc->extrusion)
437 || isnan (arc->radius) || isnan (arc->start_angle)
438 || isnan (arc->end_angle) || entity_invisible (obj))
439 return;
440 transform_OCS (¢er, arc->center, arc->extrusion);
441
442 x_start = center.x + arc->radius * cos (arc->start_angle);
443 y_start = center.y + arc->radius * sin (arc->start_angle);
444 x_end = center.x + arc->radius * cos (arc->end_angle);
445 y_end = center.y + arc->radius * sin (arc->end_angle);
446 // Assuming clockwise arcs.
447 large_arc = (arc->end_angle - arc->start_angle < M_PI) ? 0 : 1;
448
449 printf ("\t<!-- arc-%d -->\n", obj->index);
450 printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f A %f,%f 0 %d,0 %f,%f\"\n\t",
451 obj->index, transform_X (x_start), transform_Y (y_start),
452 arc->radius, arc->radius, large_arc, transform_X (x_end),
453 transform_Y (y_end));
454 common_entity (obj->tio.entity);
455 }
456
457 // FIXME
458 static void
output_ELLIPSE(Dwg_Object * obj)459 output_ELLIPSE (Dwg_Object *obj)
460 {
461 Dwg_Entity_ELLIPSE *ell = obj->tio.entity->tio.ELLIPSE;
462 BITCODE_2DPOINT radius;
463 //BITCODE_3DPOINT center, sm_axis;
464 //double x_start, y_start, x_end, y_end;
465
466 if (isnan_3BD (ell->center) || isnan_3BD (ell->extrusion)
467 || isnan_3BD (ell->sm_axis)
468 || isnan (ell->axis_ratio) || isnan (ell->start_angle)
469 || isnan (ell->end_angle) || entity_invisible (obj))
470 return;
471 /* The 2 points are already WCS */
472 //transform_OCS (¢er, ell->center, ell->extrusion);
473 //transform_OCS (&sm_axis, ell->sm_axis, ell->extrusion);
474 radius.x = ell->sm_axis.x;
475 radius.y = ell->sm_axis.y * ell->axis_ratio;
476
477 /*
478 x_start = ell->center.x + radius.x * cos (ell->start_angle);
479 y_start = ell->center.y + radius.y * sin (ell->start_angle);
480 x_end = ell->center.x + radius.x * cos (ell->end_angle);
481 y_end = ell->center.y + radius.y * sin (ell->end_angle);
482 */
483
484 // TODO: rotate, start,end_angle
485 printf ("\t<!-- ellipse-%d -->\n", obj->index);
486 printf (
487 "\t<!-- sm_axis=(%f,%f,%f) axis_ratio=%f start_angle=%f end_angle=%f-->\n",
488 ell->sm_axis.x, ell->sm_axis.y, ell->sm_axis.z, ell->axis_ratio,
489 ell->start_angle, ell->end_angle);
490 printf ("\t<ellipse id=\"dwg-object-%d\" cx=\"%f\" cy=\"%f\" rx=\"%f\" "
491 "ry=\"%f\" transform=\"rotate=(%f)\"\n\t",
492 obj->index, ell->center.x, ell->center.y, radius.x, radius.y,
493 cos (ell->sm_axis.x));
494 common_entity (obj->tio.entity);
495 }
496
497 // untested
498 static void
output_SOLID(Dwg_Object * obj)499 output_SOLID (Dwg_Object *obj)
500 {
501 Dwg_Entity_SOLID *sol = obj->tio.entity->tio.SOLID;
502 BITCODE_2DPOINT c1, c2, c3, c4;
503 BITCODE_2DPOINT s1, s2, s3, s4;
504
505 memcpy (&s1, &sol->corner1, sizeof s1);
506 memcpy (&s2, &sol->corner2, sizeof s1);
507 memcpy (&s3, &sol->corner3, sizeof s1);
508 memcpy (&s4, &sol->corner4, sizeof s1);
509 if (isnan_2BD (s1) || isnan_2BD (s2) || isnan_2BD (s3) || isnan_2BD (s4)
510 || entity_invisible (obj))
511 return;
512 transform_OCS_2d (&c1, s1, sol->extrusion);
513 transform_OCS_2d (&c2, s2, sol->extrusion);
514 transform_OCS_2d (&c3, s3, sol->extrusion);
515 transform_OCS_2d (&c4, s4, sol->extrusion);
516
517 printf ("\t<!-- solid-%d -->\n", obj->index);
518 printf ("\t<polygon id=\"dwg-object-%d\" "
519 "points=\"%f,%f %f,%f %f,%f %f,%f\"\n\t",
520 obj->index, transform_X (c1.x), transform_Y (c1.y),
521 transform_X (c2.x), transform_Y (c2.y), transform_X (c3.x),
522 transform_Y (c3.y), transform_X (c4.x), transform_Y (c4.y));
523 common_entity (obj->tio.entity);
524 }
525
526 // untested
527 static void
output_3DFACE(Dwg_Object * obj)528 output_3DFACE (Dwg_Object *obj)
529 {
530 Dwg_Entity__3DFACE *ent = obj->tio.entity->tio._3DFACE;
531
532 if (isnan_3BD (ent->corner1) || isnan_3BD (ent->corner2)
533 || isnan_3BD (ent->corner3) || isnan_3BD (ent->corner4)
534 || entity_invisible (obj))
535 return;
536 printf ("\t<!-- 3dface-%d -->\n", obj->index);
537 if (ent->invis_flags)
538 {
539 // move to 1
540 printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f", obj->index,
541 ent->corner1.x, ent->corner1.y);
542 printf (" %s %f,%f", ent->invis_flags & 1 ? "M" : "L", ent->corner2.x,
543 ent->corner2.y);
544 printf (" %s %f,%f", ent->invis_flags & 2 ? "M" : "L", ent->corner3.x,
545 ent->corner3.y);
546 printf (" %s %f,%f", ent->invis_flags & 4 ? "M" : "L", ent->corner4.x,
547 ent->corner4.y);
548 printf (" %s %f,%f\"\n\t", ent->invis_flags & 8 ? "M" : "L", ent->corner1.x,
549 ent->corner1.y);
550 }
551 else
552 printf ("\t<polygon id=\"dwg-object-%d\" "
553 "points=\"%f,%f %f,%f %f,%f %f,%f\"\n\t",
554 obj->index, ent->corner1.x, ent->corner1.y, ent->corner2.x,
555 ent->corner2.y, ent->corner3.x, ent->corner3.y, ent->corner4.x,
556 ent->corner4.y);
557 common_entity (obj->tio.entity);
558 }
559
560 static void
output_POLYLINE_2D(Dwg_Object * obj)561 output_POLYLINE_2D (Dwg_Object *obj)
562 {
563 int error;
564 Dwg_Entity_POLYLINE_2D *pline = obj->tio.entity->tio.POLYLINE_2D;
565 BITCODE_RL numpts;
566
567 if (entity_invisible (obj))
568 return;
569 numpts = dwg_object_polyline_2d_get_numpoints (obj, &error);
570 if (numpts && !error)
571 {
572 BITCODE_2DPOINT pt, ptin;
573 dwg_point_2d *pts = dwg_object_polyline_2d_get_points (obj, &error);
574 BITCODE_RL j;
575
576 if (error || isnan_2pt (pts[0]) || isnan_3BD (pline->extrusion))
577 return;
578 ptin.x = pts[0].x;
579 ptin.y = pts[0].y;
580 transform_OCS_2d (&pt, ptin, pline->extrusion);
581 printf ("\t<!-- polyline_2d-%d -->\n", obj->index);
582 printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f", obj->index,
583 transform_X (pt.x), transform_Y (pt.y));
584 // TODO curve_types, C for Bezier https://svgwg.org/specs/paths/#PathData
585 for (j = 1; j < numpts; j++)
586 {
587 ptin.x = pts[j].x;
588 ptin.y = pts[j].y;
589 if (isnan_2BD (ptin))
590 continue;
591 transform_OCS_2d (&pt, ptin, pline->extrusion);
592 // TODO bulge -> arc, widths
593 printf (" L %f,%f", transform_X (pt.x), transform_Y (pt.y));
594 }
595 if (pline->flag & 1) // closed
596 printf (" Z");
597 printf ("\"\n\t");
598 common_entity (obj->tio.entity);
599 free (pts);
600 }
601 }
602
603 static void
output_LWPOLYLINE(Dwg_Object * obj)604 output_LWPOLYLINE (Dwg_Object *obj)
605 {
606 int error;
607 Dwg_Entity_LWPOLYLINE *pline = obj->tio.entity->tio.LWPOLYLINE;
608 BITCODE_RL numpts;
609
610 if (entity_invisible (obj))
611 return;
612 numpts = dwg_ent_lwpline_get_numpoints (pline, &error);
613 if (numpts && !error)
614 {
615 BITCODE_2DPOINT pt, ptin;
616 dwg_point_2d *pts = dwg_ent_lwpline_get_points (pline, &error);
617 BITCODE_RL j;
618
619 if (error || isnan_2pt (pts[0]) || isnan_3BD (pline->extrusion))
620 return;
621 ptin.x = pts[0].x;
622 ptin.y = pts[0].y;
623 transform_OCS_2d (&pt, ptin, pline->extrusion);
624 printf ("\t<!-- lwpolyline-%d -->\n", obj->index);
625 printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f", obj->index,
626 transform_X (pt.x), transform_Y (pt.y));
627 // TODO curve_types, C for Bezier https://svgwg.org/specs/paths/#PathData
628 for (j = 1; j < numpts; j++)
629 {
630 ptin.x = pts[j].x;
631 ptin.y = pts[j].y;
632 if (isnan_2BD (ptin))
633 continue;
634 transform_OCS_2d (&pt, ptin, pline->extrusion);
635 // TODO bulge -> arc, widths
636 printf (" L %f,%f", transform_X (pt.x), transform_Y (pt.y));
637 }
638 if (pline->flag & 512) // closed
639 printf (" Z");
640 printf ("\"\n\t");
641 common_entity (obj->tio.entity);
642 free (pts);
643 }
644 }
645
646 // TODO: MINSERT
647 static void
output_INSERT(Dwg_Object * obj)648 output_INSERT (Dwg_Object *obj)
649 {
650 Dwg_Entity_INSERT *insert = obj->tio.entity->tio.INSERT;
651 if (entity_invisible (obj))
652 return;
653 if (insert->block_header && insert->block_header->handleref.value)
654 {
655 BITCODE_3DPOINT ins_pt;
656 if (isnan_3BD (insert->ins_pt) || isnan_3BD (insert->extrusion)
657 || isnan (insert->rotation) || isnan_3BD (insert->scale))
658 return;
659 transform_OCS (&ins_pt, insert->ins_pt, insert->extrusion);
660 printf ("\t<!-- insert-%d -->\n", obj->index);
661 printf ("\t<use id=\"dwg-object-%d\" transform=\"translate(%f %f) "
662 "rotate(%f) scale(%f %f)\" xlink:href=\"#symbol-%lX\" />"
663 "<!-- block_header->handleref: " FORMAT_H " -->\n",
664 obj->index, transform_X (ins_pt.x), transform_Y (ins_pt.y),
665 (180.0 / M_PI) * insert->rotation, insert->scale.x,
666 insert->scale.y, insert->block_header->absolute_ref,
667 ARGS_H (insert->block_header->handleref));
668 }
669 else
670 {
671 printf ("\n\n<!-- WRONG INSERT(" FORMAT_H ") -->\n",
672 ARGS_H (obj->handle));
673 }
674 }
675
676 static int
output_object(Dwg_Object * obj)677 output_object (Dwg_Object *obj)
678 {
679 int num = 1;
680 if (!obj)
681 {
682 fprintf (stderr, "object is NULL\n");
683 return 0;
684 }
685
686 switch (obj->type)
687 {
688 case DWG_TYPE_INSERT:
689 output_INSERT (obj);
690 break;
691 case DWG_TYPE_LINE:
692 output_LINE (obj);
693 break;
694 case DWG_TYPE_CIRCLE:
695 output_CIRCLE (obj);
696 break;
697 case DWG_TYPE_TEXT:
698 output_TEXT (obj);
699 break;
700 case DWG_TYPE_ARC:
701 output_ARC (obj);
702 break;
703 case DWG_TYPE_POINT:
704 output_POINT (obj);
705 break;
706 case DWG_TYPE_ELLIPSE:
707 output_ELLIPSE (obj);
708 break;
709 case DWG_TYPE_SOLID:
710 output_SOLID (obj);
711 break;
712 case DWG_TYPE__3DFACE:
713 output_3DFACE (obj);
714 break;
715 case DWG_TYPE_POLYLINE_2D:
716 output_POLYLINE_2D (obj);
717 break;
718 case DWG_TYPE_LWPOLYLINE:
719 output_LWPOLYLINE (obj);
720 break;
721 case DWG_TYPE_RAY:
722 output_RAY (obj);
723 break;
724 case DWG_TYPE_XLINE:
725 output_XLINE (obj);
726 break;
727 case DWG_TYPE_SEQEND:
728 case DWG_TYPE_VIEWPORT:
729 break;
730 default:
731 num = 0;
732 if (obj->supertype == DWG_SUPERTYPE_ENTITY)
733 fprintf (stderr, "%s ignored\n", obj->name);
734 // all other non-graphical objects are silently ignored
735 break;
736 }
737 return num;
738 }
739
740 static int
output_BLOCK_HEADER(Dwg_Object_Ref * ref)741 output_BLOCK_HEADER (Dwg_Object_Ref *ref)
742 {
743 Dwg_Object *obj;
744 Dwg_Object_BLOCK_HEADER *hdr;
745 int is_g = 0;
746 int num = 0;
747
748 if (!ref) // silently ignore empty pspaces
749 return 0;
750 if (!ref->obj)
751 return 0;
752 obj = ref->obj;
753 if (obj->type != DWG_TYPE_BLOCK_HEADER)
754 {
755 fprintf (stderr, "Argument not a BLOCK_HEADER reference\n");
756 return 0;
757 }
758 if (!obj->tio.object)
759 { // TODO could be an assert also
760 fprintf (stderr, "Found null obj->tio.object\n");
761 return 0;
762 }
763 if (!obj->tio.object->tio.BLOCK_HEADER)
764 { // TODO could be an assert also
765 fprintf (stderr, "Found null obj->tio.object->tio.BLOCK_HEADER\n");
766 return 0;
767 }
768
769 hdr = obj->tio.object->tio.BLOCK_HEADER;
770 if (hdr->name)
771 {
772 char *escaped;
773 Dwg_Data *dwg = obj->parent;
774 if (dwg->header.version >= R_2007)
775 escaped = htmlwescape ((BITCODE_TU)hdr->name);
776 else
777 escaped = htmlescape (hdr->name, dwg->header.codepage);
778 // fatal: The string "--" is not permitted within comments.
779 if (escaped && strstr (escaped, "--"))
780 {
781 char *s;
782 while ((s = strstr (escaped, "--")))
783 {
784 *s = '_';
785 *(s + 1) = '_';
786 }
787 }
788 // don't group *Model_Space
789 if (!escaped || strcmp (escaped, "*Model_Space") != 0)
790 {
791 is_g = 1;
792 printf ("\t<g id=\"symbol-%lX\" >\n\t\t<!-- %s -->\n", ref->absolute_ref,
793 escaped ? escaped : "");
794 }
795 else
796 printf ("\t<!-- %s -->\n", escaped);
797 if (escaped)
798 free (escaped);
799 }
800
801 obj = get_first_owned_entity (ref->obj);
802 while (obj)
803 {
804 num += output_object (obj);
805 obj = get_next_owned_entity (ref->obj, obj);
806 }
807
808 if (is_g)
809 printf ("\t</g>\n");
810 return num;
811 }
812
813 static void
output_SVG(Dwg_Data * dwg)814 output_SVG (Dwg_Data *dwg)
815 {
816 BITCODE_BS i;
817 int num = 0;
818 Dwg_Object *obj;
819 Dwg_Object_Ref *ref;
820 Dwg_Object_BLOCK_CONTROL *block_control;
821 double dx, dy;
822
823 model_xmin = dwg_model_x_min (dwg);
824 model_ymin = dwg_model_y_min (dwg);
825 model_xmax = dwg_model_x_max (dwg);
826 model_ymax = dwg_model_y_max (dwg);
827
828 dx = model_xmax - model_xmin;
829 dy = model_ymax - model_ymin;
830 // double scale_x = dx / (dwg_page_x_max(dwg) - dwg_page_x_min(dwg));
831 // double scale_y = dy / (dwg_page_y_max(dwg) - dwg_page_y_min(dwg));
832 scale = 25.4 / 72; // pt:mm
833 if (isnan (dx))
834 dx = 100.0;
835 if (isnan (dy))
836 dy = 100.0;
837 page_width = dx;
838 page_height = dy;
839 // scale *= (scale_x > scale_y ? scale_x : scale_y);
840
841 // optional, for xmllint
842 // <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
843 // "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
844 // But we use jing with relaxng, which is better. Just LaTeXML shipped a broken rng
845 printf ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
846 "<svg\n"
847 " xmlns:svg=\"http://www.w3.org/2000/svg\"\n"
848 " xmlns=\"http://www.w3.org/2000/svg\"\n"
849 " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
850 " version=\"1.1\" baseProfile=\"basic\"\n"
851 " width=\"100%%\" height=\"100%%\"\n"
852 " viewBox=\"%f %f %f %f\">\n",
853 model_xmin, model_ymin, page_width, page_height);
854
855 if (!mspace && (ref = dwg_paper_space_ref (dwg)))
856 num = output_BLOCK_HEADER (ref); // how many paper-space entities we did print
857 if (!num && (ref = dwg_model_space_ref (dwg)))
858 output_BLOCK_HEADER (ref);
859 printf ("\t<defs>\n");
860 for (i = 0; i < dwg->block_control.num_entries; i++)
861 {
862 if ((ref = dwg->block_control.entries[i]))
863 output_BLOCK_HEADER (ref);
864 }
865 printf ("\t</defs>\n");
866 printf ("</svg>\n");
867 }
868
869 int
main(int argc,char * argv[])870 main (int argc, char *argv[])
871 {
872 int error;
873 int force_free = 0;
874 int i = 1;
875 int c;
876 #ifdef HAVE_GETOPT_LONG
877 int option_index = 0;
878 static struct option long_options[]
879 = { { "verbose", 1, &opts, 1 }, // optional
880 { "mspace", 0, 0, 0 },
881 { "force-free", 0, 0, 0 },
882 { "help", 0, 0, 0 },
883 { "version", 0, 0, 0 },
884 { NULL, 0, NULL, 0 } };
885 #endif
886
887 if (argc < 2)
888 return usage ();
889
890 while
891 #ifdef HAVE_GETOPT_LONG
892 ((c = getopt_long (argc, argv, ":v:m::h", long_options, &option_index))
893 != -1)
894 #else
895 ((c = getopt (argc, argv, ":v:m::hi")) != -1)
896 #endif
897 {
898 if (c == -1)
899 break;
900 switch (c)
901 {
902 case ':': // missing arg
903 if (optarg && !strcmp (optarg, "v"))
904 {
905 opts = 1;
906 break;
907 }
908 fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
909 optopt);
910 break;
911 #ifdef HAVE_GETOPT_LONG
912 case 0:
913 /* This option sets a flag */
914 if (!strcmp (long_options[option_index].name, "verbose"))
915 {
916 if (opts < 0 || opts > 9)
917 return usage ();
918 # if defined(USE_TRACING) && defined(HAVE_SETENV)
919 {
920 char v[2];
921 *v = opts + '0';
922 *(v + 1) = 0;
923 setenv ("LIBREDWG_TRACE", v, 1);
924 }
925 # endif
926 break;
927 }
928 if (!strcmp (long_options[option_index].name, "version"))
929 return opt_version ();
930 if (!strcmp (long_options[option_index].name, "help"))
931 return help ();
932 if (!strcmp (long_options[option_index].name, "force-free"))
933 force_free = 1;
934 if (!strcmp (long_options[option_index].name, "mspace"))
935 mspace = 1;
936 break;
937 #else
938 case 'i':
939 return opt_version ();
940 #endif
941 case 'v': // support -v3 and -v
942 i = (optind > 0 && optind < argc) ? optind - 1 : 1;
943 if (!memcmp (argv[i], "-v", 2))
944 {
945 opts = argv[i][2] ? argv[i][2] - '0' : 1;
946 }
947 if (opts < 0 || opts > 9)
948 return usage ();
949 #if defined(USE_TRACING) && defined(HAVE_SETENV)
950 {
951 char v[2];
952 *v = opts + '0';
953 *(v + 1) = 0;
954 setenv ("LIBREDWG_TRACE", v, 1);
955 }
956 #endif
957 break;
958 case 'h':
959 return help ();
960 case '?':
961 fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
962 optopt);
963 break;
964 default:
965 return usage ();
966 }
967 }
968 i = optind;
969 if (i >= argc)
970 return usage ();
971
972 memset (&g_dwg, 0, sizeof (Dwg_Data));
973 g_dwg.opts = opts;
974 error = dwg_read_file (argv[i], &g_dwg);
975
976 if (opts)
977 fprintf (stderr, "\nSVG\n===\n");
978 if (error < DWG_ERR_CRITICAL)
979 output_SVG (&g_dwg);
980
981 #if defined __SANITIZE_ADDRESS__ || __has_feature(address_sanitizer)
982 {
983 char *asanenv = getenv("ASAN_OPTIONS");
984 if (!asanenv)
985 force_free = 1;
986 // detect_leaks is enabled by default. see if it's turned off
987 else if (strstr (asanenv, "detect_leaks=0") == NULL) /* not found */
988 force_free = 1;
989 }
990 #endif
991
992 // forget about leaks. really huge DWG's need endlessly here.
993 if ((g_dwg.header.version && g_dwg.num_objects < 1000)
994 || force_free
995 #ifdef HAVE_VALGRIND_VALGRIND_H
996 || (RUNNING_ON_VALGRIND)
997 #endif
998 )
999 {
1000 dwg_free (&g_dwg);
1001 }
1002 return error >= DWG_ERR_CRITICAL ? 1 : 0;
1003 }
1004