/*****************************************************************************/ /* LibreDWG - free implementation of the DWG file format */ /* */ /* Copyright (C) 2018-2021 Free Software Foundation, Inc. */ /* */ /* This library is free software, licensed under the terms of the GNU */ /* General Public License as published by the Free Software Foundation, */ /* either version 3 of the License, or (at your option) any later version. */ /* You should have received a copy of the GNU General Public License */ /* along with this program. If not, see . */ /*****************************************************************************/ /* * out_dxf.c: write as Ascii DXF * written by Reini Urban */ /* Works mostly. TODO: * down-conversions from unsupported entities on older DXF versions. Since r13: Entities: LWPOLYLINE, HATCH, SPLINE, LEADER, DIMENSION, MTEXT, IMAGE, BLOCK_RECORD. Add CLASSES for those. */ #include "config.h" #include #include #include #include //#include #define IS_DXF #include "common.h" #include "bits.h" #include "myalloca.h" #include "dwg.h" #include "decode.h" #include "encode.h" #include "out_dxf.h" static unsigned int loglevel; #define DWG_LOGLEVEL loglevel #include "logging.h" /* the current version per spec block */ static unsigned int cur_ver = 0; static char buf[255]; static BITCODE_BL rcount1, rcount2; // imported char *dwg_obj_table_get_name (const Dwg_Object *restrict obj, int *restrict error); #ifndef _DWG_API_H_ Dwg_Object *dwg_obj_generic_to_object (const void *restrict obj, int *restrict error); #endif // private static int dxf_common_entity_handle_data (Bit_Chain *restrict dat, const Dwg_Object *restrict obj); static int dwg_dxf_object (Bit_Chain *restrict dat, const Dwg_Object *restrict obj, int *restrict); static int dxf_3dsolid (Bit_Chain *restrict dat, const Dwg_Object *restrict obj, Dwg_Entity_3DSOLID *restrict _obj); static void dxf_fixup_string (Bit_Chain *restrict dat, char *restrict str, const int opts, const int dxf, const int dxfcont); static void dxf_CMC (Bit_Chain *restrict dat, Dwg_Color *restrict color, const int dxf, const int opt); /*-------------------------------------------------------------------------------- * MACROS */ #define ACTION dxf #define FIELD(nam, type) VALUE (_obj->nam, type, 0) #define FIELDG(nam, type, dxf) VALUE (_obj->nam, type, dxf) #define FIELD_CAST(nam, type, cast, dxf) FIELDG (nam, cast, dxf) #define FIELD_TRACE(nam, type) #define SUB_FIELD(o, nam, type, dxf) FIELDG (o.nam, type, dxf) #define SUB_FIELD_CAST(o, nam, type, cast, dxf) FIELDG (o.nam, cast, dxf) #define VALUE_TV(value, dxf) \ { \ GROUP (dxf); \ dxf_fixup_string (dat, (char *)value, 1, dxf, dxf); \ } #define VALUE_TV0(value, dxf) \ if (dxf && value && *value) { \ GROUP (dxf); \ dxf_fixup_string (dat, (char *)value, 1, dxf, dxf); \ } // in_json writes all strings as TV, in_dxf and decode not. #define VALUE_TU(wstr, dxf) \ { \ if (dat->opts & DWG_OPTS_INJSON) \ { \ VALUE_TV (wstr, dxf); \ } \ else if (dxf) \ { \ char *u8 = bit_convert_TU ((BITCODE_TU)wstr); \ GROUP (dxf); \ if (u8) \ { \ dxf_fixup_string (dat, u8, 1, dxf, dxf); \ } \ else \ fprintf (dat->fh, "\r\n"); \ free (u8); \ } \ } #define VALUE_TFF(str, dxf) \ { \ if (dxf) \ { \ GROUP (dxf); \ dxf_fixup_string (dat, (char *)str, 0, dxf, dxf); \ } \ } #define VALUE_BINARY(value, size, dxf) \ { \ if (value && dxf) \ { \ for (unsigned long j = 0; j < (unsigned long)(size); j++) \ { \ if (!(j % 127)) \ { \ if (j) \ fprintf (dat->fh, "\r\n"); \ GROUP (dxf); \ } \ fprintf (dat->fh, "%02X", (value)[j]); \ } \ fprintf (dat->fh, "\r\n"); \ } \ } #define FIELD_BINARY(name, size, dxf) \ if (dxf) \ VALUE_BINARY (_obj->name, size, dxf) #define FIELD_VALUE(nam) _obj->nam #define ANYCODE -1 // the hex code #define VALUE_HANDLE(ref, nam, handle_code, dxf) \ if (dxf) \ { \ fprintf (dat->fh, "%3i\r\n%lX\r\n", dxf, \ ref ? ((BITCODE_H)ref)->absolute_ref : 0UL); \ } // the name in the table, referenced by the handle // names on: 6 7 8. which else? there are more styles: plot, ... // rather skip unknown handles #define FIELD_HANDLE(nam, handle_code, dxf) \ if (dxf != 0) \ { \ if (!_obj->nam) \ fprintf (dat->fh, "%3i\r\n%lX\r\n", dxf, 0UL); \ else if (dxf == 6) \ FIELD_HANDLE_NAME (nam, dxf, LTYPE) \ else if (dxf == 2) \ FIELD_HANDLE_NAME (nam, dxf, BLOCK_HEADER) \ else if (dxf == 3) \ FIELD_HANDLE_NAME (nam, dxf, DIMSTYLE) \ else if (dxf == 7) \ FIELD_HANDLE_NAME (nam, dxf, STYLE) \ else if (dxf == 8) \ FIELD_HANDLE_NAME (nam, dxf, LAYER) \ else if (dat->version >= R_13) \ fprintf (dat->fh, "%3i\r\n%lX\r\n", dxf, \ _obj->nam->obj ? _obj->nam->absolute_ref : 0UL); \ } #define SUB_FIELD_HANDLE(o, nam, handle_code, dxf) \ if (dxf != 0) \ { \ if (!_obj->o.nam) \ fprintf (dat->fh, "%3i\r\n%lX\r\n", dxf, 0UL); \ else if (dxf == 6) \ SUB_FIELD_HANDLE_NAME (o, nam, dxf, LTYPE) \ else if (dxf == 3) \ SUB_FIELD_HANDLE_NAME (o, nam, dxf, DIMSTYLE) \ else if (dxf == 7) \ SUB_FIELD_HANDLE_NAME (o, nam, dxf, STYLE) \ else if (dxf == 8) \ SUB_FIELD_HANDLE_NAME (o, nam, dxf, LAYER) \ else if (dat->version >= R_13) \ fprintf (dat->fh, "%3i\r\n%lX\r\n", dxf, \ _obj->o.nam->obj ? _obj->o.nam->absolute_ref : 0UL); \ } #define FIELD_HANDLE0(nam, handle_code, dxf) \ if (_obj->nam && _obj->nam->absolute_ref) \ { \ FIELD_HANDLE (nam, handle_code, dxf); \ } #define SUB_FIELD_HANDLE0(o, nam, handle_code, dxf) \ if (_obj->o.nam && _obj->o.nam->absolute_ref) \ { \ SUB_FIELD_HANDLE (o, nam, handle_code, dxf); \ } #define HEADER_9(nam) \ { \ GROUP (9); \ fprintf (dat->fh, "$%s\r\n", #nam); \ } #define VALUE_H(value, dxf) \ if (dxf) \ fprintf (dat->fh, "%3i\r\n%lX\r\n", dxf, value) #define HEADER_H(nam, dxf) \ { \ HEADER_9 (nam); \ VALUE_HANDLE (dwg->header_vars.nam, nam, 0, dxf); \ } #define HEADER_H0(nam, dxf) \ if (dwg->header_vars.nam && dwg->header_vars.nam->absolute_ref) \ { \ HEADER_9 (nam); \ VALUE_H (dwg->header_vars.nam->absolute_ref, dxf); \ } #define HEADER_VALUE(nam, type, dxf, value) \ if (dxf) \ { \ GROUP (9); \ fprintf (dat->fh, "$" #nam "\r\n"); \ VALUE (value, type, dxf); \ } #define HEADER_VAR(nam, type, dxf) \ HEADER_VALUE (nam, type, dxf, dwg->header_vars.nam) #define HEADER_VALUE_TV(nam, dxf, value) \ if (dxf) \ { \ GROUP (9); \ fprintf (dat->fh, "$" #nam "\r\n"); \ VALUE_TV (value, dxf); \ } #define HEADER_VALUE_TU(nam, dxf, value) \ if (dxf) \ { \ GROUP (9); \ fprintf (dat->fh, "$" #nam "\r\n"); \ VALUE_TU (value, dxf); \ } #define HEADER_VALUE_TU0(nam, dxf, value) \ if (dxf && !bit_empty_T (dat, (BITCODE_T)value)) \ { \ GROUP (9); \ fprintf (dat->fh, "$" #nam "\r\n"); \ VALUE_TU (value, dxf); \ } #define HEADER_3D(nam) \ { \ HEADER_9 (nam); \ POINT_3D (nam, header_vars.nam, 10, 20, 30); \ } #define HEADER_2D(nam) \ { \ HEADER_9 (nam); \ POINT_2D (nam, header_vars.nam, 10, 20); \ } #define HEADER_BLL(nam, dxf) \ { \ HEADER_9 (nam); \ VALUE_BLL (dwg->header_vars.nam, dxf); \ } #define SECTION(section) \ LOG_INFO ("\nSection " #section "\n") \ fprintf (dat->fh, " 0\r\nSECTION\r\n 2\r\n" #section "\r\n") #define ENDSEC() fprintf (dat->fh, " 0\r\nENDSEC\r\n") #define TABLE(table) fprintf (dat->fh, " 0\r\nTABLE\r\n 2\r\n" #table "\r\n") #define ENDTAB() fprintf (dat->fh, " 0\r\nENDTAB\r\n") #define RECORD(record) fprintf (dat->fh, " 0\r\n" #record "\r\n") #define record(record) fprintf (dat->fh, " 0\r\n%s\r\n", record) #define SUBCLASS(text) \ if (dat->version >= R_13) \ { \ VALUE_TV (#text, 100); \ } #define GROUP(dxf) fprintf (dat->fh, "%3i\r\n", dxf) /* avoid empty numbers, and fixup some bad %f GNU/BSD libc formatting */ #define VALUE(value, type, dxf) \ if (dxf) \ { \ const char *_fmt = dxf_format (dxf); \ assert (_fmt); \ if (strEQc (_fmt, DXF_FORMAT_FLT)) \ { \ dxf_print_rd (dat, (double)(value), dxf); \ } \ else \ { \ /* -Wpointer-to-int-cast */ \ const int32_t _si = (int32_t) (intptr_t) (value); \ GROUP (dxf); \ GCC46_DIAG_IGNORE (-Wformat-nonliteral) \ snprintf (buf, 255, _fmt, value); \ GCC46_DIAG_RESTORE \ /* not a string, empty num. must be zero */ \ if (strEQc (_fmt, "%s") && !*buf) \ fprintf (dat->fh, "0\r\n"); \ else if (90 <= dxf && dxf < 100) \ { \ fprintf (dat->fh, "%9i\r\n", _si); \ } \ else \ fprintf (dat->fh, "%s\r\n", buf); \ } \ } static void dxf_print_rd (Bit_Chain *dat, BITCODE_RD value, int dxf) { if (dxf) { char _buf[128]; int k; fprintf (dat->fh, "%3i\r\n", dxf); #ifndef DEBUG_CLASSES if (bit_isnan (value)) value = 0.0; #endif snprintf (_buf, 127, DXF_FORMAT_FLT, value); k = strlen (_buf); if (strrchr (_buf, '.') && _buf[k - 1] == '0') { for (k--; k > 1 && _buf[k - 1] != '.' && _buf[k] == '0'; k--) _buf[k] = '\0'; } // max len 17 resp. 18 with - if (value < 0.0) _buf[DXF_FLT_MAXLEN+1] = '\0'; else _buf[DXF_FLT_MAXLEN] = '\0'; fprintf (dat->fh, "%s\r\n", _buf); } } #define VALUE_BSd(value, dxf) \ if (dxf) \ { \ GROUP (dxf); \ fprintf (dat->fh, "%6i\r\n", value); \ } #define VALUE_RD(value, dxf) dxf_print_rd (dat, value, dxf) #define VALUE_B(value, dxf) \ if (dxf) \ { \ GROUP (dxf); \ if (value == 0) \ fprintf (dat->fh, " 0\r\n"); \ else \ fprintf (dat->fh, " 1\r\n"); \ } #define FIELD_HANDLE_NAME(nam, dxf, table) \ { \ Dwg_Object_Ref *ref = _obj->nam; \ Dwg_Object *o = ref ? ref->obj : NULL; \ if (o && strEQc (o->dxfname, #table)) \ dxf_cvt_tablerecord ( \ dat, o, o ? o->tio.object->tio.table->name : (char *)"0", dxf); \ else \ { \ VALUE_TFF ("", dxf) \ } \ } #define SUB_FIELD_HANDLE_NAME(ob, nam, dxf, table) \ { \ Dwg_Object_Ref *ref = _obj->ob.nam; \ Dwg_Object *o = ref ? ref->obj : NULL; \ if (o && strEQc (o->dxfname, #table)) \ dxf_cvt_tablerecord ( \ dat, o, o ? o->tio.object->tio.table->name : (char *)"0", dxf); \ else \ { \ VALUE_TFF ("", dxf) \ } \ } #define HEADER_HANDLE_NAME(nam, dxf, table) \ HEADER_9 (nam); \ FIELD_HANDLE_NAME (nam, dxf, table) #define FIELD_DATAHANDLE(nam, code, dxf) \ { \ Dwg_Object_Ref *ref = _obj->nam; \ char s[16]; \ HEADER_9 (nam); \ VALUE_H (ref ? ref->handleref.value : 0UL, dxf); \ } #define HEADER_RC(nam, dxf) \ HEADER_9 (nam); \ FIELDG (nam, RC, dxf) #define HEADER_RC0(nam, dxf) \ if (FIELD_VALUE (nam)) \ { \ HEADER_9 (nam); \ FIELDG (nam, RC, dxf); \ } #define HEADER_RS(nam, dxf) \ { \ HEADER_9 (nam); \ FIELDG (nam, RS, dxf); \ } #define HEADER_RS0(nam, dxf) \ if (FIELD_VALUE (nam)) \ { \ HEADER_9 (nam); \ FIELDG (nam, RS, dxf); \ } #define HEADER_RD(nam, dxf) \ { \ HEADER_9 (nam); \ FIELD_RD (nam, dxf); \ } #define HEADER_RL(nam, dxf) \ { \ HEADER_9 (nam); \ FIELDG (nam, RL, dxf); \ } #define HEADER_RLL(nam, dxf) \ HEADER_9 (nam); \ FIELDG (nam, RLL, dxf) #define HEADER_TV(nam, dxf) \ HEADER_9 (nam); \ VALUE_TV (_obj->nam, dxf) #define HEADER_TU(nam, dxf) \ HEADER_9 (nam); \ VALUE_TU (_obj->nam, dxf) #define HEADER_T(nam, dxf) \ HEADER_9 (nam); \ VALUE_T ((char *)_obj->nam, dxf) #define HEADER_T0(nam, dxf) \ if (dxf && !bit_empty_T (dat, _obj->nam)) \ { \ HEADER_9 (nam); \ VALUE_T ((char*)_obj->nam, dxf); \ } #define HEADER_B(nam, dxf) \ HEADER_9 (nam); \ FIELD_B (nam, dxf) #define HEADER_BS(nam, dxf) \ HEADER_9 (nam); \ FIELDG (nam, BS, dxf) #define HEADER_BSd(nam, dxf) HEADER_BS(nam, dxf) #define HEADER_BD(nam, dxf) \ HEADER_9 (nam); \ FIELD_BD (nam, dxf) #define HEADER_BL(nam, dxf) \ HEADER_9 (nam); \ FIELDG (nam, BL, dxf) #define HEADER_BLd(nam, dxf) \ HEADER_9 (nam); \ FIELDG (nam, BLd, dxf) #define VALUE_BB(value, dxf) VALUE (value, RC, dxf) #define VALUE_3B(value, dxf) VALUE (value, RC, dxf) #define VALUE_BS(value, dxf) VALUE (value, RS, dxf) #define VALUE_BL(value, dxf) VALUE (value, BL, dxf) #define VALUE_BLL(value, dxf) VALUE (value, RLL, dxf) #define VALUE_BD(value, dxf) \ { \ if (dxf >= 50 && dxf < 55) \ { \ BITCODE_RD _f = rad2deg (value); \ VALUE_RD (_f, dxf); \ } \ else \ { \ VALUE_RD (value, dxf); \ } \ } #define VALUE_RC(value, dxf) VALUE (value, RC, dxf) #define VALUE_RS(value, dxf) VALUE (value, RS, dxf) #define VALUE_RL(value, dxf) VALUE (value, RL, dxf) #define VALUE_RLd(value, dxf) VALUE (value, RL, dxf) #define VALUE_RLL(value, dxf) VALUE (value, RLL, dxf) #define VALUE_MC(value, dxf) VALUE (value, MC, dxf) #define VALUE_MS(value, dxf) VALUE (value, MS, dxf) #define VALUE_2RD(pt, dxf) \ { \ VALUE_RD (pt.x, dxf); \ VALUE_RD (pt.y, dxf + 10); \ } #define VALUE_3BD(pt, dxf) \ { \ VALUE_RD (pt.x, dxf); \ VALUE_RD (pt.y, dxf + 10); \ VALUE_RD (pt.z, dxf + 20); \ } #define FIELD_RD(nam, dxf) VALUE_RD (_obj->nam, dxf) #define FIELD_B(nam, dxf) VALUE_B (_obj->nam, dxf) #define FIELD_BB(nam, dxf) FIELDG (nam, BB, dxf) #define FIELD_3B(nam, dxf) FIELDG (nam, 3B, dxf) #define FIELD_BS(nam, dxf) FIELDG (nam, BS, dxf) #define FIELD_BSd(nam, dxf) FIELDG (nam, BSd, dxf) #define FIELD_BL(nam, dxf) FIELDG (nam, BL, dxf) #define FIELD_BLL(nam, dxf) FIELDG (nam, BLL, dxf) #define FIELD_BD(nam, dxf) VALUE_BD (_obj->nam, dxf) #define FIELD_RC(nam, dxf) FIELDG (nam, RC, dxf) #define FIELD_RS(nam, dxf) FIELDG (nam, RS, dxf) #define FIELD_RL(nam, dxf) FIELDG (nam, RL, dxf) #define FIELD_RLL(nam, dxf) FIELDG (nam, RLL, dxf) #define FIELD_MC(nam, dxf) FIELDG (nam, MC, dxf) #define FIELD_MS(nam, dxf) FIELDG (nam, MS, dxf) #define FIELD_TF(nam, len, dxf) VALUE_TV (_obj->nam, dxf) #define FIELD_TFF(nam, len, dxf) VALUE_TV (_obj->nam, dxf) #define FIELD_TV(nam, dxf) \ if (dxf) \ { \ VALUE_TV (_obj->nam, dxf); \ } #define FIELD_TU(nam, dxf) \ if (dxf) \ { \ VALUE_TU ((BITCODE_TU)_obj->nam, dxf); \ } #define FIELD_T(nam, dxf) \ { \ if (IS_FROM_TU (dat)) \ { \ FIELD_TU (nam, dxf); \ } \ else \ { \ FIELD_TV (nam, dxf); \ } \ } #define VALUE_T(value, dxf) \ { \ if (IS_FROM_TU (dat)) \ { \ VALUE_TU (value, dxf); \ } \ else \ { \ VALUE_TV (value, dxf); \ } \ } #define VALUE_T0(value, dxf) \ if (!bit_empty_T (dat, value)) VALUE_T (value,dxf) #define FIELD_BT(nam, dxf) FIELDG (nam, BT, dxf); #define FIELD_4BITS(nam, dxf) FIELDG (nam, 4BITS, dxf) #define FIELD_BE(nam, dxf) \ { \ if (dxf && !(_obj->nam.x == 0.0 && _obj->nam.y == 0.0 && _obj->nam.z == 1.0)) \ FIELD_3RD (nam, dxf) \ } // skip if 0 #define FIELD_RD0(nam, dxf) FIELD_BD0(nam, dxf) #define FIELD_BD0(nam, dxf) \ if (dxf) { \ if (_obj->nam != 0.0) \ FIELD_BD (nam, dxf) \ } // for scale (1.0, 1.0, 1.0) and width_factor #define FIELD_RD1(nam, dxf) FIELD_BD1(nam, dxf) #define FIELD_BD1(nam, dxf) \ if (dxf) { \ if (_obj->nam != 1.0) \ FIELD_BD (nam, dxf) \ } #define FIELD_BL0(nam, dxf) \ if (dxf) { \ if (_obj->nam != 0) \ FIELD_BL (nam, dxf) \ } #define FIELD_BS0(nam, dxf) \ if (dxf) { \ if (_obj->nam != 0) \ FIELD_BS (nam, dxf) \ } #define FIELD_BS1(nam, dxf) \ if (dxf) { \ if (_obj->nam != 1) \ FIELD_BS (nam, dxf) \ } #define FIELD_B0(nam, dxf) \ if (dxf) { \ if (_obj->nam) \ FIELD_B (nam, dxf) \ } #define FIELD_RC0(nam, dxf) \ { \ if (_obj->nam != 0) \ FIELD_RC (nam, dxf) \ } #define FIELD_RS0(nam, dxf) \ { \ if (_obj->nam != 0) \ FIELD_RS (nam, dxf) \ } #define FIELD_RL0(nam, dxf) \ { \ if (_obj->nam != 0) \ FIELD_RL (nam, dxf) \ } #define FIELD_BT0(nam, dxf) \ { \ if (_obj->nam != 0) \ FIELD_BT (nam, dxf) \ } #define FIELD_T0(nam, dxf) \ { \ if (_obj->nam) \ { \ if (IS_FROM_TU (dat)) \ { \ char *u8 = bit_convert_TU ((BITCODE_TU)_obj->nam); \ if (u8 && *u8) \ { \ GROUP (dxf); \ fprintf (dat->fh, "%s\r\n", u8); \ } \ free (u8); \ } \ else if (*_obj->nam) \ { \ FIELD_TV (nam, dxf); \ } \ } \ } #define FIELD_TV0(nam, dxf) VALUE_TV0 (_obj->nam, dxf) #define SUB_FIELD_BL0(o, nam, dxf) \ { \ if (_obj->o.nam != 0) \ SUB_FIELD_BL (o, nam, dxf) \ } #define FIELD_DD(nam, _default, dxf) FIELD_BD (nam, dxf) #define FIELD_2DD(nam, def, dxf) \ if (dxf) { \ FIELD_DD (nam.x, FIELD_VALUE (def.x), dxf); \ FIELD_DD (nam.y, FIELD_VALUE (def.y), dxf + 10); \ } #define FIELD_3DD(nam, def, dxf) \ if (dxf) { \ FIELD_DD (nam.x, FIELD_VALUE (def.x), dxf); \ FIELD_DD (nam.y, FIELD_VALUE (def.y), dxf + 10); \ FIELD_DD (nam.z, FIELD_VALUE (def.z), dxf + 20); \ } #define FIELD_2RD(nam, dxf) \ if (dxf) { \ FIELD_RD (nam.x, dxf); \ FIELD_RD (nam.y, dxf + 10); \ } #define FIELD_2RD0(nam, dxf) \ if (dxf && _obj->nam.x != 0.0 && _obj->nam.y != 0.0) { \ FIELD_RD (nam.x, dxf); \ FIELD_RD (nam.y, dxf + 10); \ } #define FIELD_2BD(nam, dxf) \ if (dxf) { \ FIELD_BD (nam.x, dxf); \ FIELD_BD (nam.y, dxf + 10); \ } #define FIELD_2BD_1(nam, dxf) \ if (dxf) { \ FIELD_BD (nam.x, dxf); \ FIELD_BD (nam.y, dxf + 1); \ } #define FIELD_3RD(nam, dxf) \ if (dxf) { \ FIELD_RD (nam.x, dxf); \ FIELD_RD (nam.y, dxf + 10); \ FIELD_RD (nam.z, dxf + 20); \ } #define FIELD_3BD(nam, dxf) \ if (dxf) { \ FIELD_BD (nam.x, dxf); \ FIELD_BD (nam.y, dxf + 10); \ FIELD_BD (nam.z, dxf + 20); \ } #define FIELD_3BD_1(nam, dxf) \ if (dxf) { \ FIELD_BD (nam.x, dxf); \ FIELD_BD (nam.y, dxf + 1); \ FIELD_BD (nam.z, dxf + 2); \ } #define FIELD_3DPOINT(nam, dxf) FIELD_3BD (nam, dxf) #define FIELD_CMC(color, dxf) dxf_CMC (dat, (Dwg_Color*)&_obj->color, dxf, 0) #define SUB_FIELD_CMC(o, color, dxf) dxf_CMC (dat, (Dwg_Color*)&_obj->o.color, dxf, 0) #define FIELD_CMC0(color, dxf) dxf_CMC (dat, (Dwg_Color*)&_obj->color, dxf, 1) #define HEADER_TIMEBLL(nam, dxf) { \ HEADER_9 (nam); \ FIELD_TIMEBLL (nam, dxf); \ } #define FIELD_TIMEBLL(nam, dxf) \ GROUP (dxf); \ fprintf (dat->fh, "%.09f\r\n", _obj->nam.value) #define HEADER_CMC(nam, dxf) \ HEADER_9 (nam); \ VALUE_RS (dwg->header_vars.nam.index, dxf) #define POINT_3D(nam, var, c1, c2, c3) \ { \ VALUE_RD (dwg->var.x, c1); \ VALUE_RD (dwg->var.y, c2); \ VALUE_RD (dwg->var.z, c3); \ } #define POINT_2D(nam, var, c1, c2) \ { \ VALUE_RD (dwg->var.x, c1); \ VALUE_RD (dwg->var.y, c2); \ } // FIELD_VECTOR_N(nam, type, size): // reads data of the type indicated by 'type' 'size' times and stores // it all in the vector called 'nam'. #define FIELD_VECTOR_N(nam, type, size, dxf) \ if (dxf && _obj->nam) \ { \ for (vcount = 0; vcount < (BITCODE_BL)size; vcount++) \ { \ VALUE_##type (_obj->nam[vcount], dxf); \ } \ } #define FIELD_VECTOR_N1(nam, type, size, dxf) \ if (dxf && _obj->nam) \ { \ int _dxf = dxf; \ for (vcount = 0; vcount < (BITCODE_BL)size; _dxf++, vcount++) \ { \ VALUE_##type (_obj->nam[vcount], _dxf); \ } \ } #define FIELD_VECTOR_T(nam, type, size, dxf) \ if (dxf && _obj->nam) \ { \ if (IS_FROM_TU (dat)) \ { \ for (vcount = 0; vcount < (BITCODE_BL)_obj->size; vcount++) \ VALUE_TU (_obj->nam[vcount], dxf); \ } \ else \ { \ for (vcount = 0; vcount < (BITCODE_BL)_obj->size; vcount++) \ VALUE_TV (_obj->nam[vcount], dxf); \ } \ } #define FIELD_VECTOR(nam, type, size, dxf) \ FIELD_VECTOR_N (nam, type, _obj->size, dxf) #define FIELD_2RD_VECTOR(nam, size, dxf) \ if (dxf && _obj->nam) \ { \ for (vcount = 0; vcount < (BITCODE_BL)_obj->size; vcount++) \ { \ FIELD_2RD (nam[vcount], dxf); \ } \ } #define FIELD_2DD_VECTOR(nam, size, dxf) \ FIELD_2RD (nam[0], dxf); \ if (dxf && _obj->nam) \ { \ for (vcount = 1; vcount < (BITCODE_BL)_obj->size; vcount++) \ { \ FIELD_2DD (nam[vcount], nam[vcount - 1], dxf); \ } \ } #define FIELD_3DPOINT_VECTOR(nam, size, dxf) \ if (dxf) \ { \ for (vcount = 0; vcount < (BITCODE_BL)_obj->size; vcount++) \ { \ FIELD_3DPOINT (nam[vcount], dxf); \ } \ } #define VALUE_HANDLE_N(hdlptr, nam, vcount, handle_code, dxf) \ if (dxf && hdlptr && size) \ { \ for (vcount = 0; vcount < (BITCODE_BL)size; vcount++) \ { \ VALUE_HANDLE (hdlptr[vcount], nam, handle_code, dxf); \ } \ } #define FIELD_HANDLE_N(nam, size, handle_code, dxf) \ VALUE_HANDLE (_obj->nam, nam, handle_code, dxf) #define HANDLE_VECTOR_N(nam, size, code, dxf) \ if (dxf && _obj->nam && size) \ { \ for (vcount = 0; vcount < (BITCODE_BL)size; vcount++) \ { \ FIELD_HANDLE (nam[vcount], code, dxf); \ } \ } #define HANDLE_VECTOR(nam, sizefield, code, dxf) \ HANDLE_VECTOR_N (nam, FIELD_VALUE (sizefield), code, dxf) #define FIELD_NUM_INSERTS(num_inserts, type, dxf) \ FIELDG (num_inserts, type, dxf) #define FIELD_XDATA(nam, size) \ dxf_write_xdata (dat, obj, _obj->nam, _obj->size) #define _XDICOBJHANDLE(code) \ if (dat->version >= R_13 && obj->tio.object->xdicobjhandle \ && obj->tio.object->xdicobjhandle->absolute_ref) \ { \ fprintf (dat->fh, "102\r\n{ACAD_XDICTIONARY\r\n"); \ VALUE_HANDLE (obj->tio.object->xdicobjhandle, xdicobjhandle, code, \ 360); \ fprintf (dat->fh, "102\r\n}\r\n"); \ } #define _REACTORS(code) \ if (dat->version >= R_13 && obj->tio.object->num_reactors \ && obj->tio.object->reactors) \ { \ fprintf (dat->fh, "102\r\n{ACAD_REACTORS\r\n"); \ for (vcount = 0; vcount < obj->tio.object->num_reactors; vcount++) \ { /* soft ptr */ \ VALUE_HANDLE (obj->tio.object->reactors[vcount], reactors, code, \ 330); \ } \ fprintf (dat->fh, "102\r\n}\r\n"); \ } #define ENT_REACTORS(code) \ if (dat->version >= R_13 && _obj->num_reactors && _obj->reactors) \ { \ fprintf (dat->fh, "102\r\n{ACAD_REACTORS\r\n"); \ for (vcount = 0; vcount < _obj->num_reactors; vcount++) \ { \ VALUE_HANDLE (_obj->reactors[vcount], reactors, code, 330); \ } \ fprintf (dat->fh, "102\r\n}\r\n"); \ } #define REACTORS(code) #define XDICOBJHANDLE(code) #define ENT_XDICOBJHANDLE(code) \ if (dat->version >= R_13 && obj->tio.entity->xdicobjhandle \ && obj->tio.entity->xdicobjhandle->absolute_ref) \ { \ fprintf (dat->fh, "102\r\n{ACAD_XDICTIONARY\r\n"); \ VALUE_HANDLE (obj->tio.entity->xdicobjhandle, xdicobjhandle, code, \ 360); \ fprintf (dat->fh, "102\r\n}\r\n"); \ } #define BLOCK_NAME(nam, dxf) dxf_cvt_blockname (dat, _obj->nam, dxf) #define COMMON_ENTITY_HANDLE_DATA #define SECTION_STRING_STREAM #define START_STRING_STREAM #define END_STRING_STREAM #define START_HANDLE_STREAM #ifndef DEBUG_CLASSES static int dwg_dxf_TABLECONTENT (Bit_Chain *restrict dat, const Dwg_Object *restrict obj) { (void)dat; (void)obj; return 0; } #else static int dwg_dxf_TABLECONTENT (Bit_Chain *restrict dat, const Dwg_Object *restrict obj); #endif // The strcmp is being optimized away at compile-time! // https://godbolt.org/g/AqkhwL #define DWG_ENTITY(token) \ static int dwg_dxf_##token##_private ( \ Bit_Chain *dat, Bit_Chain *hdl_dat, Bit_Chain *str_dat, \ const Dwg_Object *restrict obj); \ static int dwg_dxf_##token (Bit_Chain *restrict dat, \ const Dwg_Object *restrict obj) \ { \ int error = 0; \ Bit_Chain *hdl_dat = dat; \ Bit_Chain *str_dat = dat; \ if (obj->fixedtype != DWG_TYPE_##token) \ { \ LOG_ERROR ("Invalid type 0x%x, expected 0x%x %s", obj->fixedtype, \ DWG_TYPE_##token, #token); \ return DWG_ERR_INVALIDTYPE; \ } \ if (strEQc (#token, "GEOPOSITIONMARKER")) \ RECORD (POSITIONMARKER); \ else if (dat->version < R_13 && strlen (#token) == 10 \ && strEQc (#token, "LWPOLYLINE")) \ RECORD (POLYLINE); \ else if (strlen (#token) > 10 && !memcmp (#token, "DIMENSION_", 10)) \ RECORD (DIMENSION); \ else if (strlen (#token) > 9 && !memcmp (#token, "POLYLINE_", 9)) \ RECORD (POLYLINE); \ else if (strlen (#token) > 7 && !memcmp (#token, "VERTEX_", 7)) \ RECORD (VERTEX); \ else if (strEQc (#token, "MINSERT")) \ RECORD (INSERT); \ else if (dat->version >= R_2010 && strEQc (#token, "TABLE")) \ { \ RECORD (ACAD_TABLE); \ return dwg_dxf_TABLECONTENT (dat, obj); \ } \ else if (strlen (#token) > 3 && !memcmp (#token, "_3D", 3)) \ record (obj->dxfname); \ else if (obj->type >= 498 && obj->dxfname) \ record (obj->dxfname); \ else \ RECORD (token); \ LOG_INFO ("Entity " #token ":\n") \ SINCE (R_11) \ { \ LOG_TRACE ("Entity handle: " FORMAT_H "\n", ARGS_H (obj->handle)); \ fprintf (dat->fh, "%3i\r\n%lX\r\n", 5, obj->handle.value); \ } \ SINCE (R_13) { error |= dxf_common_entity_handle_data (dat, obj); } \ error |= dwg_dxf_##token##_private (dat, hdl_dat, str_dat, obj); \ error |= dxf_write_eed (dat, obj->tio.object); \ return error; \ } \ static int dwg_dxf_##token##_private ( \ Bit_Chain *dat, Bit_Chain *hdl_dat, Bit_Chain *str_dat, \ const Dwg_Object *restrict obj) { \ int error = 0; \ BITCODE_BL vcount, rcount3, rcount4; \ Dwg_Data *dwg = obj->parent; \ Dwg_Entity_##token *_obj = obj->tio.entity->tio.token; \ Dwg_Object_Entity *_ent = obj->tio.entity; #define DWG_ENTITY_END \ return error; \ } #define DWG_OBJECT(token) \ static int dwg_dxf_##token##_private ( \ Bit_Chain *dat, Bit_Chain *hdl_dat, Bit_Chain *str_dat, \ const Dwg_Object *restrict obj); \ static int dwg_dxf_##token (Bit_Chain *restrict dat, \ const Dwg_Object *restrict obj) \ { \ int error = 0; \ Bit_Chain *hdl_dat = dat, *str_dat = dat; \ LOG_INFO ("Object " #token ":\n") \ if (obj->fixedtype != DWG_TYPE_##token) \ { \ LOG_ERROR ("Invalid type 0x%x, expected 0x%x %s", obj->fixedtype, \ DWG_TYPE_##token, #token); \ return DWG_ERR_INVALIDTYPE; \ } \ PRE (R_14) { \ if (obj->fixedtype == DWG_TYPE_PLACEHOLDER) \ return 0; \ } \ if (!dwg_obj_is_control (obj)) \ { \ if (obj->fixedtype == DWG_TYPE_TABLE) \ ; \ else if (obj->type >= 500 && obj->dxfname) \ fprintf (dat->fh, " 0\r\n%s\r\n", obj->dxfname); \ else if (obj->type == DWG_TYPE_PLACEHOLDER) \ RECORD (ACDBPLACEHOLDER); \ else if (obj->fixedtype == DWG_TYPE_PROXY_OBJECT) \ RECORD (ACAD_PROXY_OBJECT); \ else if (obj->type != DWG_TYPE_BLOCK_HEADER) \ RECORD (token); \ \ SINCE (R_13) \ { \ BITCODE_BL vcount; \ const int dxf = obj->type == DWG_TYPE_DIMSTYLE ? 105 : 5; \ VALUE_H (obj->handle.value, dxf); \ _XDICOBJHANDLE (3); \ _REACTORS (4); \ } \ SINCE (R_14) \ { \ VALUE_HANDLE (obj->tio.object->ownerhandle, ownerhandle, 3, 330); \ } \ } \ if (DWG_LOGLEVEL >= DWG_LOGLEVEL_TRACE) \ { \ if (dwg_obj_is_table (obj)) \ { \ char *_name = dwg_obj_table_get_name (obj, &error); \ LOG_TRACE ("Object handle: " FORMAT_H ", name: %s\n", \ ARGS_H (obj->handle), _name); \ if (IS_FROM_TU (dat)) \ free (_name); \ } \ else \ LOG_TRACE ("Object handle: " FORMAT_H "\n", ARGS_H (obj->handle)) \ } \ error |= dwg_dxf_##token##_private (dat, hdl_dat, str_dat, obj); \ error |= dxf_write_eed (dat, obj->tio.object); \ return error; \ } \ static int dwg_dxf_##token##_private ( \ Bit_Chain *dat, Bit_Chain *hdl_dat, Bit_Chain *str_dat, \ const Dwg_Object *restrict obj) { \ int error = 0; \ BITCODE_BL vcount, rcount3, rcount4; \ Dwg_Data *dwg = obj->parent; \ Dwg_Object_##token *_obj = obj->tio.object->tio.token; // then 330, SUBCLASS #define DWG_OBJECT_END \ return error; \ } #undef DXF_3DSOLID #define DXF_3DSOLID dxf_3dsolid (dat, obj, (Dwg_Entity_3DSOLID *)_obj); // Skip index 256 bylayer // 257 is for method c8 NONE. Which index is for ByBlock? // If the dxf code is 90-99 rather emit the rgb only static void dxf_CMC (Bit_Chain *restrict dat, Dwg_Color *restrict color, const int dxf, const int opt) { if (dat->version >= R_2004) { if (dat->from_version < R_2004) bit_upconvert_CMC (dat, color); if (dxf >= 90) { VALUE_RLd (color->rgb, dxf); return; } else if (color->method == 0xc3) { VALUE_RS (color->rgb & 0x00ffffff, dxf); return; } else if (color->method == 0xc8) { VALUE_RS (257, dxf); return; } if (!opt || color->index) { VALUE_RS (color->index, dxf); } if (color->method != 0xc2) return; VALUE_RL (color->rgb, dxf + 420 - 62); if (color->flag & 2 && color->book_name) { char name[256]; if (IS_FROM_TU (dat)) { char *u8 = bit_convert_TU ((BITCODE_TU)color->book_name); if (u8) strncpy (name, u8, 127); else name[0] = '\0'; free (u8); u8 = bit_convert_TU ((BITCODE_TU)color->name); if (u8) { if (*name) strcat (name, "$"); strncat (name, u8, 127); free (u8); } } else { strncpy (name, color->book_name, 127); if (color->name) { strcat (name, "$"); strncat (name, color->name, 127); } } VALUE_TV (name, dxf + 430 - 62); } else if (color->flag & 1 && color->name) { VALUE_T (color->name, dxf + 430 - 62); } else if (color->flag) { VALUE_TFF ("UNNAMED", dxf + 430 - 62); } } else { bit_downconvert_CMC (dat, color); VALUE_RS (color->index, dxf); } } /* fixme: shift-jis decoding "\M+182B1\M+182CC\M+1907D\M+19867\M+182CD\M+18354\M+18393\M+18376\M+1838B\M+182C5\M+182B7\M+18142\M+18ED0\M+193E0\M+182CC\M+18B4B\M+18A69\M+182E2\M+18376\M+1838D\M+18362\M+1835E\M+182CC\M+18F6F\M+197CD\M+194CD\M+188CD\M+182C9\M+18D87\M+182ED\M+182B9\M+182C4\M+1934B\M+19396\M+182C9\M+195CF\M+18D58\M+182B5\M+182C4\M+182B2\M+19798\M+19770\M+182AD\M+182BE\M+182B3\M+182A2\M+18142" => "\U+3053\U+306E\U+56F3\U+67A0\U+306F\U+30B5\U+30F3\U+30D7\U+30EB\U+3067\U+3059\U+3002\U+793E\U+5185\U+306E\U+898F\U+683C\U+3084\U+30D7\U+30ED\U+30C3\U+30BF\U+306E\U+51FA\U+529B\U+7BC4\U+56F2\U+306B\U+5408\U+308F\U+305B\U+3066\U+9069\U+5F53\U+306B\U+5909\U+66F4\U+3057\U+3066\U+3054\U+5229\U+7528\U+304F\U+3060\U+3055\U+3044\U+3002" */ static char * cquote (char *restrict dest, const int len, const char *restrict src) { char c; char *d = dest; const char* dend = dest + len; const char* send = src + strlen(src); char *s = (char *)src; while ((s < send) && (c = *s++) && dest < dend) { if (c == '\n' && dest+1 < dend) { *dest++ = '^'; *dest++ = 'J'; } else if (c == '\r' && dest+1 < dend) { *dest++ = '^'; *dest++ = 'M'; } // convert shiftjis \M+1xxxx to \U+xxxx else if (c == '\\' && dest+7 < dend && memBEGINc (s, "M+1")) { // e.g. \M+1(8140) => \U+3000, 82a0 => 3042 uint32_t x; sscanf (&s[3], "%4X", &x); // just convert a small subset, Hiragana + Katakana letters // see https://www.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT if (x < 0x829f) // < Hiragana: 8140 -> 3000: FULLWIDTH LATIN LETTER x -= 0x5140; else if (x >= 0x889f) { // CJK LOG_WARN ("Unsupported \\M+1%04X shift-jis character", x); x -= (0x889f - 0x404c); // not really } else if (x >= 0x839f) // > Katakana: Greek x -= (0x839f - 0x391); else x -= 0x525e; // our safe range of Hiragana + Katakana letters if (x < 0x10100) { snprintf (dest, dend - dest, "\\U+%04X", x); dest += 7; s += 7; } else { LOG_ERROR ("Invalid shift-jis sequence %s", s-1); s += 3; } } else *dest++ = c; } d[len - 1] = '\0'; // add final delim, skipped above return d; } /* If opts 1: quote \n => ^J \M+xxxxx => \U+XXXX (shift-jis) Splits overlong (len>255) lines into dxf 3 chunks with group 1 */ static void dxf_fixup_string (Bit_Chain *restrict dat, char *restrict str, const int opts, const int dxf, const int dxfcont) { if (str && *str) { if (opts && (strchr (str, '\n') || strchr (str, '\r') || strstr (str, "\\M+1"))) { const int origlen = strlen (str); int len = (2 * origlen) + 1; char *_buf; if (len > 1024) { fprintf (dat->fh, "\r\n"); LOG_ERROR ("Overlarge DXF string, len=%d", origlen); return; } _buf = (char *)alloca (len); if (!_buf) { fprintf (dat->fh, "\r\n"); LOG_ERROR ("Out of stack memory"); return; } _buf[len - 1] = '\0'; _buf = cquote (_buf, len, str); if (!_buf) { fprintf (dat->fh, "\r\n"); LOG_ERROR ("Out of stack"); return; } len = strlen (_buf); if (len > 255 && dxf == 1) { // GROUP 1 already printed while (len > 0) { fprintf (dat->fh, "%.*s\r\n", len > 255 ? 255 : len, _buf); len -= 255; _buf += 255; if (len > 0) fprintf (dat->fh, "%3d\r\n", dxfcont); } } else fprintf (dat->fh, "%s\r\n", _buf); freea (_buf); } else { int len = strlen (str); if (len > 255 && dxf == 1) { // GROUP 1 already printed while (len > 0) { fprintf (dat->fh, "%.*s\r\n", len > 255 ? 255 : len, str); len -= 255; str += 255; if (len > 0) fprintf (dat->fh, " 3\r\n"); } } else fprintf (dat->fh, "%s\r\n", str); } } else fprintf (dat->fh, "\r\n"); } static int dxf_write_eed (Bit_Chain *restrict dat, const Dwg_Object_Object *restrict obj) { int error = 0; Dwg_Data *dwg = obj->dwg; for (BITCODE_BL i = 0; i < obj->num_eed; i++) { const Dwg_Eed* _obj = &obj->eed[i]; if (_obj->size) { // name of APPID Dwg_Object *appid = dwg_resolve_handle (dwg, _obj->handle.value); if (appid && appid->fixedtype == DWG_TYPE_APPID) VALUE_T (appid->tio.object->tio.APPID->name, 1001) else VALUE_TFF ("ACAD", 1001); } if (_obj->data) { const Dwg_Eed_Data *data = _obj->data; const int dxf = data->code + 1000; switch (data->code) { case 0: if (!data->u.eed_0.length) fprintf (dat->fh, "1000\r\n\r\n"); else if (data->u.eed_0.is_tu) VALUE_TU (data->u.eed_0_r2007.string, 1000) else VALUE_TV (data->u.eed_0.string, 1000) break; case 2: if (data->u.eed_2.close) VALUE_TFF ("}", 1002) else VALUE_TFF ("{", 1002) break; case 3: GROUP (dxf); fprintf (dat->fh, "%9lu\r\n", (unsigned long)data->u.eed_3.layer); //VALUE_RLL (data->u.eed_3.layer, dxf); break; case 4: VALUE_BINARY (data->u.eed_4.data, data->u.eed_4.length, dxf); break; case 5: VALUE_H (data->u.eed_5.entity, dxf); break; // not in DXF case 10: case 11: case 12: case 13: case 14: case 15: VALUE_3BD (data->u.eed_10.point, dxf); break; case 40: case 41: case 42: VALUE_RD (data->u.eed_40.real, dxf); break; case 70: VALUE_RS (data->u.eed_70.rs, dxf); break; case 71: VALUE_RL (data->u.eed_71.rl, dxf); break; default: VALUE_RC (0, dxf); } } } return error; } /* If the name contains "$0$" */ bool dxf_is_xrefdep_name (Bit_Chain *restrict dat, const char *name) { if (IS_FROM_TU (dat)) { BITCODE_TU wstr = (BITCODE_TU)name; #if defined (HAVE_NATIVE_WCHAR2) && defined (HAVE_WCSSTR) if (wstr && *wstr && wcsstr (&wstr[1], L"$0$")) return true; else return false; #else bool result; char* u8 = bit_convert_TU (wstr); if (u8 && *u8 && strstr (&u8[1], "$0$")) result = true; else result = false; if (u8) free (u8); return result; #endif } else { if (name && *name && strstr (&name[1], "$0$")) return true; else return false; } } /* Layer names with active dependent xref have a name like "REF|name", or "REF|REFNAME$0$name" name. Otherwise we get Layer name with vertical bar is not marked dependent (gh44-error_2013) */ bool dxf_has_xrefdep_vertbar (Bit_Chain *restrict dat, const char *name) { if (IS_FROM_TU (dat)) { BITCODE_TU wstr = (BITCODE_TU)name; #if defined (HAVE_NATIVE_WCHAR2) && defined (HAVE_WCSCHR) if (wstr && *wstr && wcschr (&wstr[1], L'|')) return true; else return false; #else bool result; char* u8 = bit_convert_TU (wstr); if (u8 && *u8 && strchr (&u8[1], '|')) result = true; else result = false; if (u8) free (u8); return result; #endif } else { if (name && *name && strchr (&name[1], '|')) return true; else return false; } } bool dxf_has_STYLE_eed (Bit_Chain *restrict dat, const Dwg_Object_Object *restrict obj) { bool result = false; bool has_acadappid = false; Dwg_Data *dwg = obj->dwg; for (BITCODE_BL i = 0; i < obj->num_eed; i++) { const Dwg_Eed* _obj = &obj->eed[i]; if (_obj->size) { // check for APPID ACAD and first data to be a string 0. TODO 2nd to be 71 Dwg_Object *appid = dwg_resolve_handle (dwg, _obj->handle.value); if (appid && appid->fixedtype == DWG_TYPE_APPID && bit_eq_T (dat, appid->tio.object->tio.APPID->name, "ACAD")) { has_acadappid = true; if (_obj->data && _obj->data->code == 0) return true; } } } return result; } GCC30_DIAG_IGNORE (-Wformat-nonliteral) static int dxf_write_xdata (Bit_Chain *restrict dat, const Dwg_Object *restrict obj, Dwg_Resbuf *restrict rbuf, BITCODE_BL size) { Dwg_Resbuf *tmp; int i; while (rbuf) { const char *fmt; short type; int dxftype = rbuf->type; fmt = dxf_format (rbuf->type); type = dwg_resbuf_value_type (rbuf->type); dxftype = (rbuf->type > 1000 || obj->fixedtype == DWG_TYPE_XRECORD) ? rbuf->type : rbuf->type + 1000; if (obj->fixedtype == DWG_TYPE_XRECORD && dxftype >= 80 && dxftype < 90) { fmt = "(unknown code)"; type = DWG_VT_INVALID; } if (strEQc (fmt, "(unknown code)")) { if (type == DWG_VT_INVALID) { LOG_WARN ("Invalid xdata code %d", dxftype); } else { LOG_WARN ("Unknown xdata code %d => %d", dxftype, (BITCODE_BL)type); } } tmp = rbuf->nextrb; switch (type) { case DWG_VT_STRING: if (IS_FROM_TU (dat)) { VALUE_TU (rbuf->value.str.u.wdata, dxftype); } else { VALUE_TV (rbuf->value.str.u.data, dxftype); } break; case DWG_VT_REAL: VALUE_RD (rbuf->value.dbl, dxftype); break; case DWG_VT_BOOL: case DWG_VT_INT8: VALUE_RC (rbuf->value.i8, dxftype); break; case DWG_VT_INT16: VALUE_RS (rbuf->value.i16, dxftype); break; case DWG_VT_INT32: VALUE_RL (rbuf->value.i32, dxftype); break; case DWG_VT_INT64: VALUE_RLL (rbuf->value.i64, dxftype); break; case DWG_VT_POINT3D: VALUE_RD (rbuf->value.pt[0], dxftype); VALUE_RD (rbuf->value.pt[1], dxftype + 10); VALUE_RD (rbuf->value.pt[2], dxftype + 20); break; case DWG_VT_BINARY: VALUE_BINARY (rbuf->value.str.u.data, rbuf->value.str.size, dxftype); break; case DWG_VT_HANDLE: case DWG_VT_OBJECTID: fprintf (dat->fh, "%3i\r\n%lX\r\n", dxftype, (unsigned long)*(uint64_t *)rbuf->value.hdl); break; case DWG_VT_INVALID: break; // skip default: fprintf (dat->fh, "%3i\r\n\r\n", dxftype); break; } rbuf = tmp; } return 0; } // r13+ converts STANDARD to Standard, BYLAYER to ByLayer, BYBLOCK to ByBlock static void dxf_cvt_tablerecord (Bit_Chain *restrict dat, const Dwg_Object *restrict obj, char *restrict name, const int dxf) { if (obj && obj->supertype == DWG_SUPERTYPE_OBJECT && name != NULL) { if (IS_FROM_TU (dat)) { name = bit_convert_TU ((BITCODE_TU)name); } if (dat->from_version >= R_13 && dat->version < R_13) { // convert the other way round, from newer to older if (strEQc (name, "Standard")) fprintf (dat->fh, "%3i\r\nSTANDARD\r\n", dxf); else if (strEQc (name, "ByLayer")) fprintf (dat->fh, "%3i\r\nBYLAYER\r\n", dxf); else if (strEQc (name, "ByBlock")) fprintf (dat->fh, "%3i\r\nBYBLOCK\r\n", dxf); else if (strEQc (name, "*Active")) fprintf (dat->fh, "%3i\r\n*ACTIVE\r\n", dxf); else fprintf (dat->fh, "%3i\r\n%s\r\n", dxf, name); } else { // convert some standard names if (dat->version >= R_13 && strEQc (name, "STANDARD")) fprintf (dat->fh, "%3i\r\nStandard\r\n", dxf); else if (dat->version >= R_13 && strEQc (name, "BYLAYER")) fprintf (dat->fh, "%3i\r\nByLayer\r\n", dxf); else if (dat->version >= R_13 && strEQc (name, "BYBLOCK")) fprintf (dat->fh, "%3i\r\nByBlock\r\n", dxf); else if (dat->version >= R_13 && strEQc (name, "*ACTIVE")) fprintf (dat->fh, "%3i\r\n*Active\r\n", dxf); else fprintf (dat->fh, "%3i\r\n%s\r\n", dxf, name); } if (IS_FROM_TU (dat)) free (name); } else { fprintf (dat->fh, "%3i\r\n\r\n", dxf); } } /* pre-r13 mspace and pspace blocks have different names: *Model_Space => $MODEL_SPACE *Paper_Space => $PAPER_SPACE TODO: Better use proper EXTNAMES */ static void dxf_cvt_blockname (Bit_Chain *restrict dat, char *restrict name, const int dxf) { if (!name) { fprintf (dat->fh, "%3i\r\n\r\n", dxf); return; } if (IS_FROM_TU (dat)) // r2007+ unicode names { name = bit_convert_TU ((BITCODE_TU)name); } if (dat->version == dat->from_version) // no conversion { fprintf (dat->fh, "%3i\r\n%s\r\n", dxf, name); } else if (dat->version < R_13 && dat->from_version >= R_13) // to older { if (strlen (name) < 10) fprintf (dat->fh, "%3i\r\n%s\r\n", dxf, name); else if (strEQc (name, "*Model_Space")) fprintf (dat->fh, "%3i\r\n$MODEL_SPACE\r\n", dxf); else if (strEQc (name, "*Paper_Space")) fprintf (dat->fh, "%3i\r\n$PAPER_SPACE\r\n", dxf); else if (!memcmp (name, "*Paper_Space", sizeof ("*Paper_Space") - 1)) fprintf (dat->fh, "%3i\r\n$PAPER_SPACE%s\r\n", dxf, &name[12]); else fprintf (dat->fh, "%3i\r\n%s\r\n", dxf, name); } else if (dat->version >= R_13 && dat->from_version < R_13) // to newer { if (strlen (name) < 10) fprintf (dat->fh, "%3i\r\n%s\r\n", dxf, name); else if (strEQc (name, "$MODEL_SPACE")) fprintf (dat->fh, "%3i\r\n*Model_Space\r\n", dxf); else if (strEQc (name, "$PAPER_SPACE")) fprintf (dat->fh, "%3i\r\n*Paper_Space\r\n", dxf); else if (!memcmp (name, "$PAPER_SPACE", sizeof ("$PAPER_SPACE") - 1)) fprintf (dat->fh, "%3i\r\n*Paper_Space%s\r\n", dxf, &name[12]); else fprintf (dat->fh, "%3i\r\n%s\r\n", dxf, name); } if (IS_FROM_TU (dat)) { free (name); name = NULL; } } #define START_OBJECT_HANDLE_STREAM // Handle 5 written here first #define COMMON_TABLE_CONTROL_FLAGS \ if (ctrl) \ { \ SINCE (R_13) \ { \ VALUE_H (ctrl->handle.value, 5); \ _XDICOBJHANDLE (3); \ _REACTORS (4); \ } \ SINCE (R_14) \ { \ VALUE_HANDLE (ctrl->tio.object->ownerhandle, ownerhandle, 3, 330); \ } \ } \ SINCE (R_13) { VALUE_TV ("AcDbSymbolTable", 100); } #define COMMON_TABLE_FLAGS(acdbname) \ SINCE (R_13) \ { \ VALUE_TV ("AcDbSymbolTableRecord", 100); \ VALUE_TV ("AcDb" #acdbname "TableRecord", 100); \ } \ if (strEQc (#acdbname, "Block") && dat->version >= R_13) \ { \ Dwg_Object *blk = dwg_ref_object ( \ dwg, ((Dwg_Object_BLOCK_HEADER *)_obj)->block_entity); \ if (blk && blk->type == DWG_TYPE_BLOCK) \ { \ Dwg_Entity_BLOCK *_blk = blk->tio.entity->tio.BLOCK; \ VALUE_T (_blk->name, 2) \ } \ else if (_obj->name) \ { \ VALUE_T (_obj->name, 2) \ } \ else \ VALUE_TV ("*", 2) \ } \ /* Empty name with xref shape names */ \ else if (strEQc (#acdbname, "TextStyle") && \ _obj->flag & 1 && \ dxf_is_xrefdep_name (dat, _obj->name)) \ VALUE_TV ("", 2) \ else if (_obj->name) \ dxf_cvt_tablerecord (dat, obj, _obj->name, 2); \ else \ VALUE_TV ("*", 2) \ if (strEQc (#acdbname, "Layer") && dat->version >= R_2000) \ { \ /* Mask off plotflag and linewt. */ \ BITCODE_RC _flag = _obj->flag & ~0x3e0; \ /* Don't keep bit 16 when not xrefdep like "XREF|name" */ \ if (_flag & 0x10 && !dxf_has_xrefdep_vertbar (dat, _obj->name)) \ _flag &= ~0x10; \ VALUE_RC (_flag, 70); \ } \ else if (strEQc (#acdbname, "Block") && dat->version >= R_2000) \ ; /* skip 70 for AcDbBlockTableRecord here. done in AcDbBlockBegin */ \ else \ { \ /* mask off 64, the loaded bit 6 */ \ VALUE_RC (_obj->flag & ~64, 70); \ } // unused #define LAYER_TABLE_FLAGS(acdbname) \ SINCE (R_13) \ { \ VALUE_TV ("AcDbSymbolTableRecord", 100); \ VALUE_TV ("AcDb" #acdbname "TableRecord", 100); \ } \ if (_obj->name) \ dxf_cvt_tablerecord (dat, obj, _obj->name, 2); \ FIELD_RS (flag, 70) #include "dwg.spec" /* This was previously in encode, but since out_dxf needs it for r2013+ 3DSOLIDs and --disable-write is still an option, we need to move it here. A global acis_data_idx is needed, since encr_acis_data is split into blocks, but acis_data is a single stream, so we need to keep track of the current position. */ EXPORT char * dwg_encrypt_SAT1 (BITCODE_BL blocksize, BITCODE_RC *restrict acis_data, int *restrict acis_data_idx) { BITCODE_RC* encr_sat_data = (BITCODE_RC*)calloc (blocksize + 1, 1); int i; for (i = 0; i < (int)blocksize; i++) { if (acis_data[i] <= 32) encr_sat_data[i] = acis_data[i]; else encr_sat_data[i] = 159 - acis_data[i]; } *acis_data_idx = i; return (char*)encr_sat_data; } static int new_encr_sat_data_line (Dwg_Entity_3DSOLID *restrict _obj,Bit_Chain *dest, unsigned int i) { /* if (i + 1 >= _obj->num_blocks) { _obj->encr_sat_data = realloc (_obj->encr_sat_data, (i + 2) * sizeof (char*)); _obj->block_size = realloc (_obj->block_size, (i + 2) * sizeof (BITCODE_BL)); _obj->num_blocks = i + 1; } _obj->encr_sat_data[i] = calloc (dest->byte + 2, 1); // fresh, the dest buf is too large bit_write_TF (dest, (BITCODE_TF) "\n\000", 2); // ensure proper eol, dxf out relies on that. memcpy (_obj->encr_sat_data[i], dest->chain, dest->byte); _obj->block_size[i] = dest->byte - 1; // dont count the final 0 bit_set_position (dest, 0); i++; */ bit_write_TF (dest, (BITCODE_TF) "\n", 1); // ensure proper eol, dxf out relies on that return i; } // TODO SAT_enum // logical values are class-specific, and must be strings. static const char* SAT_boolean (const char *act_record, bool value) { static int argc = 0; if (!strEQc (act_record, "varblendsplsur") && !strEQc (act_record, "face") && !strEQc (act_record, "bdy_geom")) argc = 0; if (strEQc (act_record, "sphere") || strEQc (act_record, "plane") || strEQc (act_record, "stripc") || strEQc (act_record, "torus")) return !value ? "forward_v" : "reverse_v"; else if (strEQc (act_record, "spline") || strEQc (act_record, "edge") || strEQc (act_record, "meshsurf") || strEQc (act_record, "pcurve") || strEQc (act_record, "intcurve")) return !value ? "forward" : "reversed"; else if (strEQc (act_record, "surfcur") || strEQc (act_record, "bldcur") || strEQc (act_record, "parcur") || strEQc (act_record, "projcur") || strEQc (act_record, "perspsil")) return !value ? "surf2" : "surf1"; else if (strEQc (act_record, "sweepsur")) return !value ? "normal" : "angled"; else if (strEQc (act_record, "var_cross_section")) return value ? "radius" : "no_radius"; else if (strEQc (act_record, "var_radius")) return value ? "uncalibrated" : "calibrated"; else if (strEQc (act_record, "wire")) return value ? "in" : "out"; else if (strEQc (act_record, "adv_var_blend")) return !value ? "sharp" : "smooth"; else if (strEQc (act_record, "attrib_fhlhead")) return !value ? "invalid" : "valid"; else if (strEQc (act_record, "attrib_fhlplist") || strEQc (act_record, "attrib_fhl_slist")) return !value ? "invisible" : "visible"; else if (strEQc (act_record, "bl_ent_ent") || strEQc (act_record, "bl_inst")) return !value ? "unset" : "set"; // TODO orthosur undocumented // now the few classes with mult. locical args else if (strEQc (act_record, "face")) { if (!argc) { argc++; return !value ? "forward" : "reversed"; } else if (argc == 1) { argc++; return !value ? "single" : "double"; } else { argc = 0; return !value ? "out" : "in"; } } else if (strEQc (act_record, "varblendsplsur")) { if (!argc) { argc++; return !value ? "concave" : "convex"; } else { argc = 0; return !value ? "rb_snapshot" : "rb_envelope"; } } else if (strEQc (act_record, "attrib_var_blend")) { if (!argc) { argc++; return value ? "uncalibrated" : "calibrated"; } else if (argc == 1) { argc++; return !value ? "one_radius" : "two_radii"; } else { argc = 0; return !value ? "forward" : "reversed"; } } else if (strEQc (act_record, "bdy_geom")) { if (!argc) { argc++; return value ? "non_cross" : "cross"; } else { argc++; return !value ? "non_smooth" : "smooth"; } } else return value ? "I" : "F"; } /* Converts v2 SAB acis_data in-place to SAT v1 encr_sat_data[]. Sets dxf_sab_converted to 1, denoting that encr_sat_data is NOT the encrypted acis_data anymore, rather the converted from SAB for DXF. */ EXPORT int dwg_convert_SAB_to_SAT1 (Dwg_Entity_3DSOLID *restrict _obj) { Bit_Chain dest = { NULL, 0, 0, 0 }; Bit_Chain src = { NULL, 0, 0, 0 }; unsigned int i = 0, num_blocks = 2; BITCODE_RC c; char *p = (char*)&_obj->acis_data[15]; //char *end, *dest, *enddest; long unsigned int size; BITCODE_RL version, num_records, num_entities, has_history; //char *product_string, *acis_version, *date; BITCODE_RD num_mm_units, resabs, resnor; // Note that r2013+ has ASM data instead. // const char enddata[] = "\016\003End\016\002of\016\004ACIS\r\004data"; // const char enddata1[] = "\016\003End\016\002of\016\003ASM\r\004data"; int l = 0; int forward = 0; int skip_hist = 0; char act_record [80]; int error; // We need dwg->header.version for the target ACIS version. const Dwg_Object* obj = dwg_obj_generic_to_object (_obj, &error); const Dwg_Data *dwg = obj ? obj->parent : NULL; Dwg_Version_Type dwg_version = dwg ? dwg->header.version : R_2000; // Hack: For DXF dont write n the AcDs vs format. No history, no ASM, early versions. #ifndef CAN_ACIS_IN_DS_DATA if (dwg_version >= R_2013) dwg_version = R_2004; #endif if (_obj->num_blocks) num_blocks = _obj->num_blocks; if (!_obj->block_size) _obj->block_size = calloc (num_blocks, sizeof (BITCODE_BL)); if (!_obj->encr_sat_data) _obj->encr_sat_data = calloc (num_blocks, sizeof (char *)); _obj->_dxf_sab_converted = 1; if (!_obj->sab_size) _obj->sab_size = _obj->block_size[0]; //end = &p[_obj->sab_size]; if (!memBEGINc ((char*)_obj->acis_data, "ACIS BinaryFile")) { LOG_ERROR ("acis_data is not a SAB 2 'ACIS BinaryFile'"); _obj->num_blocks = 0; _obj->encr_sat_data[0] = NULL; return 1; } //dest = &_obj->encr_sat_data[0][0]; //enddest = &dest[size]; bit_chain_alloc (&dest); src.chain = &_obj->acis_data[15]; src.size = _obj->sab_size - 15; // header only #define SAB_RD(key) \ c = bit_read_RC (&src); /* 6 */ \ LOG_HANDLE (#key " [%d] ", c) \ key = bit_read_RD (&src); \ dest.byte += sprintf ((char*)&dest.chain[dest.byte], "%g ", key); \ LOG_TRACE ("%g ", key) // record variant #define SAB_RD1() \ { \ double f = bit_read_RD (&src); \ int s; \ if (dest.byte + 16 >= dest.size) \ bit_chain_alloc (&dest); \ if (l + 16 > 255) \ { \ bit_write_TF (&dest, (BITCODE_TF) "\n", 1); \ LOG_TRACE ("Split overlong SAT line\n"); \ l = 0; \ } \ s = sprintf ((char*)&dest.chain[dest.byte], "%g ", f); \ dest.byte += s; l += s; \ LOG_TRACE ("%g ", f); \ } // key is ignored here. header only #define SAB_T(key) \ { \ int len, s; \ c = bit_read_RC (&src); \ LOG_HANDLE (#key " [%d] ", c) \ len = bit_read_RC (&src); \ s = sprintf ((char*)&dest.chain[dest.byte], "%d ", len); \ dest.byte += s; l += s; \ LOG_TRACE ("%d %.*s ", len, len, &src.chain[src.byte]); \ bit_write_TF (&dest, &src.chain[src.byte], len); \ bit_write_TF (&dest, (BITCODE_TF) " ", 1); \ l += len + 1; \ src.byte += len; \ } #define SAB_TF(x) \ if (l + sizeof(x) > 255) \ { \ bit_write_TF (&dest, (BITCODE_TF) "\n", 1); \ LOG_TRACE ("Split overlong SAT line\n"); \ l = 0; \ } \ bit_write_TF (&dest, (BITCODE_TF) x, sizeof (x) - 1); \ bit_write_TF (&dest, (BITCODE_TF) " ", 1); \ l += sizeof (x); \ LOG_TRACE ("%s ", x) #define SAB_TV(x) \ if (l + strlen (x) > 255) \ { \ bit_write_TF (&dest, (BITCODE_TF) "\n", 1); \ LOG_TRACE ("Split overlong SAT line\n"); \ l = 0; \ } \ bit_write_TF (&dest, (BITCODE_TF) x, strlen (x)); \ bit_write_TF (&dest, (BITCODE_TF) " ", 1); \ l += strlen (x) + 1; \ LOG_TRACE ("%s ", x) // create the two vectors encr_sat_data[] and block_size[] on the fly from the SAB // http://paulbourke.net/dataformats/sat/sat.pdf /* ACIS BinaryFile [64 81 6x0[ [2 7x0] "\a\020Autodesk AutoCAD\a\024ASM 223.0.1.1930 OSX\a\030Mon Jun 18 11:09:32 2018\006" 6x0 16 63 "\006\215\355\265\240\367ư>" "\006\273\275\327\331\337|\333= "\r\tasmheader" \f\377\377\377\377\004\377\377\377\377\a\f223.0.1.1930\021 \r\004body \f\377\377\377\377\004\377\377\377\377\f\377\377\377\377\f\002" */ version = bit_read_RL (&src); num_records = bit_read_RL (&src); // if 0 until end marker. total num_entities = bit_read_RL (&src); // named top in the ACIS docs has_history = bit_read_RL (&src); // named as flags in the ACIS docs LOG_TRACE ("%d %d %d %d \n", version, num_records, num_entities, has_history); #ifndef CAN_ACIS_HISTORY if (dwg_version < R_2010) has_history = 0; // FIXME: need to delete all persubent-acadSolidHistory-attrib lines then. #endif if (dwg_version >= R_2013) version = 21800; else if (dwg_version >= R_2010) version = 21500; else if (dwg_version >= R_2007) version = 21200; else if (dwg_version >= R_2004) version = 20800; else if (dwg_version >= R_2000) version = 400; else if (dwg_version < R_2000) version = 106; dest.byte += sprintf ((char*)dest.chain, "%d %d %d %d ", version, num_records, num_entities, has_history); LOG_TRACE ("=> %d %d %d %d \n", version, num_records, num_entities, has_history); i = new_encr_sat_data_line (_obj, &dest, i); SAB_T (product_string) SAB_T (acis_version) SAB_T (date) i = new_encr_sat_data_line (_obj, &dest, i); LOG_TRACE ("\n"); SAB_RD (num_mm_units); SAB_RD (resabs); SAB_RD (resnor); i = new_encr_sat_data_line (_obj, &dest, i); LOG_TRACE ("\n"); c = bit_read_RC (&src); // type tag while (src.byte < src.size) { LOG_HANDLE ("[%d] ", c) switch (c) { // check size, realloc encr_sat_data[i], set dest case 17: // # end of record forward = 0; if (dest.byte + 2 >= dest.size) bit_chain_alloc (&dest); if (skip_hist && !has_history) { // delete any persubent-acadSolidHistory-attrib line char *s = strrchr ((char*)dest.chain, '#'); int diff = s ? (char*)&dest.chain[dest.byte] - s : 0; if (diff > 0 && dest.chain[dest.byte - diff + 1] == '\n') { dest.byte -= (diff - 2); } LOG_TRACE ("# --deleted--\n"); } else { int s = sprintf ((char*)&dest.chain[dest.byte], "#\n"); dest.byte += s; l += s; LOG_TRACE ("#\n"); } //i = new_encr_sat_data_line (_obj, &dest, i); l = 0; skip_hist = 0; break; case 13: // ident _obj->encr_sat_data[i] = (char*)dest.chain; // fallthru case 7: // char len case 14: // subident { int len = bit_read_RC (&src); if (dest.byte + len + 4 >= dest.size) bit_chain_alloc (&dest); if (c == 7 && i < 3) { int s = sprintf ((char*)&dest.chain[dest.byte], "%d ", len); dest.byte += s; l += s; LOG_TRACE ("%d ", len); } LOG_TRACE ("%.*s%s", len, &src.chain[src.byte], c == 14 ? "-" : " "); #ifndef CAN_ACIS_IN_DS_DATA // fixup End-of-ASM-data => End-of-ACIS-data if >= r2013 // downconvert ASM (AcDs) to ACIS for DXF if (len == 3 && c == 14 && !memcmp (&src.chain[src.byte], "ASM", 3)) { LOG_TRACE ("=>ACIS-"); skip_hist = 1; bit_write_TF (&dest, (BITCODE_TF)"ACIS", 4); } else #endif bit_write_TF (&dest, &src.chain[src.byte], len); #ifndef CAN_ACIS_HISTORY if (c == 14 && len == strlen ("acadSolidHistory") && !memcmp (&src.chain[src.byte], "acadSolidHistory", len)) { skip_hist = 1; // skip the whole line } #endif if (c == 13 && len < 80) { memcpy (act_record, &src.chain[src.byte], len); act_record[len] = '\0'; // to find identifier-specific true/false strings } // TODO Begin-of-ACIS-History-Data => new line // TODO End-of-ACIS-History-Section => new line //if (has_history && c == 13 && len = 4 && memBEGINc (&src.chain[src.byte], "Data")) // i = new_encr_sat_data_line (_obj, &dest, i); src.byte += len; if (c == 14) bit_write_TF (&dest, (BITCODE_TF)"-", 1); else bit_write_TF (&dest, (BITCODE_TF)" ", 1); l++; break; } case 8: // short len { int len = bit_read_RS (&src); if (src.byte + len >= src.size) { LOG_ERROR ("Invalid SAB"); bit_chain_free (&dest); _obj->num_blocks = 0; _obj->encr_sat_data[0] = NULL; return 1; } if (dest.byte + len + 1 >= dest.size) bit_chain_alloc (&dest); LOG_TRACE ("%.*s%s", len, &src.chain[src.byte], " ") bit_write_TF (&dest, &src.chain[src.byte], len); src.byte += len; bit_write_TF (&dest, (BITCODE_TF)" ", 1); l += len + 1; break; } case 9: // long len { int len = bit_read_RL (&src); if (src.byte + len >= src.size) { LOG_ERROR ("Invalid SAB"); bit_chain_free (&dest); _obj->num_blocks = 0; _obj->encr_sat_data[0] = NULL; return 1; } if (dest.byte + len + 1 >= dest.size) bit_chain_alloc (&dest); if (l + len > 255) { bit_write_TF (&dest, (BITCODE_TF) "\n", 1); LOG_TRACE ("Split overlong SAT line\n"); l = 0; } LOG_TRACE ("%.*s%s", len, &src.chain[src.byte], " ") bit_write_TF (&dest, &src.chain[src.byte], len); src.byte += len; bit_write_TF (&dest, (BITCODE_TF)" ", 1); l += len + 1; break; } case 10: // 0 byte TRUE { const char *s = SAT_boolean (act_record, 1); SAB_TV (s); } break; case 11: // 0 byte FALSE { const char *s = SAT_boolean (act_record, 0); SAB_TV (s); } break; case 15: // 0 byte subtype start SAB_TF ("{"); break; case 16: // 1 byte subtype end SAB_TF ("}"); LOG_HANDLE ("[%c] ", src.chain[src.byte]); //src.byte++; break; case 2: // char constant { int s; int8_t ll = (int8_t)bit_read_RC (&src); if (dest.byte + 4 >= dest.size) bit_chain_alloc (&dest); if (l + 3 > 255) { bit_write_TF (&dest, (BITCODE_TF) "\n", 1); LOG_TRACE ("Split overlong SAT line\n"); l = 0; } s = sprintf ((char*)&dest.chain[dest.byte], "%" PRId8 " ", ll); dest.byte += s; l += s; LOG_TRACE ("%" PRId8 " ", ll) } break; case 3: // short constant { int s; int16_t ll = (int16_t)bit_read_RS (&src); if (dest.byte + 8 >= dest.size) bit_chain_alloc (&dest); s = sprintf ((char*)&dest.chain[dest.byte], "%" PRId16 " ", ll); dest.byte += s; l += s; LOG_TRACE ("%" PRId16 " ", ll) } break; case 4: // long constant case 21: // enum value. See GH jmplonka/InventorLoader:Acis.py { int s; BITCODE_RLd ll = (BITCODE_RLd)bit_read_RL (&src); if (dest.byte + 16 >= dest.size) bit_chain_alloc (&dest); s = sprintf ((char*)&dest.chain[dest.byte], "$%" PRId32 " ", ll); dest.byte += s; l += s; LOG_TRACE ("$%" PRId32 " ", ll) } break; case 5: // float constant { int s; float f = bit_read_RL (&src); if (dest.byte + 16 >= dest.size) bit_chain_alloc (&dest); if (l + 16 > 255) { bit_write_TF (&dest, (BITCODE_TF) "\n", 1); LOG_TRACE ("Split overlong SAT line\n"); l = 0; } s = sprintf ((char*)&dest.chain[dest.byte], "%g ", (double)f); dest.byte += s; l += s; LOG_TRACE ("%g ", (double)f) } break; case 12: // 4 byte pointer index { int s; BITCODE_RLd ll = (BITCODE_RLd)bit_read_RL (&src); if (dest.byte + 16 >= dest.size) bit_chain_alloc (&dest); if (l + 6 > 255) { bit_write_TF (&dest, (BITCODE_TF) "\n", 1); LOG_TRACE ("Split overlong SAT line\n"); l = 0; } s = sprintf ((char*)&dest.chain[dest.byte], "$%" PRId32 " ", ll); dest.byte += s; l += s; LOG_TRACE ("$%" PRId32 " ", ll) } break; case 19: // 3x double-float position case 20: // 3x double-float vector SAB_RD1(); SAB_RD1(); // fallthru case 6: // 8 byte double-float, e.g. in the 2nd header line SAB_RD1(); break; //case 22: // U-V-Vector // break; case 23: // int64 { int s; int64_t i64 = (int64_t)bit_read_RLL (&src); if (dest.byte + 16 >= dest.size) bit_chain_alloc (&dest); if (l + 16 > 255) { bit_write_TF (&dest, (BITCODE_TF) "\n", 1); LOG_TRACE ("Split overlong SAT line\n"); l = 0; } s = sprintf ((char*)&dest.chain[dest.byte], "$%" PRId64 " ", i64); dest.byte += s; l += s; LOG_TRACE ("$%" PRId64 " ", i64) } break; default: LOG_ERROR ("Unknown SAB tag %d", c); } c = bit_read_RC (&src); } //if (c != 17) // last line didn't end with #, but End-of-ACIS-data or End-of-ASM-data // i = new_encr_sat_data_line (_obj, &dest, i); num_blocks = _obj->num_blocks = 1; // Nope, keep it. Teigha always adds it, ASM not. //if (strEQc((char*)&dest.chain[dest.byte-17], "End-of-ACIS-data ")) // dest.byte -= 17; bit_write_TF (&dest, (BITCODE_TF)"\n", 1); size = dest.byte; // total size { unsigned long off = 0; while (size > 4096) { // chunks of 4096 if (i >= num_blocks) { _obj->block_size = realloc (_obj->block_size, (i + 2) * sizeof (BITCODE_BL)); _obj->encr_sat_data = realloc (_obj->encr_sat_data, (i + 1) * sizeof (char**)); num_blocks = i + 1; } _obj->encr_sat_data[i] = calloc (4096, 1); memcpy (_obj->encr_sat_data[i], &dest.chain[off], 4096); _obj->block_size[i] = 4096; LOG_TRACE ("block_size[%d] = 4096\n", i); i++; size -= 4096; off += 4096; } // and the smaller rest if (i >= num_blocks) { _obj->block_size = realloc (_obj->block_size, (i + 2) * sizeof (BITCODE_BL)); _obj->encr_sat_data = realloc (_obj->encr_sat_data, (i + 1) * sizeof (char**)); num_blocks = i + 1; } _obj->encr_sat_data[i] = calloc (size + 1, 1); // shrink it memcpy (_obj->encr_sat_data[i], &dest.chain[off], size); _obj->block_size[i] = size; LOG_TRACE ("block_size[%d] = %lu\n", i, size); } bit_chain_free (&dest); LOG_TRACE ("\n"); _obj->acis_empty = 0; _obj->version = 1; // conversion complete if (i + 2 >= num_blocks) _obj->block_size = realloc (_obj->block_size, (i + 2) * sizeof (BITCODE_BL)); _obj->num_blocks = i; _obj->block_size[i + 1] = 0; return 0; } /* Add history_id for old version 1 SAT if there's one */ static void dxf_check_history_id (Bit_Chain *restrict dat, const Dwg_Object *restrict obj, Dwg_Entity_3DSOLID *restrict _obj) { if (!_obj->history_id || !_obj->history_id->absolute_ref) { Dwg_Data *dwg = obj->parent; Dwg_Handle *hdl = NULL; if (_obj->history_id) { /* Avoid NULL HDL */ Dwg_Object *o = dwg_ref_object (dwg, _obj->history_id); if (o) hdl = &o->handle; } if (!hdl) /* FIXME Just take the first HISTORY_CLASS TODO Check HISTORY_CLASS.owner (2, 360) */ hdl = dwg_find_first_type_handle (dwg, DWG_TYPE_ACSH_HISTORY_CLASS); if (hdl) _obj->history_id = dwg_add_handleref (dwg, 4, hdl->value, obj); if (_obj->history_id) LOG_TRACE ("Empty %s.history_id => " FORMAT_REF "\n", obj->name, ARGS_REF (_obj->history_id)) else LOG_WARN ("Empty %s.history_id\n", obj->name) } } static int dxf_3dsolid (Bit_Chain *restrict dat, const Dwg_Object *restrict obj, Dwg_Entity_3DSOLID *restrict _obj) { BITCODE_BL i; int error = 0; COMMON_ENTITY_HANDLE_DATA; FIELD_B0 (acis_empty, 290); if (!FIELD_VALUE (acis_empty)) { FIELD_B (unknown, 0); if (FIELD_VALUE (version) == 2) { BITCODE_RC *acis_data; LOG_TRACE ("Convert SAB ACIS BinaryFile v2 to encrypted SAT v1\n"); error |= dwg_convert_SAB_to_SAT1 (_obj); for (i = 0; i < FIELD_VALUE (num_blocks); i++) { int idx = 0; // here idx is just local, always starting at 0. ignored char *ptr = dwg_encrypt_SAT1 ( _obj->block_size[i], (BITCODE_RC *)_obj->encr_sat_data[i], &idx); free (_obj->encr_sat_data[i]); _obj->encr_sat_data[i] = ptr; } _obj->version = 1; // conversion complete } FIELD_BS (version, 70); // always 1 for (i = 0; i < FIELD_VALUE (num_blocks); i++) { char *s = FIELD_VALUE (encr_sat_data[i]); // FIXME // only DXF 1, always keep len <255 //int len = _obj->_dxf_sab_converted ? FIELD_VALUE (block_size[i]) : strlen (s); int len = FIELD_VALUE (block_size[i]); while (len > 0) { char *n = strchr (s, '\n'); int l = len > 255 ? 255 : len; char *caret = strchr (s, '^'); if (n && (n - s < len)) { l = n - s; } if (l) { GROUP (1); // replace "^" by "^ " while (caret && caret < n) { int lc = caret - s; fprintf (dat->fh, "%.*s^ ", lc, s); lc++; l -= lc; len -= lc; s += lc; caret = l > 1 ? strchr (s, '^') : NULL; } if (l > 255) LOG_ERROR ("Overlong SAT line \"%s\" len=%d", s, l) if (s[l - 1] == '\r') fprintf (dat->fh, "%.*s\n", l, s); else fprintf (dat->fh, "%.*s\r\n", l, s); l++; len -= l; s += l; } else { len--; s++; } } } } dxf_check_history_id (dat, obj, _obj); // the rest is done in COMMON_3DSOLID in the spec. return error; } /* returns 0 on success */ static int dwg_dxf_variable_type (const Dwg_Data *restrict dwg, Bit_Chain *restrict dat, Dwg_Object *restrict obj) { int i; Dwg_Class *klass; int is_entity; i = obj->type - 500; if (i < 0 || i >= dwg->num_classes) return DWG_ERR_INVALIDTYPE; klass = &dwg->dwg_class[i]; if (!klass || !klass->dxfname) return DWG_ERR_INTERNALERROR; is_entity = dwg_class_is_entity (klass); if (dat->version < R_2000) { // keep only: IMAGE, LWPOLYLINE, HATCH if (is_entity && strNE (klass->dxfname, "IMAGE") && strNEc (klass->dxfname, "LWPOLYLINE") && strNEc (klass->dxfname, "HATCH")) { LOG_WARN ("Skip %s\n", klass->dxfname) return DWG_ERR_UNHANDLEDCLASS; } // keep only: DICTIONARYVAR, MATERIAL, RASTERVARIABLES, IMAGEDEF_REACTOR, XRECORD, IDBUFFER else if (!is_entity && strNEc (klass->dxfname, "DICTIONARYVAR") && strNEc (klass->dxfname, "MATERIAL") && strNEc (klass->dxfname, "RASTERVARIABLES") && strNEc (klass->dxfname, "IDBUFFER") && strNEc (klass->dxfname, "IMAGEDEF_REACTOR") && strNEc (klass->dxfname, "XRECORD")) { LOG_WARN ("Skip %s\n", klass->dxfname) return DWG_ERR_UNHANDLEDCLASS; } } // if (!is_entity) // fprintf(dat->fh, " 0\r\n%s\r\n", dxfname); // clang-format off #include "classes.inc" // clang-format on return DWG_ERR_UNHANDLEDCLASS; } /* process unsorted vertices until SEQEND */ #define decl_dxf_process_VERTEX(token) \ static int dxf_process_VERTEX_##token (Bit_Chain *restrict dat, \ const Dwg_Object *restrict obj, \ int *restrict i) \ { \ int error = 0; \ Dwg_Entity_POLYLINE_##token *_obj \ = obj->tio.entity->tio.POLYLINE_##token; \ \ VERSIONS (R_13, R_2000) \ { \ Dwg_Object *last_vertex = _obj->last_vertex ? _obj->last_vertex->obj : NULL; \ Dwg_Object *o = _obj->first_vertex ? _obj->first_vertex->obj : NULL; \ if (!o || !last_vertex) \ return DWG_ERR_INVALIDHANDLE; \ if (o->fixedtype == DWG_TYPE_VERTEX_##token) \ error |= dwg_dxf_VERTEX_##token (dat, o); \ *i = *i + 1; \ do \ { \ o = dwg_next_object (o); \ if (!o) \ return DWG_ERR_INVALIDHANDLE; \ if (strEQc (#token, "PFACE") \ && o->fixedtype == DWG_TYPE_VERTEX_PFACE_FACE) \ { \ error |= dwg_dxf_VERTEX_PFACE_FACE (dat, o); \ } \ else if (o->fixedtype == DWG_TYPE_VERTEX_##token) \ { \ error |= dwg_dxf_VERTEX_##token (dat, o); \ } \ *i = *i + 1; \ } \ while (o->fixedtype != DWG_TYPE_SEQEND && o != last_vertex); \ o = _obj->seqend ? _obj->seqend->obj : NULL; \ if (o && o->fixedtype == DWG_TYPE_SEQEND) \ error |= dwg_dxf_SEQEND (dat, o); \ *i = *i + 1; \ } \ SINCE (R_2004) \ { \ Dwg_Object *o; \ for (BITCODE_BL j = 0; j < _obj->num_owned; j++) \ { \ o = _obj->vertex && _obj->vertex[j] ? _obj->vertex[j]->obj : NULL; \ if (strEQc (#token, "PFACE") && o \ && o->fixedtype == DWG_TYPE_VERTEX_PFACE_FACE) \ { \ error |= dwg_dxf_VERTEX_PFACE_FACE (dat, o); \ } \ else if (o && o->fixedtype == DWG_TYPE_VERTEX_##token) \ { \ error |= dwg_dxf_VERTEX_##token (dat, o); \ } \ } \ o = _obj->seqend ? _obj->seqend->obj : NULL; \ if (o && o->fixedtype == DWG_TYPE_SEQEND) \ error |= dwg_dxf_SEQEND (dat, o); \ *i = *i + _obj->num_owned + 1; \ } \ return error; \ } // clang-format off decl_dxf_process_VERTEX (2D) decl_dxf_process_VERTEX (3D) decl_dxf_process_VERTEX (MESH) decl_dxf_process_VERTEX (PFACE) // clang-format on /* process if seqend before attribs */ #define decl_dxf_process_INSERT(token) \ static int dxf_process_##token (Bit_Chain *restrict dat, \ const Dwg_Object *restrict obj, \ int *restrict i) \ { \ int error = 0; \ Dwg_Entity_##token *_obj = obj->tio.entity->tio.token; \ \ if (!_obj->has_attribs) \ return 0; \ VERSIONS (R_13, R_2000) \ { \ Dwg_Object *last_attrib \ = _obj->last_attrib ? _obj->last_attrib->obj : NULL; \ Dwg_Object *o = _obj->first_attrib ? _obj->first_attrib->obj : NULL; \ if (!o || !last_attrib) \ return DWG_ERR_INVALIDHANDLE; \ if (o->fixedtype == DWG_TYPE_ATTRIB) \ error |= dwg_dxf_ATTRIB (dat, o); \ *i = *i + 1; \ do \ { \ o = dwg_next_object (o); \ if (!o) \ return DWG_ERR_INVALIDHANDLE; \ if (o->fixedtype == DWG_TYPE_ATTRIB) \ error |= dwg_dxf_ATTRIB (dat, o); \ *i = *i + 1; \ } \ while (o->fixedtype == DWG_TYPE_ATTRIB && o != last_attrib); \ o = _obj->seqend ? _obj->seqend->obj : NULL; \ if (o && o->fixedtype == DWG_TYPE_SEQEND) \ error |= dwg_dxf_SEQEND (dat, o); \ *i = *i + 1; \ } \ SINCE (R_2004) \ { \ Dwg_Object *o; \ for (BITCODE_BL j = 0; j < _obj->num_owned; j++) \ { \ o = _obj->attribs && _obj->attribs[j] \ ? _obj->attribs[j]->obj \ : NULL; \ if (o && o->fixedtype == DWG_TYPE_ATTRIB) \ error |= dwg_dxf_ATTRIB (dat, o); \ } \ o = _obj->seqend ? _obj->seqend->obj : NULL; \ if (o && o->fixedtype == DWG_TYPE_SEQEND) \ error |= dwg_dxf_SEQEND (dat, o); \ *i = *i + _obj->num_owned + 1; \ } \ return error; \ } // clang-format off decl_dxf_process_INSERT (INSERT) decl_dxf_process_INSERT (MINSERT) // clang-format on static int dwg_dxf_object (Bit_Chain *restrict dat, const Dwg_Object *restrict obj, int *restrict i) { int error = 0; int minimal; if (!obj || !obj->parent) return DWG_ERR_INTERNALERROR; minimal = obj->parent->opts & DWG_OPTS_MINIMAL; switch (obj->type) { case DWG_TYPE_TEXT: return dwg_dxf_TEXT (dat, obj); case DWG_TYPE_ATTDEF: return dwg_dxf_ATTDEF (dat, obj); case DWG_TYPE_BLOCK: return dwg_dxf_BLOCK (dat, obj); case DWG_TYPE_ENDBLK: LOG_WARN ("stale %s subentity", obj->dxfname); return 0; // dwg_dxf_ENDBLK(dat, obj); case DWG_TYPE_SEQEND: LOG_WARN ("stale %s subentity", obj->dxfname); return 0; // dwg_dxf_SEQEND(dat, obj); case DWG_TYPE_INSERT: error = dwg_dxf_INSERT (dat, obj); return error | dxf_process_INSERT (dat, obj, i); case DWG_TYPE_MINSERT: error = dwg_dxf_MINSERT (dat, obj); return error | dxf_process_MINSERT (dat, obj, i); case DWG_TYPE_POLYLINE_2D: error = dwg_dxf_POLYLINE_2D (dat, obj); return error | dxf_process_VERTEX_2D (dat, obj, i); case DWG_TYPE_POLYLINE_3D: error = dwg_dxf_POLYLINE_3D (dat, obj); return error | dxf_process_VERTEX_3D (dat, obj, i); case DWG_TYPE_POLYLINE_PFACE: error = dwg_dxf_POLYLINE_PFACE (dat, obj); return error | dxf_process_VERTEX_PFACE (dat, obj, i); case DWG_TYPE_POLYLINE_MESH: error = dwg_dxf_POLYLINE_MESH (dat, obj); return error | dxf_process_VERTEX_MESH (dat, obj, i); case DWG_TYPE_ATTRIB: LOG_WARN ("stale %s subentity", obj->dxfname); return dwg_dxf_ATTRIB (dat, obj); case DWG_TYPE_VERTEX_2D: LOG_WARN ("stale %s subentity", obj->dxfname); return dwg_dxf_VERTEX_2D (dat, obj); case DWG_TYPE_VERTEX_3D: LOG_WARN ("stale %s subentity", obj->dxfname); return dwg_dxf_VERTEX_3D (dat, obj); case DWG_TYPE_VERTEX_MESH: LOG_WARN ("stale %s subentity", obj->dxfname); return dwg_dxf_VERTEX_MESH (dat, obj); case DWG_TYPE_VERTEX_PFACE: LOG_WARN ("stale %s subentity", obj->dxfname); return dwg_dxf_VERTEX_PFACE (dat, obj); case DWG_TYPE_VERTEX_PFACE_FACE: LOG_WARN ("stale %s subentity", obj->dxfname); return dwg_dxf_VERTEX_PFACE_FACE (dat, obj); case DWG_TYPE_ARC: return dwg_dxf_ARC (dat, obj); case DWG_TYPE_CIRCLE: return dwg_dxf_CIRCLE (dat, obj); case DWG_TYPE_LINE: return dwg_dxf_LINE (dat, obj); case DWG_TYPE_DIMENSION_ORDINATE: return dwg_dxf_DIMENSION_ORDINATE (dat, obj); case DWG_TYPE_DIMENSION_LINEAR: return dwg_dxf_DIMENSION_LINEAR (dat, obj); case DWG_TYPE_DIMENSION_ALIGNED: return dwg_dxf_DIMENSION_ALIGNED (dat, obj); case DWG_TYPE_DIMENSION_ANG3PT: return dwg_dxf_DIMENSION_ANG3PT (dat, obj); case DWG_TYPE_DIMENSION_ANG2LN: return dwg_dxf_DIMENSION_ANG2LN (dat, obj); case DWG_TYPE_DIMENSION_RADIUS: return dwg_dxf_DIMENSION_RADIUS (dat, obj); case DWG_TYPE_DIMENSION_DIAMETER: return dwg_dxf_DIMENSION_DIAMETER (dat, obj); case DWG_TYPE_POINT: return dwg_dxf_POINT (dat, obj); case DWG_TYPE__3DFACE: return dwg_dxf__3DFACE (dat, obj); case DWG_TYPE_SOLID: return dwg_dxf_SOLID (dat, obj); case DWG_TYPE_TRACE: return dwg_dxf_TRACE (dat, obj); case DWG_TYPE_SHAPE: return dwg_dxf_SHAPE (dat, obj); case DWG_TYPE_VIEWPORT: return minimal ? 0 : dwg_dxf_VIEWPORT (dat, obj); case DWG_TYPE_ELLIPSE: return dwg_dxf_ELLIPSE (dat, obj); case DWG_TYPE_SPLINE: return dwg_dxf_SPLINE (dat, obj); case DWG_TYPE_REGION: return dwg_dxf_REGION (dat, obj); case DWG_TYPE__3DSOLID: return dwg_dxf__3DSOLID (dat, obj); case DWG_TYPE_BODY: return dwg_dxf_BODY (dat, obj); case DWG_TYPE_RAY: return dwg_dxf_RAY (dat, obj); case DWG_TYPE_XLINE: return dwg_dxf_XLINE (dat, obj); case DWG_TYPE_DICTIONARY: return minimal ? 0 : dwg_dxf_DICTIONARY (dat, obj); case DWG_TYPE_MTEXT: return dwg_dxf_MTEXT (dat, obj); case DWG_TYPE_LEADER: return dwg_dxf_LEADER (dat, obj); case DWG_TYPE_TOLERANCE: return dwg_dxf_TOLERANCE (dat, obj); case DWG_TYPE_MLINE: #if 1 || defined DEBUG_CLASSES // TODO: looks good, but acad import crashes return dwg_dxf_MLINE (dat, obj); #else LOG_WARN ("Unhandled Entity MLINE in out_dxf %u/%lX", obj->index, obj->handle.value) if (0) dwg_dxf_MLINE (dat, obj); return DWG_ERR_UNHANDLEDCLASS; #endif case DWG_TYPE_BLOCK_CONTROL: case DWG_TYPE_BLOCK_HEADER: case DWG_TYPE_LAYER_CONTROL: case DWG_TYPE_LAYER: case DWG_TYPE_STYLE_CONTROL: case DWG_TYPE_STYLE: case DWG_TYPE_LTYPE_CONTROL: case DWG_TYPE_LTYPE: case DWG_TYPE_VIEW_CONTROL: case DWG_TYPE_VIEW: case DWG_TYPE_UCS_CONTROL: case DWG_TYPE_UCS: case DWG_TYPE_VPORT_CONTROL: case DWG_TYPE_VPORT: case DWG_TYPE_APPID_CONTROL: case DWG_TYPE_APPID: case DWG_TYPE_DIMSTYLE_CONTROL: case DWG_TYPE_DIMSTYLE: case DWG_TYPE_VX_CONTROL: case DWG_TYPE_VX_TABLE_RECORD: break; case DWG_TYPE_GROUP: return dwg_dxf_GROUP (dat, obj); case DWG_TYPE_MLINESTYLE: return minimal ? 0 : dwg_dxf_MLINESTYLE (dat, obj); case DWG_TYPE_OLE2FRAME: return minimal ? 0 : dwg_dxf_OLE2FRAME (dat, obj); case DWG_TYPE_DUMMY: return 0; case DWG_TYPE_LONG_TRANSACTION: return minimal ? 0 : dwg_dxf_LONG_TRANSACTION (dat, obj); case DWG_TYPE_LWPOLYLINE: return dwg_dxf_LWPOLYLINE (dat, obj); case DWG_TYPE_HATCH: return dwg_dxf_HATCH (dat, obj); case DWG_TYPE_XRECORD: return minimal ? 0 : dwg_dxf_XRECORD (dat, obj); case DWG_TYPE_PLACEHOLDER: return minimal ? 0 : dwg_dxf_PLACEHOLDER (dat, obj); case DWG_TYPE_PROXY_ENTITY: // avoid unused warnings error |= dwg_dxf_PROXY_ENTITY(dat, obj); return DWG_ERR_UNHANDLEDCLASS; case DWG_TYPE_OLEFRAME: return minimal ? 0 : dwg_dxf_OLEFRAME (dat, obj); case DWG_TYPE_VBA_PROJECT: return minimal ? 0 : dwg_dxf_VBA_PROJECT (dat, obj); case DWG_TYPE_LAYOUT: return minimal ? 0 : dwg_dxf_LAYOUT (dat, obj); default: if (obj->type == obj->parent->layout_type) { return minimal ? 0 : dwg_dxf_LAYOUT (dat, obj); } /* > 500 */ else if ((error = dwg_dxf_variable_type (obj->parent, dat, (Dwg_Object *)obj)) & DWG_ERR_UNHANDLEDCLASS) { Dwg_Data *dwg = obj->parent; int j = obj->type - 500; Dwg_Class *klass = NULL; if (j >= 0 && j < (int)dwg->num_classes) klass = &dwg->dwg_class[j]; if (!klass) { LOG_WARN ("Unknown object, skipping eed/reactors/xdic"); return DWG_ERR_INVALIDTYPE; } return error; } } return DWG_ERR_UNHANDLEDCLASS; } static int dxf_common_entity_handle_data (Bit_Chain *restrict dat, const Dwg_Object *restrict obj) { const Dwg_Data *dwg = obj->parent; const Dwg_Object_Entity *ent = obj->tio.entity; const Dwg_Object_Entity *_obj = ent; int error = 0; BITCODE_BL vcount = 0; // clang-format off #include "common_entity_handle_data.spec" #include "common_entity_data.spec" // clang-format on return error; } RETURNS_NONNULL const char * dxf_format (int code) { if (0 <= code && code < 5) return "%s"; if (code == 5 || code == -5) return "%lX"; if (5 < code && code < 10) return "%s"; if (code < 60) return DXF_FORMAT_FLT; if (code < 80) return "%6i"; if (80 <= code && code <= 99) // BL int32 lgtm [cpp/constant-comparison] return "%9li"; if (code == 100) return "%s"; if (code == 102) return "%s"; if (code == 105) return "%lX"; if (110 <= code && code <= 149) return DXF_FORMAT_FLT; if (160 <= code && code <= 169) return "%12li"; if (170 <= code && code <= 179) return "%6i"; if (210 <= code && code <= 239) return DXF_FORMAT_FLT; if (270 <= code && code <= 289) return "%6i"; if (290 <= code && code <= 299) return "%6i"; // boolean if (300 <= code && code <= 319) return "%s"; if (320 <= code && code <= 369) return "%lX"; if (370 <= code && code <= 389) return "%6i"; if (390 <= code && code <= 399) return "%lX"; if (400 <= code && code <= 409) return "%6i"; if (410 <= code && code <= 419) return "%s"; if (420 <= code && code <= 429) return "%9li"; // int32_t if (430 <= code && code <= 439) return "%s"; if (440 <= code && code <= 449) return "%9li"; // int32_t if (450 <= code && code <= 459) return "%12li"; // long if (460 <= code && code <= 469) return DXF_FORMAT_FLT; if (470 <= code && code <= 479) return "%s"; if (480 <= code && code <= 481) return "%lX"; if (code == 999) return "%s"; if (1000 <= code && code <= 1009) return "%s"; if (1010 <= code && code <= 1059) return DXF_FORMAT_FLT; if (1060 <= code && code <= 1070) return "%6i"; if (code == 1071) return "%9li"; // int32_t if (code == 1002) // string => RC return "%6i"; if (code == 1003) // RL layer return "%9li"; if (code > 1000) return dxf_format (code - 1000); return "(unknown code)"; } /* num => string. for the reverse see in_dxf.c:dxf_fixup_header() TODO: maybe use a table. */ RETURNS_NONNULL const char * dxf_codepage (int code, Dwg_Data *dwg) { if (code == 30 || code == 0) return "ANSI_1252"; // WesternEurope Windows else if (code == 1) return "US_ASCII"; else if (code == 2) return "ISO-8859-1"; // WesternEurope Latin-1 else if (code == 3) return "ISO-8859-2"; // MiddleEurope Latin-2 else if (code == 4) return "ISO-8859-3"; // SouthEurope Latin-3 else if (code == 5) return "ISO-8859-4"; // NorthEurope Latin-4 else if (code == 6) return "ISO-8859-5"; // Cyrillic else if (code == 7) return "ISO-8859-6"; // Arabic else if (code == 8) return "ISO-8859-7"; // Greek else if (code == 9) return "ISO-8859-8"; // Hebrew else if (code == 10) return "ISO-8859-9"; // Turkish Latin-5 else if (code == 11) return "CP437"; // DOS-US, DOS Latin US else if (code == 12) return "CP850"; // WesternEurope DOS else if (code == 13) return "CP852"; // CentralEurope DOS else if (code == 14) return "CP855"; // Cyrillic DOS else if (code == 15) return "CP857"; // Turkish DOS else if (code == 16) return "CP860"; // Portuguese DOS else if (code == 17) return "CP861"; // Icelandic DOS else if (code == 18) return "CP863"; // French DOS else if (code == 19) return "CP864"; // Arabic DOS else if (code == 20) return "CP865"; // Nordic DOS else if (code == 21) return "CP869"; // Greek DOS else if (code == 22) return "CP932"; // Japanese Shift JIS DOS else if (code == 23) return "MACINTOSH"; else if (code == 24) return "BIG5"; // Taiwan DOS else if (code == 25) return "CP949"; // Korean UnifiedHangulCode DOS else if (code == 26) return "JOHAB"; // Korean cp1361 DOS else if (code == 27) return "CP866"; // Russian DOS else if (code == 28) return "ANSI_1250"; // CentralEurope Windows else if (code == 29) return "ANSI_1251"; // Cyrillic Windows else if (code == 31) return "GB2312"; // SimplifiedChinese else if (code == 32) return "ANSI_1253"; // Greek Windows else if (code == 33) return "ANSI_1254"; // Turkish Windows else if (code == 34) return "ANSI_1255"; // Hebrew Windows else if (code == 35) return "ANSI_1256"; // Arabic Windows else if (code == 36) return "ANSI_1257"; // Baltic Windows else if (code == 37) return "ANSI_874"; // Thai Windows else if (code == 38) return "ANSI_932"; // Japanese Windows else if (code == 39) return "ANSI_936"; // gbk UnifiedChinese Windows else if (code == 40) return "ANSI_949"; // Korean Windows else if (code == 41) return "ANSI_950"; // TradChinese Windows else if (code == 42) return "ANSI_1361"; // Korean JOHAB Windows else if (code == 43) return "ANSI_1200"; // UTF-16 LE Microsoft else if (code == 44) return "ANSI_1258"; // Vietnamese Windows else if (dwg->header.version >= R_2007) return "UTF-8"; // dwg internally: UCS-16, for DXF: UTF-8 else return ""; } // see // https://www.autodesk.com/techpubs/autocad/acad2000/dxf/header_section_group_codes_dxf_02.htm GCC30_DIAG_IGNORE (-Wformat-nonliteral) AFL_GCC_TOOBIG static int dxf_header_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) { Dwg_Header_Variables *_obj = &dwg->header_vars; Dwg_Object *obj = NULL; double ms; const int minimal = dwg->opts & DWG_OPTS_MINIMAL; const char *codepage = dxf_codepage (dwg->header.codepage, dwg); if (!*codepage) { LOG_WARN ("Unknown codepage %d, assuming ANSI_1252", dwg->header.codepage); } // clang-format off #include "header_variables_dxf.spec" // clang-format on return 0; } AFL_GCC_POP // only called since r2000. but not really needed, unless referenced static int dxf_classes_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) { BITCODE_BS j; SECTION (CLASSES); LOG_TRACE ("num_classes: %u\n", dwg->num_classes); for (j = 0; j < dwg->num_classes; j++) { const char *dxfname = dwg->dwg_class[j].dxfname; if (!dxfname) continue; // some classes are now builtin if (dat->version >= R_2004 && (strEQc (dxfname, "ACDBPLACEHOLDER") || strEQc (dxfname, "LAYOUT"))) continue; if (strEQc (dxfname, "DATATABLE")) dxfname = "ACDBDATATABLE"; RECORD (CLASS); VALUE_TV (dxfname, 1); VALUE_T (dwg->dwg_class[j].cppname, 2); if (strEQc (dxfname, "SPATIAL_INDEX")) VALUE_TFF ("AutoCAD 2000", 3) // special-cased for DXF else VALUE_T (dwg->dwg_class[j].appname, 3) VALUE_RL (dwg->dwg_class[j].proxyflag, 90); SINCE (R_2004) { VALUE_RL (dwg->dwg_class[j].num_instances, 91); } VALUE_RS (dwg->dwg_class[j].is_zombie, 280); // acad: was-a-zombie // Is-an-entity. 1f2 for entities, 1f3 for objects VALUE_RS (dwg->dwg_class[j].item_class_id == 0x1F2 ? 1 : 0, 281); } ENDSEC (); return 0; } static int dxf_tables_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) { int error = 0; unsigned int i; BITCODE_BL vcount; SECTION (TABLES); SINCE (R_9c1) { Dwg_Object *ctrl = dwg_get_first_object (dwg, DWG_TYPE_VPORT_CONTROL); if (ctrl && ctrl->tio.object && ctrl->tio.object->tio.VPORT_CONTROL) { Dwg_Object_VPORT_CONTROL *_ctrl = ctrl->tio.object->tio.VPORT_CONTROL; Dwg_Object *obj = ctrl; TABLE (VPORT); // add handle 5 here at first COMMON_TABLE_CONTROL_FLAGS; error |= dwg_dxf_VPORT_CONTROL (dat, ctrl); // TODO how far back can DXF read 1000? if (dat->version != dat->from_version && dat->from_version >= R_2000) { /* if saved from newer version, eg. AC1032: */ VALUE_TV ("ACAD", 1001); VALUE_TV ("DbSaveVer", 1000); VALUE_RS (dwg->header.dwg_version, 1071); // so that 69 is R_2018 } for (i = 0; i < _ctrl->num_entries; i++) { if (!_ctrl->entries) break; if (!_ctrl->entries[i]) continue; obj = dwg_ref_object (dwg, _ctrl->entries[i]); if (obj && obj->type == DWG_TYPE_VPORT) { // reordered in the DXF: 2,70,10,11,12,13,14,15,16,... // special-cased in the spec error |= dwg_dxf_VPORT (dat, obj); } } ENDTAB (); } } { Dwg_Object *ctrl = dwg_get_first_object (dwg, DWG_TYPE_LTYPE_CONTROL); if (ctrl && ctrl->tio.object && ctrl->tio.object->tio.LTYPE_CONTROL) { Dwg_Object_LTYPE_CONTROL *_ctrl = ctrl->tio.object->tio.LTYPE_CONTROL; Dwg_Object *obj = ctrl; TABLE (LTYPE); COMMON_TABLE_CONTROL_FLAGS; error |= dwg_dxf_LTYPE_CONTROL (dat, ctrl); // first the 2 builtin ltypes: ByBlock, ByLayer if ((obj = dwg_ref_object (dwg, dwg->header_vars.LTYPE_BYBLOCK)) && obj->type == DWG_TYPE_LTYPE) { error |= dwg_dxf_LTYPE (dat, obj); } if ((obj = dwg_ref_object (dwg, dwg->header_vars.LTYPE_BYLAYER)) && obj->type == DWG_TYPE_LTYPE) { error |= dwg_dxf_LTYPE (dat, obj); } // here LTYPE_CONTINUOUS is already included for (i = 0; i < _ctrl->num_entries; i++) { if (!_ctrl->entries) break; if (!_ctrl->entries[i]) continue; obj = dwg_ref_object (dwg, _ctrl->entries[i]); if (obj && obj->type == DWG_TYPE_LTYPE) { error |= dwg_dxf_LTYPE (dat, obj); } } ENDTAB (); } } { Dwg_Object *ctrl = dwg_get_first_object (dwg, DWG_TYPE_LAYER_CONTROL); if (ctrl && ctrl->tio.object && ctrl->tio.object->tio.LAYER_CONTROL) { Dwg_Object_LAYER_CONTROL *_ctrl = ctrl->tio.object->tio.LAYER_CONTROL; Dwg_Object *obj = ctrl; TABLE (LAYER); COMMON_TABLE_CONTROL_FLAGS; error |= dwg_dxf_LAYER_CONTROL (dat, ctrl); for (i = 0; i < _ctrl->num_entries; i++) { if (!_ctrl->entries) break; if (!_ctrl->entries[i]) continue; obj = dwg_ref_object (dwg, _ctrl->entries[i]); if (obj && obj->type == DWG_TYPE_LAYER) error |= dwg_dxf_LAYER (dat, obj); // else if (obj && obj->type == DWG_TYPE_DICTIONARY) // error |= dwg_dxf_DICTIONARY(dat, obj); } ENDTAB (); } } { Dwg_Object *ctrl = dwg_get_first_object (dwg, DWG_TYPE_STYLE_CONTROL); if (ctrl && ctrl->tio.object && ctrl->tio.object->tio.STYLE_CONTROL) { Dwg_Object_STYLE_CONTROL *_ctrl = ctrl->tio.object->tio.STYLE_CONTROL; Dwg_Object *obj = ctrl; TABLE (STYLE); COMMON_TABLE_CONTROL_FLAGS; error |= dwg_dxf_STYLE_CONTROL (dat, ctrl); for (i = 0; i < _ctrl->num_entries; i++) { if (!_ctrl->entries) break; if (!_ctrl->entries[i]) continue; obj = dwg_ref_object (dwg, _ctrl->entries[i]); if (obj && obj->type == DWG_TYPE_STYLE) { error |= dwg_dxf_STYLE (dat, obj); } } ENDTAB (); } } { Dwg_Object *ctrl = dwg_get_first_object (dwg, DWG_TYPE_VIEW_CONTROL); if (ctrl && ctrl->tio.object && ctrl->tio.object->tio.VIEW_CONTROL) { Dwg_Object_VIEW_CONTROL *_ctrl = ctrl->tio.object->tio.VIEW_CONTROL; Dwg_Object *obj = ctrl; TABLE (VIEW); COMMON_TABLE_CONTROL_FLAGS; error |= dwg_dxf_VIEW_CONTROL (dat, ctrl); for (i = 0; i < _ctrl->num_entries; i++) { if (!_ctrl->entries) break; if (!_ctrl->entries[i]) continue; obj = dwg_ref_object (dwg, _ctrl->entries[i]); if (obj && obj->type == DWG_TYPE_VIEW) error |= dwg_dxf_VIEW (dat, obj); } ENDTAB (); } } SINCE (R_9c1) { Dwg_Object *ctrl = dwg_get_first_object (dwg, DWG_TYPE_UCS_CONTROL); if (ctrl && ctrl->tio.object && ctrl->tio.object->tio.UCS_CONTROL) { Dwg_Object_UCS_CONTROL *_ctrl = ctrl->tio.object->tio.UCS_CONTROL; Dwg_Object *obj = ctrl; TABLE (UCS); COMMON_TABLE_CONTROL_FLAGS; error |= dwg_dxf_UCS_CONTROL (dat, ctrl); for (i = 0; i < _ctrl->num_entries; i++) { if (!_ctrl->entries) break; if (!_ctrl->entries[i]) continue; obj = dwg_ref_object (dwg, _ctrl->entries[i]); if (obj && obj->type == DWG_TYPE_UCS) { error |= dwg_dxf_UCS (dat, obj); } } ENDTAB (); } } SINCE (R_10c1) { Dwg_Object *ctrl = dwg_get_first_object (dwg, DWG_TYPE_APPID_CONTROL); if (ctrl && ctrl->tio.object && ctrl->tio.object->tio.APPID_CONTROL) { Dwg_Object_APPID_CONTROL *_ctrl = ctrl->tio.object->tio.APPID_CONTROL; Dwg_Object *obj = ctrl; TABLE (APPID); COMMON_TABLE_CONTROL_FLAGS; error |= dwg_dxf_APPID_CONTROL (dat, ctrl); for (i = 0; i < _ctrl->num_entries; i++) { if (!_ctrl->entries) break; if (!_ctrl->entries[i]) continue; obj = dwg_ref_object (dwg, _ctrl->entries[i]); if (obj && obj->type == DWG_TYPE_APPID) { error |= dwg_dxf_APPID (dat, obj); } } ENDTAB (); } } SINCE (R_10c1) { Dwg_Object *ctrl = dwg_get_first_object (dwg, DWG_TYPE_DIMSTYLE_CONTROL); if (ctrl && ctrl->tio.object && ctrl->tio.object->tio.DIMSTYLE_CONTROL) { Dwg_Object_DIMSTYLE_CONTROL *_ctrl = ctrl->tio.object->tio.DIMSTYLE_CONTROL; Dwg_Object *obj = ctrl; TABLE (DIMSTYLE); COMMON_TABLE_CONTROL_FLAGS; error |= dwg_dxf_DIMSTYLE_CONTROL (dat, ctrl); // ignoring morehandles for (i = 0; i < _ctrl->num_entries; i++) { if (!_ctrl->entries) break; if (!_ctrl->entries[i]) continue; obj = dwg_ref_object (dwg, _ctrl->entries[i]); if (obj && obj->type == DWG_TYPE_DIMSTYLE) { error |= dwg_dxf_DIMSTYLE (dat, obj); } } ENDTAB (); } } // fool the warnings. this table is nowhere to be found in the wild. maybe pre-R_11 if (0) { Dwg_Object *ctrl = dwg_get_first_object (dwg, DWG_TYPE_VX_CONTROL); if (ctrl) { Dwg_Object_VX_CONTROL *_ctrl = ctrl->tio.object->tio.VX_CONTROL; Dwg_Object *obj = ctrl; TABLE (VX); COMMON_TABLE_CONTROL_FLAGS; error |= dwg_dxf_VX_CONTROL (dat, ctrl); for (i = 0; i < _ctrl->num_entries; i++) { if (!_ctrl->entries) break; if (!_ctrl->entries[i]) continue; obj = dwg_ref_object (dwg, _ctrl->entries[i]); if (obj && obj->type == DWG_TYPE_VX_TABLE_RECORD) { error |= dwg_dxf_VX_TABLE_RECORD (dat, obj); } } ENDTAB (); } } SINCE (R_12) { Dwg_Object *ctrl, *obj; Dwg_Object_BLOCK_CONTROL *_ctrl = dwg_block_control (dwg); Dwg_Object_Ref *ref; Dwg_Object *mspace = NULL, *pspace = NULL; if (!_ctrl) { LOG_ERROR ("BLOCK_CONTROL missing"); return DWG_ERR_INVALIDDWG; } ctrl = dwg_obj_generic_to_object (_ctrl, &error); if (!ctrl || ctrl->fixedtype != DWG_TYPE_BLOCK_CONTROL) return DWG_ERR_INVALIDDWG; obj = ctrl; TABLE (BLOCK_RECORD); COMMON_TABLE_CONTROL_FLAGS; error |= dwg_dxf_BLOCK_CONTROL (dat, ctrl); #if 0 // unordered pspace for (i = 0; i < dwg->num_objects; i++) { Dwg_Object *hdr = &dwg->object[i]; if (hdr && hdr->supertype == DWG_SUPERTYPE_OBJECT && hdr->type == DWG_TYPE_BLOCK_HEADER) { // not necessarily in the same order as DXF, but exhaustive RECORD (BLOCK_RECORD); error |= dwg_dxf_BLOCK_HEADER (dat, hdr); } } #else mspace = dwg_model_space_object (dwg); if (!mspace) return DWG_ERR_INVALIDDWG; RECORD (BLOCK_RECORD); error |= dwg_dxf_BLOCK_HEADER (dat, mspace); ref = dwg_paper_space_ref (dwg); pspace = ref ? dwg_ref_object (dwg, ref) : NULL; if (pspace) { RECORD (BLOCK_RECORD); error |= dwg_dxf_BLOCK_HEADER (dat, pspace); } for (i = 0; i < dwg->block_control.num_entries; i++) { if (!dwg->block_control.entries) break; if (!dwg->block_control.entries[i]) continue; obj = dwg_ref_object (dwg, dwg->block_control.entries[i]); if (obj && obj->type == DWG_TYPE_BLOCK_HEADER && obj != mspace && obj != pspace) { RECORD (BLOCK_RECORD); error |= dwg_dxf_BLOCK_HEADER (dat, obj); } } #endif ENDTAB (); } ENDSEC (); return 0; } static void dxf_ENDBLK_empty (Bit_Chain *restrict dat, const Dwg_Object *restrict hdr) { // temp. only. not registered in dwg->object[] Dwg_Object *obj = (Dwg_Object *)calloc (1, sizeof (Dwg_Object)); Dwg_Data *dwg = hdr->parent; // Dwg_Entity_ENDBLK *_obj; obj->parent = dwg; obj->index = dwg->num_objects; dwg_setup_ENDBLK (obj); obj->tio.entity->ownerhandle = (BITCODE_H)calloc (1, sizeof (Dwg_Object_Ref)); obj->tio.entity->ownerhandle->obj = (Dwg_Object *)hdr; obj->tio.entity->ownerhandle->handleref = hdr->handle; obj->tio.entity->ownerhandle->absolute_ref = hdr->handle.value; //_obj = obj->tio.entity->tio.ENDBLK; dwg_dxf_ENDBLK (dat, obj); free (obj->tio.entity->tio.ENDBLK); free (obj->tio.entity->ownerhandle); free (obj->tio.entity); free (obj); } // a BLOCK_HEADER static int dxf_block_write (Bit_Chain *restrict dat, const Dwg_Object *restrict hdr, const Dwg_Object *restrict mspace, const Dwg_Object *restrict pspace, int *restrict i) { int error = 0; const Dwg_Object_BLOCK_HEADER *restrict _hdr = hdr->tio.object->tio.BLOCK_HEADER; Dwg_Object *restrict obj = get_first_owned_block (hdr); // BLOCK Dwg_Object *restrict endblk = NULL; unsigned long int mspace_ref = mspace ? mspace->handle.value : 0; unsigned long int pspace_ref = pspace ? pspace->handle.value : 0; if (obj) error |= dwg_dxf_object (dat, obj, i); else { SINCE (R_2004) { // first_owned_block if (IS_FROM_TU (dat)) { char *s = bit_convert_TU ((BITCODE_TU)_hdr->name); LOG_ERROR ("BLOCK_HEADER %s block_entity[0] missing", s); free (s); } else LOG_ERROR ("BLOCK_HEADER %s block_entity[0] missing", _hdr->name); return DWG_ERR_INVALIDDWG; } else LOG_WARN ("BLOCK_HEADER %s block_entity[0] missing", _hdr->name); } // Skip all *Model_Space and *Paper_Space entities, esp. new ones: UNDERLAY, MULTILEADER, ... // They are all under ENTITIES later. But WIPEOUT and VIEWPORT is here. // Note: the objects may vary (e.g. example_2000), but the index not. if ((hdr == mspace) || (hdr->index == mspace->index)) obj = NULL; else if ((hdr == pspace) || (pspace && hdr->index == pspace->index)) obj = NULL; else obj = get_first_owned_entity (hdr); // first_entity or entities[0] while (obj) { if (obj->supertype == DWG_SUPERTYPE_ENTITY && obj->fixedtype != DWG_TYPE_ENDBLK && obj->tio.entity != NULL && (obj->tio.entity->entmode != 2 || (obj->tio.entity->ownerhandle != NULL && obj->tio.entity->ownerhandle->absolute_ref != mspace_ref && obj->tio.entity->ownerhandle->absolute_ref != pspace_ref))) error |= dwg_dxf_object (dat, obj, i); obj = get_next_owned_block_entity (hdr, obj); // until last_entity } endblk = get_last_owned_block (hdr); if (endblk) { error |= dwg_dxf_ENDBLK (dat, endblk); LOG_INFO ("\n") } else { LOG_WARN ("Empty ENDBLK for \"%s\" %lX", _hdr->name, hdr ? hdr->handle.value : 0); dxf_ENDBLK_empty (dat, hdr); LOG_INFO ("\n") } return error; } static int dxf_blocks_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) { int error = 0; int i; Dwg_Object *restrict mspace = dwg_model_space_object (dwg); Dwg_Object *restrict pspace = dwg_paper_space_object (dwg); if (!mspace) return DWG_ERR_UNHANDLEDCLASS; // If there's no *Model_Space block skip this BLOCKS section. // Or try handle 1F with r2000+, 17 with r14 // obj = get_first_owned_block(hdr); // if (!obj) // obj = dwg_resolve_handle(dwg, dwg->header.version >= R_2000 ? 0x1f : // 0x17); // if (!obj) // return 1; SECTION (BLOCKS); /* There may be unconnected pspace blocks (not caught by above), such as pspace referred by a LAYOUT or DIMENSION, so for simplicity just scan all BLOCK_HEADER's and just skip *Model_Space and *Paper_Space. pspace might be NULL. #81 BLOCK_HEADER - LAYOUT - BLOCK - ENDBLK */ for (i = 0; (BITCODE_BL)i < dwg->num_objects; i++) { const Dwg_Object *restrict obj = &dwg->object[i]; if (obj->supertype == DWG_SUPERTYPE_OBJECT && obj->type == DWG_TYPE_BLOCK_HEADER) { error |= dxf_block_write (dat, obj, mspace, pspace, &i); } } ENDSEC (); return error; } static int dxf_entities_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) { int error = 0; Dwg_Object *restrict ms = dwg_model_space_object (dwg); Dwg_Object *restrict ps = dwg_paper_space_object (dwg); Dwg_Object *obj; if (!ms) return DWG_ERR_INVALIDDWG; SECTION (ENTITIES); // how to order the entities: // 1. first all ms, then all ps #if 1 // First mspace obj = get_first_owned_entity (ms); // first_entity or entities[0] while (obj) { int i = obj->index; error |= dwg_dxf_object (dat, obj, &i); obj = get_next_owned_block_entity (ms, obj); // until last_entity } // Then all pspace entities. just filter out other BLOCKS entities if (ps) { obj = get_first_owned_entity (ps); while (obj) { int i = obj->index; error |= dwg_dxf_object (dat, obj, &i); obj = get_next_owned_block_entity (ps, obj); } } #elif 0 // 2. all entities in iteration order. filter out not owned by ms or ps entities. obj = get_first_owned_entity (ms); if (!obj) obj = get_first_owned_entity (ps); while (obj) { int i = obj->index; Dwg_Object_Ref *owner = obj->tio.entity->ownerhandle; if (!owner || (owner->obj == ms || owner->obj == ps)) error |= dwg_dxf_object (dat, obj, &i); obj = dwg_next_entity (obj); } #else // 3. all objects in handle order, filter out not owned ms or ps entities. for (int i = 0; (BITCODE_BL)i < dwg->num_objects; i++) { Dwg_Object *obj = &dwg->object[i]; if (obj->supertype == DWG_SUPERTYPE_ENTITY && obj->type != DWG_TYPE_BLOCK && obj->type != DWG_TYPE_ENDBLK) { Dwg_Object_Ref *owner = obj->tio.entity->ownerhandle; if (!owner || (owner->obj == ms || owner->obj == ps)) error |= dwg_dxf_object (dat, obj, &i); } } #endif ENDSEC (); return error; } // check for valid ownerhandle. set to 0 if not int dxf_validate_DICTIONARY (Dwg_Object *obj) { Dwg_Object_Ref *ownerhandle = obj->tio.object->ownerhandle; if (ownerhandle && !dwg_ref_object (obj->parent, ownerhandle)) { LOG_INFO ("Wrong DICTIONARY.ownerhandle %lX\n", ownerhandle->absolute_ref); ownerhandle->absolute_ref = 0; return 0; } return 1; } static int dxf_objects_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) { int error = 0; int i; SECTION (OBJECTS); for (i = 0; (BITCODE_BL)i < dwg->num_objects; i++) { const Dwg_Object *restrict obj = &dwg->object[i]; if (obj->supertype == DWG_SUPERTYPE_OBJECT && obj->type != DWG_TYPE_BLOCK_HEADER && !dwg_obj_is_control (obj)) error |= dwg_dxf_object (dat, obj, &i); } ENDSEC (); return error; } // New ACDSDATA r2013+ section, with ACDSSCHEMA elements static int dxf_acds_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) { //const char *section = "AcDsProtoype"; Dwg_AcDs *_obj = &dwg->acds; if (0 && _obj->num_segidx) { SECTION (ACSDSDATA); // FIXME // 70 // 71 // 0 ACDSSCHEMA // TODO ... ENDSEC (); } return 0; } GCC30_DIAG_IGNORE (-Wformat-nonliteral) // TODO: Beware, there's also a new ACDSDATA section, with ACDSSCHEMA elements // and the Thumbnail_Data (per block?) static int dxf_thumbnail_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) { Bit_Chain *pic = (Bit_Chain *)&dwg->thumbnail; if (pic->chain && pic->size && pic->size > 10) { SECTION (THUMBNAILIMAGE); VALUE_RL (pic->size, 90); VALUE_BINARY (pic->chain, pic->size, 310); ENDSEC (); } return 0; } AFL_GCC_TOOBIG EXPORT int dwg_write_dxf (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) { const int minimal = dwg->opts & DWG_OPTS_MINIMAL; //struct Dwg_Header *obj = &dwg->header; loglevel = dwg->opts & DWG_OPTS_LOGLEVEL; if (dat->from_version == R_INVALID) dat->from_version = dwg->header.from_version; if (dwg->header.version <= R_2000 && dwg->header.from_version > R_2000) dwg_fixup_BLOCKS_entities (dwg); VALUE_TV (PACKAGE_STRING, 999); // A minimal header requires only $ACADVER, $HANDSEED, and then ENTITIES // see https://pythonhosted.org/ezdxf/dxfinternals/filestructure.html dxf_header_write (dat, dwg); if (!minimal) { // if downgraded to r13, but we still have classes, keep the // classes if ((dat->from_version >= R_13 && dwg->num_classes) || dat->version >= R_2000) { if (dxf_classes_write (dat, dwg) >= DWG_ERR_CRITICAL) goto fail; } if (dxf_tables_write (dat, dwg) >= DWG_ERR_CRITICAL) goto fail; if (dxf_blocks_write (dat, dwg) >= DWG_ERR_CRITICAL) goto fail; } if (dxf_entities_write (dat, dwg) >= DWG_ERR_CRITICAL) goto fail; if (!minimal) { SINCE (R_13) { if (dxf_objects_write (dat, dwg) >= DWG_ERR_CRITICAL) goto fail; } SINCE (R_2013) { if (dxf_acds_write (dat, dwg) >= DWG_ERR_CRITICAL) goto fail; } SINCE (R_2000) { if (dxf_thumbnail_write (dat, dwg) >= DWG_ERR_CRITICAL) goto fail; } } RECORD (EOF); return 0; fail: return 1; } AFL_GCC_POP #undef IS_PRINT #undef IS_DXF