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", ¢er))
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", ¢er);
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