1 /*****************************************************************************/
2 /* LibreDWG - free implementation of the DWG file format */
3 /* */
4 /* Copyright (C) 2009-2023 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 * dwg2ps.c: create a PostScript file of lines from a DWG
16 * TODO: more 2D elements, see dwg2SVG
17 * written by Felipe Castro
18 * modified by Felipe CorrĂȘa da Silva Sances
19 * modified by Rodrigo Rodrigues da Silva
20 * modified by Thien-Thi Nguyen
21 * modified by Reini Urban
22 */
23
24 #include "../src/config.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <libps/pslib.h>
29 #include "my_getopt.h"
30
31 #include <dwg.h>
32 #include <dwg_api.h>
33 #include "common.h"
34 #include "../src/bits.h" //bit_convert_TU
35 #include "suffix.inc"
36 #include "geom.h"
37
38 static int opts = 0;
39
40 static int
41 usage (void)
42 {
43 printf ("\nUsage: dwg2ps [-v[0-9]] DWGFILE [PSFILE]\n");
44 return 1;
45 }
46 static int
47 opt_version (void)
48 {
49 printf ("dwg2ps %s\n", PACKAGE_VERSION);
50 return 0;
51 }
52 static int
53 help (void)
54 {
55 printf ("\nUsage: dwg2ps [OPTION]... DWGFILE [PSFILE]\n");
56 printf ("Converts some 2D elements of the DWG to a Postscript file.\n"
57 "\n");
58 #ifdef HAVE_GETOPT_LONG
59 printf (" -v[0-9], --verbose [0-9] verbosity\n");
60 printf (" --help display this help and exit\n");
61 printf (" --version output version information and exit\n"
62 "\n");
63 #else
64 printf (" -v[0-9] verbosity\n");
65 printf (" -h display this help and exit\n");
66 printf (" -i output version information and exit\n"
67 "\n");
68 #endif
69 printf ("GNU LibreDWG online manual: "
70 "<https://www.gnu.org/software/libredwg/>\n");
71 return 0;
72 }
73
74 /* handles r2007 wide chars (=> utf8) */
75 static int
76 set_info (PSDoc *restrict ps, Dwg_Data *restrict dwg, const char *restrict key,
77 BITCODE_T16 restrict text)
78 {
79 int ret = 0;
80 if (!text)
81 return 0;
82 if (dwg->header.from_version < R_2007)
83 {
84 PS_set_info (ps, key, text);
85 ret = 1;
86 }
87 else
88 {
89 char *u8 = bit_convert_TU ((BITCODE_TU)text);
90 if (u8 && strlen (u8))
91 {
92 PS_set_info (ps, key, u8);
93 ret = 1;
94 }
95 if (u8)
96 free (u8);
97 }
98 return ret;
99 }
100
101 static void
102 create_postscript (Dwg_Data *dwg, char *output)
103 {
104 double dx;
105 double dy;
106 double scale_x;
107 double scale_y;
108 double scale;
109 BITCODE_BL i;
110 // FILE *fh;
111 PSDoc *ps;
112
113 /* Initialization
114 */
115 PS_boot ();
116 ps = PS_new ();
117 if (PS_open_file (ps, output) < 0)
118 {
119 puts ("Cannot write PostScript file");
120 return;
121 }
122
123 PS_set_info (ps, "Creator", "dwg2ps " PACKAGE_VERSION);
124 (void)set_info (ps, dwg, "Subject", dwg->summaryinfo.SUBJECT);
125 (void)set_info (ps, dwg, "Author", dwg->summaryinfo.LASTSAVEDBY);
126 if (set_info (ps, dwg, "Title", dwg->summaryinfo.TITLE))
127 (void)set_info (ps, dwg, "Keywords", dwg->summaryinfo.KEYWORDS);
128 else
129 {
130 PS_set_info (ps, "Title", output);
131 PS_set_info (ps, "Keywords", "dwg, postscript, conversion, CAD, plot");
132 }
133
134 /* First page: Model Space (?)
135 */
136 dx = (dwg_model_x_max (dwg) - dwg_model_x_min (dwg));
137 dy = (dwg_model_y_max (dwg) - dwg_model_y_min (dwg));
138 scale_x = dx / (dwg_model_x_max (dwg) - dwg_model_x_min (dwg));
139 scale_y = dy / (dwg_model_y_max (dwg) - dwg_model_y_min (dwg));
140 scale = 25.4 / 72; // pt:mm
141 PS_begin_page (ps, dx / scale, dy / scale);
142 scale *= (scale_x > scale_y ? scale_x : scale_y);
143 PS_scale (ps, (float)scale, (float)scale);
144 PS_translate (ps, (float)-dwg_model_x_min (dwg),
145 (float)-dwg_model_y_min (dwg));
146 if (dwg->opts & DWG_OPTS_LOGLEVEL)
147 {
148 fprintf (stderr, "Limits: %f, %f\n", dx, dy);
149 fprintf (stderr, "Scale: %f (%f, %f)\n", scale, scale_x, scale_y);
150 }
151
152 /* Mark the origin with a crossed circle
153 */
154 #define H 1
155 PS_circle (ps, 0, 0, H);
156 PS_moveto (ps, 0, H);
157 PS_lineto (ps, 0, -H);
158 PS_moveto (ps, -H, 0);
159 PS_lineto (ps, H, 0);
160 PS_stroke (ps);
161
162 /* Iterate all entities
163 */
164 for (i = 0; i < dwg->num_objects; i++)
165 {
166 Dwg_Object *obj = &dwg->object[i];
167 if (obj->supertype == DWG_SUPERTYPE_OBJECT) // no entity
168 continue;
169 // if (obj->tio.entity->entity_mode == 0) // belongs to block
170 // continue;
171 if (obj->fixedtype == DWG_TYPE_LINE)
172 {
173 Dwg_Entity_LINE *line = obj->tio.entity->tio.LINE;
174 BITCODE_3DPOINT start, end;
175
176 transform_OCS (&start, line->start, line->extrusion);
177 transform_OCS (&end, line->end, line->extrusion);
178 PS_moveto (ps, (float)start.x, (float)start.y);
179 PS_lineto (ps, (float)end.x, (float)end.y);
180 PS_stroke (ps);
181 }
182 else if (obj->fixedtype == DWG_TYPE_POLYLINE_2D)
183 {
184 int error;
185 BITCODE_RL j,
186 numpts = dwg_object_polyline_2d_get_numpoints (obj, &error);
187 dwg_point_2d *pts = dwg_object_polyline_2d_get_points (obj, &error);
188 Dwg_Entity_POLYLINE_2D *pline = obj->tio.entity->tio.POLYLINE_2D;
189 if (numpts && !error)
190 {
191 BITCODE_2DPOINT pt0, ptin;
192 ptin.x = pts[0].x;
193 ptin.y = pts[0].y;
194 transform_OCS_2d (&pt0, ptin, pline->extrusion);
195 PS_moveto (ps, (float)pt0.x, (float)pt0.y);
196 for (j = 1; j < numpts; j++)
197 {
198 BITCODE_2DPOINT pt;
199 ptin.x = pts[j].x;
200 ptin.y = pts[j].y;
201 transform_OCS_2d (&pt, ptin, pline->extrusion);
202 PS_lineto (ps, (float)pt.x, (float)pt.y);
203 PS_stroke (ps);
204 }
205 if (pline->flag & 1) // closed
206 {
207 PS_lineto (ps, (float)pt0.x, (float)pt0.y);
208 PS_stroke (ps);
209 }
210 free (pts);
211 }
212 }
213 else if (obj->fixedtype == DWG_TYPE_LWPOLYLINE)
214 {
215 int error;
216 Dwg_Entity_LWPOLYLINE *pline = obj->tio.entity->tio.LWPOLYLINE;
217 BITCODE_RL numpts = dwg_ent_lwpline_get_numpoints (pline, &error);
218 if (numpts && !error)
219 {
220 BITCODE_2DPOINT pt0, ptin;
221 dwg_point_2d *pts = dwg_ent_lwpline_get_points (pline, &error);
222 BITCODE_RL j;
223 ptin.x = pts[0].x;
224 ptin.y = pts[0].y;
225 transform_OCS_2d (&pt0, ptin, pline->extrusion);
226 PS_moveto (ps, (float)pt0.x, (float)pt0.y);
227 for (j = 1; j < numpts; j++)
228 {
229 BITCODE_2DPOINT pt;
230 ptin.x = pts[j].x;
231 ptin.y = pts[j].y;
232 transform_OCS_2d (&pt, ptin, pline->extrusion);
233 PS_lineto (ps, (float)pt.x, (float)pt.y);
234 PS_stroke (ps);
235 }
236 if (pline->flag & 512) // closed
237 {
238 PS_lineto (ps, (float)pt0.x, (float)pt0.y);
239 PS_stroke (ps);
240 }
241
242 free (pts);
243 }
244 }
245 else if (obj->fixedtype == DWG_TYPE_ARC)
246 {
247 Dwg_Entity_ARC *arc = obj->tio.entity->tio.ARC;
248 BITCODE_3DPOINT center;
249 transform_OCS (¢er, arc->center, arc->extrusion);
250 PS_arc (ps, (float)center.x, (float)center.y, (float)arc->radius,
251 (float)arc->start_angle, (float)arc->end_angle);
252 }
253 else if (obj->fixedtype == DWG_TYPE_CIRCLE)
254 {
255 Dwg_Entity_CIRCLE *circle = obj->tio.entity->tio.CIRCLE;
256 BITCODE_3DPOINT center;
257 transform_OCS (¢er, circle->center, circle->extrusion);
258 PS_circle (ps, (float)center.x, (float)center.y,
259 (float)circle->radius);
260 }
261 }
262
263 /* End Model Space */
264 PS_end_page (ps);
265
266 PS_close (ps);
267 PS_delete (ps);
268 PS_shutdown ();
269 }
270
271 int
272 main (int argc, char *argv[])
273 {
274 int error;
275 char *outfile;
276 int i = 1;
277 Dwg_Data dwg;
278
279 int c;
280 #ifdef HAVE_GETOPT_LONG
281 int option_index = 0;
282 static struct option long_options[]
283 = { { "verbose", 1, &opts, 1 }, // optional
284 { "help", 0, 0, 0 },
285 { "version", 0, 0, 0 },
286 { NULL, 0, NULL, 0 } };
287 #endif
288
289 if (argc < 2)
290 return usage ();
291
292 while
293 #ifdef HAVE_GETOPT_LONG
294 ((c = getopt_long (argc, argv, ":v::h", long_options, &option_index))
295 != -1)
296 #else
297 ((c = getopt (argc, argv, ":v::hi")) != -1)
298 #endif
299 {
300 if (c == -1)
301 break;
302 switch (c)
303 {
304 case ':': // missing arg
305 if (optarg && !strcmp (optarg, "v"))
306 {
307 opts = 1;
308 break;
309 }
310 fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
311 optopt);
312 break;
313 #ifdef HAVE_GETOPT_LONG
314 case 0:
315 /* This option sets a flag */
316 if (!strcmp (long_options[option_index].name, "verbose"))
317 {
318 if (opts < 0 || opts > 9)
319 return usage ();
320 # if defined(USE_TRACING) && defined(HAVE_SETENV)
321 {
322 char v[2];
323 *v = opts + '0';
324 *(v + 1) = 0;
325 setenv ("LIBREDWG_TRACE", v, 1);
326 }
327 # endif
328 break;
329 }
330 if (!strcmp (long_options[option_index].name, "version"))
331 return opt_version ();
332 if (!strcmp (long_options[option_index].name, "help"))
333 return help ();
334 break;
335 #else
336 case 'i':
337 return opt_version ();
338 #endif
339 case 'v': // support -v3 and -v
340 i = (optind > 0 && optind < argc) ? optind - 1 : 1;
341 if (!memcmp (argv[i], "-v", 2))
342 {
343 opts = argv[i][2] ? argv[i][2] - '0' : 1;
344 }
345 if (opts < 0 || opts > 9)
346 return usage ();
347 #if defined(USE_TRACING) && defined(HAVE_SETENV)
348 {
349 char v[2];
350 *v = opts + '0';
351 *(v + 1) = 0;
352 setenv ("LIBREDWG_TRACE", v, 1);
353 }
354 #endif
355 break;
356 case 'h':
357 return help ();
358 case '?':
359 fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
360 optopt);
361 break;
362 default:
363 return usage ();
364 }
365 }
366 i = optind;
367 if (i >= argc)
368 return usage ();
369
370 memset (&dwg, 0, sizeof (Dwg_Data));
371 dwg.opts = opts;
372 error = dwg_read_file (argv[i], &dwg);
373 if (error >= DWG_ERR_CRITICAL)
374 {
375 fprintf (stderr, "READ ERROR 0x%x\n", error);
376 dwg_free (&dwg);
377 return 1;
378 }
379
380 outfile = suffix (argv[i], "ps");
381 create_postscript (&dwg, outfile);
382
383 printf ("Success! See the file '%s'\n", outfile);
384 free (outfile);
385 dwg_free (&dwg);
386 return 0;
387 }