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