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 (&center, 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 (&center, 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 (&center, 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