1 /*****************************************************************************/
2 /* LibreDWG - free implementation of the DWG file format */
3 /* */
4 /* Copyright (C) 2009-2010,2018-2021 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 * dwg.c: main functions and API
15 * written by Felipe Castro
16 * modified by Felipe Corrêa da Silva Sances
17 * modified by Rodrigo Rodrigues da Silva
18 * modified by Anderson Pierre Cardoso
19 * modified by Reini Urban
20 */
21
22 #include "config.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdbool.h>
27 #include <sys/stat.h>
28 #include <assert.h>
29 // strings.h or string.h
30 #ifdef AX_STRCASECMP_HEADER
31 # include AX_STRCASECMP_HEADER
32 #endif
33 #include <libgen.h> // basename
34
35 #include "bits.h"
36 #include "common.h"
37 #include "decode.h"
38 #include "dwg.h"
39 #include "hash.h"
40 #include "dynapi.h"
41 #ifdef USE_WRITE
42 # include "encode.h"
43 #endif
44 #include "free.h"
45
46 /* The logging level per .o */
47 static unsigned int loglevel;
48 #ifdef USE_TRACING
49 /* This flag means we have checked the environment variable
50 LIBREDWG_TRACE and set `loglevel' appropriately. */
51 static bool env_var_checked_p;
52 #endif /* USE_TRACING */
53 #define DWG_LOGLEVEL loglevel
54 #include "logging.h"
55
56 /*------------------------------------------------------------------------------
57 * Imports bypassing its headers.
58 * We dont want to import in_dxf.h as it is redefining FORMAT_BD.
59 */
60 #ifndef DISABLE_DXF
61 EXPORT int dwg_read_dxf (Bit_Chain *restrict dat, Dwg_Data *restrict dwg);
62 EXPORT int dwg_read_dxfb (Bit_Chain *restrict dat, Dwg_Data *restrict dwg);
63 #endif
64
65 /*------------------------------------------------------------------------------
66 * Internal functions
67 */
68 // used in in_dxf.c, encode.c
69 BITCODE_H
70 dwg_find_tablehandle_silent (Dwg_Data *restrict dwg, const char *restrict name,
71 const char *restrict table);
72 // used in encode.c
73 void set_handle_size (Dwg_Handle *restrict hdl);
74
75 /*------------------------------------------------------------------------------
76 * Public functions
77 */
78
79 EXPORT int
dat_read_file(Bit_Chain * restrict dat,FILE * restrict fp,const char * restrict filename)80 dat_read_file (Bit_Chain *restrict dat, FILE *restrict fp,
81 const char *restrict filename)
82 {
83 size_t size;
84 if (!dat->size && fp)
85 {
86 struct stat attrib;
87 int fd = fileno (fp);
88 if (fd >= 0 && !fstat (fd, &attrib))
89 dat->size = attrib.st_size;
90 }
91 dat->chain = (unsigned char *)calloc (1, dat->size + 1);
92 if (!dat->chain)
93 {
94 loglevel = dat->opts & DWG_OPTS_LOGLEVEL;
95 LOG_ERROR ("Not enough memory.\n")
96 fclose (fp);
97 return DWG_ERR_OUTOFMEM;
98 }
99
100 size = fread (dat->chain, sizeof (char), dat->size, fp);
101 if (size != dat->size)
102 {
103 loglevel = dat->opts & DWG_OPTS_LOGLEVEL;
104 LOG_ERROR ("Could not read file (%lu out of %lu): %s\n",
105 (long unsigned int)size, dat->size, filename)
106 fclose (fp);
107 free (dat->chain);
108 dat->chain = NULL;
109 dat->size = 0;
110 return DWG_ERR_IOERROR;
111 }
112 dat->chain[dat->size] = '\0'; // ensure zero-termination for strstr, strtol, ...
113 return 0;
114 }
115
116 // fast bulk-read when we known the size
117 EXPORT int
dat_read_size(Bit_Chain * restrict dat)118 dat_read_size (Bit_Chain *restrict dat)
119 {
120 if (!dat->chain)
121 dat->chain = (unsigned char *)calloc (1, dat->size + 2);
122 else
123 dat->chain = (unsigned char *)realloc (dat->chain, dat->size + 2);
124 if (!dat->chain)
125 {
126 loglevel = dat->opts & DWG_OPTS_LOGLEVEL;
127 LOG_ERROR ("Not enough memory");
128 fclose (dat->fh);
129 return DWG_ERR_OUTOFMEM;
130 }
131 if (fread (dat->chain, 1, dat->size, dat->fh) != dat->size)
132 {
133 fclose (dat->fh);
134 free (dat->chain);
135 dat->chain = NULL;
136 return DWG_ERR_IOERROR;
137 }
138 dat->chain[dat->size] = '\0'; // ensure zero-termination
139 return 0;
140 }
141
142 EXPORT int
dat_read_stream(Bit_Chain * restrict dat,FILE * restrict fp)143 dat_read_stream (Bit_Chain *restrict dat, FILE *restrict fp)
144 {
145 size_t size = 0;
146 loglevel = dat->opts & DWG_OPTS_LOGLEVEL;
147
148 do
149 {
150 if (dat->chain)
151 dat->chain = (unsigned char *)realloc (dat->chain, dat->size + 4096);
152 else
153 {
154 dat->chain = (unsigned char *)calloc (1, 4096);
155 dat->size = 0;
156 }
157 if (!dat->chain)
158 {
159 LOG_ERROR ("Not enough memory.\n");
160 fclose (fp);
161 return DWG_ERR_OUTOFMEM;
162 }
163 size = fread (&dat->chain[dat->size], sizeof (char), 4096, fp);
164 dat->size += size;
165 }
166 while (size == 4096);
167
168 if (dat->size == 0)
169 {
170 LOG_ERROR ("Could not read from stream (%lu out of %lu)\n",
171 (long unsigned int)size, dat->size);
172 fclose (fp);
173 free (dat->chain);
174 dat->chain = NULL;
175 return DWG_ERR_IOERROR;
176 }
177
178 // clear the slack and realloc
179 size = dat->size & 0xfff;
180 if (size)
181 memset (&dat->chain[dat->size], 0, 0xfff - size);
182 dat->chain = (unsigned char *)realloc (dat->chain, dat->size + 1);
183 // ensure NULL termination, for sscanf, strtol and friends.
184 dat->chain[dat->size] = '\0';
185 return 0;
186 }
187
188 /** dwg_read_file
189 * returns 0 on success.
190 *
191 * everything in dwg is cleared
192 * and then either read from dat, or set to a default.
193 */
194 EXPORT int
dwg_read_file(const char * restrict filename,Dwg_Data * restrict dwg)195 dwg_read_file (const char *restrict filename, Dwg_Data *restrict dwg)
196 {
197 FILE *fp;
198 struct stat attrib;
199 size_t size;
200 Bit_Chain bit_chain = { 0 };
201 int error;
202
203 loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
204 memset (dwg, 0, sizeof (Dwg_Data));
205 dwg->opts = loglevel;
206
207 if (strEQc (filename, "-"))
208 {
209 fp = stdin;
210 }
211 else
212 {
213 if (stat (filename, &attrib))
214 {
215 LOG_ERROR ("File not found: %s\n", filename);
216 return DWG_ERR_IOERROR;
217 }
218 if (!(S_ISREG (attrib.st_mode)
219 #ifndef _WIN32
220 || S_ISLNK (attrib.st_mode)
221 #endif
222 ))
223 {
224 LOG_ERROR ("Illegal input file %s\n", filename);
225 return DWG_ERR_IOERROR;
226 }
227 fp = fopen (filename, "rb");
228 }
229 if (!fp)
230 {
231 LOG_ERROR ("Could not open file: %s\n", filename)
232 return DWG_ERR_IOERROR;
233 }
234
235 /* Load whole file into memory, even if streamed (for now)
236 */
237 memset (&bit_chain, 0, sizeof (Bit_Chain));
238 if (fp == stdin)
239 {
240 error = dat_read_stream (&bit_chain, fp);
241 if (error >= DWG_ERR_CRITICAL)
242 return error;
243 }
244 else
245 {
246 bit_chain.size = attrib.st_size;
247 error = dat_read_file (&bit_chain, fp, filename);
248 if (error >= DWG_ERR_CRITICAL)
249 return error;
250 }
251 fclose (fp);
252
253 /* Decode the dwg structure */
254 error = dwg_decode (&bit_chain, dwg);
255 if (error >= DWG_ERR_CRITICAL)
256 {
257 LOG_ERROR ("Failed to decode file: %s 0x%x\n", filename, error)
258 free (bit_chain.chain);
259 bit_chain.chain = NULL;
260 bit_chain.size = 0;
261 return error;
262 }
263
264 // TODO: does dwg hold any char* pointers to the bit_chain or are they all
265 // copied?
266 free (bit_chain.chain);
267 bit_chain.chain = NULL;
268 bit_chain.size = 0;
269
270 return error;
271 }
272
273 #if !defined(DISABLE_DXF) && defined(USE_WRITE)
274 /** dxf_read_file
275 * returns 0 on success.
276 *
277 * detects binary or ascii file.
278 * everything in dwg is cleared
279 * and then either read from dat, or set to a default.
280 */
281 EXPORT int
dxf_read_file(const char * restrict filename,Dwg_Data * restrict dwg)282 dxf_read_file (const char *restrict filename, Dwg_Data *restrict dwg)
283 {
284 int error;
285 FILE *fp;
286 struct stat attrib;
287 size_t size;
288 Bit_Chain dat = { 0 };
289 Dwg_Version_Type version;
290
291 loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
292
293 if (!filename || stat (filename, &attrib))
294 {
295 LOG_ERROR ("File not found: %s\n", filename ? filename : "(null)")
296 return DWG_ERR_IOERROR;
297 }
298 if (!(S_ISREG (attrib.st_mode)
299 # ifndef _WIN32
300 || S_ISLNK (attrib.st_mode)
301 # endif
302 ))
303 {
304 LOG_ERROR ("Error: %s\n", filename)
305 return DWG_ERR_IOERROR;
306 }
307 fp = fopen (filename, "rb");
308 if (!fp)
309 {
310 LOG_ERROR ("Could not open file: %s\n", filename)
311 return DWG_ERR_IOERROR;
312 }
313
314 /* Load whole file into memory
315 */
316 version = dwg->header.version;
317 memset (dwg, 0, sizeof (Dwg_Data));
318 dwg->opts = loglevel | DWG_OPTS_INDXF;
319 dwg->header.version = version;
320
321 memset (&dat, 0, sizeof (Bit_Chain));
322 dat.size = attrib.st_size;
323 dat.chain = (unsigned char *)calloc (1, dat.size + 2);
324 if (!dat.chain)
325 {
326 LOG_ERROR ("Not enough memory.\n");
327 fclose (fp);
328 return DWG_ERR_OUTOFMEM;
329 }
330 dat.byte = 0;
331 dat.bit = 0;
332 dat.from_version = dwg->header.from_version;
333 dat.version = dwg->header.version;
334 dat.opts = dwg->opts;
335
336 size = fread (dat.chain, sizeof (char), dat.size, fp);
337 fclose (fp);
338 if (size != dat.size)
339 {
340 LOG_ERROR ("Could not read the entire file (%lu out of %lu): %s\n",
341 (long unsigned int)size, dat.size, filename)
342 free (dat.chain);
343 dat.chain = NULL;
344 dat.size = 0;
345 return DWG_ERR_IOERROR;
346 }
347 if (size < 256)
348 {
349 LOG_ERROR ("File %s too small, %lu byte.\n", filename,
350 (long unsigned int)size)
351 return DWG_ERR_IOERROR;
352 }
353 // properly end the buffer for strtol()/... readers
354 if (dat.chain[size-1] != '\n')
355 {
356 dat.chain[size] = '\n';
357 dat.size++;
358 }
359 dat.chain[size] = '\0';
360
361 /* Fail on DWG */
362 if (!memcmp (dat.chain, "AC10", 4) ||
363 !memcmp (dat.chain, "AC1.", 4) ||
364 !memcmp (dat.chain, "AC2.10", 4) ||
365 !memcmp (dat.chain, "MC0.0", 4))
366 {
367 LOG_ERROR ("This is a DWG, not a DXF file: %s\n", filename)
368 free (dat.chain);
369 dat.chain = NULL;
370 dat.size = 0;
371 return DWG_ERR_INVALIDDWG;
372 }
373 /* See if binary or ascii */
374 if (!memcmp (dat.chain, "AutoCAD Binary DXF",
375 sizeof ("AutoCAD Binary DXF") - 1))
376 {
377 dat.byte = 22;
378 error = dwg_read_dxfb (&dat, dwg);
379 }
380 else
381 error = dwg_read_dxf (&dat, dwg);
382
383 dwg->opts |= (DWG_OPTS_INDXF | loglevel);
384 if (error >= DWG_ERR_CRITICAL)
385 {
386 LOG_ERROR ("Failed to decode DXF file: %s\n", filename)
387 free (dat.chain);
388 dat.chain = NULL;
389 dat.size = 0;
390 return error;
391 }
392
393 // TODO: does dwg hold any char* pointers to the dat or are they all copied?
394 free (dat.chain);
395 dat.chain = NULL;
396 dat.size = 0;
397
398 return 0;
399 }
400 #endif /* DISABLE_DXF */
401
402 #ifdef USE_WRITE
403 /** Encode the DWG struct into dat (in memory).
404 * Needs 2x DWG heap, dwg + dat.
405 */
406 EXPORT int
dwg_write_file(const char * restrict filename,const Dwg_Data * restrict dwg)407 dwg_write_file (const char *restrict filename, const Dwg_Data *restrict dwg)
408 {
409 FILE *fh;
410 struct stat attrib;
411 Bit_Chain dat = { 0 };
412 int error;
413
414 loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
415 assert (filename);
416 assert (dwg);
417 dat.opts = dwg->opts;
418 dat.version = (Dwg_Version_Type)dwg->header.version;
419 dat.from_version = (Dwg_Version_Type)dwg->header.from_version;
420
421 // json HACK. no wide chars from JSON, because we just encode to R_2000
422 if (dwg->opts & (DWG_OPTS_INJSON | DWG_OPTS_INDXF))
423 dat.from_version = dat.version;
424
425 if (dwg->header.version <= R_2000 && dwg->header.from_version > R_2000)
426 dwg_fixup_BLOCKS_entities ((Dwg_Data *)dwg);
427
428 dat.size = 0;
429 error = dwg_encode ((Dwg_Data *)dwg, &dat);
430 if (error >= DWG_ERR_CRITICAL)
431 {
432 LOG_ERROR ("Failed to encode Dwg_Data\n");
433 /* In development we want to look at the corpses */
434 #ifdef IS_RELEASE
435 if (dat.size > 0)
436 {
437 free (dat.chain);
438 dat.chain = NULL;
439 dat.size = 0;
440 }
441 return error;
442 #endif
443 }
444
445 // try opening the output file in write mode
446 if (!stat (filename, &attrib)
447 #ifdef _WIN32
448 && strNE (filename, "NUL")
449 #else
450 && strNE (filename, "/dev/null")
451 #endif
452 )
453 {
454 LOG_ERROR ("The file already exists. We won't overwrite it.")
455 return error | DWG_ERR_IOERROR;
456 }
457 fh = fopen (filename, "wb");
458 if (!fh || !dat.chain)
459 {
460 LOG_ERROR ("Failed to create the file: %s\n", filename)
461 return error | DWG_ERR_IOERROR;
462 }
463
464 // Write the data into the file
465 if (fwrite (dat.chain, sizeof (char), dat.size, fh) != dat.size)
466 {
467 LOG_ERROR ("Failed to write data into the file: %s\n", filename)
468 fclose (fh);
469 free (dat.chain);
470 dat.chain = NULL;
471 dat.size = 0;
472 return error | DWG_ERR_IOERROR;
473 }
474 fclose (fh);
475
476 if (dat.size > 0)
477 {
478 free (dat.chain);
479 dat.chain = NULL;
480 dat.size = 0;
481 }
482
483 return error;
484 }
485 #endif /* USE_WRITE */
486
487 /* THUMBNAIL IMAGE DATA (R13C3+).
488 Supports multiple preview pictures.
489 Currently 3 types: BMP, WMF and PNG. but returns only the size of the BMP.
490 */
491 EXPORT unsigned char *
dwg_bmp(const Dwg_Data * restrict dwg,BITCODE_RL * restrict size)492 dwg_bmp (const Dwg_Data *restrict dwg, BITCODE_RL *restrict size)
493 {
494 BITCODE_RC i, num_pictures, type;
495 int found;
496 BITCODE_RL header_size, address, osize;
497 Bit_Chain dat = { 0 };
498
499 loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
500 *size = 0;
501 assert (dwg);
502 // copy the chain data. bit_* needs a full chain with opts and version
503 dat = *(Bit_Chain *)&dwg->thumbnail;
504 if (!dat.size || !dat.chain)
505 {
506 LOG_INFO ("no THUMBNAIL Image Data\n")
507 return NULL;
508 }
509 //dat.byte = 0; sentinel at 16
510 dat.bit = 0;
511 dat.opts = dwg->opts;
512 dat.from_version = dwg->header.from_version;
513 dat.version = dwg->header.version;
514 dat.fh = NULL;
515
516 #ifdef USE_TRACING
517 /* Before starting, set the logging level, but only do so once. */
518 if (!env_var_checked_p)
519 {
520 char *probe = getenv ("LIBREDWG_TRACE");
521 if (probe)
522 loglevel = atoi (probe);
523 env_var_checked_p = true;
524 }
525 #endif /* USE_TRACING */
526
527 osize = bit_read_RL (&dat); /* overall size of all images */
528 LOG_TRACE ("overall size: " FORMAT_RL " [RL]\n", osize);
529 if (osize > (dat.size - 4))
530 {
531 LOG_ERROR ("Preview overflow > %ld", dat.size - 4);
532 return NULL;
533 }
534 num_pictures = bit_read_RC (&dat);
535 LOG_INFO ("num_pictures: %d [RC]\n", (int)num_pictures)
536
537 found = 0;
538 header_size = 0;
539 for (i = 0; i < num_pictures; i++)
540 {
541 if (dat.byte > dat.size)
542 {
543 LOG_ERROR ("Preview overflow");
544 break;
545 }
546 type = bit_read_RC (&dat);
547 LOG_TRACE ("\t[%i] Code: %i [RC]\n", i, type)
548 address = bit_read_RL (&dat);
549 LOG_TRACE ("\t\tHeader data start: 0x%x [RL]\n", address)
550 if (type == 1)
551 {
552 header_size += bit_read_RL (&dat);
553 LOG_TRACE ("\t\tHeader data size: %i [RL]\n", header_size)
554 }
555 else if (type == 2 && found == 0)
556 {
557 *size = bit_read_RL (&dat);
558 found = 1;
559 LOG_INFO ("\t\tBMP size: %i [RL]\n", *size)
560 if (*size > (dat.size - 4))
561 {
562 LOG_ERROR ("BMP thumbnail overflow > %ld", dat.size - 4);
563 return NULL;
564 }
565 }
566 else if (type == 3)
567 {
568 osize = bit_read_RL (&dat);
569 LOG_INFO ("\t\tWMF size: %i [RL]\n", osize)
570 }
571 else if (type == 4) // type 4?
572 {
573 osize = bit_read_RL (&dat);
574 LOG_INFO ("\t\tPNG size: %i [RL]\n", osize)
575 }
576 else
577 {
578 osize = bit_read_RL (&dat);
579 LOG_TRACE ("\t\tSize of unknown type %i: %i [RL]\n", type, osize)
580 }
581 }
582 dat.byte += header_size;
583 if (*size)
584 LOG_TRACE ("BMP offset: %lu\n", dat.byte);
585 if (dat.byte > dat.size)
586 {
587 *size = 0;
588 LOG_ERROR ("Preview overflow");
589 return NULL;
590 }
591
592 if (*size > 0)
593 return (dat.chain + dat.byte);
594 else
595 return NULL;
596 }
597
598 EXPORT double
dwg_model_x_min(const Dwg_Data * dwg)599 dwg_model_x_min (const Dwg_Data *dwg)
600 {
601 assert (dwg);
602 return dwg->header_vars.EXTMIN.x;
603 }
604
605 EXPORT double
dwg_model_x_max(const Dwg_Data * dwg)606 dwg_model_x_max (const Dwg_Data *dwg)
607 {
608 assert (dwg);
609 return dwg->header_vars.EXTMAX.x;
610 }
611
612 EXPORT double
dwg_model_y_min(const Dwg_Data * dwg)613 dwg_model_y_min (const Dwg_Data *dwg)
614 {
615 assert (dwg);
616 return dwg->header_vars.EXTMIN.y;
617 }
618
619 EXPORT double
dwg_model_y_max(const Dwg_Data * dwg)620 dwg_model_y_max (const Dwg_Data *dwg)
621 {
622 assert (dwg);
623 return dwg->header_vars.EXTMAX.y;
624 }
625
626 EXPORT double
dwg_model_z_min(const Dwg_Data * dwg)627 dwg_model_z_min (const Dwg_Data *dwg)
628 {
629 assert (dwg);
630 return dwg->header_vars.EXTMIN.z;
631 }
632
633 EXPORT double
dwg_model_z_max(const Dwg_Data * dwg)634 dwg_model_z_max (const Dwg_Data *dwg)
635 {
636 assert (dwg);
637 return dwg->header_vars.EXTMAX.z;
638 }
639
640 EXPORT double
dwg_page_x_min(const Dwg_Data * dwg)641 dwg_page_x_min (const Dwg_Data *dwg)
642 {
643 assert (dwg);
644 return dwg->header_vars.EXTMIN.x;
645 }
646
647 EXPORT double
dwg_page_x_max(const Dwg_Data * dwg)648 dwg_page_x_max (const Dwg_Data *dwg)
649 {
650 assert (dwg);
651 return dwg->header_vars.PEXTMAX.x;
652 }
653
654 EXPORT double
dwg_page_y_min(const Dwg_Data * dwg)655 dwg_page_y_min (const Dwg_Data *dwg)
656 {
657 assert (dwg);
658 return dwg->header_vars.PEXTMIN.y;
659 }
660
661 EXPORT double
dwg_page_y_max(const Dwg_Data * dwg)662 dwg_page_y_max (const Dwg_Data *dwg)
663 {
664 assert (dwg);
665 return dwg->header_vars.PEXTMAX.y;
666 }
667
668 EXPORT unsigned int
dwg_get_layer_count(const Dwg_Data * dwg)669 dwg_get_layer_count (const Dwg_Data *dwg)
670 {
671 Dwg_Object *ctrl;
672 assert (dwg);
673 ctrl = dwg_get_first_object (dwg, DWG_TYPE_LAYER_CONTROL);
674 if (ctrl && ctrl->tio.object && ctrl->tio.object->tio.LAYER_CONTROL)
675 return ctrl->tio.object->tio.LAYER_CONTROL->num_entries;
676 else
677 return 0;
678 }
679
680 EXPORT Dwg_Object_LAYER **
dwg_get_layers(const Dwg_Data * dwg)681 dwg_get_layers (const Dwg_Data *dwg)
682 {
683 unsigned int i;
684 unsigned int num_layers = dwg_get_layer_count (dwg);
685 Dwg_Object_LAYER_CONTROL *_ctrl;
686 Dwg_Object_LAYER **layers;
687 Dwg_Object *ctrl;
688
689 assert (dwg);
690 ctrl = dwg_get_first_object (dwg, DWG_TYPE_LAYER_CONTROL);
691 if (!ctrl || !ctrl->tio.object || !ctrl->tio.object->tio.LAYER_CONTROL)
692 return NULL;
693 _ctrl = ctrl->tio.object->tio.LAYER_CONTROL;
694 assert (_ctrl);
695 layers = (Dwg_Object_LAYER **)calloc (num_layers, sizeof (Dwg_Object_LAYER *));
696 for (i = 0; i < num_layers; i++)
697 {
698 Dwg_Object *obj = dwg_ref_object (dwg, _ctrl->entries[i]);
699 if (obj && obj->fixedtype == DWG_TYPE_LAYER)
700 layers[i] = obj->tio.object->tio.LAYER;
701 }
702 return layers;
703 }
704
705 BITCODE_BL
dwg_get_object_num_objects(const Dwg_Data * dwg)706 dwg_get_object_num_objects (const Dwg_Data *dwg)
707 {
708 assert (dwg);
709 return dwg->num_objects - dwg->num_entities;
710 }
711
712 BITCODE_BL
dwg_get_num_objects(const Dwg_Data * dwg)713 dwg_get_num_objects (const Dwg_Data *dwg)
714 {
715 assert (dwg);
716 return dwg->num_objects;
717 }
718
719 BITCODE_BL
dwg_get_num_entities(const Dwg_Data * dwg)720 dwg_get_num_entities (const Dwg_Data *dwg)
721 {
722 assert (dwg);
723 return dwg->num_entities;
724 }
725
726 /** Returns a copy of all entities */
727 EXPORT Dwg_Object_Entity **
dwg_get_entities(const Dwg_Data * dwg)728 dwg_get_entities (const Dwg_Data *dwg)
729 {
730 BITCODE_BL i, ent_count = 0;
731 Dwg_Object_Entity **entities;
732
733 assert (dwg);
734 entities = (Dwg_Object_Entity **)calloc (dwg_get_num_entities (dwg),
735 sizeof (Dwg_Object_Entity *));
736 for (i = 0; i < dwg->num_objects; i++)
737 {
738 if (dwg->object[i].supertype == DWG_SUPERTYPE_ENTITY)
739 {
740 entities[ent_count] = dwg->object[i].tio.entity;
741 ent_count++;
742 assert (ent_count < dwg->num_objects);
743 }
744 }
745 return entities;
746 }
747
748 EXPORT Dwg_Object_LAYER *
dwg_get_entity_layer(const Dwg_Object_Entity * ent)749 dwg_get_entity_layer (const Dwg_Object_Entity *ent)
750 {
751 // TODO: empty means default layer 0
752 return ent->layer ? ent->layer->obj->tio.object->tio.LAYER : NULL;
753 }
754
755 EXPORT Dwg_Object *
dwg_next_object(const Dwg_Object * obj)756 dwg_next_object (const Dwg_Object *obj)
757 {
758 Dwg_Data *dwg;
759 if (!obj)
760 return NULL;
761 dwg = obj->parent;
762 if ((obj->index + 1) > (dwg->num_objects - 1))
763 return NULL;
764 return &dwg->object[obj->index + 1];
765 }
766
767 /**
768 * Find an object given its handle
769 */
770 EXPORT Dwg_Object *
dwg_ref_object(const Dwg_Data * restrict dwg,Dwg_Object_Ref * restrict ref)771 dwg_ref_object (const Dwg_Data *restrict dwg, Dwg_Object_Ref *restrict ref)
772 {
773 if (!ref)
774 return NULL;
775 if (ref->obj && !dwg->dirty_refs)
776 return ref->obj;
777 // Without obj we don't get an absolute_ref from relative OFFSETOBJHANDLE
778 // handle types.
779 if ((ref->handleref.code < 6
780 && dwg_resolve_handleref (ref, NULL))
781 || ref->absolute_ref)
782 {
783 Dwg_Object *obj;
784 loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
785 obj = dwg_resolve_handle (dwg, ref->absolute_ref);
786 if (!dwg->dirty_refs && obj)
787 ref->obj = obj;
788 return obj;
789 }
790 else
791 return NULL;
792 }
793
794 /**
795 * Find an object given its handle and relative base object.
796 * OFFSETOBJHANDLE, handleref.code > 6.
797 */
798 EXPORT Dwg_Object *
dwg_ref_object_relative(const Dwg_Data * restrict dwg,Dwg_Object_Ref * restrict ref,const Dwg_Object * restrict obj)799 dwg_ref_object_relative (const Dwg_Data *restrict dwg,
800 Dwg_Object_Ref *restrict ref,
801 const Dwg_Object *restrict obj)
802 {
803 if (ref->obj && !dwg->dirty_refs)
804 return ref->obj;
805 if (dwg_resolve_handleref (ref, obj))
806 {
807 Dwg_Object *o = dwg_resolve_handle (dwg, ref->absolute_ref);
808 if (!dwg->dirty_refs && o)
809 ref->obj = o;
810 return o;
811 }
812 else
813 return NULL;
814 }
815
816 /**
817 * Find a pointer to an object given it's absolute id (handle).
818 * TODO: Check and update each handleref obj cache.
819 * Note that absref 0 is illegal here, I think.
820 */
821 EXPORT Dwg_Object *
dwg_resolve_handle(const Dwg_Data * dwg,const unsigned long absref)822 dwg_resolve_handle (const Dwg_Data *dwg, const unsigned long absref)
823 {
824 uint32_t i;
825 if (!absref) // illegal usage
826 return NULL;
827 i = hash_get (dwg->object_map, (uint32_t)absref);
828 if (i != HASH_NOT_FOUND)
829 LOG_HANDLE ("object_map{%lX} => %u\n", absref, i);
830 if (i == HASH_NOT_FOUND
831 || (BITCODE_BL)i >= dwg->num_objects) // the latter being an invalid
832 // handle (read from DWG)
833 {
834 // ignore warning on invalid handles. These are warned earlier already
835 if (absref && absref < dwg->num_objects)
836 {
837 LOG_WARN ("Object handle not found, %lu/%lX in " FORMAT_BL
838 " objects",
839 absref, absref, dwg->num_objects);
840 }
841 return NULL;
842 }
843 return &dwg->object[i]; // allow value 0
844 }
845
846 /**
847 * Silent variant of dwg_resolve_handle
848 */
849 EXPORT Dwg_Object *
dwg_resolve_handle_silent(const Dwg_Data * dwg,const BITCODE_BL absref)850 dwg_resolve_handle_silent (const Dwg_Data *dwg, const BITCODE_BL absref)
851 {
852 uint32_t i;
853 if (!absref) // illegal usage
854 return NULL;
855 i = hash_get (dwg->object_map, (uint32_t)absref);
856 if (i == HASH_NOT_FOUND
857 || (BITCODE_BL)i >= dwg->num_objects) // the latter being an invalid
858 // handle (read from DWG)
859 return NULL;
860 return &dwg->object[i]; // allow value 0
861 }
862
863 EXPORT Dwg_Object *
dwg_ref_object_silent(const Dwg_Data * restrict dwg,Dwg_Object_Ref * restrict ref)864 dwg_ref_object_silent (const Dwg_Data *restrict dwg,
865 Dwg_Object_Ref *restrict ref)
866 {
867 if (!ref)
868 return NULL;
869 if (ref->obj && !dwg->dirty_refs)
870 return ref->obj;
871 if ((ref->handleref.code < 6
872 && dwg_resolve_handleref ((Dwg_Object_Ref *)ref, NULL))
873 || ref->absolute_ref)
874 {
875 Dwg_Object *obj = dwg_resolve_handle_silent (dwg, ref->absolute_ref);
876 if (!dwg->dirty_refs && obj)
877 ref->obj = obj;
878 return obj;
879 }
880 else
881 return NULL;
882 }
883
884 /* set ref->absolute_ref from obj, for a subsequent dwg_resolve_handle() */
885 EXPORT int
dwg_resolve_handleref(Dwg_Object_Ref * restrict ref,const Dwg_Object * restrict obj)886 dwg_resolve_handleref (Dwg_Object_Ref *restrict ref,
887 const Dwg_Object *restrict obj)
888 {
889 /*
890 * With TYPEDOBJHANDLE 2-5 the code indicates the type of ownership:
891 * 2 Soft owner
892 * 3 Hard owner
893 * 4 Soft pointer
894 * 5 Hard pointer
895 * With OFFSETOBJHANDLE >5 the code 4 handle is stored as an offset from some
896 * other handle.
897 */
898 switch (ref->handleref.code)
899 {
900 // implicit code: 4
901 case 6:
902 ref->absolute_ref = (obj->handle.value + 1);
903 break;
904 case 8:
905 ref->absolute_ref = (obj->handle.value - 1);
906 break;
907 case 10:
908 ref->absolute_ref = (obj->handle.value + ref->handleref.value);
909 break;
910 case 12:
911 ref->absolute_ref = (obj->handle.value - ref->handleref.value);
912 break;
913 case 2:
914 case 3:
915 case 4:
916 case 5:
917 ref->absolute_ref = ref->handleref.value;
918 break;
919 case 0: // ignore?
920 ref->absolute_ref = ref->handleref.value;
921 break;
922 default:
923 ref->absolute_ref = ref->handleref.value;
924 LOG_WARN ("Invalid handle pointer code %d", ref->handleref.code);
925 return 0;
926 }
927 return 1;
928 }
929
930 /** Returns the block_control for the DWG,
931 containing the list of all blocks headers.
932 */
933 EXPORT Dwg_Object_BLOCK_CONTROL *
dwg_block_control(Dwg_Data * dwg)934 dwg_block_control (Dwg_Data *dwg)
935 {
936 if (!dwg->block_control.parent)
937 {
938 Dwg_Object *obj;
939 Dwg_Object_Ref *ctrl = dwg->header_vars.BLOCK_CONTROL_OBJECT;
940 if (!ctrl || !(obj = dwg_ref_object (dwg, ctrl)) || obj->type != DWG_TYPE_BLOCK_CONTROL)
941 {
942 LOG_ERROR ("dwg.block_control and HEADER.BLOCK_CONTROL_OBJECT missing");
943 return NULL;
944 }
945 else
946 {
947 dwg->block_control = *obj->tio.object->tio.BLOCK_CONTROL;
948 }
949 }
950 return &(dwg->block_control);
951 }
952
953 /** Returns the model space block object for the DWG.
954 On r2010 and r2013 it could be different to the canonical
955 dwg->block_control.model_space.
956 */
957 EXPORT Dwg_Object_Ref *
dwg_model_space_ref(Dwg_Data * dwg)958 dwg_model_space_ref (Dwg_Data *dwg)
959 {
960 Dwg_Object *obj;
961 Dwg_Object_BLOCK_CONTROL *block_control;
962 if (dwg->header_vars.BLOCK_RECORD_MSPACE
963 && dwg->header_vars.BLOCK_RECORD_MSPACE->obj)
964 return dwg->header_vars.BLOCK_RECORD_MSPACE;
965 if (dwg->block_control.model_space && dwg->block_control.model_space->obj)
966 {
967 dwg->header_vars.BLOCK_RECORD_MSPACE = dwg->block_control.model_space;
968 return dwg->block_control.model_space;
969 }
970 block_control = dwg_block_control (dwg);
971 if (block_control && block_control->model_space && block_control->model_space->obj)
972 {
973 dwg->block_control.model_space = block_control->model_space;
974 dwg->header_vars.BLOCK_RECORD_MSPACE = block_control->model_space;
975 return block_control->model_space;
976 }
977 obj = dwg_get_first_object (dwg, DWG_TYPE_BLOCK_CONTROL);
978 if (!obj)
979 return NULL;
980 block_control = obj->tio.object->tio.BLOCK_CONTROL;
981 if (block_control && block_control->model_space && block_control->model_space->obj)
982 {
983 dwg->block_control.model_space = block_control->model_space;
984 dwg->header_vars.BLOCK_RECORD_MSPACE = block_control->model_space;
985 return block_control->model_space;
986 }
987 return NULL;
988 }
989
990 /** Returns the paper space block object for the DWG.
991 */
992 EXPORT Dwg_Object_Ref *
dwg_paper_space_ref(Dwg_Data * dwg)993 dwg_paper_space_ref (Dwg_Data *dwg)
994 {
995 if (dwg->header_vars.BLOCK_RECORD_PSPACE
996 && dwg->header_vars.BLOCK_RECORD_PSPACE->obj)
997 return dwg->header_vars.BLOCK_RECORD_PSPACE;
998 return dwg->block_control.paper_space && dwg->block_control.paper_space->obj
999 ? dwg->block_control.paper_space
1000 : NULL;
1001 }
1002
1003 /** Returns the model space block object for the DWG.
1004 */
1005 EXPORT Dwg_Object *
dwg_model_space_object(Dwg_Data * dwg)1006 dwg_model_space_object (Dwg_Data *dwg)
1007 {
1008 Dwg_Object_Ref *msref = dwg_model_space_ref (dwg);
1009 Dwg_Object_BLOCK_CONTROL *ctrl;
1010
1011 if (msref && msref->obj && msref->obj->type == DWG_TYPE_BLOCK_HEADER)
1012 return msref->obj;
1013 ctrl = dwg_block_control (dwg);
1014 if (ctrl && ctrl->model_space && ctrl->model_space->obj)
1015 return ctrl->model_space->obj;
1016 if (dwg->header_vars.BLOCK_RECORD_MSPACE
1017 && dwg->header_vars.BLOCK_RECORD_MSPACE->obj)
1018 return dwg->header_vars.BLOCK_RECORD_MSPACE->obj;
1019 if (!dwg->object_map) // for dwg_add_document()
1020 dwg->object_map = hash_new (100);
1021 return dwg_resolve_handle (dwg, dwg->header.version >= R_2000 ? 0x1F : 0x17);
1022 }
1023
1024 /** Returns the paper space block object for the DWG.
1025 */
1026 EXPORT Dwg_Object *
dwg_paper_space_object(Dwg_Data * dwg)1027 dwg_paper_space_object (Dwg_Data *dwg)
1028 {
1029 Dwg_Object_Ref *psref = dwg_paper_space_ref (dwg);
1030 Dwg_Object_BLOCK_CONTROL *ctrl;
1031
1032 if (psref && psref->obj && psref->obj->type == DWG_TYPE_BLOCK_HEADER)
1033 return psref->obj;
1034 ctrl = dwg_block_control (dwg);
1035 if (ctrl && ctrl->paper_space && ctrl->paper_space->obj)
1036 return ctrl->paper_space->obj;
1037 if (dwg->header_vars.BLOCK_RECORD_PSPACE
1038 && dwg->header_vars.BLOCK_RECORD_PSPACE->obj)
1039 return dwg->header_vars.BLOCK_RECORD_PSPACE->obj;
1040 else
1041 return NULL;
1042 }
1043
1044 /** Returns the first entity owned by the block hdr, or NULL.
1045 */
1046 EXPORT Dwg_Object *
get_first_owned_entity(const Dwg_Object * hdr)1047 get_first_owned_entity (const Dwg_Object *hdr)
1048 {
1049 unsigned int version = hdr->parent->header.version;
1050 Dwg_Object_BLOCK_HEADER *_hdr = hdr->tio.object->tio.BLOCK_HEADER;
1051 if (hdr->type != DWG_TYPE_BLOCK_HEADER)
1052 {
1053 LOG_ERROR ("Invalid BLOCK_HEADER type %d", hdr->type);
1054 return NULL;
1055 }
1056
1057 if (R_13 <= version && version <= R_2000)
1058 {
1059 /* With r2000 we rather follow the next_entity chain */
1060 return _hdr->first_entity ? _hdr->first_entity->obj : NULL;
1061 }
1062 else if (version >= R_2004)
1063 {
1064 _hdr->__iterator = 0;
1065 if (_hdr->entities && _hdr->num_owned && _hdr->entities[0])
1066 {
1067 if (!_hdr->entities[0]->obj)
1068 dwg_resolve_objectrefs_silent (hdr->parent);
1069 return _hdr->entities[0]->obj;
1070 }
1071 else
1072 return NULL;
1073 }
1074
1075 // TODO: preR13 block table
1076 LOG_ERROR ("Unsupported version: %d\n", version);
1077 return NULL;
1078 }
1079
1080 /** Returns the next entity owned by mspace or pspace or NULL.
1081 */
1082 EXPORT Dwg_Object *
dwg_next_entity(const Dwg_Object * restrict obj)1083 dwg_next_entity (const Dwg_Object *restrict obj)
1084 {
1085 Dwg_Object_Ref *restrict next;
1086
1087 if (obj == NULL ||
1088 obj->parent == NULL ||
1089 obj->supertype != DWG_SUPERTYPE_ENTITY)
1090 return NULL;
1091 if (obj->parent->header.version < R_2004)
1092 {
1093 if (!obj->tio.entity) // decoding error
1094 goto next_obj;
1095 next = obj->tio.entity->next_entity;
1096 if (next && next->absolute_ref)
1097 return dwg_ref_object_silent (obj->parent, next);
1098 else
1099 goto next_obj;
1100 }
1101 else
1102 {
1103 next_obj:
1104 obj = dwg_next_object (obj);
1105 while (obj && obj->supertype != DWG_SUPERTYPE_ENTITY)
1106 {
1107 obj = dwg_next_object (obj);
1108 }
1109 return (Dwg_Object*)obj;
1110 }
1111 }
1112
1113 /** Returns the next entity owned by the block hdr, or NULL.
1114 * Not subentities: ATTRIB, VERTEX.
1115 */
1116 EXPORT Dwg_Object *
get_next_owned_entity(const Dwg_Object * restrict hdr,const Dwg_Object * restrict current)1117 get_next_owned_entity (const Dwg_Object *restrict hdr,
1118 const Dwg_Object *restrict current)
1119 {
1120 Dwg_Data *dwg = hdr->parent;
1121 Dwg_Version_Type version = dwg->header.version;
1122 Dwg_Object_BLOCK_HEADER *_hdr = hdr->tio.object->tio.BLOCK_HEADER;
1123 if (hdr->type != DWG_TYPE_BLOCK_HEADER)
1124 {
1125 LOG_ERROR ("Invalid BLOCK_HEADER type %d", hdr->type);
1126 return NULL;
1127 }
1128
1129 if (R_13 <= version && version <= R_2000)
1130 {
1131 Dwg_Object *obj;
1132 if (_hdr->last_entity == NULL
1133 || current->handle.value >= _hdr->last_entity->absolute_ref)
1134 return NULL;
1135 obj = dwg_next_entity (current);
1136 while (obj && (obj->type == DWG_TYPE_ATTDEF
1137 || obj->type == DWG_TYPE_ATTRIB
1138 || obj->type == DWG_TYPE_VERTEX_2D
1139 || obj->type == DWG_TYPE_VERTEX_3D
1140 || obj->type == DWG_TYPE_VERTEX_MESH
1141 || obj->type == DWG_TYPE_VERTEX_PFACE
1142 || obj->type == DWG_TYPE_VERTEX_PFACE_FACE))
1143 {
1144 obj = dwg_next_entity (obj);
1145 // this may happen with r2000 attribs
1146 if (obj
1147 && obj->tio.entity != NULL
1148 && obj->tio.entity->ownerhandle != NULL
1149 && obj->tio.entity->ownerhandle->absolute_ref
1150 != hdr->handle.value)
1151 obj = NULL;
1152 if (obj == _hdr->last_entity->obj) // early exit
1153 return obj;
1154 }
1155 return obj;
1156 }
1157 else if (version >= R_2004)
1158 {
1159 Dwg_Object_Ref *ref;
1160 _hdr->__iterator++;
1161 if (_hdr->__iterator == _hdr->num_owned)
1162 return NULL;
1163 ref = _hdr->entities ? _hdr->entities[_hdr->__iterator] : NULL;
1164 return ref ? dwg_ref_object (dwg, ref) : NULL;
1165 }
1166
1167 LOG_ERROR ("Unsupported version: %d\n", version);
1168 return NULL;
1169 }
1170
1171 /** Returns the first subentity owned by the insert or polyline.
1172 */
1173 EXPORT Dwg_Object *
get_first_owned_subentity(const Dwg_Object * owner)1174 get_first_owned_subentity (const Dwg_Object *owner)
1175 {
1176 Dwg_Data *dwg = owner->parent;
1177 Dwg_Version_Type version = dwg->header.version;
1178 const unsigned int type = owner->type;
1179 if (type == DWG_TYPE_INSERT)
1180 {
1181 Dwg_Entity_INSERT *_obj = owner->tio.entity->tio.INSERT;
1182 if (version <= R_2000)
1183 return _obj->first_attrib ? _obj->first_attrib->obj : NULL;
1184 else
1185 return _obj->attribs && _obj->attribs[0]
1186 ? dwg_ref_object (dwg, _obj->attribs[0])
1187 : NULL;
1188 }
1189 else if (type == DWG_TYPE_MINSERT)
1190 {
1191 Dwg_Entity_MINSERT *_obj = owner->tio.entity->tio.MINSERT;
1192 if (version <= R_2000)
1193 return _obj->first_attrib ? dwg_ref_object (dwg, _obj->first_attrib) : NULL;
1194 else
1195 return _obj->attribs && _obj->attribs[0]
1196 ? dwg_ref_object (dwg, _obj->attribs[0])
1197 : NULL;
1198 }
1199 else if (type == DWG_TYPE_POLYLINE_2D || type == DWG_TYPE_POLYLINE_3D
1200 || type == DWG_TYPE_POLYLINE_PFACE
1201 || type == DWG_TYPE_POLYLINE_MESH)
1202 {
1203 // guaranteed structure
1204 Dwg_Entity_POLYLINE_2D *_obj = owner->tio.entity->tio.POLYLINE_2D;
1205 if (version <= R_2000)
1206 return _obj->first_vertex ? dwg_ref_object (dwg, _obj->first_vertex) : NULL;
1207 else
1208 return _obj->vertex && _obj->vertex[0] ? dwg_ref_object (dwg, _obj->vertex[0]) : NULL;
1209 }
1210 else
1211 {
1212 LOG_ERROR ("Wrong type %d, has no subentity", type);
1213 }
1214 return NULL;
1215 }
1216
1217 /** Returns the next subentity owned by the object.
1218 */
1219 EXPORT Dwg_Object *
get_next_owned_subentity(const Dwg_Object * restrict owner,const Dwg_Object * restrict current)1220 get_next_owned_subentity (const Dwg_Object *restrict owner,
1221 const Dwg_Object *restrict current)
1222 {
1223 Dwg_Data *dwg = owner->parent;
1224 Dwg_Version_Type version = dwg->header.version;
1225 const Dwg_Object_Type type = (const Dwg_Object_Type)owner->type;
1226 Dwg_Object_Entity *ent = owner->tio.entity;
1227 Dwg_Object *obj = dwg_next_object (current);
1228
1229 if (type == DWG_TYPE_INSERT)
1230 {
1231 Dwg_Entity_INSERT *_obj = owner->tio.entity->tio.INSERT;
1232 if (version <= R_2000)
1233 return (_obj->last_attrib && current != _obj->last_attrib->obj
1234 && obj->type == DWG_TYPE_ATTRIB)
1235 ? obj
1236 : NULL;
1237 else
1238 {
1239 ent->__iterator++;
1240 if (ent->__iterator == _obj->num_owned)
1241 {
1242 ent->__iterator = 0;
1243 return NULL;
1244 }
1245 else
1246 return _obj->attribs
1247 ? dwg_ref_object (dwg, _obj->attribs[ent->__iterator])
1248 : NULL;
1249 }
1250 }
1251 else if (type == DWG_TYPE_MINSERT)
1252 {
1253 Dwg_Entity_MINSERT *_obj = owner->tio.entity->tio.MINSERT;
1254 if (version <= R_2000)
1255 return (_obj->last_attrib && current != _obj->last_attrib->obj
1256 && obj->type == DWG_TYPE_ATTRIB)
1257 ? obj
1258 : NULL;
1259 else
1260 {
1261 ent->__iterator++;
1262 if (ent->__iterator == _obj->num_owned)
1263 {
1264 ent->__iterator = 0;
1265 return NULL;
1266 }
1267 else
1268 return _obj->attribs
1269 ? dwg_ref_object (dwg, _obj->attribs[ent->__iterator])
1270 : NULL;
1271 }
1272 }
1273 else if (type == DWG_TYPE_POLYLINE_2D || type == DWG_TYPE_POLYLINE_3D
1274 || type == DWG_TYPE_POLYLINE_PFACE
1275 || type == DWG_TYPE_POLYLINE_MESH)
1276 {
1277 // guaranteed structure
1278 Dwg_Entity_POLYLINE_2D *_obj = owner->tio.entity->tio.POLYLINE_2D;
1279 if (version <= R_2000)
1280 return (_obj->last_vertex && current != _obj->last_vertex->obj) ? obj
1281 : NULL;
1282 else
1283 {
1284 ent->__iterator++;
1285 if (ent->__iterator == _obj->num_owned)
1286 {
1287 ent->__iterator = 0;
1288 return NULL;
1289 }
1290 else
1291 return _obj->vertex ? dwg_ref_object (dwg, _obj->vertex[ent->__iterator]) : NULL;
1292 }
1293 }
1294 else
1295 {
1296 LOG_ERROR ("Wrong type %d, has no subentity", type);
1297 }
1298 return NULL;
1299 }
1300
1301 /** Returns the BLOCK entity owned by the block hdr.
1302 * Only NULL on illegal hdr argument or dwg version.
1303 */
1304 EXPORT Dwg_Object *
get_first_owned_block(const Dwg_Object * hdr)1305 get_first_owned_block (const Dwg_Object *hdr)
1306 {
1307 Dwg_Data *dwg = hdr->parent;
1308 Dwg_Version_Type version = dwg->header.version;
1309 const Dwg_Object_BLOCK_HEADER *restrict _hdr
1310 = hdr->tio.object->tio.BLOCK_HEADER;
1311 if (hdr->type != DWG_TYPE_BLOCK_HEADER)
1312 {
1313 LOG_ERROR ("Invalid BLOCK_HEADER type %d", hdr->type);
1314 return NULL;
1315 }
1316
1317 if (version >= R_13)
1318 {
1319 if (_hdr->block_entity)
1320 {
1321 if (!_hdr->block_entity->obj)
1322 dwg_resolve_objectrefs_silent (dwg);
1323 return dwg_ref_object (dwg, _hdr->block_entity);
1324 }
1325 else
1326 {
1327 Dwg_Object *obj = (Dwg_Object *)hdr;
1328 while (obj && obj->type != DWG_TYPE_BLOCK)
1329 obj = dwg_next_object (obj);
1330 return obj;
1331 }
1332 }
1333
1334 // TODO: preR13 block table
1335 LOG_ERROR ("Unsupported version: %s\n", dwg_version_type (version));
1336 return NULL;
1337 }
1338
1339 /** Returns the next block object after current owned by the block hdr, or
1340 * NULL.
1341 */
1342 EXPORT Dwg_Object *
get_next_owned_block(const Dwg_Object * restrict hdr,const Dwg_Object * restrict current)1343 get_next_owned_block (const Dwg_Object *restrict hdr,
1344 const Dwg_Object *restrict current)
1345 {
1346 Dwg_Data *dwg = hdr->parent;
1347 Dwg_Version_Type version = dwg->header.version;
1348 const Dwg_Object_BLOCK_HEADER *restrict _hdr
1349 = hdr->tio.object->tio.BLOCK_HEADER;
1350 if (hdr->type != DWG_TYPE_BLOCK_HEADER)
1351 {
1352 LOG_ERROR ("Invalid BLOCK_HEADER type %d", hdr->type);
1353 return NULL;
1354 }
1355
1356 if (version >= R_13)
1357 {
1358 if (!_hdr->endblk_entity || current->handle.value >= _hdr->endblk_entity->absolute_ref)
1359 return NULL;
1360 return dwg_next_object (current);
1361 }
1362
1363 LOG_ERROR ("Unsupported version: %s\n", dwg_version_type (version));
1364 return NULL;
1365 }
1366
1367 /** Returns the next block object until last_entity
1368 * after current owned by the block hdr, or NULL.
1369 */
1370 EXPORT Dwg_Object *
get_next_owned_block_entity(const Dwg_Object * restrict hdr,const Dwg_Object * restrict current)1371 get_next_owned_block_entity (const Dwg_Object *restrict hdr,
1372 const Dwg_Object *restrict current)
1373 {
1374 Dwg_Data *dwg;
1375 Dwg_Version_Type version;
1376 Dwg_Object_BLOCK_HEADER *restrict _hdr;
1377
1378 if (hdr->type != DWG_TYPE_BLOCK_HEADER)
1379 {
1380 LOG_ERROR ("Invalid BLOCK_HEADER type %d", hdr->type);
1381 return NULL;
1382 }
1383
1384 dwg = hdr->parent;
1385 version = dwg->header.version;
1386 _hdr = hdr->tio.object->tio.BLOCK_HEADER;
1387
1388 if (R_13 <= version && version <= R_2000)
1389 {
1390 /* With r2000 we rather follow the next_entity chain. It may jump around the linked list. */
1391 if (!_hdr->last_entity
1392 || current->handle.value == _hdr->last_entity->absolute_ref)
1393 return NULL;
1394 return dwg_next_entity (current);
1395 }
1396 if (version > R_2000)
1397 {
1398 Dwg_Object_Ref *ref;
1399 _hdr->__iterator++;
1400 if (_hdr->__iterator == _hdr->num_owned)
1401 return NULL;
1402 ref = _hdr->entities ? _hdr->entities[_hdr->__iterator] : NULL;
1403 return ref ? dwg_ref_object (dwg, ref) : NULL;
1404 }
1405
1406 LOG_ERROR ("Unsupported version: %s\n", dwg_version_type (version));
1407 return NULL;
1408 }
1409
1410 /** Returns the last ENDBLK entity owned by the block hdr.
1411 * Only NULL on illegal hdr argument or dwg version.
1412 */
1413 EXPORT Dwg_Object *
get_last_owned_block(const Dwg_Object * restrict hdr)1414 get_last_owned_block (const Dwg_Object *restrict hdr)
1415 {
1416 Dwg_Data *dwg = hdr->parent;
1417 Dwg_Object_BLOCK_HEADER *restrict _hdr = hdr->tio.object->tio.BLOCK_HEADER;
1418 unsigned int version = dwg->header.version;
1419 if (hdr->type != DWG_TYPE_BLOCK_HEADER)
1420 {
1421 LOG_ERROR ("Invalid BLOCK_HEADER type %d", hdr->type);
1422 return NULL;
1423 }
1424
1425 if (version >= R_13)
1426 {
1427 if (_hdr->endblk_entity && _hdr->endblk_entity->obj)
1428 return _hdr->endblk_entity->obj;
1429 else
1430 {
1431 Dwg_Object *obj = (Dwg_Object *)hdr;
1432 while (obj && obj->type != DWG_TYPE_ENDBLK)
1433 obj = dwg_next_object (obj);
1434 if (obj && obj->type == DWG_TYPE_ENDBLK)
1435 {
1436 if (!_hdr->endblk_entity)
1437 {
1438 _hdr->endblk_entity = (BITCODE_H)calloc (1, sizeof (Dwg_Object_Ref));
1439 if (_hdr->endblk_entity)
1440 {
1441 _hdr->endblk_entity->obj = obj;
1442 _hdr->endblk_entity->handleref.value
1443 = _hdr->endblk_entity->absolute_ref
1444 = obj->handle.value;
1445 }
1446 }
1447 else if (!_hdr->endblk_entity->obj)
1448 _hdr->endblk_entity->obj = obj;
1449 }
1450 return obj;
1451 }
1452 }
1453
1454 LOG_ERROR ("Unsupported version: %d\n", version);
1455 return NULL;
1456 }
1457
1458 EXPORT Dwg_Object *
dwg_get_first_object(const Dwg_Data * dwg,const Dwg_Object_Type type)1459 dwg_get_first_object (const Dwg_Data *dwg, const Dwg_Object_Type type)
1460 {
1461 for (unsigned i = 0; i < dwg->num_objects; i++)
1462 {
1463 Dwg_Object *const obj = &dwg->object[i];
1464 if (obj->fixedtype == type &&
1465 obj->tio.object &&
1466 obj->tio.object->tio.APPID)
1467 return obj;
1468 }
1469 return NULL;
1470 }
1471
1472 EXPORT int
dwg_class_is_entity(const Dwg_Class * restrict klass)1473 dwg_class_is_entity (const Dwg_Class *restrict klass)
1474 {
1475 return (klass != NULL && (int)klass->item_class_id == 0x1f2) ? 1 : 0;
1476 }
1477
1478 EXPORT int
dwg_obj_is_control(const Dwg_Object * obj)1479 dwg_obj_is_control (const Dwg_Object *obj)
1480 {
1481 const unsigned int type = obj->type;
1482 return (obj->supertype == DWG_SUPERTYPE_OBJECT)
1483 && (type == DWG_TYPE_BLOCK_CONTROL || type == DWG_TYPE_LAYER_CONTROL
1484 || type == DWG_TYPE_STYLE_CONTROL
1485 || type == DWG_TYPE_LTYPE_CONTROL || type == DWG_TYPE_VIEW_CONTROL
1486 || type == DWG_TYPE_UCS_CONTROL || type == DWG_TYPE_VPORT_CONTROL
1487 || type == DWG_TYPE_APPID_CONTROL
1488 || type == DWG_TYPE_DIMSTYLE_CONTROL
1489 || type == DWG_TYPE_VX_CONTROL);
1490 }
1491
1492 EXPORT int
dwg_obj_is_table(const Dwg_Object * obj)1493 dwg_obj_is_table (const Dwg_Object *obj)
1494 {
1495 const unsigned int type = obj->type;
1496 return (obj->supertype == DWG_SUPERTYPE_OBJECT)
1497 && (type == DWG_TYPE_BLOCK_HEADER || type == DWG_TYPE_LAYER
1498 || type == DWG_TYPE_STYLE || type == DWG_TYPE_LTYPE
1499 || type == DWG_TYPE_VIEW || type == DWG_TYPE_UCS
1500 || type == DWG_TYPE_VPORT || type == DWG_TYPE_APPID
1501 || type == DWG_TYPE_DIMSTYLE
1502 || type == DWG_TYPE_VX_TABLE_RECORD);
1503 }
1504
1505 EXPORT int
dwg_obj_is_subentity(const Dwg_Object * obj)1506 dwg_obj_is_subentity (const Dwg_Object *obj)
1507 {
1508 const unsigned int type = obj->type;
1509 return (obj->supertype == DWG_SUPERTYPE_ENTITY)
1510 && (type == DWG_TYPE_ATTRIB || type == DWG_TYPE_VERTEX_2D
1511 || type == DWG_TYPE_VERTEX_3D || type == DWG_TYPE_VERTEX_MESH
1512 || type == DWG_TYPE_VERTEX_PFACE
1513 || type == DWG_TYPE_VERTEX_PFACE_FACE);
1514 }
1515
1516 EXPORT int
dwg_obj_has_subentity(const Dwg_Object * obj)1517 dwg_obj_has_subentity (const Dwg_Object *obj)
1518 {
1519 const unsigned int type = obj->type;
1520 return (obj->supertype == DWG_SUPERTYPE_ENTITY)
1521 && (type == DWG_TYPE_INSERT || type == DWG_TYPE_MINSERT
1522 || type == DWG_TYPE_POLYLINE_2D || type == DWG_TYPE_POLYLINE_3D
1523 || type == DWG_TYPE_POLYLINE_PFACE
1524 || type == DWG_TYPE_POLYLINE_MESH);
1525 }
1526
1527 // All entities deriving from 3DSOLID/AcDbModelerGeometry
1528 EXPORT int
dwg_obj_is_3dsolid(const Dwg_Object * obj)1529 dwg_obj_is_3dsolid (const Dwg_Object *obj)
1530 {
1531 const Dwg_Object_Type type = obj->fixedtype;
1532 return
1533 (obj->supertype == DWG_SUPERTYPE_OBJECT
1534 && (type == DWG_TYPE_ACSH_BREP_CLASS ||
1535 type == DWG_TYPE_ASSOCASMBODYACTIONPARAM))
1536 ||
1537 (obj->supertype == DWG_SUPERTYPE_ENTITY
1538 && (type == DWG_TYPE__3DSOLID ||
1539 type == DWG_TYPE_REGION ||
1540 type == DWG_TYPE_BODY ||
1541 type == DWG_TYPE_EXTRUDEDSURFACE ||
1542 type == DWG_TYPE_LOFTEDSURFACE ||
1543 type == DWG_TYPE_NURBSURFACE ||
1544 type == DWG_TYPE_PLANESURFACE ||
1545 type == DWG_TYPE_REVOLVEDSURFACE ||
1546 type == DWG_TYPE_SWEPTSURFACE));
1547 }
1548
1549 EXPORT int
dwg_obj_is_acsh(const Dwg_Object * obj)1550 dwg_obj_is_acsh (const Dwg_Object *obj)
1551 {
1552 const Dwg_Object_Type type = obj->fixedtype;
1553 return (obj->supertype == DWG_SUPERTYPE_OBJECT
1554 && (type == DWG_TYPE_ACSH_BOOLEAN_CLASS
1555 || type == DWG_TYPE_ACSH_BOX_CLASS
1556 || type == DWG_TYPE_ACSH_BREP_CLASS
1557 || type == DWG_TYPE_ACSH_CHAMFER_CLASS
1558 || type == DWG_TYPE_ACSH_CONE_CLASS
1559 || type == DWG_TYPE_ACSH_CYLINDER_CLASS
1560 || type == DWG_TYPE_ACSH_EXTRUSION_CLASS
1561 || type == DWG_TYPE_ACSH_FILLET_CLASS
1562 || type == DWG_TYPE_ACSH_HISTORY_CLASS
1563 || type == DWG_TYPE_ACSH_LOFT_CLASS
1564 || type == DWG_TYPE_ACSH_PYRAMID_CLASS
1565 || type == DWG_TYPE_ACSH_REVOLVE_CLASS
1566 || type == DWG_TYPE_ACSH_SPHERE_CLASS
1567 || type == DWG_TYPE_ACSH_SWEEP_CLASS
1568 || type == DWG_TYPE_ACSH_TORUS_CLASS
1569 || type == DWG_TYPE_ACSH_WEDGE_CLASS));
1570 }
1571
1572 EXPORT Dwg_Section_Type
dwg_section_type(const char * restrict name)1573 dwg_section_type (const char* restrict name)
1574 {
1575 if (name == NULL)
1576 {
1577 return SECTION_UNKNOWN; // but could also be INFO or SYSTEM_MAP
1578 }
1579 else if (strEQc (name, "AcDb:Header"))
1580 {
1581 return SECTION_HEADER;
1582 }
1583 else if (strEQc (name, "AcDb:Classes"))
1584 {
1585 return SECTION_CLASSES;
1586 }
1587 else if (strEQc (name, "AcDb:SummaryInfo"))
1588 {
1589 return SECTION_SUMMARYINFO;
1590 }
1591 else if (strEQc (name, "AcDb:Preview"))
1592 {
1593 return SECTION_PREVIEW;
1594 }
1595 else if (strEQc (name, "AcDb:VBAProject"))
1596 {
1597 return SECTION_VBAPROJECT;
1598 }
1599 else if (strEQc (name, "AcDb:AppInfo"))
1600 {
1601 return SECTION_APPINFO;
1602 }
1603 else if (strEQc (name, "AcDb:FileDepList"))
1604 {
1605 return SECTION_FILEDEPLIST;
1606 }
1607 else if (strEQc (name, "AcDb:RevHistory"))
1608 {
1609 return SECTION_REVHISTORY;
1610 }
1611 else if (strEQc (name, "AcDb:Security"))
1612 {
1613 return SECTION_SECURITY;
1614 }
1615 else if (strEQc (name, "AcDb:AcDbObjects"))
1616 {
1617 return SECTION_OBJECTS;
1618 }
1619 else if (strEQc (name, "AcDb:ObjFreeSpace"))
1620 {
1621 return SECTION_OBJFREESPACE;
1622 }
1623 else if (strEQc (name, "AcDb:Template"))
1624 {
1625 return SECTION_TEMPLATE;
1626 }
1627 else if (strEQc (name, "AcDb:Handles"))
1628 {
1629 return SECTION_HANDLES;
1630 }
1631 else if (strEQc (name, "AcDb:AcDsPrototype_1b"))
1632 { // r2013+
1633 return SECTION_ACDS; // 0xc or 0xd
1634 }
1635 else if (strEQc (name, "AcDb:AuxHeader"))
1636 {
1637 return SECTION_AUXHEADER;
1638 }
1639 else if (strEQc (name, "AcDb:Signature"))
1640 {
1641 return SECTION_SIGNATURE;
1642 }
1643 else if (strEQc (name, "AcDb:AppInfoHistory"))
1644 { // AC1021
1645 return SECTION_APPINFOHISTORY;
1646 }
1647 return SECTION_UNKNOWN;
1648 }
1649
1650 EXPORT Dwg_Section_Type
dwg_section_wtype(const DWGCHAR * restrict wname)1651 dwg_section_wtype (const DWGCHAR *restrict wname)
1652 {
1653 DWGCHAR *wp;
1654 char name[24];
1655 uint16_t c;
1656 int i = 0;
1657
1658 if (wname == NULL)
1659 return SECTION_UNKNOWN; // but could also be INFO or SYSTEM_MAP
1660 wp = (DWGCHAR *)wname;
1661 while ((c = *wp++))
1662 {
1663 name[i++] = (char)(c & 0xff);
1664 }
1665 name[i] = '\0';
1666 return dwg_section_type (name);
1667 }
1668
1669 static const char * const dwg_section_r2004_names[] =
1670 {
1671 "UNKNOWN", // 0
1672 "AcDb:Header", // 1
1673 "AcDb:AuxHeader", // 2
1674 "AcDb:Classes", // 3
1675 "AcDb:Handles", // 4
1676 "AcDb:Template", // 5
1677 "AcDb:ObjFreeSpace", // 6
1678 "AcDb:AcDbObjects", // 7
1679 "AcDb:RevHistory", // 8
1680 "AcDb:SummaryInfo", // 9
1681 "AcDb:Preview", // 10
1682 "AcDb:AppInfo", // 11
1683 "AcDb:AppInfoHistory", // 12
1684 "AcDb:FileDepList", // 13
1685 "AcDb:Security", // 14
1686 "AcDb:VBAProject", // 15
1687 "AcDb:Signature", // 16
1688 "AcDb:AcDsPrototype_1b", // 17
1689 "INFO", // 18
1690 "SYSTEM_MAP", // 19
1691 };
1692 static const char * const dwg_section_r13_names[] =
1693 {
1694 "Header", // 0
1695 "Classes", // 1
1696 "Handles", // 2
1697 "2ndHeader", // 3
1698 "Template", // 4
1699 "AuxHeader" // 5
1700 };
1701 static const char * const dwg_section_r11_names[] =
1702 {
1703 "HEADER", // 0
1704 "BLOCK", // 1
1705 "LAYER" // 2
1706 "STYLE", // 3
1707 "LTYPE", // 4
1708 "VIEW", // 5
1709 "UCS", // 6
1710 "VPORT", // 7
1711 "APPID", // 8
1712 "DIMSTYLE", // 9
1713 "VX" // 10
1714 };
1715
1716 const char *
dwg_section_name(const Dwg_Data * dwg,const unsigned int sec_id)1717 dwg_section_name (const Dwg_Data *dwg, const unsigned int sec_id)
1718 {
1719 if (dwg->header.version >= R_2004)
1720 {
1721 return (sec_id <= SECTION_SYSTEM_MAP) ? dwg_section_r2004_names[sec_id] : NULL;
1722 }
1723 else if (dwg->header.version > R_11)
1724 {
1725 return (sec_id <= SECTION_AUXHEADER_R2000) ? dwg_section_r13_names[sec_id] : NULL;
1726 }
1727 else
1728 {
1729 return (sec_id <= SECTION_VX) ? dwg_section_r11_names[sec_id] : NULL;
1730 }
1731 }
1732
1733 EXPORT enum RESBUF_VALUE_TYPE
dwg_resbuf_value_type(short gc)1734 dwg_resbuf_value_type (short gc)
1735 {
1736 if (gc >= 300)
1737 {
1738 if (gc >= 440)
1739 {
1740 if (gc >= 1000) // 1000-1071
1741 {
1742 if (gc == 1004)
1743 return DWG_VT_BINARY;
1744 if (gc <= 1009)
1745 return DWG_VT_STRING;
1746 if (gc <= 1059)
1747 return DWG_VT_REAL;
1748 if (gc <= 1070)
1749 return DWG_VT_INT16;
1750 if (gc == 1071)
1751 return DWG_VT_INT32;
1752 }
1753 else // 440-999
1754 {
1755 if (gc <= 459)
1756 return DWG_VT_INT32;
1757 if (gc <= 469)
1758 return DWG_VT_REAL;
1759 if (gc <= 479)
1760 return DWG_VT_STRING;
1761 if (gc <= 998)
1762 return DWG_VT_INVALID;
1763 if (gc == 999)
1764 return DWG_VT_STRING; // lgtm [cpp/constant-comparison]
1765 }
1766 }
1767 else // <440
1768 {
1769 if (gc >= 390) // 390-439
1770 {
1771 if (gc <= 399)
1772 return DWG_VT_HANDLE;
1773 if (gc <= 409)
1774 return DWG_VT_INT16;
1775 if (gc <= 419)
1776 return DWG_VT_STRING;
1777 if (gc <= 429)
1778 return DWG_VT_INT32;
1779 if (gc <= 439)
1780 return DWG_VT_STRING; // lgtm [cpp/constant-comparison]
1781 }
1782 else // 330-389
1783 {
1784 if (gc <= 309)
1785 return DWG_VT_STRING;
1786 if (gc <= 319)
1787 return DWG_VT_BINARY;
1788 if (gc <= 329)
1789 return DWG_VT_HANDLE;
1790 if (gc <= 369)
1791 return DWG_VT_OBJECTID;
1792 if (gc <= 389)
1793 return DWG_VT_INT16; // lgtm [cpp/constant-comparison]
1794 }
1795 }
1796 }
1797 else if (gc >= 105)
1798 {
1799 if (gc >= 210) // 210-299
1800 {
1801 if (gc <= 239)
1802 return DWG_VT_REAL;
1803 if (gc <= 269)
1804 return DWG_VT_INVALID;
1805 if (gc <= 279)
1806 return DWG_VT_INT16;
1807 if (gc <= 289)
1808 return DWG_VT_INT8;
1809 if (gc <= 299)
1810 return DWG_VT_BOOL; // lgtm [cpp/constant-comparison]
1811 }
1812 else // 105-209
1813 {
1814 if (gc == 105)
1815 return DWG_VT_HANDLE;
1816 if (gc <= 109)
1817 return DWG_VT_INVALID;
1818 if (gc <= 149)
1819 return DWG_VT_REAL;
1820 if (gc <= 169) // e.g. REQUIREDVERSIONS 160 r2013+
1821 return DWG_VT_INT64;
1822 if (gc <= 179)
1823 return DWG_VT_INT16;
1824 if (gc <= 209)
1825 return DWG_VT_INVALID; // lgtm [cpp/constant-comparison]
1826 }
1827 }
1828 else // <105
1829 {
1830 if (gc >= 38) // 38-102
1831 {
1832 if (gc <= 59)
1833 return DWG_VT_REAL;
1834 if (gc <= 79)
1835 return DWG_VT_INT16;
1836 if (gc <= 99)
1837 return DWG_VT_INT32;
1838 if (gc <= 101)
1839 return DWG_VT_STRING;
1840 if (gc == 102)
1841 return DWG_VT_STRING;
1842 }
1843 else // 0-37
1844 {
1845 if (gc < 0)
1846 return DWG_VT_HANDLE;
1847 if (gc <= 4)
1848 return DWG_VT_STRING;
1849 if (gc == 5)
1850 return DWG_VT_HANDLE;
1851 if (gc <= 9)
1852 return DWG_VT_STRING; // but 9 never TU
1853 if (gc <= 37)
1854 return DWG_VT_POINT3D; // lgtm [cpp/constant-comparison]
1855 }
1856 }
1857 return DWG_VT_INVALID;
1858 }
1859
1860 // See acdb.h: 100th of a mm, enum of
1861 const int lweights[] = { 0,
1862 5,
1863 9,
1864 13,
1865 15,
1866 18,
1867 20,
1868 25,
1869 30,
1870 35,
1871 40,
1872 50,
1873 53,
1874 60,
1875 70,
1876 80,
1877 90,
1878 100,
1879 106,
1880 120,
1881 /*20:*/ 140,
1882 158,
1883 200,
1884 211,
1885 /*illegal/reserved:*/ 0,
1886 0,
1887 0,
1888 0,
1889 /*28:*/ 0, // 0x1c
1890 /*29:*/ -1, // 0x1d BYLAYER
1891 -2, // BYBLOCK
1892 -3 }; // BYLWDEFAULT
1893
1894 EXPORT int
dxf_cvt_lweight(const BITCODE_BSd value)1895 dxf_cvt_lweight (const BITCODE_BSd value)
1896 {
1897 return lweights[value % 32];
1898 }
1899
1900 EXPORT BITCODE_BSd
dxf_revcvt_lweight(const int lw)1901 dxf_revcvt_lweight (const int lw)
1902 {
1903 for (BITCODE_BSd i = 0; i < (BITCODE_BSd)ARRAY_SIZE (lweights); i++)
1904 if (lweights[i] == lw)
1905 return i;
1906 return 0;
1907 }
1908
1909 void
set_handle_size(Dwg_Handle * restrict hdl)1910 set_handle_size (Dwg_Handle *restrict hdl)
1911 {
1912 if (hdl->value)
1913 {
1914 int i;
1915 unsigned char *val;
1916 memset (&val, 0, sizeof(val));
1917 val = (unsigned char *)&hdl->value;
1918 // FIXME: little endian only
1919 for (i = sizeof(val) - 1; i >= 0; i--)
1920 if (val[i])
1921 break;
1922 hdl->size = i + 1;
1923 }
1924 else
1925 hdl->size = 0;
1926 }
1927
1928 /** For encode:
1929 * May need obj to shorten the code to a relative offset, but not in
1930 * header_vars. There obj is NULL.
1931 */
1932 EXPORT int
dwg_add_handle(Dwg_Handle * restrict hdl,const BITCODE_RC code,const unsigned long absref,const Dwg_Object * restrict obj)1933 dwg_add_handle (Dwg_Handle *restrict hdl, const BITCODE_RC code,
1934 const unsigned long absref, const Dwg_Object *restrict obj)
1935 {
1936 int offset = obj ? (absref - (int)obj->handle.value) : 0;
1937 hdl->code = code;
1938 hdl->value = absref;
1939 if (obj && (code == 0 || !offset) && absref) // only if same obj
1940 {
1941 Dwg_Data *dwg = obj->parent;
1942 LOG_HANDLE ("object_map{%lX} = %u\n", absref, obj->index);
1943 assert (dwg);
1944 if (!dwg->object_map) // for dwg_add_document()
1945 dwg->object_map = hash_new (100);
1946 hash_set (dwg->object_map, absref, (uint32_t)obj->index);
1947 }
1948
1949 set_handle_size (hdl);
1950 if ((code == 4 || code > 5) && obj && absref)
1951 {
1952 // change code to 6.0.0 or 8.0.0
1953 if (offset == 1)
1954 {
1955 hdl->code = 6;
1956 hdl->value = 0;
1957 hdl->size = 0;
1958 }
1959 else if (offset == -1)
1960 {
1961 hdl->code = 8;
1962 hdl->value = 0;
1963 hdl->size = 0;
1964 }
1965 else if (offset > 0)
1966 {
1967 hdl->code = 10;
1968 hdl->value = offset;
1969 set_handle_size (hdl);
1970 }
1971 else if (offset < 0)
1972 {
1973 hdl->code = 12;
1974 hdl->value = -offset;
1975 set_handle_size (hdl);
1976 }
1977 }
1978 return 0;
1979 }
1980
1981
1982 // Returns an existing ref with the same ownership (hard/soft, owner/pointer)
1983 // or creates it. May return a freshly allocated ref via dwg_new_ref.
1984 EXPORT Dwg_Object_Ref *
dwg_add_handleref(Dwg_Data * restrict dwg,const BITCODE_RC code,const unsigned long absref,const Dwg_Object * restrict obj)1985 dwg_add_handleref (Dwg_Data *restrict dwg, const BITCODE_RC code,
1986 const unsigned long absref, const Dwg_Object *restrict obj)
1987 {
1988 Dwg_Object_Ref *ref;
1989 // ENTITY, DICTIONARY, XRECORD or class may need to be relative.
1990 // GROUP needs to be absolute. DICTIONARYVAr absolute
1991 // TODO: prev_entity/next_entity also
1992 // skip the search for existing absolute ref then.
1993 if (code > 5
1994 || (code == 4 && obj
1995 && ((obj->fixedtype == DWG_TYPE_DICTIONARY
1996 || obj->fixedtype == DWG_TYPE_XRECORD
1997 || obj->supertype == DWG_SUPERTYPE_ENTITY
1998 || (obj->type > DWG_TYPE_GROUP
1999 && obj->fixedtype != DWG_TYPE_DICTIONARYVAR)))))
2000 ;
2001 else
2002 {
2003 // search of this code-absref pair already exists
2004 for (BITCODE_BL i = 0; i < dwg->num_object_refs; i++)
2005 {
2006 Dwg_Object_Ref *refi = dwg->object_ref[i];
2007 if (refi->absolute_ref == absref && refi->handleref.code == code)
2008 return refi;
2009 }
2010 }
2011 // else create a new global ref
2012 ref = dwg_new_ref (dwg);
2013 dwg_add_handle (&ref->handleref, code, absref, obj);
2014 ref->absolute_ref = absref;
2015 // fill ->obj later
2016 return ref;
2017 }
2018
2019 /** Return a link to the global ref or a new one. Or a NULLHDL. */
2020 EXPORT Dwg_Object_Ref *
dwg_dup_handleref(Dwg_Data * restrict dwg,const Dwg_Object_Ref * restrict ref)2021 dwg_dup_handleref (Dwg_Data *restrict dwg, const Dwg_Object_Ref *restrict ref)
2022 {
2023 if (ref)
2024 return dwg_add_handleref (dwg, ref->handleref.code, ref->absolute_ref, NULL);
2025 else
2026 return dwg_add_handleref (dwg, 5, 0, NULL);
2027 }
2028
2029 // Creates a non-global, free'able handle ref.
2030 EXPORT Dwg_Object_Ref *
dwg_add_handleref_free(const BITCODE_RC code,const unsigned long absref)2031 dwg_add_handleref_free (const BITCODE_RC code, const unsigned long absref)
2032 {
2033 Dwg_Object_Ref *ref = (Dwg_Object_Ref *)calloc (1, sizeof (Dwg_Object_Ref));
2034 dwg_add_handle (&ref->handleref, code, absref, NULL);
2035 return ref;
2036 }
2037
2038 // Not checking the header_vars entry, only searching the objects
2039 // Returning a hardowner ref (code 3) to it, as stored in header_vars.
2040 EXPORT BITCODE_H
dwg_find_table_control(Dwg_Data * restrict dwg,const char * restrict table)2041 dwg_find_table_control (Dwg_Data *restrict dwg, const char *restrict table)
2042 {
2043 BITCODE_BL i;
2044 for (i = 0; i < dwg->num_objects; i++)
2045 {
2046 if (dwg->object[i].name && strEQ (dwg->object[i].name, table))
2047 {
2048 Dwg_Handle *hdl = &dwg->object[i].handle;
2049 return dwg_add_handleref (dwg, 3, hdl->value, NULL);
2050 }
2051 }
2052 // if we haven't read all objects yet, ignore this error
2053 LOG_TRACE ("dwg_find_table_control: table control object %s not found\n",
2054 table)
2055 return NULL;
2056 }
2057
2058 // Searching for a named dictionary entry, name is utf8.
2059 // Returning a hardpointer ref (5) to it, as stored in header_vars.
2060 // Usually another dictionary.
2061 EXPORT BITCODE_H
dwg_find_dictionary(Dwg_Data * restrict dwg,const char * restrict name)2062 dwg_find_dictionary (Dwg_Data *restrict dwg, const char *restrict name)
2063 {
2064 // The NOD (Named Object Dict) is always the very first DICTIONARY
2065 Dwg_Object_DICTIONARY *nod;
2066 Dwg_Object *obj = dwg_get_first_object (dwg, DWG_TYPE_DICTIONARY);
2067 if (!obj || !obj->tio.object || obj->fixedtype != DWG_TYPE_DICTIONARY)
2068 {
2069 LOG_ERROR ("dwg_find_dictionary: 1st NOD DICTIONARY not found")
2070 return NULL;
2071 }
2072 nod = obj->tio.object->tio.DICTIONARY;
2073 for (BITCODE_BL j = 0; j < nod->numitems; j++)
2074 {
2075 char *u8;
2076 if (!nod->texts || !nod->itemhandles)
2077 continue;
2078 u8 = nod->texts[j];
2079 if (!u8)
2080 continue;
2081 if (IS_FROM_TU_DWG (dwg))
2082 u8 = bit_convert_TU ((BITCODE_TU)u8);
2083 if (u8 && strEQ (u8, name))
2084 {
2085 Dwg_Object_Ref *ref = nod->itemhandles[j];
2086 if (!ref)
2087 continue;
2088 // relative? (8.0.0, 6.0.0, ...)
2089 dwg_resolve_handleref (ref, obj);
2090 if (IS_FROM_TU_DWG (dwg))
2091 free (u8);
2092 return dwg_add_handleref (dwg, 5, ref->absolute_ref, NULL);
2093 }
2094 if (IS_FROM_TU_DWG (dwg))
2095 free (u8);
2096 }
2097 LOG_TRACE ("dwg_find_dictionary: DICTIONARY with %s not found\n", name)
2098 return NULL;
2099 }
2100
2101 // find the named dict entry
2102 EXPORT BITCODE_H
dwg_find_dicthandle(Dwg_Data * restrict dwg,BITCODE_H dict,const char * restrict name)2103 dwg_find_dicthandle (Dwg_Data *restrict dwg, BITCODE_H dict, const char *restrict name)
2104 {
2105 BITCODE_BL i;
2106 Dwg_Object_DICTIONARY *_obj;
2107 Dwg_Object *obj = dwg_resolve_handle (dwg, dict->absolute_ref);
2108
2109 if (!obj || !obj->tio.object)
2110 {
2111 LOG_TRACE ("dwg_find_dicthandle: Could not resolve dict " FORMAT_REF "\n",
2112 ARGS_REF(dict));
2113 return NULL;
2114 }
2115 if (obj->type != DWG_TYPE_DICTIONARY)
2116 {
2117 LOG_ERROR ("dwg_find_dicthandle: dict not a DICTIONARY\n");
2118 return NULL;
2119 }
2120
2121 _obj = obj->tio.object->tio.DICTIONARY;
2122 if (!_obj->numitems)
2123 return NULL;
2124 for (i = 0; i < _obj->numitems; i++)
2125 {
2126 BITCODE_T *texts = _obj->texts;
2127 BITCODE_H *hdlv = _obj->itemhandles;
2128
2129 if (!hdlv || !texts || !texts[i])
2130 continue;
2131 if (IS_FROM_TU_DWG (dwg))
2132 {
2133 if (bit_eq_TU (name, (BITCODE_TU)texts[i]))
2134 return hdlv[i];
2135 }
2136 else
2137 {
2138 if (strEQ (name, texts[i]))
2139 return hdlv[i];
2140 }
2141 }
2142 return NULL;
2143 }
2144
2145 // find dict entry and match its name
2146 EXPORT BITCODE_H
dwg_find_dicthandle_objname(Dwg_Data * restrict dwg,BITCODE_H dict,const char * restrict name)2147 dwg_find_dicthandle_objname (Dwg_Data *restrict dwg, BITCODE_H dict, const char *restrict name)
2148 {
2149 BITCODE_BL i;
2150 Dwg_Object_DICTIONARY *_obj;
2151 Dwg_Object *obj = dwg_resolve_handle (dwg, dict->absolute_ref);
2152
2153 if (!obj || !obj->tio.object)
2154 {
2155 LOG_TRACE ("dwg_find_dicthandle: Could not resolve dict " FORMAT_REF "\n",
2156 ARGS_REF(dict));
2157 return NULL;
2158 }
2159 if (obj->type != DWG_TYPE_DICTIONARY)
2160 {
2161 LOG_ERROR ("dwg_find_dicthandle: dict not a DICTIONARY\n");
2162 return NULL;
2163 }
2164
2165 _obj = obj->tio.object->tio.DICTIONARY;
2166 if (!_obj->numitems)
2167 return 0;
2168 for (i = 0; i < _obj->numitems; i++)
2169 {
2170 char *hdlname;
2171 BITCODE_H *hdlv = _obj->itemhandles;
2172 Dwg_Object *hobj;
2173 Dwg_Object_APPID *_o; // just some random type
2174 int isnew = 0;
2175 bool ok;
2176
2177 if (!hdlv || !hdlv[i])
2178 continue;
2179 hobj = dwg_resolve_handle (dwg, hdlv[i]->absolute_ref);
2180 if (!hobj || !hobj->tio.object || !hobj->tio.object->tio.APPID || !hobj->name)
2181 continue;
2182 _o = hobj->tio.object->tio.APPID;
2183 ok = dwg_dynapi_entity_utf8text (_o, hobj->name, "name", &hdlname, &isnew, NULL);
2184 LOG_HANDLE (" %s.%s[%d] => %s.name: %s\n", obj->name, "entries", i,
2185 hobj->name, hdlname ? hdlname : "NULL");
2186 if (ok && hdlname && (strEQ (name, hdlname) || !strcasecmp (name, hdlname)))
2187 {
2188 if (isnew)
2189 free (hdlname);
2190 return hdlv[i];
2191 }
2192 if (ok && isnew && hdlname)
2193 free (hdlname);
2194 }
2195 return NULL;
2196 }
2197
2198 /* Return the handle of the first object of the given type. */
2199 Dwg_Handle *
dwg_find_first_type_handle(Dwg_Data * restrict dwg,enum DWG_OBJECT_TYPE type)2200 dwg_find_first_type_handle (Dwg_Data *restrict dwg, enum DWG_OBJECT_TYPE type)
2201 {
2202 for (BITCODE_BL i = 0; i < dwg->num_objects; i++)
2203 {
2204 if (dwg->object[i].fixedtype == type)
2205 return &dwg->object[i].handle;
2206 }
2207 return NULL;
2208 }
2209
2210 BITCODE_H
dwg_find_tablehandle_silent(Dwg_Data * restrict dwg,const char * restrict name,const char * restrict table)2211 dwg_find_tablehandle_silent (Dwg_Data *restrict dwg, const char *restrict name,
2212 const char *restrict table)
2213 {
2214 BITCODE_H ref;
2215 int oldopts = dwg->opts;
2216 dwg->opts &= ~(DWG_OPTS_LOGLEVEL);
2217 loglevel = 0;
2218
2219 ref = dwg_find_tablehandle (dwg, name, table);
2220
2221 dwg->opts = oldopts;
2222 loglevel = oldopts & DWG_OPTS_LOGLEVEL;
2223 return ref;
2224 }
2225
2226 // return the matching _CONTROL table, DICTIONARY entry, or NULL
2227 EXPORT BITCODE_H
dwg_ctrl_table(Dwg_Data * restrict dwg,const char * restrict table)2228 dwg_ctrl_table (Dwg_Data *restrict dwg, const char *restrict table)
2229 {
2230 BITCODE_H ctrl = NULL;
2231 Dwg_Header_Variables *vars = &dwg->header_vars;
2232
2233 if (!dwg || !table)
2234 return NULL;
2235 if (strEQc (table, "BLOCK"))
2236 {
2237 if (!(ctrl = vars->BLOCK_CONTROL_OBJECT))
2238 vars->BLOCK_CONTROL_OBJECT = ctrl
2239 = dwg_find_table_control (dwg, "BLOCK_CONTROL");
2240 }
2241 else if (strEQc (table, "LAYER"))
2242 {
2243 if (!(ctrl = vars->LAYER_CONTROL_OBJECT))
2244 vars->LAYER_CONTROL_OBJECT = ctrl
2245 = dwg_find_table_control (dwg, "LAYER_CONTROL");
2246 }
2247 else if (strEQc (table, "STYLE"))
2248 {
2249 if (!(ctrl = vars->STYLE_CONTROL_OBJECT))
2250 vars->STYLE_CONTROL_OBJECT = ctrl
2251 = dwg_find_table_control (dwg, "STYLE_CONTROL");
2252 }
2253 else if (strEQc (table, "LTYPE"))
2254 {
2255 if (!(ctrl = vars->LTYPE_CONTROL_OBJECT))
2256 vars->LTYPE_CONTROL_OBJECT = ctrl
2257 = dwg_find_table_control (dwg, "LTYPE_CONTROL");
2258 }
2259 else if (strEQc (table, "VIEW"))
2260 {
2261 if (!(ctrl = vars->VIEW_CONTROL_OBJECT))
2262 vars->VIEW_CONTROL_OBJECT = ctrl
2263 = dwg_find_table_control (dwg, "VIEW_CONTROL");
2264 }
2265 else if (strEQc (table, "UCS"))
2266 {
2267 if (!(ctrl = vars->UCS_CONTROL_OBJECT))
2268 vars->UCS_CONTROL_OBJECT = ctrl
2269 = dwg_find_table_control (dwg, "UCS_CONTROL");
2270 }
2271 else if (strEQc (table, "VPORT"))
2272 {
2273 if (!(ctrl = vars->VPORT_CONTROL_OBJECT))
2274 vars->VPORT_CONTROL_OBJECT = ctrl
2275 = dwg_find_table_control (dwg, "VPORT_CONTROL");
2276 }
2277 else if (strEQc (table, "APPID"))
2278 {
2279 if (!(ctrl = vars->APPID_CONTROL_OBJECT))
2280 vars->APPID_CONTROL_OBJECT = ctrl
2281 = dwg_find_table_control (dwg, "APPID_CONTROL");
2282 }
2283 // TODO ACAD_DSTYLE_DIM* are probably different handles
2284 else if (strEQc (table, "DIMSTYLE") || memBEGINc (table, "ACAD_DSTYLE_DIM"))
2285 {
2286 if (!(ctrl = vars->DIMSTYLE_CONTROL_OBJECT))
2287 vars->DIMSTYLE_CONTROL_OBJECT = ctrl
2288 = dwg_find_table_control (dwg, "DIMSTYLE_CONTROL");
2289 }
2290 else if (strEQc (table, "VX"))
2291 {
2292 if (!(ctrl = vars->VX_CONTROL_OBJECT))
2293 vars->VX_CONTROL_OBJECT = ctrl
2294 = dwg_find_table_control (dwg, "VX_CONTROL");
2295 }
2296 else if (strEQc (table, "GROUP"))
2297 {
2298 if (!(ctrl = vars->DICTIONARY_ACAD_GROUP))
2299 vars->DICTIONARY_ACAD_GROUP = ctrl
2300 = dwg_find_dictionary (dwg, "ACAD_GROUP");
2301 }
2302 else if (strEQc (table, "MLSTYLE") || strEQc (table, "MLINESTYLE"))
2303 {
2304 if (!(ctrl = vars->DICTIONARY_ACAD_MLINESTYLE))
2305 vars->DICTIONARY_ACAD_MLINESTYLE = ctrl
2306 = dwg_find_dictionary (dwg, "ACAD_MLINESTYLE");
2307 }
2308 else if (strEQc (table, "MLEADERSTYLE") || strEQc (table, "ACAD_MLEADERVER"))
2309 {
2310 ctrl = dwg_find_dictionary (dwg, "ACAD_MLEADERSTYLE");
2311 }
2312 else if (strEQc (table, "NAMED_OBJECT"))
2313 // The very first DICTIONARY 0.1.C with all the names
2314 {
2315 if (!(ctrl = vars->DICTIONARY_NAMED_OBJECT))
2316 vars->DICTIONARY_NAMED_OBJECT = ctrl
2317 = dwg_add_handleref (dwg, 3, 0xC, NULL);
2318 }
2319 else if (strEQc (table, "LAYOUT"))
2320 {
2321 if (!(ctrl = vars->DICTIONARY_LAYOUT))
2322 vars->DICTIONARY_LAYOUT = ctrl
2323 = dwg_find_dictionary (dwg, "ACAD_LAYOUT");
2324 }
2325 else if (strEQc (table, "PLOTSETTINGS"))
2326 {
2327 if (!(ctrl = vars->DICTIONARY_PLOTSETTINGS))
2328 vars->DICTIONARY_PLOTSETTINGS = ctrl
2329 = dwg_find_dictionary (dwg, "ACAD_PLOTSETTINGS");
2330 }
2331 else if (strEQc (table, "PLOTSTYLENAME"))
2332 {
2333 if (!(ctrl = vars->DICTIONARY_PLOTSTYLENAME))
2334 vars->DICTIONARY_PLOTSTYLENAME = ctrl
2335 = dwg_find_dictionary (dwg, "ACAD_PLOTSTYLENAME");
2336 }
2337 // TODO but maybe the mappers are different
2338 else if (strEQc (table, "MATERIAL")
2339 || memBEGINc (table, "ACAD_MATERIAL_MAPPER"))
2340 {
2341 if (!(ctrl = vars->DICTIONARY_MATERIAL))
2342 vars->DICTIONARY_MATERIAL = ctrl
2343 = dwg_find_dictionary (dwg, "ACAD_MATERIAL");
2344 }
2345 else if (strEQc (table, "COLOR"))
2346 {
2347 if (!(ctrl = vars->DICTIONARY_COLOR))
2348 vars->DICTIONARY_COLOR = ctrl
2349 = dwg_find_dictionary (dwg, "ACAD_COLOR");
2350 }
2351 else if (strEQc (table, "VISUALSTYLE"))
2352 {
2353 if (!(ctrl = vars->DICTIONARY_VISUALSTYLE))
2354 vars->DICTIONARY_VISUALSTYLE = ctrl
2355 = dwg_find_dictionary (dwg, "ACAD_VISUALSTYLE");
2356 }
2357 else if (strEQc (table, "LIGHTLIST"))
2358 {
2359 if (!(ctrl = vars->DICTIONARY_LIGHTLIST))
2360 vars->DICTIONARY_LIGHTLIST = ctrl
2361 = dwg_find_dictionary (dwg, "ACAD_LIGHTLIST");
2362 }
2363 else
2364 {
2365 LOG_ERROR ("dwg_ctrl_table: Unsupported table %s", table);
2366 return 0;
2367 }
2368 return ctrl;
2369 }
2370
2371 // Search for name in associated table, and return its handle.
2372 // Note that newer tables, like MATERIAL are stored in a DICTIONARY instead.
2373 // Note that we cannot set the ref->obj here, as it may still move by realloc
2374 // dwg->object[]
2375 EXPORT BITCODE_H
dwg_find_tablehandle(Dwg_Data * restrict dwg,const char * restrict name,const char * restrict table)2376 dwg_find_tablehandle (Dwg_Data *restrict dwg, const char *restrict name,
2377 const char *restrict table)
2378 {
2379 BITCODE_BL i, num_entries = 0;
2380 BITCODE_H ctrl = NULL, *hdlv = NULL;
2381 Dwg_Object *obj;
2382 Dwg_Object_APPID_CONTROL *_obj; // just some random generic type
2383 Dwg_Header_Variables *vars = &dwg->header_vars;
2384
2385 if (!dwg || !name || !table)
2386 return NULL;
2387 // look for the _CONTROL table, and search for name in all entries
2388 ctrl = dwg_ctrl_table (dwg, table);
2389 if (strEQc (table, "LTYPE"))
2390 {
2391 if (strEQc (name, "BYLAYER") || strEQc (name, "ByLayer"))
2392 {
2393 if (vars->LTYPE_BYLAYER)
2394 return vars->LTYPE_BYLAYER;
2395 }
2396 else if (strEQc (name, "BYBLOCK") || strEQc (name, "ByBlock"))
2397 {
2398 if (vars->LTYPE_BYBLOCK)
2399 return vars->LTYPE_BYBLOCK;
2400 }
2401 else if (strEQc (name, "CONTINUOUS") || strEQc (name, "Continuous"))
2402 {
2403 if (vars->LTYPE_CONTINUOUS)
2404 return vars->LTYPE_CONTINUOUS;
2405 }
2406 }
2407 if (!ctrl)
2408 { // TODO: silently search table_control. header_vars can be empty
2409 LOG_TRACE ("dwg_find_tablehandle: Empty header_vars table %s\n", table);
2410 return NULL;
2411 }
2412 obj = dwg_resolve_handle (dwg, ctrl->absolute_ref);
2413 if (!obj)
2414 {
2415 LOG_TRACE ("dwg_find_tablehandle: Could not resolve table %s\n", table);
2416 return NULL;
2417 }
2418 if (obj->type == DWG_TYPE_DICTIONARY)
2419 return dwg_find_dicthandle_objname (dwg, ctrl, name);
2420 if (!dwg_obj_is_control (obj))
2421 {
2422 LOG_ERROR ("dwg_find_tablehandle: Could not resolve CONTROL object %s "
2423 "for table %s",
2424 obj->name, table);
2425 return NULL;
2426 }
2427 _obj = obj->tio.object->tio.APPID_CONTROL; // just random type
2428 dwg_dynapi_entity_value (_obj, obj->name, "num_entries", &num_entries, NULL);
2429 if (!num_entries)
2430 return NULL;
2431 dwg_dynapi_entity_value (_obj, obj->name, "entries", &hdlv, NULL);
2432 if (!hdlv)
2433 return NULL;
2434 for (i = 0; i < num_entries; i++)
2435 {
2436 char *hdlname;
2437 Dwg_Object *hobj;
2438 Dwg_Object_APPID *_o;
2439 int isnew = 0;
2440 bool ok;
2441
2442 if (!hdlv[i])
2443 continue;
2444 hobj = dwg_resolve_handle (dwg, hdlv[i]->absolute_ref);
2445 if (!hobj || !hobj->tio.object || !hobj->tio.object->tio.APPID)
2446 continue;
2447 _o = hobj->tio.object->tio.APPID;
2448 ok = dwg_dynapi_entity_utf8text (_o, hobj->name, "name", &hdlname, &isnew, NULL);
2449 LOG_HANDLE (" %s.%s[%d] => %s.name: %s\n", obj->name, "entries", i,
2450 hobj->name, hdlname ? hdlname : "NULL");
2451 if (ok && hdlname && (strEQ (name, hdlname) || !strcasecmp (name, hdlname)))
2452 {
2453 if (isnew)
2454 free (hdlname);
2455 return hdlv[i];
2456 }
2457 if (ok && isnew && hdlname)
2458 free (hdlname);
2459 }
2460
2461 return NULL;
2462 }
2463
2464 // Search for handle in associated table, and return its name. (as UTF-8)
2465 // Always returns a copy.
2466 EXPORT char *
dwg_handle_name(Dwg_Data * restrict dwg,const char * restrict table,const BITCODE_H restrict handle)2467 dwg_handle_name (Dwg_Data *restrict dwg, const char *restrict table,
2468 const BITCODE_H restrict handle)
2469 {
2470 BITCODE_BL i, num_entries = 0;
2471 BITCODE_H ctrl = NULL, *hdlv = NULL;
2472 Dwg_Object *obj;
2473 Dwg_Object_APPID_CONTROL *_obj; // just some random generic type
2474 Dwg_Header_Variables *vars = &dwg->header_vars;
2475
2476 if (!dwg || !table || !handle)
2477 return NULL;
2478 if (!handle->absolute_ref)
2479 return NULL;
2480 // look for the _CONTROL table, and search for name in all entries
2481 ctrl = dwg_ctrl_table (dwg, table);
2482 if (!ctrl)
2483 { // TODO: silently search table_control. header_vars can be empty
2484 LOG_TRACE ("dwg_handle_name: Empty header_vars table %s\n", table);
2485 return 0;
2486 }
2487 obj = dwg_resolve_handle (dwg, ctrl->absolute_ref);
2488 if (!obj)
2489 {
2490 LOG_TRACE ("dwg_handle_name: Could not resolve table %s\n", table);
2491 return 0;
2492 }
2493 //if (obj->type == DWG_TYPE_DICTIONARY)
2494 // return dwg_find_dicthandle_objname (dwg, ctrl, name);
2495 if (!dwg_obj_is_control (obj))
2496 {
2497 LOG_ERROR ("dwg_handle_name: Could not resolve CONTROL object %s "
2498 "for table %s",
2499 obj->name, table);
2500 return 0;
2501 }
2502 _obj = obj->tio.object->tio.APPID_CONTROL; // just a random type
2503 dwg_dynapi_entity_value (_obj, obj->name, "num_entries", &num_entries, NULL);
2504 if (!num_entries)
2505 return NULL;
2506 dwg_dynapi_entity_value (_obj, obj->name, "entries", &hdlv, NULL);
2507 if (!hdlv)
2508 return NULL;
2509 for (i = 0; i < num_entries; i++)
2510 {
2511 char *hdlname, *name;
2512 Dwg_Object *hobj;
2513 Dwg_Object_APPID *_o;
2514 int isnew = 0;
2515 bool ok;
2516
2517 if (!hdlv[i])
2518 continue;
2519 hobj = dwg_resolve_handle (dwg, hdlv[i]->absolute_ref);
2520 if (!hobj || !hobj->tio.object || !hobj->tio.object->tio.APPID)
2521 continue;
2522 if (hdlv[i]->absolute_ref != handle->absolute_ref)
2523 continue;
2524 _o = hobj->tio.object->tio.APPID;
2525 name = hobj->name;
2526 /* For BLOCK search the BLOCK entities instead.
2527 The BLOCK_HEADER has only the abbrevated name, but we want "*D30", not "*D" */
2528 if (strEQc (table, "BLOCK") && hobj->fixedtype == DWG_TYPE_BLOCK_HEADER)
2529 {
2530 Dwg_Object_BLOCK_HEADER *_bh = hobj->tio.object->tio.BLOCK_HEADER;
2531 Dwg_Object *bo = dwg_ref_object (dwg, _bh->block_entity);
2532 if (bo != NULL && bo->fixedtype == DWG_TYPE_BLOCK)
2533 {
2534 _o = (Dwg_Object_APPID *)bo->tio.entity->tio.BLOCK;
2535 name = bo->name;
2536 }
2537 }
2538 ok = dwg_dynapi_entity_utf8text (_o, name, "name", &hdlname, &isnew, NULL);
2539 LOG_HANDLE (" %s.%s[%d] => %s.name: %s\n", obj->name, "entries", i,
2540 hobj->name, hdlname ? hdlname : "NULL");
2541 if (ok)
2542 {
2543 if (!isnew) return strdup (hdlname);
2544 else return hdlname;
2545 }
2546 else
2547 return NULL;
2548 }
2549 return NULL;
2550 }
2551
2552 /* Returns the string value of the member of the AcDbVariableDictionary.
2553 NULL if not found.
2554 The name is ascii. E.g. LIGHTINGUNITS => "0" */
2555 EXPORT char *
dwg_variable_dict(Dwg_Data * restrict dwg,const char * restrict name)2556 dwg_variable_dict (Dwg_Data *restrict dwg, const char *restrict name)
2557 {
2558 static BITCODE_H var_dict = NULL;
2559 BITCODE_H var;
2560 Dwg_Object *obj;
2561 Dwg_Object_DICTIONARYVAR *_obj;
2562
2563 if (!var_dict || dwg->dirty_refs)
2564 var_dict = dwg_find_dictionary (dwg, "AcDbVariableDictionary");
2565 if (!var_dict)
2566 return NULL;
2567 var = dwg_find_dicthandle (dwg, var_dict, name);
2568 if (!var)
2569 return NULL;
2570 obj = dwg_ref_object_silent (dwg, var);
2571 if (!obj || obj->fixedtype != DWG_TYPE_DICTIONARYVAR)
2572 return NULL;
2573 _obj = obj->tio.object->tio.DICTIONARYVAR;
2574 return _obj->strvalue;
2575 }
2576
2577 static bool
xdata_string_match(Dwg_Data * restrict dwg,Dwg_Resbuf * restrict xdata,int type,char * restrict str)2578 xdata_string_match (Dwg_Data *restrict dwg, Dwg_Resbuf *restrict xdata,
2579 int type, char *restrict str)
2580 {
2581 if (xdata->type != type)
2582 return 0;
2583 if (!IS_FROM_TU_DWG (dwg))
2584 {
2585 return strEQ (xdata->value.str.u.data, str);
2586 }
2587 else
2588 {
2589 return memcmp (xdata->value.str.u.wdata, str, xdata->value.str.size * 2) == 0;
2590 }
2591 }
2592
2593 static bool
is_extnames_xrecord(Dwg_Data * restrict dwg,Dwg_Object * restrict xrec,Dwg_Object * restrict xdic)2594 is_extnames_xrecord (Dwg_Data *restrict dwg, Dwg_Object *restrict xrec,
2595 Dwg_Object *restrict xdic)
2596 {
2597 const int16_t w[8] = { 'E', 'X', 'T', 'N', 'A', 'M', 'E', 'S' };
2598 const char *extnames = dwg->header.from_version < R_2007 ? "EXTNAMES" : (const char*)w;
2599
2600 return (xrec
2601 && xdic
2602 && dwg
2603 && xrec->fixedtype == DWG_TYPE_XRECORD
2604 && xrec->tio.object->ownerhandle
2605 && xrec->tio.object->ownerhandle->absolute_ref
2606 == xdic->handle.value
2607 && xrec->tio.object->tio.XRECORD->num_xdata >= 2
2608 && xrec->tio.object->tio.XRECORD->xdata
2609 && xdata_string_match (dwg, xrec->tio.object->tio.XRECORD->xdata,
2610 102, (char *)extnames));
2611 }
2612
2613 // Return a table EXTNAME or NULL. extnames only exist for r13-r14 dwgs
2614 EXPORT char*
dwg_find_table_extname(Dwg_Data * restrict dwg,Dwg_Object * restrict obj)2615 dwg_find_table_extname (Dwg_Data *restrict dwg, Dwg_Object *restrict obj)
2616 {
2617 char *name;
2618 Dwg_Object *xdic;
2619 Dwg_Object_DICTIONARY *_xdic;
2620 Dwg_Object_Ref *xdicref;
2621 Dwg_Object *xrec = NULL;
2622 Dwg_Object_XRECORD *_xrec;
2623 Dwg_Resbuf *xdata;
2624 BITCODE_BL i;
2625
2626 // (GH #167) via DICTIONARY ACAD_XREC_ROUNDTRIP to XRECORD EXTNAMES
2627 if (!dwg_obj_is_table (obj))
2628 return NULL;
2629 // HACK: we can guarantee that the table name is always the first field. See
2630 // dwg_obj_table_get_name().
2631 name = obj->tio.object->tio.LAYER->name;
2632 xdicref = obj->tio.object->xdicobjhandle;
2633 if (!xdicref)
2634 return NULL;
2635 xdic = dwg_ref_object (dwg, xdicref);
2636 if (!xdic || xdic->type != DWG_TYPE_DICTIONARY)
2637 return NULL;
2638 _xdic = xdic->tio.object->tio.DICTIONARY;
2639 if (_xdic->numitems < 1 || !_xdic->texts[0])
2640 return NULL;
2641 if (xdic->tio.object->ownerhandle->absolute_ref != obj->handle.value)
2642 return NULL;
2643 for (i = 0; i < _xdic->numitems; i++)
2644 {
2645 if (strEQc (_xdic->texts[i], "ACAD_XREC_ROUNDTRIP"))
2646 break;
2647 }
2648 if (i == _xdic->numitems) // not found
2649 return NULL;
2650 xrec = dwg_ref_object (dwg, _xdic->itemhandles[i]);
2651 if (!xrec || !is_extnames_xrecord (dwg, xrec, xdic))
2652 return NULL;
2653
2654 _xrec = xrec->tio.object->tio.XRECORD;
2655 xdata = _xrec->xdata;
2656 xdata = xdata->nextrb;
2657 if (xdata->type == 1) // pairs of 1: old name, 2: new name
2658 {
2659 // step to the matching name
2660 cmp:
2661 if (!xdata_string_match (dwg, xdata, 1, name))
2662 {
2663 xdata = xdata->nextrb;
2664 while (xdata && xdata->type != 1 && xdata->type != 102)
2665 xdata = xdata->nextrb;
2666 if (xdata)
2667 goto cmp;
2668 }
2669 if (!xdata)
2670 return NULL;
2671 xdata = xdata->nextrb;
2672 if (xdata->type == 2) // new name
2673 {
2674 if (!IS_FROM_TU_DWG (dwg))
2675 return xdata->value.str.u.data;
2676 else
2677 return (char *)xdata->value.str.u.wdata;
2678 }
2679 }
2680
2681 return NULL;
2682 }
2683
2684 static const Dwg_RGB_Palette rgb_palette[256] = {
2685 { 0x00, 0x00, 0x00 }, // 0
2686 { 0xFF, 0x00, 0x00 }, { 0xFF, 0xFF, 0x00 }, { 0x00, 0xFF, 0x00 },
2687 { 0x00, 0xFF, 0xFF }, { 0x00, 0x00, 0xFF }, // 5
2688 { 0xFF, 0x00, 0xFF }, { 0xFF, 0xFF, 0xFF }, { 0x41, 0x41, 0x41 },
2689 { 0x80, 0x80, 0x80 }, { 0xFF, 0x00, 0x00 }, // 10
2690 { 0xFF, 0xAA, 0xAA }, { 0xBD, 0x00, 0x00 }, { 0xBD, 0x7E, 0x7E },
2691 { 0x81, 0x00, 0x00 }, { 0x81, 0x56, 0x56 }, // 15
2692 { 0x68, 0x00, 0x00 }, { 0x68, 0x45, 0x45 }, { 0x4F, 0x00, 0x00 },
2693 { 0x4F, 0x35, 0x35 }, { 0xFF, 0x3F, 0x00 }, // 20
2694 { 0xFF, 0xBF, 0xAA }, { 0xBD, 0x2E, 0x00 }, { 0xBD, 0x8D, 0x7E },
2695 { 0x81, 0x1F, 0x00 }, { 0x81, 0x60, 0x56 }, // 25
2696 { 0x68, 0x19, 0x00 }, { 0x68, 0x4E, 0x45 }, { 0x4F, 0x13, 0x00 },
2697 { 0x4F, 0x3B, 0x35 }, { 0xFF, 0x7F, 0x00 }, // 30
2698 { 0xFF, 0xD4, 0xAA }, { 0xBD, 0x5E, 0x00 }, { 0xBD, 0x9D, 0x7E },
2699 { 0x81, 0x40, 0x00 }, { 0x81, 0x6B, 0x56 }, // 35
2700 { 0x68, 0x34, 0x00 }, { 0x68, 0x56, 0x45 }, { 0x4F, 0x27, 0x00 },
2701 { 0x4F, 0x42, 0x35 }, { 0xFF, 0xBF, 0x00 }, // 40
2702 { 0xFF, 0xEA, 0xAA }, { 0xBD, 0x8D, 0x00 }, { 0xBD, 0xAD, 0x7E },
2703 { 0x81, 0x60, 0x00 }, { 0x81, 0x76, 0x56 }, // 45
2704 { 0x68, 0x4E, 0x00 }, { 0x68, 0x5F, 0x45 }, { 0x4F, 0x3B, 0x00 },
2705 { 0x4F, 0x49, 0x35 }, { 0xFF, 0xFF, 0x00 }, // 50
2706 { 0xFF, 0xFF, 0xAA }, { 0xBD, 0xBD, 0x00 }, { 0xBD, 0xBD, 0x7E },
2707 { 0x81, 0x81, 0x00 }, { 0x81, 0x81, 0x56 }, // 55
2708 { 0x68, 0x68, 0x00 }, { 0x68, 0x68, 0x45 }, { 0x4F, 0x4F, 0x00 },
2709 { 0x4F, 0x4F, 0x35 }, { 0xBF, 0xFF, 0x00 }, // 60
2710 { 0xEA, 0xFF, 0xAA }, { 0x8D, 0xBD, 0x00 }, { 0xAD, 0xBD, 0x7E },
2711 { 0x60, 0x81, 0x00 }, { 0x76, 0x81, 0x56 }, // 65
2712 { 0x4E, 0x68, 0x00 }, { 0x5F, 0x68, 0x45 }, { 0x3B, 0x4F, 0x00 },
2713 { 0x49, 0x4F, 0x35 }, { 0x7F, 0xFF, 0x00 }, // 70
2714 { 0xD4, 0xFF, 0xAA }, { 0x5E, 0xBD, 0x00 }, { 0x9D, 0xBD, 0x7E },
2715 { 0x40, 0x81, 0x00 }, { 0x6B, 0x81, 0x56 }, // 75
2716 { 0x34, 0x68, 0x00 }, { 0x56, 0x68, 0x45 }, { 0x27, 0x4F, 0x00 },
2717 { 0x42, 0x4F, 0x35 }, { 0x3F, 0xFF, 0x00 }, // 80
2718 { 0xBF, 0xFF, 0xAA }, { 0x2E, 0xBD, 0x00 }, { 0x8D, 0xBD, 0x7E },
2719 { 0x1F, 0x81, 0x00 }, { 0x60, 0x81, 0x56 }, // 85
2720 { 0x19, 0x68, 0x00 }, { 0x4E, 0x68, 0x45 }, { 0x13, 0x4F, 0x00 },
2721 { 0x3B, 0x4F, 0x35 }, { 0x00, 0xFF, 0x00 }, // 90
2722 { 0xAA, 0xFF, 0xAA }, { 0x00, 0xBD, 0x00 }, { 0x7E, 0xBD, 0x7E },
2723 { 0x00, 0x81, 0x00 }, { 0x56, 0x81, 0x56 }, // 95
2724 { 0x00, 0x68, 0x00 }, { 0x45, 0x68, 0x45 }, { 0x00, 0x4F, 0x00 },
2725 { 0x35, 0x4F, 0x35 }, { 0x00, 0xFF, 0x3F }, // 100
2726 { 0xAA, 0xFF, 0xBF }, { 0x00, 0xBD, 0x2E }, { 0x7E, 0xBD, 0x8D },
2727 { 0x00, 0x81, 0x1F }, { 0x56, 0x81, 0x60 }, // 105
2728 { 0x00, 0x68, 0x19 }, { 0x45, 0x68, 0x4E }, { 0x00, 0x4F, 0x13 },
2729 { 0x35, 0x4F, 0x3B }, { 0x00, 0xFF, 0x7F }, // 110
2730 { 0xAA, 0xFF, 0xD4 }, { 0x00, 0xBD, 0x5E }, { 0x7E, 0xBD, 0x9D },
2731 { 0x00, 0x81, 0x40 }, { 0x56, 0x81, 0x6B }, // 115
2732 { 0x00, 0x68, 0x34 }, { 0x45, 0x68, 0x56 }, { 0x00, 0x4F, 0x27 },
2733 { 0x35, 0x4F, 0x42 }, { 0x00, 0xFF, 0xBF }, // 120
2734 { 0xAA, 0xFF, 0xEA }, { 0x00, 0xBD, 0x8D }, { 0x7E, 0xBD, 0xAD },
2735 { 0x00, 0x81, 0x60 }, { 0x56, 0x81, 0x76 }, // 125
2736 { 0x00, 0x68, 0x4E }, { 0x45, 0x68, 0x5F }, { 0x00, 0x4F, 0x3B },
2737 { 0x35, 0x4F, 0x49 }, { 0x00, 0xFF, 0xFF }, // 130
2738 { 0xAA, 0xFF, 0xFF }, { 0x00, 0xBD, 0xBD }, { 0x7E, 0xBD, 0xBD },
2739 { 0x00, 0x81, 0x81 }, { 0x56, 0x81, 0x81 }, // 135
2740 { 0x00, 0x68, 0x68 }, { 0x45, 0x68, 0x68 }, { 0x00, 0x4F, 0x4F },
2741 { 0x35, 0x4F, 0x4F }, { 0x00, 0xBF, 0xFF }, // 140
2742 { 0xAA, 0xEA, 0xFF }, { 0x00, 0x8D, 0xBD }, { 0x7E, 0xAD, 0xBD },
2743 { 0x00, 0x60, 0x81 }, { 0x56, 0x76, 0x81 }, // 145
2744 { 0x00, 0x4E, 0x68 }, { 0x45, 0x5F, 0x68 }, { 0x00, 0x3B, 0x4F },
2745 { 0x35, 0x49, 0x4F }, { 0x00, 0x7F, 0xFF }, // 150
2746 { 0xAA, 0xD4, 0xFF }, { 0x00, 0x5E, 0xBD }, { 0x7E, 0x9D, 0xBD },
2747 { 0x00, 0x40, 0x81 }, { 0x56, 0x6B, 0x81 }, // 155
2748 { 0x00, 0x34, 0x68 }, { 0x45, 0x56, 0x68 }, { 0x00, 0x27, 0x4F },
2749 { 0x35, 0x42, 0x4F }, { 0x00, 0x3F, 0xFF }, // 160
2750 { 0xAA, 0xBF, 0xFF }, { 0x00, 0x2E, 0xBD }, { 0x7E, 0x8D, 0xBD },
2751 { 0x00, 0x1F, 0x81 }, { 0x56, 0x60, 0x81 }, // 165
2752 { 0x00, 0x19, 0x68 }, { 0x45, 0x4E, 0x68 }, { 0x00, 0x13, 0x4F },
2753 { 0x35, 0x3B, 0x4F }, { 0x00, 0x00, 0xFF }, // 170
2754 { 0xAA, 0xAA, 0xFF }, { 0x00, 0x00, 0xBD }, { 0x7E, 0x7E, 0xBD },
2755 { 0x00, 0x00, 0x81 }, { 0x56, 0x56, 0x81 }, // 175
2756 { 0x00, 0x00, 0x68 }, { 0x45, 0x45, 0x68 }, { 0x00, 0x00, 0x4F },
2757 { 0x35, 0x35, 0x4F }, { 0x3F, 0x00, 0xFF }, // 180
2758 { 0xBF, 0xAA, 0xFF }, { 0x2E, 0x00, 0xBD }, { 0x8D, 0x7E, 0xBD },
2759 { 0x1F, 0x00, 0x81 }, { 0x60, 0x56, 0x81 }, // 185
2760 { 0x19, 0x00, 0x68 }, { 0x4E, 0x45, 0x68 }, { 0x13, 0x00, 0x4F },
2761 { 0x3B, 0x35, 0x4F }, { 0x7F, 0x00, 0xFF }, // 190
2762 { 0xD4, 0xAA, 0xFF }, { 0x5E, 0x00, 0xBD }, { 0x9D, 0x7E, 0xBD },
2763 { 0x40, 0x00, 0x81 }, { 0x6B, 0x56, 0x81 }, // 195
2764 { 0x34, 0x00, 0x68 }, { 0x56, 0x45, 0x68 }, { 0x27, 0x00, 0x4F },
2765 { 0x42, 0x35, 0x4F }, { 0xBF, 0x00, 0xFF }, // 200
2766 { 0xEA, 0xAA, 0xFF }, { 0x8D, 0x00, 0xBD }, { 0xAD, 0x7E, 0xBD },
2767 { 0x60, 0x00, 0x81 }, { 0x76, 0x56, 0x81 }, // 205
2768 { 0x4E, 0x00, 0x68 }, { 0x5F, 0x45, 0x68 }, { 0x3B, 0x00, 0x4F },
2769 { 0x49, 0x35, 0x4F }, { 0xFF, 0x00, 0xFF }, // 210
2770 { 0xFF, 0xAA, 0xFF }, { 0xBD, 0x00, 0xBD }, { 0xBD, 0x7E, 0xBD },
2771 { 0x81, 0x00, 0x81 }, { 0x81, 0x56, 0x81 }, // 215
2772 { 0x68, 0x00, 0x68 }, { 0x68, 0x45, 0x68 }, { 0x4F, 0x00, 0x4F },
2773 { 0x4F, 0x35, 0x4F }, { 0xFF, 0x00, 0xBF }, // 220
2774 { 0xFF, 0xAA, 0xEA }, { 0xBD, 0x00, 0x8D }, { 0xBD, 0x7E, 0xAD },
2775 { 0x81, 0x00, 0x60 }, { 0x81, 0x56, 0x76 }, // 225
2776 { 0x68, 0x00, 0x4E }, { 0x68, 0x45, 0x5F }, { 0x4F, 0x00, 0x3B },
2777 { 0x4F, 0x35, 0x49 }, { 0xFF, 0x00, 0x7F }, // 230
2778 { 0xFF, 0xAA, 0xD4 }, { 0xBD, 0x00, 0x5E }, { 0xBD, 0x7E, 0x9D },
2779 { 0x81, 0x00, 0x40 }, { 0x81, 0x56, 0x6B }, // 235
2780 { 0x68, 0x00, 0x34 }, { 0x68, 0x45, 0x56 }, { 0x4F, 0x00, 0x27 },
2781 { 0x4F, 0x35, 0x42 }, { 0xFF, 0x00, 0x3F }, // 240
2782 { 0xFF, 0xAA, 0xBF }, { 0xBD, 0x00, 0x2E }, { 0xBD, 0x7E, 0x8D },
2783 { 0x81, 0x00, 0x1F }, { 0x81, 0x56, 0x60 }, // 245
2784 { 0x68, 0x00, 0x19 }, { 0x68, 0x45, 0x4E }, { 0x4F, 0x00, 0x13 },
2785 { 0x4F, 0x35, 0x3B }, { 0x33, 0x33, 0x33 }, // 250
2786 { 0x50, 0x50, 0x50 }, { 0x69, 0x69, 0x69 }, { 0x82, 0x82, 0x82 },
2787 { 0xBE, 0xBE, 0xBE }, { 0xFF, 0xFF, 0xFF } // 255
2788 };
2789
dwg_rgb_palette(void)2790 EXPORT const Dwg_RGB_Palette *dwg_rgb_palette (void)
2791 {
2792 return rgb_palette;
2793 }
2794
dwg_rgb_palette_index(BITCODE_BS index)2795 EXPORT BITCODE_BL dwg_rgb_palette_index (BITCODE_BS index)
2796 {
2797 // BS in unsigned
2798 if (index >= 256)
2799 return 0;
2800 else
2801 {
2802 Dwg_RGB_Palette rgb;
2803 assert (index < 256);
2804 rgb = rgb_palette[index];
2805 return (rgb.r << 16) | (rgb.g << 8) | rgb.b;
2806 }
2807 }
2808
dwg_find_color_index(BITCODE_BL rgb)2809 EXPORT BITCODE_BS dwg_find_color_index (BITCODE_BL rgb)
2810 {
2811 Dwg_RGB_Palette pal;
2812 rgb &= 0x00ffffff;
2813 pal.r = rgb & 0xff0000;
2814 pal.g = rgb & 0x00ff00;
2815 pal.b = rgb & 0x0000ff;
2816 // linear search is good enough for 256. the palette is unsorted.
2817 for (BITCODE_BS i = 0; i < 256; i++)
2818 {
2819 if (memcmp (&pal, &rgb_palette[i], 3) == 0)
2820 return i;
2821 }
2822 return 256;
2823 }
2824
2825 // map [rVER] to our enum number, not the dwg->header.dwgversion
2826 // Acad 2018 offers SaveAs DWG: 2018,2013,2010,2007,2004,2004,2000,r14
2827 // DXF: 2018,2013,2010,2007,2004,2004,2000,r12
2828 // libdxfrw dwg2dxf offers R12, v2000, v2004, v2007, v2010
2829 EXPORT Dwg_Version_Type
dwg_version_as(const char * version)2830 dwg_version_as (const char *version)
2831 {
2832 if (strEQc (version, "r2000"))
2833 return R_2000;
2834 else if (strEQc (version, "r2004"))
2835 return R_2004;
2836 else if (strEQc (version, "r2007"))
2837 return R_2007;
2838 else if (strEQc (version, "r2010"))
2839 return R_2010;
2840 else if (strEQc (version, "r2013"))
2841 return R_2013;
2842 else if (strEQc (version, "r2018"))
2843 return R_2018;
2844 else if (strEQc (version, "r14"))
2845 return R_14;
2846 else if (strEQc (version, "r13"))
2847 return R_13;
2848 else if (strEQc (version, "r13c3"))
2849 return R_13c3;
2850 else if (strEQc (version, "r11") || strEQc (version, "r12"))
2851 return R_11;
2852 else if (strEQc (version, "r10"))
2853 return R_10;
2854 else if (strEQc (version, "r9"))
2855 return R_9;
2856 else if (strEQc (version, "r2.6"))
2857 return R_2_6;
2858 else if (strEQc (version, "r2.5"))
2859 return R_2_5;
2860 else if (strEQc (version, "r2.4"))
2861 return R_2_4;
2862 else if (strEQc (version, "r2.1"))
2863 return R_2_1;
2864 else if (strEQc (version, "r2.0"))
2865 return R_2_0;
2866 else if (strEQc (version, "r1.4"))
2867 return R_1_4;
2868 else if (strEQc (version, "r1.3"))
2869 return R_1_3;
2870 else if (strEQc (version, "r1.2"))
2871 return R_1_2;
2872 else if (strEQc (version, "r1.1"))
2873 return R_1_1;
2874 else
2875 return R_INVALID;
2876 }
2877
2878 /** The reverse of dwg_version_as(char*) */
2879 const char *
dwg_version_type(const Dwg_Version_Type version)2880 dwg_version_type (const Dwg_Version_Type version)
2881 {
2882 switch (version)
2883 {
2884 case R_INVALID:
2885 return "invalid version";
2886 case R_1_1:
2887 return "r1.1";
2888 case R_1_2:
2889 return "r1.2";
2890 case R_1_3:
2891 return "r1.3";
2892 case R_1_4:
2893 return "r1.4";
2894 case R_1_402b:
2895 return "r1.402b";
2896 case R_2_0:
2897 return "r2.0";
2898 case R_2_1:
2899 return "r2.1";
2900 case R_2_21:
2901 return "r2.21";
2902 case R_2_22:
2903 return "r2.22";
2904 case R_2_4:
2905 return "r2.4";
2906 case R_2_5:
2907 return "r2.5";
2908 case R_2_6:
2909 return "r2.6";
2910 case R_9:
2911 return "r9";
2912 case R_9c1:
2913 return "r9c1";
2914 case R_10:
2915 return "r10";
2916 case R_10c1:
2917 return "r10c1";
2918 case R_10c2:
2919 return "r10c2";
2920 case R_11:
2921 return "r11";
2922 case R_12:
2923 return "r12";
2924 case R_12c1:
2925 return "r12c1";
2926 case R_13:
2927 return "r13";
2928 case R_13c3:
2929 return "r13c3";
2930 case R_14:
2931 return "r14";
2932 case R_2000:
2933 return "r2000";
2934 case R_2004:
2935 return "r2004";
2936 case R_2007:
2937 return "r2007";
2938 case R_2010:
2939 return "r2010";
2940 case R_2013:
2941 return "r2013";
2942 case R_2018:
2943 return "r2018";
2944 case R_AFTER:
2945 return "invalid after";
2946 default:
2947 return "";
2948 }
2949 }
2950
2951 // print errors as string to stderr
2952 EXPORT void
dwg_errstrings(int error)2953 dwg_errstrings (int error)
2954 {
2955 if (error & 1)
2956 HANDLER (OUTPUT, "WRONGCRC ");
2957 if (error & 2)
2958 HANDLER (OUTPUT, "NOTYETSUPPORTED ");
2959 if (error & 4)
2960 HANDLER (OUTPUT, "UNHANDLEDCLASS ");
2961 if (error & 8)
2962 HANDLER (OUTPUT, "INVALIDTYPE ");
2963 if (error & 16)
2964 HANDLER (OUTPUT, "INVALIDHANDLE ");
2965 if (error & 32)
2966 HANDLER (OUTPUT, "INVALIDEED ");
2967 if (error & 64)
2968 HANDLER (OUTPUT, "VALUEOUTOFBOUNDS ");
2969 // -- critical --
2970 if (error > 127)
2971 HANDLER (OUTPUT, "\nCritical: ");
2972 if (error & 128)
2973 HANDLER (OUTPUT, "CLASSESNOTFOUND ");
2974 if (error & 256)
2975 HANDLER (OUTPUT, "SECTIONNOTFOUND ");
2976 if (error & 512)
2977 HANDLER (OUTPUT, "PAGENOTFOUND ");
2978 if (error & 1024)
2979 HANDLER (OUTPUT, "INTERNALERROR ");
2980 if (error & 2048)
2981 HANDLER (OUTPUT, "INVALIDDWG ");
2982 if (error & 4096)
2983 HANDLER (OUTPUT, "IOERROR ");
2984 if (error & 8192)
2985 HANDLER (OUTPUT, "OUTOFMEM ");
2986 HANDLER (OUTPUT, "\n");
2987 }
2988
2989 // return name of color.method
2990 EXPORT const char*
dwg_color_method_name(unsigned m)2991 dwg_color_method_name (unsigned m)
2992 {
2993 switch (m) {
2994 case 0xc0: return "ByLayer";
2995 case 0xc1: return "ByBlock";
2996 case 0xc2: return "entity (default)";
2997 case 0xc3: return "Truecolor";
2998 case 0xc5: return "Foreground";
2999 case 0xc8: return "none";
3000 default: return "";
3001 }
3002 }
3003
3004 EXPORT unsigned long
dwg_next_handle(const Dwg_Data * dwg)3005 dwg_next_handle (const Dwg_Data *dwg)
3006 {
3007 BITCODE_H last_hdl;
3008 unsigned long seed = 0;
3009 // check the object map for the next available handle
3010 last_hdl = dwg->num_object_refs ? dwg->object_ref[ dwg->num_object_refs - 1] : NULL;
3011 if (last_hdl)
3012 {
3013 // find the largest handle
3014 seed = last_hdl->absolute_ref;
3015 //LOG_TRACE ("compute HANDSEED %lu ", seed);
3016 for (unsigned i = 0; i < dwg->num_object_refs; i++)
3017 {
3018 Dwg_Object_Ref *ref = dwg->object_ref[i];
3019 if (ref->absolute_ref > seed)
3020 seed = ref->absolute_ref;
3021 }
3022 return seed + 1;
3023 }
3024 else
3025 {
3026 Dwg_Object *obj = &dwg->object[dwg->num_objects - 1];
3027 return obj->handle.value + 1;
3028 }
3029 }
3030
3031 // on init some handles have holes on purpose.
3032 void dwg_set_next_hdl (Dwg_Data *dwg, const unsigned long value);
3033 void dwg_set_next_objhandle (Dwg_Object *obj);
3034
dwg_set_next_hdl(Dwg_Data * dwg,const unsigned long value)3035 void dwg_set_next_hdl (Dwg_Data *dwg, const unsigned long value)
3036 {
3037 dwg->next_hdl = value;
3038 }
3039
dwg_set_next_objhandle(Dwg_Object * obj)3040 void dwg_set_next_objhandle (Dwg_Object *obj)
3041 {
3042 Dwg_Data *dwg = obj->parent;
3043 if (!dwg->object_map)
3044 dwg->object_map = hash_new (200);
3045 if (dwg->next_hdl)
3046 {
3047 obj->handle.value = dwg->next_hdl;
3048 set_handle_size (&obj->handle);
3049 hash_set (dwg->object_map, obj->handle.value, (uint32_t)obj->index);
3050 dwg->next_hdl = 0;
3051 return;
3052 }
3053 if (!dwg->num_objects)
3054 {
3055 obj->handle.size = 1;
3056 obj->handle.value = 1UL;
3057 }
3058 else
3059 {
3060 Dwg_Object *lastobj = &dwg->object[dwg->num_objects - 1];
3061 /* ADD_OBJECT might have just added a zeroed object */
3062 if (!lastobj->handle.value && dwg->num_objects > 1)
3063 lastobj = &dwg->object[dwg->num_objects - 2];
3064 obj->handle.value = lastobj->handle.value + 1;
3065 set_handle_size (&obj->handle);
3066 }
3067 hash_set (dwg->object_map, obj->handle.value, (uint32_t)obj->index);
3068 dwg->next_hdl = 0;
3069 return;
3070 }
3071
3072 // <path-to>/dxf.ext => copy of "dxf", "ext"
3073 // returns a malloc'ed copy of basename without extension, and
3074 // sets ext to the char behind the last "." of basename
split_filepath(const char * filepath,char ** extp)3075 char *split_filepath (const char *filepath, char **extp)
3076 {
3077 char *copy, *base, *dot;
3078 //int len;
3079 if (!filepath)
3080 return NULL;
3081 copy = strdup (filepath);
3082 #ifdef HAVE_BASENAME
3083 base = basename (copy);
3084 #else
3085 base = strrchr (copy, '/');
3086 #endif
3087 if (!base)
3088 base = copy;
3089 //len = strlen (base);
3090 if ((dot = strrchr (base, '.')) && *dot)
3091 {
3092 *extp = dot + 1;
3093 *dot = '\0';
3094 }
3095 return base;
3096 }
3097