Bug Summary

File:libraries/libdxfrw/src/drw_entities.cpp
Warning:line 6929, column 5
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name drw_entities.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/home/runner/work/LibreCAD/LibreCAD/libraries/libdxfrw -fcoverage-compilation-dir=/home/runner/work/LibreCAD/LibreCAD/libraries/libdxfrw -resource-dir /usr/lib/llvm-18/lib/clang/18 -D _REENTRANT -D MUPARSER_STATIC -D QT_NO_DEBUG -I . -I ../../../Qt/6.9.0/gcc_64/mkspecs/linux-g++ -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=gnu++1z -fdeprecated-macro -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/runner/work/LibreCAD/LibreCAD/out/2026-08-25-160220-5038-1 -x c++ src/drw_entities.cpp
1/******************************************************************************
2** libDXFrw - Library to read/write DXF files (ascii & binary) **
3** **
4** Copyright (C) 2016-2022 A. Stebich (librecad@mail.lordofbikes.de) **
5** Copyright (C) 2011-2015 José F. Soriano, rallazz@gmail.com **
6** Copyright (C) 2026 LibreCAD (librecad.org) **
7** **
8** This library is free software, licensed under the terms of the GNU **
9** General Public License as published by the Free Software Foundation, **
10** either version 2 of the License, or (at your option) any later version. **
11** You should have received a copy of the GNU General Public License **
12** along with this program. If not, see <http://www.gnu.org/licenses/>. **
13******************************************************************************/
14
15#include <algorithm>
16#include <cctype>
17#include <cmath>
18#include <cstdio>
19#include <cstdlib>
20#include <cstring>
21#include <limits>
22#include <vector>
23#include "drw_entities.h"
24#include "intern/dxfreader.h"
25#include "intern/dwgbuffer.h"
26#include "intern/dwgbufferw.h"
27#include "intern/drw_textcodec.h"
28#include "intern/drw_dbg.h"
29#include "intern/drw_reserve.h"
30#include "intern/dwgreader.h"
31
32namespace {
33
34constexpr std::uint32_t kMaxTableRows = 10000;
35constexpr std::uint32_t kMaxTableColumns = 1000;
36constexpr std::uint32_t kMaxTableCells = 200000;
37constexpr std::uint32_t kMaxTableItems = 100000;
38constexpr std::uint32_t kMaxTableStringBytes = 16 * 1024 * 1024;
39constexpr std::int32_t kMaxLWPolylineVertices = 1000000;
40constexpr std::int32_t kMaxSplineItems = 1000000;
41constexpr std::int32_t kMaxSplineDegree = 1024;
42
43constexpr std::int32_t kSplineFlagMethodFitPoints = 1;
44constexpr std::int32_t kSplineFlagClosed = 4;
45constexpr std::int32_t kSplineFlagUseKnotParameter = 8;
46constexpr std::int32_t kSplineKnotParamCustom = 15;
47
48bool isValidCount(std::int32_t count, std::int32_t maxCount) {
49 return count >= 0 && count <= maxCount;
50}
51
52int hexNibble(char c) {
53 if (c >= '0' && c <= '9')
54 return c - '0';
55 if (c >= 'A' && c <= 'F')
56 return c - 'A' + 10;
57 if (c >= 'a' && c <= 'f')
58 return c - 'a' + 10;
59 return -1;
60}
61
62bool decodeHexBytes(const std::string& hex, std::vector<std::uint8_t>& out) {
63 if ((hex.size() % 2) != 0)
64 return false;
65
66 std::vector<std::uint8_t> decoded;
67 decoded.reserve(hex.size() / 2);
68 for (std::size_t i = 0; i < hex.size(); i += 2) {
69 const int hi = hexNibble(hex[i]);
70 const int lo = hexNibble(hex[i + 1]);
71 if (hi < 0 || lo < 0)
72 return false;
73 decoded.push_back(static_cast<std::uint8_t>((hi << 4) | lo));
74 }
75
76 out = std::move(decoded);
77 return true;
78}
79
80void appendBytes(std::vector<std::uint8_t>& out,
81 const std::vector<std::uint8_t>& bytes) {
82 out.insert(out.end(), bytes.begin(), bytes.end());
83}
84
85void appendTextBytes(std::vector<std::uint8_t>& out, const std::string& text) {
86 out.insert(out.end(), text.begin(), text.end());
87}
88
89std::uint64_t currentDwgBit(const dwgBuffer *buf) {
90 return buf->getPosition() * 8 + buf->getBitPos();
91}
92
93DRW_DwgSubrecordRange makeDwgSubrecordRange(const char *name, std::uint64_t startBit,
94 std::uint64_t endBit, DRW::Version version,
95 std::uint32_t count, bool parseComplete) {
96 DRW_DwgSubrecordRange range;
97 range.m_name = name;
98 range.m_startBit = startBit;
99 range.m_bitSize = endBit >= startBit ? endBit - startBit : 0;
100 range.m_version = version;
101 range.m_count = count;
102 range.m_parseComplete = parseComplete;
103 return range;
104}
105
106bool isValidSplineDegree(int degree) {
107 return degree >= 1 && degree <= kMaxSplineDegree;
108}
109
110bool isValidControlSplineLayout(int degree, std::int32_t knotCount, std::int32_t controlCount) {
111 if (!isValidSplineDegree(degree) || !isValidCount(knotCount, kMaxSplineItems) ||
112 !isValidCount(controlCount, kMaxSplineItems)) {
113 return false;
114 }
115
116 if (controlCount < degree + 1) {
117 return false;
118 }
119
120 const std::int64_t expectedKnots = static_cast<std::int64_t>(controlCount) + degree + 1;
121 return expectedKnots <= kMaxSplineItems && knotCount == expectedKnots;
122}
123
124bool isValidFitSplineLayout(int degree, std::int32_t fitCount) {
125 return isValidSplineDegree(degree) && isValidCount(fitCount, kMaxSplineItems) &&
126 fitCount >= 2;
127}
128
129bool differsFromUnitWeight(double weight) {
130 return std::fabs(weight - 1.0) > 1e-12;
131}
132
133//! \brief Compare two doubles by stored representation.
134//! For values a compact DWG form restores verbatim, the match has to be exact:
135//! dataFlags 3 makes the reader return exactly 1.0 and dataFlags 2 makes it
136//! return xscale for y and z (see DRW_Insert::parseDwg), so picking those forms
137//! on a tolerant match would silently round the scale that gets written. This
138//! asks the question that is actually meant - "will the reader reconstruct this
139//! value?" - without floating point comparison semantics, and as a side effect
140//! keeps -0.0 distinct from 0.0, which `==` does not.
141bool sameStoredDouble(double a, double b) {
142 return std::memcmp(&a, &b, sizeof a) == 0;
143}
144
145void putHardPointerHandle(dwgBufferW *buf, std::uint32_t ref) {
146 dwgHandle h;
147 h.code = 5;
148 h.ref = ref;
149 h.size = 0;
150 if (ref != 0) {
151 std::uint32_t t = ref;
152 while (t != 0) {
153 t >>= 8;
154 ++h.size;
155 }
156 }
157 buf->putHandle(h);
158}
159
160void putNullableHardPointerHandle(dwgBufferW *buf, std::uint32_t ref) {
161 dwgHandle h;
162 h.code = ref == 0 ? 0 : 5;
163 h.ref = ref;
164 h.size = 0;
165 if (ref != 0) {
166 std::uint32_t t = ref;
167 while (t != 0) {
168 t >>= 8;
169 ++h.size;
170 }
171 }
172 buf->putHandle(h);
173}
174
175std::uint16_t bitShortFromInt(int value) {
176 if (value < 0)
177 return 0;
178 if (value > 0xffff)
179 return 0xffff;
180 return static_cast<std::uint16_t>(value);
181}
182
183std::uint32_t readTableHandle(dwgBuffer *hdlBuf) {
184 if (hdlBuf == nullptr || !hdlBuf->isGood())
185 return 0;
186 dwgHandle h = hdlBuf->getHandle();
187 return h.ref;
188}
189
190void seekTableObjectHandleStream(DRW::Version version, dwgBuffer *buf, std::uint32_t objSize) {
191 if (version > DRW::AC1018) {
192 buf->setPosition(objSize >> 3);
193 buf->setBitPos(objSize & 7);
194 }
195}
196
197void readTableObjectCommonHandles(dwgBuffer *buf, std::uint32_t baseHandle,
198 std::int32_t numReactors, std::uint8_t xDictFlag,
199 int *parentHandle) {
200 dwgHandle parentH = buf->getOffsetHandle(baseHandle);
201 if (parentHandle)
202 *parentHandle = parentH.ref;
203 for (int i = 0; i < numReactors; ++i)
204 buf->getOffsetHandle(baseHandle);
205 if (xDictFlag != 1)
206 buf->getOffsetHandle(baseHandle);
207}
208
209bool readTableValueBytes(dwgBuffer *buf, std::vector<std::uint8_t>& raw, const char *label) {
210 const std::uint32_t byteCount = buf->getBitLong();
211 if (byteCount > kMaxTableStringBytes) {
212 DRW_DBG(label)DRW_dbg::dbg(label); DRW_DBG(" too large: ")DRW_dbg::dbg(" too large: "); DRW_DBG(byteCount)DRW_dbg::dbg(byteCount); DRW_DBG("\n")DRW_dbg::dbg("\n");
213 return false;
214 }
215 raw.resize(byteCount);
216 const bool good = byteCount == 0 || buf->getBytes(raw.data(), raw.size());
217 if (!good) {
218 DRW_DBG(label)DRW_dbg::dbg(label); DRW_DBG(" byte payload read failed, size: ")DRW_dbg::dbg(" byte payload read failed, size: "); DRW_DBG(byteCount)DRW_dbg::dbg(byteCount);
219 DRW_DBG(" remaining: ")DRW_dbg::dbg(" remaining: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
220 }
221 return good;
222}
223
224UTF8STRINGstd::string decodeTableValueText(DRW::Version version, dwgBuffer *buf, const std::vector<std::uint8_t>& raw) {
225 if (raw.empty())
226 return UTF8STRINGstd::string();
227 std::string s(reinterpret_cast<const char*>(raw.data()), raw.size());
228 if (version > DRW::AC1018 && s.size() >= 2 && s[s.size() - 1] == '\0'
229 && s[s.size() - 2] == '\0') {
230 s.resize(s.size() - 2);
231 } else {
232 while (!s.empty() && s.back() == '\0')
233 s.pop_back();
234 }
235 if (buf->decoder)
236 s = buf->decoder->toUtf8(s);
237 return s;
238}
239
240UTF8STRINGstd::string readTableText(DRW::Version version, dwgBuffer *buf) {
241 if (!buf)
242 return UTF8STRINGstd::string();
243 if (version <= DRW::AC1018)
244 return buf->getVariableText(version, false);
245
246 const std::uint32_t byteLen = buf->getBitShort();
247 if (byteLen == 0)
248 return UTF8STRINGstd::string();
249 if (byteLen > kMaxTableStringBytes) {
250 DRW_DBG("TABLE text byte length invalid: ")DRW_dbg::dbg("TABLE text byte length invalid: "); DRW_DBG(byteLen)DRW_dbg::dbg(byteLen); DRW_DBG("\n")DRW_dbg::dbg("\n");
251 return UTF8STRINGstd::string();
252 }
253
254 std::vector<std::uint8_t> raw(static_cast<size_t>(byteLen) + 2, 0);
255 if (!buf->getBytes(raw.data(), byteLen))
256 return UTF8STRINGstd::string();
257
258 std::string s(reinterpret_cast<const char*>(raw.data()), byteLen);
259 if (buf->decoder)
260 s = buf->decoder->toUtf8(s);
261 return s;
262}
263
264bool readTableValuePoint(dwgBuffer *buf, DRW_CadValue& value, int dimensions) {
265 value.m_dataSize = static_cast<std::uint32_t>(buf->getBitLong());
266 const std::uint32_t expectedSize = static_cast<std::uint32_t>(dimensions) * 8;
267 if (value.m_dataSize > kMaxTableStringBytes)
268 return false;
269 if (value.m_dataSize < expectedSize) {
270 value.m_rawData.resize(value.m_dataSize);
271 if (value.m_dataSize > 0 && !buf->getBytes(value.m_rawData.data(), value.m_rawData.size()))
272 return false;
273 value.m_value.addBinary(310, value.m_rawData);
274 return true;
275 }
276
277 DRW_Coord c;
278 c.x = buf->getRawDouble();
279 c.y = buf->getRawDouble();
280 c.z = dimensions == 3 ? buf->getRawDouble() : 0.0;
281 value.m_value.addCoord(11, c);
282
283 const std::uint32_t extraBytes = value.m_dataSize - expectedSize;
284 value.m_rawData.resize(extraBytes);
285 return extraBytes == 0 || buf->getBytes(value.m_rawData.data(), value.m_rawData.size());
286}
287
288bool readTableCadValue(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf,
289 dwgBuffer *hdlBuf, DRW_CadValue& value) {
290 if (version > DRW::AC1018)
291 value.m_formatFlags = buf->getBitLong();
292
293 value.m_dataType = buf->getBitLong();
294 const bool emptyR2007Value = version > DRW::AC1018 && (value.m_formatFlags & 3);
295 if (!emptyR2007Value) {
296 switch (value.m_dataType) {
297 case 0:
298 case 1:
299 value.m_value.addInt(91, buf->getBitLong());
300 break;
301 case 2:
302 value.m_value.addDouble(140, buf->getBitDouble());
303 break;
304 case 4:
305 case 512:
306 if (!readTableValueBytes(buf, value.m_rawData, "TABLE value byte payload"))
307 return false;
308 value.m_dataSize = static_cast<std::uint32_t>(value.m_rawData.size());
309 value.m_value.addString(1, decodeTableValueText(version, buf, value.m_rawData));
310 break;
311 case 8: {
312 if (!readTableValueBytes(buf, value.m_rawData, "TABLE value date payload"))
313 return false;
314 value.m_dataSize = static_cast<std::uint32_t>(value.m_rawData.size());
315 value.m_value.addBinary(310, value.m_rawData);
316 break;
317 }
318 case 16:
319 if (!readTableValuePoint(buf, value, 2))
320 return false;
321 break;
322 case 32:
323 if (!readTableValuePoint(buf, value, 3))
324 return false;
325 break;
326 case 64:
327 value.m_handle = readTableHandle(hdlBuf);
328 value.m_value.addInt(330, static_cast<std::uint32_t>(value.m_handle));
329 break;
330 case 128:
331 case 256:
332 DRW_DBG("unsupported TABLE CadValue buffer data type: ")DRW_dbg::dbg("unsupported TABLE CadValue buffer data type: "); DRW_DBG(value.m_dataType)DRW_dbg::dbg(value.m_dataType); DRW_DBG("\n")DRW_dbg::dbg("\n");
333 return false;
334 default:
335 DRW_DBG("unsupported TABLE CadValue data type: ")DRW_dbg::dbg("unsupported TABLE CadValue data type: "); DRW_DBG(value.m_dataType)DRW_dbg::dbg(value.m_dataType); DRW_DBG("\n")DRW_dbg::dbg("\n");
336 return false;
337 }
338 }
339
340 if (version > DRW::AC1018) {
341 dwgBuffer *textBuf = strBuf ? strBuf : buf;
342 value.m_unitType = buf->getBitLong();
343 value.m_formatString = readTableText(version, textBuf);
344 value.m_valueString = readTableText(version, textBuf);
345 }
346
347 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
348 if (!good) {
349 DRW_DBG("TABLE CadValue stream failed, flags: ")DRW_dbg::dbg("TABLE CadValue stream failed, flags: "); DRW_DBG(value.m_formatFlags)DRW_dbg::dbg(value.m_formatFlags);
350 DRW_DBG(" type: ")DRW_dbg::dbg(" type: "); DRW_DBG(value.m_dataType)DRW_dbg::dbg(value.m_dataType);
351 DRW_DBG(" unit: ")DRW_dbg::dbg(" unit: "); DRW_DBG(value.m_unitType)DRW_dbg::dbg(value.m_unitType);
352 DRW_DBG(" bufGood: ")DRW_dbg::dbg(" bufGood: "); DRW_DBG(buf->isGood() ? 1 : 0)DRW_dbg::dbg(buf->isGood() ? 1 : 0);
353 DRW_DBG(" strGood: ")DRW_dbg::dbg(" strGood: "); DRW_DBG((!strBuf || strBuf->isGood()) ? 1 : 0)DRW_dbg::dbg((!strBuf || strBuf->isGood()) ? 1 : 0);
354 DRW_DBG(" hdlGood: ")DRW_dbg::dbg(" hdlGood: "); DRW_DBG((!hdlBuf || hdlBuf->isGood()) ? 1 : 0)DRW_dbg::dbg((!hdlBuf || hdlBuf->isGood()) ? 1 : 0);
355 DRW_DBG(" bufPos: ")DRW_dbg::dbg(" bufPos: "); DRW_DBG(buf->getPosition())DRW_dbg::dbg(buf->getPosition());
356 DRW_DBG(" strPos: ")DRW_dbg::dbg(" strPos: "); if (strBuf) DRW_DBG(strBuf->getPosition())DRW_dbg::dbg(strBuf->getPosition()); else DRW_DBG(-1)DRW_dbg::dbg(-1);
357 DRW_DBG(" hdlPos: ")DRW_dbg::dbg(" hdlPos: "); if (hdlBuf) DRW_DBG(hdlBuf->getPosition())DRW_dbg::dbg(hdlBuf->getPosition()); else DRW_DBG(-1)DRW_dbg::dbg(-1);
358 DRW_DBG("\n")DRW_dbg::dbg("\n");
359 }
360 return good;
361}
362
363bool skipTableCustomData(DRW::Version version, dwgBuffer *buf,
364 dwgBuffer *strBuf, dwgBuffer *hdlBuf) {
365 dwgBuffer *textBuf = strBuf ? strBuf : buf;
366 UTF8STRINGstd::string key = readTableText(version, textBuf);
367 if (strBuf && !strBuf->isGood()) {
368 DRW_DBG("TABLE custom data key string read failed\n")DRW_dbg::dbg("TABLE custom data key string read failed\n");
369 return false;
370 }
371 DRW_CadValue value;
372 const bool good = readTableCadValue(version, buf, strBuf, hdlBuf, value);
373 if (!good) {
374 DRW_DBG("TABLE custom data key failed: ")DRW_dbg::dbg("TABLE custom data key failed: "); DRW_DBG(key.c_str())DRW_dbg::dbg(key.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
375 }
376 return good;
377}
378
379void readTableCmColor(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf) {
380 dwgBuffer *textBuf = strBuf ? strBuf : buf;
381 if (version < DRW::AC1018) {
382 buf->getSBitShort();
383 return;
384 }
385
386 buf->getBitShort();
387 const std::uint32_t rgb = buf->getBitLong();
388 const std::uint8_t colorFlags = buf->getRawChar8();
389 DRW_DBG("\ntype COLOR: ")DRW_dbg::dbg("\ntype COLOR: "); DRW_DBGH(rgb >> 24)DRW_dbg::dbgH(rgb >> 24);
390 DRW_DBG("\nRGB COLOR: ")DRW_dbg::dbg("\nRGB COLOR: "); DRW_DBGH(rgb)DRW_dbg::dbgH(rgb);
391 DRW_DBG("\nbyte COLOR: ")DRW_dbg::dbg("\nbyte COLOR: "); DRW_DBGH(colorFlags)DRW_dbg::dbgH(colorFlags);
392 if (colorFlags & 1)
393 readTableText(version, textBuf);
394 if (colorFlags & 2)
395 readTableText(version, textBuf);
396}
397
398bool skipR2007TableCellOverrides(DRW::Version version, dwgBuffer *buf,
399 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
400 DRW_TableCell& cell,
401 std::vector<DRW_DwgSubrecordRange> *ranges) {
402 const std::uint64_t startBit = currentDwgBit(buf);
403 cell.m_overrideFlags = static_cast<std::uint32_t>(buf->getBitLong());
404 cell.m_virtualEdgeFlags = buf->getRawChar8();
405
406 if (cell.m_overrideFlags & 0x00001)
407 buf->getRawShort16();
408 if (cell.m_overrideFlags & 0x00002)
409 buf->getBit();
410 if (cell.m_overrideFlags & 0x00004)
411 readTableCmColor(version, buf, strBuf);
412 if (cell.m_overrideFlags & 0x00008)
413 readTableCmColor(version, buf, strBuf);
414 if (cell.m_overrideFlags & 0x00010)
415 cell.m_textStyleOverrideHandle = readTableHandle(hdlBuf);
416 if (cell.m_overrideFlags & 0x00020)
417 buf->getBitDouble();
418 if (cell.m_overrideFlags & 0x00040)
419 readTableCmColor(version, buf, strBuf);
420 if (cell.m_overrideFlags & 0x00400)
421 buf->getBitShort();
422 if (cell.m_overrideFlags & 0x04000)
423 buf->getBitShort();
424 if (cell.m_overrideFlags & 0x00080)
425 readTableCmColor(version, buf, strBuf);
426 if (cell.m_overrideFlags & 0x00800)
427 buf->getBitShort();
428 if (cell.m_overrideFlags & 0x08000)
429 buf->getBitShort();
430 if (cell.m_overrideFlags & 0x00100)
431 readTableCmColor(version, buf, strBuf);
432 if (cell.m_overrideFlags & 0x01000)
433 buf->getBitShort();
434 if (cell.m_overrideFlags & 0x10000)
435 buf->getBitShort();
436 if (cell.m_overrideFlags & 0x00200)
437 readTableCmColor(version, buf, strBuf);
438 if (cell.m_overrideFlags & 0x02000)
439 buf->getBitShort();
440 if (cell.m_overrideFlags & 0x20000)
441 buf->getBitShort();
442
443 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
444 if (ranges != nullptr) {
445 ranges->push_back(makeDwgSubrecordRange(
446 "r2007-table-cell-overrides", startBit, currentDwgBit(buf),
447 version, cell.m_overrideFlags, good));
448 }
449 return good;
450}
451
452bool parseR2007TableCell(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf,
453 dwgBuffer *hdlBuf, DRW_TableCell& cell,
454 std::vector<DRW_DwgSubrecordRange> *ranges) {
455 dwgBuffer *textBuf = strBuf ? strBuf : buf;
456 cell.m_type = buf->getBitShort();
457 cell.m_edgeFlags = buf->getRawChar8();
458 cell.m_isMerged = buf->getBit() != 0;
459 cell.m_autoFit = buf->getBit() != 0;
460 cell.m_mergedWidth = buf->getBitLong();
461 cell.m_mergedHeight = buf->getBitLong();
462 cell.m_rotation = buf->getBitDouble();
463 cell.m_valueHandle = readTableHandle(hdlBuf);
464
465 if (cell.m_type == 1) {
466 cell.m_textStyleHandle = cell.m_valueHandle;
467 if (cell.m_textStyleHandle == 0 && version < DRW::AC1021) {
468 DRW_TableCellContent content;
469 content.m_type = 1;
470 content.m_text = readTableText(version, textBuf);
471 content.m_value.m_dataType = 4;
472 content.m_value.m_value.addString(1, content.m_text);
473 cell.m_contents.push_back(content);
474 }
475 } else if (cell.m_type == 2) {
476 cell.m_blockHandle = cell.m_valueHandle;
477 cell.m_blockScale = buf->getBitDouble();
478 if (buf->getBit() != 0) {
479 const std::uint16_t numAttributes = buf->getBitShort();
480 cell.m_attributes.reserve(numAttributes);
481 for (std::uint16_t i = 0; i < numAttributes; ++i) {
482 DRW_TableCellAttribute attribute;
483 attribute.m_attdefHandle = readTableHandle(hdlBuf);
484 attribute.m_index = buf->getBitShort();
485 attribute.m_text = readTableText(version, textBuf);
486 cell.m_attributes.push_back(attribute);
487 }
488 }
489
490 DRW_TableCellContent content;
491 content.m_type = 4;
492 content.m_handle = cell.m_blockHandle;
493 cell.m_contents.push_back(content);
494 }
495
496 if (buf->getBit() != 0
497 && !skipR2007TableCellOverrides(version, buf, strBuf, hdlBuf, cell, ranges))
498 return false;
499
500 if (version > DRW::AC1018) {
501 buf->getBitLong();
502 DRW_TableCellContent content;
503 content.m_type = 1;
504 if (!readTableCadValue(version, buf, strBuf, hdlBuf, content.m_value))
505 return false;
506 if (content.m_value.m_value.type() == DRW_Variant::STRING)
507 content.m_text = content.m_value.m_value.c_str();
508 else if (!content.m_value.m_valueString.empty())
509 content.m_text = content.m_value.m_valueString;
510 cell.m_contents.push_back(content);
511 }
512
513 return buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
514}
515
516bool skipR2007TableOverrides(DRW::Version version, dwgBuffer *buf,
517 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
518 std::vector<DRW_DwgSubrecordRange> *ranges = nullptr) {
519 const std::uint64_t startBit = currentDwgBit(buf);
520 std::uint32_t maskCount = 0;
521 if (buf->getBit() != 0) {
522 const std::uint32_t flags = static_cast<std::uint32_t>(buf->getBitLong());
523 ++maskCount;
524 if (flags & 0x000001)
525 buf->getBit();
526 if (flags & 0x000004)
527 buf->getBitShort();
528 if (flags & 0x000008)
529 buf->getBitDouble();
530 if (flags & 0x000010)
531 buf->getBitDouble();
532 if (flags & 0x000020)
533 readTableCmColor(version, buf, strBuf);
534 if (flags & 0x000040)
535 readTableCmColor(version, buf, strBuf);
536 if (flags & 0x000080)
537 readTableCmColor(version, buf, strBuf);
538 if (flags & 0x000100)
539 buf->getBit();
540 if (flags & 0x000200)
541 buf->getBit();
542 if (flags & 0x000400)
543 buf->getBit();
544 if (flags & 0x000800)
545 readTableCmColor(version, buf, strBuf);
546 if (flags & 0x001000)
547 readTableCmColor(version, buf, strBuf);
548 if (flags & 0x002000)
549 readTableCmColor(version, buf, strBuf);
550 if (flags & 0x004000)
551 buf->getBitShort();
552 if (flags & 0x008000)
553 buf->getBitShort();
554 if (flags & 0x010000)
555 buf->getBitShort();
556 if (flags & 0x020000)
557 readTableHandle(hdlBuf);
558 if (flags & 0x040000)
559 readTableHandle(hdlBuf);
560 if (flags & 0x080000)
561 readTableHandle(hdlBuf);
562 if (flags & 0x100000)
563 buf->getBitDouble();
564 if (flags & 0x200000)
565 buf->getBitDouble();
566 if (flags & 0x400000)
567 buf->getBitDouble();
568 }
569
570 if (buf->getBit() != 0) {
571 const std::uint32_t flags = static_cast<std::uint32_t>(buf->getBitLong());
572 ++maskCount;
573 for (int i = 0; i < 18; ++i) {
574 if (flags & (1u << i))
575 readTableCmColor(version, buf, strBuf);
576 }
577 }
578
579 if (buf->getBit() != 0) {
580 const std::uint32_t flags = static_cast<std::uint32_t>(buf->getBitLong());
581 ++maskCount;
582 for (int i = 0; i < 18; ++i) {
583 if (flags & (1u << i))
584 buf->getBitShort();
585 }
586 }
587
588 if (buf->getBit() != 0) {
589 const std::uint32_t flags = static_cast<std::uint32_t>(buf->getBitLong());
590 ++maskCount;
591 for (int i = 0; i < 18; ++i) {
592 if (flags & (1u << i))
593 buf->getBitShort();
594 }
595 }
596
597 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
598 if (ranges != nullptr && (maskCount != 0 || currentDwgBit(buf) != startBit)) {
599 ranges->push_back(makeDwgSubrecordRange(
600 "r2007-table-overrides", startBit, currentDwgBit(buf),
601 version, maskCount, good));
602 }
603 return good;
604}
605
606bool skipTableContentFormat(DRW::Version version, dwgBuffer *buf,
607 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
608 std::vector<DRW_DwgSubrecordRange> *ranges = nullptr) {
609 const std::uint64_t startBit = currentDwgBit(buf);
610 dwgBuffer *textBuf = strBuf ? strBuf : buf;
611 buf->getBitLong(); // property override flags
612 buf->getBitLong(); // property flags
613 buf->getBitLong(); // value data type
614 buf->getBitLong(); // value unit type
615 readTableText(version, textBuf);
616 buf->getBitDouble(); // rotation
617 buf->getBitDouble(); // block scale
618 buf->getBitLong(); // alignment
619 std::int32_t rgb = -1;
620 UTF8STRINGstd::string name;
621 UTF8STRINGstd::string book;
622 buf->getCmColor(version, &rgb, textBuf, &name, &book);
623 readTableHandle(hdlBuf); // text style
624 buf->getBitDouble(); // text height
625 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
626 if (ranges != nullptr) {
627 ranges->push_back(makeDwgSubrecordRange(
628 "table-content-format", startBit, currentDwgBit(buf),
629 version, 1, good));
630 }
631 return good;
632}
633
634bool skipTableCellStyle(DRW::Version version, dwgBuffer *buf,
635 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
636 std::vector<DRW_DwgSubrecordRange> *ranges = nullptr) {
637 const std::uint64_t startBit = currentDwgBit(buf);
638 buf->getBitLong(); // style type
639 const bool hasData = buf->getBitShort() != 0;
640 if (!hasData) {
641 if (ranges != nullptr) {
642 ranges->push_back(makeDwgSubrecordRange(
643 "table-cell-style", startBit, currentDwgBit(buf),
644 version, 0, buf->isGood()));
645 }
646 return buf->isGood();
647 }
648
649 buf->getBitLong(); // property override flags
650 buf->getBitLong(); // merge flags
651 std::int32_t rgb = -1;
652 UTF8STRINGstd::string name;
653 UTF8STRINGstd::string book;
654 dwgBuffer *textBuf = strBuf ? strBuf : buf;
655 buf->getCmColor(version, &rgb, textBuf, &name, &book);
656 buf->getBitLong(); // content layout
657 if (!skipTableContentFormat(version, buf, strBuf, hdlBuf, ranges))
658 return false;
659
660 const std::uint16_t marginFlags = buf->getBitShort();
661 if (marginFlags != 0) {
662 for (int i = 0; i < 6; ++i)
663 buf->getBitDouble();
664 }
665
666 const std::uint32_t borders = buf->getBitLong();
667 if (borders > 6) {
668 DRW_DBG("TABLE cell style border count out of range: ")DRW_dbg::dbg("TABLE cell style border count out of range: "); DRW_DBG(borders)DRW_dbg::dbg(borders); DRW_DBG("\n")DRW_dbg::dbg("\n");
669 return false;
670 }
671 for (std::uint32_t i = 0; i < borders; ++i) {
672 const std::uint32_t edgeFlags = buf->getBitLong();
673 if (edgeFlags == 0)
674 continue;
675 buf->getBitLong(); // border overrides
676 buf->getBitLong(); // border type
677 buf->getCmColor(version, &rgb, textBuf, &name, &book);
678 buf->getBitLong(); // line weight
679 readTableHandle(hdlBuf); // linetype
680 buf->getBitLong(); // visible/invisible
681 buf->getBitDouble(); // double line spacing
682 }
683
684 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
685 if (ranges != nullptr) {
686 ranges->push_back(makeDwgSubrecordRange(
687 "table-cell-style", startBit, currentDwgBit(buf),
688 version, borders, good));
689 }
690 return good;
691}
692
693bool parseTableCell(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf,
694 dwgBuffer *hdlBuf, DRW_TableCell& cell,
695 std::vector<DRW_DwgSubrecordRange> *ranges) {
696 dwgBuffer *textBuf = strBuf ? strBuf : buf;
697 cell.m_flags = buf->getBitLong();
698 cell.m_toolTip = readTableText(version, textBuf);
699 if (strBuf && !strBuf->isGood()) {
700 DRW_DBG("TABLE cell tooltip string read failed\n")DRW_dbg::dbg("TABLE cell tooltip string read failed\n");
701 return false;
702 }
703 buf->getBitLong(); // custom data
704
705 const std::uint32_t customItems = buf->getBitLong();
706 if (customItems > kMaxTableItems) {
707 DRW_DBG("TABLE cell custom item count out of range: ")DRW_dbg::dbg("TABLE cell custom item count out of range: "); DRW_DBG(customItems)DRW_dbg::dbg(customItems); DRW_DBG("\n")DRW_dbg::dbg("\n");
708 return false;
709 }
710 for (std::uint32_t i = 0; i < customItems; ++i) {
711 if (!skipTableCustomData(version, buf, strBuf, hdlBuf)) {
712 DRW_DBG("TABLE cell custom data parse incomplete\n")DRW_dbg::dbg("TABLE cell custom data parse incomplete\n");
713 return false;
714 }
715 }
716
717 if (buf->getBitLong() != 0) {
718 readTableHandle(hdlBuf);
719 buf->getBitLong();
720 buf->getBitLong();
721 buf->getBitLong();
722 }
723
724 const std::uint32_t contentCount = buf->getBitLong();
725 if (contentCount > kMaxTableItems) {
726 DRW_DBG("TABLE cell content count out of range: ")DRW_dbg::dbg("TABLE cell content count out of range: "); DRW_DBG(contentCount)DRW_dbg::dbg(contentCount); DRW_DBG("\n")DRW_dbg::dbg("\n");
727 return false;
728 }
729 cell.m_contents.reserve(contentCount);
730 for (std::uint32_t i = 0; i < contentCount; ++i) {
731 DRW_TableCellContent content;
732 content.m_type = buf->getBitLong();
733 if (content.m_type == 1) {
734 if (!readTableCadValue(version, buf, strBuf, hdlBuf, content.m_value)) {
735 DRW_DBG("TABLE cell value parse incomplete\n")DRW_dbg::dbg("TABLE cell value parse incomplete\n");
736 return false;
737 }
738 if (content.m_value.m_value.type() == DRW_Variant::STRING)
739 content.m_text = content.m_value.m_value.c_str();
740 else if (!content.m_value.m_valueString.empty())
741 content.m_text = content.m_value.m_valueString;
742 } else if (content.m_type == 2 || content.m_type == 4) {
743 content.m_handle = readTableHandle(hdlBuf);
744 }
745
746 const std::uint32_t numAttrs = buf->getBitLong();
747 if (numAttrs > kMaxTableItems) {
748 DRW_DBG("TABLE cell attribute count out of range: ")DRW_dbg::dbg("TABLE cell attribute count out of range: "); DRW_DBG(numAttrs)DRW_dbg::dbg(numAttrs); DRW_DBG("\n")DRW_dbg::dbg("\n");
749 return false;
750 }
751 for (std::uint32_t attr = 0; attr < numAttrs; ++attr) {
752 readTableHandle(hdlBuf);
753 readTableText(version, textBuf);
754 buf->getBitLong();
755 }
756
757 const bool hasContentFormat = buf->getBitShort() != 0;
758 if (hasContentFormat
759 && !skipTableContentFormat(version, buf, strBuf, hdlBuf, ranges)) {
760 DRW_DBG("TABLE cell content format parse incomplete\n")DRW_dbg::dbg("TABLE cell content format parse incomplete\n");
761 return false;
762 }
763 cell.m_contents.push_back(content);
764 }
765
766 if (!skipTableCellStyle(version, buf, strBuf, hdlBuf, ranges)) {
767 DRW_DBG("TABLE cell style override parse incomplete\n")DRW_dbg::dbg("TABLE cell style override parse incomplete\n");
768 return false;
769 }
770
771 cell.m_styleId = buf->getBitLong();
772 const std::uint64_t geometryStartBit = currentDwgBit(buf);
773 const std::uint32_t hasGeometry = buf->getBitLong();
774 if (hasGeometry != 0) {
775 buf->getBitLong(); // unknown AC1027+ geometry marker
776 cell.m_width = buf->getBitDouble();
777 cell.m_height = buf->getBitDouble();
778 cell.m_geometryFlags = buf->getBitLong();
779 cell.m_geometryHandle = readTableHandle(hdlBuf);
780 if (cell.m_geometryFlags != 0) {
781 cell.m_geometryTopLeft = buf->get3BitDouble();
782 cell.m_geometryCenter = buf->get3BitDouble();
783 cell.m_contentWidth = buf->getBitDouble();
784 cell.m_contentHeight = buf->getBitDouble();
785 cell.m_geometryWidth = buf->getBitDouble();
786 cell.m_geometryHeight = buf->getBitDouble();
787 cell.m_geometryRecordFlags = buf->getBitLong();
788 }
789 if (ranges != nullptr) {
790 const bool geometryGood = buf->isGood() && (!hdlBuf || hdlBuf->isGood());
791 ranges->push_back(makeDwgSubrecordRange(
792 "table-cell-geometry-tail", geometryStartBit, currentDwgBit(buf),
793 version, cell.m_geometryFlags, geometryGood));
794 }
795 }
796
797 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
798 if (!good)
799 DRW_DBG("TABLE cell stream ended unexpectedly\n")DRW_dbg::dbg("TABLE cell stream ended unexpectedly\n");
800 return good;
801}
802
803bool parseTableContent(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf,
804 dwgBuffer *hdlBuf, DRW_TableContent& content) {
805 dwgBuffer *textBuf = strBuf ? strBuf : buf;
806 content.m_name = readTableText(version, textBuf);
807 content.m_description = readTableText(version, textBuf);
808
809 const std::uint32_t columns = buf->getBitLong();
810 if (columns > kMaxTableColumns) {
811 DRW_DBG("TABLECONTENT column count out of range: ")DRW_dbg::dbg("TABLECONTENT column count out of range: "); DRW_DBG(columns)DRW_dbg::dbg(columns); DRW_DBG("\n")DRW_dbg::dbg("\n");
812 return false;
813 }
814 content.m_columns.clear();
815 content.m_columns.reserve(columns);
816 for (std::uint32_t col = 0; col < columns; ++col) {
817 DRW_TableColumn column;
818 column.m_name = readTableText(version, textBuf);
819 buf->getBitLong(); // custom data
820 const std::uint32_t customItems = buf->getBitLong();
821 if (customItems > kMaxTableItems) {
822 DRW_DBG("TABLECONTENT column custom item count out of range: ")DRW_dbg::dbg("TABLECONTENT column custom item count out of range: "
)
; DRW_DBG(customItems)DRW_dbg::dbg(customItems); DRW_DBG("\n")DRW_dbg::dbg("\n");
823 return false;
824 }
825 for (std::uint32_t i = 0; i < customItems; ++i) {
826 if (!skipTableCustomData(version, buf, strBuf, hdlBuf)) {
827 DRW_DBG("TABLECONTENT column custom data parse incomplete\n")DRW_dbg::dbg("TABLECONTENT column custom data parse incomplete\n"
)
;
828 return false;
829 }
830 }
831 if (!skipTableCellStyle(version, buf, strBuf, hdlBuf,
832 &content.m_subrecordRanges)) {
833 DRW_DBG("TABLECONTENT column cell style parse incomplete\n")DRW_dbg::dbg("TABLECONTENT column cell style parse incomplete\n"
)
;
834 return false;
835 }
836 buf->getBitLong(); // style id
837 column.m_width = buf->getBitDouble();
838 content.m_columns.push_back(column);
839 }
840
841 const std::uint32_t rows = buf->getBitLong();
842 if (rows > kMaxTableRows || (columns != 0 && rows > kMaxTableCells / columns)) {
843 DRW_DBG("TABLECONTENT row count out of range: ")DRW_dbg::dbg("TABLECONTENT row count out of range: "); DRW_DBG(rows)DRW_dbg::dbg(rows); DRW_DBG("\n")DRW_dbg::dbg("\n");
844 return false;
845 }
846 content.m_rows.clear();
847 content.m_rows.reserve(rows);
848 for (std::uint32_t rowIndex = 0; rowIndex < rows; ++rowIndex) {
849 DRW_TableRow row;
850 const std::uint32_t cells = buf->getBitLong();
851 if (cells > kMaxTableColumns || cells > kMaxTableItems) {
852 DRW_DBG("TABLECONTENT row cell count out of range: ")DRW_dbg::dbg("TABLECONTENT row cell count out of range: "); DRW_DBG(cells)DRW_dbg::dbg(cells); DRW_DBG("\n")DRW_dbg::dbg("\n");
853 return false;
854 }
855 row.m_cells.reserve(cells);
856 for (std::uint32_t cellIndex = 0; cellIndex < cells; ++cellIndex) {
857 DRW_TableCell cell;
858 if (!parseTableCell(version, buf, strBuf, hdlBuf, cell,
859 &content.m_subrecordRanges)) {
860 DRW_DBG("TABLECONTENT cell parse incomplete at row ")DRW_dbg::dbg("TABLECONTENT cell parse incomplete at row "); DRW_DBG(rowIndex)DRW_dbg::dbg(rowIndex);
861 DRW_DBG(" cell ")DRW_dbg::dbg(" cell "); DRW_DBG(cellIndex)DRW_dbg::dbg(cellIndex); DRW_DBG("\n")DRW_dbg::dbg("\n");
862 return false;
863 }
864 row.m_cells.push_back(cell);
865 }
866
867 buf->getBitLong(); // custom data
868 const std::uint32_t customItems = buf->getBitLong();
869 if (customItems > kMaxTableItems) {
870 DRW_DBG("TABLECONTENT row custom item count out of range: ")DRW_dbg::dbg("TABLECONTENT row custom item count out of range: "
)
; DRW_DBG(customItems)DRW_dbg::dbg(customItems); DRW_DBG("\n")DRW_dbg::dbg("\n");
871 return false;
872 }
873 for (std::uint32_t i = 0; i < customItems; ++i) {
874 if (!skipTableCustomData(version, buf, strBuf, hdlBuf)) {
875 DRW_DBG("TABLECONTENT row custom data parse incomplete\n")DRW_dbg::dbg("TABLECONTENT row custom data parse incomplete\n"
)
;
876 return false;
877 }
878 }
879 if (!skipTableCellStyle(version, buf, strBuf, hdlBuf,
880 &content.m_subrecordRanges)) {
881 DRW_DBG("TABLECONTENT row cell style parse incomplete\n")DRW_dbg::dbg("TABLECONTENT row cell style parse incomplete\n"
)
;
882 return false;
883 }
884 buf->getBitLong(); // style id
885 row.m_height = buf->getBitDouble();
886 content.m_rows.push_back(row);
887 }
888
889 const std::uint32_t fieldRefs = buf->getBitLong();
890 if (fieldRefs > kMaxTableItems) {
891 DRW_DBG("TABLECONTENT field reference count out of range: ")DRW_dbg::dbg("TABLECONTENT field reference count out of range: "
)
; DRW_DBG(fieldRefs)DRW_dbg::dbg(fieldRefs); DRW_DBG("\n")DRW_dbg::dbg("\n");
892 return false;
893 }
894 content.m_fieldHandles.clear();
895 content.m_fieldHandles.reserve(fieldRefs);
896 for (std::uint32_t i = 0; i < fieldRefs; ++i) {
897 const std::uint32_t ref = readTableHandle(hdlBuf);
898 if (ref != 0)
899 content.m_fieldHandles.push_back(ref);
900 }
901
902 if (!skipTableCellStyle(version, buf, strBuf, hdlBuf,
903 &content.m_subrecordRanges)) {
904 DRW_DBG("TABLECONTENT table cell style parse incomplete\n")DRW_dbg::dbg("TABLECONTENT table cell style parse incomplete\n"
)
;
905 return false;
906 }
907
908 const std::uint32_t mergedRanges = buf->getBitLong();
909 if (mergedRanges > kMaxTableItems) {
910 DRW_DBG("TABLECONTENT merged range count out of range: ")DRW_dbg::dbg("TABLECONTENT merged range count out of range: "
)
; DRW_DBG(mergedRanges)DRW_dbg::dbg(mergedRanges); DRW_DBG("\n")DRW_dbg::dbg("\n");
911 return false;
912 }
913 content.m_mergedRanges.clear();
914 content.m_mergedRanges.reserve(mergedRanges);
915 for (std::uint32_t i = 0; i < mergedRanges; ++i) {
916 DRW_TableMergedRange range;
917 range.m_topRow = buf->getBitLong();
918 range.m_leftColumn = buf->getBitLong();
919 range.m_bottomRow = buf->getBitLong();
920 range.m_rightColumn = buf->getBitLong();
921 content.m_mergedRanges.push_back(range);
922 }
923
924 content.m_tableStyleHandle = readTableHandle(hdlBuf);
925 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
926 if (!good)
927 DRW_DBG("TABLECONTENT stream ended unexpectedly\n")DRW_dbg::dbg("TABLECONTENT stream ended unexpectedly\n");
928 return good;
929}
930
931} // namespace
932
933//! Calculate arbitrary axis
934/*!
935* Calculate arbitrary axis for apply extrusions
936* @author Rallaz
937*/
938void DRW_Entity::calculateAxis(DRW_Coord extPoint){
939 //Follow the arbitrary DXF definitions for extrusion axes.
940 if (fabs(extPoint.x) < 0.015625 && fabs(extPoint.y) < 0.015625) {
941 //If we get here, implement Ax = Wy x N where Wy is [0,1,0] per the DXF spec.
942 //The cross product works out to Wy.y*N.z-Wy.z*N.y, Wy.z*N.x-Wy.x*N.z, Wy.x*N.y-Wy.y*N.x
943 //Factoring in the fixed values for Wy gives N.z,0,-N.x
944 extAxisX.x = extPoint.z;
945 extAxisX.y = 0;
946 extAxisX.z = -extPoint.x;
947 } else {
948 //Otherwise, implement Ax = Wz x N where Wz is [0,0,1] per the DXF spec.
949 //The cross product works out to Wz.y*N.z-Wz.z*N.y, Wz.z*N.x-Wz.x*N.z, Wz.x*N.y-Wz.y*N.x
950 //Factoring in the fixed values for Wz gives -N.y,N.x,0.
951 extAxisX.x = -extPoint.y;
952 extAxisX.y = extPoint.x;
953 extAxisX.z = 0;
954 }
955
956 extAxisX.unitize();
957
958 //Ay = N x Ax
959 extAxisY.x = (extPoint.y * extAxisX.z) - (extAxisX.y * extPoint.z);
960 extAxisY.y = (extPoint.z * extAxisX.x) - (extAxisX.z * extPoint.x);
961 extAxisY.z = (extPoint.x * extAxisX.y) - (extAxisX.x * extPoint.y);
962
963 extAxisY.unitize();
964}
965
966//! Extrude a point using arbitrary axis
967/*!
968* apply extrusion in a point using arbitrary axis (previous calculated)
969* @author Rallaz
970*/
971void DRW_Entity::extrudePoint(DRW_Coord extPoint, DRW_Coord *point){
972 double px, py, pz;
973 px = (extAxisX.x*point->x)+(extAxisY.x*point->y)+(extPoint.x*point->z);
974 py = (extAxisX.y*point->x)+(extAxisY.y*point->y)+(extPoint.y*point->z);
975 pz = (extAxisX.z*point->x)+(extAxisY.z*point->y)+(extPoint.z*point->z);
976
977 point->x = px;
978 point->y = py;
979 point->z = pz;
980}
981
982bool DRW_Entity::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
983 switch (code) {
984 case DRW::dxfCode::HANDLE:
985 handle = reader->getHandleString();
986 break;
987 case DRW::dxfCode::OWNER_HANDLE:
988 parentHandle = reader->getHandleString();
989 break;
990 case DRW::dxfCode::LAYER:
991 layer = reader->getUtf8String();
992 break;
993 case 6:
994 lineType = reader->getUtf8String();
995 break;
996 case DRW::dxfCode::COLOR:
997 color = reader->getInt32();
998 break;
999 case DRW::dxfCode::LINEWEIGHT:
1000 lWeight = DRW_LW_Conv::dxfInt2lineWidth(reader->getInt32());
1001 break;
1002 case 48:
1003 ltypeScale = reader->getDouble();
1004 break;
1005 case DRW::dxfCode::INVISIBLE:
1006 visible = (reader->getInt32() & 1) == 0;
1007 break;
1008 case 420:
1009 color24 = reader->getInt32();
1010 break;
1011 case 430:
1012 colorName = reader->getString();
1013 break;
1014 case 67:
1015 space = static_cast<DRW::Space>(reader->getInt32());
1016 break;
1017 case 102:
1018 return parseDxfGroups(code, reader);
1019 case 284:
1020 shadow = static_cast<DRW::ShadowMode>(reader->getInt32() & 0x3);
1021 break;
1022 case 347:
1023 material = static_cast<std::uint32_t>(reader->getHandleString());
1024 break;
1025 case DRW::dxfCode::PLOTSTYLE:
1026 plotStyle = reader->getHandleString();
1027 break;
1028 case 440:
1029 transparency = reader->getInt32();
1030 break;
1031 case 92:
1032 case 160:
1033 // Proxy entity graphics byte count (ODA §20.4.95): 92 for R13–R2007,
1034 // 160 for R2010+. Introduces the 310 hex chunks below; gating the 310
1035 // capture on this keeps unrelated binary-310 streams out of the proxy
1036 // buffer. (Entities that repurpose 92 — e.g. MESH — handle it in their
1037 // own parseCode and never reach here.)
1038 numProxyGraph = reader->getInt32();
1039 break;
1040 case 310:
1041 if (numProxyGraph != 0) {
1042 // Proxy graphics binary, hex-encoded across many ≤254-char chunks.
1043 const std::string& hex = reader->getString();
1044 proxyGraphics.reserve(proxyGraphics.size() + hex.size() / 2);
1045 auto hexVal = [](char c) -> int {
1046 if (c >= '0' && c <= '9') return c - '0';
1047 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
1048 if (c >= 'A' && c <= 'F') return c - 'A' + 10;
1049 return -1;
1050 };
1051 for (std::size_t i = 0; i + 1 < hex.size(); i += 2) {
1052 int hi = hexVal(hex[i]), lo = hexVal(hex[i + 1]);
1053 if (hi < 0 || lo < 0) break;
1054 proxyGraphics.push_back(static_cast<char>((hi << 4) | lo));
1055 }
1056 }
1057 break;
1058 case 1000:
1059 case 1001:
1060 case 1002:
1061 case 1003:
1062 case 1004:
1063 case 1005:
1064 extData.push_back(std::make_shared<DRW_Variant>(code, reader->getString()));
1065 break;
1066 case 1010:
1067 case 1011:
1068 case 1012:
1069 case 1013:
1070 curr =std::make_shared<DRW_Variant>(code, DRW_Coord(reader->getDouble(), 0.0, 0.0));
1071 extData.push_back(curr);
1072 break;
1073 case 1020:
1074 case 1021:
1075 case 1022:
1076 case 1023:
1077 if (curr)
1078 curr->setCoordY(reader->getDouble());
1079 break;
1080 case 1030:
1081 case 1031:
1082 case 1032:
1083 case 1033:
1084 if (curr)
1085 curr->setCoordZ(reader->getDouble());
1086 //FIXME, why do we discard curr right after setting the its Z
1087// curr=NULL;
1088 break;
1089 case 1040:
1090 case 1041:
1091 case 1042:
1092 extData.push_back(std::make_shared<DRW_Variant>(code, reader->getDouble() ));
1093 break;
1094 case 1070:
1095 case 1071:
1096 extData.push_back(std::make_shared<DRW_Variant>(code, reader->getInt32() ));
1097 break;
1098 default:
1099 break;
1100 }
1101 return true;
1102}
1103
1104//parses dxf 102 groups to read entity
1105bool DRW_Entity::parseDxfGroups(int code, const std::unique_ptr<dxfReader>& reader){
1106 std::list<DRW_Variant> ls;
1107 DRW_Variant curr;
1108 std::string appName= reader->getString();
1109 bool complete = true;
1110 if (!appName.empty() && appName.at(0)== '{') {
1111 curr.addString(code, appName.substr(1));
1112 ls.push_back(curr);
1113 int depth = 1;
1114 int nextCode = 0;
1115 while (depth > 0 && reader->readRec(&nextCode)) {
1116 DRW_Variant value;
1117 if (nextCode == 102) {
1118 std::string marker = reader->getString();
1119 value.addString(nextCode, marker);
1120 if (!marker.empty() && marker.at(0) == '{')
1121 ++depth;
1122 else if (!marker.empty() && marker.at(0) == '}')
1123 --depth;
1124 } else if ((nextCode >= 320 && nextCode <= 369)
1125 || (nextCode >= 390 && nextCode <= 399)
1126 || nextCode == 480 || nextCode == 481
1127 || nextCode == 1005) {
1128 value.addString(nextCode, reader->getString());
1129 } else {
1130 switch (reader->type) {
1131 case dxfReader::STRING:
1132 case dxfReader::BINARY:
1133 value.addString(nextCode, reader->getString());
1134 break;
1135 case dxfReader::INT32:
1136 case dxfReader::BOOL:
1137 value.addInt(nextCode, reader->getInt32());
1138 break;
1139 case dxfReader::INT64:
1140 value.addInt64(nextCode, static_cast<std::int64_t>(reader->getInt64()));
1141 break;
1142 case dxfReader::DOUBLE:
1143 value.addDouble(nextCode, reader->getDouble());
1144 break;
1145 default:
1146 break;
1147 }
1148 }
1149 ls.push_back(value);
1150 }
1151 complete = depth == 0;
1152 }
1153
1154 appData.push_back(ls);
1155 return complete;
1156}
1157
1158bool DRW_Entity::parseDwg(DRW::Version version, dwgBuffer *buf, dwgBuffer* strBuf, std::uint32_t bs){
1159 objSize=0;
1160 DRW_DBG("\n***************************** parsing entity *********************************************\n")DRW_dbg::dbg("\n***************************** parsing entity *********************************************\n"
)
;
1161 oType = buf->getObjType(version);
1162 DRW_DBG("Object type: ")DRW_dbg::dbg("Object type: "); DRW_DBG(oType)DRW_dbg::dbg(oType); DRW_DBG(", ")DRW_dbg::dbg(", "); DRW_DBGH(oType)DRW_dbg::dbgH(oType);
1163
1164 if (version > DRW::AC1014 && version < DRW::AC1024) {//2000 & 2004
1165 objSize = buf->getRawLong32(); //RL 32bits object size in bits
1166 DRW_DBG(" Object size: ")DRW_dbg::dbg(" Object size: "); DRW_DBG(objSize)DRW_dbg::dbg(objSize); DRW_DBG("\n")DRW_dbg::dbg("\n");
1167 }
1168 if (version > DRW::AC1021) {//2010+
1169 std::uint32_t ms = buf->size();
1170 // Clamp: a corrupt bs > ms*8 would underflow objSize (unsigned) to a
1171 // huge value and drive strBuf->moveBitPos(objSize-1) past the buffer.
1172 objSize = (bs <= ms*8u) ? ms*8u - bs : 0u;
1173 DRW_DBG(" Object size: ")DRW_dbg::dbg(" Object size: "); DRW_DBG(objSize)DRW_dbg::dbg(objSize); DRW_DBG("\n")DRW_dbg::dbg("\n");
1174 }
1175
1176 if (strBuf != NULL__null && version > DRW::AC1018) {//2007+
1177 strBuf->moveBitPos(objSize-1);
1178 DRW_DBG(" strBuf strbit pos 2007: ")DRW_dbg::dbg(" strBuf strbit pos 2007: "); DRW_DBG(strBuf->getPosition())DRW_dbg::dbg(strBuf->getPosition()); DRW_DBG(" strBuf bpos 2007: ")DRW_dbg::dbg(" strBuf bpos 2007: "); DRW_DBG(strBuf->getBitPos())DRW_dbg::dbg(strBuf->getBitPos()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1179 if (strBuf->getBit() == 1){
1180 DRW_DBG("DRW_TableEntry::parseDwg string bit is 1\n")DRW_dbg::dbg("DRW_TableEntry::parseDwg string bit is 1\n");
1181 strBuf->moveBitPos(-17);
1182 std::uint16_t strDataSize = strBuf->getRawShort16();
1183 DRW_DBG("\nDRW_TableEntry::parseDwg string strDataSize: ")DRW_dbg::dbg("\nDRW_TableEntry::parseDwg string strDataSize: "
)
; DRW_DBGH(strDataSize)DRW_dbg::dbgH(strDataSize); DRW_DBG("\n")DRW_dbg::dbg("\n");
1184 if ( (strDataSize& 0x8000) == 0x8000){
1185 DRW_DBG("\nDRW_TableEntry::parseDwg string 0x8000 bit is set")DRW_dbg::dbg("\nDRW_TableEntry::parseDwg string 0x8000 bit is set"
)
;
1186 strBuf->moveBitPos(-32);
1187 std::uint16_t hiSize = strBuf->getRawShort16();
1188 strDataSize = ((strDataSize&0x7fff) | (hiSize<<15));
1189 }
1190 strBuf->moveBitPos( -strDataSize -16); //-14
1191 DRW_DBG("strBuf start strDataSize pos 2007: ")DRW_dbg::dbg("strBuf start strDataSize pos 2007: "); DRW_DBG(strBuf->getPosition())DRW_dbg::dbg(strBuf->getPosition()); DRW_DBG(" strBuf bpos 2007: ")DRW_dbg::dbg(" strBuf bpos 2007: "); DRW_DBG(strBuf->getBitPos())DRW_dbg::dbg(strBuf->getBitPos()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1192 } else
1193 DRW_DBG("\nDRW_TableEntry::parseDwg string bit is 0")DRW_dbg::dbg("\nDRW_TableEntry::parseDwg string bit is 0");
1194 DRW_DBG("strBuf start pos 2007: ")DRW_dbg::dbg("strBuf start pos 2007: "); DRW_DBG(strBuf->getPosition())DRW_dbg::dbg(strBuf->getPosition()); DRW_DBG(" strBuf bpos 2007: ")DRW_dbg::dbg(" strBuf bpos 2007: "); DRW_DBG(strBuf->getBitPos())DRW_dbg::dbg(strBuf->getBitPos()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1195 }
1196
1197 dwgHandle ho = buf->getHandle();
1198 handle = ho.ref;
1199 DRW_DBG("Entity Handle: ")DRW_dbg::dbg("Entity Handle: "); DRW_DBGHL(ho.code, ho.size, ho.ref)DRW_dbg::dbgHL(ho.code, ho.size, ho.ref);
1200 // ODA DWG spec §28 "Extended Entity Data". The outer loop yields one
1201 // BS-prefixed byte chunk per APPID-attached group; size==0 terminates.
1202 // Each chunk's payload is a sequence of (1-byte type code + value)
1203 // items; we walk it with a nested loop and push DRW_Variant entries
1204 // into @ref extData. Handle-typed items (type 3 layer-ref, type 5
1205 // entity-ref) and the per-chunk APPID handle are resolved post-hoc
1206 // in dwgReader::parseAttribs once the symbol tables are available.
1207 std::uint16_t extDataSize = buf->getBitShort(); //BS (unsigned: a >32767 chunk size must not go negative)
1208 DRW_DBG(" ext data size: ")DRW_dbg::dbg(" ext data size: "); DRW_DBG(extDataSize)DRW_dbg::dbg(extDataSize);
1209 while (extDataSize>0 && buf->isGood()) {
1210 dwgHandle ah = buf->getHandle();
1211 DRW_DBG("App Handle: ")DRW_dbg::dbg("App Handle: "); DRW_DBGHL(ah.code, ah.size, ah.ref)DRW_dbg::dbgHL(ah.code, ah.size, ah.ref);
1212 std::vector<std::uint8_t> tmpExtData(static_cast<std::size_t>(extDataSize));
1213 if (!buf->getBytes(tmpExtData.data(), extDataSize))
1214 return false;
1215 dwgBuffer tmpExtDataBuf(tmpExtData.data(), extDataSize, buf->decoder);
1216
1217 // Placeholder for the APPID name (DXF group 1001). Filled in by
1218 // parseAttribs from appIdmap; falls back to ACAD_<hex> if unknown.
1219 extData.push_back(std::make_shared<DRW_Variant>(1001, std::string{}));
1220 pendingAppIdResolutions.push_back({extData.size() - 1, ah.ref});
1221
1222 while (tmpExtDataBuf.numRemainingBytes() > 0 && tmpExtDataBuf.isGood()) {
1223 std::uint8_t dxfCode = tmpExtDataBuf.getRawChar8();
1224 DRW_DBG(" eed type: ")DRW_dbg::dbg(" eed type: "); DRW_DBG(dxfCode)DRW_dbg::dbg(dxfCode);
1225 switch (dxfCode){
1226 case 0: { //string
1227 std::string s;
1228 if (version > DRW::AC1018) { //R2007+
1229 if (tmpExtDataBuf.numRemainingBytes() < 2) break;
1230 std::uint16_t nChars = tmpExtDataBuf.getRawShort16();
1231 DRW_DBG(" EED string nChars: ")DRW_dbg::dbg(" EED string nChars: "); DRW_DBG(nChars)DRW_dbg::dbg(nChars);
1232 if (nChars > 0) {
1233 // R2007+ EED strings are UTF-16LE (nChars 16-bit code units).
1234 std::uint64_t byteLen = static_cast<std::uint64_t>(nChars) * 2;
1235 if ((std::uint64_t)tmpExtDataBuf.numRemainingBytes() < byteLen) break;
1236 std::vector<std::uint8_t> bytes(byteLen);
1237 tmpExtDataBuf.getBytes(bytes.data(), byteLen);
1238 for (std::uint16_t i = 0; i < nChars; ++i) {
1239 std::uint16_t c = static_cast<std::uint16_t>(bytes[2*i]) |
1240 (static_cast<std::uint16_t>(bytes[2*i+1]) << 8);
1241 if (c < 0x80) {
1242 s.push_back(static_cast<char>(c));
1243 } else if (c < 0x800) {
1244 s.push_back(static_cast<char>(0xC0 | (c >> 6)));
1245 s.push_back(static_cast<char>(0x80 | (c & 0x3F)));
1246 } else {
1247 s.push_back(static_cast<char>(0xE0 | (c >> 12)));
1248 s.push_back(static_cast<char>(0x80 | ((c >> 6) & 0x3F)));
1249 s.push_back(static_cast<char>(0x80 | (c & 0x3F)));
1250 }
1251 }
1252 }
1253 } else { //R13–R2004: 1-byte len + 2-byte BE codepage hint + bytes (+NUL)
1254 if (tmpExtDataBuf.numRemainingBytes() < 3) break;
1255 std::uint8_t strLength = tmpExtDataBuf.getRawChar8();
1256 std::uint16_t cp = tmpExtDataBuf.getBERawShort16();
1257 if (strLength > 0 && tmpExtDataBuf.numRemainingBytes() >= strLength) {
1258 std::string raw(strLength, '\0');
1259 tmpExtDataBuf.getBytes(reinterpret_cast<std::uint8_t*>(&raw[0]), strLength);
1260 s = decodeEedString(cp, raw, tmpExtDataBuf.decoder);
1261 }
1262 //consume the optional trailing NUL terminator if present
1263 if (tmpExtDataBuf.numRemainingBytes() > 0) {
1264 tmpExtDataBuf.getRawChar8();
1265 }
1266 }
1267 extData.push_back(std::make_shared<DRW_Variant>(1000, s));
1268 break;
1269 }
1270 case 2: { //control character: 0 = '{', 1 = '}'
1271 if (tmpExtDataBuf.numRemainingBytes() < 1) break;
1272 std::uint8_t ctrl = tmpExtDataBuf.getRawChar8();
1273 extData.push_back(std::make_shared<DRW_Variant>(
1274 1002, std::string(ctrl == 0 ? "{" : "}")));
1275 break;
1276 }
1277 case 3: { //layer-table reference (8 raw BE bytes -> handle)
1278 if (tmpExtDataBuf.numRemainingBytes() < 8) break;
1279 std::uint8_t hb[8];
1280 tmpExtDataBuf.getBytes(hb, 8);
1281 std::uint64_t ref = 0;
1282 for (int i = 0; i < 8; ++i) {
1283 ref = (ref << 8) | hb[i];
1284 }
1285 // Placeholder layer-ref string; resolved post-hoc.
1286 extData.push_back(std::make_shared<DRW_Variant>(
1287 1003, std::string{}, /*isLayerRef=*/true));
1288 pendingLayerRefResolutions.push_back(
1289 {extData.size() - 1, static_cast<std::uint32_t>(ref)});
1290 break;
1291 }
1292 case 4: { //binary chunk: 1-byte length + bytes
1293 if (tmpExtDataBuf.numRemainingBytes() < 1) break;
1294 std::uint8_t binLen = tmpExtDataBuf.getRawChar8();
1295 std::vector<std::uint8_t> bytes(binLen);
1296 if (binLen > 0 && tmpExtDataBuf.numRemainingBytes() >= binLen) {
1297 tmpExtDataBuf.getBytes(bytes.data(), binLen);
1298 }
1299 extData.push_back(std::make_shared<DRW_Variant>(1004, std::move(bytes)));
1300 break;
1301 }
1302 case 5: { //entity-handle reference (8 raw BE bytes -> hex string)
1303 if (tmpExtDataBuf.numRemainingBytes() < 8) break;
1304 std::uint8_t hb[8];
1305 tmpExtDataBuf.getBytes(hb, 8);
1306 std::uint64_t ref = 0;
1307 for (int i = 0; i < 8; ++i) {
1308 ref = (ref << 8) | hb[i];
1309 }
1310 char tmp[24];
1311 std::snprintf(tmp, sizeof(tmp), "%llX",
1312 static_cast<unsigned long long>(ref));
1313 extData.push_back(std::make_shared<DRW_Variant>(1005, std::string{tmp}));
1314 break;
1315 }
1316 case 10: case 11: case 12: case 13: { //3-double point
1317 if (tmpExtDataBuf.numRemainingBytes() < 24) break;
1318 DRW_Coord c;
1319 c.x = tmpExtDataBuf.getRawDouble();
1320 c.y = tmpExtDataBuf.getRawDouble();
1321 c.z = tmpExtDataBuf.getRawDouble();
1322 extData.push_back(std::make_shared<DRW_Variant>(1000 + dxfCode, c));
1323 break;
1324 }
1325 case 40: case 41: case 42: { //real
1326 if (tmpExtDataBuf.numRemainingBytes() < 8) break;
1327 double d = tmpExtDataBuf.getRawDouble();
1328 extData.push_back(std::make_shared<DRW_Variant>(1000 + dxfCode, d));
1329 break;
1330 }
1331 case 70: { //int16
1332 if (tmpExtDataBuf.numRemainingBytes() < 2) break;
1333 std::int16_t i = static_cast<std::int16_t>(tmpExtDataBuf.getRawShort16());
1334 extData.push_back(std::make_shared<DRW_Variant>(1070, static_cast<std::int32_t>(i)));
1335 break;
1336 }
1337 case 71: { //int32
1338 if (tmpExtDataBuf.numRemainingBytes() < 4) break;
1339 std::int32_t i = static_cast<std::int32_t>(tmpExtDataBuf.getRawLong32());
1340 extData.push_back(std::make_shared<DRW_Variant>(1071, i));
1341 break;
1342 }
1343 default:
1344 DRW_DBG(" unknown EED type: ")DRW_dbg::dbg(" unknown EED type: "); DRW_DBG(dxfCode)DRW_dbg::dbg(dxfCode); DRW_DBG("\n")DRW_dbg::dbg("\n");
1345 // Unknown type — bail on this app's chunk; we cannot
1346 // know how many bytes the rest of the item occupies.
1347 tmpExtDataBuf.setPosition(tmpExtDataBuf.size());
1348 break;
1349 }
1350 }
1351 extDataSize = buf->getBitShort(); //BS
1352 DRW_DBG(" ext data size: ")DRW_dbg::dbg(" ext data size: "); DRW_DBG(extDataSize)DRW_dbg::dbg(extDataSize);
1353 } //end parsing extData (EED)
1354 DRW_DBG(" [bidi-debug pre-graphFlag bufpos=")DRW_dbg::dbg(" [bidi-debug pre-graphFlag bufpos="); DRW_DBG(buf->getPosition())DRW_dbg::dbg(buf->getPosition()); DRW_DBG(" bitpos=")DRW_dbg::dbg(" bitpos="); DRW_DBG(buf->getBitPos())DRW_dbg::dbg(buf->getBitPos()); DRW_DBG("]\n")DRW_dbg::dbg("]\n");
1355 std::uint8_t graphFlag = buf->getBit(); //B
1356 DRW_DBG(" graphFlag: ")DRW_dbg::dbg(" graphFlag: "); DRW_DBG(graphFlag)DRW_dbg::dbg(graphFlag); DRW_DBG("\n")DRW_dbg::dbg("\n");
1357 if (graphFlag) {
1358 DRW_DBG(" [bidi-debug pre-graphSize bufpos=")DRW_dbg::dbg(" [bidi-debug pre-graphSize bufpos="); DRW_DBG(buf->getPosition())DRW_dbg::dbg(buf->getPosition()); DRW_DBG(" bitpos=")DRW_dbg::dbg(" bitpos="); DRW_DBG(buf->getBitPos())DRW_dbg::dbg(buf->getBitPos()); DRW_DBG("]\n")DRW_dbg::dbg("]\n");
1359 const std::uint64_t graphDataSize = (version >= DRW::AC1024)
1360 ? buf->getBitLongLong()
1361 : buf->getRawLong32();
1362 DRW_DBG("graphData in bytes: ")DRW_dbg::dbg("graphData in bytes: "); DRW_DBG(static_cast<std::uint32_t>(graphDataSize))DRW_dbg::dbg(static_cast<std::uint32_t>(graphDataSize)); DRW_DBG("\n")DRW_dbg::dbg("\n");
1363 const std::uint64_t maxMoveBytes = static_cast<std::uint64_t>(std::numeric_limits<std::int32_t>::max() / 8);
1364 if (graphDataSize > static_cast<std::uint64_t>(buf->numRemainingBytes())
1365 || graphDataSize > maxMoveBytes) {
1366 DRW_DBG("graphData size outside object body\n")DRW_dbg::dbg("graphData size outside object body\n");
1367 return false;
1368 }
1369 // Capture the proxy-graphics byte stream instead of skipping it. These
1370 // are cached drawable primitives (lines/arcs/polylines/text) that any
1371 // reader can render for proxy/custom entities (STDPART2D, AEC_*, tables)
1372 // — previously discarded via moveBitPos, leaving proxyGraphics empty.
1373 // dwgBuffer::getBytes is bit-aware (reconstructs each byte at a non-zero
1374 // bitPos), so it lands at the exact same position moveBitPos(8N) did.
1375 // (write-review #32 / read-coverage gap #1)
1376 if (graphDataSize > 0) {
1377 proxyGraphics.resize(graphDataSize);
1378 if (!buf->getBytes(reinterpret_cast<std::uint8_t*>(&proxyGraphics[0]),
1379 graphDataSize))
1380 return false;
1381 numProxyGraph = static_cast<int>(graphDataSize);
1382 }
1383 }
1384 if (version < DRW::AC1015) {//14-
1385 objSize = buf->getRawLong32(); //RL 32bits object size in bits
1386 DRW_DBG(" Object size in bits: ")DRW_dbg::dbg(" Object size in bits: "); DRW_DBG(objSize)DRW_dbg::dbg(objSize); DRW_DBG("\n")DRW_dbg::dbg("\n");
1387 }
1388
1389 std::uint8_t entmode = buf->get2Bits(); //BB
1390 if (entmode == 0)
1391 ownerHandle= true;
1392 // entmode = 2;
1393 else if(entmode ==2)
1394 entmode = 0;
1395 space = (DRW::Space)entmode; //RLZ verify cast values
1396 DRW_DBG("entmode: ")DRW_dbg::dbg("entmode: "); DRW_DBG(entmode)DRW_dbg::dbg(entmode);
1397 numReactors = buf->getBitLong(); //BL per spec §20.4.1
1398 DRW_DBG(", numReactors: ")DRW_dbg::dbg(", numReactors: "); DRW_DBG(numReactors)DRW_dbg::dbg(numReactors);
1399
1400 if (version < DRW::AC1015) {//14-
1401 if(buf->getBit()) {//is bylayer line type
1402 lineType = "BYLAYER";
1403 ltFlags = 0;
1404 } else {
1405 lineType = "";
1406 ltFlags = 3;
1407 }
1408 DRW_DBG(" lineType: ")DRW_dbg::dbg(" lineType: "); DRW_DBG(lineType.c_str())DRW_dbg::dbg(lineType.c_str());
1409 DRW_DBG(" ltFlags: ")DRW_dbg::dbg(" ltFlags: "); DRW_DBG(ltFlags)DRW_dbg::dbg(ltFlags);
1410 }
1411 if (version > DRW::AC1015) {//2004+
1412 xDictFlag = buf->getBit();
1413 DRW_DBG(" xDictFlag: ")DRW_dbg::dbg(" xDictFlag: "); DRW_DBG(xDictFlag)DRW_dbg::dbg(xDictFlag); DRW_DBG("\n")DRW_dbg::dbg("\n");
1414 }
1415
1416 // libreDWG common_entity_data.spec — the bit at this stream position has two
1417 // disjoint meanings by version:
1418 // * R13..R2002 (version < AC1018): `nolinks` (B). 1 = no prev/next handles
1419 // in the handle section; 0 = read prev+next at parseDwgEntHandle.
1420 // * R2004..R2010 (AC1018..AC1024): NO bit in the stream — reader forces
1421 // haveNextLinks=1 to skip the prev/next handle reads.
1422 // * R2013+ (version > AC1024): `has_ds_data` (B). 1 = inline ACIS SAB
1423 // datastore present. Stored separately because it gates SAB handling,
1424 // not prev/next links (which are already version<AC1018 gated).
1425 // Total bit consumption is unchanged for every version.
1426 if (version < DRW::AC1018) {
1427 haveNextLinks = buf->getBit(); //nolinks //B
1428 DRW_DBG(", haveNextLinks (0 yes, 1 prev next): ")DRW_dbg::dbg(", haveNextLinks (0 yes, 1 prev next): "); DRW_DBG(haveNextLinks)DRW_dbg::dbg(haveNextLinks); DRW_DBG("\n")DRW_dbg::dbg("\n");
1429 } else {
1430 haveNextLinks = 1; //AC1018+: not in stream, force 1 (no prev/next)
1431 DRW_DBG(", haveNextLinks (forced): ")DRW_dbg::dbg(", haveNextLinks (forced): "); DRW_DBG(haveNextLinks)DRW_dbg::dbg(haveNextLinks); DRW_DBG("\n")DRW_dbg::dbg("\n");
1432 }
1433 if (version > DRW::AC1024) {
1434 hasDsData = buf->getBit(); //has_ds_data //B (R2013+)
1435 DRW_DBG(", hasDsData (R2013+): ")DRW_dbg::dbg(", hasDsData (R2013+): "); DRW_DBG(hasDsData)DRW_dbg::dbg(hasDsData); DRW_DBG("\n")DRW_dbg::dbg("\n");
1436 }
1437//ENC color
1438 color = buf->getEnColor(version); //BS or CMC //ok for R14 or negate
1439 // Capture the AcDbColor side-channel BEFORE any subsequent ENC read.
1440 // libreDWG common_entity_data.spec:454-459 — the corresponding handle
1441 // is consumed at the start of the handle stream in parseDwgEntHandle.
1442 hasAcDbColorH = buf->lastEnColorHadDbColorRef;
1443 // libreDWG common_entity_data.spec:432-453 — ENC alpha_raw (DXF code
1444 // 440) is encoded as (alpha_type<<24) | alpha. Stored verbatim; the
1445 // filter (RS_FilterDXFRW::setEntityAttributes) decodes alpha_type==3
1446 // into a per-entity pen alpha, otherwise inherits from layer/block.
1447 if (buf->lastEnColorAlphaRaw != 0) {
1448 transparency = static_cast<int>(buf->lastEnColorAlphaRaw);
1449 }
1450 // libreDWG common_entity_data.spec:468-475 — inline TV name/book name
1451 // (flags 0x41/0x42) override any dbColorMap-resolved name. Captured
1452 // immediately; entryParse will skip the override only if colorName is
1453 // already populated here.
1454 if (!buf->lastEnColorName.empty()) {
1455 colorName = buf->lastEnColorBookName.empty()
1456 ? buf->lastEnColorName
1457 : (buf->lastEnColorBookName + "$" + buf->lastEnColorName);
1458 }
1459 ltypeScale = buf->getBitDouble(); //BD
1460 DRW_DBG(" entity color: ")DRW_dbg::dbg(" entity color: "); DRW_DBG(color)DRW_dbg::dbg(color);
1461 DRW_DBG(" ltScale: ")DRW_dbg::dbg(" ltScale: "); DRW_DBG(ltypeScale)DRW_dbg::dbg(ltypeScale); DRW_DBG("\n")DRW_dbg::dbg("\n");
1462 if (version > DRW::AC1014) {//2000+ — §19.4.1: linetype-flags BB then plot-flags BB
1463 ltFlags = buf->get2Bits(); //BB
1464 if (ltFlags == 0) lineType = "BYLAYER";
1465 else if (ltFlags == 1) lineType = "BYBLOCK";
1466 else if (ltFlags == 2) lineType = "CONTINUOUS";
1467 else lineType = ""; //3 → handle at end
1468 DRW_DBG("ltFlags: ")DRW_dbg::dbg("ltFlags: "); DRW_DBG(ltFlags)DRW_dbg::dbg(ltFlags);
1469 DRW_DBG(" lineType: ")DRW_dbg::dbg(" lineType: "); DRW_DBG(lineType.c_str())DRW_dbg::dbg(lineType.c_str());
1470
1471 plotFlags = buf->get2Bits(); //BB
1472 DRW_DBG(", plotFlags: ")DRW_dbg::dbg(", plotFlags: "); DRW_DBG(plotFlags)DRW_dbg::dbg(plotFlags);
1473 }
1474 if (version > DRW::AC1018) {//2007+
1475 materialFlag = buf->get2Bits(); //BB
1476 DRW_DBG("materialFlag: ")DRW_dbg::dbg("materialFlag: "); DRW_DBG(materialFlag)DRW_dbg::dbg(materialFlag);
1477 shadowFlag = buf->getRawChar8(); //RC, low 2 bits is shadow mode 0..3
1478 DRW_DBG("shadowFlag: ")DRW_dbg::dbg("shadowFlag: "); DRW_DBG(shadowFlag)DRW_dbg::dbg(shadowFlag); DRW_DBG("\n")DRW_dbg::dbg("\n");
1479 shadow = static_cast<DRW::ShadowMode>(shadowFlag & 0x3);
1480 }
1481 if (version > DRW::AC1021) {//2010+ — §19.4.1: three single-bit flags
1482 // Ground-truth: libreDWG common_entity_data.spec lines 523-528
1483 // and ODA spec v5.4.1 §19.4.1 both define three FIELD_B (single bit)
1484 // flags here, one each for full/face/edge visual style. Total bit
1485 // consumption (3 bits) is identical to the historical BB+B shape;
1486 // only the semantics differ. The corresponding handles are read
1487 // conditionally in parseDwgEntHandle after the plotstyle handle.
1488 hasFullVisualStyle = buf->getBit(); //B
1489 hasFaceVisualStyle = buf->getBit(); //B
1490 hasEdgeVisualStyle = buf->getBit(); //B
1491 DRW_DBG("hasFull/Face/Edge VisualStyle: ")DRW_dbg::dbg("hasFull/Face/Edge VisualStyle: ");
1492 DRW_DBG(hasFullVisualStyle)DRW_dbg::dbg(hasFullVisualStyle); DRW_DBG(" ")DRW_dbg::dbg(" ");
1493 DRW_DBG(hasFaceVisualStyle)DRW_dbg::dbg(hasFaceVisualStyle); DRW_DBG(" ")DRW_dbg::dbg(" ");
1494 DRW_DBG(hasEdgeVisualStyle)DRW_dbg::dbg(hasEdgeVisualStyle); DRW_DBG("\n")DRW_dbg::dbg("\n");
1495 }
1496 std::int16_t invisibleFlag = buf->getBitShort(); //BS
1497 DRW_DBG(" invisibleFlag: ")DRW_dbg::dbg(" invisibleFlag: "); DRW_DBG(invisibleFlag)DRW_dbg::dbg(invisibleFlag);
1498 // DXF group 60: bit 0 = invisible (1) / visible (0). libreDWG
1499 // common_entity_data.spec masks bit 0 only (`invisible & 1`) and ignores
1500 // the higher bits, so use the same mask rather than `== 0`. Paired with
1501 // the encode emit below.
1502 visible = ((invisibleFlag & 1) == 0);
1503 if (version > DRW::AC1014) {//2000+
1504 lWeight = DRW_LW_Conv::dwgInt2lineWidth( buf->getRawChar8() ); //RC
1505 DRW_DBG(" lwFlag (lWeight): ")DRW_dbg::dbg(" lwFlag (lWeight): "); DRW_DBG(lWeight)DRW_dbg::dbg(lWeight); DRW_DBG("\n")DRW_dbg::dbg("\n");
1506 }
1507 //Only in blocks ????????
1508// if (version > DRW::AC1018) {//2007+
1509// std::uint8_t unk = buf->getBit();
1510// DRW_DBG("unknown bit: "); DRW_DBG(unk); DRW_DBG("\n");
1511// }
1512 return buf->isGood();
1513}
1514
1515bool DRW_Entity::parseDwgEntHandle(DRW::Version version, dwgBuffer *buf, bool resetHandleStream){
1516 if (resetHandleStream && version > DRW::AC1018) {//2007+ skip string area
1517 buf->setPosition(objSize >> 3);
1518 buf->setBitPos(objSize & 7);
1519 }
1520
1521 // libreDWG common_entity_data.spec:454-459: when ENC flag 0x40 is set,
1522 // an AcDbColor reference handle is the FIRST item in the handle stream
1523 // — read before owner / reactors / xdic / etc. Set in parseDwg via
1524 // dwgBuffer::lastEnColorHadDbColorRef. The dwgReader resolves this
1525 // handle against dbColorMap after parseDwg returns and patches
1526 // color24 + colorName onto the entity.
1527 if (hasAcDbColorH && version > DRW::AC1015 && buf->numRemainingBytes() >= 4) {
1528 dwgHandle dbcH = buf->getOffsetHandle(handle);
1529 acDbColorHandle = dbcH.ref;
1530 DRW_DBG(" AcDbColor Handle: ")DRW_dbg::dbg(" AcDbColor Handle: ");
1531 DRW_DBGHL(dbcH.code, dbcH.size, dbcH.ref)DRW_dbg::dbgHL(dbcH.code, dbcH.size, dbcH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1532 }
1533
1534 if(ownerHandle){//entity are in block or in a polyline
1535 dwgHandle ownerH = buf->getOffsetHandle(handle);
1536 DRW_DBG("owner (parent) Handle: ")DRW_dbg::dbg("owner (parent) Handle: "); DRW_DBGHL(ownerH.code, ownerH.size, ownerH.ref)DRW_dbg::dbgHL(ownerH.code, ownerH.size, ownerH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1537 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1538 parentHandle = ownerH.ref;
1539 DRW_DBG("Block (parent) Handle: ")DRW_dbg::dbg("Block (parent) Handle: "); DRW_DBGHL(ownerH.code, ownerH.size, parentHandle)DRW_dbg::dbgHL(ownerH.code, ownerH.size, parentHandle); DRW_DBG("\n")DRW_dbg::dbg("\n");
1540 } else
1541 DRW_DBG("NO Block (parent) Handle\n")DRW_dbg::dbg("NO Block (parent) Handle\n");
1542
1543 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1544 reactorHandles.clear();
1545 for (int i=0; i< numReactors;++i) {
1546 dwgHandle reactorsH = buf->getHandle();
1547 reactorHandles.push_back(reactorsH.ref); // 2a.2: persist reactors
1548 DRW_DBG(" reactorsH control Handle: ")DRW_dbg::dbg(" reactorsH control Handle: "); DRW_DBGHL(reactorsH.code, reactorsH.size, reactorsH.ref)DRW_dbg::dbgHL(reactorsH.code, reactorsH.size, reactorsH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1549 }
1550 if (xDictFlag !=1){//linetype in 2004 seems not have XDicObjH or NULL handle
1551 dwgHandle XDicObjH = buf->getHandle();
1552 xDictHandle = XDicObjH.ref; // 2a.2: persist xdict
1553 DRW_DBG(" XDicObj control Handle: ")DRW_dbg::dbg(" XDicObj control Handle: "); DRW_DBGHL(XDicObjH.code, XDicObjH.size, XDicObjH.ref)DRW_dbg::dbgHL(XDicObjH.code, XDicObjH.size, XDicObjH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1554 }
1555 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1556
1557 if (version < DRW::AC1015) {//R14-
1558 //layer handle
1559 layerH = buf->getOffsetHandle(handle);
1560 DRW_DBG(" layer Handle: ")DRW_dbg::dbg(" layer Handle: "); DRW_DBGHL(layerH.code, layerH.size, layerH.ref)DRW_dbg::dbgHL(layerH.code, layerH.size, layerH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1561 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1562 //lineType handle
1563 if(ltFlags == 3){
1564 lTypeH = buf->getOffsetHandle(handle);
1565 DRW_DBG("linetype Handle: ")DRW_dbg::dbg("linetype Handle: "); DRW_DBGHL(lTypeH.code, lTypeH.size, lTypeH.ref)DRW_dbg::dbgHL(lTypeH.code, lTypeH.size, lTypeH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1566 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1567 }
1568 }
1569 if (version < DRW::AC1018) {//2000+
1570 if (haveNextLinks == 0) {
1571 dwgHandle nextLinkH = buf->getOffsetHandle(handle);
1572 DRW_DBG(" prev nextLinkers Handle: ")DRW_dbg::dbg(" prev nextLinkers Handle: "); DRW_DBGHL(nextLinkH.code, nextLinkH.size, nextLinkH.ref)DRW_dbg::dbgHL(nextLinkH.code, nextLinkH.size, nextLinkH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1573 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1574 prevEntLink = nextLinkH.ref;
1575 nextLinkH = buf->getOffsetHandle(handle);
1576 DRW_DBG(" next nextLinkers Handle: ")DRW_dbg::dbg(" next nextLinkers Handle: "); DRW_DBGHL(nextLinkH.code, nextLinkH.size, nextLinkH.ref)DRW_dbg::dbgHL(nextLinkH.code, nextLinkH.size, nextLinkH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1577 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1578 nextEntLink = nextLinkH.ref;
1579 } else {
1580 nextEntLink = handle+1;
1581 prevEntLink = handle-1;
1582 }
1583 }
1584 if (version > DRW::AC1015) {//2004+
1585 //Parses Bookcolor handle
1586 }
1587 if (version > DRW::AC1014) {//2000+
1588 //layer handle
1589 layerH = buf->getOffsetHandle(handle);
1590 DRW_DBG(" layer Handle: ")DRW_dbg::dbg(" layer Handle: "); DRW_DBGHL(layerH.code, layerH.size, layerH.ref)DRW_dbg::dbgHL(layerH.code, layerH.size, layerH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1591 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1592 //lineType handle
1593 if(ltFlags == 3){
1594 lTypeH = buf->getOffsetHandle(handle);
1595 DRW_DBG("linetype Handle: ")DRW_dbg::dbg("linetype Handle: "); DRW_DBGHL(lTypeH.code, lTypeH.size, lTypeH.ref)DRW_dbg::dbgHL(lTypeH.code, lTypeH.size, lTypeH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1596 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1597 }
1598 }
1599 if (version > DRW::AC1014) {//2000+
1600 if (version > DRW::AC1018) {//2007+
1601 if (materialFlag == 3) {
1602 dwgHandle materialH = buf->getOffsetHandle(handle);
1603 material = materialH.ref;
1604 DRW_DBG(" material Handle: ")DRW_dbg::dbg(" material Handle: "); DRW_DBGHL(materialH.code, materialH.size, materialH.ref)DRW_dbg::dbgHL(materialH.code, materialH.size, materialH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1605 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1606 }
1607 if (shadowFlag == 3) {
1608 // AcDbShadow object handle (separate from entity shadow mode
1609 // populated from shadowFlag & 0x3 above). LibreCAD has no
1610 // shadow object consumer; leave discarding.
1611 dwgHandle shadowH = buf->getOffsetHandle(handle);
1612 DRW_DBG(" shadow Handle: ")DRW_dbg::dbg(" shadow Handle: "); DRW_DBGHL(shadowH.code, shadowH.size, shadowH.ref)DRW_dbg::dbgHL(shadowH.code, shadowH.size, shadowH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1613 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1614 }
1615 }
1616 if (plotFlags == 3) {
1617 dwgHandle plotStyleH = buf->getOffsetHandle(handle);
1618 plotStyle = static_cast<int>(plotStyleH.ref);
1619 DRW_DBG(" plot style Handle: ")DRW_dbg::dbg(" plot style Handle: "); DRW_DBGHL(plotStyleH.code, plotStyleH.size, plotStyleH.ref)DRW_dbg::dbgHL(plotStyleH.code, plotStyleH.size, plotStyleH.ref
)
; DRW_DBG("\n")DRW_dbg::dbg("\n");
1620 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1621 }
1622 if (version > DRW::AC1021) {//2010+ — §19.4.2: visual-style handles
1623 // Ground-truth: libreDWG common_entity_handle_data.spec lines
1624 // 141-150 and ODA spec v5.4.1 §19.4.2. Order matches: full,
1625 // face, edge — each conditional on its single-bit flag from
1626 // §19.4.1 (set in parseDwg above). All three are hard pointers
1627 // (libreDWG FIELD_HANDLE code 5), matching the existing
1628 // material/shadow/plotstyle handles in this block.
1629 if (hasFullVisualStyle) {
1630 dwgHandle h = buf->getOffsetHandle(handle);
1631 fullVisualStyleHandle = h.ref;
1632 DRW_DBG(" full visual-style H: ")DRW_dbg::dbg(" full visual-style H: ");
1633 DRW_DBGHL(h.code, h.size, h.ref)DRW_dbg::dbgHL(h.code, h.size, h.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1634 }
1635 if (hasFaceVisualStyle) {
1636 dwgHandle h = buf->getOffsetHandle(handle);
1637 faceVisualStyleHandle = h.ref;
1638 DRW_DBG(" face visual-style H: ")DRW_dbg::dbg(" face visual-style H: ");
1639 DRW_DBGHL(h.code, h.size, h.ref)DRW_dbg::dbgHL(h.code, h.size, h.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1640 }
1641 if (hasEdgeVisualStyle) {
1642 dwgHandle h = buf->getOffsetHandle(handle);
1643 edgeVisualStyleHandle = h.ref;
1644 DRW_DBG(" edge visual-style H: ")DRW_dbg::dbg(" edge visual-style H: ");
1645 DRW_DBGHL(h.code, h.size, h.ref)DRW_dbg::dbgHL(h.code, h.size, h.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
1646 }
1647 }
1648 }
1649 const int rb = buf->numRemainingBytes();
1650 DRW_DBG("\n DRW_Entity::parseDwgEntHandle Remaining bytes: ")DRW_dbg::dbg("\n DRW_Entity::parseDwgEntHandle Remaining bytes: "
)
; DRW_DBG(rb)DRW_dbg::dbg(rb); DRW_DBG("\n")DRW_dbg::dbg("\n");
1651 if (rb > 4) { // 2-byte CRC + slack
1652 DRW_DBG("\n*** parseDwgEntHandle leftover ")DRW_dbg::dbg("\n*** parseDwgEntHandle leftover ");
1653 DRW_DBG(rb)DRW_dbg::dbg(rb);
1654 DRW_DBG(" bytes; entity handle ")DRW_dbg::dbg(" bytes; entity handle ");
1655 DRW_DBGH(handle)DRW_dbg::dbgH(handle);
1656 DRW_DBG(" oType ")DRW_dbg::dbg(" oType ");
1657 DRW_DBG(oType)DRW_dbg::dbg(oType);
1658 DRW_DBG(" — possible bit-stream misalignment ***\n")DRW_dbg::dbg(" — possible bit-stream misalignment ***\n");
1659 }
1660 return buf->isGood();
1661}
1662
1663bool DRW_Point::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
1664 switch (code) {
1665 case 10:
1666 basePoint.x = reader->getDouble();
1667 break;
1668 case 20:
1669 basePoint.y = reader->getDouble();
1670 break;
1671 case 30:
1672 basePoint.z = reader->getDouble();
1673 break;
1674 case 39:
1675 thickness = reader->getDouble();
1676 break;
1677 case 50:
1678 // DXF code 50 is in degrees; the field is radians (matching the DWG
1679 // path, drw_entities.cpp:1787). degrees -> radians is /ARAD (x pi/180);
1680 // the prior *ARAD only canceled with the writer's /ARAD on DXF->DXF and
1681 // corrupted the value for DXF->DWG. ARAD = 180/pi.
1682 xAxisAngle = reader->getDouble() / ARAD57.29577951308232; // DXF degrees -> radians
1683 break;
1684 case 210:
1685 haveExtrusion = true;
1686 extPoint.x = reader->getDouble();
1687 break;
1688 case 220:
1689 extPoint.y = reader->getDouble();
1690 break;
1691 case 230:
1692 extPoint.z = reader->getDouble();
1693 break;
1694 default:
1695 return DRW_Entity::parseCode(code, reader);
1696 }
1697
1698 return true;
1699}
1700
1701// ---------------------------------------------------------------------------
1702// Phase 4a (drafted 2026-05-15)
1703// ---------------------------------------------------------------------------
1704// `DRW_Entity::encodeDwgCommon` and `encodeDwgEntHandle` are R2000-only
1705// inverses of the corresponding parseDwg fragments above. The version
1706// conditionals collapse: all `version > AC1014` branches fire (R2000 is
1707// AC1015 > AC1014), all `version > AC1015` and `version > AC1018` and
1708// `version > AC1021` branches skip. Likewise `version < AC1015` skips.
1709//
1710// Discarded fields (Risk 4i):
1711// - graphFlag B + optional graphData — we always emit graphFlag=0.
1712// - haveNextLinks B — we emit 1 (no prev/next chain).
1713// - acDbColorH — only fires when ENC flag 0x40 set; R2000 entity
1714// encoders don't emit DBCOLOR refs yet.
1715//
1716// We always emit:
1717// - entmode = 2 (modelspace, no owner-handle in stream — caller can
1718// override before calling encodeDwgCommon if entity needs an owner).
1719// - numReactors = 0
1720// - ltFlags = 0 (BYLAYER), plotFlags = 0 (BYLAYER)
1721// - invisibleFlag = 0 (visible)
1722//
1723// Caller must:
1724// - Pre-populate `eType`, `handle`, `color`, `ltypeScale`, `lWeight`,
1725// `layerH.ref` (handle of the layer this entity belongs to).
1726// - The body emit between encodeDwgCommon and encodeDwgEntHandle is
1727// per-entity (3BD basePoint for Point, etc.).
1728
1729// Phase-2a kill switch for the full common-entity-header write contract
1730// (entity reactors/xdict/EED/visibility/entmode emission). Default ON. The
1731// emission is gated by DATA PRESENCE (empty reactorHandles/extData + visible
1732// == today's hardcoded zeros), so flipping this OFF restores the legacy
1733// byte-identical output as an emergency escape hatch. The per-field emission
1734// arms land in 2a.1..2a.5; this scaffolding commit changes no bytes.
1735#ifndef LIBDXFRW_FULL_COMMON_HEADER1
1736#define LIBDXFRW_FULL_COMMON_HEADER1 1
1737#endif
1738
1739bool DRW_Entity::encodeDwgCommon(DRW::Version version, dwgBufferW *buf,
1740 dwgBufferW *strBuf) {
1741 (void)strBuf; // common data contains no strings
1742 if (version != DRW::AC1015 && version != DRW::AC1018 &&
1743 version != DRW::AC1024 && version != DRW::AC1027 &&
1744 version != DRW::AC1032) return false;
1745
1746 // Object type: BS for AC1015/AC1018, OT for AC1024+.
1747 buf->putObjType(version, static_cast<std::uint16_t>(oType));
1748
1749 // objSize stub — back-patched for AC1015/AC1018 only. AC1024 derives
1750 // objSize from the body buffer size, so no RL is emitted.
1751 if (version < DRW::AC1024) {
1752 buf->putRawLong32(0);
1753 }
1754
1755 // Own handle: code 0 per spec §20.4.1.
1756 dwgHandle ownH;
1757 ownH.code = 0;
1758 ownH.ref = handle;
1759 ownH.size = 0;
1760 if (handle != 0) {
1761 std::uint32_t t = handle;
1762 while (t != 0) { t >>= 8; ++ownH.size; }
1763 }
1764 buf->putHandle(ownH);
1765
1766 // No EED yet.
1767 buf->putBitShort(0); // extDataSize=0
1768
1769 // No graphics data.
1770 buf->putBit(0); // graphFlag=0
1771
1772 const bool hasOwner = parentHandle != DRW::NoHandle;
1773
1774 // entmode BB (ODA §20.4.1 / Open Design FE):
1775 // 0 = owner handle follows in the handle stream
1776 // 1 = paperspace entity without owner-relative handle
1777 // 2 = modelspace entity without owner-relative handle
1778 // Prefer owner when present; otherwise honor DRW_Entity::space.
1779 std::uint8_t entmode = 2;
1780 if (hasOwner)
1781 entmode = 0;
1782 else if (space == DRW::PaperSpace)
1783 entmode = 1;
1784 buf->put2Bits(entmode);
1785
1786 // numReactors (BL per spec §20.4.1). 2a.2: emit the real count; empty
1787 // reactorHandles → 0 → byte-identical to legacy.
1788#if LIBDXFRW_FULL_COMMON_HEADER1
1789 buf->putBitLong(static_cast<std::int32_t>(reactorHandles.size()));
1790#else
1791 buf->putBitLong(0);
1792#endif
1793
1794 // R2004/R2010 (AC1018, AC1024): reader reads xDictFlag bit (version > AC1015)
1795 // then forces haveNextLinks=1 (no bit in stream). We always emit
1796 // xDictFlag=0 (xdic-present) so the reader reads exactly one xdic handle
1797 // in the handle section — we emit the real handle when xDictHandle!=0 and
1798 // a null handle otherwise. This keeps the empty case byte-identical to the
1799 // legacy path (bit 0 + null handle) while round-tripping a real xdict.
1800 // R2000 (AC1015): no xDictFlag bit; reader's xDictFlag stays 0 so it ALWAYS
1801 // reads an xdic handle — same emit rule applies.
1802 // R2013+ (AC1027+): reader reads xDictFlag then reads haveNextLinks (bit restored).
1803 if (version == DRW::AC1015) {
1804 buf->putBit(1); // nolinks=1 (R2000: no prev/next chain)
1805 } else {
1806 buf->putBit(0); // xDictFlag=0 (xdic present; real-or-null handle follows)
1807 if (version > DRW::AC1024) {
1808 // libreDWG common_entity_data.spec — R2013+ has_ds_data (B). libdxfrw
1809 // never inlines an ACIS SAB datastore, so emit hasDsData (default 0).
1810 // The old code emitted literal 1 (mislabeled haveNextLinks), falsely
1811 // advertising an SAB blob and risking misparse in strict readers.
1812 buf->putBit(hasDsData);
1813 }
1814 }
1815
1816 // ENC color (BS for R2000/R2004/R2010).
1817 buf->putEnColor(version, static_cast<std::uint16_t>(color));
1818
1819 // ltypeScale BD.
1820 buf->putBitDouble(ltypeScale);
1821
1822 // ltFlags BB: 0=BYLAYER, 1=BYBLOCK, 2=CONTINUOUS, 3=lTypeH present.
1823 // Prefer an already-set ltFlags; otherwise derive from lineType / lTypeH.
1824 {
1825 auto upper = [](std::string s) {
1826 for (char& c : s)
1827 c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
1828 return s;
1829 };
1830 std::uint8_t flags = ltFlags;
1831 if (flags > 3)
1832 flags = 0;
1833 if (flags == 0 && lTypeH.ref != 0)
1834 flags = 3;
1835 if (flags == 0) {
1836 const std::string lt = upper(lineType);
1837 if (lt.empty() || lt == "BYLAYER")
1838 flags = 0;
1839 else if (lt == "BYBLOCK")
1840 flags = 1;
1841 else if (lt == "CONTINUOUS")
1842 flags = 2;
1843 else
1844 flags = 3; // named linetype — handle required
1845 }
1846 ltFlags = flags;
1847 }
1848 buf->put2Bits(ltFlags);
1849 // plotFlags BB: keep BYLAYER (0) this pass unless already set to 3.
1850 buf->put2Bits(plotFlags & 0x3);
1851
1852 // R2010 (AC1024): materialFlag BB + shadowFlag RC (version > AC1018).
1853 if (version > DRW::AC1018) {
1854 buf->put2Bits(0); // materialFlag BB = 0 (inherit)
1855 buf->putRawChar8(0); // shadowFlag RC = 0 (inherit)
1856 }
1857
1858 // R2010 (AC1024): three visual-style flag bits (version > AC1021).
1859 if (version > DRW::AC1021) {
1860 buf->putBit(0); // hasFullVisualStyle
1861 buf->putBit(0); // hasFaceVisualStyle
1862 buf->putBit(0); // hasEdgeVisualStyle
1863 }
1864
1865 // invisibleFlag BS (DXF 60). 2a.1: emit from `visible` (bit 0 = invisible)
1866 // instead of a hardcoded 0. visible==true → 0 → byte-identical to legacy.
1867#if LIBDXFRW_FULL_COMMON_HEADER1
1868 buf->putBitShort(visible ? 0 : 1);
1869#else
1870 buf->putBitShort(0);
1871#endif
1872
1873 // lWeight RC (0 = byLayer per DRW_LW_Conv).
1874 buf->putRawChar8(static_cast<std::uint8_t>(lWeight));
1875
1876 return true;
1877}
1878
1879bool DRW_Entity::encodeDwgEntHandle(DRW::Version version, dwgBufferW *buf,
1880 dwgBufferW *handleBuf) {
1881 if (version != DRW::AC1015 && version != DRW::AC1018 &&
1882 version != DRW::AC1024 && version != DRW::AC1027 &&
1883 version != DRW::AC1032) return false;
1884
1885 // For AC1024, handles are directed to handleBuf (the separate handle section);
1886 // for AC1015/AC1018, handles go into buf alongside the data.
1887 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
1888
1889 // Owner handle is present only when encodeDwgCommon emitted entmode=0.
1890 if (parentHandle != DRW::NoHandle) {
1891 dwgHandle owner;
1892 owner.code = 4; // soft pointer owner, read via getOffsetHandle()
1893 owner.ref = parentHandle;
1894 owner.size = 0;
1895 std::uint32_t t = parentHandle;
1896 while (t != 0) { t >>= 8; ++owner.size; }
1897 hb->putHandle(owner);
1898 }
1899
1900 // Reactor handles (2a.2): emitted before xdic, one per numReactors written
1901 // in the DATA section, as ABSOLUTE handles (reader uses getHandle()). Empty
1902 // reactorHandles → nothing emitted → byte-identical to legacy.
1903#if LIBDXFRW_FULL_COMMON_HEADER1
1904 for (std::uint32_t ref : reactorHandles) {
1905 dwgHandle rh;
1906 rh.code = 4; // soft pointer
1907 rh.ref = ref;
1908 rh.size = 0;
1909 if (ref != 0) { std::uint32_t t = ref; while (t != 0) { t >>= 8; ++rh.size; } }
1910 hb->putHandle(rh);
1911 }
1912#endif
1913
1914 // XDic handle — xDictFlag=0 in the DATA section means the reader reads one
1915 // XDicObj handle here: emit the real handle when xDictHandle!=0, else the
1916 // null handle (matching the legacy byte-for-byte for the empty case).
1917 dwgHandle xDic;
1918 xDic.code = 3;
1919#if LIBDXFRW_FULL_COMMON_HEADER1
1920 xDic.ref = xDictHandle;
1921 xDic.size = 0;
1922 if (xDictHandle != 0) {
1923 std::uint32_t t = xDictHandle; while (t != 0) { t >>= 8; ++xDic.size; }
1924 }
1925#else
1926 xDic.ref = 0;
1927 xDic.size = 0;
1928#endif
1929 hb->putHandle(xDic);
1930
1931 // Layer handle (R2000+ unconditional). Hard pointer.
1932 dwgHandle lH;
1933 lH.code = layerH.ref == 0 ? 0 : 5; // 5 = hard pointer for layer ref
1934 lH.ref = layerH.ref;
1935 lH.size = 0;
1936 if (lH.ref != 0) {
1937 std::uint32_t t = lH.ref;
1938 while (t != 0) { t >>= 8; ++lH.size; }
1939 }
1940 hb->putHandle(lH);
1941
1942 // ltFlags=3 → lTypeH (hard pointer, code 5) present; else omit.
1943 if (ltFlags == 3) {
1944 dwgHandle ltH;
1945 ltH.code = lTypeH.ref == 0 ? 0 : 5;
1946 ltH.ref = lTypeH.ref;
1947 ltH.size = 0;
1948 if (ltH.ref != 0) {
1949 std::uint32_t t = ltH.ref;
1950 while (t != 0) { t >>= 8; ++ltH.size; }
1951 }
1952 hb->putHandle(ltH);
1953 }
1954 // plotFlags remain 0 this pass → no plot-style handle.
1955 // materialFlag / visualStyle flags remain 0 → no extra handles.
1956
1957 return true;
1958}
1959
1960bool DRW_Point::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
1961 (void)bs; (void)strBuf;
1962 oType = 27; // POINT class id — see dwgreader.cpp:1111 dispatch
1963 if (!encodeDwgCommon(version, buf)) return false;
1964
1965 // Point body — mirror of DRW_Point::parseDwg below.
1966 buf->putBitDouble(basePoint.x);
1967 buf->putBitDouble(basePoint.y);
1968 buf->putBitDouble(basePoint.z);
1969 buf->putThickness(thickness, /*b_R2000_style=*/true);
1970 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
1971 buf->putBitDouble(xAxisAngle); // ODA §20.4.31 code 50
1972
1973 return encodeDwgEntHandle(version, buf, handleBuf);
1974}
1975
1976bool DRW_Point::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
1977 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
1978 if (!ret)
1979 return ret;
1980 DRW_DBG("\n***************************** parsing point *********************************************\n")DRW_dbg::dbg("\n***************************** parsing point *********************************************\n"
)
;
1981
1982 basePoint.x = buf->getBitDouble();
1983 basePoint.y = buf->getBitDouble();
1984 basePoint.z = buf->getBitDouble();
1985 DRW_DBG("point: ")DRW_dbg::dbg("point: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
1986 thickness = buf->getThickness(version > DRW::AC1014);//BD
1987 DRW_DBG("\nthickness: ")DRW_dbg::dbg("\nthickness: "); DRW_DBG(thickness)DRW_dbg::dbg(thickness);
1988 extPoint = buf->getExtrusion(version > DRW::AC1014);
1989 DRW_DBG(", Extrusion: ")DRW_dbg::dbg(", Extrusion: "); DRW_DBGPT(extPoint.x, extPoint.y, extPoint.z)DRW_dbg::dbgPT(extPoint.x, extPoint.y, extPoint.z);
1990
1991 xAxisAngle = buf->getBitDouble(); // ODA §20.4.31 code 50, stored in radians
1992 DRW_DBG("\n x_axis: ")DRW_dbg::dbg("\n x_axis: "); DRW_DBG(xAxisAngle)DRW_dbg::dbg(xAxisAngle); DRW_DBG("\n")DRW_dbg::dbg("\n");
1993 ret = DRW_Entity::parseDwgEntHandle(version, buf);
1994 if (!ret)
1995 return ret;
1996 // RS crc; //RS */
1997
1998 return buf->isGood();
1999}
2000
2001bool DRW_Line::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
2002 switch (code) {
2003 case 11:
2004 secPoint.x = reader->getDouble();
2005 break;
2006 case 21:
2007 secPoint.y = reader->getDouble();
2008 break;
2009 case 31:
2010 secPoint.z = reader->getDouble();
2011 break;
2012 default:
2013 return DRW_Point::parseCode(code, reader);
2014 }
2015
2016 return true;
2017}
2018
2019bool DRW_Line::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2020 (void)bs; (void)strBuf;
2021 oType = 19; // LINE class id — see dwgreader.cpp:1105
2022 if (!encodeDwgCommon(version, buf)) return false;
2023
2024 // R2000+ Line body — zIsZero shortcut: if both z's are 0, omit the
2025 // z fields entirely. Reader reads `zIsZero` first, then RD x +
2026 // DD secX (default = x), RD y + DD secY (default = y), and
2027 // conditionally RD z + DD secZ. Our putDefaultDouble always emits
2028 // the full RD via code 0b11; reader's getDefaultDouble with code
2029 // 0b11 returns the raw double.
2030 bool zIsZero = (basePoint.z == 0.0 && secPoint.z == 0.0);
2031 buf->putBit(zIsZero ? 1 : 0);
2032 buf->putRawDouble(basePoint.x);
2033 buf->putDefaultDouble(basePoint.x, secPoint.x);
2034 buf->putRawDouble(basePoint.y);
2035 buf->putDefaultDouble(basePoint.y, secPoint.y);
2036 if (!zIsZero) {
2037 buf->putRawDouble(basePoint.z);
2038 buf->putDefaultDouble(basePoint.z, secPoint.z);
2039 }
2040 buf->putThickness(thickness, /*b_R2000_style=*/true);
2041 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
2042
2043 return encodeDwgEntHandle(version, buf, handleBuf);
2044}
2045
2046bool DRW_Circle::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2047 (void)bs; (void)strBuf;
2048 oType = 18; // CIRCLE class id — see dwgreader.cpp:1099
2049 if (!encodeDwgCommon(version, buf)) return false;
2050
2051 // Circle body — mirror of DRW_Circle::parseDwg.
2052 buf->putBitDouble(basePoint.x);
2053 buf->putBitDouble(basePoint.y);
2054 buf->putBitDouble(basePoint.z);
2055 buf->putBitDouble(radious);
2056 buf->putThickness(thickness, /*b_R2000_style=*/true);
2057 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
2058
2059 return encodeDwgEntHandle(version, buf, handleBuf);
2060}
2061
2062bool DRW_Ray::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2063 (void)bs; (void)strBuf;
2064 // Ray = 40, Xline = 41 — derive from runtime type so DRW_Xline can
2065 // share this encoder (it inherits from DRW_Ray).
2066 oType = (eType == DRW::XLINE) ? 41 : 40;
2067 if (!encodeDwgCommon(version, buf)) return false;
2068
2069 // 3 BD basePoint + 3 BD vector — same layout as parseDwg.
2070 buf->putBitDouble(basePoint.x);
2071 buf->putBitDouble(basePoint.y);
2072 buf->putBitDouble(basePoint.z);
2073 buf->putBitDouble(secPoint.x);
2074 buf->putBitDouble(secPoint.y);
2075 buf->putBitDouble(secPoint.z);
2076
2077 return encodeDwgEntHandle(version, buf, handleBuf);
2078}
2079
2080bool DRW_Trace::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2081 (void)bs; (void)strBuf;
2082 oType = 32; // TRACE = 32 — see dwgreader.cpp:1317
2083 if (!encodeDwgCommon(version, buf)) return false;
2084
2085 // Trace body — mirror of parseDwg. Note the unusual layout:
2086 // thickness FIRST, then elevation (basePoint.z) as BD, then 4
2087 // corners as 2RD (z values share basePoint.z).
2088 buf->putThickness(thickness, /*b_R2000_style=*/true);
2089 buf->putBitDouble(basePoint.z);
2090 buf->putRawDouble(basePoint.x);
2091 buf->putRawDouble(basePoint.y);
2092 buf->putRawDouble(secPoint.x);
2093 buf->putRawDouble(secPoint.y);
2094 buf->putRawDouble(thirdPoint.x);
2095 buf->putRawDouble(thirdPoint.y);
2096 buf->putRawDouble(fourPoint.x);
2097 buf->putRawDouble(fourPoint.y);
2098 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
2099
2100 return encodeDwgEntHandle(version, buf, handleBuf);
2101}
2102
2103bool DRW_Spline::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2104 (void)bs; (void)strBuf;
2105 oType = 36; // SPLINE class id — see dwgreader.cpp:1329
2106 if (!encodeDwgCommon(version, buf)) return false;
2107 encodeDwgSplineBody(version, buf);
2108 return encodeDwgEntHandle(version, buf, handleBuf);
2109}
2110
2111// Spline body encode: the scenario/degree/knots/ctrl/fit section, WITHOUT the
2112// leading encodeDwgCommon or the trailing encodeDwgEntHandle. Factored out so
2113// DRW_Helix::encodeDwg can reuse the identical payload (Phase 8a-1).
2114// Omits the DXF-only flag70/extrusion (210-230) which the DWG stream never
2115// carries here.
2116void DRW_Spline::encodeDwgSplineBody(DRW::Version version, dwgBufferW *buf) const {
2117 // Scenario:
2118 // 1 = control-point / rational / planar (uses knots + control + weights)
2119 // 2 = fit-point (uses fit points + tangents + tolerance)
2120 // When both lists are populated (e.g. DXF-sourced splines), prefer scenario 1
2121 // (ctrl + knots) and drop the fit list from the DWG stream — scenario 1 has no
2122 // fit-point section, so writing both would corrupt all subsequent entities.
2123 const bool hasFit = !fitlist.empty();
2124 const bool hasCtrl = !controllist.empty();
2125 std::int32_t scenario = (hasFit && !hasCtrl) ? 2 : 1;
2126 if (m_scenario == 1 && hasCtrl) {
2127 scenario = 1;
2128 } else if (m_scenario == 2 && hasFit) {
2129 scenario = 2;
2130 }
2131 buf->putBitLong(scenario);
2132 if (version > DRW::AC1024) {
2133 // splFlag1 bit 0: method = fit points; bit 2: closed;
2134 // bit 3: knotParam participates in R2013+ scenario selection.
2135 std::int32_t splFlag1 = m_splineFlags1;
2136 splFlag1 &= ~(kSplineFlagMethodFitPoints | kSplineFlagUseKnotParameter | kSplineFlagClosed);
2137 if (scenario == 2) {
2138 splFlag1 |= kSplineFlagMethodFitPoints | kSplineFlagUseKnotParameter;
2139 if (flags & 0x01) splFlag1 |= kSplineFlagClosed;
2140 } else {
2141 if (flags & 0x01) splFlag1 |= kSplineFlagClosed;
2142 }
2143 buf->putBitLong(splFlag1);
2144 std::int32_t knotParam = m_knotParam;
2145 if (scenario == 1) {
2146 knotParam = kSplineKnotParamCustom;
2147 } else if (knotParam == kSplineKnotParamCustom) {
2148 knotParam = 0;
2149 }
2150 buf->putBitLong(knotParam);
2151 }
2152 buf->putBitLong(static_cast<std::int32_t>(degree));
2153
2154 if (scenario == 2) {
2155 buf->putBitDouble(tolfit);
2156 buf->put3BitDouble(tgStart);
2157 buf->put3BitDouble(tgEnd);
2158 const std::int32_t nFit = static_cast<std::int32_t>(fitlist.size());
2159 buf->putBitLong(nFit);
2160 } else {
2161 // scenario == 1
2162 // Reader at parseDwg reads three flag bits in this order:
2163 // rational bit (flags bit 2 → 0x04)
2164 // closed bit (flags bit 0 → 0x01)
2165 // periodic bit (flags bit 1 → 0x02)
2166 const bool hasNonDefaultWeights = std::any_of(weightlist.begin(), weightlist.end(), differsFromUnitWeight);
2167 buf->putBit(((flags & 0x4) || hasNonDefaultWeights) ? 1 : 0); // rational
2168 buf->putBit((flags & 0x1) ? 1 : 0); // closed
2169 buf->putBit((flags & 0x2) ? 1 : 0); // periodic
2170 buf->putBitDouble(tolknot);
2171 buf->putBitDouble(tolcontrol);
2172 const std::int32_t nKnots = static_cast<std::int32_t>(knotslist.size());
2173 const std::int32_t nCtrl = static_cast<std::int32_t>(controllist.size());
2174 buf->putBitLong(nKnots);
2175 buf->putBitLong(nCtrl);
2176 // weight bit: caller populates weightlist when each control point
2177 // has a non-default weight (NURBS conics).
2178 bool hasWeights = !weightlist.empty();
2179 buf->putBit(hasWeights ? 1 : 0);
2180 }
2181
2182 // Data sections are scenario-gated to avoid stream corruption:
2183 // parseDwg reads knots+ctrl only for scenario 1, fit only for scenario 2.
2184 if (scenario == 1) {
2185 for (double k : knotslist) buf->putBitDouble(k);
2186 for (size_t i = 0; i < controllist.size(); ++i) {
2187 buf->put3BitDouble(*controllist[i]);
2188 if (!weightlist.empty()) {
2189 double w = (i < weightlist.size()) ? weightlist[i] : 1.0;
2190 buf->putBitDouble(w);
2191 }
2192 }
2193 } else {
2194 for (const auto& fp : fitlist) buf->put3BitDouble(*fp);
2195 }
2196}
2197
2198// DRW_Helix::encodeDwg — spline body (oType = HELIX class 503) + AcDbHelix
2199// trailer, then the common entity handle data. Trailer field order MUST match
2200// DRW_Helix::parseDwg (Phase 8a-1).
2201bool DRW_Helix::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2202 (void)bs; (void)strBuf;
2203 oType = kDwgClassNum; // HELIX custom class 503
2204 if (!encodeDwgCommon(version, buf)) return false;
2205 encodeDwgSplineBody(version, buf);
2206
2207 // AcDbHelix trailer (same order as parseDwg):
2208 buf->putBitLong(m_majorVersion);
2209 buf->putBitLong(m_maintVersion);
2210 buf->put3BitDouble(axisBasePt);
2211 buf->put3BitDouble(startPt);
2212 buf->put3BitDouble(axisVector);
2213 buf->putBitDouble(radius);
2214 buf->putBitDouble(turns);
2215 buf->putBitDouble(turnHeight);
2216 buf->putBit(handedness ? 1 : 0);
2217 buf->putRawChar8(static_cast<std::uint8_t>(constraintType));
2218
2219 return encodeDwgEntHandle(version, buf, handleBuf);
2220}
2221
2222bool DRW_MText::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2223 (void)bs;
2224 oType = 44; // MTEXT class id — see dwgreader.cpp:1215
2225 if (!encodeDwgCommon(version, buf)) return false;
2226
2227 // R2000/R2004/R2010 MTEXT body — mirror of DRW_MText::parseDwg.
2228 buf->put3BitDouble(basePoint); // insertion
2229 buf->put3BitDouble(extPoint); // extrusion
2230 buf->put3BitDouble(secPoint); // X-axis dir
2231 buf->putBitDouble(widthscale); // rect width
2232 if (version > DRW::AC1018) {
2233 buf->putBitDouble(0.0); // rect height, R2007+
2234 }
2235 buf->putBitDouble(height); // text height
2236 buf->putBitShort(static_cast<std::uint16_t>(textgen)); // attachment
2237 buf->putBitShort(static_cast<std::uint16_t>(alignH)); // drawing dir
2238 buf->putBitDouble(0.0); // ext_ht (extents height; undocumented)
2239 buf->putBitDouble(0.0); // ext_wid (extents width; undocumented)
2240 // For AC1024: text goes to string buffer; for AC1015/AC1018: inline.
2241 (strBuf ? strBuf : buf)->putVariableText(version, text);
2242 // R2000+ extras:
2243 buf->putBitShort(linespacingStyle); // linespacing style BS 73
2244 buf->putBitDouble(interlin); // linespacing factor BD
2245 buf->putBit(0); // unknown bit
2246 if (version > DRW::AC1015) { // R2004+: background flags BL
2247 buf->putBitLong(m_backgroundFlags);
2248 if ((m_backgroundFlags & 0x01) || (version >= DRW::AC1032 && (m_backgroundFlags & 0x10))) {
2249 buf->putBitDouble(m_backgroundScale); // BitDouble (matches the read fix)
2250 buf->putCmColor(version, static_cast<std::uint16_t>(m_backgroundColor));
2251 buf->putBitLong(m_backgroundTransparency);
2252 }
2253 }
2254 if (version >= DRW::AC1032) {
2255 buf->putBit(m_r2018IsNotAnnotative ? 1 : 0);
2256 if (m_r2018IsNotAnnotative) {
2257 buf->putBitShort(m_r2018Version);
2258 buf->putBit(m_r2018DefaultFlag ? 1 : 0);
2259 buf->putBitLong(m_r2018Attachment);
2260 buf->put3BitDouble(m_r2018XAxisDir);
2261 buf->put3BitDouble(m_r2018InsertionPoint);
2262 buf->putBitDouble(m_r2018RectWidth);
2263 buf->putBitDouble(m_r2018RectHeight);
2264 buf->putBitDouble(m_r2018ExtentsHeight);
2265 buf->putBitDouble(m_r2018ExtentsWidth);
2266 buf->putBitShort(m_r2018ColumnType);
2267 if (m_r2018ColumnType != 0) {
2268 std::int32_t columnCount = m_r2018ColumnCount;
2269 if (!m_r2018ColumnAutoHeight && m_r2018ColumnType == 2
2270 && !m_r2018ColumnHeights.empty()) {
2271 columnCount = static_cast<std::int32_t>(m_r2018ColumnHeights.size());
2272 }
2273 buf->putBitLong(columnCount);
2274 buf->putBitDouble(m_r2018ColumnWidth);
2275 buf->putBitDouble(m_r2018ColumnGutter);
2276 buf->putBit(m_r2018ColumnAutoHeight ? 1 : 0);
2277 buf->putBit(m_r2018ColumnFlowReversed ? 1 : 0);
2278 if (!m_r2018ColumnAutoHeight && m_r2018ColumnType == 2) {
2279 for (std::int32_t i = 0; i < columnCount; ++i) {
2280 const double columnHeight = static_cast<size_t>(i) < m_r2018ColumnHeights.size()
2281 ? m_r2018ColumnHeights[static_cast<size_t>(i)]
2282 : 0.0;
2283 buf->putBitDouble(columnHeight);
2284 }
2285 }
2286 }
2287 }
2288 }
2289
2290 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
2291
2292 // styleH — hard pointer to STYLE table record (default STANDARD).
2293 dwgBufferW *hb = handleBuf ? handleBuf : buf;
2294 putHardPointerHandle(hb, (styleH.ref == 0) ? 0x13 : styleH.ref);
2295 if (version >= DRW::AC1032 && m_r2018IsNotAnnotative)
2296 putHardPointerHandle(hb, (m_r2018AppIdHandle == 0) ? 0x14 : m_r2018AppIdHandle);
2297 return true;
2298}
2299
2300bool DRW_Insert::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2301 (void)bs; (void)strBuf;
2302 // 2b.6: emit MINSERT (oType 8) when a column/row grid is present;
2303 // otherwise a plain INSERT (oType 7). The reader keys the grid block off
2304 // oType==8 (parseDwg :3189).
2305 oType = isMInsert() ? 8 : 7;
2306 if (!encodeDwgCommon(version, buf)) return false;
2307
2308 // INSERT body — mirror of DRW_Insert::parseDwg for R2000.
2309 buf->putBitDouble(basePoint.x);
2310 buf->putBitDouble(basePoint.y);
2311 buf->putBitDouble(basePoint.z);
2312
2313 // dataFlags: pick the most compact form based on actual scales.
2314 // 3 → all scales default to 1.0 (no emit)
2315 // 2 → uniform scale (xscale RD only; yscale=zscale=xscale)
2316 // 1 → xscale defaults to 1, yscale/zscale as DD against xscale
2317 // 0 → xscale RD; yscale/zscale as DD against xscale
2318 if (sameStoredDouble(xscale, 1.0) && sameStoredDouble(yscale, 1.0)
2319 && sameStoredDouble(zscale, 1.0)) {
2320 buf->put2Bits(3);
2321 } else if (sameStoredDouble(xscale, yscale) && sameStoredDouble(yscale, zscale)) {
2322 buf->put2Bits(2);
2323 buf->putRawDouble(xscale);
2324 } else if (sameStoredDouble(xscale, 1.0)) {
2325 // xscale is exactly 1.0 (parseDwg leaves it at its 1.0 default and
2326 // never reads it in this branch), so it can be omitted; y and z are
2327 // independent and go through the same DD-against-1.0 path parseDwg
2328 // reads them with.
2329 buf->put2Bits(1);
2330 buf->putDefaultDouble(1.0, yscale);
2331 buf->putDefaultDouble(1.0, zscale);
2332 } else {
2333 // Use dataFlags=0 (general case): RD x + DD y + DD z.
2334 buf->put2Bits(0);
2335 buf->putRawDouble(xscale);
2336 buf->putDefaultDouble(xscale, yscale);
2337 buf->putDefaultDouble(xscale, zscale);
2338 }
2339
2340 buf->putBitDouble(angle); // radians
2341 buf->putExtrusion(extPoint, /*b_R2000_style=*/false);
2342 buf->putBit(0); // hasAttrib = 0 (no ATTRIBs)
2343 // hasAttrib==0 ⇒ the SINCE-R2004 num_owned BL is absent (parse :3184), so
2344 // the MINSERT grid (oType==8) follows the hasAttrib bit directly. Field
2345 // order mirrors parseDwg :3190-3193 (colcount BS, rowcount BS, colspace BD,
2346 // rowspace BD) and libreDWG dwg.spec num_cols/num_rows/col_spacing/row_spacing.
2347 if (oType == 8) { // MINSERT grid
2348 buf->putBitShort(static_cast<std::uint16_t>(colcount));
2349 buf->putBitShort(static_cast<std::uint16_t>(rowcount));
2350 buf->putBitDouble(colspace);
2351 buf->putBitDouble(rowspace);
2352 }
2353
2354 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
2355
2356 // BLOCK_RECORD hard pointer.
2357 dwgHandle bhH;
2358 bhH.code = (blockRecH.ref == 0) ? 0 : 5;
2359 bhH.ref = blockRecH.ref;
2360 bhH.size = 0;
2361 if (bhH.ref != 0) {
2362 std::uint32_t t = bhH.ref;
2363 while (t != 0) { t >>= 8; ++bhH.size; }
2364 }
2365 (handleBuf ? handleBuf : buf)->putHandle(bhH);
2366 return true;
2367}
2368
2369bool DRW_3Dface::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2370 (void)bs; (void)strBuf;
2371 oType = 28; // 3DFACE class id — see dwgreader.cpp:1237
2372 if (!encodeDwgCommon(version, buf)) return false;
2373
2374 // R2000+ 3DFACE body — mirror of parseDwg's z_is_zero / has_no_flag
2375 // optimization. Reader checks `invisibleflag != NoEdge`; if NoEdge,
2376 // emit has_no_flag=1 to suppress the BS read.
2377 bool hasNoFlag = (invisibleflag == /*NoEdge*/0);
2378 bool zIsZero = (basePoint.z == 0.0);
2379 buf->putBit(hasNoFlag ? 1 : 0);
2380 buf->putBit(zIsZero ? 1 : 0);
2381 buf->putRawDouble(basePoint.x);
2382 buf->putRawDouble(basePoint.y);
2383 if (!zIsZero) buf->putRawDouble(basePoint.z);
2384 buf->putDefaultDouble(basePoint.x, secPoint.x);
2385 buf->putDefaultDouble(basePoint.y, secPoint.y);
2386 buf->putDefaultDouble(basePoint.z, secPoint.z);
2387 buf->putDefaultDouble(secPoint.x, thirdPoint.x);
2388 buf->putDefaultDouble(secPoint.y, thirdPoint.y);
2389 buf->putDefaultDouble(secPoint.z, thirdPoint.z);
2390 buf->putDefaultDouble(thirdPoint.x, fourPoint.x);
2391 buf->putDefaultDouble(thirdPoint.y, fourPoint.y);
2392 buf->putDefaultDouble(thirdPoint.z, fourPoint.z);
2393 if (!hasNoFlag) buf->putBitShort(static_cast<std::uint16_t>(invisibleflag));
2394
2395 return encodeDwgEntHandle(version, buf, handleBuf);
2396}
2397
2398bool DRW_Solid::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2399 (void)bs; (void)strBuf;
2400 oType = 31; // SOLID class id — see dwgreader.cpp:1305
2401 if (!encodeDwgCommon(version, buf)) return false;
2402
2403 // Same body layout as TRACE (4 corners + extrusion). Duplicated
2404 // here rather than delegating to DRW_Trace::encodeDwg because that
2405 // hardcodes oType=32.
2406 buf->putThickness(thickness, /*b_R2000_style=*/true);
2407 buf->putBitDouble(basePoint.z);
2408 buf->putRawDouble(basePoint.x);
2409 buf->putRawDouble(basePoint.y);
2410 buf->putRawDouble(secPoint.x);
2411 buf->putRawDouble(secPoint.y);
2412 buf->putRawDouble(thirdPoint.x);
2413 buf->putRawDouble(thirdPoint.y);
2414 buf->putRawDouble(fourPoint.x);
2415 buf->putRawDouble(fourPoint.y);
2416 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
2417
2418 return encodeDwgEntHandle(version, buf, handleBuf);
2419}
2420
2421bool DRW_LWPolyline::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2422 (void)bs; (void)strBuf;
2423 oType = 77; // LWPOLYLINE class id — see dwgreader.cpp:1202
2424 if (!encodeDwgCommon(version, buf)) return false;
2425
2426 // DRW_LWPolyline::flags carries DXF-side bits (1=closed, 128=plinegen).
2427 // DWG-side flags are different: they signal which optional fields are
2428 // present. Per parseDwg, bit 9 (0x200) = closed, bit 8 (0x100) =
2429 // plinegen. Build the DWG flags from the DXF flags plus the data.
2430 std::uint16_t dwgFlags = 0;
2431 if (flags & 1) dwgFlags |= 0x200; // closed
2432 if (flags & 128) dwgFlags |= 0x100; // plinegen
2433 if (thickness != 0.0) dwgFlags |= 0x2;
2434 if (width != 0.0) dwgFlags |= 0x4;
2435 if (elevation != 0.0) dwgFlags |= 0x8;
2436 bool defaultExt = (extPoint.x == 0.0 && extPoint.y == 0.0 && extPoint.z == 1.0);
2437 if (!defaultExt) dwgFlags |= 0x1;
2438 // Detect per-vertex bulge / width data.
2439 bool anyBulge = false;
2440 bool anyWidth = false;
2441 bool anyVertexId = false;
2442 for (const auto& v : vertlist) {
2443 if (v && v->bulge != 0.0) anyBulge = true;
2444 if (v && (v->stawidth != 0.0 || v->endwidth != 0.0)) anyWidth = true;
2445 if (v && v->identifier != 0) anyVertexId = true;
2446 }
2447 if (anyBulge) dwgFlags |= 0x10;
2448 if (anyWidth) dwgFlags |= 0x20;
2449 if (version > DRW::AC1021 && anyVertexId) dwgFlags |= 0x400;
2450
2451 buf->putBitShort(dwgFlags);
2452 if (dwgFlags & 0x4) buf->putBitDouble(width);
2453 if (dwgFlags & 0x8) buf->putBitDouble(elevation);
2454 if (dwgFlags & 0x2) buf->putBitDouble(thickness);
2455 if (dwgFlags & 0x1) buf->putExtrusion(extPoint, /*b_R2000_style=*/false);
2456
2457 const std::int32_t numVerts = static_cast<std::int32_t>(vertlist.size());
2458 buf->putBitLong(numVerts);
2459 if (dwgFlags & 0x10) buf->putBitLong(numVerts); // bulgesnum
2460 if (version > DRW::AC1021 && (dwgFlags & 0x400)) {
2461 buf->putBitLong(numVerts); // vertexIdCount
2462 }
2463 if (dwgFlags & 0x20) buf->putBitLong(numVerts); // widthsnum
2464
2465 if (numVerts > 0) {
2466 // First vertex as 2RD. Subsequent vertices as 2DD relative to
2467 // the previous, with putDefaultDouble always emitting code 0b11
2468 // (full RD); the reader's getDefaultDouble returns the raw value.
2469 buf->putRawDouble(vertlist[0]->x);
2470 buf->putRawDouble(vertlist[0]->y);
2471 for (size_t i = 1; i < vertlist.size(); ++i) {
2472 buf->putDefaultDouble(vertlist[i-1]->x, vertlist[i]->x);
2473 buf->putDefaultDouble(vertlist[i-1]->y, vertlist[i]->y);
2474 }
2475 if (dwgFlags & 0x10) {
2476 for (const auto& v : vertlist)
2477 buf->putBitDouble(v->bulge);
2478 }
2479 if (version > DRW::AC1021 && (dwgFlags & 0x400)) {
2480 for (const auto& v : vertlist)
2481 buf->putBitLong(static_cast<std::int32_t>(v->identifier));
2482 }
2483 if (dwgFlags & 0x20) {
2484 for (const auto& v : vertlist) {
2485 buf->putBitDouble(v->stawidth);
2486 buf->putBitDouble(v->endwidth);
2487 }
2488 }
2489 }
2490
2491 return encodeDwgEntHandle(version, buf, handleBuf);
2492}
2493
2494bool DRW_Block::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2495 (void)bs;
2496 // BLOCK = 4, ENDBLK = 5 per DWG spec. isEnd controls which.
2497 oType = isEnd ? 5 : 4;
2498 if (!encodeDwgCommon(version, buf)) return false;
2499 if (!isEnd) {
2500 (strBuf ? strBuf : buf)->putVariableText(version, name);
2501 }
2502 if (version > DRW::AC1018) {
2503 buf->putBit(0); // unknown bit (R2007+: always 0 for our output)
2504 }
2505 return encodeDwgEntHandle(version, buf, handleBuf);
2506}
2507
2508bool DRW_Text::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2509 (void)bs;
2510 oType = 1; // TEXT class id — see dwgreader.cpp:1208
2511 if (!encodeDwgCommon(version, buf)) return false;
2512
2513 // R2000+ TEXT body — mirror of DRW_Text::parseDwg. We emit
2514 // data_flags=0 so the reader sees every optional field rather than
2515 // substituting defaults — keeps the encoder simple, costs ~30 bytes
2516 // per TEXT versus the most compressed form.
2517 buf->putRawChar8(0); // data_flags=0
2518 buf->putRawDouble(basePoint.z); // elevation RD
2519 buf->putRawDouble(basePoint.x); // insertion 2RD
2520 buf->putRawDouble(basePoint.y);
2521 buf->putDefaultDouble(basePoint.x, secPoint.x); // alignment 2DD
2522 buf->putDefaultDouble(basePoint.y, secPoint.y);
2523 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
2524 buf->putThickness(thickness, /*b_R2000_style=*/true);
2525 buf->putRawDouble(oblique); // oblique angle
2526 // Angle: struct holds degrees; on-disk format is radians. Reader
2527 // does `angle *= ARAD` (180/π) after read. Inverse: divide here.
2528 buf->putRawDouble(angle / ARAD57.29577951308232);
2529 buf->putRawDouble(height); // text height
2530 buf->putRawDouble(widthscale); // width factor
2531 (strBuf ? strBuf : buf)->putVariableText(version, text); // text string
2532 buf->putBitShort(static_cast<std::uint16_t>(textgen));
2533 buf->putBitShort(static_cast<std::uint16_t>(alignH));
2534 buf->putBitShort(static_cast<std::uint16_t>(alignV));
2535
2536 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
2537
2538 // styleH — hard pointer to STYLE table record. Default points at
2539 // the STANDARD textstyle (handle 0x13) if caller hasn't set one.
2540 dwgHandle sH;
2541 std::uint32_t sref = (styleH.ref == 0) ? 0x13 : styleH.ref;
2542 sH.code = 5; // hard pointer
2543 sH.ref = sref;
2544 sH.size = 0;
2545 if (sref != 0) {
2546 std::uint32_t t = sref;
2547 while (t != 0) { t >>= 8; ++sH.size; }
2548 }
2549 (handleBuf ? handleBuf : buf)->putHandle(sH);
2550 return true;
2551}
2552
2553bool DRW_Ellipse::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2554 (void)bs; (void)strBuf;
2555 oType = 35; // ELLIPSE class id — see dwgreader.cpp:1117
2556 if (!encodeDwgCommon(version, buf)) return false;
2557
2558 // Ellipse body — mirror of DRW_Ellipse::parseDwg.
2559 buf->put3BitDouble(basePoint); // center
2560 buf->put3BitDouble(secPoint); // major axis vector
2561 buf->put3BitDouble(extPoint); // extrusion
2562 buf->putBitDouble(ratio); // minor/major ratio
2563 buf->putBitDouble(staparam); // start parameter
2564 buf->putBitDouble(endparam); // end parameter
2565
2566 return encodeDwgEntHandle(version, buf, handleBuf);
2567}
2568
2569bool DRW_Arc::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
2570 (void)bs; (void)strBuf;
2571 oType = 17; // ARC class id — see dwgreader.cpp:1093
2572 if (!encodeDwgCommon(version, buf)) return false;
2573
2574 // Arc body — Circle body + 2 BD angles.
2575 buf->putBitDouble(basePoint.x);
2576 buf->putBitDouble(basePoint.y);
2577 buf->putBitDouble(basePoint.z);
2578 buf->putBitDouble(radious);
2579 buf->putThickness(thickness, /*b_R2000_style=*/true);
2580 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
2581 buf->putBitDouble(staangle);
2582 buf->putBitDouble(endangle);
2583
2584 return encodeDwgEntHandle(version, buf, handleBuf);
2585}
2586
2587bool DRW_Line::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
2588 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
2589 if (!ret)
2590 return ret;
2591 DRW_DBG("\n***************************** parsing line *********************************************\n")DRW_dbg::dbg("\n***************************** parsing line *********************************************\n"
)
;
2592
2593 if (version < DRW::AC1015) {//14-
2594 basePoint.x = buf->getBitDouble();
2595 basePoint.y = buf->getBitDouble();
2596 basePoint.z = buf->getBitDouble();
2597 secPoint.x = buf->getBitDouble();
2598 secPoint.y = buf->getBitDouble();
2599 secPoint.z = buf->getBitDouble();
2600 }
2601 if (version > DRW::AC1014) {//2000+
2602 bool zIsZero = buf->getBit(); //B
2603 basePoint.x = buf->getRawDouble();//RD
2604 secPoint.x = buf->getDefaultDouble(basePoint.x);//DD
2605 basePoint.y = buf->getRawDouble();//RD
2606 secPoint.y = buf->getDefaultDouble(basePoint.y);//DD
2607 if (!zIsZero) {
2608 basePoint.z = buf->getRawDouble();//RD
2609 secPoint.z = buf->getDefaultDouble(basePoint.z);//DD
2610 }
2611 }
2612 DRW_DBG("start point: ")DRW_dbg::dbg("start point: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
2613 DRW_DBG("\nend point: ")DRW_dbg::dbg("\nend point: "); DRW_DBGPT(secPoint.x, secPoint.y, secPoint.z)DRW_dbg::dbgPT(secPoint.x, secPoint.y, secPoint.z);
2614 thickness = buf->getThickness(version > DRW::AC1014);//BD
2615 DRW_DBG("\nthickness: ")DRW_dbg::dbg("\nthickness: "); DRW_DBG(thickness)DRW_dbg::dbg(thickness);
2616 extPoint = buf->getExtrusion(version > DRW::AC1014);
2617 DRW_DBG(", Extrusion: ")DRW_dbg::dbg(", Extrusion: "); DRW_DBGPT(extPoint.x, extPoint.y, extPoint.z)DRW_dbg::dbgPT(extPoint.x, extPoint.y, extPoint.z);DRW_DBG("\n")DRW_dbg::dbg("\n");
2618 ret = DRW_Entity::parseDwgEntHandle(version, buf);
2619 if (!ret)
2620 return ret;
2621 // RS crc; //RS */
2622 return buf->isGood();
2623}
2624
2625bool DRW_Ray::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
2626 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
2627 if (!ret)
2628 return ret;
2629 DRW_DBG("\n***************************** parsing ray/xline *********************************************\n")DRW_dbg::dbg("\n***************************** parsing ray/xline *********************************************\n"
)
;
2630 basePoint.x = buf->getBitDouble();
2631 basePoint.y = buf->getBitDouble();
2632 basePoint.z = buf->getBitDouble();
2633 secPoint.x = buf->getBitDouble();
2634 secPoint.y = buf->getBitDouble();
2635 secPoint.z = buf->getBitDouble();
2636 DRW_DBG("start point: ")DRW_dbg::dbg("start point: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
2637 DRW_DBG("\nvector: ")DRW_dbg::dbg("\nvector: "); DRW_DBGPT(secPoint.x, secPoint.y, secPoint.z)DRW_dbg::dbgPT(secPoint.x, secPoint.y, secPoint.z);
2638 ret = DRW_Entity::parseDwgEntHandle(version, buf);
2639 if (!ret)
2640 return ret;
2641 // RS crc; //RS */
2642 return buf->isGood();
2643}
2644
2645void DRW_Circle::applyExtrusion(){
2646 if (haveExtrusion) {
2647 //NOTE: Commenting these out causes the the arcs being tested to be located
2648 //on the other side of the y axis (all x dimensions are negated).
2649 calculateAxis(extPoint);
2650 extrudePoint(extPoint, &basePoint);
2651 }
2652}
2653
2654bool DRW_Circle::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
2655 switch (code) {
2656 case 40:
2657 radious = reader->getDouble();
2658 break;
2659 default:
2660 return DRW_Point::parseCode(code, reader);
2661 }
2662
2663 return true;
2664}
2665
2666bool DRW_Circle::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
2667 bool ret = DRW_Entity::parseDwg(version, buf, nullptr, bs);
2668 if (!ret)
2669 return ret;
2670 DRW_DBG("\n***************************** parsing circle *********************************************\n")DRW_dbg::dbg("\n***************************** parsing circle *********************************************\n"
)
;
2671
2672 basePoint.x = buf->getBitDouble();
2673 basePoint.y = buf->getBitDouble();
2674 basePoint.z = buf->getBitDouble();
2675 DRW_DBG("center: ")DRW_dbg::dbg("center: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
2676 radious = buf->getBitDouble();
2677 DRW_DBG("\nradius: ")DRW_dbg::dbg("\nradius: "); DRW_DBG(radious)DRW_dbg::dbg(radious);
2678
2679 thickness = buf->getThickness(version > DRW::AC1014);
2680 DRW_DBG(" thickness: ")DRW_dbg::dbg(" thickness: "); DRW_DBG(thickness)DRW_dbg::dbg(thickness);
2681 extPoint = buf->getExtrusion(version > DRW::AC1014);
2682 DRW_DBG("\nextrusion: ")DRW_dbg::dbg("\nextrusion: "); DRW_DBGPT(extPoint.x, extPoint.y, extPoint.z)DRW_dbg::dbgPT(extPoint.x, extPoint.y, extPoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
2683
2684 ret = DRW_Entity::parseDwgEntHandle(version, buf);
2685 if (!ret)
2686 return ret;
2687 // RS crc; //RS */
2688 return buf->isGood();
2689}
2690
2691void DRW_Arc::applyExtrusion(){
2692 DRW_Circle::applyExtrusion();
2693
2694 if(haveExtrusion){
2695 // If the extrusion vector has a z value less than 0, the angles for the arc
2696 // have to be mirrored since DXF files use the right hand rule.
2697 // Note that the following code only handles the special case where there is a 2D
2698 // drawing with the z axis heading into the paper (or rather screen). An arbitrary
2699 // extrusion axis (with x and y values greater than 1/64) may still have issues.
2700 if (fabs(extPoint.x) < 0.015625 && fabs(extPoint.y) < 0.015625 && extPoint.z < 0.0) {
2701 staangle=M_PI3.14159265358979323846-staangle;
2702 endangle=M_PI3.14159265358979323846-endangle;
2703
2704 double temp = staangle;
2705 staangle=endangle;
2706 endangle=temp;
2707 }
2708 }
2709}
2710
2711bool DRW_Arc::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
2712 switch (code) {
2713 case 50:
2714 staangle = reader->getDouble()/ ARAD57.29577951308232;
2715 break;
2716 case 51:
2717 endangle = reader->getDouble()/ ARAD57.29577951308232;
2718 break;
2719 default:
2720 return DRW_Circle::parseCode(code, reader);
2721 }
2722
2723 return true;
2724}
2725
2726bool DRW_Arc::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
2727 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
2728 if (!ret)
2729 return ret;
2730 DRW_DBG("\n***************************** parsing circle arc *********************************************\n")DRW_dbg::dbg("\n***************************** parsing circle arc *********************************************\n"
)
;
2731
2732 basePoint.x = buf->getBitDouble();
2733 basePoint.y = buf->getBitDouble();
2734 basePoint.z = buf->getBitDouble();
2735 DRW_DBG("center point: ")DRW_dbg::dbg("center point: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
2736
2737 radious = buf->getBitDouble();
2738 DRW_DBG("\nradius: ")DRW_dbg::dbg("\nradius: "); DRW_DBG(radious)DRW_dbg::dbg(radious);
2739 thickness = buf->getThickness(version > DRW::AC1014);
2740 DRW_DBG(" thickness: ")DRW_dbg::dbg(" thickness: "); DRW_DBG(thickness)DRW_dbg::dbg(thickness);
2741 extPoint = buf->getExtrusion(version > DRW::AC1014);
2742 DRW_DBG("\nextrusion: ")DRW_dbg::dbg("\nextrusion: "); DRW_DBGPT(extPoint.x, extPoint.y, extPoint.z)DRW_dbg::dbgPT(extPoint.x, extPoint.y, extPoint.z);
2743 staangle = buf->getBitDouble();
2744 DRW_DBG("\nstart angle: ")DRW_dbg::dbg("\nstart angle: "); DRW_DBG(staangle)DRW_dbg::dbg(staangle);
2745 endangle = buf->getBitDouble();
2746 DRW_DBG(" end angle: ")DRW_dbg::dbg(" end angle: "); DRW_DBG(endangle)DRW_dbg::dbg(endangle); DRW_DBG("\n")DRW_dbg::dbg("\n");
2747 ret = DRW_Entity::parseDwgEntHandle(version, buf);
2748 if (!ret)
2749 return ret;
2750 return buf->isGood();
2751}
2752
2753bool DRW_Ellipse::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
2754 switch (code) {
2755 case 40:
2756 ratio = reader->getDouble();
2757 break;
2758 case 41:
2759 staparam = reader->getDouble();
2760 break;
2761 case 42:
2762 endparam = reader->getDouble();
2763 break;
2764 default:
2765 return DRW_Line::parseCode(code, reader);
2766 }
2767
2768 return true;
2769}
2770
2771void DRW_Ellipse::applyExtrusion(){
2772 if (haveExtrusion) {
2773 calculateAxis(extPoint);
2774 extrudePoint(extPoint, &basePoint);
2775 extrudePoint(extPoint, &secPoint);
2776 double intialparam = staparam;
2777 if (extPoint.z < 0.){
2778 staparam = M_PIx26.283185307179586 - endparam;
2779 endparam = M_PIx26.283185307179586 - intialparam;
2780 }
2781 }
2782}
2783
2784//if ratio > 1 minor axis are greather than major axis, correct it
2785void DRW_Ellipse::correctAxis(){
2786 bool complete = false;
2787 if (staparam == endparam) {
2788 staparam = 0.0;
2789 endparam = M_PIx26.283185307179586; //2*M_PI;
2790 complete = true;
2791 }
2792 if (ratio > 1){
2793 if ( fabs(endparam - staparam - M_PIx26.283185307179586) < 1.0e-10)
2794 complete = true;
2795 double incX = secPoint.x;
2796 secPoint.x = -(secPoint.y * ratio);
2797 secPoint.y = incX*ratio;
2798 ratio = 1/ratio;
2799 if (!complete){
2800 if (staparam < M_PI_21.57079632679489661923)
2801 staparam += M_PI3.14159265358979323846 *2;
2802 if (endparam < M_PI_21.57079632679489661923)
2803 endparam += M_PI3.14159265358979323846 *2;
2804 endparam -= M_PI_21.57079632679489661923;
2805 staparam -= M_PI_21.57079632679489661923;
2806 }
2807 }
2808}
2809
2810bool DRW_Ellipse::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
2811 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
2812 if (!ret)
2813 return ret;
2814 DRW_DBG("\n***************************** parsing ellipse *********************************************\n")DRW_dbg::dbg("\n***************************** parsing ellipse *********************************************\n"
)
;
2815
2816 basePoint =buf->get3BitDouble();
2817 DRW_DBG("center: ")DRW_dbg::dbg("center: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
2818 secPoint =buf->get3BitDouble();
2819 DRW_DBG(", axis: ")DRW_dbg::dbg(", axis: "); DRW_DBGPT(secPoint.x, secPoint.y, secPoint.z)DRW_dbg::dbgPT(secPoint.x, secPoint.y, secPoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
2820 extPoint =buf->get3BitDouble();
2821 DRW_DBG("Extrusion: ")DRW_dbg::dbg("Extrusion: "); DRW_DBGPT(extPoint.x, extPoint.y, extPoint.z)DRW_dbg::dbgPT(extPoint.x, extPoint.y, extPoint.z);
2822 ratio = buf->getBitDouble();//BD
2823 DRW_DBG("\nratio: ")DRW_dbg::dbg("\nratio: "); DRW_DBG(ratio)DRW_dbg::dbg(ratio);
2824 staparam = buf->getBitDouble();//BD
2825 DRW_DBG(" start param: ")DRW_dbg::dbg(" start param: "); DRW_DBG(staparam)DRW_dbg::dbg(staparam);
2826 endparam = buf->getBitDouble();//BD
2827 DRW_DBG(" end param: ")DRW_dbg::dbg(" end param: "); DRW_DBG(endparam)DRW_dbg::dbg(endparam); DRW_DBG("\n")DRW_dbg::dbg("\n");
2828
2829 ret = DRW_Entity::parseDwgEntHandle(version, buf);
2830 if (!ret)
2831 return ret;
2832 // RS crc; //RS */
2833 return buf->isGood();
2834}
2835
2836//parts are the number of vertex to split polyline, default 128
2837void DRW_Ellipse::toPolyline(DRW_Polyline *pol, int parts){
2838 double radMajor, radMinor, cosRot, sinRot, incAngle, curAngle;
2839 double cosCurr, sinCurr;
2840 radMajor = hypot(secPoint.x, secPoint.y);
2841 radMinor = radMajor*ratio;
2842 //calculate sin & cos of included angle
2843 incAngle = atan2(secPoint.y, secPoint.x);
2844 cosRot = cos(incAngle);
2845 sinRot = sin(incAngle);
2846 incAngle = M_PIx26.283185307179586 / parts;
2847 curAngle = staparam;
2848 int i = static_cast<int>(curAngle / incAngle);
2849 do {
2850 if (curAngle > endparam) {
2851 curAngle = endparam;
2852 i = parts+2;
2853 }
2854 cosCurr = cos(curAngle);
2855 sinCurr = sin(curAngle);
2856 double x = basePoint.x + (cosCurr*cosRot*radMajor) - (sinCurr*sinRot*radMinor);
2857 double y = basePoint.y + (cosCurr*sinRot*radMajor) + (sinCurr*cosRot*radMinor);
2858 pol->addVertex( DRW_Vertex(x, y, 0.0, 0.0));
2859 curAngle = (++i)*incAngle;
2860 } while (i<parts);
2861 if ( fabs(endparam - staparam - M_PIx26.283185307179586) < 1.0e-10){
2862 pol->flags = 1;
2863 }
2864 pol->layer = this->layer;
2865 pol->lineType = this->lineType;
2866 pol->color = this->color;
2867 pol->lWeight = this->lWeight;
2868 pol->extPoint = this->extPoint;
2869}
2870
2871void DRW_Trace::applyExtrusion(){
2872 if (haveExtrusion) {
2873 calculateAxis(extPoint);
2874 extrudePoint(extPoint, &basePoint);
2875 extrudePoint(extPoint, &secPoint);
2876 extrudePoint(extPoint, &thirdPoint);
2877 extrudePoint(extPoint, &fourPoint);
2878 }
2879}
2880
2881bool DRW_Trace::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
2882 switch (code) {
2883 case 12:
2884 thirdPoint.x = reader->getDouble();
2885 break;
2886 case 22:
2887 thirdPoint.y = reader->getDouble();
2888 break;
2889 case 32:
2890 thirdPoint.z = reader->getDouble();
2891 break;
2892 case 13:
2893 fourPoint.x = reader->getDouble();
2894 break;
2895 case 23:
2896 fourPoint.y = reader->getDouble();
2897 break;
2898 case 33:
2899 fourPoint.z = reader->getDouble();
2900 break;
2901 default:
2902 return DRW_Line::parseCode(code, reader);
2903 }
2904
2905 return true;
2906}
2907
2908bool DRW_Trace::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
2909 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
2910 if (!ret)
2911 return ret;
2912 DRW_DBG("\n***************************** parsing Trace *********************************************\n")DRW_dbg::dbg("\n***************************** parsing Trace *********************************************\n"
)
;
2913
2914 thickness = buf->getThickness(version>DRW::AC1014);
2915 basePoint.z = buf->getBitDouble();
2916 basePoint.x = buf->getRawDouble();
2917 basePoint.y = buf->getRawDouble();
2918 secPoint.x = buf->getRawDouble();
2919 secPoint.y = buf->getRawDouble();
2920 secPoint.z = basePoint.z;
2921 thirdPoint.x = buf->getRawDouble();
2922 thirdPoint.y = buf->getRawDouble();
2923 thirdPoint.z = basePoint.z;
2924 fourPoint.x = buf->getRawDouble();
2925 fourPoint.y = buf->getRawDouble();
2926 fourPoint.z = basePoint.z;
2927 extPoint = buf->getExtrusion(version>DRW::AC1014);
2928
2929 DRW_DBG(" - base ")DRW_dbg::dbg(" - base "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
2930 DRW_DBG("\n - sec ")DRW_dbg::dbg("\n - sec "); DRW_DBGPT(secPoint.x, secPoint.y, secPoint.z)DRW_dbg::dbgPT(secPoint.x, secPoint.y, secPoint.z);
2931 DRW_DBG("\n - third ")DRW_dbg::dbg("\n - third "); DRW_DBGPT(thirdPoint.x, thirdPoint.y, thirdPoint.z)DRW_dbg::dbgPT(thirdPoint.x, thirdPoint.y, thirdPoint.z);
2932 DRW_DBG("\n - fourth ")DRW_dbg::dbg("\n - fourth "); DRW_DBGPT(fourPoint.x, fourPoint.y, fourPoint.z)DRW_dbg::dbgPT(fourPoint.x, fourPoint.y, fourPoint.z);
2933 DRW_DBG("\n - extrusion: ")DRW_dbg::dbg("\n - extrusion: "); DRW_DBGPT(extPoint.x, extPoint.y, extPoint.z)DRW_dbg::dbgPT(extPoint.x, extPoint.y, extPoint.z);
2934 DRW_DBG("\n - thickness: ")DRW_dbg::dbg("\n - thickness: "); DRW_DBG(thickness)DRW_dbg::dbg(thickness); DRW_DBG("\n")DRW_dbg::dbg("\n");
2935
2936 /* Common Entity Handle Data */
2937 ret = DRW_Entity::parseDwgEntHandle(version, buf);
2938 if (!ret)
2939 return ret;
2940
2941 /* CRC X --- */
2942 return buf->isGood();
2943}
2944
2945bool DRW_Solid::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
2946 DRW_DBG("\n***************************** parsing Solid *********************************************\n")DRW_dbg::dbg("\n***************************** parsing Solid *********************************************\n"
)
;
2947 return DRW_Trace::parseDwg(v, buf, bs);
2948}
2949
2950bool DRW_3Dface::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
2951 switch (code) {
2952 case 70:
2953 invisibleflag = reader->getInt32();
2954 break;
2955 default:
2956 return DRW_Trace::parseCode(code, reader);
2957 }
2958
2959 return true;
2960}
2961
2962bool DRW_3Dface::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
2963 bool ret = DRW_Entity::parseDwg(v, buf, NULL__null, bs);
2964 if (!ret)
2965 return ret;
2966 DRW_DBG("\n***************************** parsing 3Dface *********************************************\n")DRW_dbg::dbg("\n***************************** parsing 3Dface *********************************************\n"
)
;
2967
2968 if ( v < DRW::AC1015 ) {// R13 & R14
2969 basePoint.x = buf->getBitDouble();
2970 basePoint.y = buf->getBitDouble();
2971 basePoint.z = buf->getBitDouble();
2972 secPoint.x = buf->getBitDouble();
2973 secPoint.y = buf->getBitDouble();
2974 secPoint.z = buf->getBitDouble();
2975 thirdPoint.x = buf->getBitDouble();
2976 thirdPoint.y = buf->getBitDouble();
2977 thirdPoint.z = buf->getBitDouble();
2978 fourPoint.x = buf->getBitDouble();
2979 fourPoint.y = buf->getBitDouble();
2980 fourPoint.z = buf->getBitDouble();
2981 invisibleflag = buf->getBitShort();
2982 } else { // 2000+
2983 bool has_no_flag = buf->getBit();
2984 bool z_is_zero = buf->getBit();
2985 basePoint.x = buf->getRawDouble();
2986 basePoint.y = buf->getRawDouble();
2987 basePoint.z = z_is_zero ? 0.0 : buf->getRawDouble();
2988 secPoint.x = buf->getDefaultDouble(basePoint.x);
2989 secPoint.y = buf->getDefaultDouble(basePoint.y);
2990 secPoint.z = buf->getDefaultDouble(basePoint.z);
2991 thirdPoint.x = buf->getDefaultDouble(secPoint.x);
2992 thirdPoint.y = buf->getDefaultDouble(secPoint.y);
2993 thirdPoint.z = buf->getDefaultDouble(secPoint.z);
2994 fourPoint.x = buf->getDefaultDouble(thirdPoint.x);
2995 fourPoint.y = buf->getDefaultDouble(thirdPoint.y);
2996 fourPoint.z = buf->getDefaultDouble(thirdPoint.z);
2997 invisibleflag = has_no_flag ? (int)NoEdge : buf->getBitShort();
2998 }
2999 drw_assert(invisibleflag>=NoEdge);
3000 drw_assert(invisibleflag<=AllEdges);
3001
3002 DRW_DBG(" - base ")DRW_dbg::dbg(" - base "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
3003 DRW_DBG(" - sec ")DRW_dbg::dbg(" - sec "); DRW_DBGPT(secPoint.x, secPoint.y, secPoint.z)DRW_dbg::dbgPT(secPoint.x, secPoint.y, secPoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
3004 DRW_DBG(" - third ")DRW_dbg::dbg(" - third "); DRW_DBGPT(thirdPoint.x, thirdPoint.y, thirdPoint.z)DRW_dbg::dbgPT(thirdPoint.x, thirdPoint.y, thirdPoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
3005 DRW_DBG(" - fourth ")DRW_dbg::dbg(" - fourth "); DRW_DBGPT(fourPoint.x, fourPoint.y, fourPoint.z)DRW_dbg::dbgPT(fourPoint.x, fourPoint.y, fourPoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
3006 DRW_DBG(" - Invisibility mask: ")DRW_dbg::dbg(" - Invisibility mask: "); DRW_DBG(invisibleflag)DRW_dbg::dbg(invisibleflag); DRW_DBG("\n")DRW_dbg::dbg("\n");
3007
3008 /* Common Entity Handle Data */
3009 ret = DRW_Entity::parseDwgEntHandle(v, buf);
3010 if (!ret)
3011 return ret;
3012 return buf->isGood();
3013}
3014
3015bool DRW_ModelerGeometry::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
3016 m_bodyBitSize = bs;
3017 bool ret = DRW_Entity::parseDwg(v, buf, nullptr, bs);
3018 if (!ret)
3019 return ret;
3020 DRW_DBG("\n***************************** parsing modeler geometry ******************\n")DRW_dbg::dbg("\n***************************** parsing modeler geometry ******************\n"
)
;
3021
3022 m_isEmpty = buf->getBit() != 0;
3023 m_hasModelerData = !m_isEmpty;
3024 m_modelerDataUnknownBit = buf->getBit() != 0;
3025 if (m_hasModelerData)
3026 m_modelerVersion = buf->getBitShort();
3027
3028 ret = DRW_Entity::parseDwgEntHandle(v, buf);
3029 if (eType == DRW::E3DSOLID && v > DRW::AC1018 && buf->numRemainingBytes() > 2) {
3030 dwgHandle historyH = buf->getHandle();
3031 m_historyHandle = historyH.ref;
3032 DRW_DBG(" 3DSOLID history Handle: ")DRW_dbg::dbg(" 3DSOLID history Handle: ");
3033 DRW_DBGHL(historyH.code, historyH.size, historyH.ref)DRW_dbg::dbgHL(historyH.code, historyH.size, historyH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3034 }
3035
3036 return ret;
3037}
3038
3039bool DRW_ModelerGeometry::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
3040 switch (code) {
3041 case 1:
3042 case 3:
3043 appendTextBytes(m_rawBytes, reader->getString());
3044 break;
3045 case 70:
3046 m_modelerVersion = static_cast<std::uint16_t>(reader->getInt32());
3047 break;
3048 case 350:
3049 case 360:
3050 m_historyHandle = static_cast<std::uint32_t>(reader->getHandleString());
3051 break;
3052 case 310:
3053 {
3054 std::vector<std::uint8_t> decoded;
3055 if (!decodeHexBytes(reader->getString(), decoded))
3056 return false;
3057 appendBytes(m_rawBytes, decoded);
3058 }
3059 break;
3060 default:
3061 return DRW_Entity::parseCode(code, reader);
3062 }
3063 return true;
3064}
3065
3066// DRW_Mesh::parseDwg — AcDbSubDMesh, field order per libreDWG dwg2.spec:2523
3067// (DWG bitstream order, NOT the DXF group-code order). R2010+ only in practice
3068// (the custom-class dispatch only fires when classesmap names "MESH").
3069bool DRW_Mesh::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
3070 bool ret = DRW_Entity::parseDwg(v, buf, nullptr, bs);
3071 if (!ret)
3072 return ret;
3073 DRW_DBG("\n***************************** parsing MESH (AcDbSubDMesh) *****************\n")DRW_dbg::dbg("\n***************************** parsing MESH (AcDbSubDMesh) *****************\n"
)
;
3074
3075 // Loose OOM/corruption guard: a count can't exceed the bits left in the
3076 // object (each item is >= 1 bit; crease/face values are bit-packed and may be
3077 // far below 1 byte each, so a per-byte bound would falsely reject valid data).
3078 auto sane = [&](std::int32_t n) {
3079 return n >= 0
3080 && static_cast<std::int64_t>(n)
3081 <= static_cast<std::int64_t>(buf->numRemainingBytes()) * 8 + 8;
3082 };
3083
3084 version = buf->getBitShort(); // BS dlevel (71)
3085 blendCrease = buf->getBit() != 0; // B is_watertight (72)
3086
3087 const std::int32_t nSubdiv = buf->getBitLong(); // BL num_subdiv_vertex (91)
3088 if (!sane(nSubdiv)) return false;
3089 subdivisionLevel = nSubdiv;
3090 subdivVertices.reserve(static_cast<size_t>(nSubdiv));
3091 for (std::int32_t i = 0; i < nSubdiv && buf->isGood(); ++i)
3092 subdivVertices.push_back(buf->get3BitDouble());
3093
3094 const std::int32_t nVert = buf->getBitLong(); // BL num_vertex (92)
3095 if (!sane(nVert)) return false;
3096 vertices.reserve(static_cast<size_t>(nVert));
3097 for (std::int32_t i = 0; i < nVert && buf->isGood(); ++i)
3098 vertices.push_back(buf->get3BitDouble());
3099
3100 // faces (93) is a FLAT BL stream of length num_faces; each face is
3101 // [count, idx0, idx1, ...]. num_faces is the stream length, not the polygon
3102 // count — group on the fly.
3103 std::int32_t remaining = buf->getBitLong(); // BL num_faces (93)
3104 if (!sane(remaining)) return false;
3105 while (remaining > 0 && buf->isGood()) {
3106 const std::int32_t cnt = buf->getBitLong();
3107 --remaining;
3108 if (cnt < 0 || cnt > remaining)
3109 break; // corrupt face run
3110 std::vector<std::int32_t> face;
3111 face.reserve(static_cast<size_t>(cnt));
3112 for (std::int32_t j = 0; j < cnt && buf->isGood(); ++j) {
3113 face.push_back(buf->getBitLong());
3114 --remaining;
3115 }
3116 faces.push_back(std::move(face));
3117 }
3118
3119 const std::int32_t nEdges = buf->getBitLong(); // BL num_edges (94)
3120 if (!sane(nEdges)) return false;
3121 edges.reserve(static_cast<size_t>(nEdges));
3122 for (std::int32_t i = 0; i < nEdges && buf->isGood(); ++i) {
3123 const std::int32_t from = buf->getBitLong();
3124 const std::int32_t to = buf->getBitLong();
3125 edges.emplace_back(from, to);
3126 }
3127
3128 const std::int32_t nCrease = buf->getBitLong(); // BL num_crease (95)
3129 if (!sane(nCrease)) return false;
3130 creases.reserve(static_cast<size_t>(nCrease));
3131 for (std::int32_t i = 0; i < nCrease && buf->isGood(); ++i)
3132 creases.push_back(buf->getBitDouble());
3133
3134 buf->getBit(); // unknown_b1
3135 buf->getBit(); // unknown_b2
3136
3137 ret = DRW_Entity::parseDwgEntHandle(v, buf);
3138 return ret;
3139}
3140
3141// DRW_Mesh::parseCode — DXF read (codes 71/72/91/92/10·20·30/93/90/94/95/140).
3142// The 90 stream is shared by faces (after 93) and edges (after 94); m_dxfState
3143// sequences which one is being filled (mirrors DRW_Image::parseCode's stateful
3144// 91/14/24 WIPEOUT-vertex accumulation).
3145bool DRW_Mesh::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
3146 switch (code) {
3147 case 71: version = reader->getInt32(); return true;
3148 case 72: blendCrease = reader->getInt32() != 0; return true;
3149 case 91: subdivisionLevel = reader->getInt32(); return true;
3150 case 92: /* base-vertex count */ vertices.reserve(reader->getInt32()); return true;
3151 case 10: vertices.emplace_back(); vertices.back().x = reader->getDouble(); return true;
3152 case 20: if (!vertices.empty()) vertices.back().y = reader->getDouble(); return true;
3153 case 30: if (!vertices.empty()) vertices.back().z = reader->getDouble(); return true;
3154 case 93: m_dxfState = 93; m_dxfPending = 0; return true; // start face stream
3155 case 94: m_dxfState = 94; m_dxfEdgeFrom = -1; (void)reader->getInt32(); return true; // edge count
3156 case 90: {
3157 const std::int32_t val = reader->getInt32();
3158 if (m_dxfState == 93) {
3159 // flat face stream: when no face is in progress, val is the next
3160 // face's vertex count; otherwise val is a vertex index.
3161 if (m_dxfPending == 0) {
3162 faces.emplace_back();
3163 m_dxfPending = (val > 0) ? val : 0;
3164 } else {
3165 if (!faces.empty()) faces.back().push_back(val);
3166 --m_dxfPending;
3167 }
3168 } else if (m_dxfState == 94) {
3169 if (m_dxfEdgeFrom < 0) m_dxfEdgeFrom = val;
3170 else { edges.emplace_back(m_dxfEdgeFrom, val); m_dxfEdgeFrom = -1; }
3171 }
3172 return true;
3173 }
3174 case 95: m_dxfState = 95; creases.reserve(static_cast<size_t>(std::max(0, reader->getInt32()))); return true;
3175 case 140: creases.push_back(reader->getDouble()); return true;
3176 default:
3177 return DRW_Entity::parseCode(code, reader);
3178 }
3179}
3180
3181bool DRW_Mesh::encodeDwg(DRW::Version dwgVersion, dwgBufferW *buf, std::uint32_t bs,
3182 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
3183 (void)bs; (void)strBuf;
3184 oType = kDwgClassNum;
3185 if (!encodeDwgCommon(dwgVersion, buf)) return false;
3186
3187 buf->putBitShort(version);
3188 buf->putBit(blendCrease ? 1 : 0);
3189
3190 // DWG stores a subdiv-vertex vector in the slot that DXF exposes as group
3191 // 91 subdivision level. Preserve the vector exactly; DXF write keeps the
3192 // public subdivisionLevel field.
3193 buf->putBitLong(static_cast<std::int32_t>(subdivVertices.size()));
3194 for (const DRW_Coord& vertex : subdivVertices)
3195 buf->put3BitDouble(vertex);
3196
3197 buf->putBitLong(static_cast<std::int32_t>(vertices.size()));
3198 for (const DRW_Coord& vertex : vertices)
3199 buf->put3BitDouble(vertex);
3200
3201 std::int32_t faceStreamCount = 0;
3202 for (const auto& face : faces)
3203 faceStreamCount += static_cast<std::int32_t>(face.size() + 1);
3204 buf->putBitLong(faceStreamCount);
3205 for (const auto& face : faces) {
3206 buf->putBitLong(static_cast<std::int32_t>(face.size()));
3207 for (std::int32_t index : face)
3208 buf->putBitLong(index);
3209 }
3210
3211 buf->putBitLong(static_cast<std::int32_t>(edges.size()));
3212 for (const auto& edge : edges) {
3213 buf->putBitLong(edge.first);
3214 buf->putBitLong(edge.second);
3215 }
3216
3217 buf->putBitLong(static_cast<std::int32_t>(creases.size()));
3218 for (double crease : creases)
3219 buf->putBitDouble(crease);
3220
3221 buf->putBit(0);
3222 buf->putBit(0);
3223
3224 return encodeDwgEntHandle(dwgVersion, buf, handleBuf);
3225}
3226
3227bool DRW_Shape::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
3228 m_bodyBitSize = bs;
3229 bool ret = DRW_Entity::parseDwg(v, buf, nullptr, bs);
3230 if (!ret)
3231 return ret;
3232 DRW_DBG("\n***************************** parsing SHAPE *****************************\n")DRW_dbg::dbg("\n***************************** parsing SHAPE *****************************\n"
)
;
3233
3234 m_insertionPoint = buf->get3BitDouble();
3235 m_scale = buf->getBitDouble();
3236 m_rotation = buf->getBitDouble();
3237 m_widthFactor = buf->getBitDouble();
3238 m_oblique = buf->getBitDouble();
3239 m_thickness = buf->getBitDouble();
3240 m_shapeIndex = buf->getBitShort();
3241 m_extrusion = buf->get3BitDouble();
3242
3243 ret = DRW_Entity::parseDwgEntHandle(v, buf);
3244 if (ret && buf->numRemainingBytes() > 2) {
3245 dwgHandle shapeFileH = buf->getHandle();
3246 m_shapeFileHandle = shapeFileH.ref;
3247 DRW_DBG(" SHAPEFILE Handle: ")DRW_dbg::dbg(" SHAPEFILE Handle: ");
3248 DRW_DBGHL(shapeFileH.code, shapeFileH.size, shapeFileH.ref)DRW_dbg::dbgHL(shapeFileH.code, shapeFileH.size, shapeFileH.ref
)
;
3249 DRW_DBG("\n")DRW_dbg::dbg("\n");
3250 }
3251 return ret && buf->isGood();
3252}
3253
3254// Phase 6.1: SHAPE encoder (fixed oType 33). Exact inverse of parseDwg above.
3255// Without this override a SHAPE would encode as a LINE (default DRW_Entity).
3256bool DRW_Shape::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
3257 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
3258 (void)bs; (void)strBuf;
3259 oType = 33; // SHAPE class id — see dwgreader.cpp case 33
3260 if (!encodeDwgCommon(version, buf)) return false;
3261
3262 buf->put3BitDouble(m_insertionPoint);
3263 buf->putBitDouble(m_scale);
3264 buf->putBitDouble(m_rotation);
3265 buf->putBitDouble(m_widthFactor);
3266 buf->putBitDouble(m_oblique);
3267 buf->putBitDouble(m_thickness);
3268 buf->putBitShort(m_shapeIndex);
3269 buf->put3BitDouble(m_extrusion);
3270
3271 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
3272
3273 // Trailing SHAPEFILE style hard pointer (code 5), byte-count-sized.
3274 dwgHandle sH;
3275 sH.code = 5;
3276 sH.ref = m_shapeFileHandle;
3277 sH.size = 0;
3278 if (m_shapeFileHandle != 0) {
3279 std::uint32_t t = m_shapeFileHandle;
3280 while (t != 0) { t >>= 8; ++sH.size; }
3281 } else {
3282 sH.code = 0; // null handle
3283 }
3284 (handleBuf ? handleBuf : buf)->putHandle(sH);
3285 return true;
3286}
3287
3288bool DRW_Ole2Frame::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
3289 m_bodyBitSize = bs;
3290 bool ret = DRW_Entity::parseDwg(v, buf, nullptr, bs);
3291 if (!ret)
3292 return ret;
3293 DRW_DBG("\n***************************** parsing OLE2FRAME ************************\n")DRW_dbg::dbg("\n***************************** parsing OLE2FRAME ************************\n"
)
;
3294
3295 m_flags = buf->getBitShort();
3296 if (v > DRW::AC1014)
3297 m_mode = buf->getBitShort();
3298 m_declaredPayloadLength = buf->getBitLong();
3299 m_payloadStartBit = currentDwgBit(buf);
3300 const std::uint64_t currentBit = currentDwgBit(buf);
3301 const std::uint64_t bodyRemainingBits =
3302 (v > DRW::AC1018 && objSize > currentBit)
3303 ? objSize - currentBit
3304 : static_cast<std::uint64_t>(buf->numRemainingBytes()) * 8u;
3305 const std::uint32_t remainingBytes =
3306 static_cast<std::uint32_t>(std::min<std::uint64_t>(
3307 bodyRemainingBits / 8u,
3308 static_cast<std::uint64_t>(std::numeric_limits<std::uint32_t>::max())));
3309 if (m_declaredPayloadLength > kMaxOlePayloadBytes) {
3310 m_payloadTooLarge = true;
3311 return false;
3312 }
3313 if (m_declaredPayloadLength > remainingBytes) { // remainingBytes is already uint32
3314 m_payloadTruncated = true;
3315 m_payloadByteCount = remainingBytes;
3316 return false;
3317 }
3318
3319 m_payloadPresent = m_declaredPayloadLength > 0;
3320 m_payloadByteCount = m_declaredPayloadLength;
3321 // Phase 6.2: capture the opaque payload bytes (was skipped via moveBitPos)
3322 // so the OLE2FRAME encoder can re-emit them byte-for-byte.
3323 if (m_declaredPayloadLength > 0) {
3324 m_payloadBytes.resize(m_declaredPayloadLength);
3325 if (!buf->getBytes(m_payloadBytes.data(), m_declaredPayloadLength)) {
3326 m_payloadTruncated = true;
3327 m_payloadBytes.clear();
3328 return false;
3329 }
3330 }
3331
3332 if (v > DRW::AC1014 && buf->numRemainingBytes() > 0) {
3333 m_hasR2000TrailingByte = true;
3334 m_r2000TrailingByte = buf->getRawChar8();
3335 }
3336
3337 // Decode the frame rectangle (DXF 10/11) from the OLE header. AutoCAD/ODA do
3338 // NOT store pt1/pt2 as DWG fields; they live in the first ~0x80 bytes of the
3339 // payload as raw little-endian doubles. (libredwg's dwg_decode_ole2 is a stub
3340 // that hardcodes one sample file's corners.) Layout reverse-engineered and
3341 // validated on TS1 + Extruder2: byte 0x00 == 0x80 marker; upper-left @0x02,
3342 // lower-right @0x32, 3 doubles each. Guarded so a non-finite/short payload
3343 // simply leaves pt1/pt2 at the origin (payload still preserved).
3344 if (m_payloadBytes.size() >= 0x4a && m_payloadBytes[0] == 0x80) {
3345 auto rd = [&](std::size_t off) {
3346 double d = 0.0;
3347 std::memcpy(&d, m_payloadBytes.data() + off, sizeof(double));
3348 return d;
3349 };
3350 DRW_Coord ul(rd(0x02), rd(0x0a), rd(0x12));
3351 DRW_Coord lr(rd(0x32), rd(0x3a), rd(0x42));
3352 if (std::isfinite(ul.x) && std::isfinite(ul.y) && std::isfinite(ul.z)
3353 && std::isfinite(lr.x) && std::isfinite(lr.y) && std::isfinite(lr.z)) {
3354 m_pt1 = ul;
3355 m_pt2 = lr;
3356 }
3357 }
3358
3359 ret = DRW_Entity::parseDwgEntHandle(v, buf);
3360 return ret && buf->isGood();
3361}
3362
3363// Phase 6.2: OLE2FRAME encoder (fixed oType 74). Inverse of parseDwg, emitting
3364// the captured opaque payload byte-for-byte. Without this override an OLE2FRAME
3365// would encode as a LINE.
3366bool DRW_Ole2Frame::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
3367 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
3368 (void)bs; (void)strBuf;
3369 oType = 74; // OLE2FRAME class id — see dwgreader.cpp case 74
3370 if (!encodeDwgCommon(version, buf)) return false;
3371
3372 buf->putBitShort(m_flags);
3373 if (version > DRW::AC1014)
3374 buf->putBitShort(m_mode);
3375 // Emit the actual captured length so the reader's data_size matches the
3376 // bytes that follow (avoids a declared-vs-actual mismatch on re-read).
3377 const std::uint32_t payloadLen = static_cast<std::uint32_t>(m_payloadBytes.size());
3378 buf->putBitLong(static_cast<std::int32_t>(payloadLen));
3379 if (payloadLen > 0)
3380 buf->putBytes(m_payloadBytes.data(), m_payloadBytes.size());
3381 // R2000+ Unknown RC (ODA §20.4.88): emitted UNCONDITIONALLY for version >
3382 // AC1014. parseDwg reads it whenever bytes remain before the handle stream
3383 // (which is always — handle data always follows), so gating the write on
3384 // m_hasR2000TrailingByte desynced a directly-constructed OLE2FRAME (the
3385 // default false): the parser consumed the first handle byte as this RC and
3386 // shifted the entity handle stream. Default m_r2000TrailingByte is 0, so
3387 // constructed entities align and round-tripped ones keep the captured byte.
3388 if (version > DRW::AC1014)
3389 buf->putRawChar8(m_r2000TrailingByte);
3390
3391 return encodeDwgEntHandle(version, buf, handleBuf);
3392}
3393
3394bool DRW_Light::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
3395 dwgBuffer sBuff = *buf;
3396 dwgBuffer *sBuf = v > DRW::AC1018 ? &sBuff : buf;
3397 bool ret = DRW_Entity::parseDwg(v, buf, sBuf, bs);
3398 if (!ret)
3399 return ret;
3400 DRW_DBG("\n***************************** parsing LIGHT *****************************\n")DRW_dbg::dbg("\n***************************** parsing LIGHT *****************************\n"
)
;
3401
3402 const std::uint64_t bodyDataEndBit = v > DRW::AC1018 ? currentDwgBit(sBuf) : 0;
3403 m_classVersion = static_cast<std::uint32_t>(buf->getBitLong());
3404 m_name = sBuf->getVariableText(v, false);
3405 m_type = static_cast<std::uint32_t>(buf->getBitLong());
3406 m_status = buf->getBit() != 0;
3407 m_color = buf->getCmColor(v);
3408 m_plotGlyph = buf->getBit() != 0;
3409 m_intensity = buf->getBitDouble();
3410 m_position = buf->get3BitDouble();
3411 m_target = buf->get3BitDouble();
3412 m_attenuationType = static_cast<std::uint32_t>(buf->getBitLong());
3413 m_useAttenuationLimits = buf->getBit() != 0;
3414 m_attenuationStartLimit = buf->getBitDouble();
3415 m_attenuationEndLimit = buf->getBitDouble();
3416 m_hotspotAngle = buf->getBitDouble();
3417 m_falloffAngle = buf->getBitDouble();
3418 m_castShadows = buf->getBit() != 0;
3419 m_shadowType = static_cast<std::uint32_t>(buf->getBitLong());
3420 m_shadowMapSize = buf->getBitShort();
3421 m_shadowMapSoftness = buf->getRawChar8();
3422
3423 if (v > DRW::AC1018 && currentDwgBit(buf) < bodyDataEndBit) {
3424 m_hasPhotometricData = buf->getBit() != 0;
3425 if (m_hasPhotometricData) {
3426 m_hasWebFile = buf->getBit() != 0;
3427 m_webFile = sBuf->getVariableText(v, false);
3428 m_physicalIntensityMethod = buf->getBitShort();
3429 m_physicalIntensity = buf->getBitDouble();
3430 m_illuminanceDistance = buf->getBitDouble();
3431 m_lampColorType = buf->getBitShort();
3432 m_lampColorTemperature = buf->getBitDouble();
3433 m_lampColorPreset = buf->getBitShort();
3434 m_webRotation = buf->get3BitDouble();
3435 m_extendedLightShape = buf->getBitShort();
3436 m_extendedLightLength = buf->getBitDouble();
3437 m_extendedLightWidth = buf->getBitDouble();
3438 m_extendedLightRadius = buf->getBitDouble();
3439 }
3440 }
3441
3442 ret = DRW_Entity::parseDwgEntHandle(v, buf);
3443 DRW_DBG("LIGHT name: ")DRW_dbg::dbg("LIGHT name: "); DRW_DBG(m_name.c_str())DRW_dbg::dbg(m_name.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3444 return ret;
3445}
3446
3447bool DRW_Light::encodeDwg(DRW::Version v, dwgBufferW *buf, std::uint32_t bs,
3448 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
3449 (void)bs;
3450 if (v < DRW::AC1021)
3451 return false;
3452
3453 oType = kDwgClassNum;
3454 if (!encodeDwgCommon(v, buf, strBuf))
3455 return false;
3456
3457 dwgBufferW *sb = strBuf ? strBuf : buf;
3458 buf->putBitLong(m_classVersion);
3459 sb->putVariableText(v, m_name);
3460 buf->putBitLong(m_type);
3461 buf->putBit(m_status ? 1 : 0);
3462 buf->putCmColor(v, static_cast<std::uint16_t>(m_color));
3463 buf->putBit(m_plotGlyph ? 1 : 0);
3464 buf->putBitDouble(m_intensity);
3465 buf->put3BitDouble(m_position);
3466 buf->put3BitDouble(m_target);
3467 buf->putBitLong(m_attenuationType);
3468 buf->putBit(m_useAttenuationLimits ? 1 : 0);
3469 buf->putBitDouble(m_attenuationStartLimit);
3470 buf->putBitDouble(m_attenuationEndLimit);
3471 buf->putBitDouble(m_hotspotAngle);
3472 buf->putBitDouble(m_falloffAngle);
3473 buf->putBit(m_castShadows ? 1 : 0);
3474 buf->putBitLong(m_shadowType);
3475 buf->putBitShort(m_shadowMapSize);
3476 buf->putRawChar8(m_shadowMapSoftness);
3477
3478 buf->putBit(m_hasPhotometricData ? 1 : 0);
3479 if (m_hasPhotometricData) {
3480 buf->putBit(m_hasWebFile ? 1 : 0);
3481 sb->putVariableText(v, m_webFile);
3482 buf->putBitShort(m_physicalIntensityMethod);
3483 buf->putBitDouble(m_physicalIntensity);
3484 buf->putBitDouble(m_illuminanceDistance);
3485 buf->putBitShort(m_lampColorType);
3486 buf->putBitDouble(m_lampColorTemperature);
3487 buf->putBitShort(m_lampColorPreset);
3488 buf->put3BitDouble(m_webRotation);
3489 buf->putBitShort(m_extendedLightShape);
3490 buf->putBitDouble(m_extendedLightLength);
3491 buf->putBitDouble(m_extendedLightWidth);
3492 buf->putBitDouble(m_extendedLightRadius);
3493 }
3494
3495 return encodeDwgEntHandle(v, buf, handleBuf);
3496}
3497
3498// DRW_Section::parseDwg — SECTIONOBJECT / AcDbSection, field order per
3499// libreDWG dwg2.spec DWG_ENTITY(SECTIONOBJECT): BL state, BL flags, T name,
3500// 3BD vert_dir, BD top/bottom height, BS indicator_alpha, CMTC indicator_color,
3501// BL num_verts + verts, BL num_blverts + blverts; then the common entity handle
3502// data followed by the section_settings hard reference (H 5, 360).
3503bool DRW_SectionObject::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
3504 // R2007+ keeps text in a separate string stream; read scalars from buf
3505 // (data stream) and text from sBuf, exactly like DRW_Light.
3506 dwgBuffer sBuff = *buf;
3507 dwgBuffer *sBuf = v > DRW::AC1018 ? &sBuff : buf;
3508 bool ret = DRW_Entity::parseDwg(v, buf, sBuf, bs);
3509 if (!ret)
3510 return true; // graceful-degrade: keep the raw shelf
3511 DRW_DBG("\n***************************** parsing SECTIONOBJECT *********************\n")DRW_dbg::dbg("\n***************************** parsing SECTIONOBJECT *********************\n"
)
;
3512
3513 m_state = static_cast<std::uint32_t>(buf->getBitLong());
3514 m_flags = static_cast<std::uint32_t>(buf->getBitLong());
3515 m_name = sBuf->getVariableText(v, false);
3516 m_vertDir = buf->get3BitDouble();
3517 m_topHeight = buf->getBitDouble();
3518 m_bottomHeight = buf->getBitDouble();
3519 m_indicatorAlpha = buf->getBitShort();
3520 m_indicatorColor = buf->getCmColor(v);
3521
3522 // num_verts / num_blverts are bounded before looping — a corrupt count must
3523 // never drive an unbounded allocation, and a short read must never drop the
3524 // object (raw shelf is the round-trip floor).
3525 constexpr std::uint32_t kMaxSectionVerts = 1u << 20; // 1,048,576
3526 std::int32_t nv = buf->getBitLong();
3527 std::uint32_t numVerts = (nv > 0) ? static_cast<std::uint32_t>(nv) : 0u;
3528 if (numVerts > kMaxSectionVerts)
3529 numVerts = 0;
3530 m_verts.clear();
3531 m_verts.reserve(numVerts);
3532 for (std::uint32_t i = 0; i < numVerts && buf->isGood(); ++i)
3533 m_verts.push_back(buf->get3BitDouble());
3534
3535 std::int32_t nb = buf->getBitLong();
3536 std::uint32_t numBl = (nb > 0) ? static_cast<std::uint32_t>(nb) : 0u;
3537 if (numBl > kMaxSectionVerts)
3538 numBl = 0;
3539 m_blVerts.clear();
3540 m_blVerts.reserve(numBl);
3541 for (std::uint32_t i = 0; i < numBl && buf->isGood(); ++i)
3542 m_blVerts.push_back(buf->get3BitDouble());
3543
3544 // Handle stream: parseDwgEntHandle reads the common entity handles — it
3545 // resets buf to objSize for R2007+ and reads inline (after the exact body
3546 // above) for <=AC1018. The section_settings hard reference follows the
3547 // common handles, so read it from buf right after (guarded by remaining
3548 // bytes so a truncated object never over-reads).
3549 ret = DRW_Entity::parseDwgEntHandle(v, buf);
3550 // The section_settings hard reference follows the common entity handles.
3551 // The R2007+ handle stream is small (a few bytes) so guard on any
3552 // remaining byte rather than the 4-byte common-object slack.
3553 if (ret && buf->isGood() && buf->numRemainingBytes() >= 1) {
3554 dwgHandle ssH = buf->getOffsetHandle(handle);
3555 m_sectionSettingsHandle = ssH.ref;
3556 DRW_DBG(" section_settings Handle: ")DRW_dbg::dbg(" section_settings Handle: ");
3557 DRW_DBGHL(ssH.code, ssH.size, ssH.ref)DRW_dbg::dbgHL(ssH.code, ssH.size, ssH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3558 }
3559 DRW_DBG("SECTIONOBJECT name: ")DRW_dbg::dbg("SECTIONOBJECT name: "); DRW_DBG(m_name.c_str())DRW_dbg::dbg(m_name.c_str());
3560 DRW_DBG(" verts: ")DRW_dbg::dbg(" verts: "); DRW_DBG(static_cast<int>(m_verts.size()))DRW_dbg::dbg(static_cast<int>(m_verts.size())); DRW_DBG("\n")DRW_dbg::dbg("\n");
3561 return true; // graceful-degrade — always deliver typed add + raw shelf
3562}
3563
3564bool DRW_Tolerance::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
3565 switch (code) {
3566 case 1:
3567 text = reader->getUtf8String();
3568 break;
3569 case 3:
3570 dimStyleName = reader->getUtf8String();
3571 break;
3572 case 10:
3573 insertionPoint.x = reader->getDouble();
3574 break;
3575 case 20:
3576 insertionPoint.y = reader->getDouble();
3577 break;
3578 case 30:
3579 insertionPoint.z = reader->getDouble();
3580 break;
3581 case 11:
3582 xAxisDirectionVector.x = reader->getDouble();
3583 break;
3584 case 21:
3585 xAxisDirectionVector.y = reader->getDouble();
3586 break;
3587 case 31:
3588 xAxisDirectionVector.z = reader->getDouble();
3589 break;
3590 case 210:
3591 extPoint.x = reader->getDouble();
3592 break;
3593 case 220:
3594 extPoint.y = reader->getDouble();
3595 break;
3596 case 230:
3597 extPoint.z = reader->getDouble();
3598 break;
3599 default:
3600 return DRW_Entity::parseCode(code, reader);
3601 }
3602 return true;
3603}
3604
3605bool DRW_Tolerance::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
3606 dwgBuffer sBuff = *buf;
3607 dwgBuffer *sBuf = buf;
3608 if (v > DRW::AC1018)
3609 sBuf = &sBuff;
3610
3611 bool ret = DRW_Entity::parseDwg(v, buf, sBuf, bs);
3612 if (!ret)
3613 return ret;
3614
3615 DRW_DBG("\n***************************** parsing tolerance *********************************************\n")DRW_dbg::dbg("\n***************************** parsing tolerance *********************************************\n"
)
;
3616 if (v < DRW::AC1015) {
3617 DRW_DBG("unknown R13/R14 short: ")DRW_dbg::dbg("unknown R13/R14 short: "); DRW_DBG(buf->getBitShort())DRW_dbg::dbg(buf->getBitShort()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3618 DRW_DBG("height at creation: ")DRW_dbg::dbg("height at creation: "); DRW_DBG(buf->getBitDouble())DRW_dbg::dbg(buf->getBitDouble()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3619 DRW_DBG("dimgap/dimscale at creation: ")DRW_dbg::dbg("dimgap/dimscale at creation: "); DRW_DBG(buf->getBitDouble())DRW_dbg::dbg(buf->getBitDouble()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3620 }
3621
3622 insertionPoint = buf->get3BitDouble();
3623 DRW_DBG("insertionPoint: ")DRW_dbg::dbg("insertionPoint: "); DRW_DBGPT(insertionPoint.x, insertionPoint.y, insertionPoint.z)DRW_dbg::dbgPT(insertionPoint.x, insertionPoint.y, insertionPoint
.z)
;
3624 xAxisDirectionVector = buf->get3BitDouble();
3625 DRW_DBG("\nxAxisDirectionVector: ")DRW_dbg::dbg("\nxAxisDirectionVector: ");
3626 DRW_DBGPT(xAxisDirectionVector.x, xAxisDirectionVector.y, xAxisDirectionVector.z)DRW_dbg::dbgPT(xAxisDirectionVector.x, xAxisDirectionVector.y
, xAxisDirectionVector.z)
;
3627 extPoint = buf->get3BitDouble();
3628 DRW_DBG("\nextPoint: ")DRW_dbg::dbg("\nextPoint: "); DRW_DBGPT(extPoint.x, extPoint.y, extPoint.z)DRW_dbg::dbgPT(extPoint.x, extPoint.y, extPoint.z);
3629 text = sBuf->getVariableText(v, false);
3630 DRW_DBG("\ntolerance text: ")DRW_dbg::dbg("\ntolerance text: "); DRW_DBG(text.c_str())DRW_dbg::dbg(text.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3631
3632 ret = DRW_Entity::parseDwgEntHandle(v, buf);
3633 if (!ret)
3634 return ret;
3635 dimStyleH = buf->getHandle();
3636 DRW_DBG("dim style Handle: ")DRW_dbg::dbg("dim style Handle: ");
3637 DRW_DBGHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref)DRW_dbg::dbgHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3638 return buf->isGood();
3639}
3640
3641bool DRW_Tolerance::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
3642 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
3643 (void)bs;
3644 oType = 46;
3645 if (!encodeDwgCommon(version, buf, strBuf))
3646 return false;
3647
3648 if (version < DRW::AC1015) {
3649 buf->putBitShort(0);
3650 buf->putBitDouble(0.0);
3651 buf->putBitDouble(0.0);
3652 }
3653
3654 buf->put3BitDouble(insertionPoint);
3655 buf->put3BitDouble(xAxisDirectionVector);
3656 buf->put3BitDouble(extPoint);
3657 (strBuf ? strBuf : buf)->putVariableText(version, text);
3658
3659 if (!encodeDwgEntHandle(version, buf, handleBuf))
3660 return false;
3661
3662 dwgBufferW *hb = handleBuf ? handleBuf : buf;
3663 putHardPointerHandle(hb, (dimStyleH.ref == 0) ? 0x15 : dimStyleH.ref);
3664 return true;
3665}
3666
3667
3668bool DRW_Block::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
3669 switch (code) {
3670 case 1:
3671 xrefPath = reader->getUtf8String();
3672 break;
3673 case 2:
3674 name = reader->getUtf8String();
3675 break;
3676 case 70:
3677 flags = reader->getInt32();
3678 break;
3679 default:
3680 return DRW_Point::parseCode(code, reader);
3681 }
3682
3683 return true;
3684}
3685
3686bool DRW_Block::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
3687 dwgBuffer sBuff = *buf;
3688 dwgBuffer *sBuf = buf;
3689 if (version > DRW::AC1018) {//2007+
3690 sBuf = &sBuff; //separate buffer for strings
3691 }
3692 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
3693 if (!ret)
3694 return ret;
3695 if (!isEnd){
3696 DRW_DBG("\n***************************** parsing block *********************************************\n")DRW_dbg::dbg("\n***************************** parsing block *********************************************\n"
)
;
3697 name = sBuf->getVariableText(version, false);
3698 DRW_DBG("Block name: ")DRW_dbg::dbg("Block name: "); DRW_DBG(name.c_str())DRW_dbg::dbg(name.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3699 } else {
3700 DRW_DBG("\n***************************** parsing end block *********************************************\n")DRW_dbg::dbg("\n***************************** parsing end block *********************************************\n"
)
;
3701 }
3702 if (version > DRW::AC1018) {//2007+
3703 std::uint8_t unk = buf->getBit();
3704 DRW_DBG("unknown bit: ")DRW_dbg::dbg("unknown bit: "); DRW_DBG(unk)DRW_dbg::dbg(unk); DRW_DBG("\n")DRW_dbg::dbg("\n");
3705 }
3706// X handleAssoc; //X
3707 ret = DRW_Entity::parseDwgEntHandle(version, buf);
3708 if (!ret)
3709 return ret;
3710 // RS crc; //RS */
3711 return buf->isGood();
3712}
3713
3714bool DRW_Insert::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
3715 switch (code) {
3716 case 2:
3717 name = reader->getUtf8String();
3718 break;
3719 case 41:
3720 xscale = reader->getDouble();
3721 break;
3722 case 42:
3723 yscale = reader->getDouble();
3724 break;
3725 case 43:
3726 zscale = reader->getDouble();
3727 break;
3728 case 50:
3729 angle = reader->getDouble();
3730 angle = angle/ARAD57.29577951308232; //convert to radian
3731 break;
3732 case 70:
3733 colcount = reader->getInt32();
3734 break;
3735 case 71:
3736 rowcount = reader->getInt32();
3737 break;
3738 case 44:
3739 colspace = reader->getDouble();
3740 break;
3741 case 45:
3742 rowspace = reader->getDouble();
3743 break;
3744 default:
3745 return DRW_Point::parseCode(code, reader);
3746 }
3747
3748 return true;
3749}
3750
3751bool DRW_Table::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
3752 auto ensureGrid = [this]() {
3753 if (m_dxfRowsExpected < 0 || m_dxfColumnsExpected < 0)
3754 return;
3755
3756 const std::uint32_t rows = static_cast<std::uint32_t>(m_dxfRowsExpected);
3757 const std::uint32_t columns = static_cast<std::uint32_t>(m_dxfColumnsExpected);
3758 if (rows > kMaxTableRows || columns > kMaxTableColumns
3759 || (columns != 0 && rows > kMaxTableCells / columns)) {
3760 return;
3761 }
3762
3763 if (m_content.m_columns.size() != columns) {
3764 m_content.m_columns.clear();
3765 m_content.m_columns.resize(columns);
3766 m_dxfColumnWidthsRead = 0;
3767 }
3768 if (m_content.m_rows.size() != rows) {
3769 m_content.m_rows.clear();
3770 m_content.m_rows.resize(rows);
3771 m_dxfRowHeightsRead = 0;
3772 }
3773 for (auto& row : m_content.m_rows)
3774 row.m_cells.resize(columns);
3775
3776 m_hasSemanticContent = true;
3777 m_semanticContentComplete = true;
3778 };
3779
3780 auto currentCell = [this]() -> DRW_TableCell* {
3781 if (m_dxfCurrentCell < 0 || m_content.m_columns.empty()
3782 || m_content.m_rows.empty()) {
3783 return nullptr;
3784 }
3785
3786 const std::size_t columns = m_content.m_columns.size();
3787 const std::size_t cell = static_cast<std::size_t>(m_dxfCurrentCell);
3788 const std::size_t row = cell / columns;
3789 const std::size_t column = cell % columns;
3790 if (row >= m_content.m_rows.size()
3791 || column >= m_content.m_rows[row].m_cells.size()) {
3792 return nullptr;
3793 }
3794 return &m_content.m_rows[row].m_cells[column];
3795 };
3796
3797 auto currentContent = [&currentCell]() -> DRW_TableCellContent* {
3798 DRW_TableCell *cell = currentCell();
3799 if (cell == nullptr)
3800 return nullptr;
3801 if (cell->m_contents.empty() || cell->m_contents.back().m_type != 1) {
3802 DRW_TableCellContent content;
3803 content.m_type = 1;
3804 cell->m_contents.push_back(content);
3805 }
3806 return &cell->m_contents.back();
3807 };
3808
3809 if (code == 100) {
3810 const std::string subclass = reader->getString();
3811 if (subclass == "AcDbBlockReference") {
3812 m_dxfSubclass = DxfSubclass::BlockReference;
3813 } else if (subclass == "AcDbTable") {
3814 m_dxfSubclass = DxfSubclass::Table;
3815 } else if (subclass == "AcDbEntity") {
3816 m_dxfSubclass = DxfSubclass::Entity;
3817 }
3818 return true;
3819 }
3820
3821 if (m_dxfSubclass != DxfSubclass::Table)
3822 return DRW_Insert::parseCode(code, reader);
3823
3824 switch (code) {
3825 case 342:
3826 m_tableStyleHandle = static_cast<std::uint32_t>(reader->getHandleString());
3827 m_content.m_tableStyleHandle = m_tableStyleHandle;
3828 break;
3829 case 343:
3830 reader->getHandleString();
3831 break;
3832 case 11:
3833 m_horizontalDirection.x = reader->getDouble();
3834 break;
3835 case 21:
3836 m_horizontalDirection.y = reader->getDouble();
3837 break;
3838 case 31:
3839 m_horizontalDirection.z = reader->getDouble();
3840 break;
3841 case 90:
3842 if (m_dxfInCellValue) {
3843 if (DRW_TableCellContent *content = currentContent())
3844 content->m_value.m_dataType = reader->getInt32();
3845 else
3846 reader->getInt32();
3847 } else {
3848 m_valueFlag = reader->getInt32();
3849 }
3850 break;
3851 case 91:
3852 if (m_dxfRowsExpected < 0 && !m_dxfInCellValue) {
3853 m_dxfRowsExpected = reader->getInt32();
3854 ensureGrid();
3855 } else {
3856 reader->getInt32();
3857 }
3858 break;
3859 case 92:
3860 if (m_dxfColumnsExpected < 0 && !m_dxfInCellValue) {
3861 m_dxfColumnsExpected = reader->getInt32();
3862 ensureGrid();
3863 } else {
3864 reader->getInt32();
3865 }
3866 break;
3867 case 93:
3868 case 94:
3869 case 95:
3870 case 96:
3871 case 172:
3872 case 173:
3873 case 174:
3874 case 175:
3875 case 176:
3876 case 178:
3877 reader->getInt32();
3878 break;
3879 case 141:
3880 ensureGrid();
3881 if (m_dxfRowHeightsRead < m_content.m_rows.size())
3882 m_content.m_rows[m_dxfRowHeightsRead++].m_height = reader->getDouble();
3883 else
3884 reader->getDouble();
3885 break;
3886 case 142:
3887 ensureGrid();
3888 if (m_dxfColumnWidthsRead < m_content.m_columns.size())
3889 m_content.m_columns[m_dxfColumnWidthsRead++].m_width = reader->getDouble();
3890 else
3891 reader->getDouble();
3892 break;
3893 case 145:
3894 reader->getDouble();
3895 break;
3896 case 171:
3897 ensureGrid();
3898 if (!m_content.m_rows.empty() && !m_content.m_columns.empty()
3899 && m_dxfNextCell < m_content.m_rows.size() * m_content.m_columns.size()) {
3900 m_dxfCurrentCell = static_cast<int>(m_dxfNextCell++);
3901 if (DRW_TableCell *cell = currentCell())
3902 cell->m_flags = reader->getInt32();
3903 else
3904 reader->getInt32();
3905 } else {
3906 m_dxfCurrentCell = -1;
3907 reader->getInt32();
3908 }
3909 m_dxfInCellValue = false;
3910 break;
3911 case 301:
3912 m_dxfInCellValue = reader->getString() == "CELL_VALUE";
3913 if (m_dxfInCellValue)
3914 currentContent();
3915 break;
3916 case 1:
3917 case 302: {
3918 const UTF8STRINGstd::string text = reader->getUtf8String();
3919 if (m_dxfInCellValue) {
3920 if (DRW_TableCellContent *content = currentContent()) {
3921 content->m_text = text;
3922 content->m_value.m_dataType = 4;
3923 content->m_value.m_value.addString(1, text);
3924 }
3925 }
3926 break;
3927 }
3928 case 300:
3929 if (m_dxfInCellValue) {
3930 if (DRW_TableCellContent *content = currentContent())
3931 content->m_value.m_valueString = reader->getUtf8String();
3932 else
3933 reader->getUtf8String();
3934 } else {
3935 reader->getUtf8String();
3936 }
3937 break;
3938 case 304:
3939 reader->getString();
3940 m_dxfInCellValue = false;
3941 break;
3942 default:
3943 return DRW_Entity::parseCode(code, reader);
3944 }
3945
3946 return true;
3947}
3948
3949bool DRW_Insert::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
3950 std::int32_t objCount = 0;
3951 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
3952 if (!ret)
3953 return ret;
3954 DRW_DBG("\n************************** parsing insert/minsert *****************************************\n")DRW_dbg::dbg("\n************************** parsing insert/minsert *****************************************\n"
)
;
3955 basePoint.x = buf->getBitDouble();
3956 basePoint.y = buf->getBitDouble();
3957 basePoint.z = buf->getBitDouble();
3958 DRW_DBG("insertion point: ")DRW_dbg::dbg("insertion point: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
3959 if (version < DRW::AC1015) {//14-
3960 xscale = buf->getBitDouble();
3961 yscale = buf->getBitDouble();
3962 zscale = buf->getBitDouble();
3963 } else {
3964 std::uint8_t dataFlags = buf->get2Bits();
3965 if (dataFlags == 3){
3966 //none default value 1,1,1
3967 } else if (dataFlags == 1){ //x default value 1, y & z can be x value
3968 yscale = buf->getDefaultDouble(xscale);
3969 zscale = buf->getDefaultDouble(xscale);
3970 } else if (dataFlags == 2){
3971 xscale = buf->getRawDouble();
3972 yscale = zscale = xscale;
3973 } else { //dataFlags == 0
3974 xscale = buf->getRawDouble();
3975 yscale = buf->getDefaultDouble(xscale);
3976 zscale = buf->getDefaultDouble(xscale);
3977 }
3978 }
3979 angle = buf->getBitDouble();
3980 DRW_DBG("scale : ")DRW_dbg::dbg("scale : "); DRW_DBGPT(xscale, yscale, zscale)DRW_dbg::dbgPT(xscale, yscale, zscale); DRW_DBG(", angle: ")DRW_dbg::dbg(", angle: "); DRW_DBG(angle)DRW_dbg::dbg(angle);
3981 extPoint = buf->getExtrusion(false); //3BD R14 style
3982 DRW_DBG("\nextrusion: ")DRW_dbg::dbg("\nextrusion: "); DRW_DBGPT(extPoint.x, extPoint.y, extPoint.z)DRW_dbg::dbgPT(extPoint.x, extPoint.y, extPoint.z);
3983
3984 bool hasAttrib = buf->getBit();
3985 DRW_DBG(" has Attrib: ")DRW_dbg::dbg(" has Attrib: "); DRW_DBG(hasAttrib)DRW_dbg::dbg(hasAttrib);
3986
3987 if (hasAttrib && version > DRW::AC1015) {//2004+
3988 objCount = buf->getBitLong();
3989 DRW_UNUSED(objCount)(void)objCount;
3990 DRW_DBG(" objCount: ")DRW_dbg::dbg(" objCount: "); DRW_DBG(objCount)DRW_dbg::dbg(objCount); DRW_DBG("\n")DRW_dbg::dbg("\n");
3991 }
3992 if (oType == 8) {//entity are minsert
3993 colcount = buf->getBitShort();
3994 rowcount = buf->getBitShort();
3995 colspace = buf->getBitDouble();
3996 rowspace = buf->getBitDouble();
3997 }
3998 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3999 ret = DRW_Entity::parseDwgEntHandle(version, buf);
4000 blockRecH = buf->getHandle(); /* H 2 BLOCK HEADER (hard pointer) */
4001 DRW_DBG("BLOCK HEADER Handle: ")DRW_dbg::dbg("BLOCK HEADER Handle: "); DRW_DBGHL(blockRecH.code, blockRecH.size, blockRecH.ref)DRW_dbg::dbgHL(blockRecH.code, blockRecH.size, blockRecH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
4002 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
4003
4004 /*attribs follows*/
4005 if (hasAttrib) {
4006 if (version < DRW::AC1018) {//2000-
4007 dwgHandle attH = buf->getHandle(); /* H 2 BLOCK HEADER (hard pointer) */
4008 DRW_DBG("first attrib Handle: ")DRW_dbg::dbg("first attrib Handle: "); DRW_DBGHL(attH.code, attH.size, attH.ref)DRW_dbg::dbgHL(attH.code, attH.size, attH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
4009 attribHandles.push_back(attH);
4010 attH = buf->getHandle(); /* H 2 BLOCK HEADER (hard pointer) */
4011 DRW_DBG("second attrib Handle: ")DRW_dbg::dbg("second attrib Handle: "); DRW_DBGHL(attH.code, attH.size, attH.ref)DRW_dbg::dbgHL(attH.code, attH.size, attH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
4012 attribHandles.push_back(attH);
4013 } else {
4014 for (std::int32_t i=0; i < objCount && buf->isGood(); ++i){
4015 dwgHandle attH = buf->getHandle(); /* H 2 BLOCK HEADER (hard pointer) */
4016 DRW_DBG("attrib Handle #")DRW_dbg::dbg("attrib Handle #"); DRW_DBG(i)DRW_dbg::dbg(i); DRW_DBG(": ")DRW_dbg::dbg(": "); DRW_DBGHL(attH.code, attH.size, attH.ref)DRW_dbg::dbgHL(attH.code, attH.size, attH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
4017 attribHandles.push_back(attH);
4018 }
4019 }
4020 seqendH = buf->getHandle(); /* H 2 BLOCK HEADER (hard pointer) */
4021 DRW_DBG("seqendH Handle: ")DRW_dbg::dbg("seqendH Handle: "); DRW_DBGHL(seqendH.code, seqendH.size, seqendH.ref)DRW_dbg::dbgHL(seqendH.code, seqendH.size, seqendH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
4022 }
4023 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
4024
4025 if (!ret)
4026 return ret;
4027 // RS crc; //RS */
4028 return buf->isGood();
4029}
4030
4031bool DRW_Table::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
4032 if (version < DRW::AC1015)
4033 return false;
4034
4035 dwgBuffer sBuff = *buf;
4036 sBuff.setVariableTextByteLength(true);
4037 dwgBuffer *sBuf = &sBuff;
4038 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
4039 if (!ret)
4040 return ret;
4041
4042 DRW_DBG("\n************************** parsing table *****************************************\n")DRW_dbg::dbg("\n************************** parsing table *****************************************\n"
)
;
4043 basePoint.x = buf->getBitDouble();
4044 basePoint.y = buf->getBitDouble();
4045 basePoint.z = buf->getBitDouble();
4046
4047 std::uint8_t dataFlags = buf->get2Bits();
4048 if (dataFlags == 3) {
4049 // default scale 1,1,1
4050 } else if (dataFlags == 1) {
4051 yscale = buf->getDefaultDouble(xscale);
4052 zscale = buf->getDefaultDouble(xscale);
4053 } else if (dataFlags == 2) {
4054 xscale = buf->getRawDouble();
4055 yscale = zscale = xscale;
4056 } else {
4057 xscale = buf->getRawDouble();
4058 yscale = buf->getDefaultDouble(xscale);
4059 zscale = buf->getDefaultDouble(xscale);
4060 }
4061
4062 angle = buf->getBitDouble();
4063 extPoint = buf->getExtrusion(false);
4064
4065 std::int32_t objCount = 0;
4066 bool hasAttrib = buf->getBit();
4067 if (hasAttrib && version > DRW::AC1015)
4068 objCount = buf->getBitLong();
4069
4070 dwgBuffer hBuff = *buf;
4071 if (version <= DRW::AC1018) {
4072 // R2000/R2004: parseDwgEntHandle only re-seeks to the handle stream
4073 // for version > AC1018 (2007+ string area). For the legacy versions
4074 // seek the snapshot to the handle-stream start (objSize is the
4075 // bit offset of the handle stream, RL field read in
4076 // DRW_Entity::parseDwg) or every handle below reads mid-DATA garbage.
4077 hBuff.setPosition(objSize >> 3);
4078 hBuff.setBitPos(objSize & 7);
4079 }
4080 ret = DRW_Entity::parseDwgEntHandle(version, &hBuff);
4081 blockRecH = hBuff.getHandle();
4082
4083 if (hasAttrib) {
4084 for (std::int32_t i = 0; i < objCount && hBuff.isGood(); ++i)
4085 attribHandles.push_back(hBuff.getHandle());
4086 seqendH = hBuff.getHandle();
4087 }
4088
4089 if (!ret)
4090 return ret;
4091
4092 if (version >= DRW::AC1024) {
4093 buf->getRawChar8();
4094 readTableHandle(&hBuff);
4095 buf->getBitLong();
4096 if (version >= DRW::AC1027)
4097 buf->getBitLong();
4098 else
4099 buf->getBit();
4100
4101 m_hasSemanticContent = true;
4102 m_semanticContentComplete = parseTableContent(version, buf, sBuf, &hBuff, m_content);
4103 if (m_content.m_tableStyleHandle != 0) {
4104 m_tableStyleHandle = m_content.m_tableStyleHandle;
4105 }
4106 if (!m_semanticContentComplete) {
4107 DRW_DBG("TABLECONTENT parse incomplete; anonymous block insert kept\n")DRW_dbg::dbg("TABLECONTENT parse incomplete; anonymous block insert kept\n"
)
;
4108 return true;
4109 }
4110
4111 buf->getBitShort();
4112 m_horizontalDirection = buf->get3BitDouble();
4113
4114 const std::uint64_t breakStartBit = currentDwgBit(buf);
4115 const bool hasBreakData = buf->getBitLong() != 0;
4116 if (hasBreakData) {
4117 buf->getBitLong();
4118 buf->getBitLong();
4119 buf->getBitDouble();
4120 buf->getBitLong();
4121 buf->getBitLong();
4122 const std::uint32_t manualPositions = buf->getBitLong();
4123 if (manualPositions > kMaxTableItems) {
4124 m_content.m_subrecordRanges.push_back(makeDwgSubrecordRange(
4125 "table-break-data", breakStartBit, currentDwgBit(buf),
4126 version, manualPositions, false));
4127 return true;
4128 }
4129 for (std::uint32_t i = 0; i < manualPositions; ++i) {
4130 buf->get3BitDouble();
4131 buf->getBitDouble();
4132 buf->getBitLong();
4133 }
4134 m_content.m_subrecordRanges.push_back(makeDwgSubrecordRange(
4135 "table-break-data", breakStartBit, currentDwgBit(buf),
4136 version, manualPositions, buf->isGood()));
4137 }
4138
4139 const std::uint64_t rowRangeStartBit = currentDwgBit(buf);
4140 const std::uint32_t rowRanges = buf->getBitLong();
4141 if (rowRanges <= kMaxTableItems) {
4142 for (std::uint32_t i = 0; i < rowRanges; ++i) {
4143 buf->get3BitDouble();
4144 buf->getBitLong();
4145 buf->getBitLong();
4146 }
4147 if (rowRanges != 0) {
4148 m_content.m_subrecordRanges.push_back(makeDwgSubrecordRange(
4149 "table-row-ranges", rowRangeStartBit, currentDwgBit(buf),
4150 version, rowRanges, buf->isGood()));
4151 }
4152 } else {
4153 m_content.m_subrecordRanges.push_back(makeDwgSubrecordRange(
4154 "table-row-ranges", rowRangeStartBit, currentDwgBit(buf),
4155 version, rowRanges, false));
4156 }
4157
4158 return true;
4159 }
4160
4161 m_valueFlag = buf->getBitShort();
4162 m_horizontalDirection = buf->get3BitDouble();
4163 const std::uint32_t columns = buf->getBitLong();
4164 const std::uint32_t rows = buf->getBitLong();
4165 if (columns > kMaxTableColumns || rows > kMaxTableRows
4166 || (columns != 0 && rows > kMaxTableCells / columns)) {
4167 return true;
4168 }
4169
4170 m_hasSemanticContent = true;
4171 m_semanticContentComplete = false;
4172 m_content.m_columns.clear();
4173 m_content.m_rows.clear();
4174 m_content.m_columns.reserve(columns);
4175 m_content.m_rows.reserve(rows);
4176 for (std::uint32_t i = 0; i < columns; ++i) {
4177 DRW_TableColumn column;
4178 column.m_width = buf->getBitDouble();
4179 m_content.m_columns.push_back(column);
4180 }
4181 for (std::uint32_t i = 0; i < rows; ++i) {
4182 DRW_TableRow row;
4183 row.m_height = buf->getBitDouble();
4184 row.m_cells.resize(columns);
4185 m_content.m_rows.push_back(row);
4186 }
4187 m_tableStyleHandle = readTableHandle(&hBuff);
4188 m_content.m_tableStyleHandle = m_tableStyleHandle;
4189 m_semanticContentComplete = true;
4190 // For <=AC1018 (R2000/R2004) there is no separate R2007+ string stream:
4191 // DRW_Entity::parseDwg only seeks sBuf when version > AC1018 (see the
4192 // `strBuf != NULL && version > DRW::AC1018` guard there), so legacy cell
4193 // text is inline in `buf`. Passing the stale sBuf copy here would read
4194 // text from the wrong position and desync `buf`. Pass nullptr so the
4195 // cell readers' `textBuf = strBuf ? strBuf : buf` falls back to the
4196 // inline `buf`. R2007 (AC1021) keeps the separate sBuf stream.
4197 dwgBuffer *cellStrBuf = (version > DRW::AC1018) ? sBuf : nullptr;
4198 for (std::uint32_t row = 0; row < rows && m_semanticContentComplete; ++row) {
4199 for (std::uint32_t column = 0; column < columns; ++column) {
4200 if (!parseR2007TableCell(version, buf, cellStrBuf, &hBuff,
4201 m_content.m_rows[row].m_cells[column],
4202 &m_content.m_subrecordRanges)) {
4203 m_semanticContentComplete = false;
4204 break;
4205 }
4206 }
4207 }
4208
4209 if (m_semanticContentComplete)
4210 m_semanticContentComplete = skipR2007TableOverrides(
4211 version, buf, cellStrBuf, &hBuff, &m_content.m_subrecordRanges);
4212 if (!m_semanticContentComplete)
4213 DRW_DBG("R2007 TABLE cell parse incomplete; anonymous block insert kept\n")DRW_dbg::dbg("R2007 TABLE cell parse incomplete; anonymous block insert kept\n"
)
;
4214
4215 return true;
4216}
4217
4218bool DRW_TableContentObject::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
4219 if (version <= DRW::AC1018)
4220 return false;
4221
4222 dwgBuffer sBuff = *buf;
4223 sBuff.setVariableTextByteLength(true);
4224 dwgBuffer *sBuf = &sBuff;
4225 bool ret = DRW_TableEntry::parseDwg(version, buf, sBuf, bs);
4226 DRW_DBG("\n************************** parsing table content object ************************\n")DRW_dbg::dbg("\n************************** parsing table content object ************************\n"
)
;
4227 if (!ret)
4228 return ret;
4229
4230 dwgBuffer hBuff = *buf;
4231 seekTableObjectHandleStream(version, &hBuff, objSize);
4232 readTableObjectCommonHandles(&hBuff, handle, numReactors, xDictFlag, &parentHandle);
4233
4234 m_parseComplete = parseTableContent(version, buf, sBuf, &hBuff, m_content);
4235 if (!m_parseComplete)
4236 DRW_DBG("TABLECONTENT object parse incomplete\n")DRW_dbg::dbg("TABLECONTENT object parse incomplete\n");
4237 return true;
4238}
4239
4240void DRW_LWPolyline::applyExtrusion(){
4241 if (haveExtrusion) {
4242 calculateAxis(extPoint);
4243 for (unsigned int i=0; i<vertlist.size(); i++) {
4244 auto& vert = vertlist.at(i);
4245 DRW_Coord v(vert->x, vert->y, elevation);
4246 extrudePoint(extPoint, &v);
4247 vert->x = v.x;
4248 vert->y = v.y;
4249 }
4250 }
4251}
4252
4253bool DRW_LWPolyline::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
4254 switch (code) {
4255 case 10: {
4256 vertex = std::make_shared<DRW_Vertex2D>();
4257 vertlist.push_back(vertex);
4258 vertex->x = reader->getDouble();
4259 break; }
4260 case 20:
4261 if(vertex)
4262 vertex->y = reader->getDouble();
4263 break;
4264 case 40:
4265 if(vertex)
4266 vertex->stawidth = reader->getDouble();
4267 break;
4268 case 41:
4269 if(vertex)
4270 vertex->endwidth = reader->getDouble();
4271 break;
4272 case 42:
4273 if(vertex)
4274 vertex->bulge = reader->getDouble();
4275 break;
4276 case 91:
4277 if (vertex)
4278 vertex->identifier = reader->getInt32();
4279 break;
4280 case 38:
4281 elevation = reader->getDouble();
4282 break;
4283 case 39:
4284 thickness = reader->getDouble();
4285 break;
4286 case 43:
4287 width = reader->getDouble();
4288 break;
4289 case 70:
4290 flags = reader->getInt32();
4291 break;
4292 case 90:
4293 vertexnum = reader->getInt32();
4294 return DRW::reserve( vertlist, vertexnum);
4295 case 210:
4296 haveExtrusion = true;
4297 extPoint.x = reader->getDouble();
4298 break;
4299 case 220:
4300 extPoint.y = reader->getDouble();
4301 break;
4302 case 230:
4303 extPoint.z = reader->getDouble();
4304 break;
4305 default:
4306 return DRW_Entity::parseCode(code, reader);
4307 }
4308
4309 return true;
4310}
4311
4312bool DRW_LWPolyline::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
4313 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
4314 if (!ret)
4315 return ret;
4316 DRW_DBG("\n***************************** parsing LWPolyline *******************************************\n")DRW_dbg::dbg("\n***************************** parsing LWPolyline *******************************************\n"
)
;
4317
4318 flags = buf->getBitShort();
4319 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(flags)DRW_dbg::dbg(flags);
4320 if (flags & 4)
4321 width = buf->getBitDouble();
4322 if (flags & 8)
4323 elevation = buf->getBitDouble();
4324 if (flags & 2)
4325 thickness = buf->getBitDouble();
4326 if (flags & 1)
4327 extPoint = buf->getExtrusion(false);
4328 vertexnum = buf->getBitLong();
4329 if (!isValidCount(vertexnum, kMaxLWPolylineVertices)) {
4330 return false;
4331 }
4332 if (!DRW::reserve( vertlist, vertexnum)) {
4333 return false;
4334 }
4335
4336 unsigned int bulgesnum = 0;
4337 if (flags & 16)
4338 bulgesnum = static_cast<unsigned int>(buf->getBitLong());
4339 int vertexIdCount = 0;
4340 if (version > DRW::AC1021) {//2010+
4341 if (flags & 1024)
4342 vertexIdCount = buf->getBitLong();
4343 }
4344 unsigned int widthsnum = 0;
4345 if (flags & 32)
4346 widthsnum = static_cast<unsigned int>(buf->getBitLong());
4347 if (bulgesnum > static_cast<unsigned int>(vertexnum) ||
4348 vertexIdCount < 0 || vertexIdCount > vertexnum ||
4349 widthsnum > static_cast<unsigned int>(vertexnum)) {
4350 return false;
4351 }
4352 DRW_DBG("\nvertex num: ")DRW_dbg::dbg("\nvertex num: "); DRW_DBG(vertexnum)DRW_dbg::dbg(vertexnum); DRW_DBG(" bulges num: ")DRW_dbg::dbg(" bulges num: "); DRW_DBG(bulgesnum)DRW_dbg::dbg(bulgesnum);
4353 DRW_DBG(" vertexIdCount: ")DRW_dbg::dbg(" vertexIdCount: "); DRW_DBG(vertexIdCount)DRW_dbg::dbg(vertexIdCount); DRW_DBG(" widths num: ")DRW_dbg::dbg(" widths num: "); DRW_DBG(widthsnum)DRW_dbg::dbg(widthsnum);
4354 // Translate DWG LWPLINE flag bits to DXF group 70 bits.
4355 // Per ODA spec 20.4.85 + libreDWG dwg.spec (DWG_ENTITY LWPOLYLINE):
4356 // DWG bit 9 (0x200, 512) -> DXF bit 0 (closed, value 1)
4357 // DWG bit 8 (0x100, 256) -> DXF bit 7 (plinegen, value 128)
4358 // All other DWG flag bits indicate which optional fields are present
4359 // and have no DXF equivalent in group 70.
4360 int dxfFlags = 0;
4361 if (flags & 512)
4362 dxfFlags |= 1;
4363 if (flags & 256)
4364 dxfFlags |= 128;
4365 flags = dxfFlags;
4366 DRW_DBG("end flags value: ")DRW_dbg::dbg("end flags value: "); DRW_DBG(flags)DRW_dbg::dbg(flags);
4367
4368 if (vertexnum > 0) { //verify if is lwpol without vertex (empty)
4369 // add vertexes
4370 vertex = std::make_shared<DRW_Vertex2D>();
4371 vertex->x = buf->getRawDouble();
4372 vertex->y = buf->getRawDouble();
4373 vertlist.push_back(vertex);
4374 auto pv = vertex;
4375 for (int i = 1; i< vertexnum; i++){
4376 vertex = std::make_shared<DRW_Vertex2D>();
4377 if (version < DRW::AC1015) {//14-
4378 vertex->x = buf->getRawDouble();
4379 vertex->y = buf->getRawDouble();
4380 } else {
4381// DRW_Vertex2D *pv = vertlist.back();
4382 vertex->x = buf->getDefaultDouble(pv->x);
4383 vertex->y = buf->getDefaultDouble(pv->y);
4384 }
4385 pv = vertex;
4386 vertlist.push_back(vertex);
4387 }
4388 //add bulges
4389 for (unsigned int i = 0; i < bulgesnum; i++){
4390 double bulge = buf->getBitDouble();
4391 if (vertlist.size()> i)
4392 vertlist.at(i)->bulge = bulge;
4393 }
4394 //add vertexId
4395 if (version > DRW::AC1021) {//2010+
4396 for (int i = 0; i < vertexIdCount; i++){
4397 std::int32_t vertexId = buf->getBitLong();
4398 if (static_cast<size_t>(i) < vertlist.size())
4399 vertlist.at(i)->identifier = vertexId;
4400 }
4401 }
4402 //add widths
4403 for (unsigned int i = 0; i < widthsnum; i++){
4404 double staW = buf->getBitDouble();
4405 double endW = buf->getBitDouble();
4406 if (i < vertlist.size()) {
4407 vertlist.at(i)->stawidth = staW;
4408 vertlist.at(i)->endwidth = endW;
4409 }
4410 }
4411 }
4412 if (DRW_DBGGLDRW_dbg::getInstance()->getLevel() == DRW_dbg::Level::Debug){
4413 DRW_DBG("\nVertex list: ")DRW_dbg::dbg("\nVertex list: ");
4414 for (auto& pv: vertlist) {
4415 DRW_DBG("\n x: ")DRW_dbg::dbg("\n x: "); DRW_DBG(pv->x)DRW_dbg::dbg(pv->x); DRW_DBG(" y: ")DRW_dbg::dbg(" y: "); DRW_DBG(pv->y)DRW_dbg::dbg(pv->y); DRW_DBG(" bulge: ")DRW_dbg::dbg(" bulge: "); DRW_DBG(pv->bulge)DRW_dbg::dbg(pv->bulge);
4416 DRW_DBG(" stawidth: ")DRW_dbg::dbg(" stawidth: "); DRW_DBG(pv->stawidth)DRW_dbg::dbg(pv->stawidth); DRW_DBG(" endwidth: ")DRW_dbg::dbg(" endwidth: "); DRW_DBG(pv->endwidth)DRW_dbg::dbg(pv->endwidth);
4417 DRW_DBG(" identifier: ")DRW_dbg::dbg(" identifier: "); DRW_DBG(pv->identifier)DRW_dbg::dbg(pv->identifier);
4418 }
4419 }
4420
4421 DRW_DBG("\n")DRW_dbg::dbg("\n");
4422 /* Common Entity Handle Data */
4423 ret = DRW_Entity::parseDwgEntHandle(version, buf);
4424 if (!ret)
4425 return ret;
4426 /* CRC X --- */
4427 return buf->isGood();
4428}
4429
4430
4431// ----------------------------------------------------------------------------
4432// DRW_MLine — multiline entity (ODA §19.4.78, fixed type 0x2F = 47).
4433// ----------------------------------------------------------------------------
4434
4435bool DRW_MLine::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
4436 switch (code) {
4437 case 2:
4438 styleName = reader->getString();
4439 break;
4440 case 340:
4441 styleHandle = static_cast<std::uint32_t>(reader->getHandleString());
4442 break;
4443 case 40:
4444 scale = reader->getDouble();
4445 break;
4446 case 70:
4447 justification = static_cast<std::uint8_t>(reader->getInt32() & 0x3);
4448 break;
4449 case 71:
4450 openClosed = reader->getInt32();
4451 break;
4452 case 72:
4453 numVerts = static_cast<std::uint16_t>(reader->getInt32());
4454 break;
4455 case 73:
4456 numLines = static_cast<std::uint8_t>(reader->getInt32());
4457 break;
4458 case 10:
4459 basePoint.x = reader->getDouble();
4460 break;
4461 case 20:
4462 basePoint.y = reader->getDouble();
4463 break;
4464 case 30:
4465 basePoint.z = reader->getDouble();
4466 break;
4467 case 210:
4468 extPoint.x = reader->getDouble();
4469 break;
4470 case 220:
4471 extPoint.y = reader->getDouble();
4472 break;
4473 case 230:
4474 extPoint.z = reader->getDouble();
4475 break;
4476 // Per-vertex block: code 11 starts a new vertex; 12/13 follow.
4477 case 11:
4478 ++m_currentVertexIdx;
4479 m_currentElementIdx = 0;
4480 if (m_currentVertexIdx >= static_cast<int>(vertlist.size())) {
4481 vertlist.resize(m_currentVertexIdx + 1);
4482 }
4483 vertlist[m_currentVertexIdx].position.x = reader->getDouble();
4484 break;
4485 case 21:
4486 if (m_currentVertexIdx >= 0)
4487 vertlist[m_currentVertexIdx].position.y = reader->getDouble();
4488 break;
4489 case 31:
4490 if (m_currentVertexIdx >= 0)
4491 vertlist[m_currentVertexIdx].position.z = reader->getDouble();
4492 break;
4493 case 12:
4494 if (m_currentVertexIdx >= 0)
4495 vertlist[m_currentVertexIdx].vertexDir.x = reader->getDouble();
4496 break;
4497 case 22:
4498 if (m_currentVertexIdx >= 0)
4499 vertlist[m_currentVertexIdx].vertexDir.y = reader->getDouble();
4500 break;
4501 case 32:
4502 if (m_currentVertexIdx >= 0)
4503 vertlist[m_currentVertexIdx].vertexDir.z = reader->getDouble();
4504 break;
4505 case 13:
4506 if (m_currentVertexIdx >= 0)
4507 vertlist[m_currentVertexIdx].miterDir.x = reader->getDouble();
4508 break;
4509 case 23:
4510 if (m_currentVertexIdx >= 0)
4511 vertlist[m_currentVertexIdx].miterDir.y = reader->getDouble();
4512 break;
4513 case 33:
4514 if (m_currentVertexIdx >= 0)
4515 vertlist[m_currentVertexIdx].miterDir.z = reader->getDouble();
4516 break;
4517 // 74 = segment-param count for current element. Sets up the inner
4518 // vector and resets the running param count. 41 reads each param.
4519 // 75 = fill-param count; 42 reads each. After fills are consumed,
4520 // advance to the next element. AutoCAD emits 74/41*/75/42* per element.
4521 case 74:
4522 if (m_currentVertexIdx >= 0) {
4523 auto& v = vertlist[m_currentVertexIdx];
4524 if (static_cast<int>(v.segParms.size()) < numLines) {
4525 v.segParms.resize(numLines);
4526 v.areaFillParms.resize(numLines);
4527 }
4528 (void)reader->getInt32(); // expected count, used only as a marker
4529 m_currentSegFillCount = 0;
4530 }
4531 break;
4532 case 41:
4533 if (m_currentVertexIdx >= 0
4534 && m_currentElementIdx < static_cast<int>(vertlist[m_currentVertexIdx].segParms.size())) {
4535 vertlist[m_currentVertexIdx].segParms[m_currentElementIdx]
4536 .push_back(reader->getDouble());
4537 }
4538 break;
4539 case 75:
4540 if (m_currentVertexIdx >= 0) {
4541 m_currentSegFillCount = reader->getInt32();
4542 // After fills are emitted (or count==0 immediate), advance element.
4543 if (m_currentSegFillCount == 0
4544 && m_currentElementIdx + 1 < numLines) {
4545 ++m_currentElementIdx;
4546 }
4547 }
4548 break;
4549 case 42:
4550 if (m_currentVertexIdx >= 0
4551 && m_currentElementIdx < static_cast<int>(vertlist[m_currentVertexIdx].areaFillParms.size())) {
4552 vertlist[m_currentVertexIdx].areaFillParms[m_currentElementIdx]
4553 .push_back(reader->getDouble());
4554 if (static_cast<int>(vertlist[m_currentVertexIdx]
4555 .areaFillParms[m_currentElementIdx].size())
4556 >= m_currentSegFillCount
4557 && m_currentElementIdx + 1 < numLines) {
4558 ++m_currentElementIdx;
4559 }
4560 }
4561 break;
4562 default:
4563 return DRW_Entity::parseCode(code, reader);
4564 }
4565 return true;
4566}
4567
4568bool DRW_MLine::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
4569 if (!DRW_Entity::parseDwg(version, buf, nullptr, bs)) return false;
4570 DRW_DBG("\n***************************** parsing MLINE *********************\n")DRW_dbg::dbg("\n***************************** parsing MLINE *********************\n"
)
;
4571 // Per ODA §19.4.78 / libreDWG dwg_decode_MLINE:
4572 // BD scale, RC justification, 3BD basePoint, BE extrusion,
4573 // BS open/closed flag, RC num_lines, BS num_verts,
4574 // then per-vertex: 3BD pos, 3BD vdir, 3BD mdir,
4575 // per-line: BS num_segparms × BD parm, BS num_areafillparms × BD parm.
4576 scale = buf->getBitDouble();
4577 justification = buf->getRawChar8();
4578 basePoint = buf->get3BitDouble();
4579 extPoint = buf->getExtrusion(false);
4580 openClosed = buf->getBitShort();
4581 numLines = buf->getRawChar8();
4582 numVerts = buf->getBitShort();
4583 DRW_DBG(" mline scale: ")DRW_dbg::dbg(" mline scale: "); DRW_DBG(scale)DRW_dbg::dbg(scale);
4584 DRW_DBG(" just: ")DRW_dbg::dbg(" just: "); DRW_DBG(static_cast<int>(justification))DRW_dbg::dbg(static_cast<int>(justification));
4585 DRW_DBG(" openClosed: ")DRW_dbg::dbg(" openClosed: "); DRW_DBG(openClosed)DRW_dbg::dbg(openClosed);
4586 DRW_DBG(" lines: ")DRW_dbg::dbg(" lines: "); DRW_DBG(static_cast<int>(numLines))DRW_dbg::dbg(static_cast<int>(numLines));
4587 DRW_DBG(" verts: ")DRW_dbg::dbg(" verts: "); DRW_DBG(numVerts)DRW_dbg::dbg(numVerts); DRW_DBG("\n")DRW_dbg::dbg("\n");
4588 // Sanity: numLines / numVerts are RC and BS so already small types,
4589 // but guard against pathological values anyway.
4590 if (numLines > 100) return true;
4591 vertlist.reserve(numVerts);
4592 for (int vi = 0; vi < numVerts; ++vi) {
4593 DRW_MLineVertex vtx;
4594 vtx.position = buf->get3BitDouble();
4595 vtx.vertexDir = buf->get3BitDouble();
4596 vtx.miterDir = buf->get3BitDouble();
4597 vtx.segParms.resize(numLines);
4598 vtx.areaFillParms.resize(numLines);
4599 for (int li = 0; li < numLines; ++li) {
4600 std::uint16_t nSeg = buf->getBitShort();
4601 vtx.segParms[li].reserve(nSeg);
4602 for (int s = 0; s < nSeg; ++s) {
4603 vtx.segParms[li].push_back(buf->getBitDouble());
4604 }
4605 std::uint16_t nFill = buf->getBitShort();
4606 vtx.areaFillParms[li].reserve(nFill);
4607 for (int f = 0; f < nFill; ++f) {
4608 vtx.areaFillParms[li].push_back(buf->getBitDouble());
4609 }
4610 }
4611 vertlist.push_back(std::move(vtx));
4612 }
4613 if (!DRW_Entity::parseDwgEntHandle(version, buf)) return false;
4614 // MLINE has one extra handle in the handle stream after the standard
4615 // entity handles: the MLINESTYLE reference. Read if available — some
4616 // older files (R14) store the style name inline instead.
4617 if (version > DRW::AC1014 && buf->numRemainingBytes() > 0) {
4618 dwgHandle styleH = buf->getOffsetHandle(handle);
4619 styleHandle = styleH.ref;
4620 DRW_DBG(" MLINE style handle: ")DRW_dbg::dbg(" MLINE style handle: ");
4621 DRW_DBGHL(styleH.code, styleH.size, styleH.ref)DRW_dbg::dbgHL(styleH.code, styleH.size, styleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
4622 }
4623 return buf->isGood();
4624}
4625
4626
4627// ----------------------------------------------------------------------------
4628// DRW_Underlay — UNDERLAY entity (PDFUNDERLAY/DGNUNDERLAY/DWFUNDERLAY).
4629// libreDWG UNDERLAYREFERENCE.spec field order:
4630// extrusion (BE) -> position (3BD) -> angle (BD radians) -> scale (3BD)
4631// -> flags (RC) -> contrast (RC) -> fade (RC) -> num_clip (BL)
4632// -> clip_verts (2RD × num_clip).
4633// Handle stream after standard entity handles: definition_id (H).
4634// ----------------------------------------------------------------------------
4635
4636bool DRW_Underlay::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
4637 switch (code) {
4638 case 340:
4639 definitionHandle = static_cast<std::uint32_t>(reader->getHandleString());
4640 break;
4641 case 10: position.x = reader->getDouble(); break;
4642 case 20: position.y = reader->getDouble(); break;
4643 case 30: position.z = reader->getDouble(); break;
4644 case 41: scale.x = reader->getDouble(); break;
4645 case 42: scale.y = reader->getDouble(); break;
4646 case 43: scale.z = reader->getDouble(); break;
4647 case 50: rotation = reader->getDouble(); break; // degrees in DXF
4648 case 210: extPoint.x = reader->getDouble(); break;
4649 case 220: extPoint.y = reader->getDouble(); break;
4650 case 230: extPoint.z = reader->getDouble(); break;
4651 case 280: flags = static_cast<std::uint8_t>(reader->getInt32() & 0xFF); break;
4652 case 281: contrast = static_cast<std::uint8_t>(reader->getInt32() & 0xFF); break;
4653 case 282: fade = static_cast<std::uint8_t>(reader->getInt32() & 0xFF); break;
4654 case 11: {
4655 ++m_currentClipVertexIdx;
4656 if (m_currentClipVertexIdx >= static_cast<int>(clipBoundary.size())) {
4657 clipBoundary.resize(m_currentClipVertexIdx + 1);
4658 }
4659 clipBoundary[m_currentClipVertexIdx].x = reader->getDouble();
4660 break;
4661 }
4662 case 21:
4663 if (m_currentClipVertexIdx >= 0
4664 && m_currentClipVertexIdx < static_cast<int>(clipBoundary.size())) {
4665 clipBoundary[m_currentClipVertexIdx].y = reader->getDouble();
4666 }
4667 break;
4668 default:
4669 return DRW_Entity::parseCode(code, reader);
4670 }
4671 return true;
4672}
4673
4674bool DRW_Underlay::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
4675 if (!DRW_Entity::parseDwg(version, buf, nullptr, bs)) return false;
4676 DRW_DBG("\n***************************** parsing UNDERLAY ***************\n")DRW_dbg::dbg("\n***************************** parsing UNDERLAY ***************\n"
)
;
4677 extPoint = buf->getExtrusion(false);
4678 position = buf->get3BitDouble();
4679 rotation = buf->getBitDouble(); // angle (radians) BEFORE scale
4680 scale = buf->get3BitDouble();
4681 flags = buf->getRawChar8();
4682 contrast = buf->getRawChar8();
4683 fade = buf->getRawChar8();
4684 std::uint32_t nClip = buf->getBitLong();
4685 DRW_DBG(" UNDERLAY pos: ")DRW_dbg::dbg(" UNDERLAY pos: "); DRW_DBG(position.x)DRW_dbg::dbg(position.x); DRW_DBG(",")DRW_dbg::dbg(",");
4686 DRW_DBG(position.y)DRW_dbg::dbg(position.y); DRW_DBG(" rot: ")DRW_dbg::dbg(" rot: "); DRW_DBG(rotation)DRW_dbg::dbg(rotation);
4687 DRW_DBG(" flags: ")DRW_dbg::dbg(" flags: "); DRW_DBGH(flags)DRW_dbg::dbgH(flags);
4688 DRW_DBG(" nClip: ")DRW_dbg::dbg(" nClip: "); DRW_DBG(nClip)DRW_dbg::dbg(nClip); DRW_DBG("\n")DRW_dbg::dbg("\n");
4689 if (nClip > 100000) return true; // sanity
4690 clipBoundary.reserve(nClip);
4691 for (std::uint32_t i = 0; i < nClip; ++i) {
4692 DRW_Coord p;
4693 p.x = buf->getRawDouble();
4694 p.y = buf->getRawDouble();
4695 p.z = 0.0;
4696 clipBoundary.push_back(p);
4697 }
4698 if (!DRW_Entity::parseDwgEntHandle(version, buf)) return false;
4699 if (version > DRW::AC1014 && buf->numRemainingBytes() >= 2) {
4700 dwgHandle defH = buf->getOffsetHandle(handle);
4701 definitionHandle = defH.ref;
4702 DRW_DBG(" UNDERLAY definitionHandle: ")DRW_dbg::dbg(" UNDERLAY definitionHandle: ");
4703 DRW_DBGHL(defH.code, defH.size, defH.ref)DRW_dbg::dbgHL(defH.code, defH.size, defH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
4704 }
4705 return buf->isGood();
4706}
4707
4708bool DRW_Underlay::encodeDwg(DRW::Version version, dwgBufferW *buf,
4709 std::uint32_t bs, dwgBufferW *strBuf,
4710 dwgBufferW *handleBuf) {
4711 (void)bs; (void)strBuf;
4712 switch (kind) {
4713 case DGN:
4714 oType = kDwgClassNumDgn;
4715 break;
4716 case DWF:
4717 oType = kDwgClassNumDwf;
4718 break;
4719 case PDF:
4720 default:
4721 oType = kDwgClassNumPdf;
4722 break;
4723 }
4724 if (!encodeDwgCommon(version, buf))
4725 return false;
4726
4727 buf->putExtrusion(extPoint, false);
4728 buf->put3BitDouble(position);
4729 buf->putBitDouble(rotation);
4730 buf->put3BitDouble(scale);
4731 buf->putRawChar8(flags);
4732 buf->putRawChar8(contrast);
4733 buf->putRawChar8(fade);
4734 constexpr std::size_t kMaxClipVerts = 100000u;
4735 const std::size_t emitVerts = std::min(clipBoundary.size(), kMaxClipVerts);
4736 buf->putBitLong(static_cast<std::int32_t>(emitVerts));
4737 for (std::size_t i = 0; i < emitVerts; ++i) {
4738 buf->putRawDouble(clipBoundary[i].x);
4739 buf->putRawDouble(clipBoundary[i].y);
4740 }
4741
4742 if (!encodeDwgEntHandle(version, buf, handleBuf))
4743 return false;
4744 putNullableHardPointerHandle(handleBuf ? handleBuf : buf, definitionHandle);
4745 return true;
4746}
4747
4748
4749bool DRW_Text::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
4750 switch (code) {
4751 case 40:
4752 height = reader->getDouble();
4753 break;
4754 case 41:
4755 widthscale = reader->getDouble();
4756 break;
4757 case 50:
4758 angle = reader->getDouble();
4759 break;
4760 case 51:
4761 oblique = reader->getDouble();
4762 break;
4763 case 71:
4764 textgen = reader->getInt32();
4765 break;
4766 case 72:
4767 alignH = (HAlign)reader->getInt32();
4768 break;
4769 case 73:
4770 alignV = (VAlign)reader->getInt32();
4771 break;
4772 case 1:
4773 text = reader->getUtf8String();
4774 break;
4775 case 7:
4776 style = reader->getUtf8String();
4777 break;
4778 default:
4779 return DRW_Line::parseCode(code, reader);
4780 }
4781
4782 return true;
4783}
4784
4785bool DRW_Text::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
4786 dwgBuffer sBuff = *buf;
4787 dwgBuffer *sBuf = buf;
4788 if (version > DRW::AC1018) {//2007+
4789 sBuf = &sBuff; //separate buffer for strings
4790 }
4791 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
4792 if (!ret)
4793 return ret;
4794 DRW_DBG("\n***************************** parsing text *********************************************\n")DRW_dbg::dbg("\n***************************** parsing text *********************************************\n"
)
;
4795
4796 // DataFlags RC Used to determine presence of subsequent data, set to 0xFF for R14-
4797 std::uint8_t data_flags = 0x00;
4798 if (version > DRW::AC1014) {//2000+
4799 data_flags = buf->getRawChar8(); /* DataFlags RC Used to determine presence of subsequent data */
4800 DRW_DBG("data_flags: ")DRW_dbg::dbg("data_flags: "); DRW_DBG(data_flags)DRW_dbg::dbg(data_flags); DRW_DBG("\n")DRW_dbg::dbg("\n");
4801 if ( !(data_flags & 0x01) ) { /* Elevation RD --- present if !(DataFlags & 0x01) */
4802 basePoint.z = buf->getRawDouble();
4803 }
4804 } else {//14-
4805 basePoint.z = buf->getBitDouble(); /* Elevation BD --- */
4806 }
4807 basePoint.x = buf->getRawDouble(); /* Insertion pt 2RD 10 */
4808 basePoint.y = buf->getRawDouble();
4809 DRW_DBG("Insert point: ")DRW_dbg::dbg("Insert point: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
4810 if (version > DRW::AC1014) {//2000+
4811 if ( !(data_flags & 0x02) ) { /* Alignment pt 2DD 11 present if !(DataFlags & 0x02), use 10 & 20 values for 2 default values.*/
4812 secPoint.x = buf->getDefaultDouble(basePoint.x);
4813 secPoint.y = buf->getDefaultDouble(basePoint.y);
4814 } else {
4815 secPoint = basePoint;
4816 }
4817 } else {//14-
4818 secPoint.x = buf->getRawDouble(); /* Alignment pt 2RD 11 */
4819 secPoint.y = buf->getRawDouble();
4820 }
4821 secPoint.z = basePoint.z;
4822 DRW_DBG("Alignment: ")DRW_dbg::dbg("Alignment: "); DRW_DBGPT(secPoint.x, secPoint.y, basePoint.z)DRW_dbg::dbgPT(secPoint.x, secPoint.y, basePoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
4823 extPoint = buf->getExtrusion(version > DRW::AC1014);
4824 DRW_DBG("Extrusion: ")DRW_dbg::dbg("Extrusion: "); DRW_DBGPT(extPoint.x, extPoint.y, extPoint.z)DRW_dbg::dbgPT(extPoint.x, extPoint.y, extPoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
4825 thickness = buf->getThickness(version > DRW::AC1014); /* Thickness BD 39 */
4826
4827 if (version > DRW::AC1014) {//2000+
4828 if ( !(data_flags & 0x04) ) { /* Oblique ang RD 51 present if !(DataFlags & 0x04) */
4829 oblique = buf->getRawDouble();
4830 }
4831 if ( !(data_flags & 0x08) ) { /* Rotation ang RD 50 present if !(DataFlags & 0x08) */
4832 angle = buf->getRawDouble();
4833 }
4834 height = buf->getRawDouble(); /* Height RD 40 */
4835 if ( !(data_flags & 0x10) ) { /* Width factor RD 41 present if !(DataFlags & 0x10) */
4836 widthscale = buf->getRawDouble();
4837 }
4838 } else {//14-
4839 oblique = buf->getBitDouble(); /* Oblique ang BD 51 */
4840 angle = buf->getBitDouble(); /* Rotation ang BD 50 */
4841 height = buf->getBitDouble(); /* Height BD 40 */
4842 widthscale = buf->getBitDouble(); /* Width factor BD 41 */
4843 }
4844 angle *= ARAD57.29577951308232;
4845 DRW_DBG("thickness: ")DRW_dbg::dbg("thickness: "); DRW_DBG(thickness)DRW_dbg::dbg(thickness); DRW_DBG(", Oblique ang: ")DRW_dbg::dbg(", Oblique ang: "); DRW_DBG(oblique)DRW_dbg::dbg(oblique); DRW_DBG(", Width: ")DRW_dbg::dbg(", Width: ");
4846 DRW_DBG(widthscale)DRW_dbg::dbg(widthscale); DRW_DBG(", Rotation: ")DRW_dbg::dbg(", Rotation: "); DRW_DBG(angle)DRW_dbg::dbg(angle); DRW_DBG(", height: ")DRW_dbg::dbg(", height: "); DRW_DBG(height)DRW_dbg::dbg(height); DRW_DBG("\n")DRW_dbg::dbg("\n");
4847 text = sBuf->getVariableText(version, false); /* Text value TV 1 */
4848 DRW_DBG("text string: ")DRW_dbg::dbg("text string: "); DRW_DBG(text.c_str())DRW_dbg::dbg(text.c_str());DRW_DBG("\n")DRW_dbg::dbg("\n");
4849 //textgen, alignH, alignV always present in R14-, data_flags set in initialisation
4850 if ( !(data_flags & 0x20) ) { /* Generation BS 71 present if !(DataFlags & 0x20) */
4851 textgen = buf->getBitShort();
4852 DRW_DBG("textgen: ")DRW_dbg::dbg("textgen: "); DRW_DBG(textgen)DRW_dbg::dbg(textgen);
4853 }
4854 if ( !(data_flags & 0x40) ) { /* Horiz align. BS 72 present if !(DataFlags & 0x40) */
4855 alignH = (HAlign)buf->getBitShort();
4856 DRW_DBG(", alignH: ")DRW_dbg::dbg(", alignH: "); DRW_DBG(alignH)DRW_dbg::dbg(alignH);
4857 }
4858 if ( !(data_flags & 0x80) ) { /* Vert align. BS 73 present if !(DataFlags & 0x80) */
4859 alignV = (VAlign)buf->getBitShort();
4860 DRW_DBG(", alignV: ")DRW_dbg::dbg(", alignV: "); DRW_DBG(alignV)DRW_dbg::dbg(alignV);
4861 }
4862 DRW_DBG("\n")DRW_dbg::dbg("\n");
4863
4864 /* Common Entity Handle Data */
4865 ret = DRW_Entity::parseDwgEntHandle(version, buf);
4866 if (!ret)
4867 return ret;
4868
4869 styleH = buf->getHandle(); /* H 7 STYLE (hard pointer) */
4870 DRW_DBG("text style Handle: ")DRW_dbg::dbg("text style Handle: "); DRW_DBGHL(styleH.code, styleH.size, styleH.ref)DRW_dbg::dbgHL(styleH.code, styleH.size, styleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
4871
4872 /* CRC X --- */
4873 return buf->isGood();
4874}
4875
4876// ---------------------------------------------------------------------------
4877// RTEXT (RText, Express Tools) — read-only, mapped onto DRW_Text.
4878// ---------------------------------------------------------------------------
4879bool DRW_RText::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
4880 // RTEXT's DXF layout is a TEXT subset (1 text, 7 style, 10/20/30 insertion,
4881 // 40 height, 50 rotation deg, 210/220/230 extrusion) plus a flags long (70)
4882 // that plain TEXT does not carry.
4883 if (70 == code) {
4884 m_rTextFlags = reader->getInt32();
4885 return true;
4886 }
4887 return DRW_Text::parseCode(code, reader);
4888}
4889
4890bool DRW_RText::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
4891 dwgBuffer sBuff = *buf;
4892 dwgBuffer *sBuf = buf;
4893 if (version > DRW::AC1018) // 2007+ strings live in a separate stream
4894 sBuf = &sBuff;
4895 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
4896 if (!ret)
4897 return ret;
4898 DRW_DBG("\n***************************** parsing rtext ********************************************\n")DRW_dbg::dbg("\n***************************** parsing rtext ********************************************\n"
)
;
4899
4900 basePoint = buf->get3BitDouble(); // insertion 3BD
4901 secPoint = basePoint; // no separate alignment point
4902 extPoint = buf->get3BitDouble(); // extrusion 3BD
4903 angle = buf->getBitDouble() * ARAD57.29577951308232; // rotation BD (radians) -> degrees
4904 height = buf->getBitDouble(); // height BD
4905 m_rTextFlags = buf->getBitShort(); // flags BS
4906 text = sBuf->getVariableText(version, false); // TV (DIESEL or literal)
4907 DRW_DBG("rtext string: ")DRW_dbg::dbg("rtext string: "); DRW_DBG(text.c_str())DRW_dbg::dbg(text.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
4908
4909 ret = DRW_Entity::parseDwgEntHandle(version, buf);
4910 if (!ret)
4911 return ret;
4912 styleH = buf->getHandle(); // STYLE (hard pointer)
4913 return buf->isGood();
4914}
4915
4916// ---------------------------------------------------------------------------
4917// ARCALIGNEDTEXT (AcDbArcAlignedText, Express Tools) — read-only, mapped onto
4918// DRW_Text as a 2D approximation (text at the arc mid-point, tangent baseline).
4919// ---------------------------------------------------------------------------
4920bool DRW_RText::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
4921 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
4922 (void)bs;
4923 oType = kDwgClassNum;
4924 if (!encodeDwgCommon(version, buf)) return false;
4925
4926 buf->put3BitDouble(basePoint);
4927 buf->put3BitDouble(extPoint);
4928 buf->putBitDouble(angle / ARAD57.29577951308232);
4929 buf->putBitDouble(height);
4930 buf->putBitShort(bitShortFromInt(m_rTextFlags));
4931 (strBuf ? strBuf : buf)->putVariableText(version, text);
4932
4933 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
4934 putHardPointerHandle(handleBuf ? handleBuf : buf,
4935 (styleH.ref == 0) ? 0x13 : styleH.ref);
4936 return true;
4937}
4938
4939void DRW_ArcAlignedText::applyArcApproximation(){
4940 const double mid = 0.5 * (m_startAngle + m_endAngle);
4941 basePoint.x = m_center.x + m_radius * std::cos(mid);
4942 basePoint.y = m_center.y + m_radius * std::sin(mid);
4943 basePoint.z = m_center.z;
4944 secPoint = basePoint;
4945 // Baseline tangent to the arc at the mid-point; angle stored in degrees to
4946 // match DRW_Text (which the DWG path fills via `angle *= ARAD`).
4947 angle = (mid + M_PI_21.57079632679489661923) * ARAD57.29577951308232;
4948 // Height from the text-size D2T string when parseable, else a fraction of
4949 // the radius so the approximation is at least visible.
4950 double h = 0.0;
4951 try { h = std::stod(m_textSize); } catch (...) { h = 0.0; }
4952 if (h > 0.0)
4953 height = h;
4954 else if (height <= 0.0)
4955 height = 0.1 * m_radius;
4956}
4957
4958// Format a D2T (double-to-text) field the way the ARCALIGNEDTEXT model stores
4959// it: the DWG body carries these as text ("2.5", "1", "0"), while the DXF path
4960// reads them as doubles (group codes 41-46 fall in the double range, so the
4961// reader populates doubleData and leaves strData stale — getString() is unsafe
4962// here). %g reproduces the same compact textual form.
4963static std::string arcAlignedD2T(double v){
4964 char buf[32];
4965 std::snprintf(buf, sizeof(buf), "%g", v);
4966 return std::string(buf);
4967}
4968
4969static std::string arcAlignedStringOrDefault(const UTF8STRINGstd::string& value,
4970 const std::string& fallback) {
4971 return value.empty() ? fallback : value;
4972}
4973
4974bool DRW_ArcAlignedText::encodeDwg(DRW::Version version, dwgBufferW *buf,
4975 std::uint32_t bs, dwgBufferW *strBuf,
4976 dwgBufferW *handleBuf) {
4977 (void)bs;
4978 oType = kDwgClassNum;
4979 if (!encodeDwgCommon(version, buf)) return false;
4980
4981 dwgBufferW *sb = strBuf ? strBuf : buf;
4982 sb->putVariableText(version, arcAlignedStringOrDefault(
4983 m_textSize, arcAlignedD2T(height > 0.0 ? height : 0.0)));
4984 sb->putVariableText(version, arcAlignedStringOrDefault(
4985 m_xScale, arcAlignedD2T(widthscale > 0.0 ? widthscale : 1.0)));
4986 sb->putVariableText(version, arcAlignedStringOrDefault(m_charSpacing, "1"));
4987 sb->putVariableText(version, style.empty() ? "Standard" : style);
4988 sb->putVariableText(version, m_fontName);
4989 sb->putVariableText(version, m_bigFontName);
4990 sb->putVariableText(version, text);
4991 sb->putVariableText(version, arcAlignedStringOrDefault(m_offsetFromArc, "0"));
4992 sb->putVariableText(version, arcAlignedStringOrDefault(m_rightOffset, "0"));
4993 sb->putVariableText(version, arcAlignedStringOrDefault(m_leftOffset, "0"));
4994
4995 buf->put3BitDouble(m_center);
4996 buf->putBitDouble(m_radius);
4997 buf->putBitDouble(m_startAngle);
4998 buf->putBitDouble(m_endAngle);
4999 buf->put3BitDouble(extPoint);
5000 buf->putBitLong(static_cast<std::int32_t>(m_rawColor));
5001 buf->putBitShort(bitShortFromInt(m_characterSet));
5002 buf->putBitShort(bitShortFromInt(m_pitchAndFamily));
5003 buf->putBitShort(bitShortFromInt(m_isShx));
5004 buf->putBitShort(bitShortFromInt(m_isBold));
5005 buf->putBitShort(bitShortFromInt(m_isItalic));
5006 buf->putBitShort(bitShortFromInt(m_isUnderlined));
5007 buf->putBitShort(bitShortFromInt(m_alignment));
5008 buf->putBitShort(bitShortFromInt(m_isReverse));
5009 buf->putBitShort(bitShortFromInt(m_wizardFlag));
5010 buf->putBitShort(bitShortFromInt(m_textPosition));
5011 buf->putBitShort(bitShortFromInt(m_textDirection));
5012
5013 if (version <= DRW::AC1018)
5014 putNullableHardPointerHandle(buf, m_arcHandle);
5015 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
5016 if (version > DRW::AC1018)
5017 putNullableHardPointerHandle(handleBuf ? handleBuf : buf, m_arcHandle);
5018 return true;
5019}
5020
5021bool DRW_ArcAlignedText::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
5022 // ARCALIGNEDTEXT repurposes several TEXT codes (2/10/40/41/50/51/70…), so it
5023 // must not delegate those to DRW_Text; unknown codes fall through to the
5024 // AcDbEntity common parser. Angles (50/51) are DXF degrees -> radians.
5025 switch (code) {
5026 case 1: text = reader->getUtf8String(); break;
5027 case 2: m_fontName = reader->getUtf8String(); break;
5028 case 3: m_bigFontName = reader->getUtf8String(); break;
5029 case 7: style = reader->getUtf8String(); break;
5030 case 10: m_center.x = reader->getDouble(); break;
5031 case 20: m_center.y = reader->getDouble(); break;
5032 case 30: m_center.z = reader->getDouble(); break;
5033 case 40: m_radius = reader->getDouble(); break;
5034 case 41: m_xScale = arcAlignedD2T(reader->getDouble()); break;
5035 case 42: m_textSize = arcAlignedD2T(reader->getDouble()); break;
5036 case 43: m_charSpacing = arcAlignedD2T(reader->getDouble()); break;
5037 case 44: m_offsetFromArc = arcAlignedD2T(reader->getDouble()); break;
5038 case 45: m_rightOffset = arcAlignedD2T(reader->getDouble()); break;
5039 case 46: m_leftOffset = arcAlignedD2T(reader->getDouble()); break;
5040 case 50: m_startAngle = reader->getDouble() / ARAD57.29577951308232; break;
5041 case 51: m_endAngle = reader->getDouble() / ARAD57.29577951308232; break;
5042 case 70: m_isReverse = reader->getInt32(); break;
5043 case 71: m_textDirection = reader->getInt32(); break;
5044 case 72: m_alignment = reader->getInt32(); break;
5045 case 73: m_textPosition = reader->getInt32(); break;
5046 case 74: m_isBold = reader->getInt32(); break;
5047 case 75: m_isItalic = reader->getInt32(); break;
5048 case 76: m_isUnderlined = reader->getInt32(); break;
5049 case 77: m_characterSet = reader->getInt32(); break;
5050 case 78: m_pitchAndFamily = reader->getInt32(); break;
5051 case 79: m_isShx = reader->getInt32(); break;
5052 case 90: m_rawColor = reader->getInt32(); break;
5053 case 210: extPoint.x = reader->getDouble(); break;
5054 case 220: extPoint.y = reader->getDouble(); break;
5055 case 230: extPoint.z = reader->getDouble(); break;
5056 case 280: m_wizardFlag = reader->getInt32(); break;
5057 default: return DRW_Entity::parseCode(code, reader);
5058 }
5059 return true;
5060}
5061
5062bool DRW_ArcAlignedText::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5063 dwgBuffer sBuff = *buf;
5064 dwgBuffer *sBuf = buf;
5065 if (version > DRW::AC1018) // 2007+ strings live in a separate stream
5066 sBuf = &sBuff;
5067 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
5068 if (!ret)
5069 return ret;
5070 DRW_DBG("\n***************************** parsing arcalignedtext **********************************\n")DRW_dbg::dbg("\n***************************** parsing arcalignedtext **********************************\n"
)
;
5071
5072 m_textSize = sBuf->getVariableText(version, false);
5073 m_xScale = sBuf->getVariableText(version, false);
5074 m_charSpacing = sBuf->getVariableText(version, false);
5075 style = sBuf->getVariableText(version, false);
5076 m_fontName = sBuf->getVariableText(version, false);
5077 m_bigFontName = sBuf->getVariableText(version, false);
5078 text = sBuf->getVariableText(version, false);
5079 m_offsetFromArc = sBuf->getVariableText(version, false);
5080 m_rightOffset = sBuf->getVariableText(version, false);
5081 m_leftOffset = sBuf->getVariableText(version, false);
5082 m_center = buf->get3BitDouble();
5083 m_radius = buf->getBitDouble();
5084 m_startAngle = buf->getBitDouble();
5085 m_endAngle = buf->getBitDouble();
5086 extPoint = buf->get3BitDouble();
5087 m_rawColor = buf->getBitLong();
5088 m_characterSet = buf->getBitShort();
5089 m_pitchAndFamily = buf->getBitShort();
5090 m_isShx = buf->getBitShort();
5091 m_isBold = buf->getBitShort();
5092 m_isItalic = buf->getBitShort();
5093 m_isUnderlined = buf->getBitShort();
5094 m_alignment = buf->getBitShort();
5095 m_isReverse = buf->getBitShort();
5096 m_wizardFlag = buf->getBitShort();
5097 m_textPosition = buf->getBitShort();
5098 m_textDirection = buf->getBitShort();
5099 DRW_DBG("arcalignedtext string: ")DRW_dbg::dbg("arcalignedtext string: "); DRW_DBG(text.c_str())DRW_dbg::dbg(text.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
5100
5101 // R2004- keeps the arc handle before the common handle stream; R2007+ after.
5102 if (version <= DRW::AC1018)
5103 m_arcHandle = (buf->numRemainingBytes() > 0) ? buf->getHandle().ref : 0;
5104
5105 ret = DRW_Entity::parseDwgEntHandle(version, buf);
5106 if (!ret)
5107 return ret;
5108 if (version > DRW::AC1018)
5109 m_arcHandle = (buf->numRemainingBytes() > 0) ? buf->getHandle().ref : 0;
5110
5111 applyArcApproximation();
5112 return buf->isGood();
5113}
5114
5115// Out-of-line special members: required because mtext is a unique_ptr<DRW_MText>
5116// declared with a forward-declared element type in the header.
5117DRW_Attrib::~DRW_Attrib() = default;
5118DRW_Attrib::DRW_Attrib(const DRW_Attrib& o)
5119 : DRW_Text(o), tag(o.tag), attribFlags(o.attribFlags),
5120 m_fieldLength(o.m_fieldLength),
5121 lockPosition(o.lockPosition), attVersion(o.attVersion),
5122 m_attributeType(o.m_attributeType),
5123 mtext(o.mtext ? std::make_unique<DRW_MText>(*o.mtext) : nullptr) {}
5124DRW_Attrib& DRW_Attrib::operator=(const DRW_Attrib& o) {
5125 if (this != &o) {
5126 DRW_Text::operator=(o);
5127 tag = o.tag;
5128 attribFlags = o.attribFlags;
5129 m_fieldLength = o.m_fieldLength;
5130 lockPosition = o.lockPosition;
5131 attVersion = o.attVersion;
5132 m_attributeType = o.m_attributeType;
5133 mtext = o.mtext ? std::make_unique<DRW_MText>(*o.mtext) : nullptr;
5134 }
5135 return *this;
5136}
5137DRW_Attrib::DRW_Attrib(DRW_Attrib&&) noexcept = default;
5138DRW_Attrib& DRW_Attrib::operator=(DRW_Attrib&&) noexcept = default;
5139
5140namespace {
5141struct EmbeddedMTextHandleInfo {
5142 bool m_ownerHandle = false;
5143 int m_numReactors = 0;
5144 std::uint8_t m_xDictFlag = 1;
5145 bool m_hasAcDbColorHandle = false;
5146 int m_ltFlags = 0;
5147 int m_plotFlags = 0;
5148 int m_materialFlag = 0;
5149 int m_shadowFlag = 0;
5150 bool m_hasFullVisualStyle = false;
5151 bool m_hasFaceVisualStyle = false;
5152 bool m_hasEdgeVisualStyle = false;
5153 bool m_hasStyleHandle = true;
5154 bool m_hasR2018AppIdHandle = false;
5155 bool m_hasAnnotativeAppHandle = false;
5156};
5157
5158static bool parseEmbeddedMTextEntityMode(DRW::Version version, dwgBuffer *buf,
5159 EmbeddedMTextHandleInfo& info) {
5160 std::uint8_t entmode = buf->get2Bits();
5161 info.m_ownerHandle = entmode == 0;
5162 info.m_numReactors = buf->getBitLong();
5163 if (version > DRW::AC1015) {
5164 info.m_xDictFlag = buf->getBit();
5165 }
5166 if (version > DRW::AC1024 || version < DRW::AC1018) {
5167 buf->getBit(); // nolinks / have-next-links
5168 }
5169 buf->getEnColor(version);
5170 info.m_hasAcDbColorHandle = buf->lastEnColorHadDbColorRef;
5171 buf->getBitDouble(); // linetype scale
5172 if (version > DRW::AC1014) {
5173 info.m_ltFlags = buf->get2Bits();
5174 info.m_plotFlags = buf->get2Bits();
5175 }
5176 if (version > DRW::AC1018) {
5177 info.m_materialFlag = buf->get2Bits();
5178 info.m_shadowFlag = buf->getRawChar8();
5179 }
5180 if (version > DRW::AC1021) {
5181 info.m_hasFullVisualStyle = buf->getBit() != 0;
5182 info.m_hasFaceVisualStyle = buf->getBit() != 0;
5183 info.m_hasEdgeVisualStyle = buf->getBit() != 0;
5184 }
5185 buf->getBitShort(); // invisibility
5186 if (version > DRW::AC1014) {
5187 buf->getRawChar8(); // lineweight
5188 }
5189 return buf->isGood();
5190}
5191
5192static bool parseEmbeddedMTextDwg(DRW::Version version, dwgBuffer *buf,
5193 dwgBuffer *sBuf, DRW_MText& mtext,
5194 EmbeddedMTextHandleInfo& info) {
5195 if (!parseEmbeddedMTextEntityMode(version, buf, info))
5196 return false;
5197
5198 mtext.basePoint = buf->get3BitDouble();
5199 mtext.extPoint = buf->get3BitDouble();
5200 mtext.secPoint = buf->get3BitDouble();
5201 mtext.angle = atan2(mtext.secPoint.y, mtext.secPoint.x) * ARAD57.29577951308232;
5202 mtext.widthscale = buf->getBitDouble();
5203 if (version > DRW::AC1018) {
5204 buf->getBitDouble(); // rect height
5205 }
5206 mtext.height = buf->getBitDouble();
5207 mtext.textgen = buf->getBitShort();
5208 mtext.alignH = static_cast<DRW_Text::HAlign>(buf->getBitShort());
5209 buf->getBitDouble(); // extents height
5210 buf->getBitDouble(); // extents width
5211 mtext.text = sBuf->getVariableText(version, false);
5212
5213 if (version > DRW::AC1014) {
5214 buf->getBitShort();
5215 mtext.interlin = buf->getBitDouble();
5216 buf->getBit();
5217 }
5218 if (version > DRW::AC1015) {
5219 mtext.m_backgroundFlags = buf->getBitLong();
5220 if ((mtext.m_backgroundFlags & 0x01)
5221 || (version >= DRW::AC1032 && (mtext.m_backgroundFlags & 0x10))) {
5222 mtext.m_backgroundScale = buf->getBitDouble(); // BitDouble, not BitLong
5223 mtext.m_backgroundColor = static_cast<int>(buf->getCmColor(version, nullptr, sBuf));
5224 mtext.m_backgroundTransparency = buf->getBitLong();
5225 }
5226 }
5227
5228 if (version >= DRW::AC1032) {
5229 mtext.m_r2018ColumnHeights.clear();
5230 mtext.m_r2018IsNotAnnotative = buf->getBit();
5231 if (mtext.m_r2018IsNotAnnotative) {
5232 mtext.m_r2018Version = buf->getBitShort();
5233 mtext.m_r2018DefaultFlag = buf->getBit();
5234 info.m_hasR2018AppIdHandle = true;
5235 mtext.m_r2018Attachment = buf->getBitLong();
5236 mtext.m_r2018XAxisDir = buf->get3BitDouble();
5237 mtext.m_r2018InsertionPoint = buf->get3BitDouble();
5238 mtext.m_r2018RectWidth = buf->getBitDouble();
5239 mtext.m_r2018RectHeight = buf->getBitDouble();
5240 mtext.m_r2018ExtentsHeight = buf->getBitDouble();
5241 mtext.m_r2018ExtentsWidth = buf->getBitDouble();
5242 mtext.m_r2018ColumnType = buf->getBitShort();
5243 if (mtext.m_r2018ColumnType != 0) {
5244 mtext.m_r2018ColumnCount = buf->getBitLong();
5245 mtext.m_r2018ColumnWidth = buf->getBitDouble();
5246 mtext.m_r2018ColumnGutter = buf->getBitDouble();
5247 mtext.m_r2018ColumnAutoHeight = buf->getBit();
5248 mtext.m_r2018ColumnFlowReversed = buf->getBit();
5249 if (!mtext.m_r2018ColumnAutoHeight && mtext.m_r2018ColumnType == 2
5250 && mtext.m_r2018ColumnCount > 0 && mtext.m_r2018ColumnCount < 10000) {
5251 mtext.m_r2018ColumnHeights.reserve(static_cast<size_t>(mtext.m_r2018ColumnCount));
5252 for (std::int32_t i = 0; i < mtext.m_r2018ColumnCount; ++i) {
5253 mtext.m_r2018ColumnHeights.push_back(buf->getBitDouble());
5254 }
5255 }
5256 }
5257 }
5258 }
5259
5260 const std::uint16_t annotativeSize = buf->getBitShort();
5261 if (annotativeSize > 0) {
5262 const int remaining = buf->numRemainingBytes();
5263 if (remaining < 0 || static_cast<std::uint64_t>(annotativeSize) > static_cast<std::uint64_t>(remaining))
5264 return false;
5265 std::vector<std::uint8_t> annotativeData(annotativeSize);
5266 buf->getBytes(annotativeData.data(), annotativeData.size());
5267 info.m_hasAnnotativeAppHandle = true;
5268 buf->getBitShort(); // unknown short, normally 0
5269 }
5270 return buf->isGood();
5271}
5272
5273static bool consumeEmbeddedMTextHandles(DRW::Version version, dwgBuffer *buf,
5274 std::uint32_t objSize,
5275 const EmbeddedMTextHandleInfo& info,
5276 DRW_MText *mtext) {
5277 if (version > DRW::AC1018) {
5278 buf->setPosition(objSize >> 3);
5279 buf->setBitPos(objSize & 7);
5280 }
5281 if (info.m_hasAcDbColorHandle) buf->getHandle();
5282 if (info.m_ownerHandle) buf->getHandle();
5283 for (int i = 0; i < info.m_numReactors; ++i) buf->getHandle();
5284 if (info.m_xDictFlag != 1) buf->getHandle();
5285 if (version > DRW::AC1014) {
5286 buf->getHandle(); // layer
5287 if (info.m_ltFlags == 3) buf->getHandle();
5288 }
5289 if (version > DRW::AC1018) {
5290 if (info.m_materialFlag == 3) buf->getHandle();
5291 if (info.m_shadowFlag == 3) buf->getHandle();
5292 }
5293 if (info.m_plotFlags == 3) buf->getHandle();
5294 if (version > DRW::AC1021) {
5295 if (info.m_hasFullVisualStyle) buf->getHandle();
5296 if (info.m_hasFaceVisualStyle) buf->getHandle();
5297 if (info.m_hasEdgeVisualStyle) buf->getHandle();
5298 }
5299 if (info.m_hasStyleHandle) {
5300 dwgHandle styleH = buf->getHandle();
5301 if (mtext) mtext->styleH = styleH;
5302 }
5303 if (info.m_hasR2018AppIdHandle) {
5304 dwgHandle appIdH = buf->getHandle();
5305 if (mtext) mtext->m_r2018AppIdHandle = appIdH.ref;
5306 }
5307 if (info.m_hasAnnotativeAppHandle) buf->getHandle();
5308 return buf->isGood();
5309}
5310
5311static bool encodeEmbeddedMTextEntityMode(DRW::Version version, dwgBufferW *buf,
5312 const DRW_MText& mtext) {
5313 if (version < DRW::AC1032)
5314 return false;
5315
5316 // Embedded MTEXT begins at AcDbEntity mode, not with an object type,
5317 // object size, own handle, EED, or graphics data.
5318 buf->put2Bits(2); // modelspace, no owner handle
5319 buf->putBitLong(0); // no reactors
5320 buf->putBit(1); // xDictFlag=1, no xdict handle
5321 buf->putBit(1); // no prev/next links
5322 buf->putEnColor(version, static_cast<std::uint16_t>(mtext.color));
5323 buf->putBitDouble(mtext.ltypeScale);
5324 buf->put2Bits(0); // linetype by layer
5325 buf->put2Bits(0); // plotstyle by layer
5326 buf->put2Bits(0); // material inherit
5327 buf->putRawChar8(0); // shadow flags
5328 buf->putBit(0); // no full visual style
5329 buf->putBit(0); // no face visual style
5330 buf->putBit(0); // no edge visual style
5331 buf->putBitShort(0); // visible
5332 buf->putRawChar8(static_cast<std::uint8_t>(mtext.lWeight));
5333 return true;
5334}
5335
5336static bool encodeEmbeddedMTextDwg(DRW::Version version, dwgBufferW *buf,
5337 dwgBufferW *strBuf, dwgBufferW *handleBuf,
5338 const DRW_MText& mtext) {
5339 if (!encodeEmbeddedMTextEntityMode(version, buf, mtext))
5340 return false;
5341
5342 buf->put3BitDouble(mtext.basePoint);
5343 buf->put3BitDouble(mtext.extPoint);
5344 buf->put3BitDouble(mtext.secPoint);
5345 buf->putBitDouble(mtext.widthscale);
5346 buf->putBitDouble(mtext.m_r2018RectHeight);
5347 buf->putBitDouble(mtext.height);
5348 buf->putBitShort(static_cast<std::uint16_t>(mtext.textgen));
5349 buf->putBitShort(static_cast<std::uint16_t>(mtext.alignH));
5350 buf->putBitDouble(mtext.m_r2018ExtentsHeight);
5351 buf->putBitDouble(mtext.m_r2018ExtentsWidth);
5352 (strBuf ? strBuf : buf)->putVariableText(version, mtext.text);
5353
5354 buf->putBitShort(0); // linespacing style
5355 buf->putBitDouble(mtext.interlin);
5356 buf->putBit(0);
5357 buf->putBitLong(mtext.m_backgroundFlags);
5358 if ((mtext.m_backgroundFlags & 0x01)
5359 || (mtext.m_backgroundFlags & 0x10)) {
5360 buf->putBitDouble(mtext.m_backgroundScale); // BitDouble, not BitLong
5361 buf->putCmColor(version, static_cast<std::uint16_t>(mtext.m_backgroundColor));
5362 buf->putBitLong(mtext.m_backgroundTransparency);
5363 }
5364
5365 buf->putBit(mtext.m_r2018IsNotAnnotative ? 1 : 0);
5366 if (mtext.m_r2018IsNotAnnotative) {
5367 buf->putBitShort(mtext.m_r2018Version);
5368 buf->putBit(mtext.m_r2018DefaultFlag ? 1 : 0);
5369 buf->putBitLong(mtext.m_r2018Attachment);
5370 buf->put3BitDouble(mtext.m_r2018XAxisDir);
5371 buf->put3BitDouble(mtext.m_r2018InsertionPoint);
5372 buf->putBitDouble(mtext.m_r2018RectWidth);
5373 buf->putBitDouble(mtext.m_r2018RectHeight);
5374 buf->putBitDouble(mtext.m_r2018ExtentsHeight);
5375 buf->putBitDouble(mtext.m_r2018ExtentsWidth);
5376 buf->putBitShort(mtext.m_r2018ColumnType);
5377 if (mtext.m_r2018ColumnType != 0) {
5378 std::int32_t columnCount = mtext.m_r2018ColumnCount;
5379 if (!mtext.m_r2018ColumnAutoHeight && mtext.m_r2018ColumnType == 2
5380 && !mtext.m_r2018ColumnHeights.empty()) {
5381 columnCount = static_cast<std::int32_t>(mtext.m_r2018ColumnHeights.size());
5382 }
5383 buf->putBitLong(columnCount);
5384 buf->putBitDouble(mtext.m_r2018ColumnWidth);
5385 buf->putBitDouble(mtext.m_r2018ColumnGutter);
5386 buf->putBit(mtext.m_r2018ColumnAutoHeight ? 1 : 0);
5387 buf->putBit(mtext.m_r2018ColumnFlowReversed ? 1 : 0);
5388 if (!mtext.m_r2018ColumnAutoHeight && mtext.m_r2018ColumnType == 2) {
5389 for (std::int32_t i = 0; i < columnCount; ++i) {
5390 const double columnHeight = static_cast<size_t>(i) < mtext.m_r2018ColumnHeights.size()
5391 ? mtext.m_r2018ColumnHeights[static_cast<size_t>(i)]
5392 : 0.0;
5393 buf->putBitDouble(columnHeight);
5394 }
5395 }
5396 }
5397 }
5398
5399 buf->putBitShort(0); // no annotative payload
5400
5401 dwgBufferW *hb = handleBuf ? handleBuf : buf;
5402 // Layer hard-pointer: consumeEmbeddedMTextHandles reads this UNCONDITIONALLY
5403 // for version > AC1014 (the entity-mode flags this encoder writes make every
5404 // other conditional embedded handle absent: no color/owner/reactor/xdict,
5405 // linetype/plotstyle/material/shadow all "by layer"). Omitting it made the
5406 // parser consume the style handle as the layer and the appId as the style,
5407 // then run into the PARENT ATTRIB/ATTDEF handle stream, shifting it. The
5408 // parser discards this value, so emit LAYER "0" (0x12) as the placeholder
5409 // (layerH is a protected DRW_Entity member, not reachable from this free
5410 // function; only the slot matters for handle-count alignment).
5411 putHardPointerHandle(hb, 0x12);
5412 putHardPointerHandle(hb, (mtext.styleH.ref == 0) ? 0x13 : mtext.styleH.ref);
5413 if (mtext.m_r2018IsNotAnnotative)
5414 putHardPointerHandle(hb, (mtext.m_r2018AppIdHandle == 0) ? 0x14 : mtext.m_r2018AppIdHandle);
5415 return true;
5416}
5417}
5418
5419bool DRW_Attrib::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
5420 // Multi-line ATTRIB (R2018+, ODA spec §20.4.4): an embedded MTEXT object
5421 // is introduced by the DXF subclass marker `100 / Embedded Object` (NOT
5422 // `AcDbMText`). After the marker, the standard MTEXT group codes follow
5423 // (10/20/30 insertion, 11/21/31 X-axis, 40 height, 41 rect width, 71
5424 // attachment point, 72 drawing direction, 1 formatted text, etc.), then
5425 // the ATTRIB-specific tail (tag=2, prompt=3 for ATTDEF, flags=70,
5426 // lock-position=280) which we must NOT route into the embedded MText.
5427 if (code == 100) {
5428 const std::string sub = reader->getString();
5429 if (sub == "Embedded Object" && !mtext) {
5430 mtext = std::make_unique<DRW_MText>();
5431 if (attVersion == 0) attVersion = 1;
5432 }
5433 return true;
5434 }
5435 // Inside the embedded MText scope, route MTEXT-owned codes to mtext but
5436 // keep ATTRIB-specific tail codes for ATTRIB / ATTDEF handling below.
5437 if (mtext) {
5438 switch (code) {
5439 case 2: // tag (ATTRIB-specific; group 1 in MText is text body)
5440 case 3: // prompt (ATTDEF-specific)
5441 case 70: // ATTRIB flags
5442 case 280: // ATTRIB lock-position
5443 break; // fall through to ATTRIB handling below
5444 default:
5445 return mtext->parseCode(code, reader);
5446 }
5447 }
5448 switch (code) {
5449 case 2:
5450 tag = reader->getUtf8String();
5451 break;
5452 case 70:
5453 attribFlags = reader->getInt32();
5454 break;
5455 case 73:
5456 // AcDbAttribute code 73 = field length (obsolete); NOT the vertical
5457 // alignment from AcDbText (which is code 73 in TEXT but 74 in ATTRIB).
5458 m_fieldLength = reader->getInt32();
5459 break;
5460 case 74:
5461 // AcDbAttribute vertical alignment (code 74); TEXT uses code 73 for
5462 // this but ATTRIB moves it here to free code 73 for field length.
5463 alignV = (VAlign)reader->getInt32();
5464 break;
5465 case 280:
5466 // Lock position flag (R2010+ DXF group code)
5467 lockPosition = reader->getInt32() != 0;
5468 break;
5469 default:
5470 return DRW_Text::parseCode(code, reader);
5471 }
5472 return true;
5473}
5474
5475bool DRW_Attrib::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5476 dwgBuffer sBuff = *buf;
5477 dwgBuffer *sBuf = buf;
5478 if (version > DRW::AC1018) {//2007+
5479 sBuf = &sBuff; //separate buffer for strings
5480 }
5481 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
5482 if (!ret)
5483 return ret;
5484 DRW_DBG("\n***************************** parsing attrib *********************************************\n")DRW_dbg::dbg("\n***************************** parsing attrib *********************************************\n"
)
;
5485
5486 // Inline TEXT subtype data (mirrors DRW_Text::parseDwg layout, sans handles)
5487 std::uint8_t data_flags = 0x00;
5488 if (version > DRW::AC1014) {
5489 data_flags = buf->getRawChar8();
5490 if (!(data_flags & 0x01)) {
5491 basePoint.z = buf->getRawDouble();
5492 }
5493 } else {
5494 basePoint.z = buf->getBitDouble();
5495 }
5496 basePoint.x = buf->getRawDouble();
5497 basePoint.y = buf->getRawDouble();
5498 DRW_DBG("Insert point: ")DRW_dbg::dbg("Insert point: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
5499 if (version > DRW::AC1014) {
5500 if (!(data_flags & 0x02)) {
5501 secPoint.x = buf->getDefaultDouble(basePoint.x);
5502 secPoint.y = buf->getDefaultDouble(basePoint.y);
5503 } else {
5504 secPoint = basePoint;
5505 }
5506 } else {
5507 secPoint.x = buf->getRawDouble();
5508 secPoint.y = buf->getRawDouble();
5509 }
5510 secPoint.z = basePoint.z;
5511 extPoint = buf->getExtrusion(version > DRW::AC1014);
5512 thickness = buf->getThickness(version > DRW::AC1014);
5513 if (version > DRW::AC1014) {
5514 if (!(data_flags & 0x04)) oblique = buf->getRawDouble();
5515 if (!(data_flags & 0x08)) angle = buf->getRawDouble();
5516 height = buf->getRawDouble();
5517 if (!(data_flags & 0x10)) widthscale = buf->getRawDouble();
5518 } else {
5519 oblique = buf->getBitDouble();
5520 angle = buf->getBitDouble();
5521 height = buf->getBitDouble();
5522 widthscale = buf->getBitDouble();
5523 }
5524 angle *= ARAD57.29577951308232;
5525 text = sBuf->getVariableText(version, false);
5526 DRW_DBG("text value: ")DRW_dbg::dbg("text value: "); DRW_DBG(text.c_str())DRW_dbg::dbg(text.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
5527 if (!(data_flags & 0x20)) textgen = buf->getBitShort();
5528 if (!(data_flags & 0x40)) alignH = (HAlign)buf->getBitShort();
5529 if (!(data_flags & 0x80)) alignV = (VAlign)buf->getBitShort();
5530
5531 // R2010+ ATTRIB version follows the common TEXT data. R2018 adds the
5532 // attribute type immediately after it.
5533 if (version >= DRW::AC1024) {
5534 attVersion = buf->getRawChar8();
5535 DRW_DBG("att version: ")DRW_dbg::dbg("att version: "); DRW_DBG(attVersion)DRW_dbg::dbg(attVersion); DRW_DBG("\n")DRW_dbg::dbg("\n");
5536 }
5537 if (version >= DRW::AC1032) {
5538 m_attributeType = buf->getRawChar8();
5539 DRW_DBG("attribute type: ")DRW_dbg::dbg("attribute type: "); DRW_DBG(m_attributeType)DRW_dbg::dbg(m_attributeType); DRW_DBG("\n")DRW_dbg::dbg("\n");
5540 }
5541
5542 bool hasEmbeddedMText = false;
5543 EmbeddedMTextHandleInfo embeddedMTextHandles;
5544 if (version >= DRW::AC1032 && m_attributeType != 0 && m_attributeType != 1) {
5545 mtext = std::make_unique<DRW_MText>();
5546 if (!parseEmbeddedMTextDwg(version, buf, sBuf, *mtext, embeddedMTextHandles)) {
5547 DRW_DBG("R2018 multi-line ATTRIB payload failed\n")DRW_dbg::dbg("R2018 multi-line ATTRIB payload failed\n");
5548 return false;
5549 }
5550 hasEmbeddedMText = true;
5551 }
5552
5553 // ATTRIB-specific fields
5554 tag = sBuf->getVariableText(version, false);
5555 DRW_DBG("attrib tag: ")DRW_dbg::dbg("attrib tag: "); DRW_DBG(tag.c_str())DRW_dbg::dbg(tag.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
5556
5557 m_fieldLength = buf->getBitShort(); /* Field length BS (obsolete, usually 0) */
5558
5559 attribFlags = buf->getRawChar8();
5560 DRW_DBG("attrib flags: ")DRW_dbg::dbg("attrib flags: "); DRW_DBG(attribFlags)DRW_dbg::dbg(attribFlags); DRW_DBG("\n")DRW_dbg::dbg("\n");
5561
5562 // lockPosition (DXF 280) appears since R2007 (AC1021) per ODA §20.4.x /
5563 // ACadSharp. Read gate lowered AC1024->AC1021 so R2007/8/9 imports keep
5564 // it. The encoder still emits it only at AC1024 (no AC1021 writer
5565 // exists), so this is read-only; parseDwgEntHandle repositions to objSize
5566 // for version>AC1018, absorbing the +1 bit without handle-stream drift.
5567 if (version >= DRW::AC1021) {
5568 lockPosition = buf->getBit();
5569 DRW_DBG("lock position: ")DRW_dbg::dbg("lock position: "); DRW_DBG(lockPosition)DRW_dbg::dbg(lockPosition); DRW_DBG("\n")DRW_dbg::dbg("\n");
5570 }
5571
5572 /* Common Entity Handle Data */
5573 if (hasEmbeddedMText
5574 && !consumeEmbeddedMTextHandles(version, buf, objSize, embeddedMTextHandles, mtext.get())) {
5575 return false;
5576 }
5577 ret = DRW_Entity::parseDwgEntHandle(version, buf, !hasEmbeddedMText);
5578 if (!ret)
5579 return ret;
5580
5581 styleH = buf->getHandle();
5582 DRW_DBG("text style Handle: ")DRW_dbg::dbg("text style Handle: "); DRW_DBGHL(styleH.code, styleH.size, styleH.ref)DRW_dbg::dbgHL(styleH.code, styleH.size, styleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
5583
5584 return buf->isGood();
5585}
5586
5587bool DRW_Attrib::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5588 (void)bs;
5589 if (version >= DRW::AC1024 && version < DRW::AC1032 && (attVersion != 0 || mtext))
5590 return false;
5591 const std::uint8_t attributeType = (m_attributeType == 0) ? 1 : m_attributeType;
5592 const bool hasEmbeddedMText = version >= DRW::AC1032 && attributeType != 1;
5593 if (hasEmbeddedMText && !mtext)
5594 return false;
5595
5596 oType = 2; // ATTRIB class id — see dwgreader.cpp:1148
5597 if (!encodeDwgCommon(version, buf)) return false;
5598
5599 // TEXT-body section — mirrors DRW_Attrib::parseDwg.
5600 // data_flags=0: emit every optional field unconditionally (same
5601 // strategy as DRW_Text::encodeDwg — simpler encoder, ~30 bytes larger).
5602 buf->putRawChar8(0); // data_flags=0
5603 buf->putRawDouble(basePoint.z); // elevation RD
5604 buf->putRawDouble(basePoint.x); // insertion 2RD
5605 buf->putRawDouble(basePoint.y);
5606 buf->putDefaultDouble(basePoint.x, secPoint.x); // alignment 2DD
5607 buf->putDefaultDouble(basePoint.y, secPoint.y);
5608 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
5609 buf->putThickness(thickness, /*b_R2000_style=*/true);
5610 buf->putRawDouble(oblique); // oblique angle RD
5611 buf->putRawDouble(angle / ARAD57.29577951308232); // angle in radians RD
5612 buf->putRawDouble(height); // text height RD
5613 buf->putRawDouble(widthscale); // width factor RD
5614 dwgBufferW *sb = strBuf ? strBuf : buf;
5615 sb->putVariableText(version, text); // text string TV
5616 buf->putBitShort(static_cast<std::uint16_t>(textgen)); // generation flags BS
5617 buf->putBitShort(static_cast<std::uint16_t>(alignH)); // horiz align BS
5618 buf->putBitShort(static_cast<std::uint16_t>(alignV)); // vert align BS
5619
5620 if (version >= DRW::AC1024) {
5621 buf->putRawChar8(hasEmbeddedMText && attVersion == 0 ? 1 : attVersion);
5622 }
5623 if (version >= DRW::AC1032) {
5624 buf->putRawChar8(attributeType);
5625 }
5626
5627 if (hasEmbeddedMText) {
5628 if (!encodeEmbeddedMTextDwg(version, buf, strBuf, handleBuf, *mtext))
5629 return false;
5630 }
5631
5632 // ATTRIB-specific tail
5633 sb->putVariableText(version, tag); // tag TV
5634 buf->putBitShort(static_cast<std::uint16_t>(m_fieldLength)); // fieldLen BS
5635 buf->putRawChar8(attribFlags); // flags RC
5636 if (version >= DRW::AC1024) {
5637 buf->putBit(lockPosition ? 1 : 0); // lock position B
5638 }
5639
5640 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
5641
5642 dwgHandle sH;
5643 std::uint32_t sref = (styleH.ref == 0) ? 0x13 : styleH.ref;
5644 sH.code = 5;
5645 sH.ref = sref;
5646 sH.size = 0;
5647 if (sref != 0) { std::uint32_t t = sref; while (t != 0) { t >>= 8; ++sH.size; } }
5648 (handleBuf ? handleBuf : buf)->putHandle(sH);
5649 return true;
5650}
5651
5652bool DRW_Attdef::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
5653 switch (code) {
5654 case 3:
5655 prompt = reader->getUtf8String();
5656 break;
5657 default:
5658 return DRW_Attrib::parseCode(code, reader);
5659 }
5660 return true;
5661}
5662
5663bool DRW_Attdef::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5664 // ATTDEF mirrors ATTRIB layout but adds a prompt string after the tag.
5665 // Implementation duplicates ATTRIB::parseDwg in order to inject the
5666 // prompt read at the correct offset; refactor opportunity if a third
5667 // sibling appears.
5668 dwgBuffer sBuff = *buf;
5669 dwgBuffer *sBuf = buf;
5670 if (version > DRW::AC1018) {
5671 sBuf = &sBuff;
5672 }
5673 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
5674 if (!ret)
5675 return ret;
5676 DRW_DBG("\n***************************** parsing attdef *********************************************\n")DRW_dbg::dbg("\n***************************** parsing attdef *********************************************\n"
)
;
5677
5678 std::uint8_t data_flags = 0x00;
5679 if (version > DRW::AC1014) {
5680 data_flags = buf->getRawChar8();
5681 if (!(data_flags & 0x01)) basePoint.z = buf->getRawDouble();
5682 } else {
5683 basePoint.z = buf->getBitDouble();
5684 }
5685 basePoint.x = buf->getRawDouble();
5686 basePoint.y = buf->getRawDouble();
5687 if (version > DRW::AC1014) {
5688 if (!(data_flags & 0x02)) {
5689 secPoint.x = buf->getDefaultDouble(basePoint.x);
5690 secPoint.y = buf->getDefaultDouble(basePoint.y);
5691 } else {
5692 secPoint = basePoint;
5693 }
5694 } else {
5695 secPoint.x = buf->getRawDouble();
5696 secPoint.y = buf->getRawDouble();
5697 }
5698 secPoint.z = basePoint.z;
5699 extPoint = buf->getExtrusion(version > DRW::AC1014);
5700 thickness = buf->getThickness(version > DRW::AC1014);
5701 if (version > DRW::AC1014) {
5702 if (!(data_flags & 0x04)) oblique = buf->getRawDouble();
5703 if (!(data_flags & 0x08)) angle = buf->getRawDouble();
5704 height = buf->getRawDouble();
5705 if (!(data_flags & 0x10)) widthscale = buf->getRawDouble();
5706 } else {
5707 oblique = buf->getBitDouble();
5708 angle = buf->getBitDouble();
5709 height = buf->getBitDouble();
5710 widthscale = buf->getBitDouble();
5711 }
5712 angle *= ARAD57.29577951308232;
5713 text = sBuf->getVariableText(version, false);
5714 if (!(data_flags & 0x20)) textgen = buf->getBitShort();
5715 if (!(data_flags & 0x40)) alignH = (HAlign)buf->getBitShort();
5716 if (!(data_flags & 0x80)) alignV = (VAlign)buf->getBitShort();
5717
5718 if (version >= DRW::AC1024) {
5719 attVersion = buf->getRawChar8();
5720 }
5721 if (version >= DRW::AC1032) {
5722 m_attributeType = buf->getRawChar8();
5723 }
5724
5725 bool hasEmbeddedMText = false;
5726 EmbeddedMTextHandleInfo embeddedMTextHandles;
5727 if (version >= DRW::AC1032 && m_attributeType != 0 && m_attributeType != 1) {
5728 mtext = std::make_unique<DRW_MText>();
5729 if (!parseEmbeddedMTextDwg(version, buf, sBuf, *mtext, embeddedMTextHandles)) {
5730 DRW_DBG("R2018 multi-line ATTDEF payload failed\n")DRW_dbg::dbg("R2018 multi-line ATTDEF payload failed\n");
5731 return false;
5732 }
5733 hasEmbeddedMText = true;
5734 }
5735
5736 tag = sBuf->getVariableText(version, false);
5737 DRW_DBG("attdef tag: ")DRW_dbg::dbg("attdef tag: "); DRW_DBG(tag.c_str())DRW_dbg::dbg(tag.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
5738
5739 m_fieldLength = buf->getBitShort(); /* field length BS (obsolete, usually 0) */
5740
5741 attribFlags = buf->getRawChar8();
5742
5743 // lockPosition (DXF 280): read gate lowered AC1024->AC1021 to match
5744 // ATTRIB (R2007+). promptVersion/keep_duplicate RC below stays AC1024+.
5745 if (version >= DRW::AC1021) {
5746 lockPosition = buf->getBit();
5747 }
5748
5749 if (version >= DRW::AC1024) {
5750 const std::uint8_t promptVersion = buf->getRawChar8();
5751 DRW_UNUSED(promptVersion)(void)promptVersion;
5752 }
5753
5754 // ATTDEF prompt follows attrib body
5755 prompt = sBuf->getVariableText(version, false);
5756 DRW_DBG("attdef prompt: ")DRW_dbg::dbg("attdef prompt: "); DRW_DBG(prompt.c_str())DRW_dbg::dbg(prompt.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
5757
5758 if (hasEmbeddedMText
5759 && !consumeEmbeddedMTextHandles(version, buf, objSize, embeddedMTextHandles, mtext.get())) {
5760 return false;
5761 }
5762 ret = DRW_Entity::parseDwgEntHandle(version, buf, !hasEmbeddedMText);
5763 if (!ret)
5764 return ret;
5765
5766 styleH = buf->getHandle();
5767
5768 return buf->isGood();
5769}
5770
5771bool DRW_Attdef::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5772 (void)bs;
5773 if (version >= DRW::AC1024 && version < DRW::AC1032 && (attVersion != 0 || mtext))
5774 return false;
5775 const std::uint8_t attributeType = (m_attributeType == 0) ? 1 : m_attributeType;
5776 const bool hasEmbeddedMText = version >= DRW::AC1032 && attributeType != 1;
5777 if (hasEmbeddedMText && !mtext)
5778 return false;
5779
5780 oType = 3; // ATTDEF class id — see dwgreader.cpp:1185
5781 if (!encodeDwgCommon(version, buf)) return false;
5782
5783 // TEXT-body section — identical layout to DRW_Attrib::encodeDwg.
5784 buf->putRawChar8(0);
5785 buf->putRawDouble(basePoint.z);
5786 buf->putRawDouble(basePoint.x);
5787 buf->putRawDouble(basePoint.y);
5788 buf->putDefaultDouble(basePoint.x, secPoint.x);
5789 buf->putDefaultDouble(basePoint.y, secPoint.y);
5790 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
5791 buf->putThickness(thickness, /*b_R2000_style=*/true);
5792 buf->putRawDouble(oblique);
5793 buf->putRawDouble(angle / ARAD57.29577951308232);
5794 buf->putRawDouble(height);
5795 buf->putRawDouble(widthscale);
5796 dwgBufferW *sb = strBuf ? strBuf : buf;
5797 sb->putVariableText(version, text);
5798 buf->putBitShort(static_cast<std::uint16_t>(textgen));
5799 buf->putBitShort(static_cast<std::uint16_t>(alignH));
5800 buf->putBitShort(static_cast<std::uint16_t>(alignV));
5801
5802 if (version >= DRW::AC1024) {
5803 buf->putRawChar8(hasEmbeddedMText && attVersion == 0 ? 1 : attVersion);
5804 }
5805 if (version >= DRW::AC1032) {
5806 buf->putRawChar8(attributeType);
5807 }
5808
5809 if (hasEmbeddedMText) {
5810 if (!encodeEmbeddedMTextDwg(version, buf, strBuf, handleBuf, *mtext))
5811 return false;
5812 }
5813
5814 sb->putVariableText(version, tag);
5815 buf->putBitShort(static_cast<std::uint16_t>(m_fieldLength)); // fieldLen BS
5816 buf->putRawChar8(attribFlags);
5817 if (version >= DRW::AC1024) {
5818 buf->putBit(lockPosition ? 1 : 0);
5819 }
5820
5821 if (version >= DRW::AC1024) {
5822 buf->putRawChar8(attVersion);
5823 }
5824
5825 // ATTDEF adds prompt between flags and handle stream
5826 sb->putVariableText(version, prompt);
5827
5828 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
5829
5830 dwgHandle sH;
5831 std::uint32_t sref = (styleH.ref == 0) ? 0x13 : styleH.ref;
5832 sH.code = 5;
5833 sH.ref = sref;
5834 sH.size = 0;
5835 if (sref != 0) { std::uint32_t t = sref; while (t != 0) { t >>= 8; ++sH.size; } }
5836 (handleBuf ? handleBuf : buf)->putHandle(sH);
5837 return true;
5838}
5839
5840bool DRW_MText::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
5841 switch (code) {
5842 case 1:
5843 text += reader->getString();
5844 text = reader->toUtf8String(text);
5845 break;
5846 case 11:
5847 hasXAxisVec = true;
5848 return DRW_Text::parseCode(code, reader);
5849 case 3:
5850 text += reader->getString();
5851 break;
5852 case 44:
5853 interlin = reader->getDouble();
5854 break;
5855 case 50: // djm: per dxf docs, last of code 11 or code 50 prevails
5856 hasXAxisVec = false;
5857 angle = reader->getDouble();
5858 break;
5859 case 73:
5860 linespacingStyle = static_cast<std::uint16_t>(reader->getInt32());
5861 break;
5862 default:
5863 return DRW_Text::parseCode(code, reader);
5864 }
5865
5866 return true;
5867}
5868
5869bool DRW_MText::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5870 dwgBuffer sBuff = *buf;
5871 dwgBuffer *sBuf = buf;
5872 if (version > DRW::AC1018) {//2007+
5873 sBuf = &sBuff; //separate buffer for strings
5874 }
5875 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
5876 if (!ret)
5877 return ret;
5878 DRW_DBG("\n***************************** parsing mtext *********************************************\n")DRW_dbg::dbg("\n***************************** parsing mtext *********************************************\n"
)
;
5879
5880 basePoint = buf->get3BitDouble(); /* Insertion pt 3BD 10 - First picked point. */
5881 DRW_DBG("Insertion: ")DRW_dbg::dbg("Insertion: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
5882 extPoint = buf->get3BitDouble(); /* Extrusion 3BD 210 Undocumented; */
5883 secPoint = buf->get3BitDouble(); /* X-axis dir 3BD 11 */
5884 hasXAxisVec = true;
5885 updateAngle();
5886 widthscale = buf->getBitDouble(); /* Rect width BD 41 */
5887 if (version > DRW::AC1018) {//2007+
5888 /* Rect height BD 46 Reference rectangle height. */
5889 /** @todo */buf->getBitDouble();
5890 }
5891 height = buf->getBitDouble();/* Text height BD 40 Undocumented */
5892 textgen = buf->getBitShort(); /* Attachment BS 71 Similar to justification; */
5893 /* Drawing dir BS 72 Left to right, etc.; see DXF doc. Reuse the
5894 * inherited alignH slot — for MTEXT this field carries the DXF group 72
5895 * "drawing direction" code (1=LtoR, 3=TtoB, 5=ByStyle), not the TEXT
5896 * horizontal-alignment values the HAlign enum was named for. The integer
5897 * round-trips cleanly; consumers compare against the raw integer. */
5898 alignH = static_cast<HAlign>(buf->getBitShort());
5899 /* Extents ht BD Undocumented and not present in DXF or entget */
5900 double ext_ht = buf->getBitDouble();
5901 DRW_UNUSED(ext_ht)(void)ext_ht;
5902 /* Extents wid BD Undocumented and not present in DXF or entget The extents
5903 rectangle, when rotated the same as the text, fits the actual text image on
5904 the screen (although we've seen it include an extra row of text in height). */
5905 double ext_wid = buf->getBitDouble();
5906 DRW_UNUSED(ext_wid)(void)ext_wid;
5907 /* Text TV 1 All text in one long string (without '\n's 3 for line wrapping).
5908 ACAD seems to add braces ({ }) and backslash-P's to indicate paragraphs
5909 based on the "\r\n"'s found in the imported file. But, all the text is in
5910 this one long string -- not broken into 1- and 3-groups as in DXF and
5911 entget. ACAD's entget breaks this string into 250-char pieces (not 255 as
5912 doc'd) – even if it's mid-word. The 1-group always gets the tag end;
5913 therefore, the 3's are always 250 chars long. */
5914 text = sBuf->getVariableText(version, false); /* Text value TV 1 */
5915 if (version > DRW::AC1014) {//2000+
5916 linespacingStyle = buf->getBitShort(); // ODA §20.4.46 code 73
5917 interlin = buf->getBitDouble();/* Linespacing Factor BD 44 */
5918 buf->getBit();/* Unknown bit B */
5919 }
5920 if (version > DRW::AC1015) {//2004+
5921 /* Background flags BL 0 = no background, 1 = background fill, 2 =background
5922 fill with drawing fill color. */
5923 m_backgroundFlags = buf->getBitLong();
5924 if ((m_backgroundFlags & 0x01) || (version >= DRW::AC1032 && (m_backgroundFlags & 0x10))) {
5925 /* Background-fill box scale, present if background flags & 1 (default
5926 1.5). It is a BitDouble, NOT a BitLong: reading it as BL consumes
5927 the wrong bit width and desyncs the stream so the following CMC
5928 fill-colour reads garbage and the entity body overruns (parse fails
5929 on every MTEXT with background fill, e.g. sample_AC1018). ACadSharp
5930 reads ReadBitDouble here. */
5931 m_backgroundScale = buf->getBitDouble();
5932 /* Background color CMC Present if background flags = 1 */
5933 m_backgroundColor = static_cast<int>(buf->getCmColor(version, nullptr, sBuf));
5934 /** @todo buf->getCMC */
5935 /* Background transparency BL Present if background flags = 1 */
5936 m_backgroundTransparency = buf->getBitLong();
5937 }
5938 }
5939
5940 bool hasR2018AppId = false;
5941 if (version >= DRW::AC1032) {
5942 m_r2018ColumnHeights.clear();
5943 m_r2018IsNotAnnotative = buf->getBit();
5944 if (m_r2018IsNotAnnotative) {
5945 m_r2018Version = buf->getBitShort();
5946 m_r2018DefaultFlag = buf->getBit();
5947 hasR2018AppId = true; // appid H follows in handle stream
5948 m_r2018Attachment = buf->getBitLong();
5949 m_r2018XAxisDir = buf->get3BitDouble();
5950 m_r2018InsertionPoint = buf->get3BitDouble();
5951 m_r2018RectWidth = buf->getBitDouble();
5952 m_r2018RectHeight = buf->getBitDouble();
5953 m_r2018ExtentsHeight = buf->getBitDouble();
5954 m_r2018ExtentsWidth = buf->getBitDouble();
5955 m_r2018ColumnType = buf->getBitShort();
5956 if (m_r2018ColumnType != 0) {
5957 m_r2018ColumnCount = buf->getBitLong();
5958 m_r2018ColumnWidth = buf->getBitDouble();
5959 m_r2018ColumnGutter = buf->getBitDouble();
5960 m_r2018ColumnAutoHeight = buf->getBit();
5961 m_r2018ColumnFlowReversed = buf->getBit();
5962 if (!m_r2018ColumnAutoHeight && m_r2018ColumnType == 2 && m_r2018ColumnCount > 0
5963 && m_r2018ColumnCount < 10000) {
5964 m_r2018ColumnHeights.reserve(static_cast<size_t>(m_r2018ColumnCount));
5965 for (std::int32_t i = 0; i < m_r2018ColumnCount; ++i) {
5966 m_r2018ColumnHeights.push_back(buf->getBitDouble());
5967 }
5968 }
5969 }
5970 }
5971 }
5972
5973 /* Common Entity Handle Data */
5974 ret = DRW_Entity::parseDwgEntHandle(version, buf);
5975 if (!ret)
5976 return ret;
5977
5978 styleH = buf->getHandle(); /* H 7 STYLE (hard pointer) */
5979 DRW_DBG("text style Handle: ")DRW_dbg::dbg("text style Handle: "); DRW_DBG(styleH.code)DRW_dbg::dbg(styleH.code); DRW_DBG(".")DRW_dbg::dbg(".");
5980 DRW_DBG(styleH.size)DRW_dbg::dbg(styleH.size); DRW_DBG(".")DRW_dbg::dbg("."); DRW_DBG(styleH.ref)DRW_dbg::dbg(styleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
5981 if (hasR2018AppId) {
5982 dwgHandle appIdH = buf->getHandle();
5983 m_r2018AppIdHandle = appIdH.ref;
5984 DRW_DBG("mtext R2018 appid Handle: ")DRW_dbg::dbg("mtext R2018 appid Handle: "); DRW_DBGHL(appIdH.code, appIdH.size, appIdH.ref)DRW_dbg::dbgHL(appIdH.code, appIdH.size, appIdH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
5985 }
5986
5987 /* CRC X --- */
5988 return buf->isGood();
5989}
5990
5991void DRW_MText::updateAngle() {
5992 if (hasXAxisVec) {
5993 angle = atan2(secPoint.y, secPoint.x) * ARAD57.29577951308232;
5994 }
5995}
5996
5997bool DRW_Polyline::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
5998 switch (code) {
5999 case 70:
6000 flags = reader->getInt32();
6001 break;
6002 case 40:
6003 defstawidth = reader->getDouble();
6004 break;
6005 case 41:
6006 defendwidth = reader->getDouble();
6007 break;
6008 case 71:
6009 vertexcount = reader->getInt32();
6010 break;
6011 case 72:
6012 facecount = reader->getInt32();
6013 break;
6014 case 73:
6015 smoothM = reader->getInt32();
6016 break;
6017 case 74:
6018 smoothN = reader->getInt32();
6019 break;
6020 case 75:
6021 curvetype = reader->getInt32();
6022 break;
6023 default:
6024 return DRW_Point::parseCode(code, reader);
6025 }
6026
6027 return true;
6028}
6029
6030//0x0F polyline 2D bit 4(8) & 5(16) NOT set
6031//0x10 polyline 3D bit 4(8) set
6032//0x1D PFACE bit 5(16) set
6033bool DRW_Polyline::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
6034 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
6035 if (!ret)
6036 return ret;
6037 DRW_DBG("\n***************************** parsing polyline *********************************************\n")DRW_dbg::dbg("\n***************************** parsing polyline *********************************************\n"
)
;
6038
6039 std::int32_t ooCount = 0;
6040 if (oType == 0x0F) { //pline 2D
6041 flags = buf->getBitShort();
6042 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(flags)DRW_dbg::dbg(flags);
6043 curvetype = buf->getBitShort();
6044 defstawidth = buf->getBitDouble();
6045 defendwidth = buf->getBitDouble();
6046 thickness = buf->getThickness(version > DRW::AC1014);
6047 basePoint = DRW_Coord(0,0,buf->getBitDouble());
6048 extPoint = buf->getExtrusion(version > DRW::AC1014);
6049 } else if (oType == 0x10) { //pline 3D
6050 std::uint8_t tmpFlag = buf->getRawChar8();
6051 DRW_DBG("flags 1 value: ")DRW_dbg::dbg("flags 1 value: "); DRW_DBG(tmpFlag)DRW_dbg::dbg(tmpFlag);
6052 if (tmpFlag & 1)
6053 curvetype = 5; // quadratic B-spline
6054 else if (tmpFlag & 2)
6055 curvetype = 6; // cubic B-spline
6056 if (tmpFlag & 3)
6057 flags |= 4; // splined (bit 2); do NOT overwrite curvetype to 8
6058 tmpFlag = buf->getRawChar8();
6059 if (tmpFlag & 1)
6060 flags |= 1;
6061 flags |= 8; //indicate 3DPOL
6062 DRW_DBG("flags 2 value: ")DRW_dbg::dbg("flags 2 value: "); DRW_DBG(tmpFlag)DRW_dbg::dbg(tmpFlag);
6063 } else if (oType == 0x1D) { //PFACE
6064 flags = 64;
6065 vertexcount = buf->getBitShort();
6066 DRW_DBG("vertex count: ")DRW_dbg::dbg("vertex count: "); DRW_DBG(vertexcount)DRW_dbg::dbg(vertexcount);
6067 facecount = buf->getBitShort();
6068 DRW_DBG("face count: ")DRW_dbg::dbg("face count: "); DRW_DBG(facecount)DRW_dbg::dbg(facecount);
6069 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(flags)DRW_dbg::dbg(flags);
6070 } else if (oType == 0x1E) { //POLYLINE_MESH per ODA spec sec 19.4.31
6071 flags = buf->getBitShort();
6072 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(flags)DRW_dbg::dbg(flags);
6073 flags |= 16; //bit 4 = 3D polygon mesh
6074 curvetype = buf->getBitShort();
6075 vertexcount = buf->getBitShort(); //M-count
6076 DRW_DBG(" M count: ")DRW_dbg::dbg(" M count: "); DRW_DBG(vertexcount)DRW_dbg::dbg(vertexcount);
6077 facecount = buf->getBitShort(); //N-count
6078 DRW_DBG(" N count: ")DRW_dbg::dbg(" N count: "); DRW_DBG(facecount)DRW_dbg::dbg(facecount);
6079 smoothM = buf->getBitShort(); //M smooth-surface density, DXF 73
6080 smoothN = buf->getBitShort(); //N smooth-surface density, DXF 74
6081 DRW_DBG(" M/N density: ")DRW_dbg::dbg(" M/N density: "); DRW_DBG(smoothM)DRW_dbg::dbg(smoothM); DRW_DBG("/")DRW_dbg::dbg("/"); DRW_DBG(smoothN)DRW_dbg::dbg(smoothN);
6082 }
6083 if (version > DRW::AC1015){ //2004+
6084 ooCount = buf->getBitLong();
6085 }
6086
6087 ret = DRW_Entity::parseDwgEntHandle(version, buf);
6088 if (!ret)
6089 return ret;
6090
6091 if (version < DRW::AC1018){ //2000-
6092 dwgHandle objectH = buf->getOffsetHandle(handle);
6093 firstEH = objectH.ref;
6094 DRW_DBG(" first Vertex Handle: ")DRW_dbg::dbg(" first Vertex Handle: "); DRW_DBGHL(objectH.code, objectH.size, objectH.ref)DRW_dbg::dbgHL(objectH.code, objectH.size, objectH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
6095 objectH = buf->getOffsetHandle(handle);
6096 lastEH = objectH.ref;
6097 DRW_DBG(" last Vertex Handle: ")DRW_dbg::dbg(" last Vertex Handle: "); DRW_DBGHL(objectH.code, objectH.size, objectH.ref)DRW_dbg::dbgHL(objectH.code, objectH.size, objectH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
6098 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
6099 } else {
6100 for (std::int32_t i = 0; i < ooCount; ++i){
6101 dwgHandle objectH = buf->getOffsetHandle(handle);
6102 hadlesList.push_back (objectH.ref);
6103 DRW_DBG(" Vertex Handle: ")DRW_dbg::dbg(" Vertex Handle: "); DRW_DBGHL(objectH.code, objectH.size, objectH.ref)DRW_dbg::dbgHL(objectH.code, objectH.size, objectH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
6104 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
6105 }
6106 }
6107 seqEndH = buf->getOffsetHandle(handle);
6108 DRW_DBG(" SEQEND Handle: ")DRW_dbg::dbg(" SEQEND Handle: "); DRW_DBGHL(seqEndH.code, seqEndH.size, seqEndH.ref)DRW_dbg::dbgHL(seqEndH.code, seqEndH.size, seqEndH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
6109 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
6110
6111// RS crc; //RS */
6112 return buf->isGood();
6113}
6114
6115bool DRW_Vertex::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
6116 switch (code) {
6117 case 70:
6118 flags = reader->getInt32();
6119 break;
6120 case 40:
6121 stawidth = reader->getDouble();
6122 break;
6123 case 41:
6124 endwidth = reader->getDouble();
6125 break;
6126 case 42:
6127 bulge = reader->getDouble();
6128 break;
6129 case 50:
6130 tgdir = reader->getDouble();
6131 break;
6132 case 71:
6133 vindex1 = reader->getInt32();
6134 break;
6135 case 72:
6136 vindex2 = reader->getInt32();
6137 break;
6138 case 73:
6139 vindex3 = reader->getInt32();
6140 break;
6141 case 74:
6142 vindex4 = reader->getInt32();
6143 break;
6144 case 91:
6145 identifier = reader->getInt32();
6146 break;
6147 default:
6148 return DRW_Point::parseCode(code, reader);
6149 }
6150
6151 return true;
6152}
6153
6154//0x0A vertex 2D
6155//0x0B vertex 3D
6156//0x0C MESH
6157//0x0D PFACE
6158//0x0E PFACE FACE
6159bool DRW_Vertex::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs, double el){
6160 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
6161 if (!ret)
6162 return ret;
6163 DRW_DBG("\n***************************** parsing pline Vertex *********************************************\n")DRW_dbg::dbg("\n***************************** parsing pline Vertex *********************************************\n"
)
;
6164
6165 if (oType == 0x0A) { //pline 2D, needed example
6166 m_dwgSubtype = DwgSubtype::Vertex2D;
6167 flags = buf->getRawChar8(); //RLZ: EC unknown type
6168 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(flags)DRW_dbg::dbg(flags);
6169 basePoint = buf->get3BitDouble();
6170 basePoint.z = el;
6171 DRW_DBG("basePoint: ")DRW_dbg::dbg("basePoint: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
6172 stawidth = buf->getBitDouble();
6173 if (stawidth < 0)
6174 endwidth = stawidth = fabs(stawidth);
6175 else
6176 endwidth = buf->getBitDouble();
6177 bulge = buf->getBitDouble();
6178 if (version > DRW::AC1021) { //2010+
6179 identifier = buf->getBitLong(); // ODA §20.4.11 code 91
6180 DRW_DBG("Vertex ID: ")DRW_dbg::dbg("Vertex ID: "); DRW_DBG(identifier)DRW_dbg::dbg(identifier);
6181 }
6182 tgdir = buf->getBitDouble();
6183 } else if (oType == 0x0B || oType == 0x0C || oType == 0x0D) { //PFACE
6184 if (oType == 0x0B)
6185 m_dwgSubtype = DwgSubtype::Vertex3D;
6186 else if (oType == 0x0C)
6187 m_dwgSubtype = DwgSubtype::Mesh;
6188 else
6189 m_dwgSubtype = DwgSubtype::Polyface;
6190 flags = buf->getRawChar8(); //RLZ: EC unknown type
6191 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(flags)DRW_dbg::dbg(flags);
6192 basePoint = buf->get3BitDouble();
6193 DRW_DBG("basePoint: ")DRW_dbg::dbg("basePoint: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
6194 } else if (oType == 0x0E) { //PFACE FACE
6195 m_dwgSubtype = DwgSubtype::PolyfaceFace;
6196 auto signedIndex = [](int value) {
6197 return value > 32767 ? value - 65536 : value;
6198 };
6199 vindex1 = signedIndex(buf->getBitShort());
6200 vindex2 = signedIndex(buf->getBitShort());
6201 vindex3 = signedIndex(buf->getBitShort());
6202 vindex4 = signedIndex(buf->getBitShort());
6203 }
6204
6205 ret = DRW_Entity::parseDwgEntHandle(version, buf);
6206 if (!ret)
6207 return ret;
6208 // RS crc; //RS */
6209 return buf->isGood();
6210}
6211
6212bool DRW_Hatch::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
6213 switch (code) {
6214 case 2:
6215 name = reader->getUtf8String();
6216 break;
6217 case 70:
6218 solid = reader->getInt32();
6219 break;
6220 case 71:
6221 associative = reader->getInt32();
6222 break;
6223 case 72: /*edge type*/
6224 if (ispol){ // polyline path: 72 is the has-bulge flag. Do NOT fold it
6225 // into pline->flags — bit 0 there is the *closed* flag (set by code
6226 // 73), and the per-vertex bulges arrive via code 42 regardless. Some
6227 // writers (e.g. ezdxf MPOLYGON) emit 73 before 72; the old code let
6228 // 72 clear the closed bit 73 had just set, leaving the boundary open
6229 // so RS_Hatch::validate() rejected the area.
6230 break;
6231 } else if (reader->getInt32() == 1){ //line
6232 addLine();
6233 } else if (reader->getInt32() == 2){ //arc
6234 addArc();
6235 } else if (reader->getInt32() == 3){ //elliptic arc
6236 addEllipse();
6237 } else if (reader->getInt32() == 4){ //spline
6238 addSpline();
6239 }
6240 break;
6241 case 10:
6242 // Spline edge: 10 is a control-point x-coord.
6243 if (spline) {
6244 spline->controllist.push_back(std::make_shared<DRW_Coord>(reader->getDouble(), 0.0, 0.0));
6245 break;
6246 }
6247 if (pt) pt->basePoint.x = reader->getDouble();
6248 else if (pline) {
6249 plvert = pline->addVertex();
6250 plvert->x = reader->getDouble();
6251 } else {
6252 // After group 98 the boundary path is closed; seed-point
6253 // coords arrive as group-10/20 pairs.
6254 DRW_Coord seed;
6255 seed.x = reader->getDouble();
6256 seedPoints.push_back(seed);
6257 }
6258 break;
6259 case 20:
6260 if (spline && !spline->controllist.empty()) {
6261 spline->controllist.back()->y = reader->getDouble();
6262 break;
6263 }
6264 if (pt) pt->basePoint.y = reader->getDouble();
6265 else if (plvert) plvert ->y = reader->getDouble();
6266 else if (!seedPoints.empty())
6267 seedPoints.back().y = reader->getDouble();
6268 break;
6269 case 11:
6270 // Spline edge: 11 is a fit-point x-coord.
6271 if (spline) {
6272 spline->fitlist.push_back(std::make_shared<DRW_Coord>(reader->getDouble(), 0.0, 0.0));
6273 break;
6274 }
6275 if (line) line->secPoint.x = reader->getDouble();
6276 else if (ellipse) ellipse->secPoint.x = reader->getDouble();
6277 break;
6278 case 21:
6279 if (spline && !spline->fitlist.empty()) {
6280 spline->fitlist.back()->y = reader->getDouble();
6281 break;
6282 }
6283 if (line) line->secPoint.y = reader->getDouble();
6284 else if (ellipse) ellipse->secPoint.y = reader->getDouble();
6285 break;
6286 case 12:
6287 if (spline) { spline->tgStart.x = reader->getDouble(); break; }
6288 break;
6289 case 22:
6290 if (spline) { spline->tgStart.y = reader->getDouble(); break; }
6291 break;
6292 case 13:
6293 if (spline) { spline->tgEnd.x = reader->getDouble(); break; }
6294 break;
6295 case 23:
6296 if (spline) { spline->tgEnd.y = reader->getDouble(); break; }
6297 break;
6298 case 40:
6299 // Spline edge: 40 is a knot value (occurs nknots times).
6300 if (spline) {
6301 spline->knotslist.push_back(reader->getDouble());
6302 break;
6303 }
6304 if (arc) arc->radious = reader->getDouble();
6305 else if (ellipse) ellipse->ratio = reader->getDouble();
6306 break;
6307 case 41:
6308 scale = reader->getDouble();
6309 break;
6310 case 42:
6311 // Spline edge: 42 is a per-control-point weight.
6312 if (spline) {
6313 spline->weightlist.push_back(reader->getDouble());
6314 break;
6315 }
6316 if (plvert) plvert ->bulge = reader->getDouble();
6317 break;
6318 case 50:
6319 if (arc) arc->staangle = reader->getDouble()/ARAD57.29577951308232;
6320 else if (ellipse) ellipse->staparam = reader->getDouble()/ARAD57.29577951308232;
6321 break;
6322 case 51:
6323 if (arc) arc->endangle = reader->getDouble()/ARAD57.29577951308232;
6324 else if (ellipse) ellipse->endparam = reader->getDouble()/ARAD57.29577951308232;
6325 break;
6326 case 47:
6327 pixelSize = reader->getDouble();
6328 break;
6329 case 52:
6330 angle = reader->getDouble();
6331 break;
6332 case 53: // pattern line angle — starts a new PatternLine record
6333 patternLines.push_back(PatternLine());
6334 patternLines.back().angle = reader->getDouble();
6335 break;
6336 case 43:
6337 if (!patternLines.empty()) patternLines.back().baseX = reader->getDouble();
6338 break;
6339 case 44:
6340 if (!patternLines.empty()) patternLines.back().baseY = reader->getDouble();
6341 break;
6342 case 45:
6343 if (!patternLines.empty()) patternLines.back().offsetX = reader->getDouble();
6344 break;
6345 case 46:
6346 if (!patternLines.empty()) patternLines.back().offsetY = reader->getDouble();
6347 break;
6348 case 79: // dash count — the 49s that follow will accumulate
6349 break;
6350 case 49:
6351 if (!patternLines.empty()) patternLines.back().dashList.push_back(reader->getDouble());
6352 break;
6353 case 73:
6354 // Spline edge: 73 is the rational flag (1 = rational).
6355 if (spline) {
6356 if (reader->getInt32()) spline->flags |= 0x4;
6357 break;
6358 }
6359 if (arc) arc->isccw = reader->getInt32();
6360 // polyline path: 73 is the is-closed flag -> set bit 0 only, leaving the
6361 // rest of pline->flags untouched (order-independent vs code 72).
6362 else if (pline) pline->flags = (pline->flags & ~1) | (reader->getInt32() ? 1 : 0);
6363 break;
6364 case 74:
6365 // Spline edge: 74 is the periodic flag (1 = periodic/closed).
6366 if (spline) {
6367 if (reader->getInt32()) spline->flags |= 0x2;
6368 }
6369 break;
6370 case 94:
6371 // Spline edge degree.
6372 if (spline) spline->degree = reader->getInt32();
6373 break;
6374 case 95:
6375 // Spline edge number of knots.
6376 if (spline) spline->nknots = reader->getInt32();
6377 break;
6378 case 96:
6379 // Spline edge number of control points.
6380 if (spline) spline->ncontrol = reader->getInt32();
6381 break;
6382 case 97:
6383 if (spline) {
6384 if (!m_splineNfitSet) {
6385 // First 97 in this spline edge = fit-point count (nfit).
6386 spline->nfit = reader->getInt32();
6387 if (spline->nfit == 0) {
6388 // No fit points or tangents follow; safe to clear spline
6389 // so the next code-97 (loop boundary count) is not
6390 // misinterpreted as another nfit.
6391 spline.reset();
6392 } else {
6393 m_splineNfitSet = true;
6394 }
6395 } else {
6396 // Second 97 while spline is active = loop boundary handle count.
6397 spline.reset();
6398 m_splineNfitSet = false;
6399 m_boundaryHandleCount = reader->getInt32();
6400 if (m_boundaryHandleCount > 0 && loop)
6401 DRW::reserve(loop->m_boundaryHandles, m_boundaryHandleCount);
6402 }
6403 break;
6404 }
6405 // No active spline: this is the loop boundary handle count.
6406 m_splineNfitSet = false;
6407 m_boundaryHandleCount = reader->getInt32();
6408 if (m_boundaryHandleCount > 0 && loop)
6409 DRW::reserve(loop->m_boundaryHandles, m_boundaryHandleCount);
6410 break;
6411 case 330:
6412 if (m_boundaryHandleCount > 0 && loop) {
6413 // getHandleString() converts the hex string to int for us.
6414 loop->m_boundaryHandles.push_back(
6415 static_cast<std::uint32_t>(reader->getHandleString()));
6416 --m_boundaryHandleCount;
6417 break;
6418 }
6419 return DRW_Point::parseCode(code, reader);
6420 case 75:
6421 hstyle = reader->getInt32();
6422 break;
6423 case 76:
6424 hpattern = reader->getInt32();
6425 break;
6426 case 77:
6427 doubleflag = reader->getInt32();
6428 break;
6429 case 78:
6430 deflines = reader->getInt32();
6431 break;
6432 case 91:
6433 loopsnum = reader->getInt32();
6434 return DRW::reserve( looplist, loopsnum);
6435 case 92:
6436 loop = std::make_shared<DRW_HatchLoop>(reader->getInt32());
6437 looplist.push_back(loop);
6438 if (reader->getInt32() & 2) {
6439 ispol = true;
6440 clearEntities();
6441 pline = std::make_shared<DRW_LWPolyline>();
6442 loop->objlist.push_back(pline);
6443 } else ispol = false;
6444 break;
6445 case 93:
6446 if (pline) pline->vertexnum = reader->getInt32();
6447 else if (loop) loop->numedges = reader->getInt32();//aqui reserve
6448 break;
6449 case 98: { // seed-point count; coords follow as group-10/20 pairs
6450 clearEntities();
6451 const int count = reader->getInt32();
6452 if (count > 0)
6453 DRW::reserve(seedPoints, count);
6454 break;
6455 }
6456 case 450:
6457 isGradient = reader->getInt32();
6458 break;
6459 case 451:
6460 gradReserved = reader->getInt32();
6461 break;
6462 case 452:
6463 singleColor = reader->getInt32();
6464 break;
6465 case 453: {
6466 const int n = reader->getInt32();
6467 if (n > 0)
6468 DRW::reserve(gradColors, n);
6469 break;
6470 }
6471 case 460:
6472 gradAngle = reader->getDouble();
6473 break;
6474 case 461:
6475 gradShift = reader->getDouble();
6476 break;
6477 case 462:
6478 gradTint = reader->getDouble();
6479 break;
6480 case 463: {
6481 DRW_Hatch::GradientStop stop;
6482 stop.value = reader->getDouble();
6483 gradColors.push_back(stop);
6484 break;
6485 }
6486 case 421:
6487 if (!gradColors.empty())
6488 gradColors.back().rgb = reader->getInt32();
6489 break;
6490 case 63:
6491 if (!gradColors.empty())
6492 gradColors.back().aciColor = reader->getInt32();
6493 else
6494 return DRW_Point::parseCode(code, reader);
6495 break;
6496 case 431:
6497 if (!gradColors.empty())
6498 gradColors.back().colorMethod = reader->getInt32();
6499 break;
6500 case 432:
6501 if (!gradColors.empty())
6502 gradColors.back().colorName = reader->getUtf8String();
6503 break;
6504 case 433:
6505 if (!gradColors.empty())
6506 gradColors.back().colorBookName = reader->getUtf8String();
6507 break;
6508 case 470:
6509 gradName = reader->getUtf8String();
6510 break;
6511 default:
6512 return DRW_Point::parseCode(code, reader);
6513 }
6514
6515 return true;
6516}
6517
6518bool DRW_MPolygon::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
6519 // MPOLYGON shares HATCH's boundary/pattern/gradient codes, so delegate those
6520 // to DRW_Hatch::parseCode. It adds a trailer that plain HATCH never emits:
6521 // 63 / 421 / 430 fill color (ACI / RGB / book-name) — the filled area's
6522 // color, which may differ from the boundary outline color;
6523 // 11 / 21 boundary x-direction vector (no render impact; left to
6524 // the base, which ignores it outside an edge context);
6525 // 99 count of degenerate boundary paths.
6526 // 63/421 are also gradient sub-codes in HATCH, so only claim them here when no
6527 // gradient is being accumulated (gradColors empty) — otherwise defer to base.
6528 switch (code) {
6529 case 63:
6530 if (gradColors.empty()) { fillColorAci = reader->getInt32(); return true; }
6531 break;
6532 case 421:
6533 if (gradColors.empty()) { fillColorRgb = reader->getInt32(); return true; }
6534 break;
6535 case 430:
6536 fillColorName = reader->getUtf8String();
6537 return true;
6538 case 99:
6539 degenerateLoops = reader->getInt32();
6540 return true;
6541 default:
6542 break;
6543 }
6544 return DRW_Hatch::parseCode(code, reader);
6545}
6546
6547// DRW_MPolygon::parseDwg — AcDbMPolygon DWG body.
6548// Layout mirrors HATCH except (per ACadSharp MPolygon / libreDWG dwg.spec):
6549// * a leading BS `style` (DXF group 75) precedes the gradient block, and
6550// * the trailer is a fill CMC + boundary x-direction (2RD) + degenerate-path
6551// count (BL) instead of HATCH's pixel-size + seed points.
6552// The gradient/elevation/extrusion/name/solid/associative prologue and the whole
6553// boundary-loop body are identical, so they reuse DRW_Hatch::parseDwgBoundaryData.
6554// DWG runtime coverage uses testdata/mpolygon_solid.dwg, ODA-synthesized from
6555// the ezdxf-verified inline DXF in mpolygon_tests.cpp and confirmed with the
6556// dwg-parser oracle.
6557bool DRW_MPolygon::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
6558 dwgBuffer sBuff = *buf;
6559 dwgBuffer *sBuf = buf;
6560 std::uint32_t totalBoundItems = 0;
6561 bool havePixelSize = false;
6562 if (version > DRW::AC1018) //2007+
6563 sBuf = &sBuff; //separate buffer for strings
6564 if (!DRW_Entity::parseDwg(version, buf, sBuf, bs))
6565 return false;
6566 DRW_DBG("\n***************************** parsing mpolygon *********************************************\n")DRW_dbg::dbg("\n***************************** parsing mpolygon *********************************************\n"
)
;
6567
6568 // Leading BS style (group 75) — read once here and again after the loops
6569 // below (HATCH has only the latter); matches the reference parser, which
6570 // discards this first read.
6571 hstyle = buf->getBitShort();
6572
6573 if (version > DRW::AC1015) { //2004+ gradient (same layout as HATCH)
6574 isGradient = buf->getBitLong();
6575 gradReserved = buf->getBitLong();
6576 gradAngle = buf->getBitDouble();
6577 gradShift = buf->getBitDouble();
6578 singleColor = buf->getBitLong();
6579 gradTint = buf->getBitDouble();
6580 std::int32_t numCol = buf->getBitLong();
6581 if (numCol > 0)
6582 DRW::reserve(gradColors, numCol);
6583 for (std::int32_t i = 0 ; i < numCol; ++i){
6584 DRW_Hatch::GradientStop stop;
6585 stop.value = buf->getBitDouble();
6586 buf->getBitShort(); // unknown short
6587 stop.rgb = buf->getBitLong();
6588 buf->getRawChar8(); // ignored color byte
6589 gradColors.push_back(stop);
6590 }
6591 gradName = sBuf->getVariableText(version, false);
6592 }
6593 basePoint.z = buf->getBitDouble(); // elevation
6594 extPoint = buf->get3BitDouble();
6595 name = sBuf->getVariableText(version, false);
6596 solid = buf->getBit();
6597 associative = buf->getBit();
6598
6599 if (!parseDwgBoundaryData(version, buf, totalBoundItems, havePixelSize))
6600 return false;
6601
6602 hstyle = buf->getBitShort();
6603 hpattern = buf->getBitShort();
6604 if (!solid){
6605 angle = buf->getBitDouble();
6606 scale = buf->getBitDouble();
6607 doubleflag = buf->getBit();
6608 deflines = buf->getBitShort();
6609 for (std::int32_t i = 0 ; i < deflines; ++i){
6610 buf->getBitDouble(); // line angle
6611 buf->getBitDouble(); // base x
6612 buf->getBitDouble(); // base y
6613 buf->getBitDouble(); // offset x
6614 buf->getBitDouble(); // offset y
6615 std::uint16_t numDashL = buf->getBitShort();
6616 for (std::uint16_t d = 0 ; d < numDashL; ++d)
6617 buf->getBitDouble(); // dash length
6618 }
6619 }
6620
6621 // MPOLYGON trailer (differs from HATCH): fill CMC + x-direction + degenerate
6622 // path count. No pixel size / seed points here.
6623 std::int32_t rgb = -1;
6624 UTF8STRINGstd::string colName;
6625 fillColorAci = static_cast<int>(buf->getCmColor(version, &rgb, sBuf, &colName));
6626 fillColorRgb = rgb;
6627 fillColorName = colName;
6628 DRW_Coord xdir = buf->get2RawDouble();
6629 xDirX = xdir.x;
6630 xDirY = xdir.y;
6631 degenerateLoops = buf->getBitLong();
6632
6633 if (!DRW_Entity::parseDwgEntHandle(version, buf))
6634 return false;
6635 for (std::uint32_t i = 0 ; i < totalBoundItems; ++i)
6636 buf->getHandle(); // boundary-source handles
6637 return buf->isGood();
6638}
6639
6640// Shared DWG boundary-loop reader for HATCH and MPOLYGON (ODA §20.4.36).
6641// Reads the loop count and, per loop, the derived-boundary flag plus its edge
6642// list or polyline. Accumulates the running boundary-source-handle total and
6643// whether any loop is derived (needs a trailing pixel size). Extracted from
6644// DRW_Hatch::parseDwg so DRW_MPolygon::parseDwg reuses the identical body while
6645// supplying its own differing leading (BS style) and trailing (fill CMC +
6646// x-direction + degenerate count) field order.
6647bool DRW_MPolygon::encodeDwg(DRW::Version version, dwgBufferW *buf,
6648 std::uint32_t bs, dwgBufferW *strBuf,
6649 dwgBufferW *handleBuf) {
6650 (void)bs;
6651 oType = kDwgClassNum;
6652 if (!encodeDwgCommon(version, buf, strBuf))
6653 return false;
6654
6655 dwgBufferW *sb = strBuf ? strBuf : buf;
6656
6657 // AcDbMPolygon has a leading style field before the HATCH-like gradient
6658 // prologue. The same style is emitted again after the boundary data.
6659 buf->putBitShort(static_cast<std::uint16_t>(hstyle));
6660 encodeDwgGradientData(version, buf, sb);
6661
6662 buf->putBitDouble(basePoint.z);
6663 buf->put3BitDouble(extPoint);
6664 sb->putVariableText(version, name);
6665 buf->putBit(static_cast<std::uint8_t>(solid));
6666 buf->putBit(static_cast<std::uint8_t>(associative));
6667 if (!encodeDwgBoundaryData(version, buf)) return false;
6668
6669 buf->putBitShort(static_cast<std::uint16_t>(hstyle));
6670 buf->putBitShort(static_cast<std::uint16_t>(hpattern));
6671
6672 if (!solid) {
6673 buf->putBitDouble(angle);
6674 buf->putBitDouble(scale);
6675 buf->putBit(static_cast<std::uint8_t>(doubleflag));
6676 buf->putBitShort(static_cast<std::uint16_t>(patternLines.size()));
6677 for (const PatternLine& pl : patternLines) {
6678 buf->putBitDouble(pl.angle);
6679 buf->putBitDouble(pl.baseX);
6680 buf->putBitDouble(pl.baseY);
6681 buf->putBitDouble(pl.offsetX);
6682 buf->putBitDouble(pl.offsetY);
6683 buf->putBitShort(static_cast<std::uint16_t>(pl.dashList.size()));
6684 for (double dash : pl.dashList)
6685 buf->putBitDouble(dash);
6686 }
6687 }
6688
6689 buf->putCmColor(version,
6690 static_cast<std::uint16_t>(fillColorAci),
6691 fillColorRgb,
6692 fillColorName,
6693 {},
6694 sb);
6695 buf->put2RawDouble(DRW_Coord{xDirX, xDirY, 0.0});
6696 buf->putBitLong(degenerateLoops);
6697
6698 return encodeDwgEntHandle(version, buf, handleBuf);
6699}
6700
6701bool DRW_Hatch::parseDwgBoundaryData(DRW::Version version, dwgBuffer *buf,
6702 std::uint32_t &totalBoundItems, bool &havePixelSize) {
6703 loopsnum = buf->getBitLong();
6704 DRW_DBG("solid: ")DRW_dbg::dbg("solid: "); DRW_DBG(solid)DRW_dbg::dbg(solid); DRW_DBG(" associative: ")DRW_dbg::dbg(" associative: "); DRW_DBG(associative)DRW_dbg::dbg(associative);
6705 DRW_DBG(" loopsnum: ")DRW_dbg::dbg(" loopsnum: "); DRW_DBG(loopsnum)DRW_dbg::dbg(loopsnum); DRW_DBG("\n")DRW_dbg::dbg("\n");
6706
6707 //read loops
6708 for (std::int32_t i = 0 ; i < loopsnum; ++i){
6709 loop = std::make_shared<DRW_HatchLoop>(buf->getBitLong());
6710 havePixelSize = havePixelSize || ((loop->type & 4) != 0);
6711 DRW_DBG(" loop[")DRW_dbg::dbg(" loop["); DRW_DBG(i)DRW_dbg::dbg(i); DRW_DBG("] type: ")DRW_dbg::dbg("] type: "); DRW_DBG(loop->type)DRW_dbg::dbg(loop->type);
6712 if (!(loop->type & 2)){ //Not polyline
6713 std::int32_t numPathSeg = buf->getBitLong();
6714 DRW_DBG(" numPathSeg: ")DRW_dbg::dbg(" numPathSeg: "); DRW_DBG(numPathSeg)DRW_dbg::dbg(numPathSeg); DRW_DBG("\n")DRW_dbg::dbg("\n");
6715 for (std::int32_t j = 0; j<numPathSeg;++j){
6716 std::uint8_t typePath = buf->getRawChar8();
6717 DRW_DBG(" seg[")DRW_dbg::dbg(" seg["); DRW_DBG(j)DRW_dbg::dbg(j); DRW_DBG("] typePath: ")DRW_dbg::dbg("] typePath: "); DRW_DBG(typePath)DRW_dbg::dbg(typePath); DRW_DBG("\n")DRW_dbg::dbg("\n");
6718 if (typePath == 1){ //line
6719 addLine();
6720 line->basePoint = buf->get2RawDouble();
6721 line->secPoint = buf->get2RawDouble();
6722 } else if (typePath == 2){ //circle arc
6723 addArc();
6724 arc->basePoint = buf->get2RawDouble();
6725 arc->radious = buf->getBitDouble();
6726 arc->staangle = buf->getBitDouble();
6727 arc->endangle = buf->getBitDouble();
6728 arc->isccw = buf->getBit();
6729 } else if (typePath == 3){ //ellipse arc
6730 addEllipse();
6731 ellipse->basePoint = buf->get2RawDouble();
6732 ellipse->secPoint = buf->get2RawDouble();
6733 ellipse->ratio = buf->getBitDouble();
6734 ellipse->staparam = buf->getBitDouble();
6735 ellipse->endparam = buf->getBitDouble();
6736 ellipse->isccw = buf->getBit();
6737 } else if (typePath == 4){ //spline
6738 addSpline();
6739 spline->degree = buf->getBitLong();
6740 bool isRational = buf->getBit();
6741 spline->flags |= (isRational << 2); //rational
6742 spline->flags |= (buf->getBit() << 1); //periodic
6743 spline->nknots = buf->getBitLong();
6744 if (!DRW::reserve( spline->knotslist, spline->nknots)) {
6745 return false;
6746 }
6747 spline->ncontrol = buf->getBitLong();
6748 if (!DRW::reserve( spline->controllist, spline->ncontrol)) {
6749 return false;
6750 }
6751 for (std::int32_t j = 0; j < spline->nknots;++j){
6752 spline->knotslist.push_back (buf->getBitDouble());
6753 }
6754 for (std::int32_t j = 0; j < spline->ncontrol;++j){
6755 std::shared_ptr<DRW_Coord> crd = std::make_shared<DRW_Coord>(buf->get2RawDouble());
6756 if(isRational)
6757 crd->z = buf->getBitDouble(); //RLZ: investigate how store weight
6758 spline->controllist.push_back(crd);
6759 }
6760 if (version > DRW::AC1021) { //2010+
6761 spline->nfit = buf->getBitLong();
6762 // Fit points AND the start/end tangents are present only
6763 // when nfit > 0 (matches ACadSharp's `if (nfitPoints > 0)`).
6764 // Reading the two tangents unconditionally on an nfit==0
6765 // spline edge over-runs the entity body and fails the parse
6766 // (e.g. svg/export_sample.dwg: a degree-3 non-rational
6767 // spline boundary edge with 9 control points / 0 fit points).
6768 if (spline->nfit > 0) {
6769 if (!DRW::reserve( spline->fitlist, spline->nfit)) {
6770 return false;
6771 }
6772 for (std::int32_t j = 0; j < spline->nfit;++j){
6773 std::shared_ptr<DRW_Coord> crd = std::make_shared<DRW_Coord>(buf->get2RawDouble());
6774 spline->fitlist.push_back(crd);
6775 }
6776 spline->tgStart = buf->get2RawDouble();
6777 spline->tgEnd = buf->get2RawDouble();
6778 }
6779 }
6780 }
6781 }
6782 } else { //end not pline, start polyline
6783 pline = std::make_shared<DRW_LWPolyline>();
6784 bool asBulge = buf->getBit();
6785 pline->flags = buf->getBit();//closed bit
6786 std::int32_t numVert = buf->getBitLong();
6787 DRW_DBG(" asBulge: ")DRW_dbg::dbg(" asBulge: "); DRW_DBG(asBulge)DRW_dbg::dbg(asBulge); DRW_DBG(" closed: ")DRW_dbg::dbg(" closed: "); DRW_DBG(pline->flags)DRW_dbg::dbg(pline->flags);
6788 DRW_DBG(" numVert: ")DRW_dbg::dbg(" numVert: "); DRW_DBG(numVert)DRW_dbg::dbg(numVert); DRW_DBG("\n")DRW_dbg::dbg("\n");
6789 for (std::int32_t j = 0; j<numVert;++j){
6790 DRW_Vertex2D v;
6791 v.x = buf->getRawDouble();
6792 v.y = buf->getRawDouble();
6793 if (asBulge)
6794 v.bulge = buf->getBitDouble();
6795 pline->addVertex(v);
6796 }
6797 loop->objlist.push_back(pline);
6798 }//end polyline
6799 loop->update();
6800 looplist.push_back(loop);
6801 totalBoundItems += buf->getBitLong();
6802 DRW_DBG(" totalBoundItems: ")DRW_dbg::dbg(" totalBoundItems: "); DRW_DBG(totalBoundItems)DRW_dbg::dbg(totalBoundItems);
6803 } //end read loops
6804 return true;
6805}
6806
6807bool DRW_Hatch::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
6808 dwgBuffer sBuff = *buf;
6809 dwgBuffer *sBuf = buf;
6810 std::uint32_t totalBoundItems = 0;
6811 bool havePixelSize = false;
6812
6813 if (version > DRW::AC1018) {//2007+
6814 sBuf = &sBuff; //separate buffer for strings
6815 }
6816 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
6817 if (!ret)
6818 return ret;
6819 DRW_DBG("\n***************************** parsing hatch *********************************************\n")DRW_dbg::dbg("\n***************************** parsing hatch *********************************************\n"
)
;
6820
6821 //Gradient data, RLZ: is ok or if grad > 0 continue read ?
6822 if (version > DRW::AC1015) { //2004+
6823 isGradient = buf->getBitLong();
6824 DRW_DBG("is Gradient: ")DRW_dbg::dbg("is Gradient: "); DRW_DBG(isGradient)DRW_dbg::dbg(isGradient);
6825 gradReserved = buf->getBitLong();
6826 DRW_DBG(" reserved: ")DRW_dbg::dbg(" reserved: "); DRW_DBG(gradReserved)DRW_dbg::dbg(gradReserved);
6827 gradAngle = buf->getBitDouble();
6828 DRW_DBG(" Gradient angle: ")DRW_dbg::dbg(" Gradient angle: "); DRW_DBG(gradAngle)DRW_dbg::dbg(gradAngle);
6829 gradShift = buf->getBitDouble();
6830 DRW_DBG(" Gradient shift: ")DRW_dbg::dbg(" Gradient shift: "); DRW_DBG(gradShift)DRW_dbg::dbg(gradShift);
6831 singleColor = buf->getBitLong();
6832 DRW_DBG("\nsingle color Grad: ")DRW_dbg::dbg("\nsingle color Grad: "); DRW_DBG(singleColor)DRW_dbg::dbg(singleColor);
6833 gradTint = buf->getBitDouble();
6834 DRW_DBG(" Gradient tint: ")DRW_dbg::dbg(" Gradient tint: "); DRW_DBG(gradTint)DRW_dbg::dbg(gradTint);
6835 std::int32_t numCol = buf->getBitLong();
6836 DRW_DBG(" num colors: ")DRW_dbg::dbg(" num colors: "); DRW_DBG(numCol)DRW_dbg::dbg(numCol);
6837 if (numCol > 0)
6838 DRW::reserve(gradColors, numCol);
6839 for (std::int32_t i = 0 ; i < numCol; ++i){
6840 GradientStop stop;
6841 // First field is the stop position (per libreDWG: BD/unkDouble holds
6842 // the stop value in [0,1]); falls back to even spacing if missing.
6843 stop.value = buf->getBitDouble();
6844 DRW_DBG("\nstop value: ")DRW_dbg::dbg("\nstop value: "); DRW_DBG(stop.value)DRW_dbg::dbg(stop.value);
6845 std::uint16_t unkShort = buf->getBitShort();
6846 DRW_DBG(" unkShort: ")DRW_dbg::dbg(" unkShort: "); DRW_DBG(unkShort)DRW_dbg::dbg(unkShort);
6847 stop.rgb = buf->getBitLong();
6848 DRW_DBG(" rgb color: ")DRW_dbg::dbg(" rgb color: "); DRW_DBG(stop.rgb)DRW_dbg::dbg(stop.rgb);
6849 std::uint8_t ignCol = buf->getRawChar8();
6850 DRW_DBG(" ignored color: ")DRW_dbg::dbg(" ignored color: "); DRW_DBG(ignCol)DRW_dbg::dbg(ignCol);
6851 gradColors.push_back(stop);
6852 }
6853 gradName = sBuf->getVariableText(version, false);
6854 DRW_DBG("\ngradient name: ")DRW_dbg::dbg("\ngradient name: "); DRW_DBG(gradName.c_str())DRW_dbg::dbg(gradName.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
6855 }
6856 basePoint.z = buf->getBitDouble();
6857 extPoint = buf->get3BitDouble();
6858 DRW_DBG("base point: ")DRW_dbg::dbg("base point: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
6859 DRW_DBG("\nextrusion: ")DRW_dbg::dbg("\nextrusion: "); DRW_DBGPT(extPoint.x, extPoint.y, extPoint.z)DRW_dbg::dbgPT(extPoint.x, extPoint.y, extPoint.z);
6860 name = sBuf->getVariableText(version, false);
6861 DRW_DBG("\nhatch pattern name: ")DRW_dbg::dbg("\nhatch pattern name: "); DRW_DBG(name.c_str())DRW_dbg::dbg(name.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
6862 solid = buf->getBit();
6863 associative = buf->getBit();
6864 if (!parseDwgBoundaryData(version, buf, totalBoundItems, havePixelSize))
6865 return false;
6866
6867 hstyle = buf->getBitShort();
6868 hpattern = buf->getBitShort();
6869 DRW_DBG("\nhatch style: ")DRW_dbg::dbg("\nhatch style: "); DRW_DBG(hstyle)DRW_dbg::dbg(hstyle); DRW_DBG(" pattern type")DRW_dbg::dbg(" pattern type"); DRW_DBG(hpattern)DRW_dbg::dbg(hpattern);
6870 if (!solid){
6871 angle = buf->getBitDouble();
6872 scale = buf->getBitDouble();
6873 doubleflag = buf->getBit();
6874 deflines = buf->getBitShort();
6875 for (std::int32_t i = 0 ; i < deflines; ++i){
6876 DRW_Coord ptL, offL;
6877 double angleL = buf->getBitDouble();
6878 ptL.x = buf->getBitDouble();
6879 ptL.y = buf->getBitDouble();
6880 offL.x = buf->getBitDouble();
6881 offL.y = buf->getBitDouble();
6882 std::uint16_t numDashL = buf->getBitShort();
6883 DRW_DBG("\ndef line: ")DRW_dbg::dbg("\ndef line: "); DRW_DBG(angleL)DRW_dbg::dbg(angleL); DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(ptL.x)DRW_dbg::dbg(ptL.x); DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(ptL.y)DRW_dbg::dbg(ptL.y);
6884 DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(offL.x)DRW_dbg::dbg(offL.x); DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(offL.y)DRW_dbg::dbg(offL.y); DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(angleL)DRW_dbg::dbg(angleL);
6885 for (std::uint16_t i = 0 ; i < numDashL; ++i){
6886 double lengthL = buf->getBitDouble();
6887 DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(lengthL)DRW_dbg::dbg(lengthL);
6888 }
6889 }//end deflines
6890 } //end not solid
6891
6892 if (havePixelSize){
6893 double pixsize = buf->getBitDouble();
6894 DRW_DBG("\npixel size: ")DRW_dbg::dbg("\npixel size: "); DRW_DBG(pixsize)DRW_dbg::dbg(pixsize);
6895 }
6896 std::int32_t numSeedPoints = buf->getBitLong();
6897 DRW_DBG("\nnum Seed Points ")DRW_dbg::dbg("\nnum Seed Points "); DRW_DBG(numSeedPoints)DRW_dbg::dbg(numSeedPoints);
6898 if (numSeedPoints > 0)
6899 DRW::reserve(seedPoints, numSeedPoints);
6900 for (std::int32_t i = 0 ; i < numSeedPoints; ++i){
6901 DRW_Coord seedPt;
6902 seedPt.x = buf->getRawDouble();
6903 seedPt.y = buf->getRawDouble();
6904 DRW_DBG("\n ")DRW_dbg::dbg("\n "); DRW_DBG(seedPt.x)DRW_dbg::dbg(seedPt.x); DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(seedPt.y)DRW_dbg::dbg(seedPt.y);
6905 seedPoints.push_back(seedPt);
6906 }
6907
6908 DRW_DBG("\n")DRW_dbg::dbg("\n");
6909 ret = DRW_Entity::parseDwgEntHandle(version, buf);
6910 if (!ret)
6911 return ret;
6912 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
6913
6914 for (std::uint32_t i = 0 ; i < totalBoundItems; ++i){
6915 dwgHandle biH = buf->getHandle();
6916 DRW_DBG("Boundary Items Handle: ")DRW_dbg::dbg("Boundary Items Handle: "); DRW_DBGHL(biH.code, biH.size, biH.ref)DRW_dbg::dbgHL(biH.code, biH.size, biH.ref);
6917 }
6918 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
6919// RS crc; //RS */
6920 return buf->isGood();
6921}
6922
6923void DRW_Hatch::encodeDwgGradientData(DRW::Version version, dwgBufferW *buf,
6924 dwgBufferW *strBuf) const {
6925 if (version <= DRW::AC1015)
7
Assuming 'version' is > AC1015
6926 return;
6927
6928 dwgBufferW *sb = strBuf ? strBuf : buf;
8
Taking false branch
9
Assuming 'strBuf' is null
10
'?' condition is false
6929 buf->putBitLong(isGradient);
11
Called C++ object pointer is null
6930 buf->putBitLong(gradReserved);
6931 buf->putBitDouble(gradAngle);
6932 buf->putBitDouble(gradShift);
6933 buf->putBitLong(singleColor);
6934 buf->putBitDouble(gradTint);
6935 buf->putBitLong(static_cast<std::int32_t>(gradColors.size()));
6936 for (const GradientStop& stop : gradColors) {
6937 buf->putBitDouble(stop.value);
6938 buf->putBitShort(static_cast<std::uint16_t>(stop.aciColor));
6939 buf->putBitLong(static_cast<std::uint32_t>(stop.rgb));
6940 buf->putRawChar8(0);
6941 }
6942 sb->putVariableText(version, gradName);
6943}
6944
6945bool DRW_Hatch::encodeDwgBoundaryData(DRW::Version version, dwgBufferW *buf) const {
6946 buf->putBitLong(static_cast<std::int32_t>(looplist.size()));
6947
6948 for (const auto& lp : looplist) {
6949 // Strip bit 4 (pixel-size flag): DRW_Hatch has no storage for the
6950 // associated pixelSize BD, so a reader would desync if the flag were
6951 // set and we then omitted the field.
6952 buf->putBitLong(lp->type & ~4);
6953
6954 if (!(lp->type & 2)) {
6955 buf->putBitLong(static_cast<std::int32_t>(lp->objlist.size()));
6956 for (const auto& seg : lp->objlist) {
6957 if (const auto* ln = dynamic_cast<const DRW_Line*>(seg.get())) {
6958 buf->putRawChar8(1); // line
6959 buf->put2RawDouble(ln->basePoint);
6960 buf->put2RawDouble(ln->secPoint);
6961 } else if (const auto* arc = dynamic_cast<const DRW_Arc*>(seg.get())) {
6962 buf->putRawChar8(2); // circular arc
6963 buf->put2RawDouble(arc->basePoint);
6964 buf->putBitDouble(arc->radious);
6965 buf->putBitDouble(arc->staangle);
6966 buf->putBitDouble(arc->endangle);
6967 buf->putBit(static_cast<std::uint8_t>(arc->isccw));
6968 } else if (const auto* el = dynamic_cast<const DRW_Ellipse*>(seg.get())) {
6969 buf->putRawChar8(3); // ellipse arc
6970 buf->put2RawDouble(el->basePoint);
6971 buf->put2RawDouble(el->secPoint);
6972 buf->putBitDouble(el->ratio);
6973 buf->putBitDouble(el->staparam);
6974 buf->putBitDouble(el->endparam);
6975 buf->putBit(static_cast<std::uint8_t>(el->isccw));
6976 } else if (const auto* sp = dynamic_cast<const DRW_Spline*>(seg.get())) {
6977 buf->putRawChar8(4); // spline
6978 buf->putBitLong(sp->degree);
6979 bool isRational = (sp->flags & 4) != 0;
6980 bool isPeriodic = (sp->flags & 2) != 0;
6981 buf->putBit(static_cast<std::uint8_t>(isRational));
6982 buf->putBit(static_cast<std::uint8_t>(isPeriodic));
6983 buf->putBitLong(static_cast<std::int32_t>(sp->knotslist.size()));
6984 buf->putBitLong(static_cast<std::int32_t>(sp->controllist.size()));
6985 for (double k : sp->knotslist)
6986 buf->putBitDouble(k);
6987 for (const auto& cp : sp->controllist) {
6988 DRW_Coord c2{cp->x, cp->y, 0.0};
6989 buf->put2RawDouble(c2);
6990 if (isRational)
6991 buf->putBitDouble(cp->z);
6992 }
6993 if (version > DRW::AC1021) {
6994 buf->putBitLong(static_cast<std::int32_t>(sp->fitlist.size()));
6995 for (const auto& fp : sp->fitlist) {
6996 DRW_Coord f2{fp->x, fp->y, 0.0};
6997 buf->put2RawDouble(f2);
6998 }
6999 buf->put2RawDouble(sp->tgStart);
7000 buf->put2RawDouble(sp->tgEnd);
7001 }
7002 } else {
7003 return false;
7004 }
7005 }
7006 } else {
7007 const DRW_LWPolyline* pl = nullptr;
7008 if (!lp->objlist.empty())
7009 pl = dynamic_cast<const DRW_LWPolyline*>(lp->objlist[0].get());
7010 if (!pl)
7011 return false;
7012
7013 bool asBulge = false;
7014 for (const auto& v : pl->vertlist)
7015 if (v->bulge != 0.0) { asBulge = true; break; }
7016
7017 buf->putBit(static_cast<std::uint8_t>(asBulge));
7018 buf->putBit(static_cast<std::uint8_t>(pl->flags & 1));
7019 buf->putBitLong(static_cast<std::int32_t>(pl->vertlist.size()));
7020 for (const auto& v : pl->vertlist) {
7021 buf->putRawDouble(v->x);
7022 buf->putRawDouble(v->y);
7023 if (asBulge)
7024 buf->putBitDouble(v->bulge);
7025 }
7026 }
7027
7028 buf->putBitLong(0); // numBoundHandles for this loop (0 = non-associative)
7029 }
7030
7031 return true;
7032}
7033
7034bool DRW_Hatch::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
7035 (void)bs;
7036 oType = 78; // HATCH class id — see dwgreader.cpp:1380
7037 if (!encodeDwgCommon(version, buf)) return false;
1
Assuming the condition is false
7038
7039 dwgBufferW *sb = strBuf ? strBuf : buf;
2
Taking false branch
3
Assuming 'strBuf' is null
4
'?' condition is false
7040 encodeDwgGradientData(version, buf, sb);
5
Passing 'sb' via 2nd parameter 'buf'
6
Calling 'DRW_Hatch::encodeDwgGradientData'
7041
7042 buf->putBitDouble(basePoint.z); // BD: elevation
7043 buf->put3BitDouble(extPoint); // 3BD: extrusion (NOT BE-style for HATCH)
7044 sb->putVariableText(version, name); // TV: hatch pattern name
7045 buf->putBit(static_cast<std::uint8_t>(solid));
7046 buf->putBit(static_cast<std::uint8_t>(associative));
7047 if (!encodeDwgBoundaryData(version, buf)) return false;
7048
7049 buf->putBitShort(static_cast<std::uint16_t>(hstyle));
7050 buf->putBitShort(static_cast<std::uint16_t>(hpattern));
7051
7052 if (!solid) {
7053 buf->putBitDouble(angle);
7054 buf->putBitDouble(scale);
7055 buf->putBit(static_cast<std::uint8_t>(doubleflag));
7056 // Pattern definition lines: the parseDwg reads them but DRW_Hatch
7057 // has no storage for per-line data, so emit 0 here.
7058 buf->putBitShort(0); // deflines = 0
7059 }
7060
7061 // pixelSize BD omitted: bit 4 is stripped from every emitted loop type
7062 // above (DRW_Hatch has no pixelSize storage), so havePixelSize is always
7063 // false on the read side and parseDwg never expects this field.
7064
7065 buf->putBitLong(static_cast<std::int32_t>(seedPoints.size()));
7066 for (const auto& sp : seedPoints) {
7067 buf->putRawDouble(sp.x);
7068 buf->putRawDouble(sp.y);
7069 }
7070
7071 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
7072 return true;
7073}
7074
7075bool DRW_Spline::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
7076 switch (code) {
7077 case 210:
7078 normalVec.x = reader->getDouble();
7079 break;
7080 case 220:
7081 normalVec.y = reader->getDouble();
7082 break;
7083 case 230:
7084 normalVec.z = reader->getDouble();
7085 break;
7086 case 12:
7087 tgStart.x = reader->getDouble();
7088 break;
7089 case 22:
7090 tgStart.y = reader->getDouble();
7091 break;
7092 case 32:
7093 tgStart.z = reader->getDouble();
7094 break;
7095 case 13:
7096 tgEnd.x = reader->getDouble();
7097 break;
7098 case 23:
7099 tgEnd.y = reader->getDouble();
7100 break;
7101 case 33:
7102 tgEnd.z = reader->getDouble();
7103 break;
7104 case 70:
7105 flags = reader->getInt32();
7106 break;
7107 case 71:
7108 degree = reader->getInt32();
7109 break;
7110 case 72:
7111 nknots = reader->getInt32();
7112 break;
7113 case 73:
7114 ncontrol = reader->getInt32();
7115 break;
7116 case 74:
7117 nfit = reader->getInt32();
7118 break;
7119 case 42:
7120 tolknot = reader->getDouble();
7121 break;
7122 case 43:
7123 tolcontrol = reader->getDouble();
7124 break;
7125 case 44:
7126 tolfit = reader->getDouble();
7127 break;
7128 case 10: {
7129 controlpoint = std::make_shared<DRW_Coord>();
7130 controllist.push_back(controlpoint);
7131 controlpoint->x = reader->getDouble();
7132 break; }
7133 case 20:
7134 if(controlpoint)
7135 controlpoint->y = reader->getDouble();
7136 break;
7137 case 30:
7138 if(controlpoint)
7139 controlpoint->z = reader->getDouble();
7140 break;
7141 case 11: {
7142 fitpoint = std::make_shared<DRW_Coord>();
7143 fitlist.push_back(fitpoint);
7144 fitpoint->x = reader->getDouble();
7145 break; }
7146 case 21:
7147 if(fitpoint)
7148 fitpoint->y = reader->getDouble();
7149 break;
7150 case 31:
7151 if(fitpoint)
7152 fitpoint->z = reader->getDouble();
7153 break;
7154 case 40:
7155 knotslist.push_back(reader->getDouble());
7156 break;
7157 case 41:
7158 weightlist.push_back(reader->getDouble());
7159 break;
7160 default:
7161 return DRW_Entity::parseCode(code, reader);
7162 }
7163
7164 return true;
7165}
7166
7167bool DRW_Helix::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
7168 if (code == 100) {
7169 const std::string subclass = reader->getString();
7170 m_parsingHelixSubclass = (subclass == "AcDbHelix");
7171 return true;
7172 }
7173
7174 if (!m_parsingHelixSubclass)
7175 return DRW_Spline::parseCode(code, reader);
7176
7177 switch (code) {
7178 case 90:
7179 m_majorVersion = reader->getInt32();
7180 break;
7181 case 91:
7182 m_maintVersion = reader->getInt32();
7183 break;
7184 case 10:
7185 axisBasePt.x = reader->getDouble();
7186 break;
7187 case 20:
7188 axisBasePt.y = reader->getDouble();
7189 break;
7190 case 30:
7191 axisBasePt.z = reader->getDouble();
7192 break;
7193 case 11:
7194 startPt.x = reader->getDouble();
7195 break;
7196 case 21:
7197 startPt.y = reader->getDouble();
7198 break;
7199 case 31:
7200 startPt.z = reader->getDouble();
7201 break;
7202 case 12:
7203 axisVector.x = reader->getDouble();
7204 break;
7205 case 22:
7206 axisVector.y = reader->getDouble();
7207 break;
7208 case 32:
7209 axisVector.z = reader->getDouble();
7210 break;
7211 case 40:
7212 radius = reader->getDouble();
7213 break;
7214 case 41:
7215 turns = reader->getDouble();
7216 break;
7217 case 42:
7218 turnHeight = reader->getDouble();
7219 break;
7220 case 290:
7221 handedness = reader->getInt32() != 0;
7222 break;
7223 case 280:
7224 constraintType = static_cast<std::uint8_t>(reader->getInt32() & 0xff);
7225 break;
7226 default:
7227 return DRW_Entity::parseCode(code, reader);
7228 }
7229
7230 return true;
7231}
7232
7233bool DRW_Spline::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
7234 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
7235 if (!ret)
7236 return ret;
7237 if (!parseDwgSplineBody(version, buf))
7238 return false;
7239 /* Common Entity Handle Data */
7240 ret = DRW_Entity::parseDwgEntHandle(version, buf);
7241 if (!ret)
7242 return ret;
7243// RS crc; //RS */
7244 return buf->isGood();
7245}
7246
7247// Spline body decode: the scenario/degree/knots/ctrl/fit section, WITHOUT
7248// the leading DRW_Entity::parseDwg(common) or the trailing parseDwgEntHandle.
7249// Factored out so DRW_Helix can reuse the identical spline payload before its
7250// AcDbHelix trailer (Phase 8a-1).
7251bool DRW_Spline::parseDwgSplineBody(DRW::Version version, dwgBuffer *buf){
7252 DRW_DBG("\n***************************** parsing spline *********************************************\n")DRW_dbg::dbg("\n***************************** parsing spline *********************************************\n"
)
;
7253 std::uint8_t weight = 0; // RLZ ??? flags, weight, code 70, bit 4 (16)
7254
7255 std::int32_t scenario = buf->getBitLong();
7256 m_scenario = scenario;
7257 DRW_DBG("scenario: ")DRW_dbg::dbg("scenario: "); DRW_DBG(scenario)DRW_dbg::dbg(scenario);
7258 if (version > DRW::AC1024) {
7259 std::int32_t splFlag1 = buf->getBitLong();
7260 m_splineFlags1 = splFlag1;
7261 std::int32_t knotParam = buf->getBitLong();
7262 m_knotParam = knotParam;
7263 if (knotParam == kSplineKnotParamCustom || !(splFlag1 & kSplineFlagUseKnotParameter)) {
7264 scenario = 1;
7265 } else if (splFlag1 & kSplineFlagMethodFitPoints) {
7266 scenario = 2;
7267 }
7268 m_scenario = scenario;
7269 DRW_DBG(" 2013 splFlag1: ")DRW_dbg::dbg(" 2013 splFlag1: "); DRW_DBG(splFlag1)DRW_dbg::dbg(splFlag1);
7270 DRW_DBG(" 2013 knotParam: ")DRW_dbg::dbg(" 2013 knotParam: "); DRW_DBG(knotParam)DRW_dbg::dbg(knotParam);
7271// DRW_DBG("unk bit: "); DRW_DBG(buf->getBit());
7272 }
7273 degree = buf->getBitLong(); //RLZ: code 71, verify with dxf
7274 DRW_DBG(" degree: ")DRW_dbg::dbg(" degree: "); DRW_DBG(degree)DRW_dbg::dbg(degree); DRW_DBG("\n")DRW_dbg::dbg("\n");
7275 if (!isValidSplineDegree(degree)) {
7276 DRW_DBG("\ndwg Spline, invalid degree ")DRW_dbg::dbg("\ndwg Spline, invalid degree "); DRW_DBG(degree)DRW_dbg::dbg(degree); DRW_DBG("\n")DRW_dbg::dbg("\n");
7277 return false;
7278 }
7279 if (scenario == 2) {
7280 flags = 8;//scenario 2 = not rational & planar
7281 if (m_splineFlags1 & kSplineFlagClosed)
7282 flags |= 1;
7283 tolfit = buf->getBitDouble();//BD
7284 DRW_DBG("flags: ")DRW_dbg::dbg("flags: "); DRW_DBG(flags)DRW_dbg::dbg(flags); DRW_DBG(" tolfit: ")DRW_dbg::dbg(" tolfit: "); DRW_DBG(tolfit)DRW_dbg::dbg(tolfit);
7285 tgStart =buf->get3BitDouble();
7286 DRW_DBG(" Start Tangent: ")DRW_dbg::dbg(" Start Tangent: "); DRW_DBGPT(tgStart.x, tgStart.y, tgStart.z)DRW_dbg::dbgPT(tgStart.x, tgStart.y, tgStart.z);
7287 tgEnd =buf->get3BitDouble();
7288 DRW_DBG("\nEnd Tangent: ")DRW_dbg::dbg("\nEnd Tangent: "); DRW_DBGPT(tgEnd.x, tgEnd.y, tgEnd.z)DRW_dbg::dbgPT(tgEnd.x, tgEnd.y, tgEnd.z);
7289 nfit = buf->getBitLong();
7290 if (!isValidFitSplineLayout(degree, nfit)) {
7291 DRW_DBG("\ndwg Spline, invalid fit layout degree/count: ")DRW_dbg::dbg("\ndwg Spline, invalid fit layout degree/count: "
)
;
7292 DRW_DBG(degree)DRW_dbg::dbg(degree); DRW_DBG("/")DRW_dbg::dbg("/"); DRW_DBG(nfit)DRW_dbg::dbg(nfit); DRW_DBG("\n")DRW_dbg::dbg("\n");
7293 return false;
7294 }
7295 DRW_DBG("\nnumber of fit points: ")DRW_dbg::dbg("\nnumber of fit points: "); DRW_DBG(nfit)DRW_dbg::dbg(nfit);
7296 } else if (scenario == 1) {
7297 flags = 8;//scenario 1 = rational & planar
7298 flags |= buf->getBit() << 2; //flags, rational, code 70, bit 2 (4)
7299 flags |= buf->getBit(); //flags, closed, code 70, bit 0 (1)
7300 flags |= buf->getBit() << 1; //flags, periodic, code 70, bit 1 (2)
7301 tolknot = buf->getBitDouble();
7302 tolcontrol = buf->getBitDouble();
7303 DRW_DBG("flags: ")DRW_dbg::dbg("flags: "); DRW_DBG(flags)DRW_dbg::dbg(flags); DRW_DBG(" knot tolerance: ")DRW_dbg::dbg(" knot tolerance: "); DRW_DBG(tolknot)DRW_dbg::dbg(tolknot);
7304 DRW_DBG(" control point tolerance: ")DRW_dbg::dbg(" control point tolerance: "); DRW_DBG(tolcontrol)DRW_dbg::dbg(tolcontrol);
7305 nknots = buf->getBitLong();
7306 ncontrol = buf->getBitLong();
7307 if (!isValidControlSplineLayout(degree, nknots, ncontrol)) {
7308 DRW_DBG("\ndwg Spline, invalid control layout degree/knots/control: ")DRW_dbg::dbg("\ndwg Spline, invalid control layout degree/knots/control: "
)
;
7309 DRW_DBG(degree)DRW_dbg::dbg(degree); DRW_DBG("/")DRW_dbg::dbg("/"); DRW_DBG(nknots)DRW_dbg::dbg(nknots); DRW_DBG("/")DRW_dbg::dbg("/");
7310 DRW_DBG(ncontrol)DRW_dbg::dbg(ncontrol); DRW_DBG("\n")DRW_dbg::dbg("\n");
7311 return false;
7312 }
7313 weight = buf->getBit(); // flags bit 4: weights present (code 70)
7314 if (weight) flags |= 0x10;
7315 DRW_DBG("\nnum of knots: ")DRW_dbg::dbg("\nnum of knots: "); DRW_DBG(nknots)DRW_dbg::dbg(nknots); DRW_DBG(" num of control pt: ")DRW_dbg::dbg(" num of control pt: ");
7316 DRW_DBG(ncontrol)DRW_dbg::dbg(ncontrol); DRW_DBG(" weight bit: ")DRW_dbg::dbg(" weight bit: "); DRW_DBG(weight)DRW_dbg::dbg(weight);
7317 } else {
7318 DRW_DBG("\ndwg Spline, unknown scenario ")DRW_dbg::dbg("\ndwg Spline, unknown scenario "); DRW_DBG(scenario)DRW_dbg::dbg(scenario);
7319 DRW_DBG(" (expected 1 or 2)\n")DRW_dbg::dbg(" (expected 1 or 2)\n");
7320 return false; //RLZ: from doc only 1 or 2 are ok ?
7321 }
7322
7323 if (!DRW::reserve( knotslist, nknots)) {
7324 return false;
7325 }
7326 for (std::int32_t i= 0; i<nknots; ++i){
7327 knotslist.push_back (buf->getBitDouble());
7328 }
7329 if (!DRW::reserve( controllist, ncontrol)) {
7330 return false;
7331 }
7332 if (weight && !DRW::reserve(weightlist, ncontrol)) {
7333 return false;
7334 }
7335 for (std::int32_t i= 0; i<ncontrol; ++i){
7336 controllist.push_back(std::make_shared<DRW_Coord>(buf->get3BitDouble()));
7337 if (weight) {
7338 //per-control-point weight; required for hyperbola/parabola
7339 //conic detection in consumers (e.g. LibreCAD addSpline)
7340 double w = buf->getBitDouble(); //RLZ Warning: D (BD or RD)
7341 weightlist.push_back(w);
7342 DRW_DBG("\n w: ")DRW_dbg::dbg("\n w: "); DRW_DBG(w)DRW_dbg::dbg(w);
7343 }
7344 }
7345 if (!DRW::reserve( fitlist, nfit)) {
7346 return false;
7347 }
7348 for (std::int32_t i= 0; i<nfit; ++i)
7349 fitlist.push_back(std::make_shared<DRW_Coord>(buf->get3BitDouble()));
7350
7351 if (DRW_DBGGLDRW_dbg::getInstance()->getLevel() == DRW_dbg::Level::Debug) {
7352 DRW_DBG("\nknots list: ")DRW_dbg::dbg("\nknots list: ");
7353 for (auto const& v: knotslist) {
7354 DRW_DBG("\n")DRW_dbg::dbg("\n"); DRW_DBG(v)DRW_dbg::dbg(v);
7355 }
7356 DRW_DBG("\ncontrol point list: ")DRW_dbg::dbg("\ncontrol point list: ");
7357 for (auto const& v: controllist) {
7358 DRW_DBG("\n")DRW_dbg::dbg("\n"); DRW_DBGPT(v->x, v->y, v->z)DRW_dbg::dbgPT(v->x, v->y, v->z);
7359 }
7360 DRW_DBG("\nfit point list: ")DRW_dbg::dbg("\nfit point list: ");
7361 for (auto const& v: fitlist) {
7362 DRW_DBG("\n")DRW_dbg::dbg("\n"); DRW_DBGPT(v->x, v->y, v->z)DRW_dbg::dbgPT(v->x, v->y, v->z);
7363 }
7364 }
7365
7366 return buf->isGood();
7367}
7368
7369// AcDbHelix trailer order (libreDWG dwg2.spec:2493-2503):
7370// major_version BL, maint_version BL, axis_base_pt 3BD, start_pt 3BD,
7371// axis_vector 3BD, radius BD, turns BD, turn_height BD, handedness B,
7372// constraint_type RC.
7373bool DRW_Helix::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
7374 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
7375 if (!ret)
7376 return ret;
7377 DRW_DBG("\n***************************** parsing helix *********************************************\n")DRW_dbg::dbg("\n***************************** parsing helix *********************************************\n"
)
;
7378 if (!parseDwgSplineBody(version, buf))
7379 return false;
7380
7381 // AcDbHelix trailer (see field order above).
7382 m_majorVersion = buf->getBitLong();
7383 m_maintVersion = buf->getBitLong();
7384 axisBasePt = buf->get3BitDouble();
7385 startPt = buf->get3BitDouble();
7386 axisVector = buf->get3BitDouble();
7387 radius = buf->getBitDouble();
7388 turns = buf->getBitDouble();
7389 turnHeight = buf->getBitDouble();
7390 handedness = buf->getBit() != 0;
7391 constraintType = buf->getRawChar8();
7392 DRW_DBG("\nhelix radius: ")DRW_dbg::dbg("\nhelix radius: "); DRW_DBG(radius)DRW_dbg::dbg(radius); DRW_DBG(" turns: ")DRW_dbg::dbg(" turns: "); DRW_DBG(turns)DRW_dbg::dbg(turns);
7393
7394 /* Common Entity Handle Data */
7395 ret = DRW_Entity::parseDwgEntHandle(version, buf);
7396 if (!ret)
7397 return ret;
7398 // RS crc; //RS */
7399 return buf->isGood();
7400}
7401
7402bool DRW_Image::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
7403 switch (code) {
7404 case 12:
7405 vVector.x = reader->getDouble();
7406 break;
7407 case 22:
7408 vVector.y = reader->getDouble();
7409 break;
7410 case 32:
7411 vVector.z = reader->getDouble();
7412 break;
7413 case 13:
7414 sizeu = reader->getDouble();
7415 break;
7416 case 23:
7417 sizev = reader->getDouble();
7418 break;
7419 case 70:
7420 m_displayProps = reader->getInt32();
7421 break;
7422 case 340:
7423 ref = reader->getHandleString();
7424 break;
7425 case 360:
7426 m_imageDefReactorHandle = reader->getHandleString();
7427 break;
7428 case 280:
7429 clip = reader->getInt32();
7430 break;
7431 case 281:
7432 brightness = reader->getInt32();
7433 break;
7434 case 282:
7435 contrast = reader->getInt32();
7436 break;
7437 case 283:
7438 fade = reader->getInt32();
7439 break;
7440 case 71:
7441 m_clipBoundaryType = reader->getInt32();
7442 break;
7443 case 91:
7444 // The declared count is a structural invariant: reject negative or
7445 // implausibly large values before reserve() can allocate unboundedly.
7446 {
7447 constexpr std::int32_t kMaxClipVertices = 100000;
7448 const std::int32_t count = reader->getInt32();
7449 if (count < 0 || count > kMaxClipVertices)
7450 return false;
7451 clipPath.clear();
7452 clipPath.reserve(static_cast<size_t>(count));
7453 m_declaredClipVertexCount = count;
7454 m_clipPathHasOpenVertex = false;
7455 }
7456 break;
7457 case 14:
7458 // WIPEOUT polygon vertex x — start a new vertex. Group 24 (y) follows.
7459 if (m_clipPathHasOpenVertex)
7460 return false;
7461 clipPath.emplace_back(reader->getDouble(), 0.0);
7462 m_clipPathHasOpenVertex = true;
7463 break;
7464 case 24:
7465 // WIPEOUT polygon vertex y — complete the most recently started vertex.
7466 if (!m_clipPathHasOpenVertex || clipPath.empty())
7467 return false;
7468 clipPath.back().y = reader->getDouble();
7469 m_clipPathHasOpenVertex = false;
7470 break;
7471 case 290:
7472 // R2010+ Clip mode (IMAGE/WIPEOUT, ODA spec §20.4.80):
7473 // 0 = mask outside the polygon, 1 = mask inside.
7474 clipMode = reader->getBool();
7475 break;
7476 default:
7477 return DRW_Line::parseCode(code, reader);
7478 }
7479
7480 return true;
7481}
7482
7483bool DRW_Image::hasValidClipBoundary() const {
7484 if (m_clipPathHasOpenVertex
7485 || (m_declaredClipVertexCount >= 0
7486 && static_cast<std::size_t>(m_declaredClipVertexCount) != clipPath.size())) {
7487 return false;
7488 }
7489 switch (m_clipBoundaryType) {
7490 case 0:
7491 return clipPath.empty();
7492 case 1:
7493 return clipPath.size() == 2;
7494 case 2:
7495 return clipPath.size() >= 3;
7496 default:
7497 return false;
7498 }
7499}
7500
7501bool DRW_Image::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
7502 dwgBuffer sBuff = *buf;
7503 dwgBuffer *sBuf = buf;
7504 if (version > DRW::AC1018) {//2007+
7505 sBuf = &sBuff; //separate buffer for strings
7506 }
7507 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
7508 if (!ret)
7509 return ret;
7510 DRW_DBG("\n***************************** parsing image *********************************************\n")DRW_dbg::dbg("\n***************************** parsing image *********************************************\n"
)
;
7511
7512 std::int32_t classVersion = buf->getBitLong();
7513 DRW_DBG("class Version: ")DRW_dbg::dbg("class Version: "); DRW_DBG(classVersion)DRW_dbg::dbg(classVersion);
7514 basePoint = buf->get3BitDouble();
7515 DRW_DBG("\nbase point: ")DRW_dbg::dbg("\nbase point: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
7516 secPoint = buf->get3BitDouble();
7517 DRW_DBG("\nU vector: ")DRW_dbg::dbg("\nU vector: "); DRW_DBGPT(secPoint.x, secPoint.y, secPoint.z)DRW_dbg::dbgPT(secPoint.x, secPoint.y, secPoint.z);
7518 vVector = buf->get3BitDouble();
7519 DRW_DBG("\nV vector: ")DRW_dbg::dbg("\nV vector: "); DRW_DBGPT(vVector.x, vVector.y, vVector.z)DRW_dbg::dbgPT(vVector.x, vVector.y, vVector.z);
7520 sizeu = buf->getRawDouble();
7521 sizev = buf->getRawDouble();
7522 DRW_DBG("\nsize U: ")DRW_dbg::dbg("\nsize U: "); DRW_DBG(sizeu)DRW_dbg::dbg(sizeu); DRW_DBG("\nsize V: ")DRW_dbg::dbg("\nsize V: "); DRW_DBG(sizev)DRW_dbg::dbg(sizev);
7523 m_displayProps = buf->getBitShort();
7524 DRW_DBG("\ndisplay props: ")DRW_dbg::dbg("\ndisplay props: "); DRW_DBG(m_displayProps)DRW_dbg::dbg(m_displayProps);
7525 clip = buf->getBit();
7526 brightness = buf->getRawChar8();
7527 contrast = buf->getRawChar8();
7528 fade = buf->getRawChar8();
7529 if (version > DRW::AC1021){ //2010+
7530 clipMode = buf->getBit() != 0; // ODA §20.4.80: Clip mode B (R2010+)
7531 }
7532 m_clipBoundaryType = buf->getBitShort();
7533 clipPath.clear();
7534 if (m_clipBoundaryType == 0) {
7535 // No clip boundary payload.
7536 } else if (m_clipBoundaryType == 1){
7537 // Rectangles are encoded as exactly two opposite corners. Keep that
7538 // canonical payload intact; rendering expands it independently.
7539 DRW_Coord ll = buf->get2RawDouble();
7540 DRW_Coord ur = buf->get2RawDouble();
7541 clipPath.push_back(ll);
7542 clipPath.push_back(ur);
7543 m_declaredClipVertexCount = 2;
7544 } else if (m_clipBoundaryType == 2) {
7545 std::int32_t numVerts = buf->getBitLong();
7546 if (numVerts < 0 || numVerts > 100000)
7547 return false;
7548 clipPath.reserve(numVerts);
7549 for (int i= 0; i< numVerts;++i)
7550 clipPath.push_back(buf->get2RawDouble());
7551 m_declaredClipVertexCount = numVerts;
7552 } else {
7553 DRW_DBG("unsupported image clip type: ")DRW_dbg::dbg("unsupported image clip type: "); DRW_DBG(m_clipBoundaryType)DRW_dbg::dbg(m_clipBoundaryType); DRW_DBG("\n")DRW_dbg::dbg("\n");
7554 return false;
7555 }
7556
7557 ret = DRW_Entity::parseDwgEntHandle(version, buf);
7558 if (!ret)
7559 return ret;
7560 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
7561
7562 dwgHandle biH = buf->getHandle();
7563 DRW_DBG("ImageDef Handle: ")DRW_dbg::dbg("ImageDef Handle: "); DRW_DBGHL(biH.code, biH.size, biH.ref)DRW_dbg::dbgHL(biH.code, biH.size, biH.ref);
7564 ref = biH.ref;
7565 biH = buf->getHandle();
7566 DRW_DBG("ImageDefReactor Handle: ")DRW_dbg::dbg("ImageDefReactor Handle: "); DRW_DBGHL(biH.code, biH.size, biH.ref)DRW_dbg::dbgHL(biH.code, biH.size, biH.ref);
7567 m_imageDefReactorHandle = biH.ref;
7568 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
7569// RS crc; //RS */
7570 return buf->isGood();
7571}
7572
7573// DRW_Image::encodeDwg — inverse of DRW_Image::parseDwg above (libreDWG
7574// dwg.spec:5533-5563). Body field order: BL class_version (0), 3 x 3BD
7575// (base/uvec/vvec), 2 x RD (sizeu/sizev), BS display_props, B clip,
7576// 3 x RC (brightness/contrast/fade), [R2010+ B clip_mode], BS
7577// clip_boundary_type + verts. Both handles (imagedef code 5 + reactor
7578// code 3) are emitted UNCONDITIONALLY at the END of the handle stream,
7579// matching parseDwg's order — NOT the spec's interleaved mid-stream slots.
7580bool DRW_Image::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
7581 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
7582 (void)bs; (void)strBuf;
7583 constexpr std::size_t kMaxClipVerts = 100000u;
7584 if (clipPath.size() > kMaxClipVerts) {
7585 DRW_DBG("IMAGE clip vertices exceed DWG limit\n")DRW_dbg::dbg("IMAGE clip vertices exceed DWG limit\n");
7586 return false;
7587 }
7588 // Callers sometimes populate clipPath without setting m_clipBoundaryType
7589 // (DXF import historically stored only the vertices). Infer a coherent type
7590 // so encode does not reject a well-formed polygon/rectangle path.
7591 if (m_clipBoundaryType == 0 && !clipPath.empty()) {
7592 if (clipPath.size() == 2)
7593 m_clipBoundaryType = 1;
7594 else if (clipPath.size() >= 3)
7595 m_clipBoundaryType = 2;
7596 }
7597 oType = 101; // IMAGE class id — see dwgreader.cpp case 101
7598 if (!encodeDwgCommon(version, buf)) return false;
7599
7600 buf->putBitLong(0); // class_version (reader discards; ODA emits 0)
7601 buf->putBitDouble(basePoint.x); buf->putBitDouble(basePoint.y); buf->putBitDouble(basePoint.z);
7602 buf->putBitDouble(secPoint.x); buf->putBitDouble(secPoint.y); buf->putBitDouble(secPoint.z); // uvec
7603 buf->putBitDouble(vVector.x); buf->putBitDouble(vVector.y); buf->putBitDouble(vVector.z);
7604 buf->putRawDouble(sizeu);
7605 buf->putRawDouble(sizev);
7606 buf->putBitShort(static_cast<std::uint16_t>(m_displayProps));
7607 buf->putBit(static_cast<std::uint8_t>(clip & 1));
7608 buf->putRawChar8(static_cast<std::uint8_t>(brightness));
7609 buf->putRawChar8(static_cast<std::uint8_t>(contrast));
7610 buf->putRawChar8(static_cast<std::uint8_t>(fade));
7611 if (version > DRW::AC1021) { // 2010+ clip mode
7612 buf->putBit(clipMode ? 1 : 0);
7613 }
7614 if (!hasValidClipBoundary()) {
7615 DRW_DBG("IMAGE has invalid clip boundary\n")DRW_dbg::dbg("IMAGE has invalid clip boundary\n");
7616 return false;
7617 }
7618 if (m_clipBoundaryType == 0) {
7619 buf->putBitShort(0); // clip_boundary_type 0 = none
7620 } else if (m_clipBoundaryType == 1) {
7621 buf->putBitShort(1);
7622 buf->put2RawDouble(clipPath[0]);
7623 buf->put2RawDouble(clipPath[1]);
7624 } else {
7625 buf->putBitShort(2);
7626 buf->putBitLong(static_cast<std::int32_t>(clipPath.size()));
7627 for (std::size_t i = 0; i < clipPath.size(); ++i)
7628 buf->put2RawDouble(clipPath[i]);
7629 }
7630
7631 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
7632
7633 // Emit both trailing handles UNCONDITIONALLY in parseDwg order:
7634 // imagedef (hard pointer, code 5) then imagedefreactor (hard owner, code 3).
7635 dwgBufferW *hb = handleBuf ? handleBuf : buf;
7636 auto makeHandle = [](std::uint8_t code, std::uint32_t r) {
7637 dwgHandle h;
7638 h.code = (r == 0) ? 0 : code;
7639 h.ref = r;
7640 h.size = 0;
7641 if (r != 0) { std::uint32_t t = r; while (t != 0) { t >>= 8; ++h.size; } }
7642 return h;
7643 };
7644 hb->putHandle(makeHandle(5, ref)); // imagedef (340)
7645 hb->putHandle(makeHandle(3, m_imageDefReactorHandle)); // imagedefreactor (360)
7646 return true;
7647}
7648
7649bool DRW_Wipeout::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
7650 return DRW_Image::parseCode(code, reader);
7651}
7652
7653bool DRW_Wipeout::hasValidBoundary() const {
7654 return m_clipBoundaryType != 0 && hasValidClipBoundary();
7655}
7656
7657bool DRW_Wipeout::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
7658 return DRW_Image::parseDwg(version, buf, bs) && hasValidBoundary();
7659}
7660
7661bool DRW_Wipeout::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
7662 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
7663 (void)bs; (void)strBuf;
7664 constexpr std::size_t kMaxClipVerts = 100000u;
7665 if (clipPath.size() > kMaxClipVerts) {
7666 DRW_DBG("WIPEOUT clip vertices exceed DWG limit\n")DRW_dbg::dbg("WIPEOUT clip vertices exceed DWG limit\n");
7667 return false;
7668 }
7669 if (!hasValidBoundary()) {
7670 DRW_DBG("WIPEOUT has invalid clip boundary\n")DRW_dbg::dbg("WIPEOUT has invalid clip boundary\n");
7671 return false;
7672 }
7673 oType = kDwgClassNum;
7674 if (!encodeDwgCommon(version, buf)) return false;
7675
7676 buf->putBitLong(0);
7677 buf->putBitDouble(basePoint.x); buf->putBitDouble(basePoint.y); buf->putBitDouble(basePoint.z);
7678 buf->putBitDouble(secPoint.x); buf->putBitDouble(secPoint.y); buf->putBitDouble(secPoint.z);
7679 buf->putBitDouble(vVector.x); buf->putBitDouble(vVector.y); buf->putBitDouble(vVector.z);
7680 buf->putRawDouble(sizeu);
7681 buf->putRawDouble(sizev);
7682 buf->putBitShort(static_cast<std::uint16_t>(m_displayProps));
7683 buf->putBit(static_cast<std::uint8_t>(clip & 1));
7684 buf->putRawChar8(static_cast<std::uint8_t>(brightness));
7685 buf->putRawChar8(static_cast<std::uint8_t>(contrast));
7686 buf->putRawChar8(static_cast<std::uint8_t>(fade));
7687 if (version > DRW::AC1021) {
7688 buf->putBit(clipMode ? 1 : 0);
7689 }
7690 if (m_clipBoundaryType == 1) {
7691 buf->putBitShort(1);
7692 buf->put2RawDouble(clipPath[0]);
7693 buf->put2RawDouble(clipPath[1]);
7694 } else {
7695 buf->putBitShort(2);
7696 buf->putBitLong(static_cast<std::int32_t>(clipPath.size()));
7697 for (std::size_t i = 0; i < clipPath.size(); ++i)
7698 buf->put2RawDouble(clipPath[i]);
7699 }
7700
7701 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
7702
7703 dwgBufferW *hb = handleBuf ? handleBuf : buf;
7704 auto makeHandle = [](std::uint8_t code, std::uint32_t r) {
7705 dwgHandle h;
7706 h.code = (r == 0) ? 0 : code;
7707 h.ref = r;
7708 h.size = 0;
7709 if (r != 0) { std::uint32_t t = r; while (t != 0) { t >>= 8; ++h.size; } }
7710 return h;
7711 };
7712 hb->putHandle(makeHandle(5, ref));
7713 hb->putHandle(makeHandle(3, m_imageDefReactorHandle));
7714 return true;
7715}
7716
7717bool DRW_PointCloud::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
7718 switch (code) {
7719 case 90: classVersion = reader->getInt32(); break;
7720 case 10: origin.x = reader->getDouble(); break;
7721 case 20: origin.y = reader->getDouble(); break;
7722 case 30: origin.z = reader->getDouble(); break;
7723 case 1: savedFilename = reader->getUtf8String(); break;
7724 case 91: sourceFileCount = reader->getInt32(); break;
7725 case 101:
7726 sourceFiles.clear();
7727 sourceFiles.reserve(static_cast<size_t>(sourceFileCount));
7728 break;
7729 case 300:
7730 if (sourceFiles.size() < static_cast<size_t>(sourceFileCount)) {
7731 sourceFiles.push_back(reader->getUtf8String());
7732 }
7733 break;
7734 case 11: extentsMin.x = reader->getDouble(); break;
7735 case 21: extentsMin.y = reader->getDouble(); break;
7736 case 31: extentsMin.z = reader->getDouble(); break;
7737 case 12: extentsMax.x = reader->getDouble(); break;
7738 case 22: extentsMax.y = reader->getDouble(); break;
7739 case 32: extentsMax.z = reader->getDouble(); break;
7740 case 92: pointCount = reader->getInt64(); break;
7741 case 2: ucsName = reader->getUtf8String(); break;
7742 case 13: ucsOrigin.x = reader->getDouble(); break;
7743 case 23: ucsOrigin.y = reader->getDouble(); break;
7744 case 33: ucsOrigin.z = reader->getDouble(); break;
7745 case 14: ucsXDirection.x = reader->getDouble(); break;
7746 case 24: ucsXDirection.y = reader->getDouble(); break;
7747 case 34: ucsXDirection.z = reader->getDouble(); break;
7748 case 15: ucsYDirection.x = reader->getDouble(); break;
7749 case 25: ucsYDirection.y = reader->getDouble(); break;
7750 case 35: ucsYDirection.z = reader->getDouble(); break;
7751 case 16: ucsZDirection.x = reader->getDouble(); break;
7752 case 26: ucsZDirection.y = reader->getDouble(); break;
7753 case 36: ucsZDirection.z = reader->getDouble(); break;
7754 case 340: definitionHandle = static_cast<std::uint32_t>(reader->getHandleString()); break;
7755 case 360: reactorHandle = static_cast<std::uint32_t>(reader->getHandleString()); break;
7756 case 290: showIntensity = reader->getBool(); break;
7757 case 280: intensityScheme = reader->getInt32(); break;
7758 case 441: intensityStyle.minIntensity = reader->getDouble(); break;
7759 case 442: intensityStyle.maxIntensity = reader->getDouble(); break;
7760 case 443: intensityStyle.lowThreshold = reader->getDouble(); break;
7761 case 444: intensityStyle.highThreshold = reader->getDouble(); break;
7762 case 291: showClipping = reader->getBool(); break;
7763 case 93: clippingCount = reader->getInt32(); break;
7764 default:
7765 return DRW_Entity::parseCode(code, reader);
7766 }
7767 return true;
7768}
7769
7770bool DRW_PointCloud::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
7771 return DRW_Entity::parseDwg(version, buf, nullptr, bs);
7772}
7773
7774bool DRW_PointCloud::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
7775 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
7776 // Typed POINTCLOUD body encode is not implemented. Refuse rather than
7777 // emit a header-only stub that third-party readers reject.
7778 (void)version; (void)buf; (void)bs; (void)strBuf; (void)handleBuf;
7779 return false;
7780}
7781
7782bool DRW_PointCloudEx::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
7783 switch (code) {
7784 case 90: classVersion = reader->getInt32(); break;
7785 case 11: extentsMin.x = reader->getDouble(); break;
7786 case 21: extentsMin.y = reader->getDouble(); break;
7787 case 31: extentsMin.z = reader->getDouble(); break;
7788 case 12: extentsMax.x = reader->getDouble(); break;
7789 case 22: extentsMax.y = reader->getDouble(); break;
7790 case 32: extentsMax.z = reader->getDouble(); break;
7791 case 13: ucsOrigin.x = reader->getDouble(); break;
7792 case 23: ucsOrigin.y = reader->getDouble(); break;
7793 case 33: ucsOrigin.z = reader->getDouble(); break;
7794 case 14: ucsXDirection.x = reader->getDouble(); break;
7795 case 24: ucsXDirection.y = reader->getDouble(); break;
7796 case 34: ucsXDirection.z = reader->getDouble(); break;
7797 case 15: ucsYDirection.x = reader->getDouble(); break;
7798 case 25: ucsYDirection.y = reader->getDouble(); break;
7799 case 35: ucsYDirection.z = reader->getDouble(); break;
7800 case 16: ucsZDirection.x = reader->getDouble(); break;
7801 case 26: ucsZDirection.y = reader->getDouble(); break;
7802 case 36: ucsZDirection.z = reader->getDouble(); break;
7803 case 290: isLocked = reader->getBool(); break;
7804 case 340: definitionHandle = static_cast<std::uint32_t>(reader->getHandleString()); break;
7805 case 360: reactorHandle = static_cast<std::uint32_t>(reader->getHandleString()); break;
7806 case 1: name = reader->getUtf8String(); break;
7807 case 291: showIntensity = reader->getBool(); break;
7808 case 292: showCropping = reader->getBool(); break;
7809 case 91: croppingCount = reader->getInt32(); break;
7810 case 92: unknownInt0 = reader->getInt32(); break;
7811 case 93: unknownInt1 = reader->getInt32(); break;
7812 case 280: stylizationType = reader->getInt32(); break;
7813 case 300: intensityColorScheme = reader->getUtf8String(); break;
7814 case 301: currentColorScheme = reader->getUtf8String(); break;
7815 case 302: classificationColorScheme = reader->getUtf8String(); break;
7816 case 440: elevationMin = reader->getDouble(); break;
7817 case 441: elevationMax = reader->getDouble(); break;
7818 case 442: intensityMin = reader->getDouble(); break;
7819 case 443: intensityMax = reader->getDouble(); break;
7820 case 281: intensityOutOfRangeBehavior = reader->getInt32(); break;
7821 case 282: elevationOutOfRangeBehavior = reader->getInt32(); break;
7822 case 293: elevationApplyToFixedRange = reader->getBool(); break;
7823 case 294: intensityAsGradient = reader->getBool(); break;
7824 case 295: elevationAsGradient = reader->getBool(); break;
7825 default:
7826 return DRW_Entity::parseCode(code, reader);
7827 }
7828 return true;
7829}
7830
7831bool DRW_PointCloudEx::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
7832 return DRW_Entity::parseDwg(version, buf, nullptr, bs);
7833}
7834
7835bool DRW_PointCloudEx::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
7836 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
7837 (void)version; (void)buf; (void)bs; (void)strBuf; (void)handleBuf;
7838 return false;
7839}
7840
7841bool DRW_Surface::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
7842 switch (code) {
7843 case 70:
7844 modelerFormatVersion = reader->getInt32();
7845 break;
7846 case 71:
7847 uIsolines = reader->getInt32();
7848 break;
7849 case 72:
7850 vIsolines = reader->getInt32();
7851 break;
7852 case 310:
7853 {
7854 std::vector<std::uint8_t> decoded;
7855 if (!decodeHexBytes(reader->getString(), decoded))
7856 return false;
7857 appendBytes(rawAcisData, decoded);
7858 }
7859 break;
7860 default:
7861 return DRW_Entity::parseCode(code, reader);
7862 }
7863 return true;
7864}
7865
7866bool DRW_Surface::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
7867 return DRW_Entity::parseDwg(version, buf, nullptr, bs);
7868}
7869
7870bool DRW_Surface::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
7871 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
7872 // Surface/ACIS body encode is not implemented as a full typed path.
7873 (void)version; (void)buf; (void)bs; (void)strBuf; (void)handleBuf;
7874 return false;
7875}
7876
7877bool DRW_Dimension::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
7878 switch (code) {
7879 case 1:
7880 text = reader->getUtf8String();
7881 break;
7882 case 2:
7883 name = reader->getString();
7884 break;
7885 case 3:
7886 style = reader->getUtf8String();
7887 break;
7888 case 70:
7889 type = reader->getInt32();
7890 break;
7891 case 71:
7892 align = reader->getInt32();
7893 break;
7894 case 72:
7895 linesty = reader->getInt32();
7896 break;
7897 case 10:
7898 defPoint.x = reader->getDouble();
7899 break;
7900 case 20:
7901 defPoint.y = reader->getDouble();
7902 break;
7903 case 30:
7904 defPoint.z = reader->getDouble();
7905 break;
7906 case 11:
7907 textPoint.x = reader->getDouble();
7908 break;
7909 case 21:
7910 textPoint.y = reader->getDouble();
7911 break;
7912 case 31:
7913 textPoint.z = reader->getDouble();
7914 break;
7915 case 12:
7916 clonePoint.x = reader->getDouble();
7917 break;
7918 case 22:
7919 clonePoint.y = reader->getDouble();
7920 break;
7921 case 32:
7922 clonePoint.z = reader->getDouble();
7923 break;
7924 case 13:
7925 def1.x = reader->getDouble();
7926 break;
7927 case 23:
7928 def1.y = reader->getDouble();
7929 break;
7930 case 33:
7931 def1.z = reader->getDouble();
7932 break;
7933 case 14:
7934 def2.x = reader->getDouble();
7935 break;
7936 case 24:
7937 def2.y = reader->getDouble();
7938 break;
7939 case 34:
7940 def2.z = reader->getDouble();
7941 break;
7942 case 15:
7943 circlePoint.x = reader->getDouble();
7944 break;
7945 case 25:
7946 circlePoint.y = reader->getDouble();
7947 break;
7948 case 35:
7949 circlePoint.z = reader->getDouble();
7950 break;
7951 case 16:
7952 arcPoint.x = reader->getDouble();
7953 break;
7954 case 26:
7955 arcPoint.y = reader->getDouble();
7956 break;
7957 case 36:
7958 arcPoint.z = reader->getDouble();
7959 break;
7960 case 41:
7961 linefactor = reader->getDouble();
7962 break;
7963 case 53:
7964 rot = reader->getDouble();
7965 break;
7966 case 50:
7967 angle = reader->getDouble();
7968 break;
7969 case 52:
7970 oblique = reader->getDouble();
7971 break;
7972 case 40:
7973 length = reader->getDouble();
7974 break;
7975 case 51:
7976 hdir = reader->getDouble();
7977 break;
7978 case 42:
7979 measureValue = reader->getDouble();
7980 break;
7981 case 74:
7982 flipArrow1 = reader->getInt32() != 0;
7983 break;
7984 case 75:
7985 flipArrow2 = reader->getInt32() != 0;
7986 break;
7987 case 76:
7988 genTol = reader->getInt32() != 0;
7989 break;
7990 case 77:
7991 limGen = reader->getInt32() != 0;
7992 break;
7993 case 43:
7994 tolPlus = reader->getDouble();
7995 break;
7996 case 44:
7997 tolMinus = reader->getDouble();
7998 break;
7999 case 45:
8000 tolScale = reader->getDouble();
8001 break;
8002 case 78:
8003 tolDecimals = reader->getInt32();
8004 break;
8005 case 79:
8006 tolAlign = reader->getInt32();
8007 break;
8008 case 80:
8009 tolZero = reader->getInt32();
8010 break;
8011 case 81:
8012 altTolDecimals = reader->getInt32();
8013 break;
8014 case 82:
8015 altZero = reader->getInt32();
8016 break;
8017 case 83:
8018 altTolZero = reader->getInt32();
8019 break;
8020 case 84:
8021 textMove = reader->getInt32();
8022 break;
8023 default:
8024 return DRW_Entity::parseCode(code, reader);
8025 }
8026
8027 return true;
8028}
8029
8030bool DRW_Dimension::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs)
8031{
8032 DRW_UNUSED( version)(void)version;
8033 DRW_UNUSED( buf)(void)buf;
8034 DRW_UNUSED( bs)(void)bs;
8035
8036 DRW_DBG("DRW_Dimension::parseDwg(): base class implemntation should never be called direct!\n")DRW_dbg::dbg("DRW_Dimension::parseDwg(): base class implemntation should never be called direct!\n"
)
;
8037
8038 return false;
8039}
8040
8041bool DRW_Dimension::parseDwg(DRW::Version version, dwgBuffer *buf, dwgBuffer *sBuf, std::uint32_t bs /*= 0*/) {
8042 dwgBuffer sBuff = *buf;
8043 sBuf = buf;
8044 if (version > DRW::AC1018) {//2007+
8045 sBuf = &sBuff; //separate buffer for strings
8046 }
8047
8048 if (!DRW_Entity::parseDwg( version, buf, sBuf, bs)) {
8049 return false;
8050 }
8051
8052 DRW_DBG("\n***************************** parsing dimension *********************************************")DRW_dbg::dbg("\n***************************** parsing dimension *********************************************"
)
;
8053 if (version > DRW::AC1021) { //2010+
8054 std::uint8_t dimVersion = buf->getRawChar8();
8055 DRW_DBG("\ndimVersion: ")DRW_dbg::dbg("\ndimVersion: "); DRW_DBG(dimVersion)DRW_dbg::dbg(dimVersion);
8056 }
8057 // ODA §20.4.22: Extrusion is plain 3BD (NOT BE) — confirmed by libreDWG dwg_spec_shared.h
8058 extPoint = buf->get3BitDouble();
8059 DRW_DBG("\nextPoint: ")DRW_dbg::dbg("\nextPoint: "); DRW_DBGPT(extPoint.x, extPoint.y, extPoint.z)DRW_dbg::dbgPT(extPoint.x, extPoint.y, extPoint.z);
8060 textPoint.x = buf->getRawDouble();
8061 textPoint.y = buf->getRawDouble();
8062 textPoint.z = buf->getBitDouble();
8063 DRW_DBG("\ntextPoint: ")DRW_dbg::dbg("\ntextPoint: "); DRW_DBGPT(textPoint.x, textPoint.y, textPoint.z)DRW_dbg::dbgPT(textPoint.x, textPoint.y, textPoint.z);
8064 type = buf->getRawChar8();
8065 DRW_DBG("\ntype (70) read: ")DRW_dbg::dbg("\ntype (70) read: "); DRW_DBG(type)DRW_dbg::dbg(type);
8066 type = (type & 1) ? type & 0x7F : type | 0x80; //set bit 7
8067 type = (type & 2) ? type | 0x20 : type & 0xDF; //set bit 5
8068 DRW_DBG(" type (70) set: ")DRW_dbg::dbg(" type (70) set: "); DRW_DBG(type)DRW_dbg::dbg(type);
8069 //clear last 3 bits to set integer dim type
8070 type &= 0xF8;
8071 text = sBuf->getVariableText(version, false);
8072 DRW_DBG("\nforced dim text: ")DRW_dbg::dbg("\nforced dim text: "); DRW_DBG(text.c_str())DRW_dbg::dbg(text.c_str());
8073 rot = buf->getBitDouble();
8074 hdir = buf->getBitDouble();
8075 DRW_Coord inspoint = buf->get3BitDouble();
8076 DRW_DBG("\ninspoint: ")DRW_dbg::dbg("\ninspoint: "); DRW_DBGPT(inspoint.x, inspoint.y, inspoint.z)DRW_dbg::dbgPT(inspoint.x, inspoint.y, inspoint.z);
8077 double insRot_code54 = buf->getBitDouble(); //RLZ: unknown, investigate
8078 DRW_DBG(" insRot_code54: ")DRW_dbg::dbg(" insRot_code54: "); DRW_DBG(insRot_code54)DRW_dbg::dbg(insRot_code54);
8079 if (version > DRW::AC1014) { //2000+
8080 align = buf->getBitShort();
8081 linesty = buf->getBitShort();
8082 linefactor = buf->getBitDouble();
8083 measureValue = buf->getBitDouble();
8084 DRW_DBG("\n actMeas_code42: ")DRW_dbg::dbg("\n actMeas_code42: "); DRW_DBG(measureValue)DRW_dbg::dbg(measureValue);
8085 if (version > DRW::AC1018) { //2007+
8086 bool unk = buf->getBit();
8087 flipArrow1 = buf->getBit();
8088 flipArrow2 = buf->getBit();
8089 DRW_DBG("\n2007, unk, flip1, flip2: ")DRW_dbg::dbg("\n2007, unk, flip1, flip2: "); DRW_DBG(unk)DRW_dbg::dbg(unk); DRW_DBG(flipArrow1)DRW_dbg::dbg(flipArrow1); DRW_DBG(flipArrow2)DRW_dbg::dbg(flipArrow2);
8090 }
8091 }
8092 clonePoint.x = buf->getRawDouble();
8093 clonePoint.y = buf->getRawDouble();
8094 DRW_DBG("\nclonePoint: ")DRW_dbg::dbg("\nclonePoint: "); DRW_DBGPT(clonePoint.x, clonePoint.y, clonePoint.z)DRW_dbg::dbgPT(clonePoint.x, clonePoint.y, clonePoint.z);
8095
8096 return buf->isGood();
8097}
8098
8099bool DRW_DimAligned::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
8100 if (!DRW_Dimension::parseDwg(version, buf, nullptr, bs)) {
8101 return false;
8102 }
8103
8104 if (oType == 0x15)
8105 DRW_DBG("\n***************************** parsing dim linear *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim linear *********************************************\n"
)
;
8106 else
8107 DRW_DBG("\n***************************** parsing dim aligned *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim aligned *********************************************\n"
)
;
8108 DRW_Coord pt = buf->get3BitDouble();
8109 setPt3(pt); //def1
8110 DRW_DBG("def1: ")DRW_dbg::dbg("def1: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8111 pt = buf->get3BitDouble();
8112 setPt4(pt);
8113 DRW_DBG("\ndef2: ")DRW_dbg::dbg("\ndef2: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8114 pt = buf->get3BitDouble();
8115 setDefPoint(pt);
8116 DRW_DBG("\ndefPoint: ")DRW_dbg::dbg("\ndefPoint: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8117 setOb52(buf->getBitDouble() * ARAD57.29577951308232); // radians → degrees
8118 if (oType == 0x15)
8119 setAn50(buf->getBitDouble() * ARAD57.29577951308232);
8120 else
8121 type |= 1;
8122 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(type)DRW_dbg::dbg(type); DRW_DBG("\n")DRW_dbg::dbg("\n");
8123
8124 if (!DRW_Entity::parseDwgEntHandle(version, buf)) {
8125 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimAligned::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimAligned::parseDwg()\n"
)
;
8126 return false;
8127 }
8128 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8129 dimStyleH = buf->getHandle();
8130 DRW_DBG("dim style Handle: ")DRW_dbg::dbg("dim style Handle: "); DRW_DBGHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref)DRW_dbg::dbgHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8131 blockH = buf->getHandle(); /* H 7 STYLE (hard pointer) */
8132 DRW_DBG("anon block Handle: ")DRW_dbg::dbg("anon block Handle: "); DRW_DBGHL(blockH.code, blockH.size, blockH.ref)DRW_dbg::dbgHL(blockH.code, blockH.size, blockH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8133 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8134
8135 // RS crc; //RS */
8136 return buf->isGood();
8137 }
8138
8139bool DRW_DimRadial::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
8140 if (!DRW_Dimension::parseDwg(version, buf, nullptr, bs)) {
8141 return false;
8142 }
8143
8144 DRW_DBG("\n***************************** parsing dim radial *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim radial *********************************************\n"
)
;
8145 DRW_Coord pt = buf->get3BitDouble();
8146 setDefPoint(pt); //code 10
8147 DRW_DBG("defPoint: ")DRW_dbg::dbg("defPoint: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8148 pt = buf->get3BitDouble();
8149 setPt5(pt); //center pt code 15
8150 DRW_DBG("\ncenter point: ")DRW_dbg::dbg("\ncenter point: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8151 setRa40(buf->getBitDouble()); //leader length code 40
8152 DRW_DBG("\nleader length: ")DRW_dbg::dbg("\nleader length: "); DRW_DBG(getRa40())DRW_dbg::dbg(getRa40());
8153 type |= 4;
8154 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(type)DRW_dbg::dbg(type); DRW_DBG("\n")DRW_dbg::dbg("\n");
8155
8156 if (!DRW_Entity::parseDwgEntHandle(version, buf)) {
8157 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimRadial::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimRadial::parseDwg()\n"
)
;
8158 return false;
8159 }
8160 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8161 dimStyleH = buf->getHandle();
8162 DRW_DBG("dim style Handle: ")DRW_dbg::dbg("dim style Handle: "); DRW_DBGHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref)DRW_dbg::dbgHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8163 blockH = buf->getHandle(); /* H 7 STYLE (hard pointer) */
8164 DRW_DBG("anon block Handle: ")DRW_dbg::dbg("anon block Handle: "); DRW_DBGHL(blockH.code, blockH.size, blockH.ref)DRW_dbg::dbgHL(blockH.code, blockH.size, blockH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8165 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8166
8167 // RS crc; //RS */
8168 return buf->isGood();
8169}
8170
8171// DRW_DimLargeRadial (AcDbRadialDimensionLarge, LARGE_RADIAL_DIMENSION).
8172// DXF group-code parser: the AcDbRadialDimensionLarge subclass overloads codes
8173// 13/14/15/40 (chord / override center / jog point / jog angle), so gate them on
8174// the subclass marker (like DRW_DimArc). The chord point is stored as the radial
8175// diameter point so the existing addDimRadial consumer renders center→chord.
8176bool DRW_DimLargeRadial::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
8177 if (code == 100) {
8178 std::string s = reader->getString();
8179 if (s == "AcDbRadialDimensionLarge") {
8180 m_largeRadialSubclassSeen = true;
8181 return true;
8182 }
8183 return DRW_Dimension::parseCode(code, reader);
8184 }
8185 if (m_largeRadialSubclassSeen) {
8186 DRW_Coord chord;
8187 switch (code) {
8188 case 13: chord = getPt5(); chord.x = reader->getDouble(); setPt5(chord); return true;
8189 case 23: chord = getPt5(); chord.y = reader->getDouble(); setPt5(chord); return true;
8190 case 33: chord = getPt5(); chord.z = reader->getDouble(); setPt5(chord); return true;
8191 case 14: overrideCenterPoint.x = reader->getDouble(); return true;
8192 case 24: overrideCenterPoint.y = reader->getDouble(); return true;
8193 case 34: overrideCenterPoint.z = reader->getDouble(); return true;
8194 case 15: jogPoint.x = reader->getDouble(); return true;
8195 case 25: jogPoint.y = reader->getDouble(); return true;
8196 case 35: jogPoint.z = reader->getDouble(); return true;
8197 case 40: jogAngle = reader->getDouble(); return true;
8198 default: break;
8199 }
8200 }
8201 return DRW_Dimension::parseCode(code, reader);
8202}
8203
8204// DRW_DimLargeRadial DWG body: five subclass reads then the dim-style and
8205// anon-block handles. The three subclass points are ordered
8206// definition point, JOG point, jog angle, CHORD point, OVERRIDDEN center
8207// so that the decoded fields match the DXF group codes (chord=13, override=14,
8208// jog=15) and libdxfrw's own DXF parseCode. The read-only reference parser
8209// (parseLargeRadialDimension) labels the 2nd/4th/5th reads chord/override/jog,
8210// i.e. a cyclic rotation of the point roles; that is inconsistent with the DXF
8211// semantics and with an ODA File Converter DXF↔DWG round-trip (which preserves
8212// codes 13/14/15 exactly). Verified by large_radial_dim_dwg_tests.cpp against
8213// an ODA-synthesized fixture, cross-checked with the dwg-parser's DXF read.
8214// Only the field labels change vs. the reference parser — the read sizes/order
8215// (3BD, 3BD, BD, 3BD, 3BD) are identical, so buffer alignment is unchanged.
8216bool DRW_DimLargeRadial::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
8217 if (!DRW_Dimension::parseDwg(version, buf, nullptr, bs)) {
8218 return false;
8219 }
8220 setDefPoint(buf->get3BitDouble()); // definition point (code 10)
8221 jogPoint = buf->get3BitDouble(); // jog vertex (code 15)
8222 jogAngle = buf->getBitDouble(); // jog transverse angle (code 40)
8223 setPt5(buf->get3BitDouble()); // chord point → radial diameter point (code 13)
8224 overrideCenterPoint = buf->get3BitDouble(); // overridden center (code 14)
8225 type |= 4; // radial dimension type bit
8226 if (!DRW_Entity::parseDwgEntHandle(version, buf)) {
8227 return false;
8228 }
8229 dimStyleH = buf->getHandle();
8230 blockH = buf->getHandle();
8231 return buf->isGood();
8232}
8233
8234bool DRW_DimDiametric::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
8235 if (!DRW_Dimension::parseDwg(version, buf, nullptr, bs)) {
8236 return false;
8237 }
8238
8239 DRW_DBG("\n***************************** parsing dim diametric *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim diametric *********************************************\n"
)
;
8240 DRW_Coord pt = buf->get3BitDouble();
8241 setPt5(pt); //center pt code 15
8242 DRW_DBG("center point: ")DRW_dbg::dbg("center point: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8243 pt = buf->get3BitDouble();
8244 setDefPoint(pt); //code 10
8245 DRW_DBG("\ndefPoint: ")DRW_dbg::dbg("\ndefPoint: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8246 setRa40(buf->getBitDouble()); //leader length code 40
8247 DRW_DBG("\nleader length: ")DRW_dbg::dbg("\nleader length: "); DRW_DBG(getRa40())DRW_dbg::dbg(getRa40());
8248 type |= 3;
8249 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(type)DRW_dbg::dbg(type); DRW_DBG("\n")DRW_dbg::dbg("\n");
8250
8251 if (!DRW_Entity::parseDwgEntHandle(version, buf)) {
8252 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimDiametric::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimDiametric::parseDwg()\n"
)
;
8253 return false;
8254 }
8255 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8256 dimStyleH = buf->getHandle();
8257 DRW_DBG("dim style Handle: ")DRW_dbg::dbg("dim style Handle: "); DRW_DBGHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref)DRW_dbg::dbgHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8258 blockH = buf->getHandle(); /* H 7 STYLE (hard pointer) */
8259 DRW_DBG("anon block Handle: ")DRW_dbg::dbg("anon block Handle: "); DRW_DBGHL(blockH.code, blockH.size, blockH.ref)DRW_dbg::dbgHL(blockH.code, blockH.size, blockH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8260 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8261
8262 // RS crc; //RS */
8263 return buf->isGood();
8264}
8265
8266bool DRW_DimAngular::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
8267 if (!DRW_Dimension::parseDwg(version, buf, nullptr, bs)) {
8268 return false;
8269 }
8270
8271 DRW_DBG("\n***************************** parsing dim angular *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim angular *********************************************\n"
)
;
8272 DRW_Coord pt;
8273 pt.x = buf->getRawDouble();
8274 pt.y = buf->getRawDouble();
8275 setPt6(pt); //code 16
8276 DRW_DBG("arc Point: ")DRW_dbg::dbg("arc Point: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8277 pt = buf->get3BitDouble();
8278 setPt3(pt); //def1 code 13
8279 DRW_DBG("\ndef1: ")DRW_dbg::dbg("\ndef1: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8280 pt = buf->get3BitDouble();
8281 setPt4(pt); //def2 code 14
8282 DRW_DBG("\ndef2: ")DRW_dbg::dbg("\ndef2: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8283 pt = buf->get3BitDouble();
8284 setPt5(pt); //center pt code 15
8285 DRW_DBG("\ncenter point: ")DRW_dbg::dbg("\ncenter point: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8286 pt = buf->get3BitDouble();
8287 setDefPoint(pt); //code 10
8288 DRW_DBG("\ndefPoint: ")DRW_dbg::dbg("\ndefPoint: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8289 type |= 0x02;
8290 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(type)DRW_dbg::dbg(type); DRW_DBG("\n")DRW_dbg::dbg("\n");
8291
8292 if (!DRW_Entity::parseDwgEntHandle(version, buf)) {
8293 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimAngular::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimAngular::parseDwg()\n"
)
;
8294 return false;
8295 }
8296 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8297 dimStyleH = buf->getHandle();
8298 DRW_DBG("dim style Handle: ")DRW_dbg::dbg("dim style Handle: "); DRW_DBGHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref)DRW_dbg::dbgHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8299 blockH = buf->getHandle(); /* H 7 STYLE (hard pointer) */
8300 DRW_DBG("anon block Handle: ")DRW_dbg::dbg("anon block Handle: "); DRW_DBGHL(blockH.code, blockH.size, blockH.ref)DRW_dbg::dbgHL(blockH.code, blockH.size, blockH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8301 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8302
8303 // RS crc; //RS */
8304 return buf->isGood();
8305}
8306
8307bool DRW_DimAngular3p::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
8308 if (!DRW_Dimension::parseDwg(version, buf, nullptr, bs)) {
8309 return false;
8310 }
8311
8312 DRW_DBG("\n***************************** parsing dim angular3p *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim angular3p *********************************************\n"
)
;
8313 DRW_Coord pt = buf->get3BitDouble();
8314 setDefPoint(pt); //code 10
8315 DRW_DBG("defPoint: ")DRW_dbg::dbg("defPoint: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8316 pt = buf->get3BitDouble();
8317 setPt3(pt); //def1 code 13
8318 DRW_DBG("\ndef1: ")DRW_dbg::dbg("\ndef1: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8319 pt = buf->get3BitDouble();
8320 setPt4(pt); //def2 code 14
8321 DRW_DBG("\ndef2: ")DRW_dbg::dbg("\ndef2: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8322 pt = buf->get3BitDouble();
8323 setPt5(pt); //center pt code 15
8324 DRW_DBG("\ncenter point: ")DRW_dbg::dbg("\ncenter point: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8325 type |= 0x05;
8326 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(type)DRW_dbg::dbg(type); DRW_DBG("\n")DRW_dbg::dbg("\n");
8327
8328 if (!DRW_Entity::parseDwgEntHandle(version, buf)) {
8329 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimAngular3p::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimAngular3p::parseDwg()\n"
)
;
8330 return false;
8331 }
8332 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8333 dimStyleH = buf->getHandle();
8334 DRW_DBG("dim style Handle: ")DRW_dbg::dbg("dim style Handle: "); DRW_DBGHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref)DRW_dbg::dbgHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8335 blockH = buf->getHandle(); /* H 7 STYLE (hard pointer) */
8336 DRW_DBG("anon block Handle: ")DRW_dbg::dbg("anon block Handle: "); DRW_DBGHL(blockH.code, blockH.size, blockH.ref)DRW_dbg::dbgHL(blockH.code, blockH.size, blockH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8337 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8338
8339 // RS crc; //RS */
8340 return buf->isGood();
8341}
8342
8343bool DRW_DimOrdinate::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
8344 if (!DRW_Dimension::parseDwg(version, buf, nullptr, bs)) {
8345 return false;
8346 }
8347
8348 DRW_DBG("\n***************************** parsing dim ordinate *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim ordinate *********************************************\n"
)
;
8349 DRW_Coord pt = buf->get3BitDouble();
8350 setDefPoint(pt);
8351 DRW_DBG("defPoint: ")DRW_dbg::dbg("defPoint: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8352 pt = buf->get3BitDouble();
8353 setPt3(pt); //def1
8354 DRW_DBG("\ndef1: ")DRW_dbg::dbg("\ndef1: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8355 pt = buf->get3BitDouble();
8356 setPt4(pt);
8357 DRW_DBG("\ndef2: ")DRW_dbg::dbg("\ndef2: "); DRW_DBGPT(pt.x, pt.y, pt.z)DRW_dbg::dbgPT(pt.x, pt.y, pt.z);
8358 std::uint8_t type2 = buf->getRawChar8();//RLZ: correct this
8359 DRW_DBG("type2 (70) read: ")DRW_dbg::dbg("type2 (70) read: "); DRW_DBG(type2)DRW_dbg::dbg(type2);
8360 // 0B.1: x-vs-y ordinate flag is DXF group-70 bit 6 (0x40), matching the
8361 // filter (rs_filterdxfrw.cpp `type & 64`) and the DWG parseCode path.
8362 // (Previously set bit 7/0x80, which the filter never checks.) The clear
8363 // mask 0xBF already clears 0x40. The DIMENSION base type byte (bit 7) is
8364 // a separate field — see :6141/:6409/:6660, NOT touched here.
8365 type = (type2 & 1) ? type | 0x40 : type & 0xBF; //set bit 6 (0x40)
8366 DRW_DBG(" type (70) set: ")DRW_dbg::dbg(" type (70) set: "); DRW_DBG(type)DRW_dbg::dbg(type);
8367 type |= 6;
8368 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(type)DRW_dbg::dbg(type); DRW_DBG("\n")DRW_dbg::dbg("\n");
8369
8370 if (!DRW_Entity::parseDwgEntHandle(version, buf)) {
8371 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimAligned::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimAligned::parseDwg()\n"
)
;
8372 return false;
8373 }
8374 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8375 dimStyleH = buf->getHandle();
8376 DRW_DBG("dim style Handle: ")DRW_dbg::dbg("dim style Handle: "); DRW_DBGHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref)DRW_dbg::dbgHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8377 blockH = buf->getHandle(); /* H 7 STYLE (hard pointer) */
8378 DRW_DBG("anon block Handle: ")DRW_dbg::dbg("anon block Handle: "); DRW_DBGHL(blockH.code, blockH.size, blockH.ref)DRW_dbg::dbgHL(blockH.code, blockH.size, blockH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8379 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8380
8381 // RS crc; //RS */
8382 return buf->isGood();
8383}
8384
8385// ----------------------------------------------------------------------------
8386// DRW_Dimension shared base encoder (R2000 / AC1015)
8387// ----------------------------------------------------------------------------
8388bool DRW_Dimension::encodeDwgDimBase(DRW::Version version, dwgBufferW *buf,
8389 dwgBufferW *strBuf) const {
8390 // ODA §20.4.22: version RC present for R2010+ (mirrors parseDwg read at version > AC1021)
8391 if (version > DRW::AC1021)
8392 buf->putRawChar8(0);
8393 // R2007+: the dim text below is routed to strBuf (the separate string
8394 // stream) via the (strBuf ? strBuf : buf) selector in putVariableText.
8395 buf->put3BitDouble(extPoint); // 3BD per ODA §20.4.22 (NOT BE, NO padding bits)
8396 buf->putRawDouble(textPoint.x);
8397 buf->putRawDouble(textPoint.y);
8398 buf->putBitDouble(textPoint.z);
8399 // Reverse the parseDwg type-byte transformation:
8400 // parseDwg bit0=0 → type bit7 set; bit0=1 → type bit7 clear
8401 // parseDwg bit1=1 → type bit5 set; bit1=0 → type bit5 clear
8402 std::uint8_t rawByte = static_cast<std::uint8_t>(
8403 ((type & 0x80) ? 0 : 1) | ((type & 0x20) ? 2 : 0));
8404 buf->putRawChar8(rawByte);
8405 (strBuf ? strBuf : buf)->putVariableText(version, text);
8406 buf->putBitDouble(rot);
8407 buf->putBitDouble(hdir);
8408 // ins_scale (3BD) of the dimension's anonymous block — not stored by the
8409 // reader, but ODA/libreDWG default it to (1,1,1) (dwg.spec FIELD_3BD_1), not
8410 // (0,0,0). A zero scale is degenerate for ODA consumers. (write-review #46)
8411 const DRW_Coord insScale{1.0, 1.0, 1.0};
8412 buf->put3BitDouble(insScale);
8413 buf->putBitDouble(0.0); // ins_rotation (code 54) — default 0, not stored
8414 // R2000 (version > AC1014): alignment, spacing, line factor, measure
8415 buf->putBitShort(static_cast<std::uint16_t>(align));
8416 buf->putBitShort(static_cast<std::uint16_t>(linesty));
8417 buf->putBitDouble(linefactor);
8418 buf->putBitDouble(measureValue);
8419 if (version > DRW::AC1018) {
8420 buf->putBit(0); // unknown R2007+ bit
8421 buf->putBit(flipArrow1 ? 1 : 0);
8422 buf->putBit(flipArrow2 ? 1 : 0);
8423 }
8424 buf->putRawDouble(clonePoint.x);
8425 buf->putRawDouble(clonePoint.y);
8426 return true;
8427}
8428
8429// Helper: emit dimStyleH (defaults to STANDARD=0x15) and blockH.
8430static void putDimHandles(dwgBufferW *buf, const dwgHandle& dimStyleH, const dwgHandle& blockH,
8431 dwgBufferW *hBuf = nullptr) {
8432 dwgBufferW *hb = hBuf ? hBuf : buf;
8433 dwgHandle dsH;
8434 dsH.code = 5;
8435 dsH.ref = (dimStyleH.ref == 0) ? 0x15 : dimStyleH.ref;
8436 dsH.size = 0;
8437 if (dsH.ref != 0) { std::uint32_t t = dsH.ref; while (t != 0) { t >>= 8; ++dsH.size; } }
8438 hb->putHandle(dsH);
8439
8440 dwgHandle bhH;
8441 bhH.code = (blockH.ref == 0) ? 0 : 5;
8442 bhH.ref = blockH.ref;
8443 bhH.size = 0;
8444 if (bhH.ref != 0) { std::uint32_t t = bhH.ref; while (t != 0) { t >>= 8; ++bhH.size; } }
8445 hb->putHandle(bhH);
8446}
8447
8448// ----------------------------------------------------------------------------
8449// DRW_DimAligned::encodeDwg (oType=22)
8450// ----------------------------------------------------------------------------
8451bool DRW_DimAligned::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
8452 (void)bs;
8453 oType = 22;
8454 if (!encodeDwgCommon(version, buf)) return false;
8455 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
8456 buf->put3BitDouble(getPt3()); // def1
8457 buf->put3BitDouble(getPt4()); // def2
8458 buf->put3BitDouble(getDefPoint()); // defPoint
8459 buf->putBitDouble(getOb52() / ARAD57.29577951308232); // oblique: degrees → radians
8460 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
8461 putDimHandles(buf, dimStyleH, blockH, handleBuf);
8462 return true;
8463}
8464
8465// ----------------------------------------------------------------------------
8466// DRW_DimLinear::encodeDwg (oType=21)
8467// ----------------------------------------------------------------------------
8468bool DRW_DimLinear::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
8469 (void)bs;
8470 oType = 21;
8471 if (!encodeDwgCommon(version, buf)) return false;
8472 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
8473 buf->put3BitDouble(getPt3()); // def1
8474 buf->put3BitDouble(getPt4()); // def2
8475 buf->put3BitDouble(getDefPoint()); // defPoint
8476 buf->putBitDouble(getOb52() / ARAD57.29577951308232); // oblique: degrees → radians
8477 buf->putBitDouble(getAn50() / ARAD57.29577951308232); // rotation angle: degrees → radians
8478 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
8479 putDimHandles(buf, dimStyleH, blockH, handleBuf);
8480 return true;
8481}
8482
8483// ----------------------------------------------------------------------------
8484// DRW_DimRadial::encodeDwg (oType=25)
8485// ----------------------------------------------------------------------------
8486bool DRW_DimRadial::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
8487 (void)bs;
8488 oType = 25;
8489 if (!encodeDwgCommon(version, buf)) return false;
8490 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
8491 buf->put3BitDouble(getDefPoint()); // center point (code 10)
8492 buf->put3BitDouble(getPt5()); // diameter point (code 15)
8493 buf->putBitDouble(getRa40()); // leader length (code 40)
8494 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
8495 putDimHandles(buf, dimStyleH, blockH, handleBuf);
8496 return true;
8497}
8498
8499// ----------------------------------------------------------------------------
8500// DRW_DimLargeRadial::encodeDwg (oType=519, custom AcDbRadialDimensionLarge)
8501// ----------------------------------------------------------------------------
8502bool DRW_DimLargeRadial::encodeDwg(DRW::Version version, dwgBufferW *buf,
8503 std::uint32_t bs, dwgBufferW *strBuf,
8504 dwgBufferW *handleBuf) {
8505 (void)bs;
8506 oType = kDwgClassNum;
8507 if (!encodeDwgCommon(version, buf)) return false;
8508 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
8509 buf->put3BitDouble(getCenterPoint()); // definition point (code 10)
8510 buf->put3BitDouble(jogPoint); // jog vertex (code 15)
8511 buf->putBitDouble(jogAngle); // jog transverse angle (code 40)
8512 buf->put3BitDouble(getChordPoint()); // chord point (code 13)
8513 buf->put3BitDouble(overrideCenterPoint); // overridden center (code 14)
8514 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
8515 putDimHandles(buf, dimStyleH, blockH, handleBuf);
8516 return true;
8517}
8518
8519// ----------------------------------------------------------------------------
8520// DRW_DimDiametric::encodeDwg (oType=26)
8521// ----------------------------------------------------------------------------
8522bool DRW_DimDiametric::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
8523 (void)bs;
8524 oType = 26;
8525 if (!encodeDwgCommon(version, buf)) return false;
8526 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
8527 buf->put3BitDouble(getPt5()); // first diameter point (code 15) — matches parseDwg order
8528 buf->put3BitDouble(getDefPoint()); // opposite point (code 10)
8529 buf->putBitDouble(getRa40()); // leader length (code 40)
8530 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
8531 putDimHandles(buf, dimStyleH, blockH, handleBuf);
8532 return true;
8533}
8534
8535// ----------------------------------------------------------------------------
8536// DRW_DimAngular::encodeDwg (oType=24, 2-line angular)
8537// ----------------------------------------------------------------------------
8538bool DRW_DimAngular::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
8539 (void)bs;
8540 oType = 24;
8541 if (!encodeDwgCommon(version, buf)) return false;
8542 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
8543 // arcPoint is 2RD (not 3BD) in parseDwg — only x and y
8544 buf->putRawDouble(getPt6().x);
8545 buf->putRawDouble(getPt6().y);
8546 buf->put3BitDouble(getPt3()); // def1 (line 1 start)
8547 buf->put3BitDouble(getPt4()); // def2 (line 1 end)
8548 buf->put3BitDouble(getPt5()); // circlePoint (center)
8549 buf->put3BitDouble(getDefPoint()); // defPoint
8550 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
8551 putDimHandles(buf, dimStyleH, blockH, handleBuf);
8552 return true;
8553}
8554
8555// ----------------------------------------------------------------------------
8556// DRW_DimAngular3p::encodeDwg (oType=23, 3-point angular)
8557// ----------------------------------------------------------------------------
8558bool DRW_DimAngular3p::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
8559 (void)bs;
8560 oType = 23;
8561 if (!encodeDwgCommon(version, buf)) return false;
8562 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
8563 buf->put3BitDouble(getDefPoint()); // defPoint (code 10)
8564 buf->put3BitDouble(getPt3()); // def1 (code 13)
8565 buf->put3BitDouble(getPt4()); // def2 (code 14)
8566 buf->put3BitDouble(getPt5()); // circlePoint / vertex (code 15)
8567 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
8568 putDimHandles(buf, dimStyleH, blockH, handleBuf);
8569 return true;
8570}
8571
8572// ----------------------------------------------------------------------------
8573// DRW_DimArc::parseCode (DXF group-code parser)
8574// ----------------------------------------------------------------------------
8575bool DRW_DimArc::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
8576 if (code == 100) {
8577 std::string s = reader->getString();
8578 if (s == "AcDbArcDimension") {
8579 m_arcSubclassSeen = true;
8580 return true;
8581 }
8582 // Fall through for AcDbEntity / AcDbDimension so base classes see them
8583 return DRW_Dimension::parseCode(code, reader);
8584 }
8585 if (m_arcSubclassSeen) {
8586 switch (code) {
8587 case 40: arcStartAngle = reader->getDouble(); return true;
8588 case 41: arcEndAngle = reader->getDouble(); return true;
8589 case 70: arcSymbol = reader->getInt32(); return true;
8590 case 71: isPartial = reader->getInt32() != 0; return true;
8591 }
8592 }
8593 switch (code) {
8594 case 17: leaderPt2.x = reader->getDouble(); return true;
8595 case 27: leaderPt2.y = reader->getDouble(); return true;
8596 case 37: leaderPt2.z = reader->getDouble(); return true;
8597 }
8598 return DRW_Dimension::parseCode(code, reader);
8599}
8600
8601// ----------------------------------------------------------------------------
8602// DRW_DimArc::parseDwg (ODA DWG spec §20.4.19)
8603// ----------------------------------------------------------------------------
8604bool DRW_DimArc::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
8605 if (!DRW_Dimension::parseDwg(version, buf, nullptr, bs)) return false;
8606 setDefPoint(buf->get3BitDouble()); // arc dim-line arc point (code 10)
8607 setPt3(buf->get3BitDouble()); // extension line 1 (code 13)
8608 setPt4(buf->get3BitDouble()); // extension line 2 (code 14)
8609 setPt5(buf->get3BitDouble()); // arc center (code 15)
8610 isPartial = buf->getBit() != 0;
8611 arcStartAngle = buf->getBitDouble();
8612 arcEndAngle = buf->getBitDouble();
8613 hasLeader = buf->getBit() != 0;
8614 // ODA §20.4.19: leader points are UNCONDITIONAL — always present in the stream
8615 setPt6(buf->get3BitDouble()); // leader point 1 (code 16)
8616 leaderPt2 = buf->get3BitDouble(); // leader point 2 (code 17)
8617 if (!DRW_Entity::parseDwgEntHandle(version, buf)) return false;
8618 dimStyleH = buf->getHandle();
8619 blockH = buf->getHandle();
8620 return buf->isGood();
8621}
8622
8623// ----------------------------------------------------------------------------
8624// DRW_DimArc::encodeDwg (oType=500 — dynamic class, classNum from writeDwgClasses)
8625// ----------------------------------------------------------------------------
8626bool DRW_DimArc::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
8627 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
8628 (void)bs;
8629 oType = DRW_DimArc::kDwgClassNum; // assigned in writeDwgClasses; reader resolves via classesmap
8630 if (!encodeDwgCommon(version, buf)) return false;
8631 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
8632 buf->put3BitDouble(getDefPoint()); // arc dim-line arc point (code 10)
8633 buf->put3BitDouble(getPt3()); // extension line 1 (code 13)
8634 buf->put3BitDouble(getPt4()); // extension line 2 (code 14)
8635 buf->put3BitDouble(getPt5()); // arc center (code 15)
8636 buf->putBit(isPartial ? 1 : 0);
8637 buf->putBitDouble(arcStartAngle);
8638 buf->putBitDouble(arcEndAngle);
8639 buf->putBit(hasLeader ? 1 : 0);
8640 // ODA §20.4.19: leader points are UNCONDITIONAL — always written; default to ext-line pts
8641 DRW_Coord lp1 = hasLeader ? getPt6() : getPt3();
8642 DRW_Coord lp2 = hasLeader ? leaderPt2 : getPt4();
8643 buf->put3BitDouble(lp1); // leader point 1 (code 16)
8644 buf->put3BitDouble(lp2); // leader point 2 (code 17)
8645 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
8646 putDimHandles(buf, dimStyleH, blockH, handleBuf);
8647 return true;
8648}
8649
8650// ----------------------------------------------------------------------------
8651// DRW_DimOrdinate::encodeDwg (oType=20)
8652// ----------------------------------------------------------------------------
8653bool DRW_DimOrdinate::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
8654 (void)bs;
8655 oType = 20;
8656 if (!encodeDwgCommon(version, buf)) return false;
8657 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
8658 buf->put3BitDouble(getDefPoint()); // origin/definition point (code 10)
8659 buf->put3BitDouble(getPt3()); // feature location point (code 13)
8660 buf->put3BitDouble(getPt4()); // leader end point (code 14)
8661 // type2 byte encodes the x-vs-y ordinate flag (bit 6 / 0x40 of type, per
8662 // 0B.1) — keeps the DWG byte round-trip self-consistent with the parse
8663 // side while making the filter's `type & 64` check fire.
8664 std::uint8_t type2byte = (type & 0x40) ? 1 : 0;
8665 buf->putRawChar8(type2byte);
8666 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
8667 putDimHandles(buf, dimStyleH, blockH, handleBuf);
8668 return true;
8669}
8670
8671bool DRW_Leader::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
8672 switch (code) {
8673 case 3:
8674 style = reader->getUtf8String();
8675 break;
8676 case 71:
8677 arrow = reader->getInt32();
8678 break;
8679 case 72:
8680 leadertype = reader->getInt32();
8681 break;
8682 case 73:
8683 flag = reader->getInt32();
8684 break;
8685 case 74:
8686 hookline = reader->getInt32();
8687 break;
8688 case 75:
8689 hookflag = reader->getInt32();
8690 break;
8691 case 76:
8692 vertnum = reader->getInt32();
8693 break;
8694 case 77:
8695 coloruse = reader->getInt32();
8696 break;
8697 case 40:
8698 textheight = reader->getDouble();
8699 break;
8700 case 41:
8701 textwidth = reader->getDouble();
8702 break;
8703 case 10:
8704 vertexpoint= std::make_shared<DRW_Coord>();
8705 vertexlist.push_back(vertexpoint);
8706 vertexpoint->x = reader->getDouble();
8707 break;
8708 case 20:
8709 if(vertexpoint)
8710 vertexpoint->y = reader->getDouble();
8711 break;
8712 case 30:
8713 if(vertexpoint)
8714 vertexpoint->z = reader->getDouble();
8715 break;
8716 case 340:
8717 annotHandle = reader->getHandleString();
8718 break;
8719 case 210:
8720 extrusionPoint.x = reader->getDouble();
8721 break;
8722 case 220:
8723 extrusionPoint.y = reader->getDouble();
8724 break;
8725 case 230:
8726 extrusionPoint.z = reader->getDouble();
8727 break;
8728 case 211:
8729 horizdir.x = reader->getDouble();
8730 break;
8731 case 221:
8732 horizdir.y = reader->getDouble();
8733 break;
8734 case 231:
8735 horizdir.z = reader->getDouble();
8736 break;
8737 case 212:
8738 offsetblock.x = reader->getDouble();
8739 break;
8740 case 222:
8741 offsetblock.y = reader->getDouble();
8742 break;
8743 case 232:
8744 offsetblock.z = reader->getDouble();
8745 break;
8746 case 213:
8747 offsettext.x = reader->getDouble();
8748 break;
8749 case 223:
8750 offsettext.y = reader->getDouble();
8751 break;
8752 case 233:
8753 offsettext.z = reader->getDouble();
8754 break;
8755 default:
8756 return DRW_Entity::parseCode(code, reader);
8757 }
8758
8759 return true;
8760}
8761
8762bool DRW_Leader::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
8763 dwgBuffer sBuff = *buf;
8764 dwgBuffer *sBuf = buf;
8765 if (version > DRW::AC1018) {//2007+
8766 sBuf = &sBuff; //separate buffer for strings
8767 }
8768 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
8769 if (!ret)
8770 return ret;
8771 DRW_DBG("\n***************************** parsing leader *********************************************\n")DRW_dbg::dbg("\n***************************** parsing leader *********************************************\n"
)
;
8772 DRW_DBG("unknown bit ")DRW_dbg::dbg("unknown bit "); DRW_DBG(buf->getBit())DRW_dbg::dbg(buf->getBit());
8773 DRW_DBG(" annot type ")DRW_dbg::dbg(" annot type "); DRW_DBG(buf->getBitShort())DRW_dbg::dbg(buf->getBitShort());
8774 leadertype = buf->getBitShort();
8775 DRW_DBG(" Path type ")DRW_dbg::dbg(" Path type "); DRW_DBG(leadertype)DRW_dbg::dbg(leadertype);
8776 std::int32_t nPt = buf->getBitLong();
8777 DRW_DBG(" Num pts ")DRW_dbg::dbg(" Num pts "); DRW_DBG(nPt)DRW_dbg::dbg(nPt);
8778
8779 // add vertexes
8780 for (int i = 0; i< nPt; i++){
8781 DRW_Coord vertex = buf->get3BitDouble();
8782 vertexlist.push_back(std::make_shared<DRW_Coord>(vertex));
8783 DRW_DBG("\nvertex ")DRW_dbg::dbg("\nvertex "); DRW_DBGPT(vertex.x, vertex.y, vertex.z)DRW_dbg::dbgPT(vertex.x, vertex.y, vertex.z);
8784 }
8785 DRW_Coord Endptproj = buf->get3BitDouble();
8786 DRW_DBG("\nEndptproj ")DRW_dbg::dbg("\nEndptproj "); DRW_DBGPT(Endptproj.x, Endptproj.y, Endptproj.z)DRW_dbg::dbgPT(Endptproj.x, Endptproj.y, Endptproj.z);
8787 // ODA §20.4.47: Extrusion is plain 3DPOINT (3BD), not BE — confirmed by libreDWG dwg.spec:3439
8788 extrusionPoint = buf->get3BitDouble();
8789 DRW_DBG("\nextrusionPoint ")DRW_dbg::dbg("\nextrusionPoint "); DRW_DBGPT(extrusionPoint.x, extrusionPoint.y, extrusionPoint.z)DRW_dbg::dbgPT(extrusionPoint.x, extrusionPoint.y, extrusionPoint
.z)
;
8790 horizdir = buf->get3BitDouble();
8791 DRW_DBG("\nhorizdir ")DRW_dbg::dbg("\nhorizdir "); DRW_DBGPT(horizdir.x, horizdir.y, horizdir.z)DRW_dbg::dbgPT(horizdir.x, horizdir.y, horizdir.z);
8792 offsetblock = buf->get3BitDouble();
8793 DRW_DBG("\noffsetblock ")DRW_dbg::dbg("\noffsetblock "); DRW_DBGPT(offsetblock.x, offsetblock.y, offsetblock.z)DRW_dbg::dbgPT(offsetblock.x, offsetblock.y, offsetblock.z);
8794 if (version > DRW::AC1012) { //R14+
8795 DRW_Coord unk = buf->get3BitDouble();
8796 DRW_DBG("\nunknown ")DRW_dbg::dbg("\nunknown "); DRW_DBGPT(unk.x, unk.y, unk.z)DRW_dbg::dbgPT(unk.x, unk.y, unk.z);
8797 }
8798 if (version < DRW::AC1015) { //R14 -
8799 DRW_DBG("\ndimgap ")DRW_dbg::dbg("\ndimgap "); DRW_DBG(buf->getBitDouble())DRW_dbg::dbg(buf->getBitDouble());
8800 }
8801 if (version < DRW::AC1024) { //2010-
8802 textheight = buf->getBitDouble();
8803 textwidth = buf->getBitDouble();
8804 DRW_DBG("\ntextheight ")DRW_dbg::dbg("\ntextheight "); DRW_DBG(textheight)DRW_dbg::dbg(textheight); DRW_DBG(" textwidth ")DRW_dbg::dbg(" textwidth "); DRW_DBG(textwidth)DRW_dbg::dbg(textwidth);
8805 }
8806 hookline = buf->getBit();
8807 arrow = buf->getBit();
8808 DRW_DBG(" hookline ")DRW_dbg::dbg(" hookline "); DRW_DBG(hookline)DRW_dbg::dbg(hookline); DRW_DBG(" arrow flag ")DRW_dbg::dbg(" arrow flag "); DRW_DBG(arrow)DRW_dbg::dbg(arrow);
8809
8810 if (version < DRW::AC1015) { //R14 -
8811 DRW_DBG("\nArrow head type ")DRW_dbg::dbg("\nArrow head type "); DRW_DBG(buf->getBitShort())DRW_dbg::dbg(buf->getBitShort());
8812 DRW_DBG("dimasz ")DRW_dbg::dbg("dimasz "); DRW_DBG(buf->getBitDouble())DRW_dbg::dbg(buf->getBitDouble());
8813 DRW_DBG("\nunk bit ")DRW_dbg::dbg("\nunk bit "); DRW_DBG(buf->getBit())DRW_dbg::dbg(buf->getBit());
8814 DRW_DBG(" unk bit ")DRW_dbg::dbg(" unk bit "); DRW_DBG(buf->getBit())DRW_dbg::dbg(buf->getBit());
8815 DRW_DBG(" unk short ")DRW_dbg::dbg(" unk short "); DRW_DBG(buf->getBitShort())DRW_dbg::dbg(buf->getBitShort());
8816 DRW_DBG(" byBlock color ")DRW_dbg::dbg(" byBlock color "); DRW_DBG(buf->getBitShort())DRW_dbg::dbg(buf->getBitShort());
8817 DRW_DBG(" unk bit ")DRW_dbg::dbg(" unk bit "); DRW_DBG(buf->getBit())DRW_dbg::dbg(buf->getBit());
8818 DRW_DBG(" unk bit ")DRW_dbg::dbg(" unk bit "); DRW_DBG(buf->getBit())DRW_dbg::dbg(buf->getBit());
8819 } else { //R2000+
8820 DRW_DBG("\nunk short ")DRW_dbg::dbg("\nunk short "); DRW_DBG(buf->getBitShort())DRW_dbg::dbg(buf->getBitShort());
8821 DRW_DBG(" unk bit ")DRW_dbg::dbg(" unk bit "); DRW_DBG(buf->getBit())DRW_dbg::dbg(buf->getBit());
8822 DRW_DBG(" unk bit ")DRW_dbg::dbg(" unk bit "); DRW_DBG(buf->getBit())DRW_dbg::dbg(buf->getBit());
8823 }
8824 DRW_DBG("\n")DRW_dbg::dbg("\n");
8825 ret = DRW_Entity::parseDwgEntHandle(version, buf);
8826 if (!ret)
8827 return ret;
8828 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8829 AnnotH = buf->getHandle();
8830 annotHandle = AnnotH.ref;
8831 DRW_DBG("annot block Handle: ")DRW_dbg::dbg("annot block Handle: "); DRW_DBGHL(AnnotH.code, AnnotH.size, dimStyleH.ref)DRW_dbg::dbgHL(AnnotH.code, AnnotH.size, dimStyleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8832 dimStyleH = buf->getHandle(); /* H 7 STYLE (hard pointer) */
8833 DRW_DBG("dim style Handle: ")DRW_dbg::dbg("dim style Handle: "); DRW_DBGHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref)DRW_dbg::dbgHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
8834 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8835// RS crc; //RS */
8836 return buf->isGood();
8837}
8838
8839// DXF CONTEXT_DATA{} nested-block state machine (§20.4.86). The nested blocks
8840// open with 300 "CONTEXT_DATA{" / 302 "LEADER{" / 304 "LEADER_LINE{" and close
8841// with the distinct codes 301 / 303 / 305, so the open block is tracked with a
8842// single state int (no stack needed). The numeric group codes are overloaded
8843// by block — e.g. 40 is the overall scale in CONTEXT, the landing distance in
8844// LEADER and the arrow size in LEADER_LINE; 10/20/30 are the content base point,
8845// the connection point and a polyline vertex respectively — so they are routed
8846// per state into `context`. Returns true when the code belongs to the context
8847// block (consumed); false at entity level so parseCode handles it.
8848bool DRW_MLeader::parseDxfContextCode(int code, const std::unique_ptr<dxfReader>& reader){
8849 switch (code) { // block open/close markers
8850 case 300: m_dxfCtxState = 1; return true; // "CONTEXT_DATA{"
8851 case 301: m_dxfCtxState = 0; return true; // "}" end context
8852 case 302: context.roots.emplace_back(); m_dxfCtxState = 2; return true; // "LEADER{"
8853 case 303: m_dxfCtxState = m_dxfCtxState ? 1 : 0; return true; // "}" end leader
8854 case 304:
8855 if (reader->getString() == "LEADER_LINE{") {
8856 if (!context.roots.empty())
8857 context.roots.back().leaderLines.emplace_back();
8858 m_dxfCtxState = 3;
8859 return true;
8860 }
8861 if (m_dxfCtxState == 1) { context.textLabel = reader->getUtf8String(); return true; }
8862 return false; // not in context: defer (unused)
8863 case 305: m_dxfCtxState = 2; return true; // "}" end leader line
8864 default: break;
8865 }
8866
8867 if (m_dxfCtxState == 0)
8868 return false; // entity level — parseCode handles it
8869
8870 if (m_dxfCtxState == 3) { // LEADER_LINE{}: a polyline + overrides
8871 DRW_MLeaderRoot* root = context.roots.empty() ? nullptr : &context.roots.back();
8872 DRW_MLeaderLeaderLine* line =
8873 (root && !root->leaderLines.empty()) ? &root->leaderLines.back() : nullptr;
8874 if (line) switch (code) {
8875 case 10: line->points.emplace_back(reader->getDouble(), 0.0, 0.0); return true;
8876 case 20: if (!line->points.empty()) line->points.back().y = reader->getDouble(); return true;
8877 case 30: if (!line->points.empty()) line->points.back().z = reader->getDouble(); return true;
8878 case 40: line->arrowSize = reader->getDouble(); return true;
8879 case 90: line->segmentIndex = reader->getInt32(); return true;
8880 case 91: line->leaderLineIndex = reader->getInt32(); return true;
8881 case 92: line->color = reader->getInt32(); return true;
8882 case 93: line->overrideFlags = reader->getInt32(); return true;
8883 case 170: line->leaderType = reader->getInt32(); return true;
8884 case 171: line->lineWeight = reader->getInt32(); return true;
8885 default: break;
8886 }
8887 return true; // swallow other line codes
8888 }
8889
8890 if (m_dxfCtxState == 2) { // LEADER{}: one root attachment
8891 DRW_MLeaderRoot* root = context.roots.empty() ? nullptr : &context.roots.back();
8892 if (root) switch (code) {
8893 case 290: root->isContentValid = (reader->getInt32() != 0); return true;
8894 case 291: root->unknown291 = (reader->getInt32() != 0); return true;
8895 case 10: root->connectionPoint.x = reader->getDouble(); return true;
8896 case 20: root->connectionPoint.y = reader->getDouble(); return true;
8897 case 30: root->connectionPoint.z = reader->getDouble(); return true;
8898 case 11: root->direction.x = reader->getDouble(); return true;
8899 case 21: root->direction.y = reader->getDouble(); return true;
8900 case 31: root->direction.z = reader->getDouble(); return true;
8901 case 90: root->leaderIndex = reader->getInt32(); return true;
8902 case 40: root->landingDistance = reader->getDouble(); return true;
8903 case 271: root->attachmentDirection = reader->getInt32(); return true;
8904 default: break;
8905 }
8906 return true; // swallow other leader codes
8907 }
8908
8909 switch (code) { // m_dxfCtxState == 1: CONTEXT_DATA{}
8910 case 40: context.overallScale = reader->getDouble(); return true;
8911 case 10: context.contentBasePoint.x = reader->getDouble(); return true;
8912 case 20: context.contentBasePoint.y = reader->getDouble(); return true;
8913 case 30: context.contentBasePoint.z = reader->getDouble(); return true;
8914 case 41: context.textHeight = reader->getDouble(); return true;
8915 case 140: context.arrowHeadSize = reader->getDouble(); return true;
8916 case 145: context.landingGap = reader->getDouble(); return true;
8917 case 174: context.styleLeftAttach = reader->getInt32(); return true;
8918 case 175: context.styleRightAttach = reader->getInt32(); return true;
8919 case 176: context.textAlignType = reader->getInt32(); return true;
8920 case 177: context.attachmentType = reader->getInt32(); return true;
8921 case 290: context.hasTextContents = (reader->getInt32() != 0); return true;
8922 /* text-content branch */
8923 case 11: context.textNormal.x = reader->getDouble(); return true;
8924 case 21: context.textNormal.y = reader->getDouble(); return true;
8925 case 31: context.textNormal.z = reader->getDouble(); return true;
8926 case 12: context.textLocation.x = reader->getDouble(); return true;
8927 case 22: context.textLocation.y = reader->getDouble(); return true;
8928 case 32: context.textLocation.z = reader->getDouble(); return true;
8929 case 13: context.textDirection.x = reader->getDouble(); return true;
8930 case 23: context.textDirection.y = reader->getDouble(); return true;
8931 case 33: context.textDirection.z = reader->getDouble(); return true;
8932 case 42: context.textRotation = reader->getDouble(); return true;
8933 case 43: context.boundaryWidth = reader->getDouble(); return true;
8934 case 44: context.boundaryHeight = reader->getDouble(); return true;
8935 case 45: context.lineSpacingFactor = reader->getDouble(); return true;
8936 case 170: context.lineSpacingStyle = reader->getInt32(); return true;
8937 case 90: context.textColor = reader->getInt32(); return true;
8938 case 171: context.alignment = reader->getInt32(); return true;
8939 case 172: context.flowDirection = reader->getInt32(); return true;
8940 case 91: context.bgFillColor = reader->getInt32(); return true;
8941 case 141: context.bgScaleFactor = reader->getDouble(); return true;
8942 case 92: context.bgTransparency = reader->getInt32(); return true;
8943 case 291: context.bgFillEnabled = (reader->getInt32() != 0); return true;
8944 case 292: context.bgMaskFillOn = (reader->getInt32() != 0); return true;
8945 case 173: context.columnType = reader->getInt32(); return true;
8946 case 293: context.textHeightAuto = (reader->getInt32() != 0); return true;
8947 case 142: context.columnWidth = reader->getDouble(); return true;
8948 case 143: context.columnGutter = reader->getDouble(); return true;
8949 case 294: context.columnFlowReversed = (reader->getInt32() != 0); return true;
8950 case 144: context.columnSizes.push_back(reader->getDouble()); return true;
8951 case 295: context.wordBreak = (reader->getInt32() != 0); return true;
8952 /* block-content branch */
8953 case 296: context.hasContentsBlock = (reader->getInt32() != 0); return true;
8954 case 14: context.blockNormal.x = reader->getDouble(); return true;
8955 case 24: context.blockNormal.y = reader->getDouble(); return true;
8956 case 34: context.blockNormal.z = reader->getDouble(); return true;
8957 case 15: context.blockLocation.x = reader->getDouble(); return true;
8958 case 25: context.blockLocation.y = reader->getDouble(); return true;
8959 case 35: context.blockLocation.z = reader->getDouble(); return true;
8960 case 16: context.blockScale.x = reader->getDouble(); return true;
8961 case 26: context.blockScale.y = reader->getDouble(); return true;
8962 case 36: context.blockScale.z = reader->getDouble(); return true;
8963 case 46: context.blockRotation = reader->getDouble(); return true;
8964 case 93: context.blockColor = reader->getInt32(); return true;
8965 /* common tail */
8966 case 110: context.basePoint.x = reader->getDouble(); return true;
8967 case 120: context.basePoint.y = reader->getDouble(); return true;
8968 case 130: context.basePoint.z = reader->getDouble(); return true;
8969 case 111: context.baseDirection.x = reader->getDouble(); return true;
8970 case 121: context.baseDirection.y = reader->getDouble(); return true;
8971 case 131: context.baseDirection.z = reader->getDouble(); return true;
8972 case 112: context.baseVertical.x = reader->getDouble(); return true;
8973 case 122: context.baseVertical.y = reader->getDouble(); return true;
8974 case 132: context.baseVertical.z = reader->getDouble(); return true;
8975 case 297: context.isNormalReversed = (reader->getInt32() != 0); return true;
8976 case 272: context.styleBottomAttach = reader->getInt32(); return true;
8977 case 273: context.styleTopAttach = reader->getInt32(); return true;
8978 default: return true; // swallow any other context code
8979 }
8980}
8981
8982bool DRW_MLeader::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
8983 // The embedded CONTEXT_DATA{} block (§20.4.86) is routed by the nested-block
8984 // state machine; the remaining (entity-level) fields are read below and
8985 // mirror the DWG body parser.
8986 if (parseDxfContextCode(code, reader))
8987 return true;
8988 switch (code) {
8989 case 170: leaderType = reader->getInt32(); break;
8990 case 171: leaderLineWeight = reader->getInt32(); break;
8991 case 172: styleContentType = reader->getInt32(); break;
8992 case 173: styleLeftAttach = reader->getInt32(); break;
8993 case 95: styleRightAttach = reader->getInt32(); break;
8994 case 174: styleTextAngleType = reader->getInt32(); break;
8995 case 175: unknown175 = reader->getInt32(); break;
8996 case 176: styleAttachmentType = reader->getInt32(); break;
8997 case 178: ipeAlign = reader->getInt32(); break;
8998 case 179: justification = reader->getInt32(); break;
8999 case 271: attachmentDirection = reader->getInt32(); break;
9000 case 272: styleBottomAttach = reader->getInt32(); break;
9001 case 273: styleTopAttach = reader->getInt32(); break;
9002 case 90: overrideFlags = reader->getInt32(); break;
9003 case 91: leaderColor = reader->getInt32(); break;
9004 case 92: styleTextColor = reader->getInt32(); break;
9005 case 93: styleBlockColor = reader->getInt32(); break;
9006 case 41: landingDistance = reader->getDouble(); break;
9007 case 42: defaultArrowHeadSize = reader->getDouble(); break;
9008 case 43: styleBlockRotation = reader->getDouble(); break;
9009 case 45: scaleFactor = reader->getDouble(); break;
9010 case 290: landingEnabled = (reader->getInt32() != 0); break;
9011 case 291: doglegEnabled = (reader->getInt32() != 0); break;
9012 case 292: styleTextFrameEnabled = (reader->getInt32() != 0); break;
9013 case 293: isAnnotative = (reader->getInt32() != 0); break;
9014 case 294: isTextDirectionNegative = (reader->getInt32() != 0); break;
9015 case 295: leaderExtendedToText = (reader->getInt32() != 0); break;
9016 default:
9017 return DRW_Entity::parseCode(code, reader);
9018 }
9019 return true;
9020}
9021
9022// Helper: parse one AcDbMLeaderObjectContextData::LeaderRoot entry (§20.4.86).
9023//
9024// Each root has: connection point + direction, optional break pairs, leader
9025// index, landing distance, then a count-and-list of leader lines. Lines
9026// themselves carry: point list, break-info pairs, and (R2010+) per-line
9027// style overrides. The handles inside (line-type / arrow per leader line)
9028// are deferred to the entity-level handle stream and not stored here.
9029static bool parseMLeaderRoot(DRW::Version version, dwgBuffer *buf,
9030 DRW_MLeaderRoot& root) {
9031 // Layout per libreDWG dwg2.spec:1316-1366 (Dwg_LEADER_Node + Dwg_LEADER_Line).
9032 // The two 3BD coords at the head of the node are conditional on the
9033 // preceding B flags; reading them unconditionally drifts the bit stream
9034 // when either flag is 0.
9035 bool hasLastPt = buf->getBit(); // 290 has_lastleaderlinepoint
9036 bool hasDogleg = buf->getBit(); // 291 has_dogleg
9037 root.isContentValid = hasLastPt;
9038 root.unknown291 = hasDogleg;
9039 if (hasLastPt) root.connectionPoint = buf->get3BitDouble();
9040 if (hasDogleg) root.direction = buf->get3BitDouble();
9041
9042 std::int32_t nBreaks = buf->getBitLong();
9043 if (nBreaks < 0 || nBreaks > 5000) return false; // libreDWG MAX_LEADER_NUMBER
9044 root.breaks.reserve(static_cast<size_t>(nBreaks));
9045 for (std::int32_t i = 0; i < nBreaks; ++i) {
9046 DRW_Coord a = buf->get3BitDouble();
9047 DRW_Coord b = buf->get3BitDouble();
9048 root.breaks.emplace_back(a, b);
9049 }
9050
9051 root.leaderIndex = buf->getBitLong(); // 90 branch_index
9052 root.landingDistance = buf->getBitDouble(); // 40 dogleg_length
9053
9054 std::int32_t nLines = buf->getBitLong();
9055 if (nLines < 0 || nLines > 5000) return false;
9056 root.leaderLines.reserve(static_cast<size_t>(nLines));
9057 for (std::int32_t i = 0; i < nLines; ++i) {
9058 DRW_MLeaderLeaderLine line;
9059 // Per libreDWG: BL num_points, points, BL num_breaks, breaks, BL line_index.
9060 // The previous 5-BL layout (brkInfoCount + segmentIndex + nPairs +
9061 // pairs + leaderLineIndex) inserted two spurious BL reads, drifting
9062 // every subsequent entity-level field (overallScale, contentType, …).
9063 std::int32_t nPts = buf->getBitLong();
9064 if (nPts < 0 || nPts > 5000) return false;
9065 line.points.reserve(static_cast<size_t>(nPts));
9066 for (std::int32_t j = 0; j < nPts; ++j) {
9067 line.points.push_back(buf->get3BitDouble());
9068 }
9069 std::int32_t nLineBreaks = buf->getBitLong();
9070 if (nLineBreaks < 0 || nLineBreaks > 5000) return false;
9071 for (std::int32_t j = 0; j < nLineBreaks; ++j) {
9072 DRW_Coord a = buf->get3BitDouble();
9073 DRW_Coord b = buf->get3BitDouble();
9074 line.breaks.emplace_back(a, b);
9075 }
9076 line.leaderLineIndex = buf->getBitLong(); // 91 line_index
9077
9078 // R2010+ per-line override block. The spec marks this block "R2010"
9079 // (§20.4.86 page 215); the override flags BL 93 says which fields
9080 // were overridden. The handle fields (340 line-type, 341 arrow) are
9081 // deferred to the trailing handle stream.
9082 if (version >= DRW::AC1024) {
9083 line.leaderType = buf->getBitShort();
9084 line.color = buf->getCmColor(version);
9085 // line type handle 340 — read from handles section later
9086 line.lineWeight = buf->getBitLong();
9087 line.arrowSize = buf->getBitDouble();
9088 // arrow handle 341 — handles section
9089 line.overrideFlags = buf->getBitLong();
9090 }
9091 root.leaderLines.push_back(std::move(line));
9092 }
9093
9094 if (version >= DRW::AC1024) {
9095 root.attachmentDirection = buf->getBitShort();
9096 }
9097
9098 return buf->isGood();
9099}
9100
9101// Helper: parse the AcDbMLeaderObjectContextData (§20.4.86) payload, the
9102// large embedded block at the start of the MLEADER body that carries the
9103// leader geometry plus either text or block content.
9104static bool parseMLeaderAnnotContext(DRW::Version version, dwgBuffer *buf,
9105 dwgBuffer *sBuf,
9106 DRW_MLeaderAnnotContext& ctx) {
9107 // NOTE: when AcDbMLeaderObjectContextData is embedded INSIDE the MLEADER
9108 // entity body (rather than serialized as a standalone object), the
9109 // AcDbObjectContextData base preamble (BS version, B has-file-ext-dict,
9110 // B default-flag) does NOT appear in the bit stream — those fields are
9111 // standalone-object metadata. The embedded AnnotContext starts directly
9112 // with the leader-roots count. AcDbAnnotScaleObjectContextData's scale
9113 // handle is deferred to the trailing handle stream.
9114
9115 // Number of leader roots.
9116 std::int32_t nRoots = buf->getBitLong();
9117 if (nRoots == 0) {
9118 bool rootCountBits[7] = {};
9119 for (bool& rootCountBit : rootCountBits)
9120 rootCountBit = buf->getBit() != 0;
9121 nRoots = rootCountBits[5] ? 2 : 1;
9122 }
9123 if (nRoots < 0 || nRoots > 1000000) return false;
9124 ctx.roots.clear();
9125 ctx.roots.reserve(static_cast<size_t>(nRoots));
9126 for (std::int32_t i = 0; i < nRoots; ++i) {
9127 DRW_MLeaderRoot root;
9128 if (!parseMLeaderRoot(version, buf, root)) return false;
9129 ctx.roots.push_back(std::move(root));
9130 }
9131
9132 // Common content fields.
9133 ctx.overallScale = buf->getBitDouble();
9134 ctx.contentBasePoint = buf->get3BitDouble();
9135 ctx.textHeight = buf->getBitDouble();
9136 ctx.arrowHeadSize = buf->getBitDouble();
9137 ctx.landingGap = buf->getBitDouble();
9138 ctx.styleLeftAttach = buf->getBitShort();
9139 ctx.styleRightAttach = buf->getBitShort();
9140 ctx.textAlignType = buf->getBitShort();
9141 ctx.attachmentType = buf->getBitShort();
9142 ctx.hasTextContents = buf->getBit();
9143
9144 if (ctx.hasTextContents) {
9145 ctx.textLabel = sBuf->getVariableText(version, false);
9146 ctx.textNormal = buf->get3BitDouble();
9147 // text style handle 340 — handles section
9148 ctx.textLocation = buf->get3BitDouble();
9149 ctx.textDirection = buf->get3BitDouble();
9150 ctx.textRotation = buf->getBitDouble();
9151 ctx.boundaryWidth = buf->getBitDouble();
9152 ctx.boundaryHeight = buf->getBitDouble();
9153 ctx.lineSpacingFactor = buf->getBitDouble();
9154 ctx.lineSpacingStyle = buf->getBitShort();
9155 ctx.textColor = buf->getCmColor(version);
9156 ctx.alignment = buf->getBitShort();
9157 ctx.flowDirection = buf->getBitShort();
9158 ctx.bgFillColor = buf->getCmColor(version);
9159 ctx.bgScaleFactor = buf->getBitDouble();
9160 ctx.bgTransparency = buf->getBitLong();
9161 ctx.bgFillEnabled = buf->getBit();
9162 ctx.bgMaskFillOn = buf->getBit();
9163 ctx.columnType = buf->getBitShort();
9164 ctx.textHeightAuto = buf->getBit();
9165 ctx.columnWidth = buf->getBitDouble();
9166 ctx.columnGutter = buf->getBitDouble();
9167 ctx.columnFlowReversed = buf->getBit();
9168 std::int32_t nColSizes = buf->getBitLong();
9169 if (nColSizes < 0 || nColSizes > 1000000) return false;
9170 ctx.columnSizes.reserve(static_cast<size_t>(nColSizes));
9171 for (std::int32_t i = 0; i < nColSizes; ++i) {
9172 ctx.columnSizes.push_back(buf->getBitDouble());
9173 }
9174 ctx.wordBreak = buf->getBit();
9175 buf->getBit(); // unknown trailing bit
9176 } else {
9177 ctx.hasContentsBlock = buf->getBit();
9178 if (ctx.hasContentsBlock) {
9179 // BlockTableRecord handle 341 — deferred
9180 ctx.blockNormal = buf->get3BitDouble();
9181 ctx.blockLocation = buf->get3BitDouble();
9182 ctx.blockScale = buf->get3BitDouble();
9183 ctx.blockRotation = buf->getBitDouble();
9184 ctx.blockColor = buf->getCmColor(version);
9185 for (size_t i = 0; i < 16; ++i) {
9186 ctx.blockTransform[i] = buf->getBitDouble();
9187 }
9188 }
9189 }
9190
9191 // Common tail.
9192 ctx.basePoint = buf->get3BitDouble();
9193 ctx.baseDirection = buf->get3BitDouble();
9194 ctx.baseVertical = buf->get3BitDouble();
9195 ctx.isNormalReversed = buf->getBit();
9196
9197 if (version >= DRW::AC1024) {
9198 ctx.styleTopAttach = buf->getBitShort();
9199 ctx.styleBottomAttach = buf->getBitShort();
9200 }
9201
9202 return buf->isGood();
9203}
9204
9205static bool encodeMLeaderRoot(DRW::Version version, dwgBufferW *buf,
9206 const DRW_MLeaderRoot& root) {
9207 if (root.breaks.size() > 5000 || root.leaderLines.size() > 5000)
9208 return false;
9209
9210 buf->putBit(root.isContentValid ? 1 : 0);
9211 buf->putBit(root.unknown291 ? 1 : 0);
9212 if (root.isContentValid)
9213 buf->put3BitDouble(root.connectionPoint);
9214 if (root.unknown291)
9215 buf->put3BitDouble(root.direction);
9216
9217 buf->putBitLong(static_cast<std::int32_t>(root.breaks.size()));
9218 for (const auto& brk : root.breaks) {
9219 buf->put3BitDouble(brk.first);
9220 buf->put3BitDouble(brk.second);
9221 }
9222
9223 buf->putBitLong(root.leaderIndex);
9224 buf->putBitDouble(root.landingDistance);
9225
9226 buf->putBitLong(static_cast<std::int32_t>(root.leaderLines.size()));
9227 for (const DRW_MLeaderLeaderLine& line : root.leaderLines) {
9228 if (line.points.size() > 5000 || line.breaks.size() > 5000)
9229 return false;
9230 buf->putBitLong(static_cast<std::int32_t>(line.points.size()));
9231 for (const DRW_Coord& point : line.points)
9232 buf->put3BitDouble(point);
9233
9234 buf->putBitLong(static_cast<std::int32_t>(line.breaks.size()));
9235 for (const auto& brk : line.breaks) {
9236 buf->put3BitDouble(brk.first);
9237 buf->put3BitDouble(brk.second);
9238 }
9239 buf->putBitLong(line.leaderLineIndex);
9240
9241 if (version >= DRW::AC1024) {
9242 buf->putBitShort(line.leaderType);
9243 buf->putCmColor(version, static_cast<std::uint16_t>(line.color));
9244 buf->putBitLong(line.lineWeight);
9245 buf->putBitDouble(line.arrowSize);
9246 buf->putBitLong(line.overrideFlags);
9247 }
9248 }
9249
9250 if (version >= DRW::AC1024)
9251 buf->putBitShort(root.attachmentDirection);
9252
9253 return true;
9254}
9255
9256static bool encodeMLeaderAnnotContext(DRW::Version version, dwgBufferW *buf,
9257 dwgBufferW *strBuf,
9258 const DRW_MLeaderAnnotContext& ctx) {
9259 if (ctx.roots.size() > 1000000 || ctx.columnSizes.size() > 1000000)
9260 return false;
9261 if (ctx.hasContentsBlock)
9262 return false;
9263
9264 buf->putBitLong(static_cast<std::int32_t>(ctx.roots.size()));
9265 for (const DRW_MLeaderRoot& root : ctx.roots) {
9266 if (!encodeMLeaderRoot(version, buf, root))
9267 return false;
9268 }
9269
9270 buf->putBitDouble(ctx.overallScale);
9271 buf->put3BitDouble(ctx.contentBasePoint);
9272 buf->putBitDouble(ctx.textHeight);
9273 buf->putBitDouble(ctx.arrowHeadSize);
9274 buf->putBitDouble(ctx.landingGap);
9275 buf->putBitShort(ctx.styleLeftAttach);
9276 buf->putBitShort(ctx.styleRightAttach);
9277 buf->putBitShort(ctx.textAlignType);
9278 buf->putBitShort(ctx.attachmentType);
9279 buf->putBit(ctx.hasTextContents ? 1 : 0);
9280
9281 if (ctx.hasTextContents) {
9282 (strBuf ? strBuf : buf)->putVariableText(version, ctx.textLabel);
9283 buf->put3BitDouble(ctx.textNormal);
9284 buf->put3BitDouble(ctx.textLocation);
9285 buf->put3BitDouble(ctx.textDirection);
9286 buf->putBitDouble(ctx.textRotation);
9287 buf->putBitDouble(ctx.boundaryWidth);
9288 buf->putBitDouble(ctx.boundaryHeight);
9289 buf->putBitDouble(ctx.lineSpacingFactor);
9290 buf->putBitShort(ctx.lineSpacingStyle);
9291 buf->putCmColor(version, static_cast<std::uint16_t>(ctx.textColor));
9292 buf->putBitShort(ctx.alignment);
9293 buf->putBitShort(ctx.flowDirection);
9294 buf->putCmColor(version, static_cast<std::uint16_t>(ctx.bgFillColor));
9295 buf->putBitDouble(ctx.bgScaleFactor);
9296 buf->putBitLong(ctx.bgTransparency);
9297 buf->putBit(ctx.bgFillEnabled ? 1 : 0);
9298 buf->putBit(ctx.bgMaskFillOn ? 1 : 0);
9299 buf->putBitShort(ctx.columnType);
9300 buf->putBit(ctx.textHeightAuto ? 1 : 0);
9301 buf->putBitDouble(ctx.columnWidth);
9302 buf->putBitDouble(ctx.columnGutter);
9303 buf->putBit(ctx.columnFlowReversed ? 1 : 0);
9304 buf->putBitLong(static_cast<std::int32_t>(ctx.columnSizes.size()));
9305 for (double columnSize : ctx.columnSizes)
9306 buf->putBitDouble(columnSize);
9307 buf->putBit(ctx.wordBreak ? 1 : 0);
9308 buf->putBit(0);
9309 } else {
9310 buf->putBit(0); // hasContentsBlock
9311 }
9312
9313 buf->put3BitDouble(ctx.basePoint);
9314 buf->put3BitDouble(ctx.baseDirection);
9315 buf->put3BitDouble(ctx.baseVertical);
9316 buf->putBit(ctx.isNormalReversed ? 1 : 0);
9317
9318 if (version >= DRW::AC1024) {
9319 buf->putBitShort(ctx.styleTopAttach);
9320 buf->putBitShort(ctx.styleBottomAttach);
9321 }
9322
9323 return true;
9324}
9325
9326bool DRW_MLeader::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
9327 dwgBuffer sBuff = *buf;
9328 dwgBuffer *sBuf = buf;
9329 if (version > DRW::AC1018) { // 2007+
9330 sBuf = &sBuff;
9331 }
9332 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
9333 if (!ret) return ret;
9334 DRW_DBG("\n***************************** parsing MLEADER ***************\n")DRW_dbg::dbg("\n***************************** parsing MLEADER ***************\n"
)
;
9335
9336 // R2010b+ class version (BS, default 2; <=R2004 was 1). libreDWG
9337 // dwg2.spec:1303-1306. Absent in R2007 streams; reading it would drift.
9338 if (version >= DRW::AC1024) {
9339 classVersion = buf->getBitShort();
9340 if (classVersion > 10) {
9341 DRW_DBG("\nMLEADER: implausible classVersion=")DRW_dbg::dbg("\nMLEADER: implausible classVersion=");
9342 DRW_DBG(static_cast<int>(classVersion))DRW_dbg::dbg(static_cast<int>(classVersion));
9343 DRW_DBG(", aborting body\n")DRW_dbg::dbg(", aborting body\n");
9344 return true;
9345 }
9346 }
9347
9348 // Phase 4 — embedded AcDbMLeaderObjectContextData / MLeaderAnnotContext.
9349 // Body misalignment is local to this entity's buffer (each entity gets a
9350 // fresh buffer from the object map), so on a partial-parse failure we
9351 // keep whatever was captured and return true. This preserves the
9352 // entity-stream-continues invariant established in Phase 2.
9353 if (!parseMLeaderAnnotContext(version, buf, sBuf, context)) {
9354 DRW_DBG("\nMLEADER: AnnotContext parse drift — partial fields kept\n")DRW_dbg::dbg("\nMLEADER: AnnotContext parse drift — partial fields kept\n"
)
;
9355 return true;
9356 }
9357
9358 // Phase 3 — entity-level fields per §20.4.48 (after the AnnotContext).
9359 // Many handle slots are deferred to the trailing handle stream and not
9360 // stored here yet (resolution comes in Phase 7).
9361 overrideFlags = buf->getBitLong();
9362 leaderType = buf->getBitShort();
9363 leaderColor = buf->getCmColor(version);
9364 // leader line type handle 341 — handle stream
9365 leaderLineWeight = buf->getBitLong();
9366 landingEnabled = buf->getBit();
9367 doglegEnabled = buf->getBit();
9368 landingDistance = buf->getBitDouble();
9369 // arrow head handle 342 — handle stream
9370 defaultArrowHeadSize = buf->getBitDouble();
9371 styleContentType = buf->getBitShort();
9372 // text style handle 343 — handle stream
9373 styleLeftAttach = buf->getBitShort();
9374 styleRightAttach = buf->getBitShort();
9375 styleTextAngleType = buf->getBitShort();
9376 unknown175 = buf->getBitShort();
9377 styleTextColor = buf->getCmColor(version);
9378 styleTextFrameEnabled = buf->getBit();
9379 // style block handle 344 — handle stream (optional)
9380 styleBlockColor = buf->getCmColor(version);
9381 styleBlockScale = buf->get3BitDouble();
9382 styleBlockRotation = buf->getBitDouble();
9383 styleAttachmentType = buf->getBitShort();
9384 isAnnotative = buf->getBit();
9385
9386 // R2007 arrays (pre-R2010 only): per spec §20.4.48. Bounds-check the
9387 // counts; a misaligned bit stream would produce huge nonsense values.
9388 // On a sanity-check trip, abort the rest of the body parse but keep
9389 // the entity (per Phase 4 contract above).
9390 if (version < DRW::AC1024) {
9391 std::int32_t nArrows = buf->getBitLong();
9392 if (nArrows < 0 || nArrows > 1000000) return true;
9393 arrowHeads.reserve(static_cast<size_t>(nArrows));
9394 for (std::int32_t i = 0; i < nArrows; ++i) {
9395 ArrowHeadEntry e;
9396 e.isDefault = buf->getBit();
9397 arrowHeads.push_back(e);
9398 }
9399 std::int32_t nLabels = buf->getBitLong();
9400 if (nLabels < 0 || nLabels > 1000000) return true;
9401 blockLabels.reserve(static_cast<size_t>(nLabels));
9402 for (std::int32_t i = 0; i < nLabels; ++i) {
9403 BlockLabelEntry e;
9404 e.labelText = sBuf->getVariableText(version, false);
9405 e.uiIndex = buf->getBitShort();
9406 e.width = buf->getBitDouble();
9407 blockLabels.push_back(std::move(e));
9408 }
9409 }
9410
9411 isTextDirectionNegative = buf->getBit();
9412 ipeAlign = buf->getBitShort();
9413 justification = buf->getBitShort();
9414 scaleFactor = buf->getBitDouble();
9415
9416 if (version >= DRW::AC1024) { // R2010+
9417 attachmentDirection = buf->getBitShort();
9418 styleTopAttach = buf->getBitShort();
9419 styleBottomAttach = buf->getBitShort();
9420 }
9421 if (version >= DRW::AC1027) { // R2013+
9422 leaderExtendedToText = buf->getBit();
9423 }
9424
9425 // Common entity handles first (owner/reactors/xdic/layer/ltype/...) —
9426 // entity-specific handles follow in declared order from libreDWG
9427 // dwg2.spec:1386-1453. Read order in the trailing handle stream:
9428 // 1. AnnotContext content handle (text_style 340 if hasTextContents,
9429 // else block_table 341 if hasContentsBlock).
9430 // 2. (R2010b+ only) per-leader-line ltype + arrow handles, in the
9431 // same iteration order as the body block.
9432 // 3. mleaderstyle (340), line_ltype (341), arrow_handle (342),
9433 // text_style (343), block_style (344) entity-level handles.
9434 // 4. (R14-R2007 only) per-arrowhead + per-blocklabel handles.
9435 ret = DRW_Entity::parseDwgEntHandle(version, buf);
9436 if (!ret) {
9437 DRW_DBG("\nMLEADER: parseDwgEntHandle hiccup — body fields kept\n")DRW_dbg::dbg("\nMLEADER: parseDwgEntHandle hiccup — body fields kept\n"
)
;
9438 return true;
9439 }
9440
9441 auto safeHandle = [&](dwgHandle& slot, const char* tag) {
9442 if (buf->numRemainingBytes() < 1) return false;
9443 slot = buf->getHandle();
9444 DRW_DBG(" ")DRW_dbg::dbg(" "); DRW_DBG(tag)DRW_dbg::dbg(tag); DRW_DBG(": ")DRW_dbg::dbg(": ");
9445 DRW_DBGHL(slot.code, slot.size, slot.ref)DRW_dbg::dbgHL(slot.code, slot.size, slot.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9446 return buf->isGood();
9447 };
9448
9449 // 1. AnnotContext content handle.
9450 if (context.hasTextContents) {
9451 if (!safeHandle(context.textStyleHandle, "ctx.text_style")) return true;
9452 } else if (context.hasContentsBlock) {
9453 if (!safeHandle(context.blockTableRecordHandle, "ctx.block_table")) return true;
9454 }
9455
9456 // 2. R2010b+ per-line handles, in body iteration order.
9457 if (version >= DRW::AC1024) {
9458 for (auto& root : context.roots) {
9459 for (auto& line : root.leaderLines) {
9460 if (!safeHandle(line.lineTypeHandle, "line.ltype")) return true;
9461 if (!safeHandle(line.arrowHandle, "line.arrow")) return true;
9462 }
9463 }
9464 }
9465
9466 // 3. Entity-level handles.
9467 if (!safeHandle(styleHandle, "mleaderstyle")) return true;
9468 if (!safeHandle(leaderLineTypeHandle, "line_ltype")) return true;
9469 if (!safeHandle(arrowHeadHandle, "arrow_handle")) return true;
9470 if (!safeHandle(styleTextStyleHandle, "text_style")) return true;
9471 if (!safeHandle(styleBlockHandle, "block_style")) return true;
9472
9473 // 4. R14-R2007 per-arrowhead + per-blocklabel handles (counts came
9474 // from the body-side arrays read earlier).
9475 if (version < DRW::AC1024) {
9476 for (auto& a : arrowHeads)
9477 if (!safeHandle(a.handle, "arrowheads.handle")) return true;
9478 for (auto& bl : blockLabels)
9479 if (!safeHandle(bl.attDefHandle, "blocklabels.attdef")) return true;
9480 }
9481
9482 const int rb = buf->numRemainingBytes();
9483 DRW_DBG("\nMLEADER tail rb=")DRW_dbg::dbg("\nMLEADER tail rb="); DRW_DBG(rb)DRW_dbg::dbg(rb); DRW_DBG("\n")DRW_dbg::dbg("\n");
9484 if (rb > 4) {
9485 DRW_DBG("MLEADER: handle-stream tail ")DRW_dbg::dbg("MLEADER: handle-stream tail "); DRW_DBG(rb)DRW_dbg::dbg(rb);
9486 DRW_DBG(" bytes unconsumed (handle ")DRW_dbg::dbg(" bytes unconsumed (handle ");
9487 DRW_DBGH(handle)DRW_dbg::dbgH(handle); DRW_DBG(") — review tail handle list\n")DRW_dbg::dbg(") — review tail handle list\n");
9488 }
9489
9490 return true;
9491}
9492
9493bool DRW_MLeader::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
9494 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
9495 (void)bs;
9496 if (version < DRW::AC1024)
9497 return false;
9498
9499 oType = kDwgClassNum;
9500 if (!encodeDwgCommon(version, buf, strBuf))
9501 return false;
9502
9503 buf->putBitShort(classVersion == 0 ? 2 : classVersion);
9504 if (!encodeMLeaderAnnotContext(version, buf, strBuf, context))
9505 return false;
9506
9507 buf->putBitLong(overrideFlags);
9508 buf->putBitShort(leaderType);
9509 buf->putCmColor(version, static_cast<std::uint16_t>(leaderColor));
9510 buf->putBitLong(leaderLineWeight);
9511 buf->putBit(landingEnabled ? 1 : 0);
9512 buf->putBit(doglegEnabled ? 1 : 0);
9513 buf->putBitDouble(landingDistance);
9514 buf->putBitDouble(defaultArrowHeadSize);
9515 buf->putBitShort(styleContentType);
9516 buf->putBitShort(styleLeftAttach);
9517 buf->putBitShort(styleRightAttach);
9518 buf->putBitShort(styleTextAngleType);
9519 buf->putBitShort(unknown175);
9520 buf->putCmColor(version, static_cast<std::uint16_t>(styleTextColor));
9521 buf->putBit(styleTextFrameEnabled ? 1 : 0);
9522 buf->putCmColor(version, static_cast<std::uint16_t>(styleBlockColor));
9523 buf->put3BitDouble(styleBlockScale);
9524 buf->putBitDouble(styleBlockRotation);
9525 buf->putBitShort(styleAttachmentType);
9526 buf->putBit(isAnnotative ? 1 : 0);
9527 buf->putBit(isTextDirectionNegative ? 1 : 0);
9528 buf->putBitShort(ipeAlign);
9529 buf->putBitShort(justification);
9530 buf->putBitDouble(scaleFactor);
9531
9532 buf->putBitShort(attachmentDirection);
9533 buf->putBitShort(styleTopAttach);
9534 buf->putBitShort(styleBottomAttach);
9535 if (version >= DRW::AC1027)
9536 buf->putBit(leaderExtendedToText ? 1 : 0);
9537
9538 if (!encodeDwgEntHandle(version, buf, handleBuf))
9539 return false;
9540
9541 dwgBufferW *hb = handleBuf ? handleBuf : buf;
9542 if (context.hasTextContents) {
9543 putHardPointerHandle(hb, context.textStyleHandle.ref);
9544 } else if (context.hasContentsBlock) {
9545 putHardPointerHandle(hb, context.blockTableRecordHandle.ref);
9546 }
9547
9548 for (const DRW_MLeaderRoot& root : context.roots) {
9549 for (const DRW_MLeaderLeaderLine& line : root.leaderLines) {
9550 putHardPointerHandle(hb, line.lineTypeHandle.ref);
9551 putHardPointerHandle(hb, line.arrowHandle.ref);
9552 }
9553 }
9554
9555 putHardPointerHandle(hb, styleHandle.ref);
9556 putHardPointerHandle(hb, leaderLineTypeHandle.ref);
9557 putHardPointerHandle(hb, arrowHeadHandle.ref);
9558 putHardPointerHandle(hb, styleTextStyleHandle.ref);
9559 putHardPointerHandle(hb, styleBlockHandle.ref);
9560
9561 return true;
9562}
9563
9564bool DRW_Viewport::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
9565 switch (code) {
9566 case 40:
9567 pswidth = reader->getDouble();
9568 break;
9569 case 41:
9570 psheight = reader->getDouble();
9571 break;
9572 case 68:
9573 vpstatus = reader->getInt32();
9574 break;
9575 case 69:
9576 vpID = reader->getInt32();
9577 break;
9578 case 12:
9579 centerPX = reader->getDouble();
9580 break;
9581 case 22:
9582 centerPY = reader->getDouble();
9583 break;
9584 case 15:
9585 gridSpX = reader->getDouble();
9586 break;
9587 case 25:
9588 gridSpY = reader->getDouble();
9589 break;
9590 case 46:
9591 circleZoom = reader->getDouble();
9592 break;
9593 case 72:
9594 majorGridLines = reader->getInt32();
9595 break;
9596 case 90:
9597 statusFlags = reader->getInt32();
9598 break;
9599 case 1:
9600 styleSheet = reader->getUtf8String();
9601 break;
9602 case 281:
9603 renderMode = reader->getInt32();
9604 break;
9605 case 71:
9606 ucsAtOrigin = reader->getInt32() != 0;
9607 break;
9608 case 74:
9609 ucsPerViewport = reader->getInt32() != 0;
9610 break;
9611 case 110:
9612 ucsOrigin.x = reader->getDouble();
9613 break;
9614 case 120:
9615 ucsOrigin.y = reader->getDouble();
9616 break;
9617 case 130:
9618 ucsOrigin.z = reader->getDouble();
9619 break;
9620 case 111:
9621 ucsXAxis.x = reader->getDouble();
9622 break;
9623 case 121:
9624 ucsXAxis.y = reader->getDouble();
9625 break;
9626 case 131:
9627 ucsXAxis.z = reader->getDouble();
9628 break;
9629 case 112:
9630 ucsYAxis.x = reader->getDouble();
9631 break;
9632 case 122:
9633 ucsYAxis.y = reader->getDouble();
9634 break;
9635 case 132:
9636 ucsYAxis.z = reader->getDouble();
9637 break;
9638 case 146:
9639 ucsElevation = reader->getDouble();
9640 break;
9641 case 76:
9642 ucsOrthographicType = reader->getInt32();
9643 break;
9644 case 148:
9645 shadePlotMode = reader->getInt32();
9646 break;
9647 case 292:
9648 useDefaultLighting = reader->getInt32() != 0;
9649 break;
9650 case 282:
9651 defaultLightingType = reader->getInt32();
9652 break;
9653 case 451:
9654 brightness = reader->getDouble();
9655 break;
9656 case 452:
9657 contrast = reader->getDouble();
9658 break;
9659 case 421:
9660 ambientColorRgb = reader->getInt32();
9661 break;
9662 case 431:
9663 ambientColorMethod = reader->getInt32();
9664 break;
9665 case 331:
9666 vpHeaderHandle = static_cast<std::uint32_t>(reader->getHandleString());
9667 break;
9668 case 340:
9669 clipBoundaryHandle = static_cast<std::uint32_t>(reader->getHandleString());
9670 break;
9671 case 345:
9672 namedUcsHandle = static_cast<std::uint32_t>(reader->getHandleString());
9673 break;
9674 case 346:
9675 baseUcsHandle = static_cast<std::uint32_t>(reader->getHandleString());
9676 break;
9677 case 347:
9678 backgroundHandle = static_cast<std::uint32_t>(reader->getHandleString());
9679 break;
9680 case 348:
9681 visualStyleHandle = static_cast<std::uint32_t>(reader->getHandleString());
9682 break;
9683 case 349:
9684 shadePlotHandle = static_cast<std::uint32_t>(reader->getHandleString());
9685 break;
9686 default:
9687 return DRW_Point::parseCode(code, reader);
9688 }
9689
9690 return true;
9691}
9692//ex 22 dec 34
9693bool DRW_Viewport::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
9694 dwgBuffer sBuff = *buf;
9695 dwgBuffer *sBuf = buf;
9696 if (version > DRW::AC1018) {//2007+
9697 sBuf = &sBuff; //separate buffer for strings
9698 }
9699 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
9700 if (!ret)
9701 return ret;
9702 DRW_DBG("\n***************************** parsing viewport *****************************************\n")DRW_dbg::dbg("\n***************************** parsing viewport *****************************************\n"
)
;
9703 basePoint.x = buf->getBitDouble();
9704 basePoint.y = buf->getBitDouble();
9705 basePoint.z = buf->getBitDouble();
9706 DRW_DBG("center ")DRW_dbg::dbg("center "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
9707 pswidth = buf->getBitDouble();
9708 psheight = buf->getBitDouble();
9709 DRW_DBG("\nWidth: ")DRW_dbg::dbg("\nWidth: "); DRW_DBG(pswidth)DRW_dbg::dbg(pswidth); DRW_DBG(", Height: ")DRW_dbg::dbg(", Height: "); DRW_DBG(psheight)DRW_dbg::dbg(psheight); DRW_DBG("\n")DRW_dbg::dbg("\n");
9710 //RLZ TODO: complete in dxf
9711 if (version > DRW::AC1014) {//2000+
9712 viewTarget.x = buf->getBitDouble();
9713 viewTarget.y = buf->getBitDouble();
9714 viewTarget.z = buf->getBitDouble();
9715 DRW_DBG("view Target ")DRW_dbg::dbg("view Target "); DRW_DBGPT(viewTarget.x, viewTarget.y, viewTarget.z)DRW_dbg::dbgPT(viewTarget.x, viewTarget.y, viewTarget.z);
9716 viewDir.x = buf->getBitDouble();
9717 viewDir.y = buf->getBitDouble();
9718 viewDir.z = buf->getBitDouble();
9719 DRW_DBG("\nview direction ")DRW_dbg::dbg("\nview direction "); DRW_DBGPT(viewDir.x, viewDir.y, viewDir.z)DRW_dbg::dbgPT(viewDir.x, viewDir.y, viewDir.z);
9720 twistAngle = buf->getBitDouble();
9721 DRW_DBG("\nView twist Angle: ")DRW_dbg::dbg("\nView twist Angle: "); DRW_DBG(twistAngle)DRW_dbg::dbg(twistAngle);
9722 viewHeight = buf->getBitDouble();
9723 DRW_DBG("\nview Height: ")DRW_dbg::dbg("\nview Height: "); DRW_DBG(viewHeight)DRW_dbg::dbg(viewHeight);
9724 viewLength = buf->getBitDouble();
9725 DRW_DBG(" Lens Length: ")DRW_dbg::dbg(" Lens Length: "); DRW_DBG(viewLength)DRW_dbg::dbg(viewLength);
9726 frontClip = buf->getBitDouble();
9727 DRW_DBG("\nfront Clip Z: ")DRW_dbg::dbg("\nfront Clip Z: "); DRW_DBG(frontClip)DRW_dbg::dbg(frontClip);
9728 backClip = buf->getBitDouble();
9729 DRW_DBG(" back Clip Z: ")DRW_dbg::dbg(" back Clip Z: "); DRW_DBG(backClip)DRW_dbg::dbg(backClip);
9730 snapAngle = buf->getBitDouble();
9731 DRW_DBG("\n snap Angle: ")DRW_dbg::dbg("\n snap Angle: "); DRW_DBG(snapAngle)DRW_dbg::dbg(snapAngle);
9732 centerPX = buf->getRawDouble();
9733 centerPY = buf->getRawDouble();
9734 DRW_DBG("\nview center X: ")DRW_dbg::dbg("\nview center X: "); DRW_DBG(centerPX)DRW_dbg::dbg(centerPX); DRW_DBG(", Y: ")DRW_dbg::dbg(", Y: "); DRW_DBG(centerPX)DRW_dbg::dbg(centerPX);
9735 snapPX = buf->getRawDouble();
9736 snapPY = buf->getRawDouble();
9737 DRW_DBG("\nSnap base point X: ")DRW_dbg::dbg("\nSnap base point X: "); DRW_DBG(snapPX)DRW_dbg::dbg(snapPX); DRW_DBG(", Y: ")DRW_dbg::dbg(", Y: "); DRW_DBG(snapPY)DRW_dbg::dbg(snapPY);
9738 snapSpPX = buf->getRawDouble();
9739 snapSpPY = buf->getRawDouble();
9740 DRW_DBG("\nSnap spacing X: ")DRW_dbg::dbg("\nSnap spacing X: "); DRW_DBG(snapSpPX)DRW_dbg::dbg(snapSpPX); DRW_DBG(", Y: ")DRW_dbg::dbg(", Y: "); DRW_DBG(snapSpPY)DRW_dbg::dbg(snapSpPY);
9741 //RLZ: need to complete
9742 DRW_DBG("\nGrid spacing X: ")DRW_dbg::dbg("\nGrid spacing X: "); DRW_DBG(buf->getRawDouble())DRW_dbg::dbg(buf->getRawDouble()); DRW_DBG(", Y: ")DRW_dbg::dbg(", Y: "); DRW_DBG(buf->getRawDouble())DRW_dbg::dbg(buf->getRawDouble());DRW_DBG("\n")DRW_dbg::dbg("\n");
9743 DRW_DBG("Circle zoom?: ")DRW_dbg::dbg("Circle zoom?: "); DRW_DBG(buf->getBitShort())DRW_dbg::dbg(buf->getBitShort()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9744 }
9745 if (version > DRW::AC1018) {//2007+
9746 DRW_DBG("Grid major?: ")DRW_dbg::dbg("Grid major?: "); DRW_DBG(buf->getBitShort())DRW_dbg::dbg(buf->getBitShort()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9747 }
9748 if (version > DRW::AC1014) {//2000+
9749 frozenLyCount = buf->getBitLong();
9750 DRW_DBG("Frozen Layer count?: ")DRW_dbg::dbg("Frozen Layer count?: "); DRW_DBG(frozenLyCount)DRW_dbg::dbg(frozenLyCount); DRW_DBG("\n")DRW_dbg::dbg("\n");
9751 DRW_DBG("Status Flags?: ")DRW_dbg::dbg("Status Flags?: "); DRW_DBG(buf->getBitLong())DRW_dbg::dbg(buf->getBitLong()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9752 //RLZ: Warning needed separate string buffer
9753 DRW_DBG("Style sheet?: ")DRW_dbg::dbg("Style sheet?: "); DRW_DBG(sBuf->getVariableText(version, false))DRW_dbg::dbg(sBuf->getVariableText(version, false)); DRW_DBG("\n")DRW_dbg::dbg("\n");
9754 DRW_DBG("Render mode?: ")DRW_dbg::dbg("Render mode?: "); DRW_DBG(buf->getRawChar8())DRW_dbg::dbg(buf->getRawChar8()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9755 DRW_DBG("UCS OMore...: ")DRW_dbg::dbg("UCS OMore...: "); DRW_DBG(buf->getBit())DRW_dbg::dbg(buf->getBit()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9756 DRW_DBG("UCS VMore...: ")DRW_dbg::dbg("UCS VMore...: "); DRW_DBG(buf->getBit())DRW_dbg::dbg(buf->getBit()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9757 DRW_DBG("UCS OMore...: ")DRW_dbg::dbg("UCS OMore...: "); DRW_DBGPT(buf->getBitDouble(), buf->getBitDouble(), buf->getBitDouble())DRW_dbg::dbgPT(buf->getBitDouble(), buf->getBitDouble()
, buf->getBitDouble())
; DRW_DBG("\n")DRW_dbg::dbg("\n");
9758 DRW_DBG("ucs XAMore...: ")DRW_dbg::dbg("ucs XAMore...: "); DRW_DBGPT(buf->getBitDouble(), buf->getBitDouble(), buf->getBitDouble())DRW_dbg::dbgPT(buf->getBitDouble(), buf->getBitDouble()
, buf->getBitDouble())
; DRW_DBG("\n")DRW_dbg::dbg("\n");
9759 DRW_DBG("UCS YMore....: ")DRW_dbg::dbg("UCS YMore....: "); DRW_DBGPT(buf->getBitDouble(), buf->getBitDouble(), buf->getBitDouble())DRW_dbg::dbgPT(buf->getBitDouble(), buf->getBitDouble()
, buf->getBitDouble())
; DRW_DBG("\n")DRW_dbg::dbg("\n");
9760 DRW_DBG("UCS EMore...: ")DRW_dbg::dbg("UCS EMore...: "); DRW_DBG(buf->getBitDouble())DRW_dbg::dbg(buf->getBitDouble()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9761 DRW_DBG("UCS OVMore...: ")DRW_dbg::dbg("UCS OVMore...: "); DRW_DBG(buf->getBitShort())DRW_dbg::dbg(buf->getBitShort()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9762 }
9763 if (version > DRW::AC1015) {//2004+
9764 DRW_DBG("ShadePlot Mode...: ")DRW_dbg::dbg("ShadePlot Mode...: "); DRW_DBG(buf->getBitShort())DRW_dbg::dbg(buf->getBitShort()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9765 }
9766 if (version > DRW::AC1018) {//2007+
9767 DRW_DBG("Use def Light...: ")DRW_dbg::dbg("Use def Light...: "); DRW_DBG(buf->getBit())DRW_dbg::dbg(buf->getBit()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9768 DRW_DBG("Def light type?: ")DRW_dbg::dbg("Def light type?: "); DRW_DBG(buf->getRawChar8())DRW_dbg::dbg(buf->getRawChar8()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9769 DRW_DBG("Brightness: ")DRW_dbg::dbg("Brightness: "); DRW_DBG(buf->getBitDouble())DRW_dbg::dbg(buf->getBitDouble()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9770 DRW_DBG("Contrast: ")DRW_dbg::dbg("Contrast: "); DRW_DBG(buf->getBitDouble())DRW_dbg::dbg(buf->getBitDouble()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9771 // ODA §20.4.38: ambient color is CMC, not ENC — confirmed by libreDWG dwg.spec:2512
9772 DRW_DBG("Ambient CMC: ")DRW_dbg::dbg("Ambient CMC: "); DRW_DBG(buf->getCmColor(version, nullptr, sBuf))DRW_dbg::dbg(buf->getCmColor(version, nullptr, sBuf)); DRW_DBG("\n")DRW_dbg::dbg("\n");
9773 }
9774 ret = DRW_Entity::parseDwgEntHandle(version, buf);
9775
9776 dwgHandle someHdl;
9777 if (version < DRW::AC1015) {//R13 & R14 only
9778 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9779 someHdl = buf->getHandle();
9780 DRW_DBG("ViewPort ent header: ")DRW_dbg::dbg("ViewPort ent header: "); DRW_DBGHL(someHdl.code, someHdl.size, someHdl.ref)DRW_dbg::dbgHL(someHdl.code, someHdl.size, someHdl.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9781 }
9782 if (version > DRW::AC1014) {//2000+
9783 for (std::uint32_t i=0; i < frozenLyCount && buf->isGood(); ++i){
9784 someHdl = buf->getHandle();
9785 DRW_DBG("Frozen layer handle ")DRW_dbg::dbg("Frozen layer handle "); DRW_DBG(i)DRW_dbg::dbg(i); DRW_DBG(": ")DRW_dbg::dbg(": "); DRW_DBGHL(someHdl.code, someHdl.size, someHdl.ref)DRW_dbg::dbgHL(someHdl.code, someHdl.size, someHdl.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9786 }
9787 someHdl = buf->getHandle();
9788 DRW_DBG("Clip bpundary handle: ")DRW_dbg::dbg("Clip bpundary handle: "); DRW_DBGHL(someHdl.code, someHdl.size, someHdl.ref)DRW_dbg::dbgHL(someHdl.code, someHdl.size, someHdl.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9789 if (version == DRW::AC1015) {//2000 only
9790 someHdl = buf->getHandle();
9791 DRW_DBG("ViewPort ent header: ")DRW_dbg::dbg("ViewPort ent header: "); DRW_DBGHL(someHdl.code, someHdl.size, someHdl.ref)DRW_dbg::dbgHL(someHdl.code, someHdl.size, someHdl.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9792 }
9793 someHdl = buf->getHandle();
9794 DRW_DBG("Named ucs handle: ")DRW_dbg::dbg("Named ucs handle: "); DRW_DBGHL(someHdl.code, someHdl.size, someHdl.ref)DRW_dbg::dbgHL(someHdl.code, someHdl.size, someHdl.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9795 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9796 someHdl = buf->getHandle();
9797 DRW_DBG("base ucs handle: ")DRW_dbg::dbg("base ucs handle: "); DRW_DBGHL(someHdl.code, someHdl.size, someHdl.ref)DRW_dbg::dbgHL(someHdl.code, someHdl.size, someHdl.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9798 }
9799 if (version > DRW::AC1018) {//2007+
9800 someHdl = buf->getHandle();
9801 DRW_DBG("background handle: ")DRW_dbg::dbg("background handle: "); DRW_DBGHL(someHdl.code, someHdl.size, someHdl.ref)DRW_dbg::dbgHL(someHdl.code, someHdl.size, someHdl.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9802 someHdl = buf->getHandle();
9803 DRW_DBG("visual style handle: ")DRW_dbg::dbg("visual style handle: "); DRW_DBGHL(someHdl.code, someHdl.size, someHdl.ref)DRW_dbg::dbgHL(someHdl.code, someHdl.size, someHdl.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9804 someHdl = buf->getHandle();
9805 DRW_DBG("shadeplot ID handle: ")DRW_dbg::dbg("shadeplot ID handle: "); DRW_DBGHL(someHdl.code, someHdl.size, someHdl.ref)DRW_dbg::dbgHL(someHdl.code, someHdl.size, someHdl.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9806 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9807 someHdl = buf->getHandle();
9808 m_sunHandle = someHdl.ref;
9809 DRW_DBG("SUN handle: ")DRW_dbg::dbg("SUN handle: "); DRW_DBGHL(someHdl.code, someHdl.size, someHdl.ref)DRW_dbg::dbgHL(someHdl.code, someHdl.size, someHdl.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9810 }
9811 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9812
9813 if (!ret)
9814 return ret;
9815 return buf->isGood();
9816}
9817
9818// ---------------------------------------------------------------------------
9819// Write helpers shared by the new encoders below.
9820// ---------------------------------------------------------------------------
9821
9822namespace {
9823// Write an absolute hard-pointer handle (code=5, ref=ref) or null handle
9824// (code=3, ref=0) depending on whether ref is non-zero.
9825static void putAbsHandle(dwgBufferW *hb, std::uint32_t ref) {
9826 dwgHandle h;
9827 h.code = (ref != 0) ? 5 : 3;
9828 h.ref = ref;
9829 h.size = 0;
9830 if (h.ref != 0) {
9831 std::uint32_t t = h.ref;
9832 while (t != 0) { t >>= 8; ++h.size; }
9833 }
9834 hb->putHandle(h);
9835}
9836} // namespace
9837
9838// ---------------------------------------------------------------------------
9839// DRW_MLine::encodeDwg — OT=47 (AC1015/AC1018/AC1024)
9840// ---------------------------------------------------------------------------
9841
9842bool DRW_MLine::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
9843 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
9844 (void)bs; (void)strBuf;
9845 oType = 47;
9846 if (!encodeDwgCommon(version, buf)) return false;
9847
9848 buf->putBitDouble(scale);
9849 buf->putRawChar8(justification);
9850 buf->put3BitDouble(basePoint);
9851 buf->putExtrusion(extPoint, false); // false = pre-2000 style (getExtrusion(false) in parser)
9852 buf->putBitShort(static_cast<std::uint16_t>(openClosed));
9853 buf->putRawChar8(numLines);
9854 buf->putBitShort(numVerts);
9855
9856 for (const auto& vtx : vertlist) {
9857 buf->put3BitDouble(vtx.position);
9858 buf->put3BitDouble(vtx.vertexDir);
9859 buf->put3BitDouble(vtx.miterDir);
9860 for (int li = 0; li < static_cast<int>(numLines); ++li) {
9861 const auto& segs = (li < static_cast<int>(vtx.segParms.size()))
9862 ? vtx.segParms[li] : std::vector<double>{};
9863 const auto& fills = (li < static_cast<int>(vtx.areaFillParms.size()))
9864 ? vtx.areaFillParms[li] : std::vector<double>{};
9865 buf->putBitShort(static_cast<std::uint16_t>(segs.size()));
9866 for (double s : segs) buf->putBitDouble(s);
9867 buf->putBitShort(static_cast<std::uint16_t>(fills.size()));
9868 for (double f : fills) buf->putBitDouble(f);
9869 }
9870 }
9871
9872 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
9873
9874 // MLINE style handle — extra handle after standard entity handles.
9875 if (version > DRW::AC1014) {
9876 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
9877 putAbsHandle(hb, styleHandle);
9878 }
9879 return true;
9880}
9881
9882// ---------------------------------------------------------------------------
9883// DRW_Vertex::encodeDwg — OT varies by flags
9884// ---------------------------------------------------------------------------
9885
9886bool DRW_Vertex::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
9887 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
9888 (void)bs; (void)strBuf;
9889 switch (m_dwgSubtype) {
9890 case DwgSubtype::Vertex2D: oType = 0x0A; break;
9891 case DwgSubtype::Vertex3D: oType = 0x0B; break;
9892 case DwgSubtype::Mesh: oType = 0x0C; break;
9893 case DwgSubtype::Polyface: oType = 0x0D; break;
9894 case DwgSubtype::PolyfaceFace: oType = 0x0E; break;
9895 case DwgSubtype::Auto:
9896 if ((flags & 64) != 0)
9897 oType = 0x0D; // VERTEX_PFACE coordinate vertex
9898 else if ((flags & 128) != 0)
9899 oType = 0x0E; // VERTEX_PFACE_FACE
9900 else if ((flags & 16) != 0)
9901 oType = 0x0C; // VERTEX_MESH
9902 else if ((flags & 32) != 0 || (flags & 8) != 0)
9903 oType = 0x0B; // VERTEX_3D
9904 else
9905 oType = 0x0A; // VERTEX_2D
9906 break;
9907 }
9908
9909 if (!encodeDwgCommon(version, buf)) return false;
9910
9911 if (oType == 0x0A) {
9912 buf->putRawChar8(static_cast<std::uint8_t>(flags));
9913 buf->put3BitDouble(basePoint);
9914 buf->putBitDouble(stawidth);
9915 buf->putBitDouble(endwidth);
9916 buf->putBitDouble(bulge);
9917 if (version > DRW::AC1021)
9918 buf->putBitLong(static_cast<std::int32_t>(identifier));
9919 buf->putBitDouble(tgdir);
9920 } else if (oType == 0x0B || oType == 0x0C || oType == 0x0D) {
9921 buf->putRawChar8(static_cast<std::uint8_t>(flags));
9922 buf->put3BitDouble(basePoint);
9923 } else { // 0x0E pface face
9924 buf->putBitShort(static_cast<std::uint16_t>(vindex1));
9925 buf->putBitShort(static_cast<std::uint16_t>(vindex2));
9926 buf->putBitShort(static_cast<std::uint16_t>(vindex3));
9927 buf->putBitShort(static_cast<std::uint16_t>(vindex4));
9928 }
9929
9930 return encodeDwgEntHandle(version, buf, handleBuf);
9931}
9932
9933bool DRW_SeqEnd::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
9934 if (!DRW_Entity::parseDwg(version, buf, nullptr, bs))
9935 return false;
9936 return DRW_Entity::parseDwgEntHandle(version, buf);
9937}
9938
9939bool DRW_SeqEnd::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
9940 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
9941 (void)bs; (void)strBuf;
9942 oType = 0x06;
9943 if (!encodeDwgCommon(version, buf))
9944 return false;
9945 return encodeDwgEntHandle(version, buf, handleBuf);
9946}
9947
9948// ---------------------------------------------------------------------------
9949// DRW_Polyline::encodeDwg — OT varies by flags; vertex handles emitted here.
9950// ---------------------------------------------------------------------------
9951
9952bool DRW_Polyline::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
9953 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
9954 (void)bs; (void)strBuf;
9955 // Determine object type from stored flags (mirror of parseDwg dispatch).
9956 if (flags & 64) oType = 0x1D; // POLYLINE_PFACE
9957 else if (flags & 16) oType = 0x1E; // POLYLINE_MESH
9958 else if (flags & 8) oType = 0x10; // POLYLINE_3D
9959 else oType = 0x0F; // POLYLINE_2D
9960
9961 if (!encodeDwgCommon(version, buf)) return false;
9962
9963 if (oType == 0x0F) {
9964 buf->putBitShort(static_cast<std::uint16_t>(flags));
9965 buf->putBitShort(static_cast<std::uint16_t>(curvetype));
9966 buf->putBitDouble(defstawidth);
9967 buf->putBitDouble(defendwidth);
9968 buf->putThickness(thickness, version > DRW::AC1014);
9969 buf->putBitDouble(basePoint.z);
9970 buf->putExtrusion(extPoint, version > DRW::AC1014);
9971 } else if (oType == 0x10) {
9972 // curvetype → 2 RC flag bytes (mirror of parser decode)
9973 std::uint8_t rc1 = 0;
9974 if (curvetype == 5) rc1 = 1;
9975 else if (curvetype == 6) rc1 = 2;
9976 else if (curvetype == 8) rc1 = 3;
9977 buf->putRawChar8(rc1);
9978 buf->putRawChar8(static_cast<std::uint8_t>(flags & 1)); // bit 0 = closed
9979 } else if (oType == 0x1D) {
9980 buf->putBitShort(static_cast<std::uint16_t>(vertexcount));
9981 buf->putBitShort(static_cast<std::uint16_t>(facecount));
9982 } else { // 0x1E MESH
9983 buf->putBitShort(static_cast<std::uint16_t>(flags & ~16)); // strip reader-added bit 4
9984 buf->putBitShort(static_cast<std::uint16_t>(curvetype));
9985 buf->putBitShort(static_cast<std::uint16_t>(vertexcount)); // M count
9986 buf->putBitShort(static_cast<std::uint16_t>(facecount)); // N count
9987 buf->putBitShort(static_cast<std::uint16_t>(smoothM)); // mDensity, DXF 73
9988 buf->putBitShort(static_cast<std::uint16_t>(smoothN)); // nDensity, DXF 74
9989 }
9990
9991 // AC2004+ (>AC1015): emit vertex count before the handle section.
9992 std::int32_t ooCount = static_cast<std::int32_t>(vertlist.size());
9993 if (version > DRW::AC1015)
9994 buf->putBitLong(ooCount);
9995
9996 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
9997
9998 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
9999
10000 if (version < DRW::AC1018) {
10001 // R2000-: first/last vertex handles (absolute hard pointers).
10002 putAbsHandle(hb, vertlist.empty() ? 0u : vertlist.front()->handle);
10003 putAbsHandle(hb, vertlist.empty() ? 0u : vertlist.back()->handle);
10004 } else {
10005 // R2004+: one handle per vertex.
10006 for (const auto& v : vertlist)
10007 putAbsHandle(hb, v ? v->handle : 0u);
10008 }
10009 putAbsHandle(hb, seqEndH.ref);
10010
10011 return true;
10012}
10013
10014// ---------------------------------------------------------------------------
10015// DRW_Leader::encodeDwg — OT=45 (AC1015/AC1018/AC1024)
10016// ---------------------------------------------------------------------------
10017
10018bool DRW_Leader::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
10019 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
10020 (void)bs; (void)strBuf;
10021 oType = 45;
10022 if (!encodeDwgCommon(version, buf)) return false;
10023
10024 buf->putBit(0); // unknown bit
10025 buf->putBitShort(0); // annotType (ignored on read)
10026 buf->putBitShort(static_cast<std::int16_t>(leadertype)); // pathType (DXF code 72)
10027 buf->putBitLong(static_cast<std::int32_t>(vertexlist.size()));
10028 for (const auto& vp : vertexlist)
10029 buf->put3BitDouble(*vp);
10030 buf->put3BitDouble(DRW_Coord(0, 0, 0)); // Endptproj (ignored on read)
10031 // ODA §20.4.47: Extrusion is plain 3DPOINT (3BD), not BE — matches parseDwg.
10032 buf->put3BitDouble(extrusionPoint);
10033
10034 buf->put3BitDouble(horizdir);
10035 buf->put3BitDouble(offsetblock);
10036
10037 if (version > DRW::AC1012)
10038 buf->put3BitDouble(DRW_Coord(0, 0, 0)); // unknown coord
10039
10040 if (version < DRW::AC1015)
10041 buf->putBitDouble(0.0); // dimgap (pre-R2000)
10042
10043 if (version < DRW::AC1024) {
10044 buf->putBitDouble(textheight);
10045 buf->putBitDouble(textwidth);
10046 }
10047
10048 buf->putBit(static_cast<std::uint8_t>(hookline));
10049 buf->putBit(static_cast<std::uint8_t>(arrow));
10050
10051 if (version < DRW::AC1015) {
10052 buf->putBitShort(0); // arrowHeadType
10053 buf->putBitDouble(0.0); // dimasz
10054 buf->putBit(0); // unk
10055 buf->putBit(0); // unk
10056 buf->putBitShort(0); // unk short
10057 buf->putBitShort(0); // byBlock color
10058 buf->putBit(0); // unk
10059 buf->putBit(0); // unk
10060 } else {
10061 buf->putBitShort(0); // unk short (R2000+)
10062 buf->putBit(0); // unk
10063 buf->putBit(0); // unk
10064 }
10065
10066 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
10067
10068 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
10069 putAbsHandle(hb, 0); // AnnotH — null (no annotation entity)
10070 putAbsHandle(hb, 0x15); // dimStyleH — hard ptr to STANDARD (handle 0x15)
10071
10072 return true;
10073}
10074
10075// ---------------------------------------------------------------------------
10076// DRW_Viewport::encodeDwg — OT=34 (AC1015/AC1018/AC1024)
10077// ---------------------------------------------------------------------------
10078
10079bool DRW_Viewport::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
10080 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
10081 (void)bs;
10082 oType = 34;
10083 // Use strBuf for TV strings in AC1024; for AC1015/AC1018 strings go inline.
10084 dwgBufferW *sb = (strBuf && version > DRW::AC1018) ? strBuf : buf;
10085 if (!encodeDwgCommon(version, buf)) return false;
10086
10087 buf->putBitDouble(basePoint.x);
10088 buf->putBitDouble(basePoint.y);
10089 buf->putBitDouble(basePoint.z);
10090 buf->putBitDouble(pswidth);
10091 buf->putBitDouble(psheight);
10092
10093 if (version > DRW::AC1014) {
10094 buf->putBitDouble(viewTarget.x);
10095 buf->putBitDouble(viewTarget.y);
10096 buf->putBitDouble(viewTarget.z);
10097 buf->putBitDouble(viewDir.x);
10098 buf->putBitDouble(viewDir.y);
10099 buf->putBitDouble(viewDir.z);
10100 buf->putBitDouble(twistAngle);
10101 buf->putBitDouble(viewHeight);
10102 buf->putBitDouble(viewLength); // lens length
10103 buf->putBitDouble(frontClip);
10104 buf->putBitDouble(backClip);
10105 buf->putBitDouble(snapAngle);
10106 buf->putRawDouble(centerPX);
10107 buf->putRawDouble(centerPY);
10108 buf->putRawDouble(snapPX);
10109 buf->putRawDouble(snapPY);
10110 buf->putRawDouble(snapSpPX);
10111 buf->putRawDouble(snapSpPY);
10112 buf->putRawDouble(0.0); // gridSpacingX
10113 buf->putRawDouble(0.0); // gridSpacingY
10114 buf->putBitShort(0); // circleZoom
10115 }
10116
10117 if (version > DRW::AC1018)
10118 buf->putBitShort(0); // gridMajor (AC2007+)
10119
10120 if (version > DRW::AC1014) {
10121 buf->putBitLong(0); // frozenLyCount
10122 buf->putBitLong(0); // statusFlags
10123 sb->putVariableText(version, ""); // styleSheet TV
10124 buf->putRawChar8(0); // renderMode
10125 buf->putBit(0); // ucsPerVP
10126 buf->putBit(0); // ucs flag
10127 // UCS origin / X-axis / Y-axis (3×3BD), elevation, ortho type
10128 for (int i = 0; i < 9; ++i) buf->putBitDouble(0.0);
10129 buf->putBitDouble(0.0); // ucsElev
10130 buf->putBitShort(0); // ucsOrthoType
10131 }
10132
10133 if (version > DRW::AC1015)
10134 buf->putBitShort(0); // shadePlotMode (AC2004+)
10135
10136 if (version > DRW::AC1018) {
10137 buf->putBit(0); // useDefLight
10138 buf->putRawChar8(0); // defLightType
10139 buf->putBitDouble(0.0); // brightness
10140 buf->putBitDouble(0.0); // contrast
10141 buf->putCmColor(version, 256); // ambientColor CMC (ByLayer) per ODA §20.4.38
10142 }
10143
10144 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
10145
10146 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
10147
10148 if (version < DRW::AC1015) {
10149 putAbsHandle(hb, 0); // viewport entity header (pre-2000)
10150 }
10151 if (version > DRW::AC1014) {
10152 // frozenLyCount=0, so no frozen layer handles.
10153 putAbsHandle(hb, 0); // clip boundary (null)
10154 if (version == DRW::AC1015)
10155 putAbsHandle(hb, 0); // viewport entity header (R2000 only)
10156 putAbsHandle(hb, 0); // namedUCS (null)
10157 putAbsHandle(hb, 0); // baseUCS (null)
10158 }
10159 if (version > DRW::AC1018) {
10160 putAbsHandle(hb, 0); // background (null)
10161 putAbsHandle(hb, 0); // visualStyle (null)
10162 putAbsHandle(hb, 0); // shadeplotID (null)
10163 putAbsHandle(hb, 0); // sun (null)
10164 }
10165
10166 return true;
10167}