1 /*****************************************************************************/
2 /*  LibreDWG - free implementation of the DWG file format                    */
3 /*                                                                           */
4 /*  Copyright (C) 2013-2020 Free Software Foundation, Inc.                   */
5 /*                                                                           */
6 /*  This library is free software, licensed under the terms of the GNU       */
7 /*  General Public License as published by the Free Software Foundation,     */
8 /*  either version 3 of the License, or (at your option) any later version.  */
9 /*  You should have received a copy of the GNU General Public License        */
10 /*  along with this program.  If not, see <http://www.gnu.org/licenses/>.    */
11 /*****************************************************************************/
12 
13 /*
14  * dwg2svg2.c: convert a DWG to SVG via the API
15  * written by Gaganjyot Singh
16  * modified by Reini Urban
17  */
18 
19 #include "../src/config.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <math.h>
25 #include <getopt.h>
26 
27 #include <dwg.h>
28 #include <dwg_api.h>
29 #include "../programs/geom.h"
30 
31 static int opts = 0;
32 static dwg_data g_dwg;
33 static double model_xmin, model_ymin;
34 static double page_width, page_height, scale;
35 
36 static int
usage(void)37 usage (void)
38 {
39   printf ("\nUsage: dwg2svg2 [-v[0-9]] DWGFILE\n");
40   return 1;
41 }
42 static int
opt_version(void)43 opt_version (void)
44 {
45   printf ("dwg2svg2 %s\n", PACKAGE_VERSION);
46   return 0;
47 }
48 
49 static int
help(void)50 help (void)
51 {
52   printf ("\nUsage: dwg2svg2 [OPTION]... DWGFILE >file.svg\n");
53   printf ("Example to use the DWG api\n"
54           "\n");
55 #ifdef HAVE_GETOPT_LONG
56   printf ("  -v[0-9], --verbose [0-9]  verbosity\n");
57   printf ("           --help           display this help and exit\n");
58   printf ("           --version        output version information and exit\n"
59           "\n");
60 #else
61   printf ("  -v[0-9]     verbosity\n");
62   printf ("  -h          display this help and exit\n");
63   printf ("  -i          output version information and exit\n"
64           "\n");
65 #endif
66   printf ("GNU LibreDWG online manual: "
67           "<https://www.gnu.org/software/libredwg/>\n");
68   return 0;
69 }
70 
71 #define log_if_error(msg)                                                     \
72   if (error)                                                                  \
73     {                                                                         \
74       fprintf (stderr, "ERROR: %s", msg);                                     \
75       exit (1);                                                               \
76     }
77 #define log_error(msg)                                                        \
78   {                                                                           \
79     fprintf (stderr, "ERROR: %s", msg);                                       \
80     exit (1);                                                                 \
81   }
82 #define dynget(obj, name, field, var)                                         \
83   if (!dwg_dynapi_entity_value (obj, "" name, "" field, var, NULL))           \
84     {                                                                         \
85       fprintf (stderr, "ERROR: %s.%s", name, field);                          \
86       exit (1);                                                               \
87     }
88 #define dynget_utf8(obj, name, field, var)                                    \
89   if (!dwg_dynapi_entity_utf8text (obj, "" name, "" field, var, &isnew, NULL)) \
90     {                                                                         \
91       fprintf (stderr, "ERROR: %s.%s", name, field);                          \
92       exit (1);                                                               \
93     }
94 
95 static double
transform_X(double x)96 transform_X (double x)
97 {
98   return x - model_xmin;
99 }
100 
101 static double
transform_Y(double y)102 transform_Y (double y)
103 {
104   return page_height - (y - model_ymin);
105 }
106 
107 static void output_SVG (dwg_data *dwg);
108 
109 static int
test_SVG(char * filename)110 test_SVG (char *filename)
111 {
112   int error;
113 
114   memset (&g_dwg, 0, sizeof (dwg_data));
115   g_dwg.opts = opts;
116   error = dwg_read_file (filename, &g_dwg);
117   if (error < DWG_ERR_CRITICAL)
118     output_SVG (&g_dwg);
119 
120   dwg_free (&g_dwg);
121   /* This value is the return value for `main',
122      so clamp it to either 0 or 1.  */
123   return error < DWG_ERR_CRITICAL ? 0 : 1;
124 }
125 
126 static void
output_TEXT(dwg_object * obj)127 output_TEXT (dwg_object *obj)
128 {
129   int error, index;
130   dwg_point_2d ins_pt;
131   Dwg_Entity_TEXT *text;
132   char *text_value;
133   double fontsize;
134   const Dwg_Version_Type dwg_version = obj->parent->header.version;
135   int isnew = 0;
136 
137   index = dwg_object_get_index (obj, &error);
138   log_if_error ("object_get_index");
139   text = dwg_object_to_TEXT (obj);
140   if (!text)
141     log_error ("dwg_object_to_TEXT");
142   dynget_utf8 (text, "TEXT", "text_value", &text_value);
143   dynget (text, "TEXT", "ins_pt", &ins_pt);
144   dynget (text, "TEXT", "height", &fontsize);
145 
146   printf ("\t<text id=\"dwg-object-%d\" x=\"%f\" y=\"%f\" "
147           "font-family=\"Verdana\" font-size=\"%f\" fill=\"blue\">%s</text>\n",
148           index, transform_X (ins_pt.x), transform_Y (ins_pt.y), fontsize,
149           text_value);
150 
151   if (text_value && isnew)
152     free (text_value);
153 }
154 
155 static void
output_LINE(dwg_object * obj)156 output_LINE (dwg_object *obj)
157 {
158   int error, index;
159   Dwg_Entity_LINE *line;
160   dwg_point_3d start, end;
161 
162   index = dwg_object_get_index (obj, &error);
163   log_if_error ("object_get_index");
164   line = dwg_object_to_LINE (obj);
165   if (!line)
166     log_error ("dwg_object_to_LINE");
167   if (!dwg_get_LINE (line, "start", &start))
168     log_error ("LINE.start");
169   if (!dwg_get_LINE (line, "end", &end))
170     log_error ("LINE.end");
171 
172   printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f %f,%f\" "
173           "style=\"fill:none;stroke:blue;stroke-width:0.1px\" />\n",
174           index, transform_X (start.x), transform_Y (start.y),
175           transform_X (end.x), transform_Y (end.y));
176 }
177 
178 static void
output_CIRCLE(dwg_object * obj)179 output_CIRCLE (dwg_object *obj)
180 {
181   Dwg_Entity_CIRCLE *circle;
182   int error, index;
183   double radius;
184   dwg_point_3d center;
185 
186   index = dwg_object_get_index (obj, &error);
187   log_if_error ("object_get_index");
188   circle = dwg_object_to_CIRCLE (obj);
189   if (!circle)
190     log_error ("dwg_object_to_CIRCLE");
191   if (!dwg_get_CIRCLE (circle, "center", &center))
192     log_error ("CIRCLE.center");
193   if (!dwg_get_CIRCLE (circle, "radius", &radius))
194     log_error ("CIRCLE.radius");
195 
196   printf ("\t<circle id=\"dwg-object-%d\" cx=\"%f\" cy=\"%f\" r=\"%f\" "
197           "fill=\"none\" stroke=\"blue\" stroke-width=\"0.1px\" />\n",
198           index, transform_X (center.x), transform_Y (center.y), radius);
199 }
200 
201 static void
output_ARC(dwg_object * obj)202 output_ARC (dwg_object *obj)
203 {
204   Dwg_Entity_ARC *arc;
205   int error, index;
206   double radius, start_angle, end_angle;
207   dwg_point_3d center;
208   double x_start, y_start, x_end, y_end;
209   int large_arc;
210 
211   index = dwg_object_get_index (obj, &error);
212   log_if_error ("object_get_index");
213   arc = dwg_object_to_ARC (obj);
214   if (!arc)
215     log_error ("dwg_object_to_ARC");
216   dynget (arc, "ARC", "radius", &radius);
217   dynget (arc, "ARC", "center", &center);
218   dynget (arc, "ARC", "start_angle", &start_angle);
219   dynget (arc, "ARC", "end_angle", &end_angle);
220 
221   x_start = center.x + radius * cos (start_angle);
222   y_start = center.y + radius * sin (start_angle);
223   x_end = center.x + radius * cos (end_angle);
224   y_end = center.y + radius * sin (end_angle);
225   // Assuming clockwise arcs.
226   large_arc = (end_angle - start_angle < M_PI) ? 0 : 1;
227 
228   printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f A %f,%f 0 %d 0 %f,%f\" "
229           "fill=\"none\" stroke=\"blue\" stroke-width=\"%f\" />\n",
230           index, transform_X (x_start), transform_Y (y_start), radius, radius,
231           large_arc, transform_X (x_end), transform_Y (y_end), 0.1);
232 }
233 
234 static void
output_INSERT(dwg_object * obj)235 output_INSERT (dwg_object *obj)
236 {
237   int index, error;
238   BITCODE_RL abs_ref;
239   double rotation;
240   dwg_ent_insert *insert;
241   dwg_point_3d ins_pt, _scale;
242   dwg_handle *obj_handle, *ins_handle;
243 
244   insert = dwg_object_to_INSERT (obj);
245   if (!insert)
246     log_error ("dwg_object_to_INSERT");
247   index = dwg_object_get_index (obj, &error);
248   log_if_error ("object_get_index");
249   dynget (insert, "INSERT", "rotation", &rotation);
250   dynget (insert, "INSERT", "ins_pt", &ins_pt);
251   dynget (insert, "INSERT", "scale", &_scale);
252   obj_handle = dwg_object_get_handle (obj, &error);
253   log_if_error ("get_handle");
254   if (!insert->block_header)
255     log_error ("insert->block_header");
256   abs_ref = insert->block_header->absolute_ref;
257 
258   if (insert->block_header->handleref.code == 5)
259     {
260       printf ("\t<use id=\"dwg-object-%d\" transform=\"translate(%f %f) "
261               "rotate(%f) scale(%f %f)\" xlink:href=\"#symbol-%X\" /><!-- "
262               "block_header->handleref: " FORMAT_H " -->\n",
263               index, transform_X (ins_pt.x), transform_Y (ins_pt.y),
264               (180.0 / M_PI) * rotation, _scale.x, _scale.y, abs_ref,
265               ARGS_H (*obj_handle));
266     }
267   else
268     {
269       printf ("\n\n<!-- WRONG INSERT(" FORMAT_H ") -->\n",
270               ARGS_H (*obj_handle));
271     }
272 }
273 
274 static void
output_object(dwg_object * obj)275 output_object (dwg_object *obj)
276 {
277   if (!obj)
278     {
279       fprintf (stderr, "object is NULL\n");
280       return;
281     }
282 
283   if (dwg_object_get_type (obj) == DWG_TYPE_INSERT)
284     {
285       output_INSERT (obj);
286     }
287 
288   if (dwg_object_get_type (obj) == DWG_TYPE_LINE)
289     {
290       output_LINE (obj);
291     }
292 
293   if (dwg_object_get_type (obj) == DWG_TYPE_CIRCLE)
294     {
295       output_CIRCLE (obj);
296     }
297 
298   if (dwg_object_get_type (obj) == DWG_TYPE_TEXT)
299     {
300       output_TEXT (obj);
301     }
302 
303   if (dwg_object_get_type (obj) == DWG_TYPE_ARC)
304     {
305       output_ARC (obj);
306     }
307 }
308 
309 static void
output_BLOCK_HEADER(dwg_object_ref * ref)310 output_BLOCK_HEADER (dwg_object_ref *ref)
311 {
312   dwg_object *hdr, *obj;
313   dwg_obj_block_header *_hdr;
314   int error;
315   BITCODE_RL abs_ref;
316   char *name;
317 
318   if (!ref)
319     {
320       fprintf (stderr,
321                "Empty BLOCK."
322                " Could not output an SVG symbol for this BLOCK_HEADER\n");
323       return;
324     }
325   hdr = dwg_ref_get_object (ref, &error);
326   if (!hdr || error)
327     return;
328   abs_ref = dwg_ref_get_absref (ref, &error);
329 
330   _hdr = dwg_object_to_BLOCK_HEADER (hdr);
331   if (_hdr)
332     {
333       dynget (_hdr, "BLOCK_HEADER", "name", &name);
334       // name = dwg_obj_block_header_get_name (_hdr, &error);
335       printf ("\t<g id=\"symbol-%X\" >\n\t\t<!-- %s -->\n", abs_ref ? abs_ref : 0,
336               name ? name : "");
337       if (name != NULL && name != _hdr->name
338           && hdr->parent->header.version >= R_2007)
339         free (name);
340     }
341   else
342     printf ("\t<g id=\"symbol-%X\" >\n\t\t<!-- ? -->\n", abs_ref ? abs_ref : 0);
343 
344   obj = get_first_owned_entity (hdr);
345   while (obj)
346     {
347       output_object (obj);
348       obj = get_next_owned_entity (hdr, obj);
349     }
350 
351   printf ("\t</g>\n");
352 }
353 
354 static void
output_SVG(dwg_data * dwg)355 output_SVG (dwg_data *dwg)
356 {
357   unsigned int i, num_hdr_objs;
358   int error;
359   dwg_obj_block_control *_ctrl;
360   dwg_object_ref **hdr_refs;
361 
362   double dx = dwg_model_x_max (dwg) - dwg_model_x_min (dwg);
363   double dy = dwg_model_y_max (dwg) - dwg_model_y_min (dwg);
364   double pdx = dwg->header_vars.PLIMMAX.x - dwg->header_vars.PLIMMIN.x;
365   double pdy = dwg->header_vars.PLIMMAX.y - dwg->header_vars.PLIMMIN.y;
366   double scale_x = dx / (pdx == 0.0 ? 1.0 : pdx);
367   double scale_y = dy / (pdy == 0.0 ? 1.0 : pdy);
368   scale = 25.4 / 72.0; // pt:mm TODO
369 
370   model_xmin = dwg_model_x_min (dwg);
371   model_ymin = dwg_model_y_min (dwg);
372   page_width = dx;
373   page_height = dy;
374   scale *= (scale_x > scale_y ? scale_x : scale_y);
375 
376   _ctrl = dwg_block_control (dwg);
377   hdr_refs = dwg_obj_block_control_get_block_headers (_ctrl, &error);
378   log_if_error ("block_control_get_block_headers");
379   num_hdr_objs = dwg_obj_block_control_get_num_entries (_ctrl, &error);
380   log_if_error ("block_control_get_num_entries");
381 
382   printf ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
383           "<svg\n"
384           "   xmlns:svg=\"http://www.w3.org/2000/svg\"\n"
385           "   xmlns=\"http://www.w3.org/2000/svg\"\n"
386           "   xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
387           "   version=\"1.1\"\n"
388           "   width=\"%f\"\n"
389           "   height=\"%f\"\n"
390           ">\n",
391           page_width, page_height);
392   printf ("\t<defs>\n");
393 
394   for (i = 0; i < num_hdr_objs; i++)
395     {
396       output_BLOCK_HEADER (hdr_refs[i]);
397     }
398   printf ("\t</defs>\n");
399 
400   output_BLOCK_HEADER (dwg_model_space_ref (dwg));
401   output_BLOCK_HEADER (dwg_paper_space_ref (dwg));
402   free (hdr_refs);
403 
404   printf ("</svg>\n");
405 }
406 
407 int
main(int argc,char * argv[])408 main (int argc, char *argv[])
409 {
410   int i = 1;
411   int c;
412 #ifdef HAVE_GETOPT_LONG
413   int option_index = 0;
414   static struct option long_options[]
415       = { { "verbose", 1, &opts, 1 }, // optional
416           { "help", 0, 0, 0 },
417           { "version", 0, 0, 0 },
418           { NULL, 0, NULL, 0 } };
419 #endif
420 
421   if (argc < 2)
422     return usage ();
423 
424   while
425 #ifdef HAVE_GETOPT_LONG
426       ((c = getopt_long (argc, argv, ":v::h", long_options, &option_index))
427        != -1)
428 #else
429       ((c = getopt (argc, argv, ":v::hi")) != -1)
430 #endif
431     {
432       if (c == -1)
433         break;
434       switch (c)
435         {
436         case ':': // missing arg
437           if (optarg && !strcmp (optarg, "v"))
438             {
439               opts = 1;
440               break;
441             }
442           fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
443                    optopt);
444           break;
445 #ifdef HAVE_GETOPT_LONG
446         case 0:
447           /* This option sets a flag */
448           if (!strcmp (long_options[option_index].name, "verbose"))
449             {
450               if (opts < 0 || opts > 9)
451                 return usage ();
452 #  if defined(USE_TRACING) && defined(HAVE_SETENV)
453               {
454                 char v[2];
455                 *v = opts + '0';
456                 *(v + 1) = 0;
457                 setenv ("LIBREDWG_TRACE", v, 1);
458               }
459 #  endif
460               break;
461             }
462           if (!strcmp (long_options[option_index].name, "version"))
463             return opt_version ();
464           if (!strcmp (long_options[option_index].name, "help"))
465             return help ();
466           break;
467 #else
468         case 'i':
469           return opt_version ();
470 #endif
471         case 'v': // support -v3 and -v
472           i = (optind > 0 && optind < argc) ? optind - 1 : 1;
473           if (!memcmp (argv[i], "-v", 2))
474             {
475               opts = argv[i][2] ? argv[i][2] - '0' : 1;
476             }
477           if (opts < 0 || opts > 9)
478             return usage ();
479 #if defined(USE_TRACING) && defined(HAVE_SETENV)
480           {
481             char v[2];
482             *v = opts + '0';
483             *(v + 1) = 0;
484             setenv ("LIBREDWG_TRACE", v, 1);
485           }
486 #endif
487           break;
488         case 'h':
489           return help ();
490         case '?':
491           fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
492                    optopt);
493           break;
494         default:
495           return usage ();
496         }
497     }
498   i = optind;
499   if (i >= argc)
500     return usage ();
501 
502   return test_SVG (argv[i]);
503 }
504