Bug Summary

File:libraries/libdxfrw/src/drw_entities.cpp
Warning:line 15117, 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 -O2 -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-21/lib/clang/21 -D _REENTRANT -D MUPARSER_STATIC -D QT_NO_DEBUG -I . -I ../../../Qt/6.9.0/gcc_64/mkspecs/linux-g++ -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/x86_64-linux-gnu/c++/15 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -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-11-06-031751-4837-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 <cstdint>
18#include <cmath>
19#include <cstdio>
20#include <cstdlib>
21#include <cstring>
22#include <limits>
23#include <vector>
24#include "drw_entities.h"
25#include "intern/dxfreader.h"
26#include "intern/dxfparserlimits.h"
27#include "intern/dwgbuffer.h"
28#include "intern/dwgbufferw.h"
29#include "intern/drw_textcodec.h"
30#include "intern/drw_dbg.h"
31#include "intern/drw_reserve.h"
32#include "intern/dwgreader.h"
33#include "intern/dwgsafety.h"
34
35namespace {
36
37// The two wire forms of an embedded MTEXT (AcDbMTextObjectEmbedded).
38enum class EmbeddedMTextForm {
39 // What AutoCAD writes into a multi-line ATTRIB / ATTDEF: every MTEXT
40 // field from the entity mode on, the R2018 "not annotative" block
41 // included, then the annotative data. Checked against an AutoCAD drawing,
42 // the ODA specification (ATTRIB, R2018+) and ACadSharp.
43 Full,
44 // IsNotAnnotative, one more bit, then the annotative data. GEOPOSITIONMARKER
45 // keeps this form because there is no AutoCAD drawing to check it against;
46 // LibreCAD also wrote it into attributes before it matched AutoCAD, so
47 // the attribute reader still accepts it.
48 Short
49};
50
51struct EmbeddedMTextHandleInfo {
52 bool m_ownerHandle = false;
53 int m_numReactors = 0;
54 // AC1015 has no xdict bit; AC1018+ supplies it and uses 1 to suppress
55 // the embedded xdictionary handle.
56 std::uint8_t m_xDictFlag = 0;
57 bool m_hasAcDbColorHandle = false;
58 int m_ltFlags = 0;
59 int m_plotFlags = 0;
60 int m_materialFlag = 0;
61 int m_shadowFlag = 0;
62 bool m_hasFullVisualStyle = false;
63 bool m_hasFaceVisualStyle = false;
64 bool m_hasEdgeVisualStyle = false;
65 bool m_hasStyleHandle = true;
66 bool m_hasR2018AppIdHandle = false;
67 bool m_hasAnnotativeAppHandle = false;
68};
69static bool parseEmbeddedMTextDwg(DRW::Version version, dwgBuffer *buf,
70 dwgBuffer *sBuf, DRW_MText& mtext,
71 EmbeddedMTextHandleInfo& info,
72 EmbeddedMTextForm form,
73 std::uint64_t bodyEndBit =
74 std::numeric_limits<std::uint64_t>::max(),
75 std::uint64_t stringEndBit =
76 std::numeric_limits<std::uint64_t>::max());
77static bool consumeEmbeddedMTextHandles(
78 DRW::Version version, dwgBuffer *buf,
79 const EmbeddedMTextHandleInfo& info, DRW_MText *mtext,
80 std::uint64_t handleEndBit =
81 std::numeric_limits<std::uint64_t>::max());
82
83constexpr std::uint32_t kMaxTableRows = 10000;
84constexpr std::uint32_t kMaxTableColumns = 1000;
85constexpr std::uint32_t kMaxTableCells = 200000;
86constexpr std::uint32_t kMaxTableItems = 100000;
87constexpr std::uint32_t kMaxTableStringBytes = 16 * 1024 * 1024;
88constexpr std::uint32_t kMaxTableCellAttributes = 10000;
89constexpr std::int32_t kMaxLWPolylineVertices = 1000000;
90constexpr std::int32_t kMaxSplineItems = 1000000;
91constexpr std::int32_t kMaxSplineDegree = 1024;
92constexpr std::int32_t kMaxHatchItems = DRW_Hatch::kMaxDxfItems;
93constexpr std::int32_t kMaxMInsertCount =
94 std::numeric_limits<std::int16_t>::max();
95constexpr std::int32_t kMaxViewportFrozenLayers = 512;
96constexpr std::int32_t kMaxMTextColumnHeights = 4096;
97constexpr std::int32_t kMaxMLeaderItems = 5000;
98constexpr std::int32_t kMaxLeaderVertices = 5000;
99constexpr std::uint16_t kMaxLoftedSurfaceModelerFormatVersion = 3;
100
101constexpr std::int32_t kSplineFlagMethodFitPoints = 1;
102constexpr std::int32_t kSplineFlagClosed = 4;
103constexpr std::int32_t kSplineFlagUseKnotParameter = 8;
104constexpr std::int32_t kSplineKnotParamCustom = 15;
105
106bool isValidCount(std::int32_t count, std::int32_t maxCount) {
107 return count >= 0 && count <= maxCount;
108}
109
110bool readDxfIntInRange(const std::unique_ptr<dxfReader>& reader,
111 int minimum, int maximum, int& value) {
112 value = reader->getInt32();
113 return value >= minimum && value <= maximum;
114}
115
116int hexNibble(char c) {
117 if (c >= '0' && c <= '9')
118 return c - '0';
119 if (c >= 'A' && c <= 'F')
120 return c - 'A' + 10;
121 if (c >= 'a' && c <= 'f')
122 return c - 'a' + 10;
123 return -1;
124}
125
126bool decodeHexBytes(const std::string& hex, std::vector<std::uint8_t>& out) {
127 if ((hex.size() % 2) != 0)
128 return false;
129
130 std::vector<std::uint8_t> decoded;
131 const std::size_t byteCount = hex.size() / 2;
132 if (byteCount > static_cast<std::size_t>(std::numeric_limits<int>::max())
133 || !DRW::reserve(decoded, static_cast<int>(byteCount)))
134 return false;
135 for (std::size_t i = 0; i < hex.size(); i += 2) {
136 const int hi = hexNibble(hex[i]);
137 const int lo = hexNibble(hex[i + 1]);
138 if (hi < 0 || lo < 0)
139 return false;
140 decoded.push_back(static_cast<std::uint8_t>((hi << 4) | lo));
141 }
142
143 out = std::move(decoded);
144 return true;
145}
146
147bool appendBytesChecked(std::vector<std::uint8_t>& destination,
148 const std::vector<std::uint8_t>& bytes,
149 std::size_t maxSize) {
150 if (destination.size() > maxSize
151 || bytes.size() > maxSize - destination.size())
152 return false;
153 const std::size_t newSize = destination.size() + bytes.size();
154 if (newSize > static_cast<std::size_t>(std::numeric_limits<int>::max())
155 || !DRW::reserve(destination, static_cast<int>(newSize)))
156 return false;
157 try {
158 destination.insert(destination.end(), bytes.begin(), bytes.end());
159 } catch (...) {
160 return false;
161 }
162 return true;
163}
164
165bool appendTextBytesChecked(std::vector<std::uint8_t>& destination,
166 const std::string& text,
167 std::size_t maxSize) {
168 if (destination.size() > maxSize
169 || text.size() > maxSize - destination.size())
170 return false;
171 const std::size_t newSize = destination.size() + text.size();
172 if (newSize > static_cast<std::size_t>(std::numeric_limits<int>::max())
173 || !DRW::reserve(destination, static_cast<int>(newSize)))
174 return false;
175 try {
176 destination.insert(destination.end(), text.begin(), text.end());
177 } catch (...) {
178 return false;
179 }
180 return true;
181}
182
183bool appendHexBytesChecked(std::string& destination, const std::string& hex,
184 std::size_t maxSize) {
185 if ((hex.size() & 1u) != 0u || destination.size() > maxSize)
186 return false;
187 const std::size_t byteCount = hex.size() / 2u;
188 if (byteCount > maxSize - destination.size()
189 || byteCount > static_cast<std::size_t>(std::numeric_limits<int>::max()))
190 return false;
191
192 std::string decoded;
193 if (!DRW::reserve(decoded, static_cast<int>(byteCount)))
194 return false;
195 for (std::size_t i = 0; i < hex.size(); i += 2) {
196 const int hi = hexNibble(hex[i]);
197 const int lo = hexNibble(hex[i + 1]);
198 if (hi < 0 || lo < 0)
199 return false;
200 decoded.push_back(static_cast<char>((hi << 4) | lo));
201 }
202 try {
203 destination.append(decoded);
204 } catch (...) {
205 return false;
206 }
207 return true;
208}
209
210std::uint64_t currentDwgBit(const dwgBuffer *buf) {
211 return buf->getPosition() * 8 + buf->getBitPos();
212}
213
214bool leaderHasEndpointProjection(DRW::Version version) {
215 return version >= DRW::AC1014;
216}
217
218bool leaderHasTextBox(DRW::Version version) {
219 // R2010+ files use the compact LEADER layout without these legacy fields.
220 return version <= DRW::AC1021;
221}
222
223std::uint64_t tableBodyEndBit(DRW::Version version,
224 const dwgBuffer *stringBuf,
225 std::uint32_t objectSize) {
226 // AC1021 objects may legally omit the string footer when they have no
227 // detached text. Keep their complete object body available; AC1024+
228 // frames have the unambiguous extended object-data boundary.
229 if (version > DRW::AC1021 && stringBuf != nullptr)
230 return currentDwgBit(stringBuf);
231 return objectSize;
232}
233
234bool readTableBodyCount(DRW::Version version, dwgBuffer *buf,
235 std::uint64_t objectSize, std::uint32_t maximum,
236 std::uint32_t minimumBitsPerItem,
237 std::uint32_t& value) {
238 if (buf == nullptr || !buf->isGood())
239 return false;
240
241 dwgBuffer probe = buf->forkIndependent();
242 const std::int32_t parsed = probe.getBitLong();
243 if (!probe.isGood() || parsed < 0
244 || static_cast<std::uint32_t>(parsed) > maximum)
245 return false;
246
247 if (version >= DRW::AC1015 && objectSize != 0) {
248 const std::uint64_t current = currentDwgBit(&probe);
249 if (current > objectSize || (minimumBitsPerItem != 0
250 && static_cast<std::uint64_t>(parsed)
251 > (objectSize - current)
252 / minimumBitsPerItem))
253 return false;
254 }
255
256 *buf = probe;
257 value = static_cast<std::uint32_t>(parsed);
258 return true;
259}
260
261std::uint64_t proxyEntityEndBit(const dwgBuffer& buffer,
262 std::uint32_t objectSize) {
263 if (objectSize != 0)
264 return objectSize;
265 return buffer.size() > std::numeric_limits<std::uint64_t>::max() / 8u
266 ? std::numeric_limits<std::uint64_t>::max()
267 : static_cast<std::uint64_t>(buffer.size()) * 8u;
268}
269
270bool entityBodyDataEndBit(const dwgBuffer& stringBuffer,
271 DRW::Version version, std::uint32_t objectSize,
272 std::uint64_t& endBit,
273 std::uint64_t* stringEndBit = nullptr) {
274 if (version <= DRW::AC1021 || objectSize == 0) {
275 endBit = proxyEntityEndBit(stringBuffer, objectSize);
276 if (stringEndBit != nullptr)
277 *stringEndBit = endBit;
278 return true;
279 }
280
281 std::uint64_t stringStartBit = 0;
282 std::uint64_t parsedStringEndBit = 0;
283 if (!stringBuffer.getR2007StringStreamBounds(
284 objectSize, stringStartBit, parsedStringEndBit)
285 || stringStartBit > parsedStringEndBit
286 || parsedStringEndBit > objectSize
287 || currentDwgBit(&stringBuffer) != stringStartBit) {
288 return false;
289 }
290 endBit = stringStartBit;
291 if (stringEndBit != nullptr)
292 *stringEndBit = parsedStringEndBit;
293 return true;
294}
295
296bool dwgHandleStreamEndBit(const dwgBuffer& buffer, DRW::Version version,
297 std::uint32_t objectSize, std::uint32_t handleBits,
298 std::uint64_t& endBit) {
299 if (version > DRW::AC1021) {
300 std::uint64_t totalBits = 0;
301 return dwgSafety::add(objectSize, handleBits, endBit)
302 && dwgSafety::multiply(buffer.size(), 8, totalBits)
303 && endBit <= totalBits;
304 }
305 return dwgSafety::multiply(buffer.size(), 8, endBit);
306}
307
308bool proxyEntityHasBits(const dwgBuffer& buffer, std::uint64_t endBit,
309 std::uint64_t count) {
310 const std::uint64_t current = currentDwgBit(&buffer);
311 return current <= endBit && count <= endBit - current;
312}
313
314bool readProxyEntityOptionalBitLong(dwgBuffer& buffer, std::uint64_t endBit,
315 bool& present, std::int32_t& value) {
316 present = false;
317 if (!proxyEntityHasBits(buffer, endBit, 2))
318 return true;
319
320 const std::uint8_t prefix = buffer.get2Bits();
321 if (!buffer.isGood())
322 return false;
323 if (prefix == 0) {
324 if (!proxyEntityHasBits(buffer, endBit, 32))
325 return false;
326 value = static_cast<std::int32_t>(buffer.getRawLong32());
327 } else if (prefix == 1) {
328 if (!proxyEntityHasBits(buffer, endBit, 8))
329 return false;
330 value = static_cast<std::int32_t>(buffer.getRawChar8());
331 } else {
332 value = 0;
333 }
334 present = buffer.isGood();
335 return present;
336}
337
338bool copyDwgBitRange(const dwgBuffer& source, std::uint64_t startBit,
339 std::uint64_t endBit,
340 std::vector<std::uint8_t>& data) {
341 data.clear();
342 if (endBit <= startBit)
343 return true;
344
345 const std::uint64_t sourceEnd = proxyEntityEndBit(source, 0);
346 if (endBit > sourceEnd)
347 return false;
348 const std::uint64_t startByte = startBit >> 3;
349 const std::uint64_t endByte = (endBit >> 3) + ((endBit & 7u) != 0);
350 const std::uint64_t byteCount = endByte - startByte;
351 if (endByte < startByte
352 || byteCount > std::numeric_limits<std::size_t>::max()
353 || byteCount > static_cast<std::uint64_t>(std::numeric_limits<int>::max())
354 || !DRW::resize(data, static_cast<int>(byteCount)))
355 return false;
356 dwgBuffer copy = source;
357 if (!copy.setPosition(startByte))
358 return false;
359 if ((startBit & 7u) != 0) {
360 copy.setBitPos(static_cast<std::uint8_t>(startBit & 7u));
361 if (!copy.isGood())
362 return false;
363 }
364 return data.empty()
365 || (copy.getBytes(data.data(), data.size()) && copy.isGood());
366}
367
368bool readBoundedBitShort(dwgBuffer& buffer, std::uint64_t endBit,
369 std::uint16_t& value) {
370 if (!proxyEntityHasBits(buffer, endBit, 2))
371 return false;
372
373 // BS has a two-bit selector followed by either no payload, one byte, or
374 // two bytes. Probe the selector on an independent cursor before touching
375 // the publishing cursor, so a frame boundary is checked exactly.
376 dwgBuffer probe = buffer.forkIndependent();
377 const std::uint8_t selector = probe.get2Bits();
378 if (!probe.isGood())
379 return false;
380 const std::uint64_t bits = selector == 0 ? 18
381 : selector == 1 ? 10 : 2;
382 if (!proxyEntityHasBits(buffer, endBit, bits))
383 return false;
384 value = buffer.getBitShort();
385 return buffer.isGood();
386}
387
388bool readBoundedRawChar8(dwgBuffer& buffer, std::uint64_t endBit,
389 std::uint8_t& value) {
390 if (!proxyEntityHasBits(buffer, endBit, 8))
391 return false;
392 value = buffer.getRawChar8();
393 return buffer.isGood();
394}
395
396bool readBounded2Bits(dwgBuffer& buffer, std::uint64_t endBit,
397 std::uint8_t& value) {
398 if (!proxyEntityHasBits(buffer, endBit, 2))
399 return false;
400 value = buffer.get2Bits();
401 return buffer.isGood();
402}
403
404bool readBoundedRawLong32(dwgBuffer& buffer, std::uint64_t endBit,
405 std::uint32_t& value) {
406 if (!proxyEntityHasBits(buffer, endBit, 32))
407 return false;
408 value = buffer.getRawLong32();
409 return buffer.isGood();
410}
411
412bool readBoundedBit(dwgBuffer& buffer, std::uint64_t endBit, bool& value) {
413 if (!proxyEntityHasBits(buffer, endBit, 1))
414 return false;
415 value = buffer.getBit() != 0;
416 return buffer.isGood();
417}
418
419bool readBoundedBitLong(dwgBuffer& buffer, std::uint64_t endBit,
420 std::int32_t& value) {
421 if (!proxyEntityHasBits(buffer, endBit, 2))
422 return false;
423 dwgBuffer probe = buffer.forkIndependent();
424 const std::uint8_t selector = probe.get2Bits();
425 if (!probe.isGood())
426 return false;
427 const std::uint64_t bits = selector == 0 ? 34
428 : selector == 1 ? 10 : 2;
429 if (!proxyEntityHasBits(buffer, endBit, bits))
430 return false;
431 value = buffer.getBitLong();
432 return buffer.isGood();
433}
434
435bool readBoundedBitLongLong(dwgBuffer& buffer, std::uint64_t endBit,
436 std::uint64_t& value) {
437 if (!proxyEntityHasBits(buffer, endBit, 3))
438 return false;
439 dwgBuffer probe = buffer.forkIndependent();
440 const std::uint8_t byteCount = probe.get3Bits();
441 const std::uint64_t bits = 3u + static_cast<std::uint64_t>(byteCount) * 8u;
442 if (!probe.isGood() || !proxyEntityHasBits(buffer, endBit, bits))
443 return false;
444 value = buffer.getBitLongLong();
445 return buffer.isGood();
446}
447
448bool readBoundedBitDouble(dwgBuffer& buffer, std::uint64_t endBit,
449 double& value) {
450 if (!proxyEntityHasBits(buffer, endBit, 2))
451 return false;
452 dwgBuffer probe = buffer.forkIndependent();
453 const std::uint8_t selector = probe.get2Bits();
454 if (!probe.isGood())
455 return false;
456 const std::uint64_t bits = selector == 0 ? 66 : 2;
457 if (!proxyEntityHasBits(buffer, endBit, bits))
458 return false;
459 dwgBuffer candidate = buffer.forkIndependent();
460 const double parsed = candidate.getBitDouble();
461 if (!candidate.isGood() || currentDwgBit(&candidate) > endBit
462 || !std::isfinite(parsed))
463 return false;
464 buffer = candidate;
465 value = parsed;
466 return true;
467}
468
469bool readBoundedBitCoord(dwgBuffer& buffer, std::uint64_t endBit,
470 DRW_Coord& value) {
471 return readBoundedBitDouble(buffer, endBit, value.x)
472 && readBoundedBitDouble(buffer, endBit, value.y)
473 && readBoundedBitDouble(buffer, endBit, value.z);
474}
475
476bool readBoundedEnColor(dwgBuffer& buffer, std::uint64_t endBit,
477 DRW::Version version, std::uint32_t& value) {
478 if (!buffer.isGood() || currentDwgBit(&buffer) > endBit)
479 return false;
480 dwgBuffer probe = buffer.forkIndependent();
481 const std::uint32_t parsed = probe.getEnColor(version);
482 if (!probe.isGood() || currentDwgBit(&probe) > endBit)
483 return false;
484 buffer = probe;
485 buffer.lastEnColorHadDbColorRef = probe.lastEnColorHadDbColorRef;
486 buffer.lastEnColorName = probe.lastEnColorName;
487 buffer.lastEnColorBookName = probe.lastEnColorBookName;
488 buffer.lastEnColorRgb = probe.lastEnColorRgb;
489 buffer.lastEnColorAlphaRaw = probe.lastEnColorAlphaRaw;
490 value = parsed;
491 return true;
492}
493
494struct TableDwgBounds {
495 std::uint64_t bodyEndBit = std::numeric_limits<std::uint64_t>::max();
496 std::uint64_t stringEndBit = std::numeric_limits<std::uint64_t>::max();
497 std::uint64_t handleEndBit = std::numeric_limits<std::uint64_t>::max();
498};
499
500std::uint64_t tableTextEndBit(const dwgBuffer *bodyBuf,
501 const dwgBuffer *textBuf,
502 const TableDwgBounds& bounds) {
503 return textBuf == bodyBuf ? bounds.bodyEndBit : bounds.stringEndBit;
504}
505
506bool skipTableBit(dwgBuffer& buffer, std::uint64_t endBit) {
507 bool value = false;
508 return readBoundedBit(buffer, endBit, value);
509}
510
511bool skipTableBitShort(dwgBuffer& buffer, std::uint64_t endBit) {
512 std::uint16_t value = 0;
513 return readBoundedBitShort(buffer, endBit, value);
514}
515
516bool skipTableBitLong(dwgBuffer& buffer, std::uint64_t endBit) {
517 std::int32_t value = 0;
518 return readBoundedBitLong(buffer, endBit, value);
519}
520
521bool skipTableBitDouble(dwgBuffer& buffer, std::uint64_t endBit) {
522 double value = 0.0;
523 return readBoundedBitDouble(buffer, endBit, value);
524}
525
526bool readBounded3BitDouble(dwgBuffer& buffer, std::uint64_t endBit,
527 DRW_Coord& value) {
528 dwgBuffer probe = buffer.forkIndependent();
529 DRW_Coord parsed;
530 if (!readBoundedBitDouble(probe, endBit, parsed.x)
531 || !readBoundedBitDouble(probe, endBit, parsed.y)
532 || !readBoundedBitDouble(probe, endBit, parsed.z))
533 return false;
534 buffer = probe;
535 value = parsed;
536 return true;
537}
538
539bool readBoundedBytes(dwgBuffer& buffer, std::uint64_t endBit,
540 std::uint8_t *data, std::size_t size) {
541 std::uint64_t bits = 0;
542 if (!dwgSafety::multiply(static_cast<std::uint64_t>(size), 8, bits)
543 || !proxyEntityHasBits(buffer, endBit, bits))
544 return false;
545 return size == 0 || (buffer.getBytes(data, size) && buffer.isGood());
546}
547
548bool readBoundedRawLong64(dwgBuffer& buffer, std::uint64_t endBit,
549 std::uint64_t& value) {
550 if (!proxyEntityHasBits(buffer, endBit, 64))
551 return false;
552 dwgBuffer probe = buffer.forkIndependent();
553 const std::uint64_t parsed = probe.getRawLong64();
554 if (!probe.isGood())
555 return false;
556 buffer = probe;
557 value = parsed;
558 return true;
559}
560
561bool readBounded2RawDouble(dwgBuffer& buffer, std::uint64_t endBit,
562 DRW_Coord& value) {
563 if (!proxyEntityHasBits(buffer, endBit, 128))
564 return false;
565 dwgBuffer probe = buffer.forkIndependent();
566 DRW_Coord parsed = probe.get2RawDouble();
567 if (!probe.isGood()
568 || !std::isfinite(parsed.x)
569 || !std::isfinite(parsed.y)
570 || !std::isfinite(parsed.z))
571 return false;
572 parsed.z = 0.0; // 2RD carries only X and Y; keep the unused component defined.
573 buffer = probe;
574 value = parsed;
575 return true;
576}
577
578bool readBoundedRawDouble(dwgBuffer& buffer, std::uint64_t endBit,
579 double& value) {
580 if (!proxyEntityHasBits(buffer, endBit, 64))
581 return false;
582 dwgBuffer probe = buffer.forkIndependent();
583 const double parsed = probe.getRawDouble();
584 if (!probe.isGood() || currentDwgBit(&probe) > endBit
585 || !std::isfinite(parsed))
586 return false;
587 buffer = probe;
588 value = parsed;
589 return true;
590}
591
592bool readBoundedDwgHandle(dwgBuffer& buffer, std::uint64_t endBit,
593 std::uint32_t baseHandle, bool offset,
594 dwgHandle& value) {
595 if (!buffer.isGood() || currentDwgBit(&buffer) > endBit)
596 return false;
597 dwgBuffer probe = buffer.forkIndependent();
598 dwgHandle parsed;
599 if (!readDwgHandleChecked(probe, baseHandle, offset, parsed)
600 || currentDwgBit(&probe) > endBit)
601 return false;
602 buffer = probe;
603 value = parsed;
604 return true;
605}
606
607bool readBoundedDwgObjectId(dwgBuffer& buffer, std::uint64_t endBit,
608 std::uint32_t baseHandle, dwgHandle& raw,
609 dwgHandle& resolved) {
610 if (!buffer.isGood() || currentDwgBit(&buffer) > endBit)
611 return false;
612
613 dwgBuffer rawProbe = buffer.forkIndependent();
614 raw = rawProbe.getHandle();
615 if (!rawProbe.isGood() || currentDwgBit(&rawProbe) > endBit)
616 return false;
617
618 dwgBuffer resolvedProbe = buffer.forkIndependent();
619 resolved = resolvedProbe.getOffsetHandle(baseHandle);
620 if (!resolvedProbe.isGood() || currentDwgBit(&resolvedProbe) > endBit)
621 return false;
622
623 buffer = resolvedProbe;
624 return true;
625}
626
627bool readBoundedVariableText(dwgBuffer& buffer, std::uint64_t endBit,
628 DRW::Version version, UTF8STRINGstd::string& value) {
629 if (!proxyEntityHasBits(buffer, endBit, 2))
630 return false;
631
632 dwgBuffer probe = buffer.forkIndependent();
633 const std::uint16_t units = probe.getBitShort();
634 if (!probe.isGood())
635 return false;
636
637 const std::uint64_t bitsPerUnit = version > DRW::AC1018 ? 16u : 8u;
638 std::uint64_t payloadBits = 0;
639 if (!dwgSafety::multiply(static_cast<std::uint64_t>(units), bitsPerUnit,
640 payloadBits)
641 || !proxyEntityHasBits(probe, endBit, payloadBits))
642 return false;
643
644 value = buffer.getVariableText(version, false);
645 return buffer.isGood() && currentDwgBit(&buffer) <= endBit;
646}
647
648bool readBoundedDefaultDouble(dwgBuffer& buffer, std::uint64_t endBit,
649 double defaultValue, double& value) {
650 if (!proxyEntityHasBits(buffer, endBit, 2))
651 return false;
652 dwgBuffer probe = buffer.forkIndependent();
653 const std::uint8_t selector = probe.get2Bits();
654 if (!probe.isGood())
655 return false;
656 const std::uint64_t bits = selector == 0 ? 2
657 : selector == 1 ? 34
658 : selector == 2 ? 50 : 66;
659 if (!proxyEntityHasBits(buffer, endBit, bits))
660 return false;
661 value = buffer.getDefaultDouble(defaultValue);
662 return buffer.isGood();
663}
664
665bool readBoundedThickness(dwgBuffer& buffer, std::uint64_t endBit,
666 bool modern, double& value) {
667 if (!modern)
668 return readBoundedBitDouble(buffer, endBit, value);
669 if (!proxyEntityHasBits(buffer, endBit, 1))
670 return false;
671 dwgBuffer probe = buffer.forkIndependent();
672 const bool isZero = probe.getBit() != 0;
673 if (!probe.isGood())
674 return false;
675 buffer.getBit();
676 if (!buffer.isGood())
677 return false;
678 if (isZero) {
679 value = 0.0;
680 return true;
681 }
682 return readBoundedBitDouble(buffer, endBit, value);
683}
684
685bool readBoundedExtrusion(dwgBuffer& buffer, std::uint64_t endBit,
686 bool modern, DRW_Coord& value) {
687 if (modern) {
688 if (!proxyEntityHasBits(buffer, endBit, 1))
689 return false;
690 dwgBuffer probe = buffer.forkIndependent();
691 const bool isDefault = probe.getBit() != 0;
692 if (!probe.isGood())
693 return false;
694 buffer.getBit();
695 if (!buffer.isGood())
696 return false;
697 if (isDefault) {
698 value = DRW_Coord{0.0, 0.0, 1.0};
699 return true;
700 }
701 }
702
703 return readBoundedBitDouble(buffer, endBit, value.x)
704 && readBoundedBitDouble(buffer, endBit, value.y)
705 && readBoundedBitDouble(buffer, endBit, value.z);
706}
707
708bool readBoundedCmColor(dwgBuffer& buffer, dwgBuffer *stringBuffer,
709 std::uint64_t endBit, DRW::Version version,
710 std::uint32_t& value, std::int32_t *rgb24 = nullptr,
711 bool *hasRgbColor = nullptr,
712 UTF8STRINGstd::string *outName = nullptr,
713 UTF8STRINGstd::string *outBookName = nullptr,
714 std::uint64_t stringEndBit =
715 std::numeric_limits<std::uint64_t>::max()) {
716 const bool separateStringBuffer = stringBuffer != nullptr
717 && stringBuffer != &buffer;
718 dwgBuffer bodyProbe = buffer.forkIndependent();
719 dwgBuffer stringProbe = separateStringBuffer
720 ? stringBuffer->forkIndependent() : bodyProbe.forkIndependent();
721 dwgBuffer *colorStringBuffer = separateStringBuffer
722 ? &stringProbe : &bodyProbe;
723 UTF8STRINGstd::string parsedName;
724 UTF8STRINGstd::string parsedBookName;
725 std::int32_t parsedRgb = -1;
726 bool parsedHasRgb = false;
727 const std::uint32_t parsed = bodyProbe.getCmColor(
728 version, &parsedRgb, colorStringBuffer, &parsedName, &parsedBookName,
729 &parsedHasRgb);
730 if (!bodyProbe.isGood() || !colorStringBuffer->isGood()
731 || currentDwgBit(&bodyProbe) > endBit
732 || (separateStringBuffer
733 && currentDwgBit(&stringProbe) > stringEndBit))
734 return false;
735 buffer = bodyProbe;
736 if (separateStringBuffer)
737 *stringBuffer = stringProbe;
738 value = parsed;
739 if (outName != nullptr)
740 *outName = std::move(parsedName);
741 if (outBookName != nullptr)
742 *outBookName = std::move(parsedBookName);
743 if (rgb24 != nullptr)
744 *rgb24 = parsedRgb;
745 if (hasRgbColor != nullptr)
746 *hasRgbColor = parsedHasRgb;
747 return true;
748}
749
750struct DwgTextBodyData {
751 DRW_Coord basePoint{0.0, 0.0, 0.0};
752 DRW_Coord secPoint{0.0, 0.0, 0.0};
753 DRW_Coord extPoint{0.0, 0.0, 1.0};
754 double thickness = 0.0;
755 double oblique = 0.0;
756 double angle = 0.0;
757 double height = 0.0;
758 double widthscale = 1.0;
759 UTF8STRINGstd::string text;
760 int textgen = 0;
761 DRW_Text::HAlign alignH = DRW_Text::HLeft;
762 DRW_Text::VAlign alignV = DRW_Text::VBaseLine;
763};
764
765bool readBoundedDwgTextBody(
766 DRW::Version version, dwgBuffer& body, dwgBuffer& stringBuffer,
767 std::uint64_t bodyEndBit, std::uint64_t stringStartBit,
768 std::uint64_t stringEndBit, DwgTextBodyData& data) {
769 std::uint8_t dataFlags = 0;
770 if (version > DRW::AC1014) {
771 if (!readBoundedRawChar8(body, bodyEndBit, dataFlags))
772 return false;
773 if (!(dataFlags & 0x01)
774 && !readBoundedRawDouble(body, bodyEndBit, data.basePoint.z))
775 return false;
776 } else if (!readBoundedBitDouble(body, bodyEndBit, data.basePoint.z)) {
777 return false;
778 }
779
780 if (!readBoundedRawDouble(body, bodyEndBit, data.basePoint.x)
781 || !readBoundedRawDouble(body, bodyEndBit, data.basePoint.y))
782 return false;
783
784 if (version > DRW::AC1014) {
785 if (!(dataFlags & 0x02)) {
786 if (!readBoundedDefaultDouble(body, bodyEndBit,
787 data.basePoint.x, data.secPoint.x)
788 || !readBoundedDefaultDouble(body, bodyEndBit,
789 data.basePoint.y,
790 data.secPoint.y))
791 return false;
792 } else {
793 data.secPoint = data.basePoint;
794 }
795 } else if (!readBoundedRawDouble(body, bodyEndBit, data.secPoint.x)
796 || !readBoundedRawDouble(body, bodyEndBit, data.secPoint.y)) {
797 return false;
798 }
799 data.secPoint.z = data.basePoint.z;
800
801 if (!readBoundedExtrusion(body, bodyEndBit, version > DRW::AC1014,
802 data.extPoint)
803 || !readBoundedThickness(body, bodyEndBit, version > DRW::AC1014,
804 data.thickness))
805 return false;
806
807 if (version > DRW::AC1014) {
808 if (!(dataFlags & 0x04)
809 && !readBoundedRawDouble(body, bodyEndBit, data.oblique))
810 return false;
811 if (!(dataFlags & 0x08)
812 && !readBoundedRawDouble(body, bodyEndBit, data.angle))
813 return false;
814 if (!readBoundedRawDouble(body, bodyEndBit, data.height))
815 return false;
816 if (!(dataFlags & 0x10)
817 && !readBoundedRawDouble(body, bodyEndBit, data.widthscale))
818 return false;
819 } else if (!readBoundedBitDouble(body, bodyEndBit, data.oblique)
820 || !readBoundedBitDouble(body, bodyEndBit, data.angle)
821 || !readBoundedBitDouble(body, bodyEndBit, data.height)
822 || !readBoundedBitDouble(body, bodyEndBit, data.widthscale)) {
823 return false;
824 }
825 data.angle *= ARAD57.29577951308232;
826
827 if (version > DRW::AC1018 && stringStartBit >= stringEndBit) {
828 data.text.clear();
829 } else if (!readBoundedVariableText(stringBuffer, stringEndBit, version,
830 data.text)) {
831 return false;
832 }
833
834 std::uint16_t parsedTextgen = 0;
835 std::uint16_t parsedAlignH = 0;
836 std::uint16_t parsedAlignV = 0;
837 if (!(dataFlags & 0x20)
838 && !readBoundedBitShort(body, bodyEndBit, parsedTextgen))
839 return false;
840 if (!(dataFlags & 0x40)
841 && !readBoundedBitShort(body, bodyEndBit, parsedAlignH))
842 return false;
843 if (!(dataFlags & 0x80)
844 && !readBoundedBitShort(body, bodyEndBit, parsedAlignV))
845 return false;
846 data.textgen = parsedTextgen;
847 data.alignH = static_cast<DRW_Text::HAlign>(parsedAlignH);
848 data.alignV = static_cast<DRW_Text::VAlign>(parsedAlignV);
849
850 const auto finite = [](const DRW_Coord& point) {
851 return std::isfinite(point.x) && std::isfinite(point.y)
852 && std::isfinite(point.z);
853 };
854 return body.isGood() && stringBuffer.isGood() && finite(data.basePoint)
855 && finite(data.secPoint) && finite(data.extPoint)
856 && std::isfinite(data.thickness) && std::isfinite(data.oblique)
857 && std::isfinite(data.angle) && std::isfinite(data.height)
858 && std::isfinite(data.widthscale);
859}
860
861// R2018 MTEXT: the fields that follow a set "is not annotative" bit. AutoCAD
862// writes the same block for a stand-alone MTEXT and for the MTEXT embedded in
863// a multi-line ATTRIB / ATTDEF, so both codecs go through these helpers.
864struct DwgMTextR2018Block {
865 std::uint16_t m_version = 0;
866 bool m_defaultFlag = false;
867 std::int32_t m_attachment = 0;
868 DRW_Coord m_xAxisDir;
869 DRW_Coord m_insertionPoint;
870 double m_rectWidth = 0.0;
871 double m_rectHeight = 0.0;
872 double m_extentsWidth = 0.0;
873 double m_extentsHeight = 0.0;
874 std::uint16_t m_columnType = 0;
875 std::int32_t m_columnCount = 0;
876 double m_columnWidth = 0.0;
877 double m_columnGutter = 0.0;
878 bool m_columnAutoHeight = false;
879 bool m_columnFlowReversed = false;
880 std::vector<double> m_columnHeights;
881};
882
883bool readBoundedDwgMTextR2018Block(dwgBuffer& body, std::uint64_t bodyEndBit,
884 DwgMTextR2018Block& block) {
885 // The registered application handle of this block is in the handle
886 // stream. The extents come width first here, height first in the fields
887 // before the text.
888 if (!readBoundedBitShort(body, bodyEndBit, block.m_version)
889 || !readBoundedBit(body, bodyEndBit, block.m_defaultFlag)
890 || !readBoundedBitLong(body, bodyEndBit, block.m_attachment)
891 || !readBoundedBitCoord(body, bodyEndBit, block.m_xAxisDir)
892 || !readBoundedBitCoord(body, bodyEndBit, block.m_insertionPoint)
893 || !readBoundedBitDouble(body, bodyEndBit, block.m_rectWidth)
894 || !readBoundedBitDouble(body, bodyEndBit, block.m_rectHeight)
895 || !readBoundedBitDouble(body, bodyEndBit, block.m_extentsWidth)
896 || !readBoundedBitDouble(body, bodyEndBit, block.m_extentsHeight)
897 || !readBoundedBitShort(body, bodyEndBit, block.m_columnType)
898 || block.m_columnType > 2)
899 return false;
900 if (block.m_columnType != 0) {
901 if (!readBoundedBitLong(body, bodyEndBit, block.m_columnCount)
902 || !isValidCount(block.m_columnCount, kMaxMTextColumnHeights)
903 || !readBoundedBitDouble(body, bodyEndBit, block.m_columnWidth)
904 || !readBoundedBitDouble(body, bodyEndBit, block.m_columnGutter)
905 || !readBoundedBit(body, bodyEndBit, block.m_columnAutoHeight)
906 || !readBoundedBit(body, bodyEndBit, block.m_columnFlowReversed))
907 return false;
908 if (!block.m_columnAutoHeight && block.m_columnType == 2
909 && block.m_columnCount > 0) {
910 if (!DRW::reserve(block.m_columnHeights, block.m_columnCount))
911 return false;
912 for (std::int32_t i = 0; i < block.m_columnCount; ++i) {
913 double columnHeight = 0.0;
914 if (!readBoundedBitDouble(body, bodyEndBit, columnHeight)
915 || !std::isfinite(columnHeight))
916 return false;
917 block.m_columnHeights.push_back(columnHeight);
918 }
919 }
920 }
921 const auto finite = [](const DRW_Coord& point) {
922 return std::isfinite(point.x) && std::isfinite(point.y)
923 && std::isfinite(point.z);
924 };
925 return body.isGood() && finite(block.m_xAxisDir)
926 && finite(block.m_insertionPoint) && std::isfinite(block.m_rectWidth)
927 && std::isfinite(block.m_rectHeight)
928 && std::isfinite(block.m_extentsWidth)
929 && std::isfinite(block.m_extentsHeight);
930}
931
932DwgMTextR2018Block dwgMTextR2018BlockOf(const DRW_MText& mtext) {
933 DwgMTextR2018Block block;
934 block.m_version = mtext.m_r2018Version;
935 block.m_defaultFlag = mtext.m_r2018DefaultFlag;
936 block.m_attachment = mtext.m_r2018Attachment;
937 block.m_xAxisDir = mtext.m_r2018XAxisDir;
938 block.m_insertionPoint = mtext.m_r2018InsertionPoint;
939 block.m_rectWidth = mtext.m_r2018RectWidth;
940 block.m_rectHeight = mtext.m_r2018RectHeight;
941 block.m_extentsWidth = mtext.m_r2018ExtentsWidth;
942 block.m_extentsHeight = mtext.m_r2018ExtentsHeight;
943 block.m_columnType = mtext.m_r2018ColumnType;
944 block.m_columnCount = mtext.m_r2018ColumnCount;
945 block.m_columnWidth = mtext.m_r2018ColumnWidth;
946 block.m_columnGutter = mtext.m_r2018ColumnGutter;
947 block.m_columnAutoHeight = mtext.m_r2018ColumnAutoHeight;
948 block.m_columnFlowReversed = mtext.m_r2018ColumnFlowReversed;
949 block.m_columnHeights = mtext.m_r2018ColumnHeights;
950 return block;
951}
952
953void storeDwgMTextR2018Block(DwgMTextR2018Block&& block, DRW_MText& mtext) {
954 mtext.m_r2018Version = block.m_version;
955 mtext.m_r2018DefaultFlag = block.m_defaultFlag;
956 mtext.m_r2018Attachment = block.m_attachment;
957 mtext.m_r2018XAxisDir = block.m_xAxisDir;
958 mtext.m_r2018InsertionPoint = block.m_insertionPoint;
959 mtext.m_r2018RectWidth = block.m_rectWidth;
960 mtext.m_r2018RectHeight = block.m_rectHeight;
961 mtext.m_r2018ExtentsWidth = block.m_extentsWidth;
962 mtext.m_r2018ExtentsHeight = block.m_extentsHeight;
963 mtext.m_r2018ColumnType = block.m_columnType;
964 mtext.m_r2018ColumnCount = block.m_columnCount;
965 mtext.m_r2018ColumnWidth = block.m_columnWidth;
966 mtext.m_r2018ColumnGutter = block.m_columnGutter;
967 mtext.m_r2018ColumnAutoHeight = block.m_columnAutoHeight;
968 mtext.m_r2018ColumnFlowReversed = block.m_columnFlowReversed;
969 mtext.m_r2018ColumnHeights = std::move(block.m_columnHeights);
970}
971
972bool putDwgMTextR2018Block(dwgBufferW *buf, const DwgMTextR2018Block& block) {
973 const bool listsHeights = !block.m_columnAutoHeight && block.m_columnType == 2;
974 std::int32_t columnCount = block.m_columnCount;
975 if (block.m_columnType > 2 || columnCount < 0
976 || columnCount > kMaxMTextColumnHeights
977 || block.m_columnHeights.size()
978 > static_cast<std::size_t>(kMaxMTextColumnHeights))
979 return false;
980 if (listsHeights && !block.m_columnHeights.empty())
981 columnCount = static_cast<std::int32_t>(block.m_columnHeights.size());
982 if (block.m_columnType != 0 && listsHeights
983 && static_cast<std::size_t>(columnCount)
984 != block.m_columnHeights.size())
985 return false;
986
987 buf->putBitShort(block.m_version);
988 buf->putBit(block.m_defaultFlag ? 1 : 0);
989 buf->putBitLong(block.m_attachment);
990 buf->put3BitDouble(block.m_xAxisDir);
991 buf->put3BitDouble(block.m_insertionPoint);
992 buf->putBitDouble(block.m_rectWidth);
993 buf->putBitDouble(block.m_rectHeight);
994 buf->putBitDouble(block.m_extentsWidth);
995 buf->putBitDouble(block.m_extentsHeight);
996 buf->putBitShort(block.m_columnType);
997 if (block.m_columnType != 0) {
998 buf->putBitLong(columnCount);
999 buf->putBitDouble(block.m_columnWidth);
1000 buf->putBitDouble(block.m_columnGutter);
1001 buf->putBit(block.m_columnAutoHeight ? 1 : 0);
1002 buf->putBit(block.m_columnFlowReversed ? 1 : 0);
1003 if (listsHeights) {
1004 for (const double columnHeight : block.m_columnHeights)
1005 buf->putBitDouble(columnHeight);
1006 }
1007 }
1008 return true;
1009}
1010
1011bool readBoundedHatchGradient(dwgBuffer& body, dwgBuffer *stringBuffer,
1012 std::uint64_t endBit, DRW::Version version,
1013 int& isGradient, int& reserved,
1014 double& angle, double& shift, int& singleColor,
1015 double& tint, UTF8STRINGstd::string& name,
1016 std::vector<DRW_Hatch::GradientStop>& colors,
1017 std::uint64_t stringEndBit =
1018 std::numeric_limits<std::uint64_t>::max()) {
1019 std::int32_t parsedIsGradient = 0;
1020 std::int32_t parsedReserved = 0;
1021 std::int32_t parsedSingleColor = 0;
1022 double parsedAngle = 0.0;
1023 double parsedShift = 0.0;
1024 double parsedTint = 0.0;
1025 if (!readBoundedBitLong(body, endBit, parsedIsGradient)
1026 || !readBoundedBitLong(body, endBit, parsedReserved)
1027 || !readBoundedBitDouble(body, endBit, parsedAngle)
1028 || !readBoundedBitDouble(body, endBit, parsedShift)
1029 || !readBoundedBitLong(body, endBit, parsedSingleColor)
1030 || !readBoundedBitDouble(body, endBit, parsedTint))
1031 return false;
1032
1033 std::uint32_t colorCount = 0;
1034 if (!readTableBodyCount(version, &body, endBit, kMaxHatchItems, 14,
1035 colorCount))
1036 return false;
1037 std::vector<DRW_Hatch::GradientStop> parsedColors;
1038 if (!DRW::reserve(parsedColors, colorCount))
1039 return false;
1040 for (std::uint32_t i = 0; i < colorCount; ++i) {
1041 DRW_Hatch::GradientStop stop;
1042 std::uint16_t aciColor = 0;
1043 std::int32_t rgb = 0;
1044 std::uint8_t ignoredColor = 0;
1045 if (!readBoundedBitDouble(body, endBit, stop.value)
1046 || !readBoundedBitShort(body, endBit, aciColor)
1047 || !readBoundedBitLong(body, endBit, rgb)
1048 || !readBoundedRawChar8(body, endBit, ignoredColor)
1049 || !std::isfinite(stop.value))
1050 return false;
1051 stop.aciColor = aciColor;
1052 stop.rgb = rgb;
1053 parsedColors.push_back(std::move(stop));
1054 }
1055
1056 UTF8STRINGstd::string parsedName;
1057 if (stringBuffer == nullptr
1058 || !readBoundedVariableText(*stringBuffer, stringEndBit, version,
1059 parsedName))
1060 return false;
1061
1062 if (!std::isfinite(parsedAngle) || !std::isfinite(parsedShift)
1063 || !std::isfinite(parsedTint))
1064 return false;
1065 isGradient = parsedIsGradient;
1066 reserved = parsedReserved;
1067 angle = parsedAngle;
1068 shift = parsedShift;
1069 singleColor = parsedSingleColor;
1070 tint = parsedTint;
1071 name = std::move(parsedName);
1072 colors = std::move(parsedColors);
1073 return true;
1074}
1075
1076struct SurfaceDwgPayloadInfo {
1077 bool acisEmpty = false;
1078 int acisVersion = 0;
1079 int modelerFormatVersion = 0;
1080 int uIsolines = 0;
1081 int vIsolines = 0;
1082 bool revolvedFieldsValid = false;
1083 std::uint32_t revolvedClassId = 0;
1084 std::uint32_t revolvedId = 0;
1085 DRW_Coord revolvedAxisPoint;
1086 DRW_Coord revolvedAxisVector;
1087 double revolvedAngle = 0.0;
1088 double revolvedStartAngle = 0.0;
1089 std::array<double, DRW_RevolvedSurface::kTransformSize> revolvedTransform{};
1090 double revolvedDraftAngle = 0.0;
1091 double revolvedDraftStartDistance = 0.0;
1092 double revolvedDraftEndDistance = 0.0;
1093 double revolvedTwistAngle = 0.0;
1094 bool revolvedSolid = false;
1095 bool revolvedCloseToAxis = false;
1096 bool extrudedFieldsValid = false;
1097 DRW_Coord extrudedSweepVector;
1098 std::array<double, DRW_ExtrudedSurface::kTransformSize>
1099 extrudedTransform{};
1100 std::array<double, DRW_ExtrudedSurface::kTransformSize>
1101 extrudedSweepEntityTransform{};
1102 std::array<double, DRW_ExtrudedSurface::kTransformSize>
1103 extrudedPathEntityTransform{};
1104 double extrudedDraftAngle = 0.0;
1105 double extrudedDraftStartDistance = 0.0;
1106 double extrudedDraftEndDistance = 0.0;
1107 double extrudedTwistAngle = 0.0;
1108 double extrudedScaleFactor = 0.0;
1109 double extrudedAlignAngle = 0.0;
1110 bool extrudedSolid = false;
1111 std::int32_t extrudedSweepAlignmentFlags = 0;
1112 std::int32_t extrudedPathFlags = 0;
1113 bool extrudedAlignStart = false;
1114 bool extrudedBank = false;
1115 bool extrudedBasePointSet = false;
1116 bool extrudedSweepEntityTransformComputed = false;
1117 bool extrudedPathEntityTransformComputed = false;
1118 DRW_Coord extrudedReferenceVector;
1119 bool sweptFieldsValid = false;
1120 std::uint32_t sweptClassVersion = 0;
1121 std::uint32_t sweptEntityId = 0;
1122 std::vector<std::uint8_t> sweptData;
1123 std::uint32_t sweptPathEntityId = 0;
1124 std::vector<std::uint8_t> sweptPathData;
1125 std::array<double, DRW_SweptSurface::kTransformSize>
1126 sweptEntityTransform{};
1127 std::array<double, DRW_SweptSurface::kTransformSize>
1128 sweptPathEntityTransform{};
1129 double sweptDraftAngle = 0.0;
1130 double sweptDraftStartDistance = 0.0;
1131 double sweptDraftEndDistance = 0.0;
1132 double sweptTwistAngle = 0.0;
1133 double sweptScaleFactor = 1.0;
1134 double sweptAlignAngle = 0.0;
1135 std::array<double, DRW_SweptSurface::kTransformSize>
1136 sweptEntityTransformed{};
1137 std::array<double, DRW_SweptSurface::kTransformSize>
1138 sweptPathEntityTransformed{};
1139 bool sweptSolid = false;
1140 std::int32_t sweptAlignmentFlags = 0;
1141 std::int32_t sweptPathFlags = 0;
1142 bool sweptAlignStart = false;
1143 bool sweptBank = false;
1144 bool sweptBasePointSet = false;
1145 bool sweptEntityTransformComputed = false;
1146 bool sweptPathEntityTransformComputed = false;
1147 DRW_Coord sweptReferenceVector;
1148 bool loftedFieldsValid = false;
1149 std::array<double, DRW_LoftedSurface::kTransformSize>
1150 loftedEntityTransform{};
1151 std::int32_t loftedPlaneNormalLoftingType = 0;
1152 double loftedStartDraftAngle = 0.0;
1153 double loftedEndDraftAngle = 0.0;
1154 double loftedStartDraftMagnitude = 0.0;
1155 double loftedEndDraftMagnitude = 0.0;
1156 bool loftedArcLengthParameterization = false;
1157 bool loftedNoTwist = true;
1158 bool loftedAlignDirection = true;
1159 bool loftedSimpleSurfaces = true;
1160 bool loftedClosedSurfaces = false;
1161 bool loftedSolid = false;
1162 bool loftedRuledSurface = false;
1163 bool loftedVirtualGuide = false;
1164 std::int32_t loftedNumCrossSections = 0;
1165 std::int32_t loftedNumGuideCurves = 0;
1166 bool nurbsFieldsValid = false;
1167 std::uint16_t nurbsShort170 = 0;
1168 bool nurbsCvHullDisplay = false;
1169 DRW_Coord nurbsUvec1;
1170 DRW_Coord nurbsVvec1;
1171 DRW_Coord nurbsUvec2;
1172 DRW_Coord nurbsVvec2;
1173};
1174
1175std::uint64_t surfaceDwgBit(const dwgBuffer& buffer) {
1176 return buffer.getPosition() * 8u + buffer.getBitPos();
1177}
1178
1179bool surfaceDwgHasBits(const dwgBuffer& buffer, std::uint64_t endBit,
1180 std::uint64_t count) {
1181 const std::uint64_t current = surfaceDwgBit(buffer);
1182 return current <= endBit && count <= endBit - current;
1183}
1184
1185bool surfaceDwgReadBit(dwgBuffer& buffer, std::uint64_t endBit,
1186 std::uint8_t& value) {
1187 if (!surfaceDwgHasBits(buffer, endBit, 1))
1188 return false;
1189 // Probe variable-width fields on a fork so a short object frame cannot
1190 // invalidate the reader before the caller re-anchors at its boundary.
1191 dwgBuffer candidate(buffer);
1192 value = candidate.getBit();
1193 if (!candidate.isGood() || surfaceDwgBit(candidate) > endBit)
1194 return false;
1195 buffer = candidate;
1196 return true;
1197}
1198
1199bool surfaceDwgReadRawChar(dwgBuffer& buffer, std::uint64_t endBit,
1200 std::uint8_t& value) {
1201 if (!surfaceDwgHasBits(buffer, endBit, 8))
1202 return false;
1203 dwgBuffer candidate(buffer);
1204 value = candidate.getRawChar8();
1205 if (!candidate.isGood() || surfaceDwgBit(candidate) > endBit)
1206 return false;
1207 buffer = candidate;
1208 return true;
1209}
1210
1211bool surfaceDwgReadRawChar(dwgBuffer& buffer, std::uint64_t endBit) {
1212 std::uint8_t value = 0;
1213 return surfaceDwgReadRawChar(buffer, endBit, value);
1214}
1215
1216bool surfaceDwgReadBitShort(dwgBuffer& buffer, std::uint64_t endBit,
1217 std::uint16_t& value) {
1218 if (!surfaceDwgHasBits(buffer, endBit, 2))
1219 return false;
1220 dwgBuffer candidate(buffer);
1221 value = candidate.getBitShort();
1222 if (!candidate.isGood() || surfaceDwgBit(candidate) > endBit)
1223 return false;
1224 buffer = candidate;
1225 return true;
1226}
1227
1228bool surfaceDwgReadBitLong(dwgBuffer& buffer, std::uint64_t endBit,
1229 std::int32_t& value) {
1230 if (!surfaceDwgHasBits(buffer, endBit, 2))
1231 return false;
1232 dwgBuffer candidate(buffer);
1233 value = candidate.getBitLong();
1234 if (!candidate.isGood() || surfaceDwgBit(candidate) > endBit)
1235 return false;
1236 buffer = candidate;
1237 return true;
1238}
1239
1240bool surfaceDwgReadDouble(dwgBuffer& buffer, std::uint64_t endBit,
1241 double& value) {
1242 if (!surfaceDwgHasBits(buffer, endBit, 2))
1243 return false;
1244 dwgBuffer candidate(buffer);
1245 value = candidate.getBitDouble();
1246 if (!candidate.isGood() || surfaceDwgBit(candidate) > endBit
1247 || !std::isfinite(value))
1248 return false;
1249 buffer = candidate;
1250 return true;
1251}
1252
1253bool surfaceDwgReadDouble(dwgBuffer& buffer, std::uint64_t endBit) {
1254 double value = 0.0;
1255 return surfaceDwgReadDouble(buffer, endBit, value);
1256}
1257
1258bool surfaceDwgRead3BitDouble(dwgBuffer& buffer, std::uint64_t endBit) {
1259 return surfaceDwgReadDouble(buffer, endBit)
1260 && surfaceDwgReadDouble(buffer, endBit)
1261 && surfaceDwgReadDouble(buffer, endBit);
1262}
1263
1264bool surfaceDwgRead3BitDouble(dwgBuffer& buffer, std::uint64_t endBit,
1265 DRW_Coord& value) {
1266 return surfaceDwgReadDouble(buffer, endBit, value.x)
1267 && surfaceDwgReadDouble(buffer, endBit, value.y)
1268 && surfaceDwgReadDouble(buffer, endBit, value.z);
1269}
1270
1271bool surfaceDwgSkipBytes(dwgBuffer& buffer, std::uint64_t endBit,
1272 std::uint64_t count) {
1273 const std::uint64_t current = surfaceDwgBit(buffer);
1274 if (current > endBit
1275 || count > static_cast<std::uint64_t>(std::numeric_limits<std::int32_t>::max()) / 8u
1276 || count > (endBit - current) / 8u)
1277 return false;
1278 return buffer.moveBitPos(static_cast<std::int32_t>(count * 8u))
1279 && buffer.isGood() && surfaceDwgBit(buffer) <= endBit;
1280}
1281
1282bool surfaceDwgReadBytes(dwgBuffer& buffer, std::uint64_t endBit,
1283 std::int32_t count, std::size_t maxCount,
1284 std::vector<std::uint8_t>& data) {
1285 if (count < 0 || static_cast<std::size_t>(count) > maxCount
1286 || !surfaceDwgHasBits(buffer, endBit,
1287 static_cast<std::uint64_t>(count) * 8u))
1288 return false;
1289 dwgBuffer candidate(buffer);
1290 std::vector<std::uint8_t> decoded;
1291 if (!DRW::resize(decoded, count))
1292 return false;
1293 for (std::uint8_t& value : decoded) {
1294 if (!surfaceDwgReadRawChar(candidate, endBit, value))
1295 return false;
1296 }
1297 data = std::move(decoded);
1298 buffer = candidate;
1299 return true;
1300}
1301
1302std::uint8_t surfaceDwgPeekByte(const std::vector<std::uint8_t>& data,
1303 std::size_t index, std::uint8_t bit) {
1304 if (index >= data.size())
1305 return 0;
1306 if (bit == 0)
1307 return data[index];
1308 const std::uint8_t next = index + 1 < data.size() ? data[index + 1] : 0;
1309 return static_cast<std::uint8_t>((data[index] << bit) | (next >> (8 - bit)));
1310}
1311
1312std::size_t surfaceDwgFindMarker(const std::vector<std::uint8_t>& data,
1313 std::size_t start, std::uint8_t bit,
1314 const std::uint8_t* marker,
1315 std::size_t markerSize) {
1316 if (marker == nullptr || markerSize == 0 || data.size() < markerSize
1317 || start > data.size() - markerSize)
1318 return std::numeric_limits<std::size_t>::max();
1319 for (std::size_t index = start; index <= data.size() - markerSize; ++index) {
1320 bool matched = true;
1321 for (std::size_t offset = 0; offset < markerSize; ++offset) {
1322 if (surfaceDwgPeekByte(data, index + offset, bit) != marker[offset]) {
1323 matched = false;
1324 break;
1325 }
1326 }
1327 if (matched)
1328 return index;
1329 }
1330 return std::numeric_limits<std::size_t>::max();
1331}
1332
1333bool surfaceDwgSkipAcis(dwgBuffer& buffer, const std::vector<std::uint8_t>& data,
1334 std::uint64_t endBit, bool& acisEmpty, int& acisVersion) {
1335 std::uint8_t empty = 0;
1336 if (!surfaceDwgReadBit(buffer, endBit, empty))
1337 return false;
1338 acisEmpty = empty != 0;
1339 if (acisEmpty)
1340 return true;
1341
1342 std::uint8_t unused = 0;
1343 std::uint16_t version = 0;
1344 if (!surfaceDwgReadBit(buffer, endBit, unused)
1345 || !surfaceDwgReadBitShort(buffer, endBit, version))
1346 return false;
1347 acisVersion = static_cast<int>(version);
1348
1349 if (version == 2) {
1350 static constexpr std::uint8_t endAcis[] = {
1351 0x0E, 0x03, 0x45, 0x6E, 0x64, 0x0E, 0x02, 0x6F, 0x66, 0x0E,
1352 0x04, 0x41, 0x43, 0x49, 0x53, 0x0D, 0x04, 0x64, 0x61, 0x74,
1353 0x61};
1354 static constexpr std::uint8_t endAsm[] = {
1355 0x0E, 0x03, 0x45, 0x6E, 0x64, 0x0E, 0x02, 0x6F, 0x66, 0x0E,
1356 0x03, 0x41, 0x53, 0x4D, 0x0D, 0x04, 0x64, 0x61, 0x74, 0x61};
1357 const std::size_t start = static_cast<std::size_t>(buffer.getPosition());
1358 const std::uint8_t bit = buffer.getBitPos();
1359 std::size_t marker = surfaceDwgFindMarker(
1360 data, start, bit, endAcis, sizeof(endAcis));
1361 std::size_t markerSize = sizeof(endAcis);
1362 if (marker == std::numeric_limits<std::size_t>::max()) {
1363 marker = surfaceDwgFindMarker(
1364 data, start, bit, endAsm, sizeof(endAsm));
1365 markerSize = sizeof(endAsm);
1366 }
1367 if (marker == std::numeric_limits<std::size_t>::max()
1368 || marker < start)
1369 return false;
1370 return surfaceDwgSkipBytes(buffer, endBit,
1371 marker - start + markerSize);
1372 }
1373
1374 for (std::uint32_t block = 0; block < 65536u; ++block) {
1375 std::int32_t blockSize = 0;
1376 if (!surfaceDwgReadBitLong(buffer, endBit, blockSize))
1377 return false;
1378 if (blockSize == 0)
1379 return true;
1380 if (blockSize < 0
1381 || !surfaceDwgSkipBytes(buffer, endBit,
1382 static_cast<std::uint32_t>(blockSize)))
1383 return false;
1384 }
1385 return false;
1386}
1387
1388bool surfaceDwgSkipWire(dwgBuffer& buffer, std::uint64_t endBit,
1389 DRW::Version version) {
1390 if (!surfaceDwgReadRawChar(buffer, endBit))
1391 return false;
1392 std::int32_t ignored = 0;
1393 if (!surfaceDwgReadBitLong(buffer, endBit, ignored))
1394 return false;
1395 std::uint16_t color = 0;
1396 if (version < DRW::AC1018) {
1397 if (!surfaceDwgReadBitShort(buffer, endBit, color))
1398 return false;
1399 } else if (!surfaceDwgReadBitLong(buffer, endBit, ignored)) {
1400 return false;
1401 }
1402 if (!surfaceDwgReadBitLong(buffer, endBit, ignored))
1403 return false;
1404
1405 std::int32_t pointCount = 0;
1406 if (!surfaceDwgReadBitLong(buffer, endBit, pointCount)
1407 || pointCount < 0 || pointCount > 1000000)
1408 return false;
1409 for (std::int32_t point = 0; point < pointCount; ++point) {
1410 if (!surfaceDwgRead3BitDouble(buffer, endBit))
1411 return false;
1412 }
1413
1414 std::uint8_t transformPresent = 0;
1415 if (!surfaceDwgReadBit(buffer, endBit, transformPresent))
1416 return false;
1417 if (transformPresent != 0) {
1418 for (int field = 0; field < 5; ++field) {
1419 if (!surfaceDwgRead3BitDouble(buffer, endBit))
1420 return false;
1421 }
1422 for (int field = 0; field < 3; ++field) {
1423 if (!surfaceDwgReadBit(buffer, endBit, transformPresent))
1424 return false;
1425 }
1426 }
1427 return true;
1428}
1429
1430bool surfaceDwgSkipCommon3dSolid(dwgBuffer& buffer, std::uint64_t endBit,
1431 DRW::Version version, int acisVersion) {
1432 std::uint8_t present = 0;
1433 if (!surfaceDwgReadBit(buffer, endBit, present))
1434 return false;
1435 if (present != 0) {
1436 if (!surfaceDwgReadBit(buffer, endBit, present))
1437 return false;
1438 if (present != 0 && !surfaceDwgRead3BitDouble(buffer, endBit))
1439 return false;
1440
1441 std::int32_t ignored = 0;
1442 if (!surfaceDwgReadBitLong(buffer, endBit, ignored)
1443 || !surfaceDwgReadBit(buffer, endBit, present))
1444 return false;
1445 if (present != 0) {
1446 std::int32_t count = 0;
1447 if (!surfaceDwgReadBitLong(buffer, endBit, count)
1448 || count < 0 || count > 1000000)
1449 return false;
1450 for (std::int32_t i = 0; i < count; ++i) {
1451 if (!surfaceDwgSkipWire(buffer, endBit, version))
1452 return false;
1453 }
1454 if (!surfaceDwgReadBitLong(buffer, endBit, count)
1455 || count < 0 || count > 1000000)
1456 return false;
1457 for (std::int32_t i = 0; i < count; ++i) {
1458 if (!surfaceDwgReadBitLong(buffer, endBit, ignored)
1459 || !surfaceDwgRead3BitDouble(buffer, endBit)
1460 || !surfaceDwgRead3BitDouble(buffer, endBit)
1461 || !surfaceDwgRead3BitDouble(buffer, endBit)
1462 || !surfaceDwgReadBit(buffer, endBit, present)
1463 || !surfaceDwgReadBit(buffer, endBit, present))
1464 return false;
1465 if (present != 0) {
1466 std::int32_t wireCount = 0;
1467 if (!surfaceDwgReadBitLong(buffer, endBit, wireCount)
1468 || wireCount < 0 || wireCount > 1000000)
1469 return false;
1470 for (std::int32_t wire = 0; wire < wireCount; ++wire) {
1471 if (!surfaceDwgSkipWire(buffer, endBit, version))
1472 return false;
1473 }
1474 }
1475 }
1476 }
1477 }
1478
1479 if (!surfaceDwgReadBit(buffer, endBit, present))
1480 return false;
1481 if (acisVersion > 1 && version >= DRW::AC1021) {
1482 std::int32_t count = 0;
1483 if (!surfaceDwgReadBitLong(buffer, endBit, count)
1484 || count < 0 || count > 1000000)
1485 return false;
1486 for (std::int32_t i = 0; i < count; ++i) {
1487 std::int32_t ignored = 0;
1488 if (!surfaceDwgReadBitLong(buffer, endBit, ignored)
1489 || !surfaceDwgReadBitLong(buffer, endBit, ignored))
1490 return false;
1491 }
1492 }
1493 if (version >= DRW::AC1027) {
1494 if (!surfaceDwgReadBit(buffer, endBit, present))
1495 return false;
1496 std::int32_t ignored = 0;
1497 std::uint16_t ignoredShort = 0;
1498 if (!surfaceDwgReadBitLong(buffer, endBit, ignored)
1499 || !surfaceDwgReadBitShort(buffer, endBit, ignoredShort)
1500 || !surfaceDwgReadBitShort(buffer, endBit, ignoredShort)
1501 || !surfaceDwgSkipBytes(buffer, endBit, 8)
1502 || !surfaceDwgReadBitLong(buffer, endBit, ignored))
1503 return false;
1504 }
1505 return true;
1506}
1507
1508bool surfaceHasModelerFormatVersion(DRW::ETYPE type) {
1509 switch (type) {
1510 case DRW::PLANESURFACE:
1511 case DRW::LOFTEDSURFACE:
1512 case DRW::REVOLVEDSURFACE:
1513 case DRW::SWEPTSURFACE:
1514 return true;
1515 case DRW::EXTRUDEDSURFACE:
1516 case DRW::NURBSURFACE:
1517 default:
1518 return false;
1519 }
1520}
1521
1522std::uint16_t surfaceModelerFormatVersionLimit(DRW::ETYPE type) {
1523 return type == DRW::LOFTEDSURFACE ? kMaxLoftedSurfaceModelerFormatVersion
1524 : std::numeric_limits<std::uint16_t>::max();
1525}
1526
1527bool readSurfaceDwgPayload(const std::vector<std::uint8_t>& data,
1528 std::uint64_t bitSize, DRW::Version version,
1529 DRW::ETYPE type,
1530 bool hasModelerFormatVersion,
1531 std::uint16_t modelerFormatVersionLimit,
1532 SurfaceDwgPayloadInfo& info) {
1533 if (data.empty() || bitSize == 0
1534 || bitSize > static_cast<std::uint64_t>(data.size()) * 8u)
1535 return false;
1536
1537 std::vector<std::uint8_t> payload = data;
1538 dwgBuffer buffer(payload.data(), payload.size());
1539 const std::uint64_t endBit = bitSize;
1540 bool acisEmpty = false;
1541 int acisVersion = 0;
1542 if (!surfaceDwgSkipAcis(buffer, payload, endBit, acisEmpty, acisVersion))
1543 return false;
1544 if (!acisEmpty
1545 && !surfaceDwgSkipCommon3dSolid(buffer, endBit, version, acisVersion))
1546 return false;
1547
1548 std::uint16_t modelerFormatVersion = 0;
1549 std::uint16_t uIsolines = 0;
1550 std::uint16_t vIsolines = 0;
1551 if ((hasModelerFormatVersion
1552 && !surfaceDwgReadBitShort(buffer, endBit, modelerFormatVersion))
1553 || !surfaceDwgReadBitShort(buffer, endBit, uIsolines)
1554 || !surfaceDwgReadBitShort(buffer, endBit, vIsolines)) {
1555 return false;
1556 }
1557 if (hasModelerFormatVersion
1558 && modelerFormatVersion > modelerFormatVersionLimit)
1559 return false;
1560
1561 if (type == DRW::EXTRUDEDSURFACE) {
1562 // DWG stores SweepOptions before the sweep vector and its transform;
1563 // keep the candidate isolated because the same fields have a
1564 // different order in the DXF subclass.
1565 dwgBuffer candidate = buffer;
1566 std::array<double, DRW_ExtrudedSurface::kTransformSize>
1567 sweepEntityTransform{};
1568 std::array<double, DRW_ExtrudedSurface::kTransformSize>
1569 pathEntityTransform{};
1570 std::array<double, DRW_ExtrudedSurface::kTransformSize>
1571 extrudedTransform{};
1572 double draftAngle = 0.0;
1573 double draftStartDistance = 0.0;
1574 double draftEndDistance = 0.0;
1575 double twistAngle = 0.0;
1576 double scaleFactor = 0.0;
1577 double alignAngle = 0.0;
1578 std::uint8_t solid = 0;
1579 std::uint16_t sweepAlignmentFlags = 0;
1580 std::uint16_t pathFlags = 0;
1581 std::uint8_t alignStart = 0;
1582 std::uint8_t bank = 0;
1583 std::uint8_t basePointSet = 0;
1584 std::uint8_t sweepEntityTransformComputed = 0;
1585 std::uint8_t pathEntityTransformComputed = 0;
1586 DRW_Coord referenceVector;
1587 DRW_Coord sweepVector;
1588 auto readMatrix = [&](std::array<double,
1589 DRW_ExtrudedSurface::kTransformSize>& matrix) {
1590 for (double& value : matrix) {
1591 if (!surfaceDwgReadDouble(candidate, endBit, value))
1592 return false;
1593 }
1594 return true;
1595 };
1596 bool valid = surfaceDwgReadDouble(candidate, endBit, draftAngle)
1597 && surfaceDwgReadDouble(candidate, endBit, draftStartDistance)
1598 && surfaceDwgReadDouble(candidate, endBit, draftEndDistance)
1599 && surfaceDwgReadDouble(candidate, endBit, twistAngle)
1600 && surfaceDwgReadDouble(candidate, endBit, scaleFactor)
1601 && surfaceDwgReadDouble(candidate, endBit, alignAngle)
1602 && readMatrix(sweepEntityTransform)
1603 && readMatrix(pathEntityTransform)
1604 && surfaceDwgReadBit(candidate, endBit, solid)
1605 && surfaceDwgReadBitShort(candidate, endBit, sweepAlignmentFlags)
1606 && surfaceDwgReadBitShort(candidate, endBit, pathFlags)
1607 && surfaceDwgReadBit(candidate, endBit, alignStart)
1608 && surfaceDwgReadBit(candidate, endBit, bank)
1609 && surfaceDwgReadBit(candidate, endBit, basePointSet)
1610 && surfaceDwgReadBit(candidate, endBit, sweepEntityTransformComputed)
1611 && surfaceDwgReadBit(candidate, endBit, pathEntityTransformComputed)
1612 && surfaceDwgRead3BitDouble(candidate, endBit, referenceVector)
1613 && surfaceDwgRead3BitDouble(candidate, endBit, sweepVector)
1614 && readMatrix(extrudedTransform);
1615 if (valid) {
1616 info.extrudedFieldsValid = true;
1617 info.extrudedSweepVector = sweepVector;
1618 info.extrudedTransform = extrudedTransform;
1619 info.extrudedSweepEntityTransform = sweepEntityTransform;
1620 info.extrudedPathEntityTransform = pathEntityTransform;
1621 info.extrudedDraftAngle = draftAngle;
1622 info.extrudedDraftStartDistance = draftStartDistance;
1623 info.extrudedDraftEndDistance = draftEndDistance;
1624 info.extrudedTwistAngle = twistAngle;
1625 info.extrudedScaleFactor = scaleFactor;
1626 info.extrudedAlignAngle = alignAngle;
1627 info.extrudedSolid = solid != 0;
1628 info.extrudedSweepAlignmentFlags = sweepAlignmentFlags;
1629 info.extrudedPathFlags = pathFlags;
1630 info.extrudedAlignStart = alignStart != 0;
1631 info.extrudedBank = bank != 0;
1632 info.extrudedBasePointSet = basePointSet != 0;
1633 info.extrudedSweepEntityTransformComputed =
1634 sweepEntityTransformComputed != 0;
1635 info.extrudedPathEntityTransformComputed =
1636 pathEntityTransformComputed != 0;
1637 info.extrudedReferenceVector = referenceVector;
1638 }
1639 } else if (type == DRW::LOFTEDSURFACE) {
1640 dwgBuffer candidate = buffer;
1641 std::array<double, DRW_LoftedSurface::kTransformSize>
1642 loftEntityTransform{};
1643 std::int32_t planeNormalLoftingType = 0;
1644 double startDraftAngle = 0.0;
1645 double endDraftAngle = 0.0;
1646 double startDraftMagnitude = 0.0;
1647 double endDraftMagnitude = 0.0;
1648 std::uint8_t arcLengthParameterization = 0;
1649 std::uint8_t noTwist = 0;
1650 std::uint8_t alignDirection = 0;
1651 std::uint8_t simpleSurfaces = 0;
1652 std::uint8_t closedSurfaces = 0;
1653 std::uint8_t solid = 0;
1654 std::uint8_t ruledSurface = 0;
1655 std::uint8_t virtualGuide = 0;
1656 std::uint16_t numCrossSections = 0;
1657 std::uint16_t numGuideCurves = 0;
1658 auto readMatrix = [&](std::array<double,
1659 DRW_LoftedSurface::kTransformSize>& matrix) {
1660 for (double& value : matrix) {
1661 if (!surfaceDwgReadDouble(candidate, endBit, value))
1662 return false;
1663 }
1664 return true;
1665 };
1666 const bool valid = readMatrix(loftEntityTransform)
1667 && surfaceDwgReadBitLong(candidate, endBit,
1668 planeNormalLoftingType)
1669 && surfaceDwgReadDouble(candidate, endBit, startDraftAngle)
1670 && surfaceDwgReadDouble(candidate, endBit, endDraftAngle)
1671 && surfaceDwgReadDouble(candidate, endBit, startDraftMagnitude)
1672 && surfaceDwgReadDouble(candidate, endBit, endDraftMagnitude)
1673 && surfaceDwgReadBit(candidate, endBit, arcLengthParameterization)
1674 && surfaceDwgReadBit(candidate, endBit, noTwist)
1675 && surfaceDwgReadBit(candidate, endBit, alignDirection)
1676 && surfaceDwgReadBit(candidate, endBit, simpleSurfaces)
1677 && surfaceDwgReadBit(candidate, endBit, closedSurfaces)
1678 && surfaceDwgReadBit(candidate, endBit, solid)
1679 && surfaceDwgReadBit(candidate, endBit, ruledSurface)
1680 && surfaceDwgReadBit(candidate, endBit, virtualGuide)
1681 && surfaceDwgReadBitShort(candidate, endBit, numCrossSections)
1682 && surfaceDwgReadBitShort(candidate, endBit, numGuideCurves)
1683 && numCrossSections <= DRW_LoftedSurface::kMaxReferenceTokenCount
1684 && numGuideCurves <= DRW_LoftedSurface::kMaxReferenceTokenCount;
1685 if (valid) {
1686 info.loftedFieldsValid = true;
1687 info.loftedEntityTransform = loftEntityTransform;
1688 info.loftedPlaneNormalLoftingType = planeNormalLoftingType;
1689 info.loftedStartDraftAngle = startDraftAngle;
1690 info.loftedEndDraftAngle = endDraftAngle;
1691 info.loftedStartDraftMagnitude = startDraftMagnitude;
1692 info.loftedEndDraftMagnitude = endDraftMagnitude;
1693 info.loftedArcLengthParameterization = arcLengthParameterization != 0;
1694 info.loftedNoTwist = noTwist != 0;
1695 info.loftedAlignDirection = alignDirection != 0;
1696 info.loftedSimpleSurfaces = simpleSurfaces != 0;
1697 info.loftedClosedSurfaces = closedSurfaces != 0;
1698 info.loftedSolid = solid != 0;
1699 info.loftedRuledSurface = ruledSurface != 0;
1700 info.loftedVirtualGuide = virtualGuide != 0;
1701 info.loftedNumCrossSections = numCrossSections;
1702 info.loftedNumGuideCurves = numGuideCurves;
1703 }
1704 } else if (type == DRW::SWEPTSURFACE) {
1705 dwgBuffer candidate = buffer;
1706 std::int32_t classVersion = 0;
1707 std::int32_t sweepEntityId = 0;
1708 std::int32_t sweepDataSize = 0;
1709 std::int32_t pathEntityId = 0;
1710 std::int32_t pathDataSize = 0;
1711 std::vector<std::uint8_t> sweepData;
1712 std::vector<std::uint8_t> pathData;
1713 std::array<double, DRW_SweptSurface::kTransformSize>
1714 sweepEntityTransform{};
1715 std::array<double, DRW_SweptSurface::kTransformSize>
1716 pathEntityTransform{};
1717 double draftAngle = 0.0;
1718 double draftStartDistance = 0.0;
1719 double draftEndDistance = 0.0;
1720 double twistAngle = 0.0;
1721 double scaleFactor = 1.0;
1722 double alignAngle = 0.0;
1723 std::uint8_t solid = 0;
1724 std::uint16_t sweepAlignmentFlags = 0;
1725 std::uint16_t pathFlags = 0;
1726 std::uint8_t alignStart = 0;
1727 std::uint8_t bank = 0;
1728 std::uint8_t basePointSet = 0;
1729 std::uint8_t sweepEntityTransformComputed = 0;
1730 std::uint8_t pathEntityTransformComputed = 0;
1731 DRW_Coord referenceVector;
1732 auto readMatrix = [&](std::array<double,
1733 DRW_SweptSurface::kTransformSize>& matrix) {
1734 for (double& value : matrix) {
1735 if (!surfaceDwgReadDouble(candidate, endBit, value))
1736 return false;
1737 }
1738 return true;
1739 };
1740 bool valid = surfaceDwgReadBitLong(candidate, endBit, classVersion)
1741 && classVersion >= 0 && classVersion <= 10
1742 && surfaceDwgReadBitLong(candidate, endBit, sweepEntityId)
1743 && sweepEntityId >= 0
1744 && surfaceDwgReadBitLong(candidate, endBit, sweepDataSize)
1745 && surfaceDwgReadBytes(candidate, endBit, sweepDataSize,
1746 DRW_SweptSurface::kMaxSweepDataSize,
1747 sweepData)
1748 && surfaceDwgReadBitLong(candidate, endBit, pathEntityId)
1749 && pathEntityId >= 0
1750 && surfaceDwgReadBitLong(candidate, endBit, pathDataSize)
1751 && surfaceDwgReadBytes(candidate, endBit, pathDataSize,
1752 DRW_SweptSurface::kMaxSweepDataSize,
1753 pathData)
1754 && surfaceDwgReadDouble(candidate, endBit, draftAngle)
1755 && surfaceDwgReadDouble(candidate, endBit, draftStartDistance)
1756 && surfaceDwgReadDouble(candidate, endBit, draftEndDistance)
1757 && surfaceDwgReadDouble(candidate, endBit, twistAngle)
1758 && surfaceDwgReadDouble(candidate, endBit, scaleFactor)
1759 && surfaceDwgReadDouble(candidate, endBit, alignAngle)
1760 && readMatrix(sweepEntityTransform)
1761 && readMatrix(pathEntityTransform)
1762 && surfaceDwgReadBit(candidate, endBit, solid)
1763 && surfaceDwgReadBitShort(candidate, endBit, sweepAlignmentFlags)
1764 && surfaceDwgReadBitShort(candidate, endBit, pathFlags)
1765 && surfaceDwgReadBit(candidate, endBit, alignStart)
1766 && surfaceDwgReadBit(candidate, endBit, bank)
1767 && surfaceDwgReadBit(candidate, endBit, basePointSet)
1768 && surfaceDwgReadBit(candidate, endBit, sweepEntityTransformComputed)
1769 && surfaceDwgReadBit(candidate, endBit, pathEntityTransformComputed)
1770 && surfaceDwgRead3BitDouble(candidate, endBit, referenceVector);
1771 if (valid) {
1772 info.sweptFieldsValid = true;
1773 info.sweptClassVersion = static_cast<std::uint32_t>(classVersion);
1774 info.sweptEntityId = static_cast<std::uint32_t>(sweepEntityId);
1775 info.sweptData = std::move(sweepData);
1776 info.sweptPathEntityId = static_cast<std::uint32_t>(pathEntityId);
1777 info.sweptPathData = std::move(pathData);
1778 info.sweptEntityTransform = sweepEntityTransform;
1779 info.sweptPathEntityTransform = pathEntityTransform;
1780 info.sweptDraftAngle = draftAngle;
1781 info.sweptDraftStartDistance = draftStartDistance;
1782 info.sweptDraftEndDistance = draftEndDistance;
1783 info.sweptTwistAngle = twistAngle;
1784 info.sweptScaleFactor = scaleFactor;
1785 info.sweptAlignAngle = alignAngle;
1786 info.sweptSolid = solid != 0;
1787 info.sweptAlignmentFlags = sweepAlignmentFlags;
1788 info.sweptPathFlags = pathFlags;
1789 info.sweptAlignStart = alignStart != 0;
1790 info.sweptBank = bank != 0;
1791 info.sweptBasePointSet = basePointSet != 0;
1792 info.sweptEntityTransformComputed =
1793 sweepEntityTransformComputed != 0;
1794 info.sweptPathEntityTransformComputed =
1795 pathEntityTransformComputed != 0;
1796 info.sweptReferenceVector = referenceVector;
1797 }
1798 } else if (type == DRW::NURBSURFACE && version >= DRW::AC1027) {
1799 dwgBuffer candidate = buffer;
1800 std::uint16_t short170 = 0;
1801 std::uint8_t cvHullDisplay = 0;
1802 DRW_Coord uvec1;
1803 DRW_Coord vvec1;
1804 DRW_Coord uvec2;
1805 DRW_Coord vvec2;
1806 const bool valid = surfaceDwgReadBitShort(candidate, endBit, short170)
1807 && surfaceDwgReadBit(candidate, endBit, cvHullDisplay)
1808 && surfaceDwgRead3BitDouble(candidate, endBit, uvec1)
1809 && surfaceDwgRead3BitDouble(candidate, endBit, vvec1)
1810 && surfaceDwgRead3BitDouble(candidate, endBit, uvec2)
1811 && surfaceDwgRead3BitDouble(candidate, endBit, vvec2);
1812 if (valid) {
1813 info.nurbsFieldsValid = true;
1814 info.nurbsShort170 = short170;
1815 info.nurbsCvHullDisplay = cvHullDisplay != 0;
1816 info.nurbsUvec1 = uvec1;
1817 info.nurbsVvec1 = vvec1;
1818 info.nurbsUvec2 = uvec2;
1819 info.nurbsVvec2 = vvec2;
1820 }
1821 } else if (type == DRW::REVOLVEDSURFACE) {
1822 // The AcDbRevolvedSurface tail is optional from the carrier's point
1823 // of view: retain the complete bounded body when a producer omits or
1824 // truncates it, but publish the typed values only as one transaction.
1825 dwgBuffer candidate = buffer;
1826 std::int32_t classVersion = 0;
1827 std::int32_t id = 0;
1828 DRW_Coord axisPoint;
1829 DRW_Coord axisVector;
1830 std::array<double, DRW_RevolvedSurface::kTransformSize> transform{};
1831 double revolveAngle = 0.0;
1832 double startAngle = 0.0;
1833 double draftAngle = 0.0;
1834 double draftStartDistance = 0.0;
1835 double draftEndDistance = 0.0;
1836 double twistAngle = 0.0;
1837 std::uint8_t solid = 0;
1838 std::uint8_t closeToAxis = 0;
1839 bool valid = surfaceDwgReadBitLong(candidate, endBit, classVersion)
1840 && classVersion >= 0 && classVersion <= 10
1841 && surfaceDwgReadBitLong(candidate, endBit, id)
1842 && id >= 0
1843 && surfaceDwgRead3BitDouble(candidate, endBit, axisPoint)
1844 && surfaceDwgRead3BitDouble(candidate, endBit, axisVector)
1845 && surfaceDwgReadDouble(candidate, endBit, revolveAngle)
1846 && surfaceDwgReadDouble(candidate, endBit, startAngle);
1847 for (double& value : transform) {
1848 valid = valid && surfaceDwgReadDouble(candidate, endBit, value);
1849 }
1850 valid = valid
1851 && surfaceDwgReadDouble(candidate, endBit, draftAngle)
1852 && surfaceDwgReadDouble(candidate, endBit, draftStartDistance)
1853 && surfaceDwgReadDouble(candidate, endBit, draftEndDistance)
1854 && surfaceDwgReadDouble(candidate, endBit, twistAngle)
1855 && surfaceDwgReadBit(candidate, endBit, solid)
1856 && surfaceDwgReadBit(candidate, endBit, closeToAxis);
1857 if (valid) {
1858 info.revolvedFieldsValid = true;
1859 info.revolvedClassId = static_cast<std::uint32_t>(classVersion);
1860 info.revolvedId = static_cast<std::uint32_t>(id);
1861 info.revolvedAxisPoint = axisPoint;
1862 info.revolvedAxisVector = axisVector;
1863 info.revolvedAngle = revolveAngle;
1864 info.revolvedStartAngle = startAngle;
1865 info.revolvedTransform = transform;
1866 info.revolvedDraftAngle = draftAngle;
1867 info.revolvedDraftStartDistance = draftStartDistance;
1868 info.revolvedDraftEndDistance = draftEndDistance;
1869 info.revolvedTwistAngle = twistAngle;
1870 info.revolvedSolid = solid != 0;
1871 info.revolvedCloseToAxis = closeToAxis != 0;
1872 }
1873 }
1874
1875 info.acisEmpty = acisEmpty;
1876 info.acisVersion = acisVersion;
1877 info.modelerFormatVersion = static_cast<int>(modelerFormatVersion);
1878 info.uIsolines = static_cast<int>(uIsolines);
1879 info.vIsolines = static_cast<int>(vIsolines);
1880 return true;
1881}
1882
1883int proxyEntityDxfCode(std::uint8_t handleCode) {
1884 switch (handleCode) {
1885 case 3: return 340;
1886 case 4:
1887 case 6:
1888 case 8:
1889 case 10:
1890 case 12: return 350;
1891 case 5: return 360;
1892 default: return 330;
1893 }
1894}
1895
1896DRW_DwgSubrecordRange makeDwgSubrecordRange(const char *name, std::uint64_t startBit,
1897 std::uint64_t endBit, DRW::Version version,
1898 std::uint32_t count, bool parseComplete) {
1899 DRW_DwgSubrecordRange range;
1900 range.m_name = name;
1901 range.m_startBit = startBit;
1902 range.m_bitSize = endBit >= startBit ? endBit - startBit : 0;
1903 range.m_version = version;
1904 range.m_count = count;
1905 range.m_parseComplete = parseComplete;
1906 return range;
1907}
1908
1909bool isValidSplineDegree(int degree) {
1910 return degree >= 1 && degree <= kMaxSplineDegree;
1911}
1912
1913bool isValidControlSplineLayout(int degree, std::int32_t knotCount, std::int32_t controlCount) {
1914 if (!isValidSplineDegree(degree) || !isValidCount(knotCount, kMaxSplineItems) ||
1915 !isValidCount(controlCount, kMaxSplineItems)) {
1916 return false;
1917 }
1918
1919 if (controlCount < degree + 1) {
1920 return false;
1921 }
1922
1923 const std::int64_t expectedKnots = static_cast<std::int64_t>(controlCount) + degree + 1;
1924 return expectedKnots <= kMaxSplineItems && knotCount == expectedKnots;
1925}
1926
1927bool isValidFitSplineLayout(int degree, std::int32_t fitCount) {
1928 return isValidSplineDegree(degree) && isValidCount(fitCount, kMaxSplineItems) &&
1929 fitCount >= 2;
1930}
1931
1932bool differsFromUnitWeight(double weight) {
1933 return std::fabs(weight - 1.0) > 1e-12;
1934}
1935
1936//! \brief Compare two doubles by stored representation.
1937//! For values a compact DWG form restores verbatim, the match has to be exact:
1938//! dataFlags 3 makes the reader return exactly 1.0 and dataFlags 2 makes it
1939//! return xscale for y and z (see DRW_Insert::parseDwg), so picking those forms
1940//! on a tolerant match would silently round the scale that gets written. This
1941//! asks the question that is actually meant - "will the reader reconstruct this
1942//! value?" - without floating point comparison semantics, and as a side effect
1943//! keeps -0.0 distinct from 0.0, which `==` does not.
1944bool sameStoredDouble(double a, double b) {
1945 return std::memcmp(&a, &b, sizeof a) == 0;
1946}
1947
1948void putDwgReference(dwgBufferW *buf, std::uint8_t code,
1949 std::uint32_t ref) {
1950 dwgHandle h;
1951 h.code = code;
1952 h.ref = ref;
1953 h.size = 0;
1954 if (ref != 0) {
1955 std::uint32_t t = ref;
1956 while (t != 0) {
1957 t >>= 8;
1958 ++h.size;
1959 }
1960 }
1961 buf->putHandle(h);
1962}
1963
1964void putHardPointerHandle(dwgBufferW *buf, std::uint32_t ref) {
1965 putDwgReference(buf, DRW::DwgHardPointer, ref);
1966}
1967
1968void appendBitBuffer(dwgBufferW *destination, const dwgBufferW& source) {
1969 if (destination == nullptr)
1970 return;
1971 const auto& bytes = source.data();
1972 for (std::uint32_t bit = 0; bit < source.bitCount(); ++bit) {
1973 const std::uint8_t value =
1974 static_cast<std::uint8_t>((bytes[bit / 8] >> (7 - (bit % 8))) & 1);
1975 destination->putBit(value);
1976 }
1977}
1978
1979void putNullableHardPointerHandle(dwgBufferW *buf, std::uint32_t ref) {
1980 dwgHandle h;
1981 h.code = ref == 0 ? 0 : 5;
1982 h.ref = ref;
1983 h.size = 0;
1984 if (ref != 0) {
1985 std::uint32_t t = ref;
1986 while (t != 0) {
1987 t >>= 8;
1988 ++h.size;
1989 }
1990 }
1991 buf->putHandle(h);
1992}
1993
1994void putSurfaceHandle(dwgBufferW *buf, std::uint32_t ref) {
1995 dwgHandle h;
1996 h.code = 5;
1997 h.ref = ref;
1998 h.size = 0;
1999 for (std::uint32_t value = ref; value != 0; value >>= 8)
2000 ++h.size;
2001 buf->putHandle(h);
2002}
2003
2004std::size_t surfaceRawBitCapacity(const std::vector<std::uint8_t>& data) {
2005 constexpr std::size_t maxBitSize =
2006 std::numeric_limits<std::uint32_t>::max();
2007 constexpr std::size_t maxBytes = maxBitSize / 8u;
2008 return data.size() > maxBytes ? maxBitSize : data.size() * 8u;
2009}
2010
2011bool putSurfaceRawBits(dwgBufferW *buf, const std::vector<std::uint8_t>& data,
2012 std::uint32_t bitSize) {
2013 const std::size_t bitCapacity = surfaceRawBitCapacity(data);
2014 if (buf == nullptr || bitSize == 0 || bitSize > bitCapacity)
2015 return false;
2016 for (std::uint32_t bit = 0; bit < bitSize; ++bit) {
2017 const std::uint8_t byte = data[bit >> 3];
2018 buf->putBit((byte >> (7u - (bit & 7u))) & 1u);
2019 }
2020 return true;
2021}
2022
2023bool finiteSurfaceCoord(const DRW_Coord& value) {
2024 return std::isfinite(value.x) && std::isfinite(value.y)
2025 && std::isfinite(value.z);
2026}
2027
2028template <std::size_t N>
2029bool finiteSurfaceMatrix(const std::array<double, N>& values) {
2030 return std::all_of(values.begin(), values.end(),
2031 [](double value) { return std::isfinite(value); });
2032}
2033
2034std::uint16_t bitShortFromInt(int value) {
2035 if (value < 0)
2036 return 0;
2037 if (value > 0xffff)
2038 return 0xffff;
2039 return static_cast<std::uint16_t>(value);
2040}
2041
2042std::uint32_t readTableHandle(
2043 dwgBuffer *hdlBuf,
2044 std::uint64_t endBit = std::numeric_limits<std::uint64_t>::max()) {
2045 if (hdlBuf == nullptr || !hdlBuf->isGood())
2046 return 0;
2047 dwgBuffer probe = hdlBuf->forkIndependent();
2048 dwgHandle h;
2049 if (!readBoundedDwgHandle(probe, endBit, 0, false, h)) {
2050 hdlBuf->invalidate();
2051 return 0;
2052 }
2053 *hdlBuf = probe;
2054 return h.ref;
2055}
2056
2057void seekTableObjectHandleStream(DRW::Version version, dwgBuffer *buf, std::uint32_t objSize) {
2058 if (version > DRW::AC1018) {
2059 buf->setPosition(objSize >> 3);
2060 buf->setBitPos(objSize & 7);
2061 }
2062}
2063
2064bool readTableObjectCommonHandles(dwgBuffer *buf, std::uint32_t baseHandle,
2065 std::int32_t numReactors, std::uint8_t xDictFlag,
2066 std::uint32_t *parentHandle,
2067 std::vector<std::uint32_t> *reactorHandles,
2068 std::uint32_t *xDictHandle,
2069 std::uint64_t handleEndBit =
2070 std::numeric_limits<std::uint64_t>::max()) {
2071 if (buf == nullptr || !buf->isGood()
2072 || !dwgSafety::validReactorCount(numReactors)) {
2073 if (buf != nullptr)
2074 buf->invalidate();
2075 return false;
2076 }
2077 std::vector<std::uint32_t> parsedReactors;
2078 dwgHandle parentH;
2079 if (!readBoundedDwgHandle(*buf, handleEndBit, baseHandle, true, parentH))
2080 return false;
2081 if (!DRW::reserve(parsedReactors, numReactors))
2082 return false;
2083 for (std::int32_t i = 0; i < numReactors; ++i) {
2084 dwgHandle reactor;
2085 if (!readBoundedDwgHandle(*buf, handleEndBit, baseHandle, true,
2086 reactor))
2087 return false;
2088 parsedReactors.push_back(reactor.ref);
2089 }
2090
2091 std::uint32_t parsedXDictHandle = 0;
2092 if (xDictFlag != 1) {
2093 dwgHandle xDict;
2094 if (!readBoundedDwgHandle(*buf, handleEndBit, baseHandle, true,
2095 xDict))
2096 return false;
2097 parsedXDictHandle = xDict.ref;
2098 }
2099 if (parentHandle)
2100 *parentHandle = parentH.ref;
2101 if (reactorHandles)
2102 *reactorHandles = std::move(parsedReactors);
2103 if (xDictHandle)
2104 *xDictHandle = parsedXDictHandle;
2105 return true;
2106}
2107
2108bool readTableValueBytes(dwgBuffer *buf, std::uint64_t bodyEndBit,
2109 std::vector<std::uint8_t>& raw, const char *label) {
2110 std::int32_t parsedByteCount = 0;
2111 if (buf == nullptr
2112 || !readBoundedBitLong(*buf, bodyEndBit, parsedByteCount)
2113 || parsedByteCount < 0
2114 || static_cast<std::uint32_t>(parsedByteCount) > kMaxTableStringBytes) {
2115 if (buf != nullptr)
2116 buf->invalidate();
2117 return false;
2118 }
2119 const std::uint32_t byteCount = static_cast<std::uint32_t>(parsedByteCount);
2120 if (byteCount > kMaxTableStringBytes) {
2121 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");
2122 return false;
2123 }
2124 if (!DRW::resize(raw, static_cast<int>(byteCount)))
2125 return false;
2126 const bool good = readBoundedBytes(*buf, bodyEndBit, raw.data(), raw.size());
2127 if (!good) {
2128 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);
2129 DRW_DBG(" remaining: ")DRW_dbg::dbg(" remaining: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
2130 }
2131 return good;
2132}
2133
2134UTF8STRINGstd::string decodeTableValueText(DRW::Version version, dwgBuffer *buf, const std::vector<std::uint8_t>& raw) {
2135 if (raw.empty())
2136 return UTF8STRINGstd::string();
2137 std::string s(reinterpret_cast<const char*>(raw.data()), raw.size());
2138 if (version > DRW::AC1018 && s.size() >= 2 && s[s.size() - 1] == '\0'
2139 && s[s.size() - 2] == '\0') {
2140 s.resize(s.size() - 2);
2141 } else {
2142 while (!s.empty() && s.back() == '\0')
2143 s.pop_back();
2144 }
2145 if (buf->decoder)
2146 s = buf->decoder->toUtf8(s);
2147 return s;
2148}
2149
2150UTF8STRINGstd::string readTableText(DRW::Version version, dwgBuffer *buf,
2151 std::uint64_t endBit) {
2152 if (!buf)
2153 return UTF8STRINGstd::string();
2154 if (version <= DRW::AC1018) {
2155 UTF8STRINGstd::string value;
2156 if (!readBoundedVariableText(*buf, endBit, version, value))
2157 buf->invalidate();
2158 return value;
2159 }
2160
2161 // R2007+ text is a BS count of UTF-16LE code units followed by two bytes
2162 // per unit (ODA spec TV/TU; the encoding dwgBuffer::getUCSText reads). The
2163 // count is not a byte count: taking `count` bytes leaves the second half of
2164 // every string unread and puts every later read of the string stream out
2165 // of step, which turns the next length into garbage.
2166 std::uint16_t parsedCharCount = 0;
2167 if (!readBoundedBitShort(*buf, endBit, parsedCharCount)) {
2168 buf->invalidate();
2169 return UTF8STRINGstd::string();
2170 }
2171 if (parsedCharCount == 0)
2172 return UTF8STRINGstd::string();
2173 // At most 2 * 65535 bytes, far below kMaxTableStringBytes.
2174 const std::uint32_t byteLen = static_cast<std::uint32_t>(parsedCharCount) * 2u;
2175
2176 std::vector<std::uint8_t> raw;
2177 if (!DRW::resize(raw, static_cast<int>(byteLen + 2)))
2178 return UTF8STRINGstd::string();
2179 std::fill(raw.begin(), raw.end(), 0);
2180 if (!readBoundedBytes(*buf, endBit, raw.data(), byteLen)) {
2181 buf->invalidate();
2182 return UTF8STRINGstd::string();
2183 }
2184
2185 std::string s(reinterpret_cast<const char*>(raw.data()), byteLen);
2186 if (buf->decoder)
2187 s = buf->decoder->toUtf8(s);
2188 while (!s.empty() && s.back() == '\0')
2189 s.pop_back();
2190 return s;
2191}
2192
2193bool readTableValuePoint(dwgBuffer *buf, std::uint64_t bodyEndBit,
2194 DRW_CadValue& value, int dimensions) {
2195 std::int32_t parsedDataSize = 0;
2196 if (buf == nullptr
2197 || !readBoundedBitLong(*buf, bodyEndBit, parsedDataSize)
2198 || parsedDataSize < 0) {
2199 if (buf != nullptr)
2200 buf->invalidate();
2201 return false;
2202 }
2203 value.m_dataSize = static_cast<std::uint32_t>(parsedDataSize);
2204 const std::uint32_t expectedSize = static_cast<std::uint32_t>(dimensions) * 8;
2205 if (value.m_dataSize > kMaxTableStringBytes)
2206 return false;
2207 if (value.m_dataSize < expectedSize) {
2208 if (!DRW::resize(value.m_rawData,
2209 static_cast<int>(value.m_dataSize)))
2210 return false;
2211 if (!readBoundedBytes(*buf, bodyEndBit, value.m_rawData.data(),
2212 value.m_rawData.size()))
2213 return false;
2214 value.m_value.addBinary(310, value.m_rawData);
2215 return true;
2216 }
2217
2218 DRW_Coord c;
2219 if (!readBoundedRawDouble(*buf, bodyEndBit, c.x)
2220 || !readBoundedRawDouble(*buf, bodyEndBit, c.y)
2221 || (dimensions == 3
2222 && !readBoundedRawDouble(*buf, bodyEndBit, c.z)))
2223 return false;
2224 if (dimensions != 3)
2225 c.z = 0.0;
2226 value.m_value.addCoord(11, c);
2227
2228 const std::uint32_t extraBytes = value.m_dataSize - expectedSize;
2229 if (!DRW::resize(value.m_rawData, static_cast<int>(extraBytes)))
2230 return false;
2231 return readBoundedBytes(*buf, bodyEndBit, value.m_rawData.data(),
2232 value.m_rawData.size());
2233}
2234
2235bool readTableCadValue(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf,
2236 dwgBuffer *hdlBuf, DRW_CadValue& value,
2237 const TableDwgBounds& bounds) {
2238 if (version > DRW::AC1018) {
2239 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, value.m_formatFlags))
2240 return false;
2241 }
2242
2243 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, value.m_dataType))
2244 return false;
2245 // Only bit 0 of the value flags marks the payload absent for ACAD_TABLE
2246 // cell values (ACadSharp CadValue.IsEmpty). Bit 1 is set on values that do
2247 // carry a payload: R2007 cells hold flags 6 (title/data text) and 2
2248 // (merged text), both with a kGeneral payload, and only flags 3 (empty
2249 // cell) has none. Testing `& 3` skips those payloads and desynchronises
2250 // every later cell. The FIELD object reads the same structure but tests
2251 // `& 3` (libredwg dwg_spec_shared.h); do not unify the two masks.
2252 const bool emptyR2007Value = version > DRW::AC1018 && (value.m_formatFlags & 1);
2253 if (!emptyR2007Value) {
2254 switch (value.m_dataType) {
2255 case 0:
2256 case 1: {
2257 std::int32_t intValue = 0;
2258 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, intValue))
2259 return false;
2260 value.m_value.addInt(91, intValue);
2261 break;
2262 }
2263 case 2: {
2264 double doubleValue = 0.0;
2265 if (!readBoundedBitDouble(*buf, bounds.bodyEndBit, doubleValue))
2266 return false;
2267 value.m_value.addDouble(140, doubleValue);
2268 break;
2269 }
2270 case 4:
2271 case 512:
2272 if (!readTableValueBytes(buf, bounds.bodyEndBit, value.m_rawData,
2273 "TABLE value byte payload"))
2274 return false;
2275 value.m_dataSize = static_cast<std::uint32_t>(value.m_rawData.size());
2276 value.m_value.addString(1, decodeTableValueText(version, buf, value.m_rawData));
2277 break;
2278 case 8: {
2279 if (!readTableValueBytes(buf, bounds.bodyEndBit, value.m_rawData,
2280 "TABLE value date payload"))
2281 return false;
2282 value.m_dataSize = static_cast<std::uint32_t>(value.m_rawData.size());
2283 value.m_value.addBinary(310, value.m_rawData);
2284 break;
2285 }
2286 case 16:
2287 if (!readTableValuePoint(buf, bounds.bodyEndBit, value, 2))
2288 return false;
2289 break;
2290 case 32:
2291 if (!readTableValuePoint(buf, bounds.bodyEndBit, value, 3))
2292 return false;
2293 break;
2294 case 64:
2295 value.m_handle = readTableHandle(hdlBuf, bounds.handleEndBit);
2296 value.m_value.addInt(330, static_cast<std::uint32_t>(value.m_handle));
2297 break;
2298 case 128:
2299 case 256:
2300 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");
2301 return false;
2302 default:
2303 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");
2304 return false;
2305 }
2306 }
2307
2308 if (version > DRW::AC1018) {
2309 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2310 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, value.m_unitType))
2311 return false;
2312 const std::uint64_t textEndBit = tableTextEndBit(buf, textBuf, bounds);
2313 value.m_formatString = readTableText(version, textBuf, textEndBit);
2314 value.m_valueString = readTableText(version, textBuf, textEndBit);
2315 }
2316
2317 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2318 if (!good) {
2319 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);
2320 DRW_DBG(" type: ")DRW_dbg::dbg(" type: "); DRW_DBG(value.m_dataType)DRW_dbg::dbg(value.m_dataType);
2321 DRW_DBG(" unit: ")DRW_dbg::dbg(" unit: "); DRW_DBG(value.m_unitType)DRW_dbg::dbg(value.m_unitType);
2322 DRW_DBG(" bufGood: ")DRW_dbg::dbg(" bufGood: "); DRW_DBG(buf->isGood() ? 1 : 0)DRW_dbg::dbg(buf->isGood() ? 1 : 0);
2323 DRW_DBG(" strGood: ")DRW_dbg::dbg(" strGood: "); DRW_DBG((!strBuf || strBuf->isGood()) ? 1 : 0)DRW_dbg::dbg((!strBuf || strBuf->isGood()) ? 1 : 0);
2324 DRW_DBG(" hdlGood: ")DRW_dbg::dbg(" hdlGood: "); DRW_DBG((!hdlBuf || hdlBuf->isGood()) ? 1 : 0)DRW_dbg::dbg((!hdlBuf || hdlBuf->isGood()) ? 1 : 0);
2325 DRW_DBG(" bufPos: ")DRW_dbg::dbg(" bufPos: "); DRW_DBG(buf->getPosition())DRW_dbg::dbg(buf->getPosition());
2326 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);
2327 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);
2328 DRW_DBG("\n")DRW_dbg::dbg("\n");
2329 }
2330 return good;
2331}
2332
2333bool skipTableCustomData(DRW::Version version, dwgBuffer *buf,
2334 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
2335 const TableDwgBounds& bounds) {
2336 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2337 UTF8STRINGstd::string key = readTableText(
2338 version, textBuf, tableTextEndBit(buf, textBuf, bounds));
2339 if (!textBuf->isGood()) {
2340 DRW_DBG("TABLE custom data key string read failed\n")DRW_dbg::dbg("TABLE custom data key string read failed\n");
2341 return false;
2342 }
2343 DRW_CadValue value;
2344 const bool good = readTableCadValue(version, buf, strBuf, hdlBuf, value,
2345 bounds);
2346 if (!good) {
2347 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");
2348 }
2349 return good;
2350}
2351
2352bool readTableCmColor(DRW::Version version, dwgBuffer *buf,
2353 dwgBuffer *strBuf, const TableDwgBounds& bounds) {
2354 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2355 if (version < DRW::AC1018) {
2356 std::uint16_t ignoredColor = 0;
2357 return readBoundedBitShort(*buf, bounds.bodyEndBit, ignoredColor);
2358 }
2359
2360 std::uint16_t ignoredColorType = 0;
2361 std::int32_t rgb = 0;
2362 std::uint8_t colorFlags = 0;
2363 if (!readBoundedBitShort(*buf, bounds.bodyEndBit, ignoredColorType)
2364 || !readBoundedBitLong(*buf, bounds.bodyEndBit, rgb)
2365 || !readBoundedRawChar8(*buf, bounds.bodyEndBit, colorFlags))
2366 return false;
2367 DRW_DBG("\ntype COLOR: ")DRW_dbg::dbg("\ntype COLOR: "); DRW_DBGH(rgb >> 24)DRW_dbg::dbgH(rgb >> 24);
2368 DRW_DBG("\nRGB COLOR: ")DRW_dbg::dbg("\nRGB COLOR: "); DRW_DBGH(rgb)DRW_dbg::dbgH(rgb);
2369 DRW_DBG("\nbyte COLOR: ")DRW_dbg::dbg("\nbyte COLOR: "); DRW_DBGH(colorFlags)DRW_dbg::dbgH(colorFlags);
2370 const std::uint64_t textEndBit = tableTextEndBit(buf, textBuf, bounds);
2371 if (colorFlags & 1)
2372 readTableText(version, textBuf, textEndBit);
2373 if (colorFlags & 2)
2374 readTableText(version, textBuf, textEndBit);
2375 return textBuf->isGood();
2376}
2377
2378bool skipR2007TableCellOverrides(DRW::Version version, dwgBuffer *buf,
2379 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
2380 DRW_TableCell& cell,
2381 std::vector<DRW_DwgSubrecordRange> *ranges,
2382 const TableDwgBounds& bounds) {
2383 const std::uint64_t startBit = currentDwgBit(buf);
2384 std::int32_t overrideFlags = 0;
2385 std::uint8_t virtualEdgeFlags = 0;
2386 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, overrideFlags)
2387 || !readBoundedRawChar8(*buf, bounds.bodyEndBit, virtualEdgeFlags))
2388 return false;
2389 cell.m_overrideFlags = static_cast<std::uint32_t>(overrideFlags);
2390 cell.m_virtualEdgeFlags = virtualEdgeFlags;
2391
2392 // The ODA specification types the cell alignment override as RS, but the
2393 // value is stored as a BS (ACadSharp reads it that way). Read as a raw
2394 // 16-bit value it swallows bits that belong to the next field: the first
2395 // cell that overrides its alignment then decodes as garbage, and so does
2396 // every cell after it.
2397 if (cell.m_overrideFlags & 0x00001
2398 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2399 return false;
2400 if (cell.m_overrideFlags & 0x00002
2401 && !skipTableBit(*buf, bounds.bodyEndBit))
2402 return false;
2403 if ((cell.m_overrideFlags & 0x00004)
2404 && !readTableCmColor(version, buf, strBuf, bounds))
2405 return false;
2406 if ((cell.m_overrideFlags & 0x00008)
2407 && !readTableCmColor(version, buf, strBuf, bounds))
2408 return false;
2409 if (cell.m_overrideFlags & 0x00010)
2410 cell.m_textStyleOverrideHandle = readTableHandle(hdlBuf, bounds.handleEndBit);
2411 if (cell.m_overrideFlags & 0x00020
2412 && !skipTableBitDouble(*buf, bounds.bodyEndBit))
2413 return false;
2414 if ((cell.m_overrideFlags & 0x00040)
2415 && !readTableCmColor(version, buf, strBuf, bounds))
2416 return false;
2417 if (cell.m_overrideFlags & 0x00400
2418 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2419 return false;
2420 if (cell.m_overrideFlags & 0x04000
2421 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2422 return false;
2423 if ((cell.m_overrideFlags & 0x00080)
2424 && !readTableCmColor(version, buf, strBuf, bounds))
2425 return false;
2426 if (cell.m_overrideFlags & 0x00800
2427 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2428 return false;
2429 if (cell.m_overrideFlags & 0x08000
2430 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2431 return false;
2432 if ((cell.m_overrideFlags & 0x00100)
2433 && !readTableCmColor(version, buf, strBuf, bounds))
2434 return false;
2435 if (cell.m_overrideFlags & 0x01000
2436 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2437 return false;
2438 if (cell.m_overrideFlags & 0x10000
2439 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2440 return false;
2441 if ((cell.m_overrideFlags & 0x00200)
2442 && !readTableCmColor(version, buf, strBuf, bounds))
2443 return false;
2444 if (cell.m_overrideFlags & 0x02000
2445 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2446 return false;
2447 if (cell.m_overrideFlags & 0x20000
2448 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2449 return false;
2450
2451 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2452 if (ranges != nullptr) {
2453 ranges->push_back(makeDwgSubrecordRange(
2454 "r2007-table-cell-overrides", startBit, currentDwgBit(buf),
2455 version, cell.m_overrideFlags, good));
2456 }
2457 return good;
2458}
2459
2460bool parseR2007TableCell(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf,
2461 dwgBuffer *hdlBuf, DRW_TableCell& cell,
2462 std::vector<DRW_DwgSubrecordRange> *ranges,
2463 const TableDwgBounds& bounds) {
2464 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2465 std::uint16_t type = 0;
2466 std::uint8_t edgeFlags = 0;
2467 if (!readBoundedBitShort(*buf, bounds.bodyEndBit, type)
2468 || !readBoundedRawChar8(*buf, bounds.bodyEndBit, edgeFlags)
2469 || !readBoundedBit(*buf, bounds.bodyEndBit, cell.m_isMerged)
2470 || !readBoundedBit(*buf, bounds.bodyEndBit, cell.m_autoFit)
2471 || !readBoundedBitLong(*buf, bounds.bodyEndBit, cell.m_mergedWidth)
2472 || !readBoundedBitLong(*buf, bounds.bodyEndBit, cell.m_mergedHeight)
2473 || !readBoundedBitDouble(*buf, bounds.bodyEndBit, cell.m_rotation))
2474 return false;
2475 cell.m_type = type;
2476 cell.m_edgeFlags = edgeFlags;
2477 cell.m_valueHandle = readTableHandle(hdlBuf, bounds.handleEndBit);
2478
2479 if (cell.m_type == 1) {
2480 cell.m_textStyleHandle = cell.m_valueHandle;
2481 if (cell.m_textStyleHandle == 0 && version < DRW::AC1021) {
2482 DRW_TableCellContent content;
2483 content.m_type = 1;
2484 content.m_text = readTableText(
2485 version, textBuf, tableTextEndBit(buf, textBuf, bounds));
2486 content.m_value.m_dataType = 4;
2487 content.m_value.m_value.addString(1, content.m_text);
2488 cell.m_contents.push_back(content);
2489 }
2490 } else if (cell.m_type == 2) {
2491 cell.m_blockHandle = cell.m_valueHandle;
2492 if (!readBoundedBitDouble(*buf, bounds.bodyEndBit, cell.m_blockScale))
2493 return false;
2494 bool hasAttributes = false;
2495 if (!readBoundedBit(*buf, bounds.bodyEndBit, hasAttributes))
2496 return false;
2497 if (hasAttributes) {
2498 std::uint16_t numAttributes = 0;
2499 if (!readBoundedBitShort(*buf, bounds.bodyEndBit,
2500 numAttributes)
2501 || numAttributes > kMaxTableCellAttributes)
2502 return false;
2503 if (!DRW::reserve(cell.m_attributes, numAttributes))
2504 return false;
2505 for (std::uint16_t i = 0; i < numAttributes; ++i) {
2506 DRW_TableCellAttribute attribute;
2507 attribute.m_attdefHandle = readTableHandle(
2508 hdlBuf, bounds.handleEndBit);
2509 std::uint16_t attributeIndex = 0;
2510 if (!readBoundedBitShort(*buf, bounds.bodyEndBit,
2511 attributeIndex))
2512 return false;
2513 attribute.m_index = attributeIndex;
2514 attribute.m_text = readTableText(
2515 version, textBuf, tableTextEndBit(buf, textBuf, bounds));
2516 cell.m_attributes.push_back(attribute);
2517 }
2518 }
2519
2520 DRW_TableCellContent content;
2521 content.m_type = 4;
2522 content.m_handle = cell.m_blockHandle;
2523 cell.m_contents.push_back(content);
2524 }
2525
2526 bool hasOverrides = false;
2527 if (!readBoundedBit(*buf, bounds.bodyEndBit, hasOverrides))
2528 return false;
2529 if (hasOverrides
2530 && !skipR2007TableCellOverrides(version, buf, strBuf, hdlBuf, cell,
2531 ranges, bounds))
2532 return false;
2533
2534 if (version > DRW::AC1018) {
2535 if (!skipTableBitLong(*buf, bounds.bodyEndBit))
2536 return false;
2537 DRW_TableCellContent content;
2538 content.m_type = 1;
2539 if (!readTableCadValue(version, buf, strBuf, hdlBuf, content.m_value,
2540 bounds))
2541 return false;
2542 if (content.m_value.m_value.type() == DRW_Variant::STRING)
2543 content.m_text = content.m_value.m_value.c_str();
2544 else if (!content.m_value.m_valueString.empty())
2545 content.m_text = content.m_value.m_valueString;
2546 cell.m_contents.push_back(content);
2547 }
2548
2549 return buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2550}
2551
2552bool skipR2007TableOverrides(DRW::Version version, dwgBuffer *buf,
2553 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
2554 std::vector<DRW_DwgSubrecordRange> *ranges,
2555 const TableDwgBounds& bounds) {
2556 const std::uint64_t startBit = currentDwgBit(buf);
2557 std::uint32_t maskCount = 0;
2558 auto readFlags = [&](std::uint32_t& flags) {
2559 std::int32_t parsed = 0;
2560 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, parsed))
2561 return false;
2562 flags = static_cast<std::uint32_t>(parsed);
2563 return true;
2564 };
2565 bool hasMask = false;
2566 if (!readBoundedBit(*buf, bounds.bodyEndBit, hasMask))
2567 return false;
2568 if (hasMask) {
2569 std::uint32_t flags = 0;
2570 if (!readFlags(flags))
2571 return false;
2572 ++maskCount;
2573 if (flags & 0x000001)
2574 if (!skipTableBit(*buf, bounds.bodyEndBit))
2575 return false;
2576 if (flags & 0x000004)
2577 if (!skipTableBitShort(*buf, bounds.bodyEndBit))
2578 return false;
2579 if (flags & 0x000008)
2580 if (!skipTableBitDouble(*buf, bounds.bodyEndBit))
2581 return false;
2582 if (flags & 0x000010)
2583 if (!skipTableBitDouble(*buf, bounds.bodyEndBit))
2584 return false;
2585 if ((flags & 0x000020)
2586 && !readTableCmColor(version, buf, strBuf, bounds))
2587 return false;
2588 if ((flags & 0x000040)
2589 && !readTableCmColor(version, buf, strBuf, bounds))
2590 return false;
2591 if ((flags & 0x000080)
2592 && !readTableCmColor(version, buf, strBuf, bounds))
2593 return false;
2594 if (flags & 0x000100)
2595 if (!skipTableBit(*buf, bounds.bodyEndBit))
2596 return false;
2597 if (flags & 0x000200)
2598 if (!skipTableBit(*buf, bounds.bodyEndBit))
2599 return false;
2600 if (flags & 0x000400)
2601 if (!skipTableBit(*buf, bounds.bodyEndBit))
2602 return false;
2603 if ((flags & 0x000800)
2604 && !readTableCmColor(version, buf, strBuf, bounds))
2605 return false;
2606 if ((flags & 0x001000)
2607 && !readTableCmColor(version, buf, strBuf, bounds))
2608 return false;
2609 if ((flags & 0x002000)
2610 && !readTableCmColor(version, buf, strBuf, bounds))
2611 return false;
2612 if (flags & 0x004000)
2613 if (!skipTableBitShort(*buf, bounds.bodyEndBit))
2614 return false;
2615 if (flags & 0x008000)
2616 if (!skipTableBitShort(*buf, bounds.bodyEndBit))
2617 return false;
2618 if (flags & 0x010000)
2619 if (!skipTableBitShort(*buf, bounds.bodyEndBit))
2620 return false;
2621 if (flags & 0x020000)
2622 if (readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2623 && hdlBuf != nullptr && !hdlBuf->isGood())
2624 return false;
2625 if (flags & 0x040000)
2626 if (readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2627 && hdlBuf != nullptr && !hdlBuf->isGood())
2628 return false;
2629 if (flags & 0x080000)
2630 if (readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2631 && hdlBuf != nullptr && !hdlBuf->isGood())
2632 return false;
2633 if (flags & 0x100000)
2634 if (!skipTableBitDouble(*buf, bounds.bodyEndBit))
2635 return false;
2636 if (flags & 0x200000)
2637 if (!skipTableBitDouble(*buf, bounds.bodyEndBit))
2638 return false;
2639 if (flags & 0x400000)
2640 if (!skipTableBitDouble(*buf, bounds.bodyEndBit))
2641 return false;
2642 }
2643
2644 if (!readBoundedBit(*buf, bounds.bodyEndBit, hasMask))
2645 return false;
2646 if (hasMask) {
2647 std::uint32_t flags = 0;
2648 if (!readFlags(flags))
2649 return false;
2650 ++maskCount;
2651 for (int i = 0; i < 18; ++i) {
2652 if (flags & (1u << i)
2653 && !readTableCmColor(version, buf, strBuf, bounds))
2654 return false;
2655 }
2656 }
2657
2658 if (!readBoundedBit(*buf, bounds.bodyEndBit, hasMask))
2659 return false;
2660 if (hasMask) {
2661 std::uint32_t flags = 0;
2662 if (!readFlags(flags))
2663 return false;
2664 ++maskCount;
2665 for (int i = 0; i < 18; ++i) {
2666 if (flags & (1u << i))
2667 if (!skipTableBitShort(*buf, bounds.bodyEndBit))
2668 return false;
2669 }
2670 }
2671
2672 if (!readBoundedBit(*buf, bounds.bodyEndBit, hasMask))
2673 return false;
2674 if (hasMask) {
2675 std::uint32_t flags = 0;
2676 if (!readFlags(flags))
2677 return false;
2678 ++maskCount;
2679 for (int i = 0; i < 18; ++i) {
2680 if (flags & (1u << i))
2681 if (!skipTableBitShort(*buf, bounds.bodyEndBit))
2682 return false;
2683 }
2684 }
2685
2686 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2687 if (ranges != nullptr && (maskCount != 0 || currentDwgBit(buf) != startBit)) {
2688 ranges->push_back(makeDwgSubrecordRange(
2689 "r2007-table-overrides", startBit, currentDwgBit(buf),
2690 version, maskCount, good));
2691 }
2692 return good;
2693}
2694
2695bool skipTableContentFormat(DRW::Version version, dwgBuffer *buf,
2696 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
2697 std::vector<DRW_DwgSubrecordRange> *ranges,
2698 const TableDwgBounds& bounds) {
2699 const std::uint64_t startBit = currentDwgBit(buf);
2700 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2701 if (!skipTableBitLong(*buf, bounds.bodyEndBit) // property override flags
2702 || !skipTableBitLong(*buf, bounds.bodyEndBit) // property flags
2703 || !skipTableBitLong(*buf, bounds.bodyEndBit) // value data type
2704 || !skipTableBitLong(*buf, bounds.bodyEndBit)) { // value unit type
2705 return false;
2706 }
2707 readTableText(version, textBuf, tableTextEndBit(buf, textBuf, bounds));
2708 if (!textBuf->isGood())
2709 return false;
2710 if (!skipTableBitDouble(*buf, bounds.bodyEndBit) // rotation
2711 || !skipTableBitDouble(*buf, bounds.bodyEndBit) // block scale
2712 || !skipTableBitLong(*buf, bounds.bodyEndBit)) // alignment
2713 return false;
2714 std::int32_t rgb = -1;
2715 UTF8STRINGstd::string name;
2716 UTF8STRINGstd::string book;
2717 std::uint32_t color = 0;
2718 if (!readBoundedCmColor(*buf, strBuf, bounds.bodyEndBit, version,
2719 color, &rgb, nullptr, &name, &book,
2720 bounds.stringEndBit)
2721 || (hdlBuf != nullptr
2722 && readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2723 && !hdlBuf->isGood())
2724 || !skipTableBitDouble(*buf, bounds.bodyEndBit)) // text height
2725 return false;
2726 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2727 if (ranges != nullptr) {
2728 ranges->push_back(makeDwgSubrecordRange(
2729 "table-content-format", startBit, currentDwgBit(buf),
2730 version, 1, good));
2731 }
2732 return good;
2733}
2734
2735bool skipTableCellStyle(DRW::Version version, dwgBuffer *buf,
2736 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
2737 std::vector<DRW_DwgSubrecordRange> *ranges,
2738 const TableDwgBounds& bounds) {
2739 const std::uint64_t startBit = currentDwgBit(buf);
2740 if (!skipTableBitLong(*buf, bounds.bodyEndBit)) // style type
2741 return false;
2742 std::uint16_t dataFlags = 0;
2743 if (!readBoundedBitShort(*buf, bounds.bodyEndBit, dataFlags))
2744 return false;
2745 const bool hasData = dataFlags != 0;
2746 if (!hasData) {
2747 if (ranges != nullptr) {
2748 ranges->push_back(makeDwgSubrecordRange(
2749 "table-cell-style", startBit, currentDwgBit(buf),
2750 version, 0, buf->isGood()));
2751 }
2752 return buf->isGood();
2753 }
2754
2755 if (!skipTableBitLong(*buf, bounds.bodyEndBit) // property override flags
2756 || !skipTableBitLong(*buf, bounds.bodyEndBit)) // merge flags
2757 return false;
2758 std::int32_t rgb = -1;
2759 UTF8STRINGstd::string name;
2760 UTF8STRINGstd::string book;
2761 std::uint32_t color = 0;
2762 if (!readBoundedCmColor(*buf, strBuf, bounds.bodyEndBit, version,
2763 color, &rgb, nullptr, &name, &book,
2764 bounds.stringEndBit)
2765 || !skipTableBitLong(*buf, bounds.bodyEndBit)) // content layout
2766 return false;
2767 if (!skipTableContentFormat(version, buf, strBuf, hdlBuf, ranges,
2768 bounds))
2769 return false;
2770
2771 std::uint16_t marginFlags = 0;
2772 if (!readBoundedBitShort(*buf, bounds.bodyEndBit, marginFlags))
2773 return false;
2774 if (marginFlags != 0) {
2775 for (int i = 0; i < 6; ++i)
2776 if (!skipTableBitDouble(*buf, bounds.bodyEndBit))
2777 return false;
2778 }
2779
2780 std::uint32_t borders = 0;
2781 if (!readTableBodyCount(version, buf, bounds.bodyEndBit, 6, 2,
2782 borders)) {
2783 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");
2784 return false;
2785 }
2786 for (std::uint32_t i = 0; i < borders; ++i) {
2787 std::int32_t edgeFlags = 0;
2788 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, edgeFlags))
2789 return false;
2790 if (edgeFlags == 0)
2791 continue;
2792 if (!skipTableBitLong(*buf, bounds.bodyEndBit) // border overrides
2793 || !skipTableBitLong(*buf, bounds.bodyEndBit)) // border type
2794 return false;
2795 if (!readBoundedCmColor(*buf, strBuf, bounds.bodyEndBit, version,
2796 color, &rgb, nullptr, &name, &book,
2797 bounds.stringEndBit)
2798 || (hdlBuf != nullptr
2799 && readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2800 && !hdlBuf->isGood())
2801 || !skipTableBitLong(*buf, bounds.bodyEndBit) // visible/invisible
2802 || !skipTableBitDouble(*buf, bounds.bodyEndBit)) // double line spacing
2803 return false;
2804 }
2805
2806 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2807 if (ranges != nullptr) {
2808 ranges->push_back(makeDwgSubrecordRange(
2809 "table-cell-style", startBit, currentDwgBit(buf),
2810 version, borders, good));
2811 }
2812 return good;
2813}
2814
2815bool parseTableCell(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf,
2816 dwgBuffer *hdlBuf, DRW_TableCell& cell,
2817 std::vector<DRW_DwgSubrecordRange> *ranges,
2818 const TableDwgBounds& bounds) {
2819 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2820 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, cell.m_flags))
2821 return false;
2822 cell.m_toolTip = readTableText(
2823 version, textBuf, tableTextEndBit(buf, textBuf, bounds));
2824 if (!textBuf->isGood()) {
2825 DRW_DBG("TABLE cell tooltip string read failed\n")DRW_dbg::dbg("TABLE cell tooltip string read failed\n");
2826 return false;
2827 }
2828 if (!skipTableBitLong(*buf, bounds.bodyEndBit)) // custom data
2829 return false;
2830
2831 std::uint32_t customItems = 0;
2832 if (!readTableBodyCount(version, buf, bounds.bodyEndBit, kMaxTableItems, 2,
2833 customItems)) {
2834 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");
2835 return false;
2836 }
2837 for (std::uint32_t i = 0; i < customItems; ++i) {
2838 if (!skipTableCustomData(version, buf, strBuf, hdlBuf, bounds)) {
2839 DRW_DBG("TABLE cell custom data parse incomplete\n")DRW_dbg::dbg("TABLE cell custom data parse incomplete\n");
2840 return false;
2841 }
2842 }
2843
2844 std::int32_t hasLinkedData = 0;
2845 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, hasLinkedData))
2846 return false;
2847 if (hasLinkedData != 0) {
2848 if (hdlBuf != nullptr
2849 && readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2850 && !hdlBuf->isGood())
2851 return false;
2852 if (!skipTableBitLong(*buf, bounds.bodyEndBit)
2853 || !skipTableBitLong(*buf, bounds.bodyEndBit)
2854 || !skipTableBitLong(*buf, bounds.bodyEndBit))
2855 return false;
2856 }
2857
2858 std::uint32_t contentCount = 0;
2859 if (!readTableBodyCount(version, buf, bounds.bodyEndBit, kMaxTableItems, 2,
2860 contentCount)) {
2861 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");
2862 return false;
2863 }
2864 if (!DRW::reserve(cell.m_contents, static_cast<int>(contentCount)))
2865 return false;
2866 for (std::uint32_t i = 0; i < contentCount; ++i) {
2867 DRW_TableCellContent content;
2868 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, content.m_type))
2869 return false;
2870 if (content.m_type == 1) {
2871 if (!readTableCadValue(version, buf, strBuf, hdlBuf,
2872 content.m_value, bounds)) {
2873 DRW_DBG("TABLE cell value parse incomplete\n")DRW_dbg::dbg("TABLE cell value parse incomplete\n");
2874 return false;
2875 }
2876 if (content.m_value.m_value.type() == DRW_Variant::STRING)
2877 content.m_text = content.m_value.m_value.c_str();
2878 else if (!content.m_value.m_valueString.empty())
2879 content.m_text = content.m_value.m_valueString;
2880 } else if (content.m_type == 2 || content.m_type == 4) {
2881 content.m_handle = readTableHandle(hdlBuf, bounds.handleEndBit);
2882 }
2883
2884 std::uint32_t numAttrs = 0;
2885 if (!readTableBodyCount(version, buf, bounds.bodyEndBit, kMaxTableItems, 2,
2886 numAttrs)) {
2887 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");
2888 return false;
2889 }
2890 for (std::uint32_t attr = 0; attr < numAttrs; ++attr) {
2891 if (hdlBuf != nullptr
2892 && readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2893 && !hdlBuf->isGood())
2894 return false;
2895 readTableText(version, textBuf,
2896 tableTextEndBit(buf, textBuf, bounds));
2897 if (!textBuf->isGood()
2898 || !skipTableBitLong(*buf, bounds.bodyEndBit))
2899 return false;
2900 }
2901
2902 std::uint16_t contentFormatFlag = 0;
2903 if (!readBoundedBitShort(*buf, bounds.bodyEndBit, contentFormatFlag))
2904 return false;
2905 const bool hasContentFormat = contentFormatFlag != 0;
2906 if (hasContentFormat
2907 && !skipTableContentFormat(version, buf, strBuf, hdlBuf, ranges,
2908 bounds)) {
2909 DRW_DBG("TABLE cell content format parse incomplete\n")DRW_dbg::dbg("TABLE cell content format parse incomplete\n");
2910 return false;
2911 }
2912 cell.m_contents.push_back(content);
2913 }
2914
2915 if (!skipTableCellStyle(version, buf, strBuf, hdlBuf, ranges, bounds)) {
2916 DRW_DBG("TABLE cell style override parse incomplete\n")DRW_dbg::dbg("TABLE cell style override parse incomplete\n");
2917 return false;
2918 }
2919
2920 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, cell.m_styleId))
2921 return false;
2922 const std::uint64_t geometryStartBit = currentDwgBit(buf);
2923 std::int32_t hasGeometry = 0;
2924 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, hasGeometry))
2925 return false;
2926 if (hasGeometry != 0) {
2927 std::int32_t geometryFlags = 0;
2928 if (!skipTableBitLong(*buf, bounds.bodyEndBit) // unknown AC1027+ geometry marker
2929 || !readBoundedBitDouble(*buf, bounds.bodyEndBit, cell.m_width)
2930 || !readBoundedBitDouble(*buf, bounds.bodyEndBit, cell.m_height)
2931 || !readBoundedBitLong(*buf, bounds.bodyEndBit, geometryFlags))
2932 return false;
2933 cell.m_geometryFlags = geometryFlags;
2934 cell.m_geometryHandle = readTableHandle(hdlBuf, bounds.handleEndBit);
2935 if (cell.m_geometryFlags != 0) {
2936 std::int32_t geometryRecordFlags = 0;
2937 if (!readBounded3BitDouble(*buf, bounds.bodyEndBit,
2938 cell.m_geometryTopLeft)
2939 || !readBounded3BitDouble(*buf, bounds.bodyEndBit,
2940 cell.m_geometryCenter)
2941 || !readBoundedBitDouble(*buf, bounds.bodyEndBit,
2942 cell.m_contentWidth)
2943 || !readBoundedBitDouble(*buf, bounds.bodyEndBit,
2944 cell.m_contentHeight)
2945 || !readBoundedBitDouble(*buf, bounds.bodyEndBit,
2946 cell.m_geometryWidth)
2947 || !readBoundedBitDouble(*buf, bounds.bodyEndBit,
2948 cell.m_geometryHeight)
2949 || !readBoundedBitLong(*buf, bounds.bodyEndBit,
2950 geometryRecordFlags))
2951 return false;
2952 cell.m_geometryRecordFlags = geometryRecordFlags;
2953 }
2954 if (ranges != nullptr) {
2955 const bool geometryGood = buf->isGood() && (!hdlBuf || hdlBuf->isGood());
2956 ranges->push_back(makeDwgSubrecordRange(
2957 "table-cell-geometry-tail", geometryStartBit, currentDwgBit(buf),
2958 version, cell.m_geometryFlags, geometryGood));
2959 }
2960 }
2961
2962 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2963 if (!good)
2964 DRW_DBG("TABLE cell stream ended unexpectedly\n")DRW_dbg::dbg("TABLE cell stream ended unexpectedly\n");
2965 return good;
2966}
2967
2968bool parseTableContent(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf,
2969 dwgBuffer *hdlBuf, DRW_TableContent& content,
2970 const TableDwgBounds& bounds) {
2971 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2972 const std::uint64_t textEndBit = tableTextEndBit(buf, textBuf, bounds);
2973 content.m_name = readTableText(version, textBuf, textEndBit);
2974 content.m_description = readTableText(version, textBuf, textEndBit);
2975 if (!textBuf->isGood())
2976 return false;
2977
2978 std::uint32_t columns = 0;
2979 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
2980 kMaxTableColumns, 2,
2981 columns)) {
2982 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");
2983 return false;
2984 }
2985 content.m_columns.clear();
2986 if (!DRW::reserve(content.m_columns, static_cast<int>(columns)))
2987 return false;
2988 for (std::uint32_t col = 0; col < columns; ++col) {
2989 DRW_TableColumn column;
2990 column.m_name = readTableText(version, textBuf, textEndBit);
2991 if (!textBuf->isGood()
2992 || !skipTableBitLong(*buf, bounds.bodyEndBit)) // custom data
2993 return false;
2994 std::uint32_t customItems = 0;
2995 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
2996 kMaxTableItems, 2,
2997 customItems)) {
2998 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");
2999 return false;
3000 }
3001 for (std::uint32_t i = 0; i < customItems; ++i) {
3002 if (!skipTableCustomData(version, buf, strBuf, hdlBuf, bounds)) {
3003 DRW_DBG("TABLECONTENT column custom data parse incomplete\n")DRW_dbg::dbg("TABLECONTENT column custom data parse incomplete\n"
)
;
3004 return false;
3005 }
3006 }
3007 if (!skipTableCellStyle(version, buf, strBuf, hdlBuf,
3008 &content.m_subrecordRanges, bounds)) {
3009 DRW_DBG("TABLECONTENT column cell style parse incomplete\n")DRW_dbg::dbg("TABLECONTENT column cell style parse incomplete\n"
)
;
3010 return false;
3011 }
3012 if (!skipTableBitLong(*buf, bounds.bodyEndBit) // style id
3013 || !readBoundedBitDouble(*buf, bounds.bodyEndBit,
3014 column.m_width))
3015 return false;
3016 content.m_columns.push_back(column);
3017 }
3018
3019 std::uint32_t rows = 0;
3020 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
3021 kMaxTableRows, 2,
3022 rows)
3023 || (columns != 0 && rows > kMaxTableCells / columns)) {
3024 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");
3025 return false;
3026 }
3027 content.m_rows.clear();
3028 if (!DRW::reserve(content.m_rows, static_cast<int>(rows)))
3029 return false;
3030 for (std::uint32_t rowIndex = 0; rowIndex < rows; ++rowIndex) {
3031 DRW_TableRow row;
3032 std::uint32_t cells = 0;
3033 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
3034 kMaxTableColumns, 2,
3035 cells)
3036 || cells > kMaxTableItems) {
3037 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");
3038 return false;
3039 }
3040 if (!DRW::reserve(row.m_cells, static_cast<int>(cells)))
3041 return false;
3042 for (std::uint32_t cellIndex = 0; cellIndex < cells; ++cellIndex) {
3043 DRW_TableCell cell;
3044 if (!parseTableCell(version, buf, strBuf, hdlBuf, cell,
3045 &content.m_subrecordRanges, bounds)) {
3046 DRW_DBG("TABLECONTENT cell parse incomplete at row ")DRW_dbg::dbg("TABLECONTENT cell parse incomplete at row "); DRW_DBG(rowIndex)DRW_dbg::dbg(rowIndex);
3047 DRW_DBG(" cell ")DRW_dbg::dbg(" cell "); DRW_DBG(cellIndex)DRW_dbg::dbg(cellIndex); DRW_DBG("\n")DRW_dbg::dbg("\n");
3048 return false;
3049 }
3050 row.m_cells.push_back(cell);
3051 }
3052
3053 if (!skipTableBitLong(*buf, bounds.bodyEndBit)) // custom data
3054 return false;
3055 std::uint32_t customItems = 0;
3056 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
3057 kMaxTableItems, 2,
3058 customItems)) {
3059 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");
3060 return false;
3061 }
3062 for (std::uint32_t i = 0; i < customItems; ++i) {
3063 if (!skipTableCustomData(version, buf, strBuf, hdlBuf, bounds)) {
3064 DRW_DBG("TABLECONTENT row custom data parse incomplete\n")DRW_dbg::dbg("TABLECONTENT row custom data parse incomplete\n"
)
;
3065 return false;
3066 }
3067 }
3068 if (!skipTableCellStyle(version, buf, strBuf, hdlBuf,
3069 &content.m_subrecordRanges, bounds)) {
3070 DRW_DBG("TABLECONTENT row cell style parse incomplete\n")DRW_dbg::dbg("TABLECONTENT row cell style parse incomplete\n"
)
;
3071 return false;
3072 }
3073 if (!skipTableBitLong(*buf, bounds.bodyEndBit) // style id
3074 || !readBoundedBitDouble(*buf, bounds.bodyEndBit, row.m_height))
3075 return false;
3076 content.m_rows.push_back(row);
3077 }
3078
3079 std::uint32_t fieldRefs = 0;
3080 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
3081 kMaxTableItems, 0,
3082 fieldRefs)) {
3083 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");
3084 return false;
3085 }
3086 content.m_fieldHandles.clear();
3087 if (!DRW::reserve(content.m_fieldHandles, static_cast<int>(fieldRefs)))
3088 return false;
3089 for (std::uint32_t i = 0; i < fieldRefs; ++i) {
3090 const std::uint32_t ref = readTableHandle(hdlBuf, bounds.handleEndBit);
3091 if (hdlBuf != nullptr && !hdlBuf->isGood())
3092 return false;
3093 if (ref != 0)
3094 content.m_fieldHandles.push_back(ref);
3095 }
3096
3097 if (!skipTableCellStyle(version, buf, strBuf, hdlBuf,
3098 &content.m_subrecordRanges, bounds)) {
3099 DRW_DBG("TABLECONTENT table cell style parse incomplete\n")DRW_dbg::dbg("TABLECONTENT table cell style parse incomplete\n"
)
;
3100 return false;
3101 }
3102
3103 std::uint32_t mergedRanges = 0;
3104 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
3105 kMaxTableItems, 8,
3106 mergedRanges)) {
3107 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");
3108 return false;
3109 }
3110 content.m_mergedRanges.clear();
3111 if (!DRW::reserve(content.m_mergedRanges,
3112 static_cast<int>(mergedRanges)))
3113 return false;
3114 for (std::uint32_t i = 0; i < mergedRanges; ++i) {
3115 DRW_TableMergedRange range;
3116 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, range.m_topRow)
3117 || !readBoundedBitLong(*buf, bounds.bodyEndBit,
3118 range.m_leftColumn)
3119 || !readBoundedBitLong(*buf, bounds.bodyEndBit,
3120 range.m_bottomRow)
3121 || !readBoundedBitLong(*buf, bounds.bodyEndBit,
3122 range.m_rightColumn))
3123 return false;
3124 content.m_mergedRanges.push_back(range);
3125 }
3126
3127 content.m_tableStyleHandle = readTableHandle(hdlBuf, bounds.handleEndBit);
3128 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
3129 if (!good)
3130 DRW_DBG("TABLECONTENT stream ended unexpectedly\n")DRW_dbg::dbg("TABLECONTENT stream ended unexpectedly\n");
3131 return good;
3132}
3133
3134} // namespace
3135
3136//! Calculate arbitrary axis
3137/*!
3138* Calculate arbitrary axis for apply extrusions
3139* @author Rallaz
3140*/
3141void DRW_Entity::calculateAxis(DRW_Coord extPoint){
3142 //Follow the arbitrary DXF definitions for extrusion axes.
3143 if (fabs(extPoint.x) < 0.015625 && fabs(extPoint.y) < 0.015625) {
3144 //If we get here, implement Ax = Wy x N where Wy is [0,1,0] per the DXF spec.
3145 //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
3146 //Factoring in the fixed values for Wy gives N.z,0,-N.x
3147 extAxisX.x = extPoint.z;
3148 extAxisX.y = 0;
3149 extAxisX.z = -extPoint.x;
3150 } else {
3151 //Otherwise, implement Ax = Wz x N where Wz is [0,0,1] per the DXF spec.
3152 //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
3153 //Factoring in the fixed values for Wz gives -N.y,N.x,0.
3154 extAxisX.x = -extPoint.y;
3155 extAxisX.y = extPoint.x;
3156 extAxisX.z = 0;
3157 }
3158
3159 extAxisX.unitize();
3160
3161 //Ay = N x Ax
3162 extAxisY.x = (extPoint.y * extAxisX.z) - (extAxisX.y * extPoint.z);
3163 extAxisY.y = (extPoint.z * extAxisX.x) - (extAxisX.z * extPoint.x);
3164 extAxisY.z = (extPoint.x * extAxisX.y) - (extAxisX.x * extPoint.y);
3165
3166 extAxisY.unitize();
3167}
3168
3169//! Extrude a point using arbitrary axis
3170/*!
3171* apply extrusion in a point using arbitrary axis (previous calculated)
3172* @author Rallaz
3173*/
3174void DRW_Entity::extrudePoint(DRW_Coord extPoint, DRW_Coord *point){
3175 double px, py, pz;
3176 px = (extAxisX.x*point->x)+(extAxisY.x*point->y)+(extPoint.x*point->z);
3177 py = (extAxisX.y*point->x)+(extAxisY.y*point->y)+(extPoint.y*point->z);
3178 pz = (extAxisX.z*point->x)+(extAxisY.z*point->y)+(extPoint.z*point->z);
3179
3180 point->x = px;
3181 point->y = py;
3182 point->z = pz;
3183}
3184
3185bool DRW_Entity::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
3186 try {
3187 switch (code) {
3188 case DRW::dxfCode::HANDLE:
3189 // A damaged file's empty or repeated handle on a typed entity is
3190 // dropped; the entity loads and gets a new handle when saved.
3191 if (reader->getString().empty())
3192 break;
3193 if (!reader->isValidHandleString())
3194 return false;
3195 if (reader->registerSelfHandle())
3196 handle = reader->getHandleString();
3197 break;
3198 case DRW::dxfCode::OWNER_HANDLE:
3199 parentHandle = reader->getHandleString();
3200 break;
3201 case DRW::dxfCode::LAYER:
3202 layer = reader->getUtf8String();
3203 break;
3204 case 6:
3205 lineType = reader->getUtf8String();
3206 break;
3207 case DRW::dxfCode::COLOR:
3208 color = reader->getInt32();
3209 break;
3210 case DRW::dxfCode::LINEWEIGHT:
3211 lWeight = DRW_LW_Conv::dxfInt2lineWidth(reader->getInt32());
3212 break;
3213 case 48:
3214 ltypeScale = reader->getDouble();
3215 break;
3216 case DRW::dxfCode::INVISIBLE:
3217 // DXF 60 is a scalar visibility value, not a DWG bit field.
3218 visible = reader->getInt32() == 0;
3219 break;
3220 case 420:
3221 color24 = reader->getInt32();
3222 break;
3223 case 430:
3224 colorName = reader->getString();
3225 break;
3226 case 67:
3227 {
3228 const int value = reader->getInt32();
3229 if (value < DRW::ModelSpace || value > DRW::PaperSpace)
3230 return false;
3231 space = static_cast<DRW::Space>(value);
3232 }
3233 break;
3234 case 102:
3235 return parseDxfGroups(code, reader);
3236 case 284:
3237 {
3238 int value = 0;
3239 if (!readDxfIntInRange(reader, DRW::CastAndReceieveShadows,
3240 DRW::IgnoreShadows, value))
3241 return false;
3242 shadow = static_cast<DRW::ShadowMode>(value);
3243 }
3244 break;
3245 case 347:
3246 material = static_cast<std::uint32_t>(reader->getHandleString());
3247 break;
3248 case 348:
3249 fullVisualStyleHandle = reader->getHandleString();
3250 break;
3251 case 360:
3252 xDictHandle = reader->getHandleString();
3253 break;
3254 case DRW::dxfCode::PLOTSTYLE:
3255 plotStyle = reader->getHandleString();
3256 break;
3257 case 440:
3258 transparency = reader->getInt32();
3259 break;
3260 case 92: {
3261 // Proxy entity graphics byte count (ODA §20.4.95): 92 for R13–R2007.
3262 // Entities that repurpose 92 (for example MESH) handle it before this
3263 // base parser is reached.
3264 const std::int32_t value = reader->getInt32();
3265 if (value < 0
3266 || static_cast<std::uint32_t>(value)
3267 > DRW::kMaxDxfBinaryPayloadBytes)
3268 return false;
3269 numProxyGraph = value;
3270 proxyGraphics.clear();
3271 break;
3272 }
3273 case 160: {
3274 // R2010+ uses a 64-bit DXF count (group 160), not group 92's 32-bit
3275 // storage. Keep the public legacy field bounded and representable.
3276 const std::int64_t value = reader->getInt64();
3277 if (value < 0
3278 || static_cast<std::uint64_t>(value)
3279 > DRW::kMaxDxfBinaryPayloadBytes)
3280 return false;
3281 numProxyGraph = static_cast<int>(value);
3282 proxyGraphics.clear();
3283 break;
3284 }
3285 case 310:
3286 if (numProxyGraph != 0) {
3287 // Proxy graphics binary, hex-encoded across many ≤254-char chunks.
3288 const std::string& hex = reader->getString();
3289 if (!appendHexBytesChecked(
3290 proxyGraphics, hex,
3291 std::min<std::size_t>(
3292 DRW::kMaxDxfBinaryPayloadBytes,
3293 static_cast<std::size_t>(numProxyGraph))))
3294 return false;
3295 }
3296 break;
3297 case 1000:
3298 case 1001:
3299 case 1002:
3300 case 1003:
3301 case 1004:
3302 case 1005:
3303 if (extData.size() >= DRW_TableEntry::kMaxExtendedDataItems)
3304 return false;
3305 extData.push_back(std::make_shared<DRW_Variant>(code,
3306 reader->getString()));
3307 break;
3308 case 1010:
3309 case 1011:
3310 case 1012:
3311 case 1013:
3312 if (extData.size() >= DRW_TableEntry::kMaxExtendedDataItems)
3313 return false;
3314 curr = std::make_shared<DRW_Variant>(
3315 code, DRW_Coord(reader->getDouble(), 0.0, 0.0));
3316 extData.push_back(curr);
3317 break;
3318 // Files repeat Y and Z after a point's Z (R12 ACAD_MATERIAL_MAPPER and
3319 // ASC_SEEDPOINT); a component with no open point is dropped.
3320 case 1020:
3321 case 1021:
3322 case 1022:
3323 case 1023:
3324 if (curr)
3325 curr->setCoordY(reader->getDouble());
3326 break;
3327 case 1030:
3328 case 1031:
3329 case 1032:
3330 case 1033:
3331 if (curr)
3332 curr->setCoordZ(reader->getDouble());
3333 curr.reset();
3334 break;
3335 case 1040:
3336 case 1041:
3337 case 1042:
3338 if (extData.size() >= DRW_TableEntry::kMaxExtendedDataItems)
3339 return false;
3340 extData.push_back(std::make_shared<DRW_Variant>(code,
3341 reader->getDouble()));
3342 break;
3343 case 1070:
3344 case 1071:
3345 if (extData.size() >= DRW_TableEntry::kMaxExtendedDataItems)
3346 return false;
3347 extData.push_back(std::make_shared<DRW_Variant>(code,
3348 reader->getInt32()));
3349 break;
3350 default:
3351 break;
3352 }
3353 return true;
3354 } catch (...) {
3355 reset();
3356 return false;
3357 }
3358}
3359
3360//parses dxf 102 groups to read entity
3361bool DRW_Entity::parseDxfGroups(int code, const std::unique_ptr<dxfReader>& reader){
3362 if (appData.size() >= DRW::kMaxDxfApplicationGroups)
3363 return false;
3364 std::list<DRW_Variant> ls;
3365 DRW_Variant curr;
3366 std::string appName= reader->getString();
3367 if (appName.size() <= 1 || appName.front() != '{')
3368 return false;
3369
3370 const bool isReactors = appName == "{ACAD_REACTORS";
3371 const bool isXDictionary = appName == "{ACAD_XDICTIONARY";
3372 std::vector<std::uint32_t> parsedReactors;
3373 std::uint32_t parsedXDictionary = 0;
3374
3375 curr.addString(code, appName.substr(1));
3376 ls.push_back(curr);
3377 int depth = 1;
3378 int nextCode = 0;
3379 while (depth > 0 && reader->readRec(&nextCode)) {
3380 if (ls.size() >= DRW::kMaxDxfApplicationGroupPairs)
3381 return false;
3382
3383 // A section/entity boundary before the matching 102 close would be
3384 // consumed irreversibly and leave the caller out of sync.
3385 if (nextCode == 0)
3386 return false;
3387
3388 DRW_Variant value;
3389 if (nextCode == 102) {
3390 const std::string marker = reader->getString();
3391 if (marker.empty() || (marker.front() == '{' && marker.size() == 1))
3392 return false;
3393 if (marker.front() == '{') {
3394 if (++depth > DRW::kMaxDxfApplicationGroupNesting)
3395 return false;
3396 } else if (marker == "}") {
3397 --depth;
3398 } else {
3399 return false;
3400 }
3401 value.addString(nextCode, marker);
3402 } else if ((nextCode >= 320 && nextCode <= 369)
3403 || (nextCode >= 390 && nextCode <= 399)
3404 || nextCode == 480 || nextCode == 481
3405 || nextCode == 1005) {
3406 const std::string rawHandle = reader->getString();
3407 value.addString(nextCode, rawHandle);
3408 if (depth == 1 && isReactors && nextCode == 330) {
3409 const auto reactor = reader->getHandleString();
3410 if (reactor != 0) {
3411 if (parsedReactors.size() >= dwgSafety::MaxReactorCount)
3412 return false;
3413 parsedReactors.push_back(static_cast<std::uint32_t>(reactor));
3414 }
3415 } else if (depth == 1 && isXDictionary && nextCode == 360
3416 && parsedXDictionary == 0) {
3417 parsedXDictionary = static_cast<std::uint32_t>(
3418 reader->getHandleString());
3419 }
3420 } else {
3421 switch (reader->type) {
3422 case dxfReader::STRING:
3423 case dxfReader::BINARY:
3424 value.addString(nextCode, reader->getString());
3425 break;
3426 case dxfReader::INT32:
3427 case dxfReader::BOOL:
3428 value.addInt(nextCode, reader->getInt32());
3429 break;
3430 case dxfReader::INT64:
3431 value.addInt64(nextCode, static_cast<std::int64_t>(reader->getInt64()));
3432 break;
3433 case dxfReader::DOUBLE:
3434 value.addDouble(nextCode, reader->getDouble());
3435 break;
3436 default:
3437 return false;
3438 }
3439 }
3440 ls.push_back(value);
3441 }
3442
3443 if (depth != 0)
3444 return false;
3445 if (isReactors)
3446 reactorHandles = std::move(parsedReactors);
3447 if (isXDictionary && parsedXDictionary != 0)
3448 xDictHandle = parsedXDictionary;
3449 appData.push_back(std::move(ls));
3450 return true;
3451}
3452
3453bool DRW_Entity::parseDwg(DRW::Version version, dwgBuffer *buf, dwgBuffer* strBuf, std::uint32_t bs){
3454 // This overload is also used by the two-stage common/handle tests and
3455 // callers; concrete record parsers own carrier reset so handle parsing can
3456 // remain transactional.
3457 if (buf == nullptr)
3458 return false;
3459
3460 // The object size in bits is read at a version-dependent point: first
3461 // for R2000/R2004, derived for R2010+, and only after the EED and the
3462 // graphics data for R13/R14. Every bound computed before that point
3463 // spells "unknown" as 0 (the whole frame), so start from it instead of
3464 // whatever an earlier parse of this object, or the construction of a
3465 // probe entity, left behind.
3466 objSize = 0;
3467
3468 DRW_DBG("\n***************************** parsing entity *********************************************\n")DRW_dbg::dbg("\n***************************** parsing entity *********************************************\n"
)
;
3469 oType = buf->getObjType(version);
3470 if (!buf->isGood())
3471 return false;
3472 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);
3473
3474 if (version > DRW::AC1014 && version < DRW::AC1024) {//2000 & 2004
3475 const std::uint64_t totalBits = static_cast<std::uint64_t>(buf->size()) * 8u;
3476 std::uint32_t parsedObjectSize = 0;
3477 if (!readBoundedRawLong32(*buf, totalBits, parsedObjectSize))
3478 return false;
3479 objSize = parsedObjectSize; //RL 32bits object size in bits
3480 DRW_DBG(" Object size: ")DRW_dbg::dbg(" Object size: "); DRW_DBG(objSize)DRW_dbg::dbg(objSize); DRW_DBG("\n")DRW_dbg::dbg("\n");
3481 }
3482 if (version > DRW::AC1021) {//2010+
3483 const std::uint64_t totalBits = static_cast<std::uint64_t>(buf->size()) * 8u;
3484 // Clamp: a corrupt bs > ms*8 would underflow objSize (unsigned) to a
3485 // huge value and drive strBuf->moveBitPos(objSize-1) past the buffer.
3486 if (totalBits > std::numeric_limits<std::uint32_t>::max()
3487 || bs > totalBits)
3488 return false;
3489 objSize = static_cast<std::uint32_t>(totalBits - bs);
3490 DRW_DBG(" Object size: ")DRW_dbg::dbg(" Object size: "); DRW_DBG(objSize)DRW_dbg::dbg(objSize); DRW_DBG("\n")DRW_dbg::dbg("\n");
3491 }
3492
3493 if (strBuf != NULL__null && version > DRW::AC1018) {//2007+
3494 if (!strBuf->seekR2007StringStream(objSize))
3495 return false;
3496 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");
3497 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");
3498 }
3499
3500 // R2007+ objSize includes the detached string stream and its footer. A
3501 // common entity prefix must stay in the data portion just like the
3502 // entity-specific readers below. Only a caller that supplies the real
3503 // modern string cursor can require the footer here; the null-cursor form
3504 // is retained for isolated common-prefix tests without object framing.
3505 std::uint64_t bodyEndBit = proxyEntityEndBit(*buf, objSize);
3506 if (version > DRW::AC1021 && strBuf != nullptr) {
3507 dwgBuffer stringBoundary = strBuf->forkIndependent();
3508 if (!stringBoundary.seekR2007StringStream(objSize))
3509 return false;
3510 bodyEndBit = currentDwgBit(&stringBoundary);
3511 }
3512 dwgDataEndBit = bodyEndBit;
3513 const auto bodyWithinBounds = [buf, &bodyEndBit]() {
3514 return currentDwgBit(buf) <= bodyEndBit;
3515 };
3516
3517 dwgHandle ho;
3518 if (!readBoundedDwgHandle(*buf, bodyEndBit, 0, false, ho))
3519 return false;
3520 handle = ho.ref;
3521 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);
3522 // EED is a bounded, repeated chunk stream. Parse all chunks before
3523 // changing the entity so a malformed item cannot leak partial metadata.
3524 std::vector<DwgEedChunk> eedChunks;
3525 if (!readDwgEed(version, *buf, eedChunks, bodyEndBit))
3526 return false;
3527 if (!bodyWithinBounds())
3528 return false;
3529 std::vector<std::shared_ptr<DRW_Variant>> parsedExtData;
3530 std::vector<PendingHandleRef> parsedAppIdResolutions;
3531 std::vector<PendingHandleRef> parsedLayerRefResolutions;
3532 try {
3533 for (const DwgEedChunk& chunk : eedChunks) {
3534 const std::size_t appIndex =
3535 extData.size() + parsedExtData.size();
3536 parsedExtData.push_back(
3537 std::make_shared<DRW_Variant>(1001, std::string{}));
3538 parsedAppIdResolutions.push_back({appIndex, chunk.appHandle});
3539 for (const DRW_Variant& item : chunk.items)
3540 parsedExtData.push_back(std::make_shared<DRW_Variant>(item));
3541 for (const DwgEedHandleRef& ref : chunk.layerRefs) {
3542 parsedLayerRefResolutions.push_back(
3543 {appIndex + 1 + ref.itemIndex, ref.handleRef});
3544 }
3545 }
3546 } catch (...) {
3547 return false;
3548 }
3549 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");
3550 bool graphFlag = false;
3551 if (!readBoundedBit(*buf, bodyEndBit, graphFlag))
3552 return false;
3553 DRW_DBG(" graphFlag: ")DRW_dbg::dbg(" graphFlag: "); DRW_DBG(graphFlag)DRW_dbg::dbg(graphFlag); DRW_DBG("\n")DRW_dbg::dbg("\n");
3554 if (!bodyWithinBounds())
3555 return false;
3556 if (graphFlag) {
3557 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");
3558 std::uint64_t graphDataSize = 0;
3559 if (version >= DRW::AC1024) {
3560 if (!readBoundedBitLongLong(*buf, bodyEndBit, graphDataSize))
3561 return false;
3562 } else {
3563 std::uint32_t parsedGraphDataSize = 0;
3564 if (!readBoundedRawLong32(*buf, bodyEndBit,
3565 parsedGraphDataSize))
3566 return false;
3567 graphDataSize = parsedGraphDataSize;
3568 }
3569 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");
3570 if (!bodyWithinBounds())
3571 return false;
3572 const std::uint64_t maxMoveBytes = static_cast<std::uint64_t>(std::numeric_limits<std::int32_t>::max() / 8);
3573 const std::uint64_t currentBit = currentDwgBit(buf);
3574 std::uint64_t graphBits = 0;
3575 if (!dwgSafety::multiply(graphDataSize, 8, graphBits)
3576 || currentBit > bodyEndBit
3577 || graphBits > bodyEndBit - currentBit
3578 || graphDataSize > static_cast<std::uint64_t>(buf->numRemainingBytes())
3579 || graphDataSize > maxMoveBytes) {
3580 DRW_DBG("graphData size outside object body\n")DRW_dbg::dbg("graphData size outside object body\n");
3581 return false;
3582 }
3583 // Capture the proxy-graphics byte stream instead of skipping it. These
3584 // are cached drawable primitives (lines/arcs/polylines/text) that any
3585 // reader can render for proxy/custom entities (STDPART2D, AEC_*, tables)
3586 // — previously discarded via moveBitPos, leaving proxyGraphics empty.
3587 // dwgBuffer::getBytes is bit-aware (reconstructs each byte at a non-zero
3588 // bitPos), so it lands at the exact same position moveBitPos(8N) did.
3589 // (write-review #32 / read-coverage gap #1)
3590 if (graphDataSize > 0) {
3591 if (!DRW::resize(proxyGraphics, static_cast<int>(graphDataSize)))
3592 return false;
3593 if (!readBoundedBytes(
3594 *buf, bodyEndBit,
3595 reinterpret_cast<std::uint8_t*>(&proxyGraphics[0]),
3596 static_cast<std::size_t>(graphDataSize)))
3597 return false;
3598 numProxyGraph = static_cast<int>(graphDataSize);
3599 }
3600 }
3601 if (version < DRW::AC1015) {//14-
3602 std::uint32_t parsedObjectSize = 0;
3603 if (!readBoundedRawLong32(*buf, proxyEntityEndBit(*buf, 0),
3604 parsedObjectSize))
3605 return false;
3606 objSize = parsedObjectSize; //RL 32bits object size in bits
3607 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");
3608 bodyEndBit = proxyEntityEndBit(*buf, objSize);
3609 }
3610
3611 std::uint8_t entmode = 0;
3612 if (!readBounded2Bits(*buf, bodyEndBit, entmode))
3613 return false;
3614 if (entmode == 0)
3615 ownerHandle= true;
3616 // entmode = 2;
3617 else if(entmode ==2)
3618 entmode = 0;
3619 space = (DRW::Space)entmode; //RLZ verify cast values
3620 DRW_DBG("entmode: ")DRW_dbg::dbg("entmode: "); DRW_DBG(entmode)DRW_dbg::dbg(entmode);
3621 if (!readBoundedBitLong(*buf, bodyEndBit, numReactors))
3622 return false; //BL per spec §20.4.1
3623 DRW_DBG(", numReactors: ")DRW_dbg::dbg(", numReactors: "); DRW_DBG(numReactors)DRW_dbg::dbg(numReactors);
3624 if (!buf->isGood() || !dwgSafety::validReactorCount(numReactors))
3625 return false;
3626
3627 if (version < DRW::AC1015) {//14-
3628 bool isByLayer = false;
3629 if (!readBoundedBit(*buf, bodyEndBit, isByLayer))
3630 return false;
3631 if (isByLayer) {//is bylayer line type
3632 lineType = "BYLAYER";
3633 ltFlags = 0;
3634 } else {
3635 lineType = "";
3636 ltFlags = 3;
3637 }
3638 DRW_DBG(" lineType: ")DRW_dbg::dbg(" lineType: "); DRW_DBG(lineType.c_str())DRW_dbg::dbg(lineType.c_str());
3639 DRW_DBG(" ltFlags: ")DRW_dbg::dbg(" ltFlags: "); DRW_DBG(ltFlags)DRW_dbg::dbg(ltFlags);
3640 }
3641 if (version > DRW::AC1015) {//2004+
3642 bool xDict = false;
3643 if (!readBoundedBit(*buf, bodyEndBit, xDict))
3644 return false;
3645 xDictFlag = xDict;
3646 DRW_DBG(" xDictFlag: ")DRW_dbg::dbg(" xDictFlag: "); DRW_DBG(xDictFlag)DRW_dbg::dbg(xDictFlag); DRW_DBG("\n")DRW_dbg::dbg("\n");
3647 }
3648
3649 // libreDWG common_entity_data.spec — the bit at this stream position has two
3650 // disjoint meanings by version:
3651 // * R13..R2002 (version < AC1018): `nolinks` (B). 1 = no prev/next handles
3652 // in the handle section; 0 = read prev+next at parseDwgEntHandle.
3653 // * R2004..R2010 (AC1018..AC1024): NO bit in the stream — reader forces
3654 // haveNextLinks=1 to skip the prev/next handle reads.
3655 // * R2013+ (version > AC1024): `has_ds_data` (B). 1 = inline ACIS SAB
3656 // datastore present. Stored separately because it gates SAB handling,
3657 // not prev/next links (which are already version<AC1018 gated).
3658 // Total bit consumption is unchanged for every version.
3659 if (version < DRW::AC1018) {
3660 bool parsedHaveNextLinks = false;
3661 if (!readBoundedBit(*buf, bodyEndBit, parsedHaveNextLinks))
3662 return false; //nolinks //B
3663 haveNextLinks = parsedHaveNextLinks ? 1 : 0;
3664 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");
3665 } else {
3666 haveNextLinks = 1; //AC1018+: not in stream, force 1 (no prev/next)
3667 DRW_DBG(", haveNextLinks (forced): ")DRW_dbg::dbg(", haveNextLinks (forced): "); DRW_DBG(haveNextLinks)DRW_dbg::dbg(haveNextLinks); DRW_DBG("\n")DRW_dbg::dbg("\n");
3668 }
3669 if (version > DRW::AC1024) {
3670 bool parsedHasDsData = false;
3671 if (!readBoundedBit(*buf, bodyEndBit, parsedHasDsData))
3672 return false; //has_ds_data //B (R2013+)
3673 hasDsData = parsedHasDsData ? 1 : 0;
3674 DRW_DBG(", hasDsData (R2013+): ")DRW_dbg::dbg(", hasDsData (R2013+): "); DRW_DBG(hasDsData)DRW_dbg::dbg(hasDsData); DRW_DBG("\n")DRW_dbg::dbg("\n");
3675 }
3676//ENC color
3677 std::uint32_t parsedColor = 0;
3678 if (!readBoundedEnColor(*buf, bodyEndBit, version, parsedColor))
3679 return false; //BS or CMC //ok for R14 or negate
3680 color = static_cast<int>(parsedColor);
3681 // Capture the AcDbColor side-channel before the common handle stream.
3682 // The handle follows owner/reactors/xdictionary and precedes layer in the
3683 // R2004+ common entity handle data.
3684 hasAcDbColorH = buf->lastEnColorHadDbColorRef;
3685 // libreDWG common_entity_data.spec:432-453 — ENC alpha_raw (DXF code
3686 // 440) is encoded as (alpha_type<<24) | alpha. Stored verbatim; the
3687 // filter (RS_FilterDXFRW::setEntityAttributes) decodes alpha_type==3
3688 // into a per-entity pen alpha, otherwise inherits from layer/block.
3689 if (buf->lastEnColorAlphaRaw != 0) {
3690 transparency = static_cast<int>(buf->lastEnColorAlphaRaw);
3691 }
3692 // libreDWG common_entity_data.spec:468-475 — inline TV name/book name
3693 // (flags 0x41/0x42) override any dbColorMap-resolved name. Captured
3694 // immediately; entryParse will skip the override only if colorName is
3695 // already populated here.
3696 if (!buf->lastEnColorName.empty()) {
3697 colorName = buf->lastEnColorBookName.empty()
3698 ? buf->lastEnColorName
3699 : (buf->lastEnColorBookName + "$" + buf->lastEnColorName);
3700 }
3701 color24 = buf->lastEnColorRgb;
3702 if (!readBoundedBitDouble(*buf, bodyEndBit, ltypeScale))
3703 return false; //BD
3704 DRW_DBG(" entity color: ")DRW_dbg::dbg(" entity color: "); DRW_DBG(color)DRW_dbg::dbg(color);
3705 DRW_DBG(" ltScale: ")DRW_dbg::dbg(" ltScale: "); DRW_DBG(ltypeScale)DRW_dbg::dbg(ltypeScale); DRW_DBG("\n")DRW_dbg::dbg("\n");
3706 if (version > DRW::AC1014) {//2000+ — §19.4.1: linetype-flags BB then plot-flags BB
3707 std::uint8_t parsedLtFlags = 0;
3708 if (!readBounded2Bits(*buf, bodyEndBit, parsedLtFlags))
3709 return false; //BB
3710 ltFlags = parsedLtFlags;
3711 if (ltFlags == 0) lineType = "BYLAYER";
3712 else if (ltFlags == 1) lineType = "BYBLOCK";
3713 else if (ltFlags == 2) lineType = "CONTINUOUS";
3714 else lineType = ""; //3 → handle at end
3715 DRW_DBG("ltFlags: ")DRW_dbg::dbg("ltFlags: "); DRW_DBG(ltFlags)DRW_dbg::dbg(ltFlags);
3716 DRW_DBG(" lineType: ")DRW_dbg::dbg(" lineType: "); DRW_DBG(lineType.c_str())DRW_dbg::dbg(lineType.c_str());
3717
3718 std::uint8_t parsedPlotFlags = 0;
3719 if (!readBounded2Bits(*buf, bodyEndBit, parsedPlotFlags))
3720 return false; //BB
3721 plotFlags = parsedPlotFlags;
3722 DRW_DBG(", plotFlags: ")DRW_dbg::dbg(", plotFlags: "); DRW_DBG(plotFlags)DRW_dbg::dbg(plotFlags);
3723 }
3724 if (version > DRW::AC1018) {//2007+
3725 std::uint8_t parsedMaterialFlag = 0;
3726 if (!readBounded2Bits(*buf, bodyEndBit, parsedMaterialFlag))
3727 return false; //BB
3728 materialFlag = parsedMaterialFlag;
3729 DRW_DBG("materialFlag: ")DRW_dbg::dbg("materialFlag: "); DRW_DBG(materialFlag)DRW_dbg::dbg(materialFlag);
3730 std::uint8_t parsedShadowFlag = 0;
3731 if (!readBoundedRawChar8(*buf, bodyEndBit, parsedShadowFlag))
3732 return false; //RC, low 2 bits is shadow mode 0..3
3733 shadowFlag = parsedShadowFlag;
3734 DRW_DBG("shadowFlag: ")DRW_dbg::dbg("shadowFlag: "); DRW_DBG(shadowFlag)DRW_dbg::dbg(shadowFlag); DRW_DBG("\n")DRW_dbg::dbg("\n");
3735 shadow = static_cast<DRW::ShadowMode>(shadowFlag & 0x3);
3736 }
3737 if (version > DRW::AC1021) {//2010+ — §19.4.1: three single-bit flags
3738 // Ground-truth: libreDWG common_entity_data.spec lines 523-528
3739 // and ODA spec v5.4.1 §19.4.1 both define three FIELD_B (single bit)
3740 // flags here, one each for full/face/edge visual style. Total bit
3741 // consumption (3 bits) is identical to the historical BB+B shape;
3742 // only the semantics differ. The corresponding handles are read
3743 // conditionally in parseDwgEntHandle after the plotstyle handle.
3744 bool parsedHasFullVisualStyle = false;
3745 bool parsedHasFaceVisualStyle = false;
3746 bool parsedHasEdgeVisualStyle = false;
3747 if (!readBoundedBit(*buf, bodyEndBit, parsedHasFullVisualStyle)
3748 || !readBoundedBit(*buf, bodyEndBit, parsedHasFaceVisualStyle)
3749 || !readBoundedBit(*buf, bodyEndBit, parsedHasEdgeVisualStyle))
3750 return false; //B
3751 hasFullVisualStyle = parsedHasFullVisualStyle ? 1 : 0;
3752 hasFaceVisualStyle = parsedHasFaceVisualStyle ? 1 : 0;
3753 hasEdgeVisualStyle = parsedHasEdgeVisualStyle ? 1 : 0;
3754 DRW_DBG("hasFull/Face/Edge VisualStyle: ")DRW_dbg::dbg("hasFull/Face/Edge VisualStyle: ");
3755 DRW_DBG(hasFullVisualStyle)DRW_dbg::dbg(hasFullVisualStyle); DRW_DBG(" ")DRW_dbg::dbg(" ");
3756 DRW_DBG(hasFaceVisualStyle)DRW_dbg::dbg(hasFaceVisualStyle); DRW_DBG(" ")DRW_dbg::dbg(" ");
3757 DRW_DBG(hasEdgeVisualStyle)DRW_dbg::dbg(hasEdgeVisualStyle); DRW_DBG("\n")DRW_dbg::dbg("\n");
3758 }
3759 std::uint16_t parsedInvisibleFlag = 0;
3760 if (!readBoundedBitShort(*buf, bodyEndBit, parsedInvisibleFlag))
3761 return false; //BS
3762 const std::int16_t invisibleFlag =
3763 static_cast<std::int16_t>(parsedInvisibleFlag);
3764 DRW_DBG(" invisibleFlag: ")DRW_dbg::dbg(" invisibleFlag: "); DRW_DBG(invisibleFlag)DRW_dbg::dbg(invisibleFlag);
3765 // DXF group 60: bit 0 = invisible (1) / visible (0). libreDWG
3766 // common_entity_data.spec masks bit 0 only (`invisible & 1`) and ignores
3767 // the higher bits, so use the same mask rather than `== 0`. Paired with
3768 // the encode emit below.
3769 visible = ((invisibleFlag & 1) == 0);
3770 if (version > DRW::AC1014) {//2000+
3771 std::uint8_t parsedLineWeight = 0;
3772 if (!readBoundedRawChar8(*buf, bodyEndBit, parsedLineWeight))
3773 return false; //RC
3774 lWeight = DRW_LW_Conv::dwgInt2lineWidth(parsedLineWeight);
3775 DRW_DBG(" lwFlag (lWeight): ")DRW_dbg::dbg(" lwFlag (lWeight): "); DRW_DBG(lWeight)DRW_dbg::dbg(lWeight); DRW_DBG("\n")DRW_dbg::dbg("\n");
3776 }
3777 //Only in blocks ????????
3778// if (version > DRW::AC1018) {//2007+
3779// std::uint8_t unk = buf->getBit();
3780// DRW_DBG("unknown bit: "); DRW_DBG(unk); DRW_DBG("\n");
3781// }
3782 if (!buf->isGood() || !bodyWithinBounds())
3783 return false;
3784
3785 const std::size_t oldExtDataSize = extData.size();
3786 const std::size_t oldAppResolutionSize =
3787 pendingAppIdResolutions.size();
3788 const std::size_t oldLayerResolutionSize =
3789 pendingLayerRefResolutions.size();
3790 const auto rollbackEed = [this, oldExtDataSize,
3791 oldAppResolutionSize, oldLayerResolutionSize]() {
3792 extData.resize(oldExtDataSize);
3793 pendingAppIdResolutions.resize(oldAppResolutionSize);
3794 pendingLayerRefResolutions.resize(oldLayerResolutionSize);
3795 };
3796 if (parsedExtData.size() > DRW_TableEntry::kMaxExtendedDataItems
3797 || extData.size() > DRW_TableEntry::kMaxExtendedDataItems
3798 || parsedExtData.size()
3799 > DRW_TableEntry::kMaxExtendedDataItems - extData.size())
3800 return false;
3801 try {
3802 extData.insert(extData.end(), parsedExtData.begin(),
3803 parsedExtData.end());
3804 pendingAppIdResolutions.insert(
3805 pendingAppIdResolutions.end(), parsedAppIdResolutions.begin(),
3806 parsedAppIdResolutions.end());
3807 pendingLayerRefResolutions.insert(
3808 pendingLayerRefResolutions.end(),
3809 parsedLayerRefResolutions.begin(), parsedLayerRefResolutions.end());
3810 } catch (...) {
3811 rollbackEed();
3812 return false;
3813 }
3814 return true;
3815}
3816
3817bool DRW_Entity::parseDwgEntHandle(
3818 DRW::Version version, dwgBuffer *buf, bool resetHandleStream,
3819 std::uint64_t handleEndBit){
3820 if (buf == nullptr || !buf->isGood()
3821 || !dwgSafety::validReactorCount(numReactors))
3822 return false;
3823 if (resetHandleStream && version > DRW::AC1018) {//2007+ skip string area
3824 if (!buf->setPosition(objSize >> 3))
3825 return false;
3826 buf->setBitPos(objSize & 7);
3827 if (!buf->isGood())
3828 return false;
3829 }
3830 if (handleEndBit != std::numeric_limits<std::uint64_t>::max()
3831 && currentDwgBit(buf) > handleEndBit)
3832 return false;
3833
3834 std::uint32_t newAcDbColorHandle = acDbColorHandle;
3835 std::uint32_t newParentHandle = ownerHandle ? parentHandle : DRW::NoHandle;
3836 std::vector<std::uint32_t> newReactorHandles;
3837 const bool hasXDictionary = version <= DRW::AC1015 || xDictFlag != 1;
3838 std::uint32_t newXDictHandle = hasXDictionary ? xDictHandle : 0;
3839 dwgHandle newLayerH = layerH;
3840 dwgHandle newLTypeH = lTypeH;
3841 std::uint32_t newNextEntLink = nextEntLink;
3842 std::uint32_t newPrevEntLink = prevEntLink;
3843 const DRW::ShadowMode newShadow = version > DRW::AC1018
3844 ? shadow : DRW::CastAndReceieveShadows;
3845 std::uint32_t newMaterial = materialFlag == 3 ? material
3846 : DRW::MaterialByLayer;
3847 int newPlotStyle = plotFlags == 3 ? plotStyle
3848 : DRW::DefaultPlotStyle;
3849 std::uint32_t newShadowHandle = shadowFlag == 3 ? shadowHandle : 0;
3850 std::uint32_t newFullVisualStyleHandle = hasFullVisualStyle
3851 ? fullVisualStyleHandle : 0;
3852 std::uint32_t newFaceVisualStyleHandle = hasFaceVisualStyle
3853 ? faceVisualStyleHandle : 0;
3854 std::uint32_t newEdgeVisualStyleHandle = hasEdgeVisualStyle
3855 ? edgeVisualStyleHandle : 0;
3856 if (version <= DRW::AC1018) {
3857 newMaterial = DRW::MaterialByLayer;
3858 newShadowHandle = 0;
3859 }
3860 if (version <= DRW::AC1021) {
3861 newFullVisualStyleHandle = 0;
3862 newFaceVisualStyleHandle = 0;
3863 newEdgeVisualStyleHandle = 0;
3864 }
3865 auto readOffsetHandle = [&](dwgHandle& out) {
3866 return handleEndBit == std::numeric_limits<std::uint64_t>::max()
3867 ? readDwgHandleChecked(*buf, handle, true, out)
3868 : readBoundedDwgHandle(*buf, handleEndBit, handle, true, out);
3869 };
3870 auto readRawHandle = [&](dwgHandle& out) {
3871 return handleEndBit == std::numeric_limits<std::uint64_t>::max()
3872 ? readDwgHandleChecked(*buf, handle, false, out)
3873 : readBoundedDwgHandle(*buf, handleEndBit, handle, false, out);
3874 };
3875 auto readHandleList = [&]() {
3876 if (handleEndBit == std::numeric_limits<std::uint64_t>::max())
3877 return readDwgHandleList(*buf, handle, numReactors, false,
3878 &newReactorHandles);
3879 if (!dwgSafety::validReactorCount(numReactors))
3880 return false;
3881 std::vector<std::uint32_t> parsed;
3882 if (!DRW::reserve(parsed, numReactors))
3883 return false;
3884 for (std::int32_t i = 0; i < numReactors; ++i) {
3885 dwgHandle reactor;
3886 if (!readBoundedDwgHandle(*buf, handleEndBit, handle, false,
3887 reactor))
3888 return false;
3889 parsed.push_back(reactor.ref);
3890 }
3891 newReactorHandles = std::move(parsed);
3892 return true;
3893 };
3894
3895 if(ownerHandle){//entity are in block or in a polyline
3896 dwgHandle ownerH;
3897 if (!readOffsetHandle(ownerH))
3898 return false;
3899 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");
3900 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");
3901 newParentHandle = ownerH.ref;
3902 DRW_DBG("Block (parent) Handle: ")DRW_dbg::dbg("Block (parent) Handle: "); DRW_DBGHL(ownerH.code, ownerH.size, newParentHandle)DRW_dbg::dbgHL(ownerH.code, ownerH.size, newParentHandle); DRW_DBG("\n")DRW_dbg::dbg("\n");
3903 } else
3904 DRW_DBG("NO Block (parent) Handle\n")DRW_dbg::dbg("NO Block (parent) Handle\n");
3905
3906 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");
3907 if (!readHandleList())
3908 return false;
3909 if (xDictFlag !=1){//linetype in 2004 seems not have XDicObjH or NULL handle
3910 dwgHandle XDicObjH;
3911 if (!readRawHandle(XDicObjH))
3912 return false;
3913 newXDictHandle = XDicObjH.ref; // 2a.2: persist xdict
3914 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");
3915 }
3916 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");
3917
3918 // ENC's optional AcDbColor reference follows the common owner, reactor,
3919 // and xdictionary handles in R2004+ files. The dwgReader resolves this
3920 // handle against dbColorMap after parsing the complete entity.
3921 if (hasAcDbColorH && version > DRW::AC1015) {
3922 dwgHandle dbcH;
3923 if (!readOffsetHandle(dbcH))
3924 return false;
3925 newAcDbColorHandle = dbcH.ref;
3926 DRW_DBG(" AcDbColor Handle: ")DRW_dbg::dbg(" AcDbColor Handle: ");
3927 DRW_DBGHL(dbcH.code, dbcH.size, dbcH.ref)DRW_dbg::dbgHL(dbcH.code, dbcH.size, dbcH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3928 }
3929
3930 if (version < DRW::AC1015) {//R14-
3931 //layer handle
3932 if (!readOffsetHandle(newLayerH))
3933 return false;
3934 DRW_DBG(" layer Handle: ")DRW_dbg::dbg(" layer Handle: "); DRW_DBGHL(newLayerH.code, newLayerH.size, newLayerH.ref)DRW_dbg::dbgHL(newLayerH.code, newLayerH.size, newLayerH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3935 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");
3936 //lineType handle
3937 if(ltFlags == 3){
3938 if (!readOffsetHandle(newLTypeH))
3939 return false;
3940 DRW_DBG("linetype Handle: ")DRW_dbg::dbg("linetype Handle: "); DRW_DBGHL(newLTypeH.code, newLTypeH.size, newLTypeH.ref)DRW_dbg::dbgHL(newLTypeH.code, newLTypeH.size, newLTypeH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3941 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");
3942 }
3943 }
3944 if (version < DRW::AC1018) {//2000+
3945 if (haveNextLinks == 0) {
3946 dwgHandle nextLinkH;
3947 if (!readOffsetHandle(nextLinkH))
3948 return false;
3949 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");
3950 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");
3951 const std::uint32_t prevLink = nextLinkH.ref;
3952 if (!readOffsetHandle(nextLinkH))
3953 return false;
3954 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");
3955 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");
3956 newPrevEntLink = prevLink;
3957 newNextEntLink = nextLinkH.ref;
3958 } else {
3959 if (handle == 0 || handle == std::numeric_limits<std::uint32_t>::max())
3960 return false;
3961 newNextEntLink = handle + 1;
3962 newPrevEntLink = handle - 1;
3963 }
3964 }
3965 if (version > DRW::AC1015) {//2004+
3966 //Parses Bookcolor handle
3967 }
3968 if (version > DRW::AC1014) {//2000+
3969 //layer handle
3970 if (!readOffsetHandle(newLayerH))
3971 return false;
3972 DRW_DBG(" layer Handle: ")DRW_dbg::dbg(" layer Handle: "); DRW_DBGHL(newLayerH.code, newLayerH.size, newLayerH.ref)DRW_dbg::dbgHL(newLayerH.code, newLayerH.size, newLayerH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3973 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");
3974 //lineType handle
3975 if(ltFlags == 3){
3976 if (!readOffsetHandle(newLTypeH))
3977 return false;
3978 DRW_DBG("linetype Handle: ")DRW_dbg::dbg("linetype Handle: "); DRW_DBGHL(newLTypeH.code, newLTypeH.size, newLTypeH.ref)DRW_dbg::dbgHL(newLTypeH.code, newLTypeH.size, newLTypeH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3979 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");
3980 }
3981 }
3982 if (version > DRW::AC1014) {//2000+
3983 if (version > DRW::AC1018) {//2007+
3984 if (materialFlag == 3) {
3985 dwgHandle materialH;
3986 if (!readOffsetHandle(materialH))
3987 return false;
3988 newMaterial = materialH.ref;
3989 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");
3990 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");
3991 }
3992 if (shadowFlag == 3) {
3993 // AcDbShadow object handle is separate from the mode encoded
3994 // in shadowFlag. Keep it even though LibreCAD has no render
3995 // consumer for the shadow object itself.
3996 dwgHandle shadowH;
3997 if (!readOffsetHandle(shadowH))
3998 return false;
3999 newShadowHandle = shadowH.ref;
4000 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");
4001 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");
4002 }
4003 }
4004 if (plotFlags == 3) {
4005 dwgHandle plotStyleH;
4006 if (!readOffsetHandle(plotStyleH))
4007 return false;
4008 newPlotStyle = static_cast<int>(plotStyleH.ref);
4009 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");
4010 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");
4011 }
4012 if (version > DRW::AC1021) {//2010+ — §19.4.2: visual-style handles
4013 // Ground-truth: libreDWG common_entity_handle_data.spec lines
4014 // 141-150 and ODA spec v5.4.1 §19.4.2. Order matches: full,
4015 // face, edge — each conditional on its single-bit flag from
4016 // §19.4.1 (set in parseDwg above). All three are hard pointers
4017 // (libreDWG FIELD_HANDLE code 5), matching the existing
4018 // material/shadow/plotstyle handles in this block.
4019 if (hasFullVisualStyle) {
4020 dwgHandle h;
4021 if (!readOffsetHandle(h))
4022 return false;
4023 newFullVisualStyleHandle = h.ref;
4024 DRW_DBG(" full visual-style H: ")DRW_dbg::dbg(" full visual-style H: ");
4025 DRW_DBGHL(h.code, h.size, h.ref)DRW_dbg::dbgHL(h.code, h.size, h.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
4026 }
4027 if (hasFaceVisualStyle) {
4028 dwgHandle h;
4029 if (!readOffsetHandle(h))
4030 return false;
4031 newFaceVisualStyleHandle = h.ref;
4032 DRW_DBG(" face visual-style H: ")DRW_dbg::dbg(" face visual-style H: ");
4033 DRW_DBGHL(h.code, h.size, h.ref)DRW_dbg::dbgHL(h.code, h.size, h.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
4034 }
4035 if (hasEdgeVisualStyle) {
4036 dwgHandle h;
4037 if (!readOffsetHandle(h))
4038 return false;
4039 newEdgeVisualStyleHandle = h.ref;
4040 DRW_DBG(" edge visual-style H: ")DRW_dbg::dbg(" edge visual-style H: ");
4041 DRW_DBGHL(h.code, h.size, h.ref)DRW_dbg::dbgHL(h.code, h.size, h.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
4042 }
4043 }
4044 }
4045 const int rb = buf->numRemainingBytes();
4046 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");
4047 if (rb > 4) { // 2-byte CRC + slack
4048 DRW_DBG("\n*** parseDwgEntHandle leftover ")DRW_dbg::dbg("\n*** parseDwgEntHandle leftover ");
4049 DRW_DBG(rb)DRW_dbg::dbg(rb);
4050 DRW_DBG(" bytes; entity handle ")DRW_dbg::dbg(" bytes; entity handle ");
4051 DRW_DBGH(handle)DRW_dbg::dbgH(handle);
4052 DRW_DBG(" oType ")DRW_dbg::dbg(" oType ");
4053 DRW_DBG(oType)DRW_dbg::dbg(oType);
4054 DRW_DBG(" — possible bit-stream misalignment ***\n")DRW_dbg::dbg(" — possible bit-stream misalignment ***\n");
4055 }
4056 if (!buf->isGood()
4057 || (handleEndBit != std::numeric_limits<std::uint64_t>::max()
4058 && currentDwgBit(buf) > handleEndBit))
4059 return false;
4060
4061 acDbColorHandle = newAcDbColorHandle;
4062 parentHandle = newParentHandle;
4063 reactorHandles = std::move(newReactorHandles);
4064 xDictHandle = newXDictHandle;
4065 layerH = newLayerH;
4066 lTypeH = newLTypeH;
4067 nextEntLink = newNextEntLink;
4068 prevEntLink = newPrevEntLink;
4069 material = newMaterial;
4070 plotStyle = newPlotStyle;
4071 shadow = newShadow;
4072 if (version <= DRW::AC1018) {
4073 materialFlag = 0;
4074 shadowFlag = 0;
4075 }
4076 shadowHandle = newShadowHandle;
4077 if (version <= DRW::AC1021) {
4078 hasFullVisualStyle = false;
4079 hasFaceVisualStyle = false;
4080 hasEdgeVisualStyle = false;
4081 }
4082 fullVisualStyleHandle = newFullVisualStyleHandle;
4083 faceVisualStyleHandle = newFaceVisualStyleHandle;
4084 edgeVisualStyleHandle = newEdgeVisualStyleHandle;
4085 // R2007+ separates this tail from the object data stream. Earlier typed
4086 // parsers may consume inline handles but cannot give raw metadata an
4087 // independently bounded common-link proof.
4088 commonLinkTailValidated = version > DRW::AC1018;
4089 return true;
4090}
4091
4092bool DRW_Entity::parseDwgCommon(DRW::Version version, dwgBuffer *buf,
4093 std::uint32_t bs) {
4094 if (buf == nullptr)
4095 return false;
4096 // R2007 (AC1021) omits the footer when an object has no string stream;
4097 // the concrete readers using this helper have no detached text fields.
4098 if (version <= DRW::AC1021)
4099 return parseDwg(version, buf, nullptr, bs);
4100
4101 // Keep the publishing cursor transactional: a missing string footer is
4102 // legal for body-only R2007-era inputs and for the low-level entity test
4103 // API, but must not leave the cursor half-consumed before that fallback.
4104 dwgBuffer bodyProbe = buf->forkIndependent();
4105 dwgBuffer stringProbe = bodyProbe.forkIndependent();
4106 if (parseDwg(version, &bodyProbe, &stringProbe, bs)) {
4107 *buf = bodyProbe;
4108 return true;
4109 }
4110 return parseDwg(version, buf, nullptr, bs);
4111}
4112
4113bool DRW_ProxyEntity::parseDwg(DRW::Version version, dwgBuffer *buf,
4114 std::uint32_t bs) {
4115 if (buf == nullptr || version < DRW::AC1012)
4116 return false;
4117
4118 DRW_Entity::reset();
4119 eType = DRW::PROXYENTITY;
4120 m_hasProxyClassId = false;
4121 m_proxyClassId = 0;
4122 m_hasProxyCarrierId = false;
4123 m_proxyCarrierId = 0;
4124 m_proxySubclass.clear();
4125 m_hasProxyDrawingFormat = false;
4126 m_proxyDrawingFormat = 0;
4127 m_proxyDwgVersion = 0;
4128 m_proxyMaintenanceVersion = 0;
4129 m_hasFromDxf = false;
4130 m_fromDxf = false;
4131 m_hasProxyGraphicsByteSize = false;
4132 m_proxyGraphicsByteSize = 0;
4133 m_hasEntityDataBitSize = false;
4134 m_entityData.clear();
4135 m_entityDataBitSize = 0;
4136 m_hasUnknownDataByteSize = false;
4137 m_unknownDataByteSize = 0;
4138 m_unknownData.clear();
4139 m_objectIdRefs.clear();
4140
4141 dwgBuffer stringBuffer = *buf;
4142 dwgBuffer *stringBuf = version > DRW::AC1018 ? &stringBuffer : buf;
4143 if (!DRW_Entity::parseDwg(version, buf, stringBuf, bs))
4144 return false;
4145
4146 std::uint64_t dataEndBit = 0;
4147 std::uint64_t stringEndBit = 0;
4148 if (!entityBodyDataEndBit(*stringBuf, version, objSize, dataEndBit,
4149 &stringEndBit)
4150 || dataEndBit > proxyEntityEndBit(*buf, 0))
4151 return false;
4152
4153 bool present = false;
4154 std::int32_t value = 0;
4155 if (!readProxyEntityOptionalBitLong(*buf, dataEndBit, present, value))
4156 return false;
4157 if (present) {
4158 m_hasProxyCarrierId = true;
4159 m_proxyCarrierId = value;
4160 }
4161
4162 if (version >= DRW::AC1018
4163 && proxyEntityHasBits(*stringBuf, stringEndBit, 2)) {
4164 if (!readBoundedVariableText(*stringBuf, stringEndBit, version,
4165 m_proxySubclass))
4166 return false;
4167 }
4168
4169 if (version >= DRW::AC1032) {
4170 bool hasDwgVersion = false;
4171 bool hasMaintenanceVersion = false;
4172 std::int32_t dwgVersion = 0;
4173 std::int32_t maintenanceVersion = 0;
4174 if (!readProxyEntityOptionalBitLong(*buf, dataEndBit,
4175 hasDwgVersion, dwgVersion)
4176 || !readProxyEntityOptionalBitLong(*buf, dataEndBit,
4177 hasMaintenanceVersion,
4178 maintenanceVersion)) {
4179 return false;
4180 }
4181 if (hasDwgVersion || hasMaintenanceVersion) {
4182 m_hasProxyDrawingFormat = true;
4183 m_proxyDwgVersion = static_cast<std::uint16_t>(dwgVersion);
4184 m_proxyMaintenanceVersion =
4185 static_cast<std::uint16_t>(maintenanceVersion);
4186 m_proxyDrawingFormat =
4187 (static_cast<std::uint32_t>(m_proxyMaintenanceVersion) << 16u)
4188 | m_proxyDwgVersion;
4189 }
4190 } else if (!readProxyEntityOptionalBitLong(*buf, dataEndBit,
4191 present, value)) {
4192 return false;
4193 } else if (present) {
4194 m_hasProxyDrawingFormat = true;
4195 m_proxyDrawingFormat = static_cast<std::uint32_t>(value);
4196 m_proxyDwgVersion = static_cast<std::uint16_t>(value);
4197 m_proxyMaintenanceVersion = static_cast<std::uint16_t>(
4198 static_cast<std::uint32_t>(value) >> 16u);
4199 }
4200
4201 if (version >= DRW::AC1015 && proxyEntityHasBits(*buf, dataEndBit, 1)) {
4202 if (!readBoundedBit(*buf, dataEndBit, m_fromDxf))
4203 return false;
4204 m_hasFromDxf = true;
4205 }
4206
4207 const std::uint64_t payloadStartBit = currentDwgBit(buf);
4208 if (payloadStartBit > dataEndBit
4209 || !copyDwgBitRange(*buf, payloadStartBit, dataEndBit, m_entityData)) {
4210 return false;
4211 }
4212 m_entityDataBitSize = static_cast<std::uint32_t>(
4213 dataEndBit - payloadStartBit);
4214
4215 dwgBuffer handleBuffer = *buf;
4216 if (!handleBuffer.setPosition(dataEndBit >> 3))
4217 return false;
4218 handleBuffer.setBitPos(static_cast<std::uint8_t>(dataEndBit & 7u));
4219 if (!handleBuffer.isGood())
4220 return false;
4221 std::uint64_t handleEndBit = 0;
4222 if (!dwgHandleStreamEndBit(*buf, version, objSize, bs, handleEndBit)
4223 || !DRW_Entity::parseDwgEntHandle(version, &handleBuffer, true,
4224 handleEndBit))
4225 return false;
4226 while (proxyEntityHasBits(handleBuffer, handleEndBit, 8)) {
4227 dwgHandle rawHandle;
4228 dwgHandle resolvedHandle;
4229 if (!readBoundedDwgObjectId(handleBuffer, handleEndBit, handle,
4230 rawHandle, resolvedHandle))
4231 return false;
4232 if (rawHandle.code == 0 && rawHandle.ref64 == 0)
4233 break;
4234
4235 DRW_ProxyObjectIdRef ref;
4236 ref.m_dxfCode = proxyEntityDxfCode(rawHandle.code);
4237 ref.m_handleCode = rawHandle.code;
4238 ref.m_handle = resolvedHandle.ref64;
4239 ref.m_rawHandle = rawHandle.ref64;
4240 m_objectIdRefs.push_back(ref);
4241 }
4242
4243 return buf->isGood() && stringBuf->isGood() && handleBuffer.isGood();
4244}
4245
4246bool DRW_Point::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
4247 switch (code) {
4248 case 10:
4249 basePoint.x = reader->getDouble();
4250 break;
4251 case 20:
4252 basePoint.y = reader->getDouble();
4253 break;
4254 case 30:
4255 basePoint.z = reader->getDouble();
4256 break;
4257 case 39:
4258 thickness = reader->getDouble();
4259 break;
4260 case 50:
4261 // DXF code 50 is in degrees; the field is radians (matching the DWG
4262 // path, drw_entities.cpp:1787). degrees -> radians is /ARAD (x pi/180);
4263 // the prior *ARAD only canceled with the writer's /ARAD on DXF->DXF and
4264 // corrupted the value for DXF->DWG. ARAD = 180/pi.
4265 xAxisAngle = reader->getDouble() / ARAD57.29577951308232; // DXF degrees -> radians
4266 break;
4267 case 210:
4268 haveExtrusion = true;
4269 extPoint.x = reader->getDouble();
4270 break;
4271 case 220:
4272 extPoint.y = reader->getDouble();
4273 break;
4274 case 230:
4275 extPoint.z = reader->getDouble();
4276 break;
4277 default:
4278 return DRW_Entity::parseCode(code, reader);
4279 }
4280
4281 return true;
4282}
4283
4284// ---------------------------------------------------------------------------
4285// Phase 4a (drafted 2026-05-15)
4286// ---------------------------------------------------------------------------
4287// `DRW_Entity::encodeDwgCommon` and `encodeDwgEntHandle` mirror the common
4288// entity-header fragments parsed above. Version gates intentionally follow
4289// the DWG reader so newer headers retain their reference flags and handles.
4290//
4291// Discarded fields (Risk 4i):
4292// - haveNextLinks B — we emit 1 (no prev/next chain).
4293// - none of the common color side channels are discarded for R2004+;
4294// ENC names remain the byte-oriented TV fields defined by the DWG
4295// common-entity specification, while ordinary entity text uses strBuf.
4296//
4297// We still emit these fixed defaults:
4298// - entmode = 2 (modelspace, no owner-handle in stream — caller can
4299// override before calling encodeDwgCommon if entity needs an owner).
4300// - numReactors = 0 (reactor handles are emitted by the handle phase when
4301// present).
4302// - ltFlags = 0 (BYLAYER); plotFlags follows plotStyle.
4303// - invisibleFlag = 0 (visible)
4304//
4305// Caller must:
4306// - Pre-populate `eType`, `handle`, `color`, `ltypeScale`, `lWeight`,
4307// `layerH.ref` (handle of the layer this entity belongs to).
4308// - The body emit between encodeDwgCommon and encodeDwgEntHandle is
4309// per-entity (3BD basePoint for Point, etc.).
4310
4311// Phase-2a kill switch for the full common-entity-header write contract
4312// (entity reactors/xdict/EED/visibility/entmode emission). Default ON. The
4313// emission is gated by DATA PRESENCE (empty reactorHandles/extData + visible
4314// == today's hardcoded zeros), so flipping this OFF restores the legacy
4315// byte-identical output as an emergency escape hatch. The per-field emission
4316// arms land in 2a.1..2a.5; this scaffolding commit changes no bytes.
4317#ifndef LIBDXFRW_FULL_COMMON_HEADER1
4318#define LIBDXFRW_FULL_COMMON_HEADER1 1
4319#endif
4320
4321namespace {
4322
4323const DRW_Entity::PendingHandleRef* findEedWriteRef(
4324 const std::vector<DRW_Entity::PendingHandleRef>& refs,
4325 std::size_t index) {
4326 const auto it = std::find_if(refs.cbegin(), refs.cend(),
4327 [index](const auto& ref) {
4328 return ref.indexInExtData == index;
4329 });
4330 if (it == refs.cend())
4331 return nullptr;
4332 return &*it;
4333}
4334
4335bool parseEedHexHandle(const std::string& text, std::uint64_t& handle) {
4336 if (text.empty())
4337 return false;
4338 std::size_t index = 0;
4339 if (text.size() > 2 && text[0] == '0'
4340 && (text[1] == 'x' || text[1] == 'X')) {
4341 index = 2;
4342 }
4343 if (index == text.size())
4344 return false;
4345
4346 std::uint64_t parsed = 0;
4347 for (; index < text.size(); ++index) {
4348 const unsigned char character =
4349 static_cast<unsigned char>(text[index]);
4350 std::uint8_t digit = 0;
4351 if (character >= '0' && character <= '9')
4352 digit = static_cast<std::uint8_t>(character - '0');
4353 else if (character >= 'a' && character <= 'f')
4354 digit = static_cast<std::uint8_t>(character - 'a' + 10);
4355 else if (character >= 'A' && character <= 'F')
4356 digit = static_cast<std::uint8_t>(character - 'A' + 10);
4357 else
4358 return false;
4359 if (parsed > (std::numeric_limits<std::uint64_t>::max() - digit) / 16U)
4360 return false;
4361 parsed = parsed * 16U + digit;
4362 }
4363 handle = parsed;
4364 return true;
4365}
4366
4367void putEedRawHandleBE(dwgBufferW& buffer, std::uint64_t handle) {
4368 for (int index = 7; index >= 0; --index) {
4369 buffer.putRawChar8(static_cast<std::uint8_t>(
4370 (handle >> (index * 8)) & 0xFFU));
4371 }
4372}
4373
4374bool encodeEedItem(DRW::Version version, const DRW_Variant& value,
4375 dwgBufferW& textEncoder, dwgBufferW& output,
4376 std::uint64_t resolvedLayerHandle,
4377 std::uint16_t codePage) {
4378 const int groupCode = value.code();
4379 switch (groupCode) {
4380 case 1000: {
4381 if (value.type() != DRW_Variant::STRING || value.content.s == nullptr)
4382 return false;
4383 const std::string encoded = textEncoder.encodeTextAreaString(
4384 *value.content.s, version > DRW::AC1018);
4385 if (version > DRW::AC1018) {
4386 if ((encoded.size() & 1U) != 0
4387 || encoded.size() / 2U > std::numeric_limits<std::uint16_t>::max()) {
4388 return false;
4389 }
4390 output.putRawChar8(0);
4391 output.putRawShort16(static_cast<std::uint16_t>(encoded.size() / 2U));
4392 } else {
4393 if (encoded.size() > std::numeric_limits<std::uint8_t>::max())
4394 return false;
4395 output.putRawChar8(0);
4396 output.putRawChar8(static_cast<std::uint8_t>(encoded.size()));
4397 output.putBERawShort16(codePage);
4398 }
4399 output.putBytes(reinterpret_cast<const std::uint8_t*>(encoded.data()),
4400 encoded.size());
4401 return output.isGood();
4402 }
4403 case 1002: {
4404 if (value.type() != DRW_Variant::STRING || value.content.s == nullptr
4405 || (*value.content.s != "{" && *value.content.s != "}")) {
4406 return false;
4407 }
4408 output.putRawChar8(2);
4409 output.putRawChar8(*value.content.s == "{" ? 0 : 1);
4410 return output.isGood();
4411 }
4412 case 1003:
4413 // Most significant byte first, as AutoCAD writes the layer handle.
4414 if (value.type() != DRW_Variant::STRING || resolvedLayerHandle == 0)
4415 return false;
4416 output.putRawChar8(3);
4417 putEedRawHandleBE(output, resolvedLayerHandle);
4418 return output.isGood();
4419 case 1004: {
4420 std::vector<std::uint8_t> decoded;
4421 const auto* bytes = value.type() == DRW_Variant::BINARY ? value.binary() : nullptr;
4422 // DXF imports retain binary chunks as hexadecimal strings.
4423 if (value.type() == DRW_Variant::STRING && value.content.s != nullptr) {
4424 if (value.content.s->size() > 2U * std::numeric_limits<std::uint8_t>::max()
4425 || !decodeHexBytes(*value.content.s, decoded)) {
4426 return false;
4427 }
4428 bytes = &decoded;
4429 }
4430 if (bytes == nullptr || bytes->size() > std::numeric_limits<std::uint8_t>::max()) {
4431 return false;
4432 }
4433 output.putRawChar8(4);
4434 output.putRawChar8(static_cast<std::uint8_t>(bytes->size()));
4435 output.putBytes(bytes->data(), bytes->size());
4436 return output.isGood();
4437 }
4438 case 1005: {
4439 if (value.type() != DRW_Variant::STRING || value.content.s == nullptr)
4440 return false;
4441 std::uint64_t handle = 0;
4442 if (!parseEedHexHandle(*value.content.s, handle))
4443 return false;
4444 output.putRawChar8(5);
4445 putEedRawHandleBE(output, handle);
4446 return output.isGood();
4447 }
4448 case 1010:
4449 case 1011:
4450 case 1012:
4451 case 1013: {
4452 if (value.type() != DRW_Variant::COORD || value.coord() == nullptr)
4453 return false;
4454 const DRW_Coord& coordinate = *value.coord();
4455 if (!std::isfinite(coordinate.x) || !std::isfinite(coordinate.y)
4456 || !std::isfinite(coordinate.z)) {
4457 return false;
4458 }
4459 output.putRawChar8(static_cast<std::uint8_t>(groupCode - 1000));
4460 output.putRawDouble(coordinate.x);
4461 output.putRawDouble(coordinate.y);
4462 output.putRawDouble(coordinate.z);
4463 return output.isGood();
4464 }
4465 case 1040:
4466 case 1041:
4467 case 1042:
4468 if (value.type() != DRW_Variant::DOUBLE || !std::isfinite(value.d_val()))
4469 return false;
4470 output.putRawChar8(static_cast<std::uint8_t>(groupCode - 1000));
4471 output.putRawDouble(value.d_val());
4472 return output.isGood();
4473 case 1070:
4474 // DXF input stores the raw 16-bit field in an int32_t. Accept the
4475 // full unsigned bit pattern before writing it back to DWG.
4476 if (value.type() != DRW_Variant::INTEGER
4477 || value.i_val() < std::numeric_limits<std::int16_t>::min()
4478 || value.i_val() > std::numeric_limits<std::uint16_t>::max()) {
4479 return false;
4480 }
4481 output.putRawChar8(70);
4482 output.putRawShort16(static_cast<std::uint16_t>(value.i_val()));
4483 return output.isGood();
4484 case 1071: {
4485 std::int64_t integer = 0;
4486 if (value.type() == DRW_Variant::INTEGER)
4487 integer = value.i_val();
4488 else if (value.type() == DRW_Variant::INTEGER64)
4489 integer = value.i64_val();
4490 else
4491 return false;
4492 if (integer < std::numeric_limits<std::int32_t>::min()
4493 || integer > std::numeric_limits<std::int32_t>::max()) {
4494 return false;
4495 }
4496 output.putRawChar8(71);
4497 output.putRawLong32(static_cast<std::uint32_t>(
4498 static_cast<std::int32_t>(integer)));
4499 return output.isGood();
4500 }
4501 default:
4502 return false;
4503 }
4504}
4505
4506} // namespace
4507
4508bool DRW_Entity::encodeDwgEed(
4509 DRW::Version version, const std::vector<DRW_Variant*>& extData,
4510 const std::vector<PendingHandleRef>& appIdRefs,
4511 const std::vector<PendingHandleRef>& layerRefs,
4512 std::uint16_t codePage, dwgBufferW& output) {
4513 if (extData.empty()) {
4514 output.putBitShort(0);
4515 return output.isGood();
4516 }
4517 if (extData.size() > dwgSafety::MaxEedTotalItems)
4518 return false;
4519
4520 std::size_t currentAppStart = 0;
4521 std::uint32_t appHandle = 0;
4522 std::uint32_t groupDepth = 0;
4523 std::uint32_t chunkCount = 0;
4524 // The current application's data is left out of the object.
4525 bool leavingOut = false;
4526 dwgBufferW payload;
4527 const auto flush = [&]() {
4528 if (payload.data().empty() || payload.data().size()
4529 > std::numeric_limits<std::uint16_t>::max()
4530 || appHandle == 0 || ++chunkCount > dwgSafety::MaxEedChunks) {
4531 return false;
4532 }
4533 output.putBitShort(static_cast<std::uint16_t>(payload.data().size()));
4534 dwgHandle appId;
4535 appId.code = 5;
4536 appId.ref = appHandle;
4537 output.putHandle(appId);
4538 output.putBytes(payload.data().data(), payload.data().size());
4539 payload.reset();
4540 return output.isGood();
4541 };
4542
4543 for (std::size_t index = 0; index < extData.size(); ++index) {
4544 const DRW_Variant* value = extData[index];
4545 if (value == nullptr)
4546 return false;
4547 if (value->code() == 1001) {
4548 if (index != currentAppStart && !leavingOut) {
4549 if (groupDepth != 0 || !flush())
4550 return false;
4551 }
4552 const PendingHandleRef* appIdRef = findEedWriteRef(appIdRefs, index);
4553 if (appIdRef == nullptr
4554 || (appIdRef->handleRef == 0 && !appIdRef->leftOut)) {
4555 return false;
4556 }
4557 appHandle = appIdRef->handleRef;
4558 currentAppStart = index + 1;
4559 groupDepth = 0;
4560 leavingOut = appIdRef->leftOut;
4561 continue;
4562 }
4563 if (index < currentAppStart)
4564 return false;
4565 if (leavingOut)
4566 continue;
4567 if (value->code() == 1002) {
4568 if (value->type() != DRW_Variant::STRING || value->content.s == nullptr)
4569 return false;
4570 if (*value->content.s == "{") {
4571 ++groupDepth;
4572 } else if (*value->content.s == "}") {
4573 if (groupDepth == 0)
4574 return false;
4575 --groupDepth;
4576 } else {
4577 return false;
4578 }
4579 }
4580
4581 std::uint64_t layerHandle = 0;
4582 if (value->code() == 1003) {
4583 const PendingHandleRef* layerRef = findEedWriteRef(layerRefs, index);
4584 if (layerRef == nullptr)
4585 return false;
4586 layerHandle = layerRef->useRawHandleRef
4587 ? layerRef->rawHandleRef : layerRef->handleRef;
4588 if (layerHandle == 0)
4589 return false;
4590 }
4591 dwgBufferW item;
4592 if (!encodeEedItem(version, *value, output, item, layerHandle,
4593 codePage))
4594 return false;
4595 if (item.data().size() > std::numeric_limits<std::uint16_t>::max())
4596 return false;
4597 if (!payload.data().empty()
4598 && payload.data().size() + item.data().size()
4599 > std::numeric_limits<std::uint16_t>::max()) {
4600 if (groupDepth != 0 || !flush())
4601 return false;
4602 }
4603 payload.putBytes(item.data().data(), item.data().size());
4604 }
4605 if (currentAppStart == 0)
4606 return false;
4607 if (!leavingOut && (groupDepth != 0 || !flush()))
4608 return false;
4609 output.putBitShort(0);
4610 return output.isGood();
4611}
4612
4613bool DRW_Entity::encodeDwgCommon(DRW::Version version, dwgBufferW *buf,
4614 dwgBufferW *strBuf) {
4615 (void)strBuf; // ENC names are byte-oriented TV fields in the data stream
4616 if (buf == nullptr || reactorHandles.size() > dwgSafety::MaxReactorCount ||
4617 (version != DRW::AC1015 && version != DRW::AC1018 &&
4618 version != DRW::AC1021 &&
4619 version != DRW::AC1024 && version != DRW::AC1027 &&
4620 version != DRW::AC1032)) return false;
4621
4622 // Object type: BS for AC1015/AC1018, OT for AC1024+.
4623 buf->putObjType(version, static_cast<std::uint16_t>(oType));
4624
4625 // objSize stub — back-patched for AC1015/AC1018 only. AC1024 derives
4626 // objSize from the body buffer size, so no RL is emitted.
4627 if (version < DRW::AC1024) {
4628 buf->putRawLong32(0);
4629 }
4630
4631 // Own handle: code 0 per spec §20.4.1.
4632 dwgHandle ownH;
4633 ownH.code = 0;
4634 ownH.ref = handle;
4635 ownH.size = 0;
4636 if (handle != 0) {
4637 std::uint32_t t = handle;
4638 while (t != 0) { t >>= 8; ++ownH.size; }
4639 }
4640 buf->putHandle(ownH);
4641
4642 std::vector<DRW_Variant*> eedValues;
4643 try {
4644 eedValues.reserve(extData.size());
4645 for (const auto& value : extData)
4646 eedValues.push_back(value.get());
4647 } catch (...) {
4648 return false;
4649 }
4650 if (!encodeDwgEed(version, eedValues, dwgEedAppIdWriteRefs,
4651 dwgEedLayerWriteRefs, dwgEedCodePage, *buf))
4652 return false;
4653
4654 // Proxy graphics lives in the common data stream. Preserve it when the
4655 // entity has a bounded byte stream; it is the cached fallback geometry
4656 // used for unrecognised custom/proxy entities.
4657 if (proxyGraphics.size()
4658 > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()
4659 / 8)) {
4660 return false;
4661 }
4662 const bool hasProxyGraphics = !proxyGraphics.empty();
4663 buf->putBit(hasProxyGraphics);
4664 if (hasProxyGraphics) {
4665 const auto graphDataSize = static_cast<std::uint64_t>(
4666 proxyGraphics.size());
4667 if (version >= DRW::AC1024)
4668 buf->putBitLongLong(graphDataSize);
4669 else
4670 buf->putRawLong32(static_cast<std::uint32_t>(graphDataSize));
4671 buf->putBytes(reinterpret_cast<const std::uint8_t*>(
4672 proxyGraphics.data()),
4673 proxyGraphics.size());
4674 if (!buf->isGood())
4675 return false;
4676 }
4677
4678 const bool hasOwner = parentHandle != DRW::NoHandle;
4679 // entmode BB (ODA §20.4.1 / Open Design FE):
4680 // 0 = owner handle follows in the handle stream
4681 // 1 = paperspace entity without owner-relative handle
4682 // 2 = modelspace entity without owner-relative handle
4683 // Prefer owner when present; otherwise honor DRW_Entity::space.
4684 std::uint8_t entmode = 2;
4685 if (hasOwner)
4686 entmode = 0;
4687 else if (space == DRW::PaperSpace)
4688 entmode = 1;
4689 buf->put2Bits(entmode);
4690
4691 // numReactors (BL per spec §20.4.1). 2a.2: emit the real count; empty
4692 // reactorHandles → 0 → byte-identical to legacy.
4693#if LIBDXFRW_FULL_COMMON_HEADER1
4694 buf->putBitLong(static_cast<std::int32_t>(reactorHandles.size()));
4695#else
4696 buf->putBitLong(0);
4697#endif
4698
4699 // R2004/R2010 (AC1018, AC1024): reader reads xDictFlag bit (version > AC1015)
4700 // then forces haveNextLinks=1 (no bit in stream). We always emit
4701 // xDictFlag=0 (xdic-present) so the reader reads exactly one xdic handle
4702 // in the handle section — we emit the real handle when xDictHandle!=0 and
4703 // a null handle otherwise. This keeps the empty case byte-identical to the
4704 // legacy path (bit 0 + null handle) while round-tripping a real xdict.
4705 // R2000 (AC1015): no xDictFlag bit; reader's xDictFlag stays 0 so it ALWAYS
4706 // reads an xdic handle — same emit rule applies.
4707 // R2013+ (AC1027+): xDictFlag is followed by the DataStorage flag.
4708 if (version == DRW::AC1015) {
4709 buf->putBit(m_dwgWriteExplicitLinks ? 0 : 1);
4710 } else {
4711 buf->putBit(0); // xDictFlag=0 (xdic present; real-or-null handle follows)
4712 if (version > DRW::AC1024) {
4713 // libreDWG common_entity_data.spec — R2013+ has_ds_data (B). libdxfrw
4714 // never inlines an ACIS SAB datastore, so emit hasDsData (default 0).
4715 // The old code emitted literal 1 (mislabeled haveNextLinks), falsely
4716 // advertising an SAB blob and risking misparse in strict readers.
4717 buf->putBit(hasDsData);
4718 }
4719 }
4720
4721 // ENC color. R2004+ retains the true-color, transparency, DBCOLOR, and
4722 // byte-oriented color/book-name side channels; AC1015 falls back to BS.
4723 UTF8STRINGstd::string encColorName = colorName;
4724 UTF8STRINGstd::string encBookName;
4725 const std::string::size_type separator = encColorName.find('$');
4726 if (separator != std::string::npos) {
4727 encBookName = encColorName.substr(0, separator);
4728 encColorName.erase(0, separator + 1);
4729 }
4730 buf->putEnColor(version, static_cast<std::uint16_t>(color), color24,
4731 encColorName, encBookName,
4732 static_cast<std::uint32_t>(transparency),
4733 version >= DRW::AC1018 && hasAcDbColorH);
4734
4735 // ltypeScale BD.
4736 buf->putBitDouble(ltypeScale);
4737
4738 // ltFlags BB: 0=BYLAYER, 1=BYBLOCK, 2=CONTINUOUS, 3=lTypeH present.
4739 // Prefer an already-set ltFlags; otherwise derive from lineType / lTypeH.
4740 {
4741 auto upper = [](std::string s) {
4742 for (char& c : s)
4743 c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
4744 return s;
4745 };
4746 std::uint8_t flags = ltFlags;
4747 if (flags > 3)
4748 flags = 0;
4749 if (flags == 0 && lTypeH.ref != 0)
4750 flags = 3;
4751 if (flags == 0) {
4752 const std::string lt = upper(lineType);
4753 if (lt.empty() || lt == "BYLAYER")
4754 flags = 0;
4755 else if (lt == "BYBLOCK")
4756 flags = 1;
4757 else if (lt == "CONTINUOUS")
4758 flags = 2;
4759 else
4760 flags = 3; // named linetype — handle required
4761 }
4762 ltFlags = flags;
4763 }
4764 buf->put2Bits(ltFlags);
4765 // plotFlags BB: a nonzero plot-style handle requires the hard-pointer
4766 // handle in the common handle stream. Keep the default zero path
4767 // unchanged for entities without an override.
4768 plotFlags = plotStyle > 0 ? 3 : 0;
4769 buf->put2Bits(plotFlags);
4770
4771 // R2010 (AC1024): materialFlag BB + shadowFlag RC (version > AC1018).
4772 if (version > DRW::AC1018) {
4773 materialFlag = material == DRW::MaterialByLayer ? 0 : 3;
4774 shadowFlag = static_cast<std::uint8_t>(shadow) & 0x3;
4775 buf->put2Bits(materialFlag);
4776 buf->putRawChar8(shadowFlag);
4777 }
4778
4779 // R2010 (AC1024): three visual-style flag bits (version > AC1021).
4780 if (version > DRW::AC1021) {
4781 hasFullVisualStyle = fullVisualStyleHandle != 0;
4782 hasFaceVisualStyle = faceVisualStyleHandle != 0;
4783 hasEdgeVisualStyle = edgeVisualStyleHandle != 0;
4784 buf->putBit(hasFullVisualStyle);
4785 buf->putBit(hasFaceVisualStyle);
4786 buf->putBit(hasEdgeVisualStyle);
4787 }
4788
4789 // invisibleFlag BS (DXF 60). 2a.1: emit from `visible` (bit 0 = invisible)
4790 // instead of a hardcoded 0. visible==true → 0 → byte-identical to legacy.
4791#if LIBDXFRW_FULL_COMMON_HEADER1
4792 buf->putBitShort(visible ? 0 : 1);
4793#else
4794 buf->putBitShort(0);
4795#endif
4796
4797 // lWeight RC (0 = byLayer per DRW_LW_Conv).
4798 buf->putRawChar8(static_cast<std::uint8_t>(lWeight));
4799
4800 return buf->isGood();
4801}
4802
4803bool DRW_Entity::encodeDwgEntHandle(DRW::Version version, dwgBufferW *buf,
4804 dwgBufferW *handleBuf) {
4805 if (buf == nullptr || reactorHandles.size() > dwgSafety::MaxReactorCount ||
4806 (version != DRW::AC1015 && version != DRW::AC1018 &&
4807 version != DRW::AC1021 &&
4808 version != DRW::AC1024 && version != DRW::AC1027 &&
4809 version != DRW::AC1032)) return false;
4810
4811 // For AC1024, handles are directed to handleBuf (the separate handle section);
4812 // for AC1015/AC1018, handles go into buf alongside the data.
4813 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
4814
4815 // Owner handle is present only when encodeDwgCommon emitted entmode=0.
4816 if (parentHandle != DRW::NoHandle) {
4817 dwgHandle owner;
4818 owner.code = 4; // soft pointer owner, read via getOffsetHandle()
4819 owner.ref = parentHandle;
4820 owner.size = 0;
4821 std::uint32_t t = parentHandle;
4822 while (t != 0) { t >>= 8; ++owner.size; }
4823 hb->putHandle(owner);
4824 }
4825
4826 // Reactor handles (2a.2): emitted before xdic, one per numReactors written
4827 // in the DATA section, as ABSOLUTE handles (reader uses getHandle()). Empty
4828 // reactorHandles → nothing emitted → byte-identical to legacy.
4829#if LIBDXFRW_FULL_COMMON_HEADER1
4830 for (std::uint32_t ref : reactorHandles) {
4831 dwgHandle rh;
4832 rh.code = 4; // soft pointer
4833 rh.ref = ref;
4834 rh.size = 0;
4835 if (ref != 0) { std::uint32_t t = ref; while (t != 0) { t >>= 8; ++rh.size; } }
4836 hb->putHandle(rh);
4837 }
4838#endif
4839
4840 // XDic handle — xDictFlag=0 in the DATA section means the reader reads one
4841 // XDicObj handle here: emit the real handle when xDictHandle!=0, else the
4842 // null handle (matching the legacy byte-for-byte for the empty case).
4843 dwgHandle xDic;
4844 xDic.code = 3;
4845#if LIBDXFRW_FULL_COMMON_HEADER1
4846 xDic.ref = xDictHandle;
4847 xDic.size = 0;
4848 if (xDictHandle != 0) {
4849 std::uint32_t t = xDictHandle; while (t != 0) { t >>= 8; ++xDic.size; }
4850 }
4851#else
4852 xDic.ref = 0;
4853 xDic.size = 0;
4854#endif
4855 hb->putHandle(xDic);
4856
4857 // R2004+ stores the optional AcDbColor reference after the common owner,
4858 // reactor, and xdictionary handles. Keep this order aligned with the
4859 // reader and the cross-project handle-stream contract.
4860 if (version > DRW::AC1015 && hasAcDbColorH)
4861 putHardPointerHandle(hb, acDbColorHandle);
4862
4863 if (version == DRW::AC1015 && m_dwgWriteExplicitLinks) {
4864 // Fixed widths let the writer finalize owner chains after all entities
4865 // are known without moving object frames or their handle-map offsets.
4866 hb->putFixedHandle(4, 4, prevEntLink);
4867 hb->putFixedHandle(4, 4, nextEntLink);
4868 }
4869
4870 // Layer handle (R2000+ unconditional). Hard pointer.
4871 dwgHandle lH;
4872 lH.code = layerH.ref == 0 ? 0 : 5; // 5 = hard pointer for layer ref
4873 lH.ref = layerH.ref;
4874 lH.size = 0;
4875 if (lH.ref != 0) {
4876 std::uint32_t t = lH.ref;
4877 while (t != 0) { t >>= 8; ++lH.size; }
4878 }
4879 hb->putHandle(lH);
4880
4881 // ltFlags=3 → lTypeH (hard pointer, code 5) present; else omit.
4882 if (ltFlags == 3) {
4883 dwgHandle ltH;
4884 ltH.code = lTypeH.ref == 0 ? 0 : 5;
4885 ltH.ref = lTypeH.ref;
4886 ltH.size = 0;
4887 if (ltH.ref != 0) {
4888 std::uint32_t t = ltH.ref;
4889 while (t != 0) { t >>= 8; ++ltH.size; }
4890 }
4891 hb->putHandle(ltH);
4892 }
4893 auto putHardPointer = [hb](std::uint32_t ref) {
4894 dwgHandle h;
4895 h.code = ref == 0 ? 0 : 5;
4896 h.ref = ref;
4897 h.size = 0;
4898 if (ref != 0) {
4899 std::uint32_t t = ref;
4900 while (t != 0) {
4901 t >>= 8;
4902 ++h.size;
4903 }
4904 }
4905 hb->putHandle(h);
4906 };
4907
4908 if (version > DRW::AC1018) {
4909 if (materialFlag == 3)
4910 putHardPointer(material);
4911 if (shadowFlag == 3)
4912 putHardPointer(shadowHandle);
4913 }
4914 if (plotFlags == 3)
4915 putHardPointer(static_cast<std::uint32_t>(plotStyle));
4916 if (version > DRW::AC1021) {
4917 if (hasFullVisualStyle)
4918 putHardPointer(fullVisualStyleHandle);
4919 if (hasFaceVisualStyle)
4920 putHardPointer(faceVisualStyleHandle);
4921 if (hasEdgeVisualStyle)
4922 putHardPointer(edgeVisualStyleHandle);
4923 }
4924
4925 return buf->isGood() && hb->isGood();
4926}
4927
4928bool DRW_Point::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
4929 (void)bs; (void)strBuf;
4930 oType = 27; // POINT class id — see dwgreader.cpp:1111 dispatch
4931 if (!encodeDwgCommon(version, buf)) return false;
4932
4933 // Point body — mirror of DRW_Point::parseDwg below.
4934 buf->putBitDouble(basePoint.x);
4935 buf->putBitDouble(basePoint.y);
4936 buf->putBitDouble(basePoint.z);
4937 buf->putThickness(thickness, /*b_R2000_style=*/true);
4938 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
4939 buf->putBitDouble(xAxisAngle); // ODA §20.4.31 code 50
4940
4941 return encodeDwgEntHandle(version, buf, handleBuf);
4942}
4943
4944void DRW_Point::resetDwgState() {
4945 DRW_Entity::reset();
4946 basePoint = DRW_Coord{0.0, 0.0, 0.0};
4947 thickness = 0.0;
4948 extPoint = DRW_Coord{0.0, 0.0, 1.0};
4949 xAxisAngle = 0.0;
4950}
4951
4952bool DRW_Point::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
4953 resetDwgState();
4954 dwgBuffer *sourceBuf = buf;
4955 auto fail = [this, sourceBuf]() {
4956 if (sourceBuf != nullptr)
4957 sourceBuf->invalidate();
4958 resetDwgState();
4959 return false;
4960 };
4961 if (sourceBuf == nullptr)
4962 return fail();
4963
4964 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
4965 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
4966 || !bodyProbe.isGood())
4967 return fail();
4968 const std::uint64_t bodyEndBit = dwgDataEndBit;
4969 DRW_Coord parsedBasePoint;
4970 DRW_Coord parsedExtrusion;
4971 double parsedThickness = 0.0;
4972 double parsedXAxisAngle = 0.0;
4973 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedBasePoint)
4974 || !readBoundedThickness(bodyProbe, bodyEndBit,
4975 version > DRW::AC1014, parsedThickness)
4976 || !readBoundedExtrusion(bodyProbe, bodyEndBit,
4977 version > DRW::AC1014, parsedExtrusion)
4978 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedXAxisAngle)
4979 || !std::isfinite(parsedBasePoint.x)
4980 || !std::isfinite(parsedBasePoint.y)
4981 || !std::isfinite(parsedBasePoint.z)
4982 || !std::isfinite(parsedThickness)
4983 || !std::isfinite(parsedExtrusion.x)
4984 || !std::isfinite(parsedExtrusion.y)
4985 || !std::isfinite(parsedExtrusion.z)
4986 || !std::isfinite(parsedXAxisAngle))
4987 return fail();
4988
4989 std::uint64_t handleEndBit = 0;
4990 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
4991 handleEndBit))
4992 return fail();
4993 dwgBuffer handleProbe = bodyProbe.forkIndependent();
4994 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
4995 handleEndBit)
4996 || !handleProbe.isGood())
4997 return fail();
4998
4999 basePoint = parsedBasePoint;
5000 thickness = parsedThickness;
5001 extPoint = parsedExtrusion;
5002 xAxisAngle = parsedXAxisAngle;
5003 *sourceBuf = handleProbe;
5004 return true;
5005}
5006
5007void DRW_Line::resetDwgState() {
5008 DRW_Point::resetDwgState();
5009 secPoint = DRW_Coord{0.0, 0.0, 0.0};
5010}
5011
5012bool DRW_Line::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
5013 switch (code) {
5014 case 11:
5015 secPoint.x = reader->getDouble();
5016 break;
5017 case 21:
5018 secPoint.y = reader->getDouble();
5019 break;
5020 case 31:
5021 secPoint.z = reader->getDouble();
5022 break;
5023 default:
5024 return DRW_Point::parseCode(code, reader);
5025 }
5026
5027 return true;
5028}
5029
5030bool DRW_Line::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5031 (void)bs; (void)strBuf;
5032 oType = 19; // LINE class id — see dwgreader.cpp:1105
5033 if (!encodeDwgCommon(version, buf)) return false;
5034
5035 // R2000+ Line body — zIsZero shortcut: if both z's are 0, omit the
5036 // z fields entirely. Reader reads `zIsZero` first, then RD x +
5037 // DD secX (default = x), RD y + DD secY (default = y), and
5038 // conditionally RD z + DD secZ. Our putDefaultDouble always emits
5039 // the full RD via code 0b11; reader's getDefaultDouble with code
5040 // 0b11 returns the raw double.
5041 bool zIsZero = (basePoint.z == 0.0 && secPoint.z == 0.0);
5042 buf->putBit(zIsZero ? 1 : 0);
5043 buf->putRawDouble(basePoint.x);
5044 buf->putDefaultDouble(basePoint.x, secPoint.x);
5045 buf->putRawDouble(basePoint.y);
5046 buf->putDefaultDouble(basePoint.y, secPoint.y);
5047 if (!zIsZero) {
5048 buf->putRawDouble(basePoint.z);
5049 buf->putDefaultDouble(basePoint.z, secPoint.z);
5050 }
5051 buf->putThickness(thickness, /*b_R2000_style=*/true);
5052 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
5053
5054 return encodeDwgEntHandle(version, buf, handleBuf);
5055}
5056
5057bool DRW_3DLine::parseDwg(DRW::Version version, dwgBuffer *buf,
5058 std::uint32_t bs) {
5059 resetDwgState();
5060 dwgBuffer *sourceBuf = buf;
5061 auto fail = [this, sourceBuf]() {
5062 if (sourceBuf != nullptr)
5063 sourceBuf->invalidate();
5064 resetDwgState();
5065 return false;
5066 };
5067 if (sourceBuf == nullptr)
5068 return fail();
5069
5070 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
5071 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
5072 || !bodyProbe.isGood())
5073 return fail();
5074
5075 const std::uint64_t bodyEndBit = dwgDataEndBit;
5076 double values[10]{};
5077 for (double& value : values) {
5078 if (!readBoundedRawDouble(bodyProbe, bodyEndBit, value)
5079 || !std::isfinite(value))
5080 return fail();
5081 }
5082
5083 std::uint64_t handleEndBit = 0;
5084 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
5085 handleEndBit))
5086 return fail();
5087 dwgBuffer handleProbe = bodyProbe.forkIndependent();
5088 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
5089 handleEndBit)
5090 || !handleProbe.isGood())
5091 return fail();
5092
5093 basePoint = DRW_Coord{values[0], values[1], values[2]};
5094 secPoint = DRW_Coord{values[3], values[4], values[5]};
5095 extPoint = DRW_Coord{values[6], values[7], values[8]};
5096 thickness = values[9];
5097 *sourceBuf = handleProbe;
5098 return true;
5099}
5100
5101bool DRW_3DLine::encodeDwg(DRW::Version version, dwgBufferW *buf,
5102 std::uint32_t bs, dwgBufferW *strBuf,
5103 dwgBufferW *handleBuf) {
5104 (void)bs;
5105 if (version < DRW::AC1015 || buf == nullptr)
5106 return false;
5107
5108 // 3DLINE is a modern custom class. Its class ordinal is writer-local,
5109 // while the body remains the fixed raw-double layout used by the reader.
5110 oType = kDwgClassNum;
5111 if (!encodeDwgCommon(version, buf, strBuf))
5112 return false;
5113
5114 for (const double coordinate : {basePoint.x, basePoint.y, basePoint.z,
5115 secPoint.x, secPoint.y, secPoint.z,
5116 extPoint.x, extPoint.y, extPoint.z,
5117 thickness}) {
5118 buf->putRawDouble(coordinate);
5119 }
5120
5121 return encodeDwgEntHandle(version, buf, handleBuf);
5122}
5123
5124bool DRW_Circle::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5125 (void)bs; (void)strBuf;
5126 oType = 18; // CIRCLE class id — see dwgreader.cpp:1099
5127 if (!encodeDwgCommon(version, buf)) return false;
5128
5129 // Circle body — mirror of DRW_Circle::parseDwg.
5130 buf->putBitDouble(basePoint.x);
5131 buf->putBitDouble(basePoint.y);
5132 buf->putBitDouble(basePoint.z);
5133 buf->putBitDouble(radious);
5134 buf->putThickness(thickness, /*b_R2000_style=*/true);
5135 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
5136
5137 return encodeDwgEntHandle(version, buf, handleBuf);
5138}
5139
5140bool DRW_Ray::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5141 (void)bs; (void)strBuf;
5142 // Ray = 40, Xline = 41 — derive from runtime type so DRW_Xline can
5143 // share this encoder (it inherits from DRW_Ray).
5144 oType = (eType == DRW::XLINE) ? 41 : 40;
5145 if (!encodeDwgCommon(version, buf)) return false;
5146
5147 // 3 BD basePoint + 3 BD vector — same layout as parseDwg.
5148 buf->putBitDouble(basePoint.x);
5149 buf->putBitDouble(basePoint.y);
5150 buf->putBitDouble(basePoint.z);
5151 buf->putBitDouble(secPoint.x);
5152 buf->putBitDouble(secPoint.y);
5153 buf->putBitDouble(secPoint.z);
5154
5155 return encodeDwgEntHandle(version, buf, handleBuf);
5156}
5157
5158bool DRW_Trace::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5159 (void)bs; (void)strBuf;
5160 oType = 32; // TRACE = 32 — see dwgreader.cpp:1317
5161 if (!encodeDwgCommon(version, buf)) return false;
5162
5163 // Trace body — mirror of parseDwg. Note the unusual layout:
5164 // thickness FIRST, then elevation (basePoint.z) as BD, then 4
5165 // corners as 2RD (z values share basePoint.z).
5166 buf->putThickness(thickness, /*b_R2000_style=*/true);
5167 buf->putBitDouble(basePoint.z);
5168 buf->putRawDouble(basePoint.x);
5169 buf->putRawDouble(basePoint.y);
5170 buf->putRawDouble(secPoint.x);
5171 buf->putRawDouble(secPoint.y);
5172 buf->putRawDouble(thirdPoint.x);
5173 buf->putRawDouble(thirdPoint.y);
5174 buf->putRawDouble(fourPoint.x);
5175 buf->putRawDouble(fourPoint.y);
5176 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
5177
5178 return encodeDwgEntHandle(version, buf, handleBuf);
5179}
5180
5181bool DRW_Spline::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5182 (void)bs; (void)strBuf;
5183 if (buf == nullptr || !validatePayloadFields())
5184 return false;
5185 oType = 36; // SPLINE class id — see dwgreader.cpp:1329
5186 if (!encodeDwgCommon(version, buf)) return false;
5187 encodeDwgSplineBody(version, buf);
5188 return encodeDwgEntHandle(version, buf, handleBuf);
5189}
5190
5191// Spline body encode: the scenario/degree/knots/ctrl/fit section, WITHOUT the
5192// leading encodeDwgCommon or the trailing encodeDwgEntHandle. Factored out so
5193// DRW_Helix::encodeDwg can reuse the identical payload (Phase 8a-1).
5194// Omits the DXF-only flag70/extrusion (210-230) which the DWG stream never
5195// carries here.
5196void DRW_Spline::encodeDwgSplineBody(DRW::Version version, dwgBufferW *buf) const {
5197 // Scenario:
5198 // 1 = control-point / rational / planar (uses knots + control + weights)
5199 // 2 = fit-point (uses fit points + tangents + tolerance)
5200 // When both lists are populated (e.g. DXF-sourced splines), prefer scenario 1
5201 // (ctrl + knots) and drop the fit list from the DWG stream — scenario 1 has no
5202 // fit-point section, so writing both would corrupt all subsequent entities.
5203 const bool hasFit = !fitlist.empty();
5204 const bool hasCtrl = !controllist.empty();
5205 std::int32_t scenario = (hasFit && !hasCtrl) ? 2 : 1;
5206 if (m_scenario == 1 && hasCtrl) {
5207 scenario = 1;
5208 } else if (m_scenario == 2 && hasFit) {
5209 scenario = 2;
5210 }
5211 buf->putBitLong(scenario);
5212 if (version > DRW::AC1024) {
5213 // splFlag1 bit 0: method = fit points; bit 2: closed;
5214 // bit 3: knotParam participates in R2013+ scenario selection.
5215 std::int32_t splFlag1 = m_splineFlags1;
5216 splFlag1 &= ~(kSplineFlagMethodFitPoints | kSplineFlagUseKnotParameter | kSplineFlagClosed);
5217 if (scenario == 2) {
5218 splFlag1 |= kSplineFlagMethodFitPoints | kSplineFlagUseKnotParameter;
5219 if (flags & 0x01) splFlag1 |= kSplineFlagClosed;
5220 } else {
5221 if (flags & 0x01) splFlag1 |= kSplineFlagClosed;
5222 }
5223 buf->putBitLong(splFlag1);
5224 std::int32_t knotParam = m_knotParam;
5225 if (scenario == 1) {
5226 knotParam = kSplineKnotParamCustom;
5227 } else if (knotParam == kSplineKnotParamCustom) {
5228 knotParam = 0;
5229 }
5230 buf->putBitLong(knotParam);
5231 }
5232 buf->putBitLong(static_cast<std::int32_t>(degree));
5233
5234 if (scenario == 2) {
5235 buf->putBitDouble(tolfit);
5236 buf->put3BitDouble(tgStart);
5237 buf->put3BitDouble(tgEnd);
5238 const std::int32_t nFit = static_cast<std::int32_t>(fitlist.size());
5239 buf->putBitLong(nFit);
5240 } else {
5241 // scenario == 1
5242 // Reader at parseDwg reads three flag bits in this order:
5243 // rational bit (flags bit 2 → 0x04)
5244 // closed bit (flags bit 0 → 0x01)
5245 // periodic bit (flags bit 1 → 0x02)
5246 const bool hasNonDefaultWeights = std::any_of(weightlist.begin(), weightlist.end(), differsFromUnitWeight);
5247 buf->putBit(((flags & 0x4) || hasNonDefaultWeights) ? 1 : 0); // rational
5248 buf->putBit((flags & 0x1) ? 1 : 0); // closed
5249 buf->putBit((flags & 0x2) ? 1 : 0); // periodic
5250 buf->putBitDouble(tolknot);
5251 buf->putBitDouble(tolcontrol);
5252 const std::int32_t nKnots = static_cast<std::int32_t>(knotslist.size());
5253 const std::int32_t nCtrl = static_cast<std::int32_t>(controllist.size());
5254 buf->putBitLong(nKnots);
5255 buf->putBitLong(nCtrl);
5256 // weight bit: caller populates weightlist when each control point
5257 // has a non-default weight (NURBS conics).
5258 bool hasWeights = !weightlist.empty();
5259 buf->putBit(hasWeights ? 1 : 0);
5260 }
5261
5262 // Data sections are scenario-gated to avoid stream corruption:
5263 // parseDwg reads knots+ctrl only for scenario 1, fit only for scenario 2.
5264 if (scenario == 1) {
5265 for (double k : knotslist) buf->putBitDouble(k);
5266 for (size_t i = 0; i < controllist.size(); ++i) {
5267 buf->put3BitDouble(*controllist[i]);
5268 if (!weightlist.empty()) {
5269 double w = (i < weightlist.size()) ? weightlist[i] : 1.0;
5270 buf->putBitDouble(w);
5271 }
5272 }
5273 } else {
5274 for (const auto& fp : fitlist) buf->put3BitDouble(*fp);
5275 }
5276}
5277
5278// DRW_Helix::encodeDwg — spline body (oType = HELIX class 503) + AcDbHelix
5279// trailer, then the common entity handle data. Trailer field order MUST match
5280// DRW_Helix::parseDwg (Phase 8a-1).
5281bool DRW_Helix::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5282 (void)bs; (void)strBuf;
5283 const auto finite = [](const DRW_Coord& point) {
5284 return std::isfinite(point.x) && std::isfinite(point.y)
5285 && std::isfinite(point.z);
5286 };
5287 if (buf == nullptr || !validatePayloadFields()
5288 || !finite(axisBasePt) || !finite(startPt) || !finite(axisVector)
5289 || !std::isfinite(radius) || !std::isfinite(turns)
5290 || !std::isfinite(turnHeight))
5291 return false;
5292 oType = kDwgClassNum; // HELIX custom class 503
5293 if (!encodeDwgCommon(version, buf)) return false;
5294 encodeDwgSplineBody(version, buf);
5295
5296 // AcDbHelix trailer (same order as parseDwg):
5297 buf->putBitLong(m_majorVersion);
5298 buf->putBitLong(m_maintVersion);
5299 buf->put3BitDouble(axisBasePt);
5300 buf->put3BitDouble(startPt);
5301 buf->put3BitDouble(axisVector);
5302 buf->putBitDouble(radius);
5303 buf->putBitDouble(turns);
5304 buf->putBitDouble(turnHeight);
5305 buf->putBit(handedness ? 1 : 0);
5306 buf->putRawChar8(static_cast<std::uint8_t>(constraintType));
5307
5308 return encodeDwgEntHandle(version, buf, handleBuf);
5309}
5310
5311bool DRW_MText::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5312 (void)bs;
5313 oType = 44; // MTEXT class id — see dwgreader.cpp:1215
5314 if (!encodeDwgCommon(version, buf)) return false;
5315
5316 // R2000/R2004/R2010 MTEXT body — mirror of DRW_MText::parseDwg.
5317 buf->put3BitDouble(basePoint); // insertion
5318 buf->put3BitDouble(extPoint); // extrusion
5319 buf->put3BitDouble(secPoint); // X-axis dir
5320 buf->putBitDouble(widthscale); // rect width
5321 if (version > DRW::AC1018) {
5322 buf->putBitDouble(0.0); // rect height, R2007+
5323 }
5324 buf->putBitDouble(height); // text height
5325 buf->putBitShort(static_cast<std::uint16_t>(textgen)); // attachment
5326 buf->putBitShort(static_cast<std::uint16_t>(alignH)); // drawing dir
5327 buf->putBitDouble(0.0); // ext_ht (extents height; undocumented)
5328 buf->putBitDouble(0.0); // ext_wid (extents width; undocumented)
5329 // For AC1024: text goes to string buffer; for AC1015/AC1018: inline.
5330 (strBuf ? strBuf : buf)->putVariableText(version, text);
5331 // R2000+ extras:
5332 buf->putBitShort(linespacingStyle); // linespacing style BS 73
5333 buf->putBitDouble(interlin); // linespacing factor BD
5334 buf->putBit(0); // unknown bit
5335 if (version > DRW::AC1015) { // R2004+: background flags BL
5336 buf->putBitLong(m_backgroundFlags);
5337 if ((m_backgroundFlags & 0x01) || (version >= DRW::AC1032 && (m_backgroundFlags & 0x10))) {
5338 buf->putBitDouble(m_backgroundScale); // BitDouble (matches the read fix)
5339 buf->putCmColor(version, static_cast<std::uint16_t>(m_backgroundColor));
5340 buf->putBitLong(m_backgroundTransparency);
5341 }
5342 }
5343 if (version >= DRW::AC1032) {
5344 buf->putBit(m_r2018IsNotAnnotative ? 1 : 0);
5345 if (m_r2018IsNotAnnotative
5346 && !putDwgMTextR2018Block(buf, dwgMTextR2018BlockOf(*this)))
5347 return false;
5348 }
5349
5350 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
5351
5352 // styleH — hard pointer to STYLE table record (default STANDARD).
5353 dwgBufferW *hb = handleBuf ? handleBuf : buf;
5354 putHardPointerHandle(hb, (styleH.ref == 0) ? 0x13 : styleH.ref);
5355 if (version >= DRW::AC1032 && m_r2018IsNotAnnotative)
5356 putHardPointerHandle(hb, (m_r2018AppIdHandle == 0) ? 0x14 : m_r2018AppIdHandle);
5357 return buf->isGood() && hb->isGood();
5358}
5359
5360bool DRW_Insert::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5361 (void)bs; (void)strBuf;
5362 if (buf == nullptr)
5363 return false;
5364 const bool hasAttrib = !attribHandles.empty();
5365 if (!hasAttrib) {
5366 if (seqendH.ref != DRW::NoHandle)
5367 return false;
5368 } else {
5369 if (seqendH.ref == DRW::NoHandle)
5370 return false;
5371 if (version < DRW::AC1018) {
5372 if (attribHandles.size() != 2 ||
5373 (attribHandles.front().ref == DRW::NoHandle) !=
5374 (attribHandles.back().ref == DRW::NoHandle)) {
5375 return false;
5376 }
5377 } else if (std::any_of(attribHandles.cbegin(), attribHandles.cend(),
5378 [](const dwgHandle& handle) {
5379 return handle.ref == DRW::NoHandle;
5380 })) {
5381 return false;
5382 }
5383 }
5384 if (attribHandles.size() > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()))
5385 return false;
5386
5387 // 2b.6: emit MINSERT (oType 8) when a column/row grid is present;
5388 // otherwise a plain INSERT (oType 7). The reader keys the grid block off
5389 // oType==8 (parseDwg :3189).
5390 oType = isMInsert() ? 8 : 7;
5391 if (!encodeDwgCommon(version, buf)) return false;
5392
5393 // INSERT body — mirror of DRW_Insert::parseDwg for R2000.
5394 buf->putBitDouble(basePoint.x);
5395 buf->putBitDouble(basePoint.y);
5396 buf->putBitDouble(basePoint.z);
5397
5398 // dataFlags: pick the most compact form based on actual scales.
5399 // 3 → all scales default to 1.0 (no emit)
5400 // 2 → uniform scale (xscale RD only; yscale=zscale=xscale)
5401 // 1 → xscale defaults to 1, yscale/zscale as DD against xscale
5402 // 0 → xscale RD; yscale/zscale as DD against xscale
5403 if (sameStoredDouble(xscale, 1.0) && sameStoredDouble(yscale, 1.0)
5404 && sameStoredDouble(zscale, 1.0)) {
5405 buf->put2Bits(3);
5406 } else if (sameStoredDouble(xscale, yscale) && sameStoredDouble(yscale, zscale)) {
5407 buf->put2Bits(2);
5408 buf->putRawDouble(xscale);
5409 } else if (sameStoredDouble(xscale, 1.0)) {
5410 // xscale is exactly 1.0 (parseDwg leaves it at its 1.0 default and
5411 // never reads it in this branch), so it can be omitted; y and z are
5412 // independent and go through the same DD-against-1.0 path parseDwg
5413 // reads them with.
5414 buf->put2Bits(1);
5415 buf->putDefaultDouble(1.0, yscale);
5416 buf->putDefaultDouble(1.0, zscale);
5417 } else {
5418 // Use dataFlags=0 (general case): RD x + DD y + DD z.
5419 buf->put2Bits(0);
5420 buf->putRawDouble(xscale);
5421 buf->putDefaultDouble(xscale, yscale);
5422 buf->putDefaultDouble(xscale, zscale);
5423 }
5424
5425 buf->putBitDouble(angle); // radians
5426 buf->putExtrusion(extPoint, /*b_R2000_style=*/false);
5427 buf->putBit(hasAttrib ? 1 : 0);
5428 if (hasAttrib && version > DRW::AC1015)
5429 buf->putBitLong(static_cast<std::int32_t>(attribHandles.size()));
5430 // hasAttrib==0 ⇒ the SINCE-R2004 num_owned BL is absent (parse :3184), so
5431 // the MINSERT grid (oType==8) follows the hasAttrib bit directly. Field
5432 // order mirrors parseDwg :3190-3193 (colcount BS, rowcount BS, colspace BD,
5433 // rowspace BD) and libreDWG dwg.spec num_cols/num_rows/col_spacing/row_spacing.
5434 if (oType == 8) { // MINSERT grid
5435 buf->putBitShort(static_cast<std::uint16_t>(colcount));
5436 buf->putBitShort(static_cast<std::uint16_t>(rowcount));
5437 buf->putBitDouble(colspace);
5438 buf->putBitDouble(rowspace);
5439 }
5440
5441 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
5442
5443 // BLOCK_RECORD hard pointer.
5444 dwgHandle bhH;
5445 bhH.code = (blockRecH.ref == 0) ? 0 : 5;
5446 bhH.ref = blockRecH.ref;
5447 bhH.size = 0;
5448 if (bhH.ref != 0) {
5449 std::uint32_t t = bhH.ref;
5450 while (t != 0) { t >>= 8; ++bhH.size; }
5451 }
5452 (handleBuf ? handleBuf : buf)->putHandle(bhH);
5453
5454 if (hasAttrib) {
5455 dwgBufferW *hb = handleBuf ? handleBuf : buf;
5456 if (version < DRW::AC1018) {
5457 for (const dwgHandle& attribHandle : attribHandles)
5458 putDwgReference(hb, DRW::DwgSoftPointer, attribHandle.ref);
5459 } else {
5460 const std::uint8_t attributeCode =
5461 oType == 8 ? DRW::DwgSoftPointer : DRW::DwgHardOwnership;
5462 for (const dwgHandle& attribHandle : attribHandles)
5463 putDwgReference(hb, attributeCode, attribHandle.ref);
5464 }
5465 putDwgReference(hb, DRW::DwgHardOwnership, seqendH.ref);
5466 }
5467 return buf->isGood() && (handleBuf == nullptr || handleBuf->isGood());
5468}
5469
5470bool DRW_3Dface::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5471 (void)bs; (void)strBuf;
5472 oType = 28; // 3DFACE class id — see dwgreader.cpp:1237
5473 if (!encodeDwgCommon(version, buf)) return false;
5474
5475 // R2000+ 3DFACE body — mirror of parseDwg's z_is_zero / has_no_flag
5476 // optimization. Reader checks `invisibleflag != NoEdge`; if NoEdge,
5477 // emit has_no_flag=1 to suppress the BS read.
5478 bool hasNoFlag = (invisibleflag == /*NoEdge*/0);
5479 bool zIsZero = (basePoint.z == 0.0);
5480 buf->putBit(hasNoFlag ? 1 : 0);
5481 buf->putBit(zIsZero ? 1 : 0);
5482 buf->putRawDouble(basePoint.x);
5483 buf->putRawDouble(basePoint.y);
5484 if (!zIsZero) buf->putRawDouble(basePoint.z);
5485 buf->putDefaultDouble(basePoint.x, secPoint.x);
5486 buf->putDefaultDouble(basePoint.y, secPoint.y);
5487 buf->putDefaultDouble(basePoint.z, secPoint.z);
5488 buf->putDefaultDouble(secPoint.x, thirdPoint.x);
5489 buf->putDefaultDouble(secPoint.y, thirdPoint.y);
5490 buf->putDefaultDouble(secPoint.z, thirdPoint.z);
5491 buf->putDefaultDouble(thirdPoint.x, fourPoint.x);
5492 buf->putDefaultDouble(thirdPoint.y, fourPoint.y);
5493 buf->putDefaultDouble(thirdPoint.z, fourPoint.z);
5494 if (!hasNoFlag) buf->putBitShort(static_cast<std::uint16_t>(invisibleflag));
5495
5496 return encodeDwgEntHandle(version, buf, handleBuf);
5497}
5498
5499bool DRW_Solid::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5500 (void)bs; (void)strBuf;
5501 oType = 31; // SOLID class id — see dwgreader.cpp:1305
5502 if (!encodeDwgCommon(version, buf)) return false;
5503
5504 // Same body layout as TRACE (4 corners + extrusion). Duplicated
5505 // here rather than delegating to DRW_Trace::encodeDwg because that
5506 // hardcodes oType=32.
5507 buf->putThickness(thickness, /*b_R2000_style=*/true);
5508 buf->putBitDouble(basePoint.z);
5509 buf->putRawDouble(basePoint.x);
5510 buf->putRawDouble(basePoint.y);
5511 buf->putRawDouble(secPoint.x);
5512 buf->putRawDouble(secPoint.y);
5513 buf->putRawDouble(thirdPoint.x);
5514 buf->putRawDouble(thirdPoint.y);
5515 buf->putRawDouble(fourPoint.x);
5516 buf->putRawDouble(fourPoint.y);
5517 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
5518
5519 return encodeDwgEntHandle(version, buf, handleBuf);
5520}
5521
5522bool DRW_LWPolyline::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5523 (void)bs; (void)strBuf;
5524 if (buf == nullptr || !validatePayloadFields())
5525 return false;
5526 oType = 77; // LWPOLYLINE class id — see dwgreader.cpp:1202
5527 if (!encodeDwgCommon(version, buf)) return false;
5528
5529 // DRW_LWPolyline::flags carries DXF-side bits (1=closed, 128=plinegen).
5530 // DWG-side flags are different: they signal which optional fields are
5531 // present. Per parseDwg, bit 9 (0x200) = closed, bit 8 (0x100) =
5532 // plinegen. Build the DWG flags from the DXF flags plus the data.
5533 std::uint16_t dwgFlags = 0;
5534 if (flags & 1) dwgFlags |= 0x200; // closed
5535 if (flags & 128) dwgFlags |= 0x100; // plinegen
5536 if (thickness != 0.0) dwgFlags |= 0x2;
5537 if (width != 0.0) dwgFlags |= 0x4;
5538 if (elevation != 0.0) dwgFlags |= 0x8;
5539 bool defaultExt = (extPoint.x == 0.0 && extPoint.y == 0.0 && extPoint.z == 1.0);
5540 if (!defaultExt) dwgFlags |= 0x1;
5541 // Detect per-vertex bulge / width data.
5542 bool anyBulge = false;
5543 bool anyWidth = false;
5544 bool anyVertexId = false;
5545 for (const auto& v : vertlist) {
5546 if (v && v->bulge != 0.0) anyBulge = true;
5547 if (v && (v->stawidth != 0.0 || v->endwidth != 0.0)) anyWidth = true;
5548 if (v && v->identifier != 0) anyVertexId = true;
5549 }
5550 if (anyBulge) dwgFlags |= 0x10;
5551 if (anyWidth) dwgFlags |= 0x20;
5552 if (version > DRW::AC1021 && anyVertexId) dwgFlags |= 0x400;
5553
5554 buf->putBitShort(dwgFlags);
5555 if (dwgFlags & 0x4) buf->putBitDouble(width);
5556 if (dwgFlags & 0x8) buf->putBitDouble(elevation);
5557 if (dwgFlags & 0x2) buf->putBitDouble(thickness);
5558 if (dwgFlags & 0x1) buf->putExtrusion(extPoint, /*b_R2000_style=*/false);
5559
5560 const std::int32_t numVerts = static_cast<std::int32_t>(vertlist.size());
5561 buf->putBitLong(numVerts);
5562 if (dwgFlags & 0x10) buf->putBitLong(numVerts); // bulgesnum
5563 if (version > DRW::AC1021 && (dwgFlags & 0x400)) {
5564 buf->putBitLong(numVerts); // vertexIdCount
5565 }
5566 if (dwgFlags & 0x20) buf->putBitLong(numVerts); // widthsnum
5567
5568 if (numVerts > 0) {
5569 // First vertex as 2RD. Subsequent vertices as 2DD relative to
5570 // the previous, with putDefaultDouble always emitting code 0b11
5571 // (full RD); the reader's getDefaultDouble returns the raw value.
5572 buf->putRawDouble(vertlist[0]->x);
5573 buf->putRawDouble(vertlist[0]->y);
5574 for (size_t i = 1; i < vertlist.size(); ++i) {
5575 buf->putDefaultDouble(vertlist[i-1]->x, vertlist[i]->x);
5576 buf->putDefaultDouble(vertlist[i-1]->y, vertlist[i]->y);
5577 }
5578 if (dwgFlags & 0x10) {
5579 for (const auto& v : vertlist)
5580 buf->putBitDouble(v->bulge);
5581 }
5582 if (version > DRW::AC1021 && (dwgFlags & 0x400)) {
5583 for (const auto& v : vertlist)
5584 buf->putBitLong(static_cast<std::int32_t>(v->identifier));
5585 }
5586 if (dwgFlags & 0x20) {
5587 for (const auto& v : vertlist) {
5588 buf->putBitDouble(v->stawidth);
5589 buf->putBitDouble(v->endwidth);
5590 }
5591 }
5592 }
5593
5594 return encodeDwgEntHandle(version, buf, handleBuf);
5595}
5596
5597bool DRW_Block::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5598 (void)bs;
5599 // BLOCK = 4, ENDBLK = 5 per DWG spec. isEnd controls which.
5600 oType = isEnd ? 5 : 4;
5601 if (!encodeDwgCommon(version, buf)) return false;
5602 if (!isEnd) {
5603 (strBuf ? strBuf : buf)->putVariableText(version, name);
5604 }
5605 if (version > DRW::AC1018) {
5606 buf->putBit(0); // unknown bit (R2007+: always 0 for our output)
5607 }
5608 return encodeDwgEntHandle(version, buf, handleBuf);
5609}
5610
5611bool DRW_Text::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5612 (void)bs;
5613 oType = 1; // TEXT class id — see dwgreader.cpp:1208
5614 if (!encodeDwgCommon(version, buf)) return false;
5615
5616 // R2000+ TEXT body — mirror of DRW_Text::parseDwg. We emit
5617 // data_flags=0 so the reader sees every optional field rather than
5618 // substituting defaults — keeps the encoder simple, costs ~30 bytes
5619 // per TEXT versus the most compressed form.
5620 buf->putRawChar8(0); // data_flags=0
5621 buf->putRawDouble(basePoint.z); // elevation RD
5622 buf->putRawDouble(basePoint.x); // insertion 2RD
5623 buf->putRawDouble(basePoint.y);
5624 buf->putDefaultDouble(basePoint.x, secPoint.x); // alignment 2DD
5625 buf->putDefaultDouble(basePoint.y, secPoint.y);
5626 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
5627 buf->putThickness(thickness, /*b_R2000_style=*/true);
5628 buf->putRawDouble(oblique); // oblique angle
5629 // Angle: struct holds degrees; on-disk format is radians. Reader
5630 // does `angle *= ARAD` (180/π) after read. Inverse: divide here.
5631 buf->putRawDouble(angle / ARAD57.29577951308232);
5632 buf->putRawDouble(height); // text height
5633 buf->putRawDouble(widthscale); // width factor
5634 (strBuf ? strBuf : buf)->putVariableText(version, text); // text string
5635 buf->putBitShort(static_cast<std::uint16_t>(textgen));
5636 buf->putBitShort(static_cast<std::uint16_t>(alignH));
5637 buf->putBitShort(static_cast<std::uint16_t>(alignV));
5638
5639 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
5640
5641 // styleH — hard pointer to STYLE table record. Default points at
5642 // the STANDARD textstyle (handle 0x13) if caller hasn't set one.
5643 dwgHandle sH;
5644 std::uint32_t sref = (styleH.ref == 0) ? 0x13 : styleH.ref;
5645 sH.code = 5; // hard pointer
5646 sH.ref = sref;
5647 sH.size = 0;
5648 if (sref != 0) {
5649 std::uint32_t t = sref;
5650 while (t != 0) { t >>= 8; ++sH.size; }
5651 }
5652 (handleBuf ? handleBuf : buf)->putHandle(sH);
5653 return true;
5654}
5655
5656bool DRW_Ellipse::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5657 (void)bs; (void)strBuf;
5658 oType = 35; // ELLIPSE class id — see dwgreader.cpp:1117
5659 if (!encodeDwgCommon(version, buf)) return false;
5660
5661 // Ellipse body — mirror of DRW_Ellipse::parseDwg.
5662 buf->put3BitDouble(basePoint); // center
5663 buf->put3BitDouble(secPoint); // major axis vector
5664 buf->put3BitDouble(extPoint); // extrusion
5665 buf->putBitDouble(ratio); // minor/major ratio
5666 buf->putBitDouble(staparam); // start parameter
5667 buf->putBitDouble(endparam); // end parameter
5668
5669 return encodeDwgEntHandle(version, buf, handleBuf);
5670}
5671
5672bool DRW_Arc::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5673 (void)bs; (void)strBuf;
5674 oType = 17; // ARC class id — see dwgreader.cpp:1093
5675 if (!encodeDwgCommon(version, buf)) return false;
5676
5677 // Arc body — Circle body + 2 BD angles.
5678 buf->putBitDouble(basePoint.x);
5679 buf->putBitDouble(basePoint.y);
5680 buf->putBitDouble(basePoint.z);
5681 buf->putBitDouble(radious);
5682 buf->putThickness(thickness, /*b_R2000_style=*/true);
5683 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
5684 buf->putBitDouble(staangle);
5685 buf->putBitDouble(endangle);
5686
5687 return encodeDwgEntHandle(version, buf, handleBuf);
5688}
5689
5690bool DRW_Line::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5691 resetDwgState();
5692 dwgBuffer *sourceBuf = buf;
5693 auto fail = [this, sourceBuf]() {
5694 if (sourceBuf != nullptr)
5695 sourceBuf->invalidate();
5696 resetDwgState();
5697 return false;
5698 };
5699 if (sourceBuf == nullptr)
5700 return fail();
5701
5702 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
5703 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
5704 || !bodyProbe.isGood())
5705 return fail();
5706 const std::uint64_t bodyEndBit = dwgDataEndBit;
5707 DRW_Coord parsedBasePoint;
5708 DRW_Coord parsedSecPoint;
5709 DRW_Coord parsedExtrusion;
5710 double parsedThickness = 0.0;
5711 if (version < DRW::AC1015) {
5712 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedBasePoint)
5713 || !readBoundedBitCoord(bodyProbe, bodyEndBit, parsedSecPoint))
5714 return fail();
5715 } else {
5716 if (!proxyEntityHasBits(bodyProbe, bodyEndBit, 1))
5717 return fail();
5718 const bool zIsZero = bodyProbe.getBit() != 0;
5719 if (!bodyProbe.isGood()
5720 || !readBoundedRawDouble(bodyProbe, bodyEndBit,
5721 parsedBasePoint.x)
5722 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit,
5723 parsedBasePoint.x,
5724 parsedSecPoint.x)
5725 || !readBoundedRawDouble(bodyProbe, bodyEndBit,
5726 parsedBasePoint.y)
5727 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit,
5728 parsedBasePoint.y,
5729 parsedSecPoint.y))
5730 return fail();
5731 if (zIsZero) {
5732 parsedBasePoint.z = 0.0;
5733 parsedSecPoint.z = 0.0;
5734 } else if (!readBoundedRawDouble(bodyProbe, bodyEndBit,
5735 parsedBasePoint.z)
5736 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit,
5737 parsedBasePoint.z,
5738 parsedSecPoint.z))
5739 return fail();
5740 }
5741 if (!readBoundedThickness(bodyProbe, bodyEndBit,
5742 version > DRW::AC1014, parsedThickness)
5743 || !readBoundedExtrusion(bodyProbe, bodyEndBit,
5744 version > DRW::AC1014, parsedExtrusion)
5745 || !std::isfinite(parsedBasePoint.x)
5746 || !std::isfinite(parsedBasePoint.y)
5747 || !std::isfinite(parsedBasePoint.z)
5748 || !std::isfinite(parsedSecPoint.x)
5749 || !std::isfinite(parsedSecPoint.y)
5750 || !std::isfinite(parsedSecPoint.z)
5751 || !std::isfinite(parsedThickness)
5752 || !std::isfinite(parsedExtrusion.x)
5753 || !std::isfinite(parsedExtrusion.y)
5754 || !std::isfinite(parsedExtrusion.z))
5755 return fail();
5756
5757 std::uint64_t handleEndBit = 0;
5758 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
5759 handleEndBit))
5760 return fail();
5761 dwgBuffer handleProbe = bodyProbe.forkIndependent();
5762 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
5763 handleEndBit)
5764 || !handleProbe.isGood())
5765 return fail();
5766
5767 basePoint = parsedBasePoint;
5768 secPoint = parsedSecPoint;
5769 thickness = parsedThickness;
5770 extPoint = parsedExtrusion;
5771 *sourceBuf = handleProbe;
5772 return true;
5773}
5774
5775bool DRW_Ray::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5776 resetDwgState();
5777 dwgBuffer *sourceBuf = buf;
5778 auto fail = [this, sourceBuf]() {
5779 if (sourceBuf != nullptr)
5780 sourceBuf->invalidate();
5781 resetDwgState();
5782 return false;
5783 };
5784 if (sourceBuf == nullptr)
5785 return fail();
5786
5787 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
5788 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
5789 || !bodyProbe.isGood())
5790 return fail();
5791 const std::uint64_t bodyEndBit = dwgDataEndBit;
5792 DRW_Coord parsedBasePoint;
5793 DRW_Coord parsedDirection;
5794 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedBasePoint)
5795 || !readBoundedBitCoord(bodyProbe, bodyEndBit, parsedDirection)
5796 || !std::isfinite(parsedBasePoint.x)
5797 || !std::isfinite(parsedBasePoint.y)
5798 || !std::isfinite(parsedBasePoint.z)
5799 || !std::isfinite(parsedDirection.x)
5800 || !std::isfinite(parsedDirection.y)
5801 || !std::isfinite(parsedDirection.z))
5802 return fail();
5803
5804 std::uint64_t handleEndBit = 0;
5805 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
5806 handleEndBit))
5807 return fail();
5808 dwgBuffer handleProbe = bodyProbe.forkIndependent();
5809 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
5810 handleEndBit)
5811 || !handleProbe.isGood())
5812 return fail();
5813
5814 basePoint = parsedBasePoint;
5815 secPoint = parsedDirection;
5816 *sourceBuf = handleProbe;
5817 return true;
5818}
5819
5820void DRW_Circle::applyExtrusion(){
5821 if (haveExtrusion) {
5822 //NOTE: Commenting these out causes the the arcs being tested to be located
5823 //on the other side of the y axis (all x dimensions are negated).
5824 calculateAxis(extPoint);
5825 extrudePoint(extPoint, &basePoint);
5826 }
5827}
5828
5829bool DRW_Circle::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
5830 switch (code) {
5831 case 40:
5832 radious = reader->getDouble();
5833 break;
5834 default:
5835 return DRW_Point::parseCode(code, reader);
5836 }
5837
5838 return true;
5839}
5840
5841void DRW_Circle::resetDwgState() {
5842 DRW_Point::resetDwgState();
5843 radious = 0.0;
5844}
5845
5846bool DRW_Circle::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5847 resetDwgState();
5848 radious = 0.0;
5849 dwgBuffer *sourceBuf = buf;
5850 auto fail = [this, sourceBuf]() {
5851 if (sourceBuf != nullptr)
5852 sourceBuf->invalidate();
5853 resetDwgState();
5854 return false;
5855 };
5856 if (sourceBuf == nullptr)
5857 return fail();
5858
5859 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
5860 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
5861 || !bodyProbe.isGood())
5862 return fail();
5863 const std::uint64_t bodyEndBit = dwgDataEndBit;
5864 DRW_Coord parsedCenter;
5865 DRW_Coord parsedExtrusion;
5866 double parsedRadius = 0.0;
5867 double parsedThickness = 0.0;
5868 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedCenter)
5869 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedRadius)
5870 || !readBoundedThickness(bodyProbe, bodyEndBit,
5871 version > DRW::AC1014, parsedThickness)
5872 || !readBoundedExtrusion(bodyProbe, bodyEndBit,
5873 version > DRW::AC1014, parsedExtrusion)
5874 || !std::isfinite(parsedCenter.x)
5875 || !std::isfinite(parsedCenter.y)
5876 || !std::isfinite(parsedCenter.z)
5877 || !std::isfinite(parsedRadius)
5878 || !std::isfinite(parsedThickness)
5879 || !std::isfinite(parsedExtrusion.x)
5880 || !std::isfinite(parsedExtrusion.y)
5881 || !std::isfinite(parsedExtrusion.z))
5882 return fail();
5883
5884 std::uint64_t handleEndBit = 0;
5885 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
5886 handleEndBit))
5887 return fail();
5888 dwgBuffer handleProbe = bodyProbe.forkIndependent();
5889 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
5890 handleEndBit)
5891 || !handleProbe.isGood())
5892 return fail();
5893
5894 basePoint = parsedCenter;
5895 radious = parsedRadius;
5896 thickness = parsedThickness;
5897 extPoint = parsedExtrusion;
5898 *sourceBuf = handleProbe;
5899 return true;
5900}
5901
5902void DRW_Arc::applyExtrusion(){
5903 DRW_Circle::applyExtrusion();
5904
5905 if(haveExtrusion){
5906 // If the extrusion vector has a z value less than 0, the angles for the arc
5907 // have to be mirrored since DXF files use the right hand rule.
5908 // Note that the following code only handles the special case where there is a 2D
5909 // drawing with the z axis heading into the paper (or rather screen). An arbitrary
5910 // extrusion axis (with x and y values greater than 1/64) may still have issues.
5911 if (fabs(extPoint.x) < 0.015625 && fabs(extPoint.y) < 0.015625 && extPoint.z < 0.0) {
5912 staangle=M_PI3.14159265358979323846-staangle;
5913 endangle=M_PI3.14159265358979323846-endangle;
5914
5915 double temp = staangle;
5916 staangle=endangle;
5917 endangle=temp;
5918 }
5919 }
5920}
5921
5922bool DRW_Arc::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
5923 switch (code) {
5924 case 50:
5925 staangle = reader->getDouble()/ ARAD57.29577951308232;
5926 break;
5927 case 51:
5928 endangle = reader->getDouble()/ ARAD57.29577951308232;
5929 break;
5930 default:
5931 return DRW_Circle::parseCode(code, reader);
5932 }
5933
5934 return true;
5935}
5936
5937bool DRW_Arc::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5938 DRW_Circle::resetDwgState();
5939 radious = 0.0;
5940 staangle = 0.0;
5941 endangle = 0.0;
5942 isccw = 1;
5943 dwgBuffer *sourceBuf = buf;
5944 auto fail = [this, sourceBuf]() {
5945 if (sourceBuf != nullptr)
5946 sourceBuf->invalidate();
5947 DRW_Circle::resetDwgState();
5948 staangle = 0.0;
5949 endangle = 0.0;
5950 isccw = 1;
5951 return false;
5952 };
5953 if (sourceBuf == nullptr)
5954 return fail();
5955
5956 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
5957 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
5958 || !bodyProbe.isGood())
5959 return fail();
5960 const std::uint64_t bodyEndBit = dwgDataEndBit;
5961 DRW_Coord parsedCenter;
5962 DRW_Coord parsedExtrusion;
5963 double parsedRadius = 0.0;
5964 double parsedThickness = 0.0;
5965 double parsedStartAngle = 0.0;
5966 double parsedEndAngle = 0.0;
5967 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedCenter)
5968 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedRadius)
5969 || !readBoundedThickness(bodyProbe, bodyEndBit,
5970 version > DRW::AC1014, parsedThickness)
5971 || !readBoundedExtrusion(bodyProbe, bodyEndBit,
5972 version > DRW::AC1014, parsedExtrusion)
5973 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedStartAngle)
5974 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedEndAngle)
5975 || !std::isfinite(parsedCenter.x)
5976 || !std::isfinite(parsedCenter.y)
5977 || !std::isfinite(parsedCenter.z)
5978 || !std::isfinite(parsedRadius)
5979 || !std::isfinite(parsedThickness)
5980 || !std::isfinite(parsedExtrusion.x)
5981 || !std::isfinite(parsedExtrusion.y)
5982 || !std::isfinite(parsedExtrusion.z)
5983 || !std::isfinite(parsedStartAngle)
5984 || !std::isfinite(parsedEndAngle))
5985 return fail();
5986
5987 std::uint64_t handleEndBit = 0;
5988 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
5989 handleEndBit))
5990 return fail();
5991 dwgBuffer handleProbe = bodyProbe.forkIndependent();
5992 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
5993 handleEndBit)
5994 || !handleProbe.isGood())
5995 return fail();
5996
5997 basePoint = parsedCenter;
5998 radious = parsedRadius;
5999 thickness = parsedThickness;
6000 extPoint = parsedExtrusion;
6001 staangle = parsedStartAngle;
6002 endangle = parsedEndAngle;
6003 *sourceBuf = handleProbe;
6004 return true;
6005}
6006
6007bool DRW_Ellipse::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
6008 switch (code) {
6009 case 40:
6010 ratio = reader->getDouble();
6011 break;
6012 case 41:
6013 staparam = reader->getDouble();
6014 break;
6015 case 42:
6016 endparam = reader->getDouble();
6017 break;
6018 default:
6019 return DRW_Line::parseCode(code, reader);
6020 }
6021
6022 return true;
6023}
6024
6025void DRW_Ellipse::applyExtrusion(){
6026 if (haveExtrusion) {
6027 calculateAxis(extPoint);
6028 extrudePoint(extPoint, &basePoint);
6029 extrudePoint(extPoint, &secPoint);
6030 double intialparam = staparam;
6031 if (extPoint.z < 0.){
6032 staparam = M_PIx26.283185307179586 - endparam;
6033 endparam = M_PIx26.283185307179586 - intialparam;
6034 }
6035 }
6036}
6037
6038//if ratio > 1 minor axis are greather than major axis, correct it
6039void DRW_Ellipse::correctAxis(){
6040 bool complete = false;
6041 if (staparam == endparam) {
6042 staparam = 0.0;
6043 endparam = M_PIx26.283185307179586; //2*M_PI;
6044 complete = true;
6045 }
6046 if (ratio > 1){
6047 if ( fabs(endparam - staparam - M_PIx26.283185307179586) < 1.0e-10)
6048 complete = true;
6049 double incX = secPoint.x;
6050 secPoint.x = -(secPoint.y * ratio);
6051 secPoint.y = incX*ratio;
6052 ratio = 1/ratio;
6053 if (!complete){
6054 if (staparam < M_PI_21.57079632679489661923)
6055 staparam += M_PI3.14159265358979323846 *2;
6056 if (endparam < M_PI_21.57079632679489661923)
6057 endparam += M_PI3.14159265358979323846 *2;
6058 endparam -= M_PI_21.57079632679489661923;
6059 staparam -= M_PI_21.57079632679489661923;
6060 }
6061 }
6062}
6063
6064bool DRW_Ellipse::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
6065 resetDwgState();
6066 ratio = 1.0;
6067 staparam = 0.0;
6068 endparam = 0.0;
6069 isccw = 1;
6070 dwgBuffer *sourceBuf = buf;
6071 auto fail = [this, sourceBuf]() {
6072 if (sourceBuf != nullptr)
6073 sourceBuf->invalidate();
6074 resetDwgState();
6075 ratio = 1.0;
6076 staparam = 0.0;
6077 endparam = 0.0;
6078 isccw = 1;
6079 return false;
6080 };
6081 if (sourceBuf == nullptr)
6082 return fail();
6083
6084 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
6085 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
6086 || !bodyProbe.isGood())
6087 return fail();
6088 const std::uint64_t bodyEndBit = dwgDataEndBit;
6089 DRW_Coord parsedCenter;
6090 DRW_Coord parsedAxis;
6091 DRW_Coord parsedExtrusion;
6092 double parsedRatio = 1.0;
6093 double parsedStart = 0.0;
6094 double parsedEnd = 0.0;
6095 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedCenter)
6096 || !readBoundedBitCoord(bodyProbe, bodyEndBit, parsedAxis)
6097 || !readBoundedBitCoord(bodyProbe, bodyEndBit, parsedExtrusion)
6098 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedRatio)
6099 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedStart)
6100 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedEnd)
6101 || !std::isfinite(parsedCenter.x)
6102 || !std::isfinite(parsedCenter.y)
6103 || !std::isfinite(parsedCenter.z)
6104 || !std::isfinite(parsedAxis.x)
6105 || !std::isfinite(parsedAxis.y)
6106 || !std::isfinite(parsedAxis.z)
6107 || !std::isfinite(parsedExtrusion.x)
6108 || !std::isfinite(parsedExtrusion.y)
6109 || !std::isfinite(parsedExtrusion.z)
6110 || !std::isfinite(parsedRatio)
6111 || !std::isfinite(parsedStart)
6112 || !std::isfinite(parsedEnd))
6113 return fail();
6114
6115 std::uint64_t handleEndBit = 0;
6116 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
6117 handleEndBit))
6118 return fail();
6119 dwgBuffer handleProbe = bodyProbe.forkIndependent();
6120 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
6121 handleEndBit)
6122 || !handleProbe.isGood())
6123 return fail();
6124
6125 basePoint = parsedCenter;
6126 secPoint = parsedAxis;
6127 extPoint = parsedExtrusion;
6128 ratio = parsedRatio;
6129 staparam = parsedStart;
6130 endparam = parsedEnd;
6131 *sourceBuf = handleProbe;
6132 return true;
6133}
6134
6135//parts are the number of vertex to split polyline, default 128
6136void DRW_Ellipse::toPolyline(DRW_Polyline *pol, int parts){
6137 double radMajor, radMinor, cosRot, sinRot, incAngle, curAngle;
6138 double cosCurr, sinCurr;
6139 radMajor = hypot(secPoint.x, secPoint.y);
6140 radMinor = radMajor*ratio;
6141 //calculate sin & cos of included angle
6142 incAngle = atan2(secPoint.y, secPoint.x);
6143 cosRot = cos(incAngle);
6144 sinRot = sin(incAngle);
6145 incAngle = M_PIx26.283185307179586 / parts;
6146 curAngle = staparam;
6147 int i = static_cast<int>(curAngle / incAngle);
6148 do {
6149 if (curAngle > endparam) {
6150 curAngle = endparam;
6151 i = parts+2;
6152 }
6153 cosCurr = cos(curAngle);
6154 sinCurr = sin(curAngle);
6155 double x = basePoint.x + (cosCurr*cosRot*radMajor) - (sinCurr*sinRot*radMinor);
6156 double y = basePoint.y + (cosCurr*sinRot*radMajor) + (sinCurr*cosRot*radMinor);
6157 pol->addVertex( DRW_Vertex(x, y, 0.0, 0.0));
6158 curAngle = (++i)*incAngle;
6159 } while (i<parts);
6160 if ( fabs(endparam - staparam - M_PIx26.283185307179586) < 1.0e-10){
6161 pol->flags = 1;
6162 }
6163 pol->layer = this->layer;
6164 pol->lineType = this->lineType;
6165 pol->color = this->color;
6166 pol->lWeight = this->lWeight;
6167 pol->extPoint = this->extPoint;
6168}
6169
6170void DRW_Trace::applyExtrusion(){
6171 if (haveExtrusion) {
6172 calculateAxis(extPoint);
6173 extrudePoint(extPoint, &basePoint);
6174 extrudePoint(extPoint, &secPoint);
6175 extrudePoint(extPoint, &thirdPoint);
6176 extrudePoint(extPoint, &fourPoint);
6177 }
6178}
6179
6180bool DRW_Trace::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
6181 switch (code) {
6182 case 12:
6183 thirdPoint.x = reader->getDouble();
6184 break;
6185 case 22:
6186 thirdPoint.y = reader->getDouble();
6187 break;
6188 case 32:
6189 thirdPoint.z = reader->getDouble();
6190 break;
6191 case 13:
6192 fourPoint.x = reader->getDouble();
6193 break;
6194 case 23:
6195 fourPoint.y = reader->getDouble();
6196 break;
6197 case 33:
6198 fourPoint.z = reader->getDouble();
6199 break;
6200 default:
6201 return DRW_Line::parseCode(code, reader);
6202 }
6203
6204 return true;
6205}
6206
6207void DRW_Trace::resetDwgState() {
6208 DRW_Line::resetDwgState();
6209 thirdPoint = DRW_Coord{0.0, 0.0, 0.0};
6210 fourPoint = DRW_Coord{0.0, 0.0, 0.0};
6211}
6212
6213bool DRW_Trace::parseDwg(DRW::Version version, dwgBuffer *buf,
6214 std::uint32_t bs) {
6215 resetDwgState();
6216 dwgBuffer *sourceBuf = buf;
6217 auto fail = [this, sourceBuf]() {
6218 if (sourceBuf != nullptr)
6219 sourceBuf->invalidate();
6220 resetDwgState();
6221 return false;
6222 };
6223 if (sourceBuf == nullptr)
6224 return fail();
6225
6226 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
6227 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
6228 || !bodyProbe.isGood())
6229 return fail();
6230
6231 const std::uint64_t bodyEndBit = dwgDataEndBit;
6232 double parsedThickness = 0.0;
6233 double parsedElevation = 0.0;
6234 std::array<double, 8> coordinates{};
6235 DRW_Coord parsedExtrusion{0.0, 0.0, 1.0};
6236 if (!readBoundedThickness(bodyProbe, bodyEndBit,
6237 version > DRW::AC1014, parsedThickness)
6238 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedElevation))
6239 return fail();
6240 for (double& coordinate : coordinates) {
6241 if (!readBoundedRawDouble(bodyProbe, bodyEndBit, coordinate)
6242 || !std::isfinite(coordinate))
6243 return fail();
6244 }
6245 if (!std::isfinite(parsedThickness)
6246 || !std::isfinite(parsedElevation)
6247 || !readBoundedExtrusion(bodyProbe, bodyEndBit,
6248 version > DRW::AC1014, parsedExtrusion)
6249 || !std::isfinite(parsedExtrusion.x)
6250 || !std::isfinite(parsedExtrusion.y)
6251 || !std::isfinite(parsedExtrusion.z))
6252 return fail();
6253
6254 std::uint64_t handleEndBit = 0;
6255 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
6256 handleEndBit))
6257 return fail();
6258 dwgBuffer handleProbe = bodyProbe.forkIndependent();
6259 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
6260 handleEndBit)
6261 || !handleProbe.isGood())
6262 return fail();
6263
6264 thickness = parsedThickness;
6265 basePoint = DRW_Coord{coordinates[0], coordinates[1], parsedElevation};
6266 secPoint = DRW_Coord{coordinates[2], coordinates[3], parsedElevation};
6267 thirdPoint = DRW_Coord{coordinates[4], coordinates[5], parsedElevation};
6268 fourPoint = DRW_Coord{coordinates[6], coordinates[7], parsedElevation};
6269 extPoint = parsedExtrusion;
6270 *sourceBuf = handleProbe;
6271 return true;
6272}
6273
6274bool DRW_Solid::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
6275 DRW_DBG("\n***************************** parsing Solid *********************************************\n")DRW_dbg::dbg("\n***************************** parsing Solid *********************************************\n"
)
;
6276 return DRW_Trace::parseDwg(v, buf, bs);
6277}
6278
6279bool DRW_3Dface::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
6280 switch (code) {
6281 case 70:
6282 invisibleflag = reader->getInt32();
6283 break;
6284 default:
6285 return DRW_Trace::parseCode(code, reader);
6286 }
6287
6288 return true;
6289}
6290
6291bool DRW_3Dface::parseDwg(DRW::Version v, dwgBuffer *buf,
6292 std::uint32_t bs) {
6293 DRW_Trace::resetDwgState();
6294 invisibleflag = 0;
6295 dwgBuffer *sourceBuf = buf;
6296 auto fail = [this, sourceBuf]() {
6297 if (sourceBuf != nullptr)
6298 sourceBuf->invalidate();
6299 DRW_Trace::resetDwgState();
6300 invisibleflag = 0;
6301 return false;
6302 };
6303 if (sourceBuf == nullptr)
6304 return fail();
6305
6306 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
6307 if (!DRW_Entity::parseDwgCommon(v, &bodyProbe, bs)
6308 || !bodyProbe.isGood())
6309 return fail();
6310
6311 const std::uint64_t bodyEndBit = dwgDataEndBit;
6312 std::array<DRW_Coord, 4> points{};
6313 std::uint16_t parsedInvisibleFlag = 0;
6314 if (v < DRW::AC1015) {
6315 for (DRW_Coord& point : points) {
6316 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, point.x)
6317 || !readBoundedBitDouble(bodyProbe, bodyEndBit, point.y)
6318 || !readBoundedBitDouble(bodyProbe, bodyEndBit, point.z))
6319 return fail();
6320 }
6321 if (!readBoundedBitShort(bodyProbe, bodyEndBit,
6322 parsedInvisibleFlag))
6323 return fail();
6324 } else {
6325 if (!proxyEntityHasBits(bodyProbe, bodyEndBit, 2))
6326 return fail();
6327 const bool hasNoFlag = bodyProbe.getBit() != 0;
6328 const bool zIsZero = bodyProbe.getBit() != 0;
6329 if (!bodyProbe.isGood()
6330 || !readBoundedRawDouble(bodyProbe, bodyEndBit, points[0].x)
6331 || !readBoundedRawDouble(bodyProbe, bodyEndBit, points[0].y))
6332 return fail();
6333 if (zIsZero) {
6334 points[0].z = 0.0;
6335 } else if (!readBoundedRawDouble(bodyProbe, bodyEndBit,
6336 points[0].z)) {
6337 return fail();
6338 }
6339
6340 if (!readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[0].x,
6341 points[1].x)
6342 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[0].y,
6343 points[1].y)
6344 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[0].z,
6345 points[1].z)
6346 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[1].x,
6347 points[2].x)
6348 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[1].y,
6349 points[2].y)
6350 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[1].z,
6351 points[2].z)
6352 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[2].x,
6353 points[3].x)
6354 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[2].y,
6355 points[3].y)
6356 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[2].z,
6357 points[3].z))
6358 return fail();
6359 if (hasNoFlag) {
6360 parsedInvisibleFlag = NoEdge;
6361 } else if (!readBoundedBitShort(bodyProbe, bodyEndBit,
6362 parsedInvisibleFlag)) {
6363 return fail();
6364 }
6365 }
6366
6367 for (const DRW_Coord& point : points) {
6368 if (!std::isfinite(point.x) || !std::isfinite(point.y)
6369 || !std::isfinite(point.z))
6370 return fail();
6371 }
6372 if (parsedInvisibleFlag > AllEdges)
6373 return fail();
6374
6375 std::uint64_t handleEndBit = 0;
6376 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
6377 return fail();
6378 dwgBuffer handleProbe = bodyProbe.forkIndependent();
6379 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
6380 || !handleProbe.isGood())
6381 return fail();
6382
6383 basePoint = points[0];
6384 secPoint = points[1];
6385 thirdPoint = points[2];
6386 fourPoint = points[3];
6387 invisibleflag = static_cast<int>(parsedInvisibleFlag);
6388 *sourceBuf = handleProbe;
6389 return true;
6390}
6391
6392void DRW_ModelerGeometry::resetDwgState() {
6393 DRW_Entity::reset();
6394 m_modelerVersion = 0;
6395 m_bodyBitSize = 0;
6396 m_objectSize = 0;
6397 m_isEmpty = false;
6398 m_hasModelerData = false;
6399 m_modelerDataUnknownBit = false;
6400 m_hasWireframe = false;
6401 m_historyHandle = 0;
6402 m_rawBytes.clear();
6403 m_payloadRanges.clear();
6404 m_wireframe = DRW_AcisBrep();
6405 m_wireframeDecoded = false;
6406}
6407
6408bool DRW_ModelerGeometry::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
6409 resetDwgState();
6410 dwgBuffer *sourceBuf = buf;
6411 auto fail = [this, sourceBuf]() {
6412 if (sourceBuf != nullptr)
6413 sourceBuf->invalidate();
6414 resetDwgState();
6415 return false;
6416 };
6417 if (sourceBuf == nullptr)
6418 return fail();
6419
6420 dwgBuffer probe = sourceBuf->forkIndependent();
6421 dwgBuffer stringProbe = sourceBuf->forkIndependent();
6422 dwgBuffer *stringStream = v > DRW::AC1018 ? &stringProbe : nullptr;
6423 bool ret = DRW_Entity::parseDwg(v, &probe, stringStream, bs);
6424 if (!ret)
6425 return fail();
6426 DRW_DBG("\n***************************** parsing modeler geometry ******************\n")DRW_dbg::dbg("\n***************************** parsing modeler geometry ******************\n"
)
;
6427
6428 std::uint64_t bodyEndBit = 0;
6429 if (!entityBodyDataEndBit(
6430 stringStream != nullptr ? *stringStream : probe,
6431 v, objSize, bodyEndBit))
6432 return fail();
6433 const std::uint64_t sourceEndBit = proxyEntityEndBit(*sourceBuf, 0);
6434 if ((v >= DRW::AC1015 && objSize == 0) || bodyEndBit > sourceEndBit
6435 || currentDwgBit(&probe) > bodyEndBit)
6436 return fail();
6437 bool parsedIsEmpty = false;
6438 bool parsedUnknownBit = false;
6439 std::uint16_t parsedModelerVersion = 0;
6440 if (!readBoundedBit(probe, bodyEndBit, parsedIsEmpty)
6441 || !readBoundedBit(probe, bodyEndBit, parsedUnknownBit))
6442 return fail();
6443 const bool parsedHasModelerData = !parsedIsEmpty;
6444 if (parsedHasModelerData
6445 && !readBoundedBitShort(probe, bodyEndBit, parsedModelerVersion))
6446 return fail();
6447 if (!probe.isGood() || (stringStream != nullptr && !stringStream->isGood()))
6448 return fail();
6449
6450 std::uint64_t handleEndBit = 0;
6451 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
6452 return fail();
6453 // R13-R2004 modeler bodies contain opaque ACIS/SAT data between this
6454 // header and the common handles. Do not interpret that payload: jump to
6455 // the bounded object-data end exactly as the surface/proxy readers do.
6456 dwgBuffer handleProbe = sourceBuf->forkIndependent();
6457 if (v <= DRW::AC1018) {
6458 if (!handleProbe.setPosition(bodyEndBit >> 3))
6459 return fail();
6460 handleProbe.setBitPos(static_cast<std::uint8_t>(bodyEndBit & 7u));
6461 if (!handleProbe.isGood())
6462 return fail();
6463 }
6464 ret = DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit);
6465 if (!ret || !handleProbe.isGood())
6466 return fail();
6467 std::uint32_t parsedHistoryHandle = 0;
6468 if (eType == DRW::E3DSOLID && v > DRW::AC1018
6469 && proxyEntityHasBits(handleProbe, handleEndBit, 8)) {
6470 dwgHandle historyH;
6471 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
6472 historyH))
6473 return fail();
6474 parsedHistoryHandle = historyH.ref;
6475 DRW_DBG(" 3DSOLID history Handle: ")DRW_dbg::dbg(" 3DSOLID history Handle: ");
6476 DRW_DBGHL(historyH.code, historyH.size, historyH.ref)DRW_dbg::dbgHL(historyH.code, historyH.size, historyH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
6477 }
6478
6479 if (handleProbe.size() > std::numeric_limits<std::uint32_t>::max())
6480 return fail();
6481 m_bodyBitSize = bs;
6482 m_objectSize = static_cast<std::uint32_t>(probe.size());
6483 m_isEmpty = parsedIsEmpty;
6484 m_hasModelerData = parsedHasModelerData;
6485 m_modelerDataUnknownBit = parsedUnknownBit;
6486 m_modelerVersion = parsedModelerVersion;
6487 m_historyHandle = parsedHistoryHandle;
6488 *sourceBuf = handleProbe;
6489 return true;
6490}
6491
6492bool DRW_ModelerGeometry::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
6493 switch (code) {
6494 case 1:
6495 case 3:
6496 if (!appendTextBytesChecked(m_rawBytes, reader->getString(),
6497 dwgSafety::MaxBufferSize))
6498 return false;
6499 break;
6500 case 70: {
6501 const int value = reader->getInt32();
6502 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
6503 return false;
6504 m_modelerVersion = static_cast<std::uint16_t>(value);
6505 break;
6506 }
6507 case 350:
6508 case 360:
6509 m_historyHandle = static_cast<std::uint32_t>(reader->getHandleString());
6510 break;
6511 case 310:
6512 // AcDbEntity proxy graphics precede the AcDbModelerGeometry subclass,
6513 // so the announced bytes belong to the entity, not to the ACIS body.
6514 if (numProxyGraph > 0
6515 && proxyGraphics.size() < static_cast<std::size_t>(numProxyGraph))
6516 return DRW_Entity::parseCode(code, reader);
6517 {
6518 std::vector<std::uint8_t> decoded;
6519 if (!decodeHexBytes(reader->getString(), decoded))
6520 return false;
6521 if (!appendBytesChecked(m_rawBytes, decoded,
6522 dwgSafety::MaxBufferSize))
6523 return false;
6524 }
6525 break;
6526 default:
6527 return DRW_Entity::parseCode(code, reader);
6528 }
6529 return true;
6530}
6531
6532void DRW_Mesh::resetDwgState() {
6533 DRW_Entity::reset();
6534 version = 2;
6535 blendCrease = false;
6536 subdivisionLevel = 0;
6537 subdivVertices.clear();
6538 vertices.clear();
6539 faces.clear();
6540 edges.clear();
6541 creases.clear();
6542 unknown = 0;
6543 propertyOverrides.clear();
6544 m_dxfMeshSubclassSeen = false;
6545 m_dxfState = 0;
6546 m_dxfVertexCount = -1;
6547 m_dxfFaceItemsRemaining = -1;
6548 m_dxfEdgeValuesRemaining = -1;
6549 m_dxfCreaseValuesRemaining = -1;
6550 m_dxfPending = 0;
6551 m_dxfEdgeFrom = -1;
6552 m_dxfOverrideEntityCount = -1;
6553 m_dxfOverridePropertyCount = -1;
6554}
6555
6556// DRW_Mesh::parseDwg — AcDbSubDMesh, field order shared by ACadSharp and
6557// dwgTs. DWG group 91 is the subdivision level, not a refined-vertex count;
6558// the following BL is the level-0 vertex count.
6559bool DRW_Mesh::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
6560 resetDwgState();
6561 dwgBuffer *sourceBuf = buf;
6562 auto fail = [this, sourceBuf]() {
6563 if (sourceBuf != nullptr)
6564 sourceBuf->invalidate();
6565 resetDwgState();
6566 return false;
6567 };
6568 if (sourceBuf == nullptr)
6569 return fail();
6570
6571 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
6572 dwgBuffer stringProbe = sourceBuf->forkIndependent();
6573 dwgBuffer *stringStream = v > DRW::AC1018 ? &stringProbe : nullptr;
6574 if (!DRW_Entity::parseDwg(v, &bodyProbe, stringStream, bs))
6575 return fail();
6576 buf = &bodyProbe;
6577 DRW_DBG("\n***************************** parsing MESH (AcDbSubDMesh) *****************\n")DRW_dbg::dbg("\n***************************** parsing MESH (AcDbSubDMesh) *****************\n"
)
;
6578 std::uint64_t bodyEndBit = 0;
6579 if (!entityBodyDataEndBit(
6580 stringStream != nullptr ? *stringStream : bodyProbe,
6581 v, objSize, bodyEndBit))
6582 return fail();
6583
6584 auto readCount = [&](std::uint32_t minimumBitsPerItem,
6585 std::uint32_t& count) {
6586 return readTableBodyCount(
6587 v, buf, static_cast<std::uint32_t>(bodyEndBit),
6588 DRW_Mesh::kMaxMeshItems, minimumBitsPerItem, count);
6589 };
6590
6591 std::uint16_t parsedVersion = 0;
6592 bool parsedBlendCrease = false;
6593 std::int32_t parsedSubdivisionLevel = 0;
6594 if (!readBoundedBitShort(*buf, bodyEndBit, parsedVersion)
6595 || !readBoundedBit(*buf, bodyEndBit, parsedBlendCrease)
6596 || !readBoundedBitLong(*buf, bodyEndBit, parsedSubdivisionLevel)
6597 || parsedSubdivisionLevel < 0) {
6598 return fail();
6599 }
6600
6601 std::uint32_t nVert = 0; // BL vertex count (92)
6602 if (!readCount(6, nVert)) return fail(); // minimum 3BD width
6603 std::vector<DRW_Coord> parsedVertices;
6604 if (!DRW::reserve(parsedVertices, static_cast<int>(nVert)))
6605 return fail();
6606 std::vector<std::vector<std::int32_t>> parsedFaces;
6607 std::vector<std::pair<std::int32_t, std::int32_t>> parsedEdges;
6608 std::vector<double> parsedCreases;
6609 for (std::uint32_t i = 0; i < nVert; ++i) {
6610 DRW_Coord point;
6611 if (!readBoundedBitCoord(*buf, bodyEndBit, point))
6612 return fail();
6613 parsedVertices.push_back(point);
6614 }
6615 const auto finite = [](const DRW_Coord& point) {
6616 return std::isfinite(point.x) && std::isfinite(point.y)
6617 && std::isfinite(point.z);
6618 };
6619 if (!buf->isGood()
6620 || (stringStream != nullptr && !stringStream->isGood())
6621 || !std::all_of(parsedVertices.begin(), parsedVertices.end(), finite))
6622 return fail();
6623
6624 // faces (93) is a FLAT BL stream of length num_faces; each face is
6625 // [count, idx0, idx1, ...]. num_faces is the stream length, not the polygon
6626 // count — group on the fly.
6627 std::uint32_t faceItems = 0; // BL face-list size (93)
6628 if (!readCount(2, faceItems)) return fail();
6629 std::int32_t remaining = static_cast<std::int32_t>(faceItems);
6630 while (remaining > 0 && buf->isGood()) {
6631 std::int32_t cnt = 0;
6632 if (!readBoundedBitLong(*buf, bodyEndBit, cnt))
6633 return fail();
6634 --remaining;
6635 if (cnt < 0 || cnt > remaining)
6636 return fail();
6637 std::vector<std::int32_t> face;
6638 if (!DRW::reserve(face, cnt))
6639 return fail();
6640 for (std::int32_t j = 0; j < cnt; ++j) {
6641 std::int32_t index = 0;
6642 if (!readBoundedBitLong(*buf, bodyEndBit, index))
6643 return fail();
6644 if (index < 0 || static_cast<std::uint32_t>(index) >= nVert)
6645 return fail();
6646 face.push_back(index);
6647 --remaining;
6648 }
6649 if (!buf->isGood()) return fail();
6650 parsedFaces.push_back(std::move(face));
6651 }
6652 if (remaining != 0 || !buf->isGood()) return fail();
6653
6654 std::uint32_t nEdges = 0; // BL edge count (94)
6655 if (!readCount(4, nEdges)) return fail(); // two minimum-width BL values
6656 if (!DRW::reserve(parsedEdges, static_cast<int>(nEdges)))
6657 return fail();
6658 for (std::uint32_t i = 0; i < nEdges; ++i) {
6659 std::int32_t from = 0;
6660 std::int32_t to = 0;
6661 if (!readBoundedBitLong(*buf, bodyEndBit, from)
6662 || !readBoundedBitLong(*buf, bodyEndBit, to))
6663 return fail();
6664 if (from < 0 || to < 0
6665 || static_cast<std::uint32_t>(from) >= nVert
6666 || static_cast<std::uint32_t>(to) >= nVert)
6667 return fail();
6668 parsedEdges.emplace_back(from, to);
6669 }
6670 if (!buf->isGood()) return fail();
6671
6672 std::uint32_t nCrease = 0; // BL crease count (95)
6673 if (!readCount(2, nCrease)) return fail(); // minimum-width BD value
6674 if (!DRW::reserve(parsedCreases, static_cast<int>(nCrease)))
6675 return fail();
6676 for (std::uint32_t i = 0; i < nCrease; ++i) {
6677 double crease = 0.0;
6678 if (!readBoundedBitDouble(*buf, bodyEndBit, crease))
6679 return fail();
6680 parsedCreases.push_back(crease);
6681 }
6682 if (!buf->isGood()
6683 || !std::all_of(parsedCreases.begin(), parsedCreases.end(),
6684 [](double value) { return std::isfinite(value); }))
6685 return fail();
6686
6687 std::int32_t parsedUnknown = 0;
6688 if (!readBoundedBitLong(*buf, bodyEndBit, parsedUnknown))
6689 return fail(); // trailing unknown (BL)
6690
6691 std::uint64_t handleEndBit = 0;
6692 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
6693 return fail();
6694 dwgBuffer handleProbe = bodyProbe.forkIndependent();
6695 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
6696 || !handleProbe.isGood())
6697 return fail();
6698 vertices = std::move(parsedVertices);
6699 faces = std::move(parsedFaces);
6700 edges = std::move(parsedEdges);
6701 creases = std::move(parsedCreases);
6702 version = parsedVersion;
6703 blendCrease = parsedBlendCrease;
6704 subdivisionLevel = parsedSubdivisionLevel;
6705 unknown = parsedUnknown;
6706 *sourceBuf = handleProbe;
6707 return true;
6708}
6709
6710// DRW_Mesh::parseCode — DXF read (codes 71/72/91/92/10·20·30/93/90/94/95/140).
6711// The 90 stream is shared by faces (after 93) and edges (after 94); m_dxfState
6712// sequences which one is being filled (mirrors DRW_Image::parseCode's stateful
6713// 91/14/24 WIPEOUT-vertex accumulation). After the crease stream, group 90
6714// starts the bounded property-override records described by AcDbSubDMesh.
6715bool DRW_Mesh::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
6716 try {
6717 switch (code) {
6718 case 71: {
6719 const std::int32_t value = reader->getInt32();
6720 if (value < 0 || value > std::numeric_limits<std::int16_t>::max())
6721 return false;
6722 version = static_cast<std::uint16_t>(value);
6723 return true;
6724 }
6725 case 72: {
6726 const std::int32_t value = reader->getInt32();
6727 if (value != 0 && value != 1)
6728 return false;
6729 blendCrease = value != 0;
6730 return true;
6731 }
6732 case 91: {
6733 const std::int32_t value = reader->getInt32();
6734 if (m_dxfOverrideEntityCount >= 0) {
6735 if (m_dxfState != 96
6736 || (!propertyOverrides.empty() && m_dxfOverridePropertyCount != 0)
6737 || propertyOverrides.size() >= static_cast<std::size_t>(m_dxfOverrideEntityCount))
6738 return false;
6739 propertyOverrides.push_back({value, {}});
6740 m_dxfOverridePropertyCount = -1;
6741 return true;
6742 }
6743 if (value < 0)
6744 return false;
6745 subdivisionLevel = value;
6746 return true;
6747 }
6748 case 100:
6749 if (reader->getString() == "AcDbSubDMesh")
6750 m_dxfMeshSubclassSeen = true;
6751 return true;
6752 case 92: { // base-vertex count; later 92 values belong to overrides
6753 if (!m_dxfMeshSubclassSeen)
6754 return DRW_Entity::parseCode(code, reader);
6755 if (m_dxfVertexCount >= 0) {
6756 if (m_dxfOverrideEntityCount < 0 || m_dxfState != 96
6757 || propertyOverrides.empty() || m_dxfOverridePropertyCount >= 0)
6758 return false;
6759 const std::int32_t count = reader->getInt32();
6760 if (!isValidCount(count, 4))
6761 return false;
6762 if (!DRW::reserve(propertyOverrides.back().propertyTypes, count))
6763 return false;
6764 m_dxfOverridePropertyCount = count;
6765 return true;
6766 }
6767 const std::int32_t count = reader->getInt32();
6768 if (!isValidCount(count, DRW_Mesh::kMaxMeshItems))
6769 return false;
6770 if (!DRW::reserve(vertices, count))
6771 return false;
6772 m_dxfVertexCount = count;
6773 m_dxfState = 92;
6774 return true;
6775 }
6776 case 10:
6777 if (m_dxfState != 92 || m_dxfVertexCount < 0
6778 || vertices.size() >= static_cast<std::size_t>(m_dxfVertexCount))
6779 return false;
6780 {
6781 const double value = reader->getDouble();
6782 if (!std::isfinite(value))
6783 return false;
6784 vertices.emplace_back();
6785 vertices.back().x = value;
6786 }
6787 return true;
6788 case 20:
6789 if (m_dxfState != 92 || vertices.empty())
6790 return false;
6791 {
6792 const double value = reader->getDouble();
6793 if (!std::isfinite(value))
6794 return false;
6795 vertices.back().y = value;
6796 }
6797 return true;
6798 case 30:
6799 if (m_dxfState != 92 || vertices.empty())
6800 return false;
6801 {
6802 const double value = reader->getDouble();
6803 if (!std::isfinite(value))
6804 return false;
6805 vertices.back().z = value;
6806 }
6807 return true;
6808 case 93: { // face-list item count
6809 if (m_dxfVertexCount < 0
6810 || vertices.size() != static_cast<std::size_t>(m_dxfVertexCount))
6811 return false;
6812 const std::int32_t count = reader->getInt32();
6813 if (!isValidCount(count, DRW_Mesh::kMaxMeshItems))
6814 return false;
6815 m_dxfState = 93;
6816 m_dxfFaceItemsRemaining = count;
6817 m_dxfPending = 0;
6818 return true;
6819 }
6820 case 94: { // edge count; each edge consumes two code-90 values
6821 if (m_dxfFaceItemsRemaining != 0 || m_dxfPending != 0)
6822 return false;
6823 const std::int32_t count = reader->getInt32();
6824 if (!isValidCount(count, DRW_Mesh::kMaxMeshItems))
6825 return false;
6826 m_dxfState = 94;
6827 m_dxfEdgeValuesRemaining = count * 2;
6828 m_dxfEdgeFrom = -1;
6829 if (!DRW::reserve(edges, count))
6830 return false;
6831 return true;
6832 }
6833 case 90: {
6834 const std::int32_t val = reader->getInt32();
6835 if (m_dxfState == 95) {
6836 if (m_dxfCreaseValuesRemaining != 0)
6837 return false;
6838 if (m_dxfOverrideEntityCount >= 0)
6839 return false;
6840 if (!isValidCount(val, DRW_Mesh::kMaxMeshItems))
6841 return false;
6842 m_dxfOverrideEntityCount = val;
6843 m_dxfOverridePropertyCount = val == 0 ? 0 : -1;
6844 m_dxfState = 96;
6845 } else if (m_dxfState == 96) {
6846 if (m_dxfOverrideEntityCount < 0 || propertyOverrides.empty()
6847 || m_dxfOverridePropertyCount <= 0 || val < 0 || val > 3)
6848 return false;
6849 propertyOverrides.back().propertyTypes.push_back(val);
6850 --m_dxfOverridePropertyCount;
6851 } else if (m_dxfState == 93) {
6852 if (m_dxfFaceItemsRemaining <= 0)
6853 return false;
6854 if (m_dxfPending == 0) {
6855 if (val < 3 || val > m_dxfFaceItemsRemaining - 1)
6856 return false;
6857 faces.emplace_back();
6858 m_dxfPending = val;
6859 --m_dxfFaceItemsRemaining;
6860 } else {
6861 if (val < 0 || val >= m_dxfVertexCount)
6862 return false;
6863 if (!faces.empty())
6864 faces.back().push_back(val);
6865 --m_dxfFaceItemsRemaining;
6866 --m_dxfPending;
6867 }
6868 } else if (m_dxfState == 94) {
6869 if (m_dxfEdgeValuesRemaining <= 0 || val < 0 || val >= m_dxfVertexCount)
6870 return false;
6871 --m_dxfEdgeValuesRemaining;
6872 if (m_dxfEdgeFrom < 0)
6873 m_dxfEdgeFrom = val;
6874 else {
6875 edges.emplace_back(m_dxfEdgeFrom, val);
6876 m_dxfEdgeFrom = -1;
6877 }
6878 } else {
6879 return false;
6880 }
6881 return true;
6882 }
6883 case 95: { // edge crease count
6884 if (m_dxfEdgeValuesRemaining != 0 || m_dxfEdgeFrom != -1)
6885 return false;
6886 const std::int32_t count = reader->getInt32();
6887 if (!isValidCount(count, DRW_Mesh::kMaxMeshItems))
6888 return false;
6889 m_dxfState = 95;
6890 m_dxfCreaseValuesRemaining = count;
6891 if (!DRW::reserve(creases, count))
6892 return false;
6893 return true;
6894 }
6895 case 140:
6896 if (m_dxfState != 95 || m_dxfCreaseValuesRemaining <= 0)
6897 return false;
6898 {
6899 const double value = reader->getDouble();
6900 if (!std::isfinite(value))
6901 return false;
6902 creases.push_back(value);
6903 }
6904 --m_dxfCreaseValuesRemaining;
6905 return true;
6906 default:
6907 return DRW_Entity::parseCode(code, reader);
6908 }
6909 } catch (...) {
6910 return false;
6911 }
6912}
6913
6914bool DRW_Mesh::validateGeometry() const {
6915 if (subdivisionLevel < 0
6916 || vertices.size() > static_cast<std::size_t>(kMaxMeshItems)
6917 || edges.size() > static_cast<std::size_t>(kMaxMeshItems)
6918 || faces.size() > static_cast<std::size_t>(kMaxMeshItems)
6919 || creases.size() > static_cast<std::size_t>(kMaxMeshItems))
6920 return false;
6921
6922 const auto finite = [](const DRW_Coord& point) {
6923 return std::isfinite(point.x) && std::isfinite(point.y)
6924 && std::isfinite(point.z);
6925 };
6926 if (!std::all_of(vertices.cbegin(), vertices.cend(), finite)
6927 || !std::all_of(creases.cbegin(), creases.cend(),
6928 [](double value) { return std::isfinite(value); }))
6929 return false;
6930 std::int32_t faceStreamCount = 0;
6931 for (const auto& face : faces) {
6932 if (face.size() < 3
6933 || face.size() > static_cast<std::size_t>(kMaxMeshItems - 1)
6934 || faceStreamCount > kMaxMeshItems
6935 - static_cast<std::int32_t>(face.size() + 1))
6936 return false;
6937 for (std::int32_t index : face) {
6938 if (index < 0
6939 || static_cast<std::size_t>(index) >= vertices.size())
6940 return false;
6941 }
6942 faceStreamCount += static_cast<std::int32_t>(face.size() + 1);
6943 }
6944 for (const auto& edge : edges) {
6945 if (edge.first < 0 || edge.second < 0
6946 || static_cast<std::size_t>(edge.first) >= vertices.size()
6947 || static_cast<std::size_t>(edge.second) >= vertices.size())
6948 return false;
6949 }
6950 return true;
6951}
6952
6953bool DRW_Mesh::validateDxf() const {
6954 if (!m_dxfMeshSubclassSeen
6955 || m_dxfVertexCount < 0
6956 || vertices.size() != static_cast<std::size_t>(m_dxfVertexCount)
6957 || m_dxfFaceItemsRemaining != 0
6958 || m_dxfPending != 0
6959 || m_dxfEdgeValuesRemaining != 0
6960 || m_dxfEdgeFrom != -1
6961 || m_dxfCreaseValuesRemaining != 0
6962 || (m_dxfOverrideEntityCount >= 0
6963 && (m_dxfOverridePropertyCount < 0
6964 || m_dxfOverridePropertyCount != 0
6965 || propertyOverrides.size() != static_cast<std::size_t>(m_dxfOverrideEntityCount))))
6966 return false;
6967 for (const auto& overrideData : propertyOverrides) {
6968 if (overrideData.subEntityMarker < 0
6969 || overrideData.propertyTypes.size() > 4u)
6970 return false;
6971 if (!std::all_of(overrideData.propertyTypes.cbegin(),
6972 overrideData.propertyTypes.cend(),
6973 [](std::int32_t type) { return type >= 0 && type <= 3; }))
6974 return false;
6975 }
6976 return validateGeometry();
6977}
6978
6979bool DRW_Mesh::validateDxfOutput() const {
6980 if (version > static_cast<std::uint16_t>(std::numeric_limits<std::int16_t>::max())
6981 || propertyOverrides.size() > static_cast<std::size_t>(kMaxMeshItems))
6982 return false;
6983 for (const auto& overrideData : propertyOverrides) {
6984 if (overrideData.subEntityMarker < 0
6985 || overrideData.propertyTypes.size() > 4u
6986 || !std::all_of(overrideData.propertyTypes.cbegin(),
6987 overrideData.propertyTypes.cend(),
6988 [](std::int32_t type) { return type >= 0 && type <= 3; }))
6989 return false;
6990 }
6991 return validateGeometry();
6992}
6993
6994bool DRW_Mesh::encodeDwg(DRW::Version dwgVersion, dwgBufferW *buf, std::uint32_t bs,
6995 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
6996 (void)bs; (void)strBuf;
6997 if (!validateGeometry())
6998 return false;
6999
7000 std::int32_t faceStreamCount = 0;
7001 for (const auto& face : faces)
7002 faceStreamCount += static_cast<std::int32_t>(face.size() + 1);
7003
7004 oType = kDwgClassNum;
7005 if (!encodeDwgCommon(dwgVersion, buf)) return false;
7006
7007 buf->putBitShort(version);
7008 buf->putBit(blendCrease ? 1 : 0);
7009
7010 buf->putBitLong(subdivisionLevel);
7011
7012 buf->putBitLong(static_cast<std::int32_t>(vertices.size()));
7013 for (const DRW_Coord& vertex : vertices)
7014 buf->put3BitDouble(vertex);
7015
7016 buf->putBitLong(faceStreamCount);
7017 for (const auto& face : faces) {
7018 buf->putBitLong(static_cast<std::int32_t>(face.size()));
7019 for (std::int32_t index : face)
7020 buf->putBitLong(index);
7021 }
7022
7023 buf->putBitLong(static_cast<std::int32_t>(edges.size()));
7024 for (const auto& edge : edges) {
7025 buf->putBitLong(edge.first);
7026 buf->putBitLong(edge.second);
7027 }
7028
7029 buf->putBitLong(static_cast<std::int32_t>(creases.size()));
7030 for (double crease : creases)
7031 buf->putBitDouble(crease);
7032
7033 buf->putBitLong(unknown);
7034
7035 return encodeDwgEntHandle(dwgVersion, buf, handleBuf);
7036}
7037
7038bool DRW_Shape::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
7039 switch (code) {
7040 case 2:
7041 m_styleName = reader->getUtf8String();
7042 break;
7043 case 10:
7044 m_insertionPoint.x = reader->getDouble();
7045 break;
7046 case 20:
7047 m_insertionPoint.y = reader->getDouble();
7048 break;
7049 case 30:
7050 m_insertionPoint.z = reader->getDouble();
7051 break;
7052 case 39:
7053 m_thickness = reader->getDouble();
7054 break;
7055 case 40:
7056 m_scale = reader->getDouble();
7057 break;
7058 case 41:
7059 m_widthFactor = reader->getDouble();
7060 break;
7061 case 50:
7062 m_rotation = reader->getDouble() / ARAD57.29577951308232;
7063 break;
7064 case 51:
7065 m_oblique = reader->getDouble() / ARAD57.29577951308232;
7066 break;
7067 case 210:
7068 m_extrusion.x = reader->getDouble();
7069 break;
7070 case 220:
7071 m_extrusion.y = reader->getDouble();
7072 break;
7073 case 230:
7074 m_extrusion.z = reader->getDouble();
7075 break;
7076 default:
7077 return DRW_Entity::parseCode(code, reader);
7078 }
7079 return true;
7080}
7081
7082void DRW_Shape::resetDwgState() {
7083 DRW_Entity::reset();
7084 m_insertionPoint = DRW_Coord{0.0, 0.0, 0.0};
7085 m_scale = 1.0;
7086 m_rotation = 0.0;
7087 m_widthFactor = 1.0;
7088 m_oblique = 0.0;
7089 m_thickness = 0.0;
7090 m_shapeIndex = 0;
7091 m_extrusion = DRW_Coord{0.0, 0.0, 1.0};
7092 m_shapeFileHandle = 0;
7093 m_objectSize = 0;
7094 m_bodyBitSize = 0;
7095 m_rawBytes.clear();
7096}
7097
7098bool DRW_Shape::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
7099 resetDwgState();
7100 dwgBuffer *sourceBuf = buf;
7101 auto fail = [this, sourceBuf]() {
7102 if (sourceBuf != nullptr)
7103 sourceBuf->invalidate();
7104 resetDwgState();
7105 return false;
7106 };
7107 if (sourceBuf == nullptr)
7108 return fail();
7109
7110 m_bodyBitSize = bs;
7111 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
7112 if (!DRW_Entity::parseDwgCommon(v, &bodyProbe, bs))
7113 return fail();
7114 DRW_DBG("\n***************************** parsing SHAPE *****************************\n")DRW_dbg::dbg("\n***************************** parsing SHAPE *****************************\n"
)
;
7115
7116 const std::uint64_t bodyEndBit = dwgDataEndBit;
7117 DRW_Coord insertionPoint;
7118 DRW_Coord extrusion;
7119 double scale = 0.0;
7120 double rotation = 0.0;
7121 double widthFactor = 0.0;
7122 double oblique = 0.0;
7123 double thickness = 0.0;
7124 std::uint16_t shapeIndex = 0;
7125 if (!readBounded3BitDouble(bodyProbe, bodyEndBit, insertionPoint)
7126 || !readBoundedBitDouble(bodyProbe, bodyEndBit, scale)
7127 || !readBoundedBitDouble(bodyProbe, bodyEndBit, rotation)
7128 || !readBoundedBitDouble(bodyProbe, bodyEndBit, widthFactor)
7129 || !readBoundedBitDouble(bodyProbe, bodyEndBit, oblique)
7130 || !readBoundedBitDouble(bodyProbe, bodyEndBit, thickness)
7131 || !readBoundedBitShort(bodyProbe, bodyEndBit, shapeIndex)
7132 || !readBounded3BitDouble(bodyProbe, bodyEndBit, extrusion))
7133 return fail();
7134 const auto finite = [](const DRW_Coord& point) {
7135 return std::isfinite(point.x) && std::isfinite(point.y)
7136 && std::isfinite(point.z);
7137 };
7138 if (!bodyProbe.isGood() || !finite(insertionPoint) || !finite(extrusion)
7139 || !std::isfinite(scale) || !std::isfinite(rotation)
7140 || !std::isfinite(widthFactor) || !std::isfinite(oblique)
7141 || !std::isfinite(thickness))
7142 return fail();
7143
7144 std::uint64_t handleEndBit = 0;
7145 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
7146 return fail();
7147 dwgBuffer handleProbe = bodyProbe.forkIndependent();
7148 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
7149 || !handleProbe.isGood())
7150 return fail();
7151
7152 // DwgObjectFrame removes the CRC trailer before exposing the body. The
7153 // SHAPEFILE hard pointer is therefore mandatory and must be parsed even
7154 // when only its exact handle bytes remain.
7155 dwgHandle shapeFileH;
7156 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
7157 shapeFileH)
7158 || shapeFileH.ref == 0)
7159 return fail();
7160
7161 m_insertionPoint = insertionPoint;
7162 m_scale = scale;
7163 m_rotation = rotation;
7164 m_widthFactor = widthFactor;
7165 m_oblique = oblique;
7166 m_thickness = thickness;
7167 m_shapeIndex = shapeIndex;
7168 m_extrusion = extrusion;
7169 m_shapeFileHandle = shapeFileH.ref;
7170 DRW_DBG(" SHAPEFILE Handle: ")DRW_dbg::dbg(" SHAPEFILE Handle: ");
7171 DRW_DBGHL(shapeFileH.code, shapeFileH.size, shapeFileH.ref)DRW_dbg::dbgHL(shapeFileH.code, shapeFileH.size, shapeFileH.ref
)
;
7172 DRW_DBG("\n")DRW_dbg::dbg("\n");
7173 *sourceBuf = handleProbe;
7174 return true;
7175}
7176
7177// Phase 6.1: SHAPE encoder (fixed oType 33). Exact inverse of parseDwg above.
7178// Without this override a SHAPE would encode as a LINE (default DRW_Entity).
7179bool DRW_Shape::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
7180 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
7181 (void)bs; (void)strBuf;
7182 const auto finite = [](const DRW_Coord& point) {
7183 return std::isfinite(point.x) && std::isfinite(point.y)
7184 && std::isfinite(point.z);
7185 };
7186 if (!finite(m_insertionPoint) || !finite(m_extrusion)
7187 || !std::isfinite(m_scale) || !std::isfinite(m_rotation)
7188 || !std::isfinite(m_widthFactor) || !std::isfinite(m_oblique)
7189 || !std::isfinite(m_thickness) || m_shapeFileHandle == 0) {
7190 return false;
7191 }
7192 oType = 33; // SHAPE class id — see dwgreader.cpp case 33
7193 if (!encodeDwgCommon(version, buf)) return false;
7194
7195 buf->put3BitDouble(m_insertionPoint);
7196 buf->putBitDouble(m_scale);
7197 buf->putBitDouble(m_rotation);
7198 buf->putBitDouble(m_widthFactor);
7199 buf->putBitDouble(m_oblique);
7200 buf->putBitDouble(m_thickness);
7201 buf->putBitShort(m_shapeIndex);
7202 buf->put3BitDouble(m_extrusion);
7203
7204 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
7205
7206 // Trailing SHAPEFILE style hard pointer (code 5), byte-count-sized.
7207 dwgHandle sH;
7208 sH.code = 5;
7209 sH.ref = m_shapeFileHandle;
7210 sH.size = 0;
7211 if (m_shapeFileHandle != 0) {
7212 std::uint32_t t = m_shapeFileHandle;
7213 while (t != 0) { t >>= 8; ++sH.size; }
7214 } else {
7215 sH.code = 0; // null handle
7216 }
7217 (handleBuf ? handleBuf : buf)->putHandle(sH);
7218 return true;
7219}
7220
7221bool DRW_Ole2Frame::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
7222 const auto readUnsigned = [&](std::uint32_t maximum,
7223 std::uint32_t& target) {
7224 const int value = reader->getInt32();
7225 if (value < 0 || static_cast<std::uint32_t>(value) > maximum)
7226 return false;
7227 target = static_cast<std::uint32_t>(value);
7228 return true;
7229 };
7230
7231 switch (code) {
7232 case 3:
7233 m_oleClient = reader->getUtf8String();
7234 break;
7235 case 10:
7236 m_pt1.x = reader->getDouble();
7237 break;
7238 case 20:
7239 m_pt1.y = reader->getDouble();
7240 break;
7241 case 30:
7242 m_pt1.z = reader->getDouble();
7243 break;
7244 case 11:
7245 m_pt2.x = reader->getDouble();
7246 break;
7247 case 21:
7248 m_pt2.y = reader->getDouble();
7249 break;
7250 case 31:
7251 m_pt2.z = reader->getDouble();
7252 break;
7253 case 70: {
7254 std::uint32_t value = 0;
7255 if (!readUnsigned(std::numeric_limits<std::uint16_t>::max(), value))
7256 return false;
7257 m_oleVersion = static_cast<std::uint16_t>(value);
7258 break;
7259 }
7260 case 71: {
7261 std::uint32_t value = 0;
7262 if (!readUnsigned(std::numeric_limits<std::uint16_t>::max(), value))
7263 return false;
7264 m_flags = static_cast<std::uint16_t>(value);
7265 break;
7266 }
7267 case 72: {
7268 std::uint32_t value = 0;
7269 if (!readUnsigned(std::numeric_limits<std::uint16_t>::max(), value))
7270 return false;
7271 m_mode = static_cast<std::uint16_t>(value);
7272 break;
7273 }
7274 case 73: {
7275 std::uint32_t value = 0;
7276 if (!readUnsigned(std::numeric_limits<std::uint8_t>::max(), value))
7277 return false;
7278 m_lockAspect = static_cast<std::uint8_t>(value);
7279 break;
7280 }
7281 case 90: {
7282 std::uint32_t value = 0;
7283 if (!readUnsigned(kMaxOlePayloadBytes, value))
7284 return false;
7285 m_declaredPayloadLength = value;
7286 m_dxfPayloadLengthSpecified = true;
7287 m_payloadPresent = value != 0;
7288 break;
7289 }
7290 case 310: {
7291 std::vector<std::uint8_t> bytes;
7292 if (!decodeHexBytes(reader->getString(), bytes))
7293 return false;
7294 if (!appendBytesChecked(m_payloadBytes, bytes,
7295 kMaxOlePayloadBytes)) {
7296 m_payloadTooLarge = m_payloadBytes.size() > kMaxOlePayloadBytes
7297 || bytes.size() > kMaxOlePayloadBytes -
7298 std::min(m_payloadBytes.size(),
7299 static_cast<std::size_t>(
7300 kMaxOlePayloadBytes));
7301 return false;
7302 }
7303 m_payloadPresent = true;
7304 break;
7305 }
7306 default:
7307 return DRW_Entity::parseCode(code, reader);
7308 }
7309 return true;
7310}
7311
7312void DRW_Ole2Frame::resetDwgState() {
7313 DRW_Entity::reset();
7314 m_flags = 0;
7315 m_mode = 0;
7316 m_declaredPayloadLength = 0;
7317 m_payloadByteCount = 0;
7318 m_payloadStartBit = 0;
7319 m_payloadPresent = false;
7320 m_payloadTruncated = false;
7321 m_payloadTooLarge = false;
7322 m_hasR2000TrailingByte = false;
7323 m_r2000TrailingByte = 0;
7324 m_objectSize = 0;
7325 m_bodyBitSize = 0;
7326 m_payloadBytes.clear();
7327 m_pt1 = DRW_Coord{0.0, 0.0, 0.0};
7328 m_pt2 = DRW_Coord{0.0, 0.0, 0.0};
7329 m_lockAspect = 3;
7330 m_dxfPayloadLengthSpecified = false;
7331 m_oleVersion = 2;
7332 m_oleClient = "OLE";
7333 m_rawBytes.clear();
7334}
7335
7336bool DRW_Ole2Frame::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
7337 resetDwgState();
7338 dwgBuffer *sourceBuf = buf;
7339 auto fail = [this, sourceBuf]() {
7340 if (sourceBuf != nullptr)
7341 sourceBuf->invalidate();
7342 resetDwgState();
7343 return false;
7344 };
7345 if (sourceBuf == nullptr)
7346 return fail();
7347
7348 m_bodyBitSize = bs;
7349 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
7350 if (!DRW_Entity::parseDwgCommon(v, &bodyProbe, bs))
7351 return fail();
7352 DRW_DBG("\n***************************** parsing OLE2FRAME ************************\n")DRW_dbg::dbg("\n***************************** parsing OLE2FRAME ************************\n"
)
;
7353
7354 const std::uint64_t bodyEndBit = dwgDataEndBit;
7355 std::uint16_t parsedFlags = 0;
7356 std::uint16_t parsedMode = 0;
7357 std::int32_t declaredLength = 0;
7358 if (!readBoundedBitShort(bodyProbe, bodyEndBit, parsedFlags)
7359 || (v > DRW::AC1014
7360 && !readBoundedBitShort(bodyProbe, bodyEndBit, parsedMode))
7361 || !readBoundedBitLong(bodyProbe, bodyEndBit, declaredLength)
7362 || declaredLength < 0
7363 || static_cast<std::uint32_t>(declaredLength) > kMaxOlePayloadBytes)
7364 return fail();
7365
7366 const std::uint64_t payloadStartBit = currentDwgBit(&bodyProbe);
7367 const auto bodyRemainingBits = [&](const dwgBuffer& cursor,
7368 std::uint64_t& remaining) {
7369 const std::uint64_t currentBit = currentDwgBit(&cursor);
7370 if (v > DRW::AC1014 && objSize != 0) {
7371 if (currentBit > bodyEndBit)
7372 return false;
7373 remaining = bodyEndBit - currentBit;
7374 return true;
7375 }
7376 const int bytes = cursor.numRemainingBytes();
7377 if (bytes < 0)
7378 return false;
7379 remaining = static_cast<std::uint64_t>(bytes) * 8u;
7380 return true;
7381 };
7382
7383 std::uint64_t remainingBits = 0;
7384 if (!bodyRemainingBits(bodyProbe, remainingBits))
7385 return fail();
7386 const std::uint64_t trailingBits = v > DRW::AC1014 ? 8u : 0u;
7387 if (remainingBits < trailingBits
7388 || static_cast<std::uint64_t>(declaredLength)
7389 > (remainingBits - trailingBits) / 8u)
7390 return fail();
7391
7392 std::vector<std::uint8_t> payload;
7393 if (!DRW::resize(payload, declaredLength))
7394 return fail();
7395 if (!readBoundedBytes(bodyProbe, bodyEndBit, payload.data(),
7396 payload.size()))
7397 return fail();
7398
7399 std::uint8_t trailingByte = 0;
7400 bool hasTrailingByte = false;
7401 if (v > DRW::AC1014) {
7402 if (!bodyRemainingBits(bodyProbe, remainingBits)
7403 || (remainingBits != 0 && remainingBits < 8))
7404 return fail();
7405 if (remainingBits >= 8) {
7406 if (!readBoundedRawChar8(bodyProbe, bodyEndBit, trailingByte))
7407 return fail();
7408 hasTrailingByte = true;
7409 }
7410 }
7411
7412 // Decode the frame rectangle (DXF 10/11) from the OLE header. AutoCAD/ODA do
7413 // NOT store pt1/pt2 as DWG fields; they live in the first ~0x80 bytes of the
7414 // payload as raw little-endian doubles. Guarded so a non-finite/short
7415 // payload simply leaves both points at the origin.
7416 DRW_Coord point1;
7417 DRW_Coord point2;
7418 if (payload.size() >= 0x4a && payload[0] == 0x80) {
7419 auto rd = [&](std::size_t off) {
7420 double value = 0.0;
7421 std::memcpy(&value, payload.data() + off, sizeof(double));
7422 return value;
7423 };
7424 const DRW_Coord upperLeft(rd(0x02), rd(0x0a), rd(0x12));
7425 const DRW_Coord lowerRight(rd(0x32), rd(0x3a), rd(0x42));
7426 if (std::isfinite(upperLeft.x) && std::isfinite(upperLeft.y)
7427 && std::isfinite(upperLeft.z) && std::isfinite(lowerRight.x)
7428 && std::isfinite(lowerRight.y) && std::isfinite(lowerRight.z)) {
7429 point1 = upperLeft;
7430 point2 = lowerRight;
7431 }
7432 }
7433
7434 std::uint64_t handleEndBit = 0;
7435 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
7436 return fail();
7437 dwgBuffer handleProbe = bodyProbe.forkIndependent();
7438 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
7439 || !handleProbe.isGood())
7440 return fail();
7441
7442 m_flags = parsedFlags;
7443 m_mode = parsedMode;
7444 m_declaredPayloadLength = static_cast<std::uint32_t>(declaredLength);
7445 m_payloadStartBit = payloadStartBit;
7446 m_payloadPresent = declaredLength > 0;
7447 m_payloadByteCount = static_cast<std::uint32_t>(declaredLength);
7448 m_payloadBytes = std::move(payload);
7449 m_hasR2000TrailingByte = hasTrailingByte;
7450 m_r2000TrailingByte = trailingByte;
7451 m_pt1 = point1;
7452 m_pt2 = point2;
7453 *sourceBuf = handleProbe;
7454 return true;
7455}
7456
7457// Phase 6.2: OLE2FRAME encoder (fixed oType 74). Inverse of parseDwg, emitting
7458// the captured opaque payload byte-for-byte. Without this override an OLE2FRAME
7459// would encode as a LINE.
7460bool DRW_Ole2Frame::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
7461 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
7462 (void)bs; (void)strBuf;
7463 if (m_payloadTooLarge || m_payloadTruncated
7464 || m_payloadBytes.size() > kMaxOlePayloadBytes
7465 || (m_payloadPresent
7466 && m_declaredPayloadLength != m_payloadBytes.size())) {
7467 return false;
7468 }
7469 oType = 74; // OLE2FRAME class id — see dwgreader.cpp case 74
7470 if (!encodeDwgCommon(version, buf)) return false;
7471
7472 buf->putBitShort(m_flags);
7473 if (version > DRW::AC1014)
7474 buf->putBitShort(m_mode);
7475 // Emit the actual captured length so the reader's data_size matches the
7476 // bytes that follow (avoids a declared-vs-actual mismatch on re-read).
7477 const std::uint32_t payloadLen = static_cast<std::uint32_t>(m_payloadBytes.size());
7478 buf->putBitLong(static_cast<std::int32_t>(payloadLen));
7479 if (payloadLen > 0)
7480 buf->putBytes(m_payloadBytes.data(), m_payloadBytes.size());
7481 // R2000+ Unknown RC (ODA §20.4.88): emitted UNCONDITIONALLY for version >
7482 // AC1014. parseDwg reads it whenever bytes remain before the handle stream
7483 // (which is always — handle data always follows), so gating the write on
7484 // m_hasR2000TrailingByte desynced a directly-constructed OLE2FRAME (the
7485 // default false): the parser consumed the first handle byte as this RC and
7486 // shifted the entity handle stream. Default m_r2000TrailingByte is 0, so
7487 // constructed entities align and round-tripped ones keep the captured byte.
7488 if (version > DRW::AC1014)
7489 buf->putRawChar8(m_r2000TrailingByte);
7490
7491 return encodeDwgEntHandle(version, buf, handleBuf);
7492}
7493
7494bool DRW_OleFrame::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
7495 switch (code) {
7496 case 70: {
7497 const int value = reader->getInt32();
7498 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
7499 return false;
7500 m_flags = static_cast<std::uint16_t>(value);
7501 break;
7502 }
7503 case 90: {
7504 const int value = reader->getInt32();
7505 if (value < 0
7506 || static_cast<std::uint32_t>(value) > kMaxOlePayloadBytes) {
7507 m_payloadTooLarge = value >= 0;
7508 return false;
7509 }
7510 m_declaredPayloadLength = static_cast<std::uint32_t>(value);
7511 m_dxfPayloadLengthSpecified = true;
7512 m_payloadPresent = value != 0;
7513 break;
7514 }
7515 case 310: {
7516 std::vector<std::uint8_t> bytes;
7517 if (!decodeHexBytes(reader->getString(), bytes))
7518 return false;
7519 if (!appendBytesChecked(m_payloadBytes, bytes,
7520 kMaxOlePayloadBytes)) {
7521 m_payloadTooLarge = m_payloadBytes.size() > kMaxOlePayloadBytes
7522 || bytes.size() > kMaxOlePayloadBytes -
7523 std::min(m_payloadBytes.size(),
7524 static_cast<std::size_t>(
7525 kMaxOlePayloadBytes));
7526 return false;
7527 }
7528 m_payloadPresent = true;
7529 break;
7530 }
7531 default:
7532 return DRW_Entity::parseCode(code, reader);
7533 }
7534 return true;
7535}
7536
7537void DRW_OleFrame::resetDwgState() {
7538 DRW_Entity::reset();
7539 m_flags = 0;
7540 m_mode = 0;
7541 m_declaredPayloadLength = 0;
7542 m_payloadByteCount = 0;
7543 m_payloadPresent = false;
7544 m_payloadTruncated = false;
7545 m_payloadTooLarge = false;
7546 m_objectSize = 0;
7547 m_bodyBitSize = 0;
7548 m_payloadBytes.clear();
7549 m_dxfPayloadLengthSpecified = false;
7550 m_rawBytes.clear();
7551}
7552
7553bool DRW_OleFrame::parseDwg(DRW::Version version, dwgBuffer *buf,
7554 std::uint32_t bs) {
7555 resetDwgState();
7556 dwgBuffer *sourceBuf = buf;
7557 auto fail = [this, sourceBuf]() {
7558 if (sourceBuf != nullptr)
7559 sourceBuf->invalidate();
7560 resetDwgState();
7561 return false;
7562 };
7563 if (sourceBuf == nullptr)
7564 return fail();
7565
7566 m_bodyBitSize = bs;
7567 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
7568 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs))
7569 return fail();
7570
7571 const std::uint64_t bodyEndBit = dwgDataEndBit;
7572 std::uint16_t parsedFlags = 0;
7573 std::uint16_t parsedMode = 0;
7574 std::int32_t declaredLength = 0;
7575 if (!readBoundedBitShort(bodyProbe, bodyEndBit, parsedFlags)
7576 || (version > DRW::AC1014
7577 && !readBoundedBitShort(bodyProbe, bodyEndBit, parsedMode))
7578 || !readBoundedBitLong(bodyProbe, bodyEndBit, declaredLength)
7579 || declaredLength < 0
7580 || static_cast<std::uint32_t>(declaredLength) > kMaxOlePayloadBytes)
7581 return fail();
7582
7583 std::uint64_t bodyRemainingBits = 0;
7584 const std::uint64_t currentBit = currentDwgBit(&bodyProbe);
7585 if (version > DRW::AC1014 && objSize != 0) {
7586 if (currentBit > bodyEndBit)
7587 return fail();
7588 bodyRemainingBits = bodyEndBit - currentBit;
7589 } else {
7590 const int remainingBytes = bodyProbe.numRemainingBytes();
7591 if (remainingBytes < 0)
7592 return fail();
7593 bodyRemainingBits = static_cast<std::uint64_t>(remainingBytes) * 8u;
7594 }
7595
7596 if (static_cast<std::uint64_t>(declaredLength) > bodyRemainingBits / 8u)
7597 return fail();
7598 std::vector<std::uint8_t> payload;
7599 if (!DRW::resize(payload, declaredLength))
7600 return fail();
7601 if (!readBoundedBytes(bodyProbe, bodyEndBit, payload.data(),
7602 payload.size()))
7603 return fail();
7604
7605 std::uint64_t handleEndBit = 0;
7606 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
7607 handleEndBit))
7608 return fail();
7609 dwgBuffer handleProbe = bodyProbe.forkIndependent();
7610 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
7611 handleEndBit)
7612 || !handleProbe.isGood())
7613 return fail();
7614
7615 m_flags = parsedFlags;
7616 m_mode = parsedMode;
7617 m_declaredPayloadLength = static_cast<std::uint32_t>(declaredLength);
7618 m_payloadPresent = declaredLength != 0;
7619 m_payloadByteCount = static_cast<std::uint32_t>(declaredLength);
7620 m_payloadBytes = std::move(payload);
7621 *sourceBuf = handleProbe;
7622 return true;
7623
7624}
7625
7626bool DRW_OleFrame::encodeDwg(DRW::Version version, dwgBufferW *buf,
7627 std::uint32_t bs, dwgBufferW *strBuf,
7628 dwgBufferW *handleBuf) {
7629 (void)bs;
7630 (void)strBuf;
7631 if (buf == nullptr || m_payloadTooLarge || m_payloadTruncated
7632 || m_payloadBytes.size() > kMaxOlePayloadBytes
7633 || (m_payloadPresent
7634 && m_declaredPayloadLength != m_payloadBytes.size())) {
7635 return false;
7636 }
7637
7638 oType = 43;
7639 if (!encodeDwgCommon(version, buf))
7640 return false;
7641 buf->putBitShort(m_flags);
7642 if (version > DRW::AC1014)
7643 buf->putBitShort(m_mode);
7644 const std::uint32_t payloadLength =
7645 static_cast<std::uint32_t>(m_payloadBytes.size());
7646 buf->putBitLong(static_cast<std::int32_t>(payloadLength));
7647 if (payloadLength != 0)
7648 buf->putBytes(m_payloadBytes.data(), m_payloadBytes.size());
7649 return encodeDwgEntHandle(version, buf, handleBuf);
7650}
7651
7652void DRW_Light::resetDwgState() {
7653 DRW_Entity::reset();
7654 m_classVersion = 0;
7655 m_name.clear();
7656 m_type = 0;
7657 m_status = false;
7658 m_color = 0;
7659 m_plotGlyph = false;
7660 m_intensity = 0.0;
7661 m_position = DRW_Coord{0.0, 0.0, 0.0};
7662 m_target = DRW_Coord{0.0, 0.0, 0.0};
7663 m_attenuationType = 0;
7664 m_useAttenuationLimits = false;
7665 m_attenuationStartLimit = 0.0;
7666 m_attenuationEndLimit = 0.0;
7667 m_hotspotAngle = 0.0;
7668 m_falloffAngle = 0.0;
7669 m_castShadows = false;
7670 m_shadowType = 0;
7671 m_shadowMapSize = 0;
7672 m_shadowMapSoftness = 0;
7673 m_hasPhotometricData = false;
7674 m_hasWebFile = false;
7675 m_webFile.clear();
7676 m_physicalIntensityMethod = 0;
7677 m_physicalIntensity = 0.0;
7678 m_illuminanceDistance = 0.0;
7679 m_lampColorType = 0;
7680 m_lampColorTemperature = 0.0;
7681 m_lampColorPreset = 0;
7682 m_webRotation = {1.0, 0.0, 0.0};
7683 m_extendedLightShape = 0;
7684 m_extendedLightLength = 0.0;
7685 m_extendedLightWidth = 0.0;
7686 m_extendedLightRadius = 0.0;
7687}
7688
7689bool DRW_Light::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
7690 resetDwgState();
7691 dwgBuffer *sourceBuf = buf;
7692 auto fail = [this, sourceBuf]() {
7693 if (sourceBuf != nullptr)
7694 sourceBuf->invalidate();
7695 resetDwgState();
7696 return false;
7697 };
7698 if (sourceBuf == nullptr)
7699 return fail();
7700
7701 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
7702 dwgBuffer stringProbe = sourceBuf->forkIndependent();
7703 dwgBuffer *stringStream = v > DRW::AC1018 ? &stringProbe : nullptr;
7704 if (!DRW_Entity::parseDwg(v, &bodyProbe, stringStream, bs)
7705 || !bodyProbe.isGood() || !stringProbe.isGood())
7706 return fail();
7707 DRW_DBG("\n***************************** parsing LIGHT *****************************\n")DRW_dbg::dbg("\n***************************** parsing LIGHT *****************************\n"
)
;
7708
7709 const std::uint64_t bodyDataEndBit = dwgDataEndBit;
7710 std::uint64_t stringEndBit = bodyDataEndBit;
7711 if (stringStream != nullptr && v > DRW::AC1021) {
7712 std::uint64_t ignoredBodyEndBit = 0;
7713 if (!entityBodyDataEndBit(*stringStream, v, objSize,
7714 ignoredBodyEndBit, &stringEndBit))
7715 return fail();
7716 }
7717
7718 std::int32_t parsedClassVersion = 0;
7719 UTF8STRINGstd::string parsedName;
7720 std::int32_t parsedType = 0;
7721 bool parsedStatus = false;
7722 std::uint32_t parsedColor = 0;
7723 bool parsedPlotGlyph = false;
7724 double parsedIntensity = 0.0;
7725 DRW_Coord parsedPosition;
7726 DRW_Coord parsedTarget;
7727 std::int32_t parsedAttenuationType = 0;
7728 bool parsedUseAttenuationLimits = false;
7729 double parsedAttenuationStartLimit = 0.0;
7730 double parsedAttenuationEndLimit = 0.0;
7731 double parsedHotspotAngle = 0.0;
7732 double parsedFalloffAngle = 0.0;
7733 bool parsedCastShadows = false;
7734 std::int32_t parsedShadowType = 0;
7735 std::uint16_t parsedShadowMapSize = 0;
7736 std::uint8_t parsedShadowMapSoftness = 0;
7737 if (!readBoundedBitLong(bodyProbe, bodyDataEndBit, parsedClassVersion)
7738 || (stringStream != nullptr
7739 ? !readBoundedVariableText(stringProbe, stringEndBit, v,
7740 parsedName)
7741 : !readBoundedVariableText(bodyProbe, bodyDataEndBit, v,
7742 parsedName))
7743 || !readBoundedBitLong(bodyProbe, bodyDataEndBit, parsedType)
7744 || !readBoundedBit(bodyProbe, bodyDataEndBit, parsedStatus)
7745 || !readBoundedCmColor(bodyProbe, stringStream, bodyDataEndBit, v,
7746 parsedColor, nullptr, nullptr, nullptr,
7747 nullptr, stringEndBit)
7748 || !readBoundedBit(bodyProbe, bodyDataEndBit, parsedPlotGlyph)
7749 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit, parsedIntensity)
7750 || !readBoundedBitCoord(bodyProbe, bodyDataEndBit, parsedPosition)
7751 || !readBoundedBitCoord(bodyProbe, bodyDataEndBit, parsedTarget)
7752 || !readBoundedBitLong(bodyProbe, bodyDataEndBit,
7753 parsedAttenuationType)
7754 || !readBoundedBit(bodyProbe, bodyDataEndBit,
7755 parsedUseAttenuationLimits)
7756 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7757 parsedAttenuationStartLimit)
7758 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7759 parsedAttenuationEndLimit)
7760 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7761 parsedHotspotAngle)
7762 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7763 parsedFalloffAngle)
7764 || !readBoundedBit(bodyProbe, bodyDataEndBit, parsedCastShadows)
7765 || !readBoundedBitLong(bodyProbe, bodyDataEndBit, parsedShadowType)
7766 || !readBoundedBitShort(bodyProbe, bodyDataEndBit,
7767 parsedShadowMapSize)
7768 || !readBoundedRawChar8(bodyProbe, bodyDataEndBit,
7769 parsedShadowMapSoftness))
7770 return fail();
7771
7772 bool hasPhotometricData = false;
7773 bool hasWebFile = false;
7774 UTF8STRINGstd::string webFile;
7775 std::uint16_t physicalIntensityMethod = 0;
7776 double physicalIntensity = 0.0;
7777 double illuminanceDistance = 0.0;
7778 std::uint16_t lampColorType = 0;
7779 double lampColorTemperature = 0.0;
7780 std::uint16_t lampColorPreset = 0;
7781 DRW_Coord webRotation{1.0, 0.0, 0.0};
7782 std::uint16_t extendedLightShape = 0;
7783 double extendedLightLength = 0.0;
7784 double extendedLightWidth = 0.0;
7785 double extendedLightRadius = 0.0;
7786 if (v > DRW::AC1018
7787 && proxyEntityHasBits(bodyProbe, bodyDataEndBit, 1)) {
7788 if (!readBoundedBit(bodyProbe, bodyDataEndBit, hasPhotometricData))
7789 return fail();
7790 if (hasPhotometricData) {
7791 if (!readBoundedBit(bodyProbe, bodyDataEndBit, hasWebFile)
7792 || !readBoundedVariableText(stringProbe, stringEndBit, v,
7793 webFile)
7794 || !readBoundedBitShort(bodyProbe, bodyDataEndBit,
7795 physicalIntensityMethod)
7796 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7797 physicalIntensity)
7798 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7799 illuminanceDistance)
7800 || !readBoundedBitShort(bodyProbe, bodyDataEndBit,
7801 lampColorType)
7802 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7803 lampColorTemperature)
7804 || !readBoundedBitShort(bodyProbe, bodyDataEndBit,
7805 lampColorPreset)
7806 || !readBoundedBitCoord(bodyProbe, bodyDataEndBit,
7807 webRotation)
7808 || !readBoundedBitShort(bodyProbe, bodyDataEndBit,
7809 extendedLightShape)
7810 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7811 extendedLightLength)
7812 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7813 extendedLightWidth)
7814 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7815 extendedLightRadius))
7816 return fail();
7817 }
7818 }
7819
7820 const auto finite = [](const DRW_Coord& point) {
7821 return std::isfinite(point.x) && std::isfinite(point.y)
7822 && std::isfinite(point.z);
7823 };
7824 if (!bodyProbe.isGood() || !stringProbe.isGood()
7825 || currentDwgBit(&bodyProbe) > bodyDataEndBit
7826 || (stringStream != nullptr && currentDwgBit(&stringProbe) > stringEndBit)
7827 || parsedClassVersion < 0 || parsedType < 0
7828 || parsedAttenuationType < 0 || parsedShadowType < 0
7829 || !finite(parsedPosition) || !finite(parsedTarget)
7830 || !finite(webRotation)
7831 || !std::isfinite(parsedIntensity)
7832 || !std::isfinite(parsedAttenuationStartLimit)
7833 || !std::isfinite(parsedAttenuationEndLimit)
7834 || !std::isfinite(parsedHotspotAngle)
7835 || !std::isfinite(parsedFalloffAngle)
7836 || !std::isfinite(physicalIntensity)
7837 || !std::isfinite(illuminanceDistance)
7838 || !std::isfinite(lampColorTemperature)
7839 || !std::isfinite(extendedLightLength)
7840 || !std::isfinite(extendedLightWidth)
7841 || !std::isfinite(extendedLightRadius))
7842 return fail();
7843
7844 std::uint64_t handleEndBit = 0;
7845 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
7846 return fail();
7847 dwgBuffer handleProbe = bodyProbe.forkIndependent();
7848 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
7849 || !handleProbe.isGood())
7850 return fail();
7851
7852 m_classVersion = static_cast<std::uint32_t>(parsedClassVersion);
7853 m_name = std::move(parsedName);
7854 m_type = static_cast<std::uint32_t>(parsedType);
7855 m_status = parsedStatus;
7856 m_color = parsedColor;
7857 m_plotGlyph = parsedPlotGlyph;
7858 m_intensity = parsedIntensity;
7859 m_position = parsedPosition;
7860 m_target = parsedTarget;
7861 m_attenuationType = static_cast<std::uint32_t>(parsedAttenuationType);
7862 m_useAttenuationLimits = parsedUseAttenuationLimits;
7863 m_attenuationStartLimit = parsedAttenuationStartLimit;
7864 m_attenuationEndLimit = parsedAttenuationEndLimit;
7865 m_hotspotAngle = parsedHotspotAngle;
7866 m_falloffAngle = parsedFalloffAngle;
7867 m_castShadows = parsedCastShadows;
7868 m_shadowType = static_cast<std::uint32_t>(parsedShadowType);
7869 m_shadowMapSize = parsedShadowMapSize;
7870 m_shadowMapSoftness = parsedShadowMapSoftness;
7871 m_hasPhotometricData = hasPhotometricData;
7872 m_hasWebFile = hasWebFile;
7873 m_webFile = std::move(webFile);
7874 m_physicalIntensityMethod = physicalIntensityMethod;
7875 m_physicalIntensity = physicalIntensity;
7876 m_illuminanceDistance = illuminanceDistance;
7877 m_lampColorType = lampColorType;
7878 m_lampColorTemperature = lampColorTemperature;
7879 m_lampColorPreset = lampColorPreset;
7880 m_webRotation = webRotation;
7881 m_extendedLightShape = extendedLightShape;
7882 m_extendedLightLength = extendedLightLength;
7883 m_extendedLightWidth = extendedLightWidth;
7884 m_extendedLightRadius = extendedLightRadius;
7885 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");
7886 *sourceBuf = handleProbe;
7887 return true;
7888}
7889
7890bool DRW_Camera::parseCode(int code,
7891 const std::unique_ptr<dxfReader>& reader) {
7892 if (code == 340) {
7893 m_viewHandle = reader->getHandleString();
7894 return true;
7895 }
7896 return DRW_Entity::parseCode(code, reader);
7897}
7898
7899void DRW_Camera::resetDwgState() {
7900 DRW_Entity::reset();
7901 m_viewHandle = 0;
7902}
7903
7904bool DRW_Camera::parseDwg(DRW::Version v, dwgBuffer *buf,
7905 std::uint32_t bs) {
7906 resetDwgState();
7907 dwgBuffer *sourceBuf = buf;
7908 auto fail = [this, sourceBuf]() {
7909 if (sourceBuf != nullptr)
7910 sourceBuf->invalidate();
7911 resetDwgState();
7912 return false;
7913 };
7914 if (sourceBuf == nullptr || v < DRW::AC1015)
7915 return fail();
7916
7917 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
7918 dwgBuffer stringProbe = sourceBuf->forkIndependent();
7919 if (!DRW_Entity::parseDwg(
7920 v, &bodyProbe, v > DRW::AC1018 ? &stringProbe : nullptr, bs)
7921 || !bodyProbe.isGood() || !stringProbe.isGood())
7922 return fail();
7923
7924 std::uint64_t handleEndBit = 0;
7925 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
7926 return fail();
7927 dwgBuffer handleProbe = bodyProbe.forkIndependent();
7928 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
7929 || !handleProbe.isGood())
7930 return fail();
7931
7932 std::uint32_t viewHandle = 0;
7933 if (proxyEntityHasBits(handleProbe, handleEndBit, 8)) {
7934 dwgHandle viewH;
7935 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false, viewH))
7936 return fail();
7937 viewHandle = viewH.ref;
7938 } else if (currentDwgBit(&handleProbe) != handleEndBit) {
7939 return fail();
7940 }
7941
7942 m_viewHandle = viewHandle;
7943 *sourceBuf = handleProbe;
7944 return true;
7945}
7946
7947bool DRW_Camera::encodeDwg(DRW::Version v, dwgBufferW *buf,
7948 std::uint32_t bs, dwgBufferW *strBuf,
7949 dwgBufferW *handleBuf) {
7950 (void)bs;
7951 if (v < DRW::AC1015 || buf == nullptr)
7952 return false;
7953
7954 oType = kDwgClassNum;
7955 if (!encodeDwgCommon(v, buf, strBuf)
7956 || !encodeDwgEntHandle(v, buf, handleBuf)) {
7957 return false;
7958 }
7959
7960 putHardPointerHandle(handleBuf ? handleBuf : buf, m_viewHandle);
7961 return true;
7962}
7963
7964bool DRW_GeoPositionMarker::parseCode(
7965 int code, const std::unique_ptr<dxfReader>& reader) {
7966 switch (code) {
7967 case 90:
7968 {
7969 int value = 0;
7970 if (!readDxfIntInRange(reader, 0, std::numeric_limits<int>::max(), value))
7971 return false;
7972 m_classVersion = static_cast<std::uint32_t>(value);
7973 }
7974 return true;
7975 case 10:
7976 m_position.x = reader->getDouble();
7977 return true;
7978 case 20:
7979 m_position.y = reader->getDouble();
7980 return true;
7981 case 30:
7982 m_position.z = reader->getDouble();
7983 return true;
7984 case 40:
7985 if (m_dxfDouble40Count++ == 0)
7986 m_radius = reader->getDouble();
7987 else if (m_dxfDouble40Count == 2)
7988 m_landingGap = reader->getDouble();
7989 else
7990 (void)reader->getDouble();
7991 return true;
7992 case 1:
7993 m_notes = reader->getUtf8String();
7994 return true;
7995 case 280:
7996 {
7997 const int value = reader->getInt32();
7998 if (value < 0 || value > std::numeric_limits<std::uint8_t>::max())
7999 return false;
8000 m_textAlignment = static_cast<std::uint8_t>(value);
8001 }
8002 return true;
8003 case 290:
8004 {
8005 int value = 0;
8006 if (!readDxfIntInRange(reader, 0, 1, value))
8007 return false;
8008 if (m_dxfBool290Count++ == 0)
8009 m_mtextVisible = value != 0;
8010 else if (m_dxfBool290Count == 2)
8011 m_enableFrameText = value != 0;
8012 }
8013 return true;
8014 default:
8015 return DRW_Entity::parseCode(code, reader);
8016 }
8017}
8018
8019bool DRW_GeoPositionMarker::parseDxfVariant(const DRW_Variant& value) {
8020 switch (value.code()) {
8021 case 90:
8022 if (value.type() != DRW_Variant::INTEGER)
8023 return false;
8024 if (value.i_val() < 0)
8025 return false;
8026 m_classVersion = static_cast<std::uint32_t>(value.i_val());
8027 return true;
8028 case 10:
8029 if (value.type() != DRW_Variant::DOUBLE)
8030 return false;
8031 m_position.x = value.d_val();
8032 return true;
8033 case 20:
8034 if (value.type() != DRW_Variant::DOUBLE)
8035 return false;
8036 m_position.y = value.d_val();
8037 return true;
8038 case 30:
8039 if (value.type() != DRW_Variant::DOUBLE)
8040 return false;
8041 m_position.z = value.d_val();
8042 return true;
8043 case 40:
8044 if (value.type() != DRW_Variant::DOUBLE)
8045 return false;
8046 if (m_dxfDouble40Count++ == 0)
8047 m_radius = value.d_val();
8048 else if (m_dxfDouble40Count == 2)
8049 m_landingGap = value.d_val();
8050 return true;
8051 case 1:
8052 if (value.type() != DRW_Variant::STRING)
8053 return false;
8054 m_notes = value.c_str();
8055 return true;
8056 case 280:
8057 if (value.type() != DRW_Variant::INTEGER)
8058 return false;
8059 if (value.i_val() < 0
8060 || value.i_val() > std::numeric_limits<std::uint8_t>::max())
8061 return false;
8062 m_textAlignment = static_cast<std::uint8_t>(value.i_val());
8063 return true;
8064 case 290:
8065 if (value.type() != DRW_Variant::INTEGER)
8066 return false;
8067 if (value.i_val() < 0 || value.i_val() > 1)
8068 return false;
8069 if (m_dxfBool290Count++ == 0)
8070 m_mtextVisible = value.i_val() != 0;
8071 else if (m_dxfBool290Count == 2)
8072 m_enableFrameText = value.i_val() != 0;
8073 return true;
8074 default:
8075 return true; // Common and unknown groups remain raw-authoritative.
8076 }
8077}
8078
8079void DRW_GeoPositionMarker::resetDwgState() {
8080 DRW_Entity::reset();
8081 m_classVersion = 0;
8082 m_position = DRW_Coord{0.0, 0.0, 0.0};
8083 m_radius = 0.0;
8084 m_notes.clear();
8085 m_landingGap = 0.0;
8086 m_mtextVisible = false;
8087 m_textAlignment = 0;
8088 m_enableFrameText = false;
8089 mtext.reset();
8090 m_dxfDouble40Count = 0;
8091 m_dxfBool290Count = 0;
8092}
8093
8094bool DRW_GeoPositionMarker::parseDwg(DRW::Version v, dwgBuffer *buf,
8095 std::uint32_t bs) {
8096 resetDwgState();
8097 dwgBuffer *sourceBuf = buf;
8098 auto fail = [this, sourceBuf]() {
8099 if (sourceBuf != nullptr)
8100 sourceBuf->invalidate();
8101 resetDwgState();
8102 return false;
8103 };
8104 if (sourceBuf == nullptr || v < DRW::AC1027)
8105 return fail();
8106
8107 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
8108 dwgBuffer stringProbe = sourceBuf->forkIndependent();
8109 if (!DRW_Entity::parseDwg(v, &bodyProbe, &stringProbe, bs)
8110 || !bodyProbe.isGood() || !stringProbe.isGood())
8111 return fail();
8112
8113 const std::uint64_t bodyEndBit = dwgDataEndBit;
8114 std::uint64_t stringStartBit = bodyEndBit;
8115 std::uint64_t stringEndBit = bodyEndBit;
8116 if (v > DRW::AC1018
8117 && (!bodyProbe.getR2007StringStreamBounds(objSize, stringStartBit,
8118 stringEndBit)
8119 || currentDwgBit(&stringProbe) > stringEndBit))
8120 return fail();
8121
8122 std::int32_t parsedClassVersion = 0;
8123 DRW_Coord position;
8124 double radius = 0.0;
8125 UTF8STRINGstd::string notes;
8126 double landingGap = 0.0;
8127 bool mtextVisible = false;
8128 std::uint8_t textAlignment = 0;
8129 bool enableFrameText = false;
8130 if (!readBoundedBitLong(bodyProbe, bodyEndBit, parsedClassVersion)
8131 || !readBoundedBitCoord(bodyProbe, bodyEndBit, position)
8132 || !readBoundedBitDouble(bodyProbe, bodyEndBit, radius)
8133 || !readBoundedVariableText(stringProbe, stringEndBit, v, notes)
8134 || !readBoundedBitDouble(bodyProbe, bodyEndBit, landingGap)
8135 || !readBoundedBit(bodyProbe, bodyEndBit, mtextVisible)
8136 || !readBoundedRawChar8(bodyProbe, bodyEndBit, textAlignment)
8137 || !readBoundedBit(bodyProbe, bodyEndBit, enableFrameText))
8138 return fail();
8139
8140 const auto finite = [](const DRW_Coord& point) {
8141 return std::isfinite(point.x) && std::isfinite(point.y)
8142 && std::isfinite(point.z);
8143 };
8144 if (!bodyProbe.isGood() || !stringProbe.isGood()
8145 || !finite(position) || !std::isfinite(radius)
8146 || !std::isfinite(landingGap))
8147 return fail();
8148
8149 // AcDbMTextObjectEmbedded follows the marker body when framing is enabled.
8150 // Parse it on a bounded probe before the parent and embedded handle passes;
8151 // otherwise a truncated nested body can consume parent handle bytes.
8152 dwgBuffer bodyTailProbe = bodyProbe.forkIndependent();
8153 std::unique_ptr<DRW_MText> parsedMText;
8154 EmbeddedMTextHandleInfo embeddedHandles;
8155 if (enableFrameText) {
8156 if (objSize == 0
8157 || !(parsedMText = std::make_unique<DRW_MText>())
8158 || !parseEmbeddedMTextDwg(v, &bodyTailProbe, &stringProbe,
8159 *parsedMText, embeddedHandles,
8160 EmbeddedMTextForm::Short,
8161 bodyEndBit, stringEndBit)
8162 || !bodyTailProbe.isGood() || !stringProbe.isGood())
8163 return fail();
8164 }
8165
8166 std::uint64_t handleEndBit = 0;
8167 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
8168 return fail();
8169
8170 dwgBuffer handleProbe = bodyTailProbe.forkIndependent();
8171 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
8172 || !handleProbe.isGood())
8173 return fail();
8174 if (enableFrameText) {
8175 if (!consumeEmbeddedMTextHandles(v, &handleProbe, embeddedHandles,
8176 parsedMText.get(), handleEndBit))
8177 return fail();
8178 }
8179
8180 m_classVersion = static_cast<std::uint32_t>(parsedClassVersion);
8181 m_position = position;
8182 m_radius = radius;
8183 m_notes = notes;
8184 m_landingGap = landingGap;
8185 m_mtextVisible = mtextVisible;
8186 m_textAlignment = textAlignment;
8187 m_enableFrameText = enableFrameText;
8188 mtext = std::move(parsedMText);
8189 *sourceBuf = handleProbe;
8190 return true;
8191}
8192
8193bool DRW_Light::encodeDwg(DRW::Version v, dwgBufferW *buf, std::uint32_t bs,
8194 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
8195 (void)bs;
8196 if (v < DRW::AC1021)
8197 return false;
8198
8199 oType = kDwgClassNum;
8200 if (!encodeDwgCommon(v, buf, strBuf))
8201 return false;
8202
8203 dwgBufferW *sb = strBuf ? strBuf : buf;
8204 buf->putBitLong(m_classVersion);
8205 sb->putVariableText(v, m_name);
8206 buf->putBitLong(m_type);
8207 buf->putBit(m_status ? 1 : 0);
8208 buf->putCmColor(v, static_cast<std::uint16_t>(m_color));
8209 buf->putBit(m_plotGlyph ? 1 : 0);
8210 buf->putBitDouble(m_intensity);
8211 buf->put3BitDouble(m_position);
8212 buf->put3BitDouble(m_target);
8213 buf->putBitLong(m_attenuationType);
8214 buf->putBit(m_useAttenuationLimits ? 1 : 0);
8215 buf->putBitDouble(m_attenuationStartLimit);
8216 buf->putBitDouble(m_attenuationEndLimit);
8217 buf->putBitDouble(m_hotspotAngle);
8218 buf->putBitDouble(m_falloffAngle);
8219 buf->putBit(m_castShadows ? 1 : 0);
8220 buf->putBitLong(m_shadowType);
8221 buf->putBitShort(m_shadowMapSize);
8222 buf->putRawChar8(m_shadowMapSoftness);
8223
8224 buf->putBit(m_hasPhotometricData ? 1 : 0);
8225 if (m_hasPhotometricData) {
8226 buf->putBit(m_hasWebFile ? 1 : 0);
8227 sb->putVariableText(v, m_webFile);
8228 buf->putBitShort(m_physicalIntensityMethod);
8229 buf->putBitDouble(m_physicalIntensity);
8230 buf->putBitDouble(m_illuminanceDistance);
8231 buf->putBitShort(m_lampColorType);
8232 buf->putBitDouble(m_lampColorTemperature);
8233 buf->putBitShort(m_lampColorPreset);
8234 buf->put3BitDouble(m_webRotation);
8235 buf->putBitShort(m_extendedLightShape);
8236 buf->putBitDouble(m_extendedLightLength);
8237 buf->putBitDouble(m_extendedLightWidth);
8238 buf->putBitDouble(m_extendedLightRadius);
8239 }
8240
8241 return encodeDwgEntHandle(v, buf, handleBuf);
8242}
8243
8244bool DRW_SectionObject::parseCode(
8245 int code, const std::unique_ptr<dxfReader>& reader) {
8246 if (code != 100 && !m_dxfInBody)
8247 return DRW_Entity::parseCode(code, reader);
8248 switch (code) {
8249 case 100:
8250 m_dxfInBody = reader->getUtf8String() == "AcDbSection";
8251 return true;
8252 case 90:
8253 m_state = static_cast<std::uint32_t>(reader->getInt32());
8254 return true;
8255 case 91:
8256 m_flags = static_cast<std::uint32_t>(reader->getInt32());
8257 return true;
8258 case 1:
8259 m_name = reader->getUtf8String();
8260 return true;
8261 case 10:
8262 m_vertDir.x = reader->getDouble();
8263 return true;
8264 case 20:
8265 m_vertDir.y = reader->getDouble();
8266 return true;
8267 case 30:
8268 m_vertDir.z = reader->getDouble();
8269 return true;
8270 case 40:
8271 m_topHeight = reader->getDouble();
8272 return true;
8273 case 41:
8274 m_bottomHeight = reader->getDouble();
8275 return true;
8276 case 70:
8277 {
8278 const int value = reader->getInt32();
8279 if (value < 0 || value > 0xFFFF)
8280 return false;
8281 m_indicatorAlpha = static_cast<std::uint16_t>(value);
8282 }
8283 return true;
8284 case 62:
8285 m_indicatorColor = static_cast<std::uint32_t>(reader->getInt32());
8286 return true;
8287 case 92:
8288 {
8289 const int value = reader->getInt32();
8290 if (value < 0
8291 || static_cast<std::size_t>(value) > DRW_SectionObject::kMaxVertices)
8292 return false;
8293 m_verts.clear();
8294 if (!DRW::reserve(m_verts, value))
8295 return false;
8296 return true;
8297 }
8298 case 93:
8299 {
8300 const int value = reader->getInt32();
8301 if (value < 0
8302 || static_cast<std::size_t>(value) > DRW_SectionObject::kMaxVertices)
8303 return false;
8304 m_blVerts.clear();
8305 if (!DRW::reserve(m_blVerts, value))
8306 return false;
8307 return true;
8308 }
8309 case 11:
8310 if (m_verts.size() >= DRW_SectionObject::kMaxVertices)
8311 return false;
8312 m_verts.emplace_back();
8313 m_verts.back().x = reader->getDouble();
8314 return true;
8315 case 21:
8316 if (m_verts.empty())
8317 return false;
8318 m_verts.back().y = reader->getDouble();
8319 return true;
8320 case 31:
8321 if (m_verts.empty())
8322 return false;
8323 m_verts.back().z = reader->getDouble();
8324 return true;
8325 case 12:
8326 if (m_blVerts.size() >= DRW_SectionObject::kMaxVertices)
8327 return false;
8328 m_blVerts.emplace_back();
8329 m_blVerts.back().x = reader->getDouble();
8330 return true;
8331 case 22:
8332 if (m_blVerts.empty())
8333 return false;
8334 m_blVerts.back().y = reader->getDouble();
8335 return true;
8336 case 32:
8337 if (m_blVerts.empty())
8338 return false;
8339 m_blVerts.back().z = reader->getDouble();
8340 return true;
8341 case 360:
8342 m_sectionSettingsHandle = reader->getHandleString();
8343 return true;
8344 default:
8345 return DRW_Entity::parseCode(code, reader);
8346 }
8347}
8348
8349// DRW_SectionObject::parseDwg — SECTIONOBJECT / AcDbSection, field order per
8350// libreDWG dwg2.spec DWG_ENTITY(SECTIONOBJECT): BL state, BL flags, T name,
8351// 3BD vert_dir, BD top/bottom height, BS indicator_alpha, CMTC indicator_color,
8352// BL num_verts + verts, BL num_blverts + blverts; then the common entity handle
8353// data followed by the section_settings hard reference (H 5, 360).
8354void DRW_SectionObject::resetDwgState() {
8355 DRW_Entity::reset();
8356 m_state = 0;
8357 m_flags = 0;
8358 m_name.clear();
8359 m_vertDir = DRW_Coord{0.0, 0.0, 0.0};
8360 m_topHeight = 0.0;
8361 m_bottomHeight = 0.0;
8362 m_indicatorAlpha = 0;
8363 m_indicatorColor = 0;
8364 m_verts.clear();
8365 m_blVerts.clear();
8366 m_sectionSettingsHandle = 0;
8367 m_dxfInBody = false;
8368}
8369
8370bool DRW_SectionObject::parseDwg(DRW::Version v, dwgBuffer *buf,
8371 std::uint32_t bs) {
8372 resetDwgState();
8373 dwgBuffer *sourceBuf = buf;
8374 auto fail = [this, sourceBuf]() {
8375 if (sourceBuf != nullptr)
8376 sourceBuf->invalidate();
8377 resetDwgState();
8378 return false;
8379 };
8380 if (sourceBuf == nullptr)
8381 return fail();
8382
8383 // R2007+ stores text in a separate stream. Independent cursors keep a
8384 // malformed field from consuming the source or a later handle stream.
8385 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
8386 dwgBuffer stringProbe = sourceBuf->forkIndependent();
8387 dwgBuffer *stringStream = v > DRW::AC1018 ? &stringProbe : &bodyProbe;
8388 if (!DRW_Entity::parseDwg(v, &bodyProbe, stringStream, bs)
8389 || !bodyProbe.isGood() || !stringStream->isGood())
8390 return fail();
8391 DRW_DBG("\n***************************** parsing SECTIONOBJECT *********************\n")DRW_dbg::dbg("\n***************************** parsing SECTIONOBJECT *********************\n"
)
;
8392
8393 // The common entity parser leaves bodyProbe at the first SECTIONOBJECT
8394 // field and stringStream at the detached string stream for R2007+.
8395 // Keep both cursors inside their declared data ranges while staging the
8396 // complete header before publishing it.
8397 const std::uint64_t bodyEndBit = tableBodyEndBit(v, stringStream, objSize);
8398 const std::uint64_t stringEndBit = v > DRW::AC1018
8399 ? objSize : bodyEndBit;
8400 std::int32_t stateValue = 0;
8401 std::int32_t flagsValue = 0;
8402 UTF8STRINGstd::string name;
8403 DRW_Coord vertDir;
8404 double topHeight = 0.0;
8405 double bottomHeight = 0.0;
8406 std::uint16_t indicatorAlpha = 0;
8407 std::uint32_t indicatorColor = 0;
8408 if (!readBoundedBitLong(bodyProbe, bodyEndBit, stateValue)
8409 || !readBoundedBitLong(bodyProbe, bodyEndBit, flagsValue)
8410 || !readBoundedVariableText(*stringStream, stringEndBit, v, name)
8411 || !readBoundedBitCoord(bodyProbe, bodyEndBit, vertDir)
8412 || !readBoundedBitDouble(bodyProbe, bodyEndBit, topHeight)
8413 || !readBoundedBitDouble(bodyProbe, bodyEndBit, bottomHeight)
8414 || !readBoundedBitShort(bodyProbe, bodyEndBit, indicatorAlpha)
8415 || !readBoundedCmColor(bodyProbe, stringStream, bodyEndBit, v,
8416 indicatorColor, nullptr, nullptr, nullptr,
8417 nullptr, stringEndBit))
8418 return fail();
8419
8420 std::uint32_t numVerts = 0;
8421 if (!readTableBodyCount(
8422 v, &bodyProbe, bodyEndBit,
8423 static_cast<std::uint32_t>(DRW_SectionObject::kMaxVertices), 6,
8424 numVerts)
8425 || !proxyEntityHasBits(
8426 bodyProbe, bodyEndBit, static_cast<std::uint64_t>(numVerts) * 6u))
8427 return fail();
8428 std::vector<DRW_Coord> verts;
8429 if (!DRW::reserve(verts, static_cast<int>(numVerts)))
8430 return fail();
8431 for (std::uint32_t i = 0; i < numVerts; ++i) {
8432 DRW_Coord vertex;
8433 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, vertex))
8434 return fail();
8435 verts.push_back(vertex);
8436 }
8437
8438 std::uint32_t numBl = 0;
8439 if (!readTableBodyCount(
8440 v, &bodyProbe, bodyEndBit,
8441 static_cast<std::uint32_t>(DRW_SectionObject::kMaxVertices), 6,
8442 numBl)
8443 || !proxyEntityHasBits(
8444 bodyProbe, bodyEndBit, static_cast<std::uint64_t>(numBl) * 6u))
8445 return fail();
8446 std::vector<DRW_Coord> blVerts;
8447 if (!DRW::reserve(blVerts, static_cast<int>(numBl)))
8448 return fail();
8449 for (std::uint32_t i = 0; i < numBl; ++i) {
8450 DRW_Coord vertex;
8451 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, vertex))
8452 return fail();
8453 blVerts.push_back(vertex);
8454 }
8455
8456 const auto finite = [](const DRW_Coord& point) {
8457 return std::isfinite(point.x) && std::isfinite(point.y)
8458 && std::isfinite(point.z);
8459 };
8460 if (!bodyProbe.isGood() || !stringStream->isGood()
8461 || currentDwgBit(&bodyProbe) > bodyEndBit
8462 || !finite(vertDir) || !std::isfinite(topHeight)
8463 || !std::isfinite(bottomHeight))
8464 return fail();
8465 if (!std::all_of(verts.begin(), verts.end(), finite)
8466 || !std::all_of(blVerts.begin(), blVerts.end(), finite))
8467 return fail();
8468
8469 // The common entity handle stream starts at the object-data boundary for
8470 // R2007+ and follows the body inline in older versions. The section
8471 // settings reference is a hard pointer (H 5), after those common handles.
8472 std::uint64_t handleEndBit = 0;
8473 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
8474 return fail();
8475 dwgBuffer handleProbe = bodyProbe.forkIndependent();
8476 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
8477 || !handleProbe.isGood())
8478 return fail();
8479 std::uint32_t sectionSettingsHandle = 0;
8480 if (proxyEntityHasBits(handleProbe, handleEndBit, 8)) {
8481 dwgHandle settingsHandle;
8482 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
8483 settingsHandle))
8484 return fail();
8485 sectionSettingsHandle = settingsHandle.ref;
8486 DRW_DBG(" section_settings Handle: ")DRW_dbg::dbg(" section_settings Handle: ");
8487 DRW_DBGHL(settingsHandle.code, settingsHandle.size,DRW_dbg::dbgHL(settingsHandle.code, settingsHandle.size, settingsHandle
.ref)
8488 settingsHandle.ref)DRW_dbg::dbgHL(settingsHandle.code, settingsHandle.size, settingsHandle
.ref)
; DRW_DBG("\n")DRW_dbg::dbg("\n");
8489 }
8490 if (!handleProbe.isGood())
8491 return fail();
8492
8493 m_state = static_cast<std::uint32_t>(stateValue);
8494 m_flags = static_cast<std::uint32_t>(flagsValue);
8495 m_name = name;
8496 m_vertDir = vertDir;
8497 m_topHeight = topHeight;
8498 m_bottomHeight = bottomHeight;
8499 m_indicatorAlpha = indicatorAlpha;
8500 m_indicatorColor = indicatorColor;
8501 m_verts = std::move(verts);
8502 m_blVerts = std::move(blVerts);
8503 m_sectionSettingsHandle = sectionSettingsHandle;
8504 *sourceBuf = handleProbe;
8505 DRW_DBG("SECTIONOBJECT name: ")DRW_dbg::dbg("SECTIONOBJECT name: "); DRW_DBG(m_name.c_str())DRW_dbg::dbg(m_name.c_str());
8506 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");
8507 return true;
8508}
8509
8510bool DRW_SectionObject::encodeDwg(DRW::Version v, dwgBufferW *buf,
8511 std::uint32_t bs, dwgBufferW *strBuf,
8512 dwgBufferW *handleBuf) {
8513 (void)bs;
8514 if (v < DRW::AC1021 || buf == nullptr
8515 || m_verts.size() > DRW_SectionObject::kMaxVertices
8516 || m_blVerts.size() > DRW_SectionObject::kMaxVertices)
8517 return false;
8518 oType = kDwgClassNum;
8519 if (!encodeDwgCommon(v, buf, strBuf))
8520 return false;
8521 dwgBufferW *stringBuffer = strBuf != nullptr ? strBuf : buf;
8522 buf->putBitLong(static_cast<std::int32_t>(m_state));
8523 buf->putBitLong(static_cast<std::int32_t>(m_flags));
8524 stringBuffer->putVariableText(v, m_name);
8525 buf->put3BitDouble(m_vertDir);
8526 buf->putBitDouble(m_topHeight);
8527 buf->putBitDouble(m_bottomHeight);
8528 buf->putBitShort(m_indicatorAlpha);
8529 buf->putCmColor(v, static_cast<std::uint16_t>(m_indicatorColor));
8530 buf->putBitLong(static_cast<std::int32_t>(m_verts.size()));
8531 for (const DRW_Coord& point : m_verts)
8532 buf->put3BitDouble(point);
8533 buf->putBitLong(static_cast<std::int32_t>(m_blVerts.size()));
8534 for (const DRW_Coord& point : m_blVerts)
8535 buf->put3BitDouble(point);
8536 if (!encodeDwgEntHandle(v, buf, handleBuf))
8537 return false;
8538 putHardPointerHandle(handleBuf != nullptr ? handleBuf : buf,
8539 m_sectionSettingsHandle);
8540 return true;
8541}
8542
8543bool DRW_Tolerance::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
8544 switch (code) {
8545 case 1:
8546 text = reader->getUtf8String();
8547 break;
8548 case 3:
8549 dimStyleName = reader->getUtf8String();
8550 break;
8551 case 10:
8552 insertionPoint.x = reader->getDouble();
8553 break;
8554 case 20:
8555 insertionPoint.y = reader->getDouble();
8556 break;
8557 case 30:
8558 insertionPoint.z = reader->getDouble();
8559 break;
8560 case 11:
8561 xAxisDirectionVector.x = reader->getDouble();
8562 break;
8563 case 21:
8564 xAxisDirectionVector.y = reader->getDouble();
8565 break;
8566 case 31:
8567 xAxisDirectionVector.z = reader->getDouble();
8568 break;
8569 case 210:
8570 extPoint.x = reader->getDouble();
8571 break;
8572 case 220:
8573 extPoint.y = reader->getDouble();
8574 break;
8575 case 230:
8576 extPoint.z = reader->getDouble();
8577 break;
8578 default:
8579 return DRW_Entity::parseCode(code, reader);
8580 }
8581 return true;
8582}
8583
8584void DRW_Tolerance::resetDwgState() {
8585 DRW_Entity::reset();
8586 text.clear();
8587 dimStyleName = "STANDARD";
8588 dimStyleH = dwgHandle{};
8589 insertionPoint = DRW_Coord{0.0, 0.0, 0.0};
8590 xAxisDirectionVector = DRW_Coord{0.0, 0.0, 0.0};
8591 extPoint = DRW_Coord{0.0, 0.0, 1.0};
8592}
8593
8594bool DRW_Tolerance::parseDwg(DRW::Version v, dwgBuffer *buf,
8595 std::uint32_t bs) {
8596 resetDwgState();
8597 dwgBuffer *sourceBuf = buf;
8598 auto fail = [this, sourceBuf]() {
8599 if (sourceBuf != nullptr)
8600 sourceBuf->invalidate();
8601 resetDwgState();
8602 return false;
8603 };
8604 if (sourceBuf == nullptr)
8605 return fail();
8606
8607 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
8608 dwgBuffer stringProbe = sourceBuf->forkIndependent();
8609 dwgBuffer *stringStream = v > DRW::AC1018 ? &stringProbe : &bodyProbe;
8610 if (!DRW_Entity::parseDwg(v, &bodyProbe, stringStream, bs)
8611 || !bodyProbe.isGood() || !stringStream->isGood())
8612 return fail();
8613
8614 DRW_DBG("\n***************************** parsing tolerance *********************************************\n")DRW_dbg::dbg("\n***************************** parsing tolerance *********************************************\n"
)
;
8615 const std::uint64_t bodyEndBit = dwgDataEndBit;
8616 const std::uint64_t stringStartBit = currentDwgBit(stringStream);
8617 std::uint64_t stringEndBit = bodyEndBit;
8618 if (v > DRW::AC1021) {
8619 std::uint64_t ignoredBodyEndBit = 0;
8620 if (!entityBodyDataEndBit(*stringStream, v, objSize,
8621 ignoredBodyEndBit, &stringEndBit))
8622 return fail();
8623 }
8624 auto read3BitDoubleChecked = [&](DRW_Coord& point) {
8625 return readBoundedBitDouble(bodyProbe, bodyEndBit, point.x)
8626 && readBoundedBitDouble(bodyProbe, bodyEndBit, point.y)
8627 && readBoundedBitDouble(bodyProbe, bodyEndBit, point.z);
8628 };
8629
8630 if (v < DRW::AC1015) {
8631 std::uint16_t unknown = 0;
8632 double heightAtCreation = 0.0;
8633 double dimGapAtCreation = 0.0;
8634 if (!readBoundedBitShort(bodyProbe, bodyEndBit, unknown)
8635 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
8636 heightAtCreation)
8637 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
8638 dimGapAtCreation)
8639 || !std::isfinite(heightAtCreation)
8640 || !std::isfinite(dimGapAtCreation))
8641 return fail();
8642 }
8643
8644 DRW_Coord parsedInsertionPoint;
8645 DRW_Coord parsedXAxisDirectionVector;
8646 DRW_Coord parsedExtPoint;
8647 if (!read3BitDoubleChecked(parsedInsertionPoint)
8648 || !read3BitDoubleChecked(parsedXAxisDirectionVector)
8649 || !read3BitDoubleChecked(parsedExtPoint))
8650 return fail();
8651 UTF8STRINGstd::string parsedText;
8652 if (v > DRW::AC1018 && stringStartBit >= stringEndBit) {
8653 parsedText.clear();
8654 } else if (!readBoundedVariableText(*stringStream, stringEndBit, v,
8655 parsedText)) {
8656 return fail();
8657 }
8658
8659 const auto finite = [](const DRW_Coord& point) {
8660 return std::isfinite(point.x) && std::isfinite(point.y)
8661 && std::isfinite(point.z);
8662 };
8663 if (!bodyProbe.isGood() || !stringStream->isGood()
8664 || currentDwgBit(&bodyProbe) > bodyEndBit
8665 || currentDwgBit(stringStream) > stringEndBit
8666 || !finite(parsedInsertionPoint)
8667 || !finite(parsedXAxisDirectionVector)
8668 || !finite(parsedExtPoint))
8669 return fail();
8670
8671 std::uint64_t handleEndBit = 0;
8672 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
8673 return fail();
8674 dwgBuffer handleProbe = bodyProbe.forkIndependent();
8675 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
8676 || !handleProbe.isGood())
8677 return fail();
8678 dwgHandle parsedDimStyleH;
8679 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
8680 parsedDimStyleH)
8681 || !handleProbe.isGood())
8682 return fail();
8683
8684 insertionPoint = parsedInsertionPoint;
8685 xAxisDirectionVector = parsedXAxisDirectionVector;
8686 extPoint = parsedExtPoint;
8687 text = parsedText;
8688 dimStyleH = parsedDimStyleH;
8689 *sourceBuf = handleProbe;
8690 DRW_DBG("insertionPoint: ")DRW_dbg::dbg("insertionPoint: ");
8691 DRW_DBGPT(insertionPoint.x, insertionPoint.y, insertionPoint.z)DRW_dbg::dbgPT(insertionPoint.x, insertionPoint.y, insertionPoint
.z)
;
8692 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");
8693 DRW_DBG("dim style Handle: ")DRW_dbg::dbg("dim style Handle: ");
8694 DRW_DBGHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref)DRW_dbg::dbgHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref);
8695 DRW_DBG("\n")DRW_dbg::dbg("\n");
8696 return true;
8697}
8698
8699bool DRW_Tolerance::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
8700 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
8701 (void)bs;
8702 oType = 46;
8703 if (!encodeDwgCommon(version, buf, strBuf))
8704 return false;
8705
8706 if (version < DRW::AC1015) {
8707 buf->putBitShort(0);
8708 buf->putBitDouble(0.0);
8709 buf->putBitDouble(0.0);
8710 }
8711
8712 buf->put3BitDouble(insertionPoint);
8713 buf->put3BitDouble(xAxisDirectionVector);
8714 buf->put3BitDouble(extPoint);
8715 (strBuf ? strBuf : buf)->putVariableText(version, text);
8716
8717 if (!encodeDwgEntHandle(version, buf, handleBuf))
8718 return false;
8719
8720 dwgBufferW *hb = handleBuf ? handleBuf : buf;
8721 putHardPointerHandle(hb, (dimStyleH.ref == 0) ? 0x15 : dimStyleH.ref);
8722 return true;
8723}
8724
8725
8726bool DRW_Block::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
8727 switch (code) {
8728 case 1:
8729 xrefPath = reader->getUtf8String();
8730 break;
8731 case 2:
8732 name = reader->getUtf8String();
8733 break;
8734 case 70:
8735 flags = reader->getInt32();
8736 break;
8737 default:
8738 return DRW_Point::parseCode(code, reader);
8739 }
8740
8741 return true;
8742}
8743
8744void DRW_Block::resetDwgState() {
8745 const bool endBlock = isEnd;
8746 DRW_Point::resetDwgState();
8747 name = endBlock ? UTF8STRINGstd::string() : UTF8STRINGstd::string("*U0");
8748 flags = 0;
8749 insUnits = 0;
8750 xrefPath.clear();
8751 previewData.clear();
8752 isEnd = endBlock;
8753}
8754
8755bool DRW_Block::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
8756 resetDwgState();
8757 if (buf == nullptr)
8758 return false;
8759 auto fail = [this, buf]() {
8760 buf->invalidate();
8761 resetDwgState();
8762 return false;
8763 };
8764 dwgBuffer bodyProbe = buf->forkIndependent();
8765 dwgBuffer sBuff = bodyProbe.forkIndependent();
8766 dwgBuffer *sBuf = &bodyProbe;
8767 if (version > DRW::AC1018) {//2007+
8768 sBuf = &sBuff; //separate buffer for strings
8769 }
8770 bool ret = DRW_Entity::parseDwg(version, &bodyProbe, sBuf, bs);
8771 if (!ret)
8772 return fail();
8773
8774 // BLOCK/ENDBLK carry one entity-specific bit after the common data range;
8775 // the detached string stream begins at dwgDataEndBit and is independent.
8776 const std::uint64_t bodyEndBit = version > DRW::AC1018
8777 && dwgDataEndBit < std::numeric_limits<std::uint64_t>::max()
8778 ? dwgDataEndBit + 1 : dwgDataEndBit;
8779 std::uint64_t stringStartBit = bodyEndBit;
8780 std::uint64_t stringEndBit = bodyEndBit;
8781 if (version > DRW::AC1018
8782 && (!bodyProbe.getR2007StringStreamBounds(objSize, stringStartBit,
8783 stringEndBit)
8784 || currentDwgBit(sBuf) > stringEndBit))
8785 return fail();
8786
8787 UTF8STRINGstd::string parsedName;
8788 if (!isEnd){
8789 DRW_DBG("\n***************************** parsing block *********************************************\n")DRW_dbg::dbg("\n***************************** parsing block *********************************************\n"
)
;
8790 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
8791 parsedName))
8792 return fail();
8793 DRW_DBG("Block name: ")DRW_dbg::dbg("Block name: "); DRW_DBG(parsedName.c_str())DRW_dbg::dbg(parsedName.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8794 } else {
8795 DRW_DBG("\n***************************** parsing end block *********************************************\n")DRW_dbg::dbg("\n***************************** parsing end block *********************************************\n"
)
;
8796 }
8797 if (version > DRW::AC1018) {//2007+
8798 bool ignoredUnknown = false;
8799 if (!readBoundedBit(bodyProbe, bodyEndBit, ignoredUnknown))
8800 return fail();
8801 DRW_DBG("unknown bit: ")DRW_dbg::dbg("unknown bit: "); DRW_DBG(ignoredUnknown)DRW_dbg::dbg(ignoredUnknown); DRW_DBG("\n")DRW_dbg::dbg("\n");
8802 }
8803// X handleAssoc; //X
8804 if (!bodyProbe.isGood() || currentDwgBit(&bodyProbe) > bodyEndBit)
8805 return fail();
8806 std::uint64_t handleEndBit = 0;
8807 if (!dwgHandleStreamEndBit(*buf, version, objSize, bs, handleEndBit))
8808 return fail();
8809 ret = DRW_Entity::parseDwgEntHandle(version, &bodyProbe, true,
8810 handleEndBit);
8811 if (!ret || !bodyProbe.isGood())
8812 return fail();
8813 // RS crc; //RS */
8814 // R13-R2004 keep the handle stream inline and may consume the final
8815 // byte-alignment padding beyond the declared object-data bit count.
8816 // Only R2007+ has a separate string stream range to validate here.
8817 if (!bodyProbe.isGood() || !sBuf->isGood()
8818 || (version > DRW::AC1018 && currentDwgBit(sBuf) > stringEndBit))
8819 return fail();
8820 *buf = bodyProbe;
8821 if (!isEnd)
8822 name = std::move(parsedName);
8823 return true;
8824}
8825
8826bool DRW_Insert::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
8827 switch (code) {
8828 case 2:
8829 name = reader->getUtf8String();
8830 break;
8831 case 41:
8832 xscale = reader->getDouble();
8833 break;
8834 case 42:
8835 yscale = reader->getDouble();
8836 break;
8837 case 43:
8838 zscale = reader->getDouble();
8839 break;
8840 case 50:
8841 angle = reader->getDouble();
8842 angle = angle/ARAD57.29577951308232; //convert to radian
8843 break;
8844 case 70: {
8845 int value = 0;
8846 if (!readDxfIntInRange(reader, 0, kMaxMInsertCount, value))
8847 return false;
8848 colcount = value;
8849 break;
8850 }
8851 case 71: {
8852 int value = 0;
8853 if (!readDxfIntInRange(reader, 0, kMaxMInsertCount, value))
8854 return false;
8855 rowcount = value;
8856 break;
8857 }
8858 case 44:
8859 colspace = reader->getDouble();
8860 break;
8861 case 45:
8862 rowspace = reader->getDouble();
8863 break;
8864 default:
8865 return DRW_Point::parseCode(code, reader);
8866 }
8867
8868 return true;
8869}
8870
8871bool DRW_Table::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
8872 auto ensureGrid = [this]() {
8873 if (m_dxfRowsExpected < 0 || m_dxfColumnsExpected < 0)
8874 return true;
8875
8876 const std::uint32_t rows = static_cast<std::uint32_t>(m_dxfRowsExpected);
8877 const std::uint32_t columns = static_cast<std::uint32_t>(m_dxfColumnsExpected);
8878 if (rows > kMaxTableRows || columns > kMaxTableColumns
8879 || (columns != 0 && rows > kMaxTableCells / columns)) {
8880 return false;
8881 }
8882
8883 if (m_content.m_columns.size() != columns) {
8884 m_content.m_columns.clear();
8885 if (!DRW::reserve(m_content.m_columns, static_cast<int>(columns)))
8886 return false;
8887 if (!DRW::resize(m_content.m_columns, static_cast<int>(columns)))
8888 return false;
8889 m_dxfColumnWidthsRead = 0;
8890 }
8891 if (m_content.m_rows.size() != rows) {
8892 m_content.m_rows.clear();
8893 if (!DRW::reserve(m_content.m_rows, static_cast<int>(rows)))
8894 return false;
8895 if (!DRW::resize(m_content.m_rows, static_cast<int>(rows)))
8896 return false;
8897 m_dxfRowHeightsRead = 0;
8898 }
8899 for (auto& row : m_content.m_rows) {
8900 if (!DRW::reserve(row.m_cells, static_cast<int>(columns)))
8901 return false;
8902 if (!DRW::resize(row.m_cells, static_cast<int>(columns)))
8903 return false;
8904 }
8905
8906 m_hasSemanticContent = true;
8907 m_semanticContentComplete = true;
8908 return true;
8909 };
8910
8911 auto currentCell = [this]() -> DRW_TableCell* {
8912 if (m_dxfCurrentCell < 0 || m_content.m_columns.empty()
8913 || m_content.m_rows.empty()) {
8914 return nullptr;
8915 }
8916
8917 const std::size_t columns = m_content.m_columns.size();
8918 const std::size_t cell = static_cast<std::size_t>(m_dxfCurrentCell);
8919 const std::size_t row = cell / columns;
8920 const std::size_t column = cell % columns;
8921 if (row >= m_content.m_rows.size()
8922 || column >= m_content.m_rows[row].m_cells.size()) {
8923 return nullptr;
8924 }
8925 return &m_content.m_rows[row].m_cells[column];
8926 };
8927
8928 auto currentContent = [&currentCell]() -> DRW_TableCellContent* {
8929 DRW_TableCell *cell = currentCell();
8930 if (cell == nullptr)
8931 return nullptr;
8932 if (cell->m_contents.empty() || cell->m_contents.back().m_type != 1) {
8933 DRW_TableCellContent content;
8934 content.m_type = 1;
8935 cell->m_contents.push_back(content);
8936 }
8937 return &cell->m_contents.back();
8938 };
8939
8940 if (code == 100) {
8941 const std::string subclass = reader->getString();
8942 if (subclass == "AcDbBlockReference") {
8943 m_dxfSubclass = DxfSubclass::BlockReference;
8944 } else if (subclass == "AcDbTable") {
8945 m_dxfSubclass = DxfSubclass::Table;
8946 } else if (subclass == "AcDbEntity") {
8947 m_dxfSubclass = DxfSubclass::Entity;
8948 }
8949 return true;
8950 }
8951
8952 if (m_dxfSubclass != DxfSubclass::Table)
8953 return DRW_Insert::parseCode(code, reader);
8954
8955 // A cell value's point, double, date and object-id groups reuse codes the
8956 // table itself and its proxy graphics own; only the displayed text is kept.
8957 if (m_dxfInCellValue
8958 && (code == 11 || code == 21 || code == 31 || code == 140
8959 || code == 310 || code == 330)) {
8960 return true;
8961 }
8962
8963 switch (code) {
8964 case 342:
8965 m_tableStyleHandle = static_cast<std::uint32_t>(reader->getHandleString());
8966 m_content.m_tableStyleHandle = m_tableStyleHandle;
8967 break;
8968 case 343:
8969 reader->getHandleString();
8970 break;
8971 case 11:
8972 m_horizontalDirection.x = reader->getDouble();
8973 break;
8974 case 21:
8975 m_horizontalDirection.y = reader->getDouble();
8976 break;
8977 case 31:
8978 m_horizontalDirection.z = reader->getDouble();
8979 break;
8980 case 90:
8981 if (m_dxfInCellValue) {
8982 if (DRW_TableCellContent *content = currentContent())
8983 content->m_value.m_dataType = reader->getInt32();
8984 else
8985 reader->getInt32();
8986 } else {
8987 m_valueFlag = reader->getInt32();
8988 }
8989 break;
8990 case 91:
8991 if (m_dxfRowsExpected < 0 && !m_dxfInCellValue) {
8992 int value = 0;
8993 if (!readDxfIntInRange(reader, 0,
8994 static_cast<int>(kMaxTableRows), value))
8995 return false;
8996 m_dxfRowsExpected = value;
8997 if (!ensureGrid())
8998 return false;
8999 } else {
9000 reader->getInt32();
9001 }
9002 break;
9003 case 92:
9004 if (m_dxfColumnsExpected < 0 && !m_dxfInCellValue) {
9005 int value = 0;
9006 if (!readDxfIntInRange(reader, 0,
9007 static_cast<int>(kMaxTableColumns), value))
9008 return false;
9009 m_dxfColumnsExpected = value;
9010 if (!ensureGrid())
9011 return false;
9012 } else {
9013 reader->getInt32();
9014 }
9015 break;
9016 case 93:
9017 case 94:
9018 case 95:
9019 case 96:
9020 case 172:
9021 case 173:
9022 case 174:
9023 case 175:
9024 case 176:
9025 case 178:
9026 reader->getInt32();
9027 break;
9028 case 141:
9029 if (!ensureGrid())
9030 return false;
9031 if (m_dxfRowHeightsRead < m_content.m_rows.size())
9032 m_content.m_rows[m_dxfRowHeightsRead++].m_height = reader->getDouble();
9033 else
9034 reader->getDouble();
9035 break;
9036 case 142:
9037 if (!ensureGrid())
9038 return false;
9039 if (m_dxfColumnWidthsRead < m_content.m_columns.size())
9040 m_content.m_columns[m_dxfColumnWidthsRead++].m_width = reader->getDouble();
9041 else
9042 reader->getDouble();
9043 break;
9044 case 145:
9045 reader->getDouble();
9046 break;
9047 case 171:
9048 if (!ensureGrid())
9049 return false;
9050 if (!m_content.m_rows.empty() && !m_content.m_columns.empty()
9051 && m_dxfNextCell < m_content.m_rows.size() * m_content.m_columns.size()) {
9052 m_dxfCurrentCell = static_cast<int>(m_dxfNextCell++);
9053 if (DRW_TableCell *cell = currentCell())
9054 cell->m_flags = reader->getInt32();
9055 else
9056 reader->getInt32();
9057 } else {
9058 m_dxfCurrentCell = -1;
9059 reader->getInt32();
9060 }
9061 m_dxfInCellValue = false;
9062 break;
9063 case 301:
9064 m_dxfInCellValue = reader->getString() == "CELL_VALUE";
9065 if (m_dxfInCellValue)
9066 currentContent();
9067 break;
9068 case 1:
9069 case 302: {
9070 const UTF8STRINGstd::string text = reader->getUtf8String();
9071 if (m_dxfInCellValue) {
9072 if (DRW_TableCellContent *content = currentContent()) {
9073 content->m_text = text;
9074 content->m_value.m_dataType = 4;
9075 content->m_value.m_value.addString(1, text);
9076 }
9077 }
9078 break;
9079 }
9080 case 300:
9081 if (m_dxfInCellValue) {
9082 if (DRW_TableCellContent *content = currentContent())
9083 content->m_value.m_valueString = reader->getUtf8String();
9084 else
9085 reader->getUtf8String();
9086 } else {
9087 reader->getUtf8String();
9088 }
9089 break;
9090 case 304:
9091 reader->getString();
9092 m_dxfInCellValue = false;
9093 break;
9094 default:
9095 return DRW_Entity::parseCode(code, reader);
9096 }
9097
9098 return true;
9099}
9100
9101void DRW_Insert::resetDwgState() {
9102 DRW_Point::resetDwgState();
9103 name.clear();
9104 xscale = yscale = zscale = 1.0;
9105 angle = 0.0;
9106 colcount = rowcount = 1;
9107 colspace = rowspace = 0.0;
9108 attlist.clear();
9109 blockRecH = dwgHandle{};
9110 seqendH = dwgHandle{};
9111 attribHandles.clear();
9112}
9113
9114bool DRW_Insert::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
9115 resetDwgState();
9116 dwgBuffer *sourceBuf = buf;
9117 auto fail = [this, sourceBuf]() {
9118 if (sourceBuf != nullptr)
9119 sourceBuf->invalidate();
9120 resetDwgState();
9121 return false;
9122 };
9123 if (sourceBuf == nullptr)
9124 return fail();
9125
9126 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
9127 buf = &bodyProbe;
9128 if (!DRW_Entity::parseDwgCommon(version, buf, bs)
9129 || !bodyProbe.isGood())
9130 return fail();
9131
9132 std::int32_t objCount = 0;
9133 std::vector<dwgHandle> parsedAttribHandles;
9134 DRW_DBG("\n************************** parsing insert/minsert *****************************************\n")DRW_dbg::dbg("\n************************** parsing insert/minsert *****************************************\n"
)
;
9135 const std::uint64_t bodyEndBit = dwgDataEndBit;
9136 DRW_Coord parsedBasePoint;
9137 DRW_Coord parsedExtPoint;
9138 double parsedXScale = 1.0;
9139 double parsedYScale = 1.0;
9140 double parsedZScale = 1.0;
9141 double parsedAngle = 0.0;
9142 bool hasAttrib = false;
9143
9144 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedBasePoint))
9145 return fail();
9146 DRW_DBG("insertion point: ")DRW_dbg::dbg("insertion point: ");
9147 DRW_DBGPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint.z)DRW_dbg::dbgPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint
.z)
;
9148 DRW_DBG("\n")DRW_dbg::dbg("\n");
9149 if (version < DRW::AC1015) {//14-
9150 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedXScale)
9151 || !readBoundedBitDouble(*buf, bodyEndBit, parsedYScale)
9152 || !readBoundedBitDouble(*buf, bodyEndBit, parsedZScale))
9153 return fail();
9154 } else {
9155 if (!proxyEntityHasBits(*buf, bodyEndBit, 2))
9156 return fail();
9157 const std::uint8_t dataFlags = buf->get2Bits();
9158 if (!buf->isGood())
9159 return fail();
9160 if (dataFlags == 3) {
9161 //none default value 1,1,1
9162 } else if (dataFlags == 1) { //x default value 1, y & z use DD(1)
9163 if (!readBoundedDefaultDouble(*buf, bodyEndBit, 1.0,
9164 parsedYScale)
9165 || !readBoundedDefaultDouble(*buf, bodyEndBit, 1.0,
9166 parsedZScale))
9167 return fail();
9168 } else if (dataFlags == 2) {
9169 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedXScale))
9170 return fail();
9171 parsedYScale = parsedZScale = parsedXScale;
9172 } else { //dataFlags == 0
9173 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedXScale)
9174 || !readBoundedDefaultDouble(*buf, bodyEndBit, parsedXScale,
9175 parsedYScale)
9176 || !readBoundedDefaultDouble(*buf, bodyEndBit, parsedXScale,
9177 parsedZScale))
9178 return fail();
9179 }
9180 }
9181 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedAngle)
9182 || !readBoundedExtrusion(*buf, bodyEndBit, false, parsedExtPoint)
9183 || !readBoundedBit(*buf, bodyEndBit, hasAttrib))
9184 return fail();
9185 DRW_DBG("scale : ")DRW_dbg::dbg("scale : ");
9186 DRW_DBGPT(parsedXScale, parsedYScale, parsedZScale)DRW_dbg::dbgPT(parsedXScale, parsedYScale, parsedZScale);
9187 DRW_DBG(", angle: ")DRW_dbg::dbg(", angle: "); DRW_DBG(parsedAngle)DRW_dbg::dbg(parsedAngle);
9188 DRW_DBG("\nextrusion: ")DRW_dbg::dbg("\nextrusion: ");
9189 DRW_DBGPT(parsedExtPoint.x, parsedExtPoint.y, parsedExtPoint.z)DRW_dbg::dbgPT(parsedExtPoint.x, parsedExtPoint.y, parsedExtPoint
.z)
;
9190
9191 DRW_DBG(" has Attrib: ")DRW_dbg::dbg(" has Attrib: "); DRW_DBG(hasAttrib)DRW_dbg::dbg(hasAttrib);
9192
9193 if (hasAttrib && version > DRW::AC1015) {//2004+
9194 if (!readBoundedBitLong(*buf, bodyEndBit, objCount)
9195 || !dwgSafety::validOwnedObjectCount(
9196 objCount, buf->numRemainingBytes())) {
9197 DRW_DBG("\nWARNING: Invalid INSERT owned-attribute count\n")DRW_dbg::dbg("\nWARNING: Invalid INSERT owned-attribute count\n"
)
;
9198 return fail();
9199 }
9200 DRW_DBG(" objCount: ")DRW_dbg::dbg(" objCount: "); DRW_DBG(objCount)DRW_dbg::dbg(objCount); DRW_DBG("\n")DRW_dbg::dbg("\n");
9201 }
9202 int parsedColCount = 1;
9203 int parsedRowCount = 1;
9204 double parsedColSpace = 0.0;
9205 double parsedRowSpace = 0.0;
9206 if (oType == 8) {//entity are minsert
9207 std::uint16_t colCount = 0;
9208 std::uint16_t rowCount = 0;
9209 if (!readBoundedBitShort(*buf, bodyEndBit, colCount)
9210 || !readBoundedBitShort(*buf, bodyEndBit, rowCount)
9211 || !readBoundedBitDouble(*buf, bodyEndBit, parsedColSpace)
9212 || !readBoundedBitDouble(*buf, bodyEndBit, parsedRowSpace))
9213 return fail();
9214 parsedColCount = static_cast<int>(colCount);
9215 parsedRowCount = static_cast<int>(rowCount);
9216 }
9217 const auto finite = [](const DRW_Coord& point) {
9218 return std::isfinite(point.x) && std::isfinite(point.y)
9219 && std::isfinite(point.z);
9220 };
9221 if (!finite(parsedBasePoint) || !finite(parsedExtPoint)
9222 || !std::isfinite(parsedXScale) || !std::isfinite(parsedYScale)
9223 || !std::isfinite(parsedZScale) || !std::isfinite(parsedAngle)
9224 || !std::isfinite(parsedColSpace) || !std::isfinite(parsedRowSpace))
9225 return fail();
9226 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");
9227 std::uint64_t handleEndBit = 0;
9228 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
9229 handleEndBit))
9230 return fail();
9231 dwgBuffer handleProbe = buf->forkIndependent();
9232 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
9233 handleEndBit))
9234 return fail();
9235 dwgHandle parsedBlockRecH;
9236 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
9237 parsedBlockRecH))
9238 return fail();
9239 dwgHandle parsedSeqendH;
9240 DRW_DBG("BLOCK HEADER Handle: ")DRW_dbg::dbg("BLOCK HEADER Handle: "); DRW_DBGHL(parsedBlockRecH.code, parsedBlockRecH.size, parsedBlockRecH.ref)DRW_dbg::dbgHL(parsedBlockRecH.code, parsedBlockRecH.size, parsedBlockRecH
.ref)
; DRW_DBG("\n")DRW_dbg::dbg("\n");
9241 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9242
9243 /*attribs follows*/
9244 if (hasAttrib) {
9245 const std::int32_t expectedCount =
9246 version < DRW::AC1018 ? 2 : objCount;
9247 if (!DRW::reserve(parsedAttribHandles, expectedCount))
9248 return fail();
9249 if (version < DRW::AC1018) {//2000-
9250 dwgHandle attH;
9251 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
9252 attH))
9253 return fail();
9254 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");
9255 parsedAttribHandles.push_back(attH);
9256 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
9257 attH))
9258 return fail();
9259 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");
9260 parsedAttribHandles.push_back(attH);
9261 } else {
9262 for (std::int32_t i=0; i < objCount; ++i){
9263 dwgHandle attH;
9264 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0,
9265 false, attH))
9266 return fail();
9267 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");
9268 parsedAttribHandles.push_back(attH);
9269 }
9270 }
9271 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
9272 parsedSeqendH))
9273 return fail();
9274 DRW_DBG("seqendH Handle: ")DRW_dbg::dbg("seqendH Handle: "); DRW_DBGHL(parsedSeqendH.code, parsedSeqendH.size, parsedSeqendH.ref)DRW_dbg::dbgHL(parsedSeqendH.code, parsedSeqendH.size, parsedSeqendH
.ref)
; DRW_DBG("\n")DRW_dbg::dbg("\n");
9275 }
9276 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9277
9278 if (!handleProbe.isGood())
9279 return fail();
9280 *sourceBuf = handleProbe;
9281 basePoint = parsedBasePoint;
9282 xscale = parsedXScale;
9283 yscale = parsedYScale;
9284 zscale = parsedZScale;
9285 angle = parsedAngle;
9286 extPoint = parsedExtPoint;
9287 colcount = parsedColCount;
9288 rowcount = parsedRowCount;
9289 colspace = parsedColSpace;
9290 rowspace = parsedRowSpace;
9291 blockRecH = parsedBlockRecH;
9292 seqendH = parsedSeqendH;
9293 attribHandles = std::move(parsedAttribHandles);
9294 // RS crc; //RS */
9295 return true;
9296}
9297
9298void DRW_Table::resetDwgState() {
9299 DRW_Insert::resetDwgState();
9300 name.clear();
9301 xscale = yscale = zscale = 1.0;
9302 angle = 0.0;
9303 colcount = rowcount = 1;
9304 colspace = rowspace = 0.0;
9305 m_valueFlag = 0;
9306 m_horizontalDirection = DRW_Coord{0.0, 0.0, 0.0};
9307 m_hasSemanticContent = false;
9308 m_semanticContentComplete = false;
9309 m_tableStyleHandle = 0;
9310 m_content = DRW_TableContent{};
9311 m_dxfSubclass = DxfSubclass::Entity;
9312 m_dxfRowsExpected = -1;
9313 m_dxfColumnsExpected = -1;
9314 m_dxfRowHeightsRead = 0;
9315 m_dxfColumnWidthsRead = 0;
9316 m_dxfNextCell = 0;
9317 m_dxfCurrentCell = -1;
9318 m_dxfInCellValue = false;
9319}
9320
9321bool DRW_Table::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
9322 resetDwgState();
9323 auto fail = [this, buf]() {
9324 if (buf != nullptr)
9325 buf->invalidate();
9326 resetDwgState();
9327 return false;
9328 };
9329 if (buf == nullptr || version < DRW::AC1015)
9330 return fail();
9331
9332 dwgBuffer *sourceBuf = buf;
9333 dwgBuffer bodyProbe = buf->forkIndependent();
9334 buf = &bodyProbe;
9335 auto commit = [&]() {
9336 *sourceBuf = bodyProbe;
9337 return true;
9338 };
9339
9340 dwgBuffer sBuff = bodyProbe.forkIndependent();
9341 sBuff.setVariableTextByteLength(true);
9342 dwgBuffer *sBuf = &sBuff;
9343 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
9344 if (!ret)
9345 return fail();
9346
9347 DRW_DBG("\n************************** parsing table *****************************************\n")DRW_dbg::dbg("\n************************** parsing table *****************************************\n"
)
;
9348 const std::uint64_t bodyEndBit = dwgDataEndBit;
9349 DRW_Coord parsedBasePoint;
9350 std::uint8_t dataFlags = 0;
9351 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedBasePoint)
9352 || !proxyEntityHasBits(*buf, bodyEndBit, 2))
9353 return fail();
9354 dataFlags = buf->get2Bits();
9355 if (!buf->isGood())
9356 return fail();
9357
9358 double parsedXScale = 1.0;
9359 double parsedYScale = 1.0;
9360 double parsedZScale = 1.0;
9361 if (dataFlags == 3) {
9362 // default scale 1,1,1
9363 } else if (dataFlags == 1) {
9364 if (!readBoundedDefaultDouble(*buf, bodyEndBit, parsedXScale,
9365 parsedYScale)
9366 || !readBoundedDefaultDouble(*buf, bodyEndBit, parsedXScale,
9367 parsedZScale))
9368 return fail();
9369 } else if (dataFlags == 2) {
9370 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedXScale))
9371 return fail();
9372 parsedYScale = parsedZScale = parsedXScale;
9373 } else {
9374 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedXScale)
9375 || !readBoundedDefaultDouble(*buf, bodyEndBit, parsedXScale,
9376 parsedYScale)
9377 || !readBoundedDefaultDouble(*buf, bodyEndBit, parsedXScale,
9378 parsedZScale))
9379 return fail();
9380 }
9381
9382 double parsedAngle = 0.0;
9383 DRW_Coord parsedExtPoint;
9384 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedAngle)
9385 || !readBoundedExtrusion(*buf, bodyEndBit, false, parsedExtPoint))
9386 return fail();
9387
9388 if (!sBuf->isGood())
9389 return fail();
9390
9391 std::int32_t objCount = 0;
9392 bool hasAttrib = false;
9393 if (!readBoundedBit(*buf, bodyEndBit, hasAttrib))
9394 return fail();
9395 if (hasAttrib && version > DRW::AC1015) {
9396 if (!readBoundedBitLong(*buf, bodyEndBit, objCount)
9397 || !dwgSafety::validOwnedObjectCount(
9398 objCount, buf->numRemainingBytes())) {
9399 DRW_DBG("\nWARNING: Invalid TABLE owned-attribute count\n")DRW_dbg::dbg("\nWARNING: Invalid TABLE owned-attribute count\n"
)
;
9400 return fail();
9401 }
9402 }
9403
9404 std::uint64_t handleEndBit = 0;
9405 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
9406 handleEndBit))
9407 return fail();
9408 dwgBuffer hBuff = buf->forkIndependent();
9409 if (version <= DRW::AC1018) {
9410 // R2000/R2004: parseDwgEntHandle only re-seeks to the handle stream
9411 // for version > AC1018 (2007+ string area). For the legacy versions
9412 // seek the snapshot to the handle-stream start (objSize is the
9413 // bit offset of the handle stream, RL field read in
9414 // DRW_Entity::parseDwg) or every handle below reads mid-DATA garbage.
9415 if (!hBuff.setPosition(objSize >> 3))
9416 return fail();
9417 hBuff.setBitPos(objSize & 7);
9418 }
9419 ret = DRW_Entity::parseDwgEntHandle(version, &hBuff, true,
9420 handleEndBit);
9421 if (!ret)
9422 return fail();
9423 dwgHandle parsedBlockRecH;
9424 if (!readBoundedDwgHandle(hBuff, handleEndBit, 0, false,
9425 parsedBlockRecH))
9426 return fail();
9427
9428 std::vector<dwgHandle> parsedAttribHandles;
9429 dwgHandle parsedSeqendH;
9430
9431 if (hasAttrib) {
9432 if (!DRW::reserve(parsedAttribHandles, objCount))
9433 return fail();
9434 for (std::int32_t i = 0; i < objCount; ++i) {
9435 dwgHandle attribHandle;
9436 if (!readBoundedDwgHandle(hBuff, handleEndBit, 0, false,
9437 attribHandle))
9438 return fail();
9439 parsedAttribHandles.push_back(attribHandle);
9440 }
9441 if (!readBoundedDwgHandle(hBuff, handleEndBit, 0, false,
9442 parsedSeqendH))
9443 return fail();
9444 }
9445
9446 if (!hBuff.isGood())
9447 return fail();
9448
9449 blockRecH = parsedBlockRecH;
9450 seqendH = parsedSeqendH;
9451 attribHandles = std::move(parsedAttribHandles);
9452 basePoint = parsedBasePoint;
9453 xscale = parsedXScale;
9454 yscale = parsedYScale;
9455 zscale = parsedZScale;
9456 angle = parsedAngle;
9457 extPoint = parsedExtPoint;
9458
9459 if (version >= DRW::AC1024) {
9460 std::uint8_t ignoredTableVersion = 0;
9461 if (!readBoundedRawChar8(*buf, bodyEndBit, ignoredTableVersion))
9462 return fail();
9463 dwgHandle parsedTableStyleHandle;
9464 if (!readBoundedDwgHandle(hBuff, handleEndBit, 0, false,
9465 parsedTableStyleHandle))
9466 return fail();
9467 const std::uint32_t tableStyleHandle = parsedTableStyleHandle.ref;
9468 std::int32_t ignoredContentFlag = 0;
9469 if (!readBoundedBitLong(*buf, bodyEndBit, ignoredContentFlag))
9470 return fail();
9471 if (version >= DRW::AC1027) {
9472 std::int32_t ignoredContentVersion = 0;
9473 if (!readBoundedBitLong(*buf, bodyEndBit, ignoredContentVersion))
9474 return fail();
9475 } else {
9476 bool ignoredContentBit = false;
9477 if (!readBoundedBit(*buf, bodyEndBit, ignoredContentBit))
9478 return fail();
9479 }
9480
9481 if (!buf->isGood() || !hBuff.isGood())
9482 return fail();
9483
9484 m_hasSemanticContent = true;
9485 // TABLECONTENT is an optional semantic tail. Decode it on independent
9486 // cursors so a newer/partially supported cell variant cannot poison the
9487 // already validated entity prefix and common handle stream.
9488 dwgBuffer contentBuf = buf->forkIndependent();
9489 dwgBuffer contentStringBuf = sBuf->forkIndependent();
9490 dwgBuffer contentHandleBuf = hBuff.forkIndependent();
9491 TableDwgBounds tableBounds;
9492 tableBounds.bodyEndBit = tableBodyEndBit(version, sBuf, objSize);
9493 if (version > DRW::AC1021) {
9494 std::uint64_t stringStartBit = 0;
9495 if (!contentStringBuf.getR2007StringStreamBounds(
9496 objSize, stringStartBit, tableBounds.stringEndBit))
9497 return fail();
9498 } else {
9499 tableBounds.stringEndBit = proxyEntityEndBit(contentStringBuf, 0);
9500 }
9501 tableBounds.handleEndBit = handleEndBit;
9502 DRW_TableContent parsedContent;
9503 m_semanticContentComplete = parseTableContent(
9504 version, &contentBuf, &contentStringBuf, &contentHandleBuf,
9505 parsedContent, tableBounds);
9506 // parseTableContent fills columns (and any rows before the row/cell
9507 // that failed) before reporting incompleteness. That grid geometry
9508 // was validated against its own bounds and is still useful even
9509 // when a later cell could not be decoded, so keep it regardless of
9510 // m_semanticContentComplete instead of dropping it with the shell.
9511 m_content = std::move(parsedContent);
9512 if (m_content.m_tableStyleHandle != 0)
9513 m_tableStyleHandle = m_content.m_tableStyleHandle;
9514 else
9515 m_tableStyleHandle = tableStyleHandle;
9516 if (!m_semanticContentComplete || !contentBuf.isGood()
9517 || !contentStringBuf.isGood() || !contentHandleBuf.isGood()) {
9518 // The frame, entity prefix, and common handles remain valid. Keep
9519 // the typed table shell and allow the raw DWG frame to preserve
9520 // content that this reader does not yet model.
9521 m_semanticContentComplete = false;
9522 return commit();
9523 }
9524 *buf = contentBuf;
9525 *sBuf = contentStringBuf;
9526 hBuff = contentHandleBuf;
9527 if (!buf->isGood() || !sBuf->isGood() || !hBuff.isGood())
9528 return fail();
9529 if (!m_semanticContentComplete) {
9530 DRW_DBG("TABLECONTENT parse incomplete; anonymous block insert kept\n")DRW_dbg::dbg("TABLECONTENT parse incomplete; anonymous block insert kept\n"
)
;
9531 return commit();
9532 }
9533
9534 std::uint16_t ignoredTableFlags = 0;
9535 DRW_Coord parsedHorizontalDirection;
9536 if (!readBoundedBitShort(*buf, bodyEndBit, ignoredTableFlags)
9537 || !readBoundedBitCoord(*buf, bodyEndBit,
9538 parsedHorizontalDirection))
9539 return fail();
9540 m_horizontalDirection = parsedHorizontalDirection;
9541
9542 const std::uint64_t breakStartBit = currentDwgBit(buf);
9543 std::int32_t breakDataFlag = 0;
9544 if (!readBoundedBitLong(*buf, bodyEndBit, breakDataFlag))
9545 return fail();
9546 const bool hasBreakData = breakDataFlag != 0;
9547 if (hasBreakData) {
9548 std::int32_t ignoredBreakType = 0;
9549 double ignoredBreakDistance = 0.0;
9550 std::int32_t manualPositionCount = 0;
9551 if (!readBoundedBitLong(*buf, bodyEndBit, ignoredBreakType)
9552 || !readBoundedBitLong(*buf, bodyEndBit, ignoredBreakType)
9553 || !readBoundedBitDouble(*buf, bodyEndBit,
9554 ignoredBreakDistance)
9555 || !readBoundedBitLong(*buf, bodyEndBit, ignoredBreakType)
9556 || !readBoundedBitLong(*buf, bodyEndBit, ignoredBreakType)
9557 || !readBoundedBitLong(*buf, bodyEndBit,
9558 manualPositionCount))
9559 return fail();
9560 if (manualPositionCount < 0
9561 || static_cast<std::uint32_t>(manualPositionCount)
9562 > kMaxTableItems) {
9563 m_content.m_subrecordRanges.push_back(makeDwgSubrecordRange(
9564 "table-break-data", breakStartBit, currentDwgBit(buf),
9565 version, manualPositionCount, false));
9566 return commit();
9567 }
9568 for (std::int32_t i = 0; i < manualPositionCount; ++i) {
9569 DRW_Coord ignoredPosition;
9570 double ignoredRotation = 0.0;
9571 std::int32_t ignoredPositionType = 0;
9572 if (!readBoundedBitCoord(*buf, bodyEndBit,
9573 ignoredPosition)
9574 || !readBoundedBitDouble(*buf, bodyEndBit,
9575 ignoredRotation)
9576 || !readBoundedBitLong(*buf, bodyEndBit,
9577 ignoredPositionType))
9578 return fail();
9579 }
9580 m_content.m_subrecordRanges.push_back(makeDwgSubrecordRange(
9581 "table-break-data", breakStartBit, currentDwgBit(buf),
9582 version, static_cast<std::uint32_t>(manualPositionCount),
9583 buf->isGood()));
9584 }
9585
9586 const std::uint64_t rowRangeStartBit = currentDwgBit(buf);
9587 std::int32_t rowRangeCount = 0;
9588 if (!readBoundedBitLong(*buf, bodyEndBit, rowRangeCount))
9589 return fail();
9590 if (rowRangeCount >= 0
9591 && static_cast<std::uint32_t>(rowRangeCount) <= kMaxTableItems) {
9592 for (std::int32_t i = 0; i < rowRangeCount; ++i) {
9593 DRW_Coord ignoredRangePoint;
9594 std::int32_t ignoredRangeValue = 0;
9595 if (!readBoundedBitCoord(*buf, bodyEndBit,
9596 ignoredRangePoint)
9597 || !readBoundedBitLong(*buf, bodyEndBit,
9598 ignoredRangeValue)
9599 || !readBoundedBitLong(*buf, bodyEndBit,
9600 ignoredRangeValue))
9601 return fail();
9602 }
9603 if (rowRangeCount != 0) {
9604 m_content.m_subrecordRanges.push_back(makeDwgSubrecordRange(
9605 "table-row-ranges", rowRangeStartBit, currentDwgBit(buf),
9606 version, static_cast<std::uint32_t>(rowRangeCount),
9607 buf->isGood()));
9608 }
9609 } else {
9610 m_content.m_subrecordRanges.push_back(makeDwgSubrecordRange(
9611 "table-row-ranges", rowRangeStartBit, currentDwgBit(buf),
9612 version, rowRangeCount, false));
9613 }
9614
9615 if (!buf->isGood() || !sBuf->isGood() || !hBuff.isGood())
9616 return fail();
9617 return commit();
9618 }
9619
9620 std::uint16_t parsedValueFlag = 0;
9621 DRW_Coord parsedHorizontalDirection;
9622 std::int32_t parsedColumnCount = 0;
9623 std::int32_t parsedRowCount = 0;
9624 if (!readBoundedBitShort(*buf, bodyEndBit, parsedValueFlag)
9625 || !readBoundedBitCoord(*buf, bodyEndBit,
9626 parsedHorizontalDirection)
9627 || !readBoundedBitLong(*buf, bodyEndBit, parsedColumnCount)
9628 || !readBoundedBitLong(*buf, bodyEndBit, parsedRowCount)
9629 || parsedColumnCount < 0 || parsedRowCount < 0)
9630 return fail();
9631 const std::uint32_t columns = static_cast<std::uint32_t>(parsedColumnCount);
9632 const std::uint32_t rows = static_cast<std::uint32_t>(parsedRowCount);
9633 if (columns > kMaxTableColumns || rows > kMaxTableRows
9634 || (columns != 0 && rows > kMaxTableCells / columns)) {
9635 return fail();
9636 }
9637
9638 m_hasSemanticContent = true;
9639 m_semanticContentComplete = false;
9640 m_content.m_columns.clear();
9641 m_content.m_rows.clear();
9642 if (!DRW::reserve(m_content.m_columns, static_cast<int>(columns))
9643 || !DRW::reserve(m_content.m_rows, static_cast<int>(rows)))
9644 return fail();
9645 for (std::uint32_t i = 0; i < columns; ++i) {
9646 DRW_TableColumn column;
9647 if (!readBoundedBitDouble(*buf, bodyEndBit, column.m_width))
9648 return fail();
9649 m_content.m_columns.push_back(column);
9650 }
9651 for (std::uint32_t i = 0; i < rows; ++i) {
9652 DRW_TableRow row;
9653 if (!readBoundedBitDouble(*buf, bodyEndBit, row.m_height))
9654 return fail();
9655 if (!DRW::reserve(row.m_cells, static_cast<int>(columns)))
9656 return fail();
9657 if (!DRW::resize(row.m_cells, static_cast<int>(columns)))
9658 return fail();
9659 m_content.m_rows.push_back(row);
9660 }
9661 dwgHandle parsedTableStyleHandle;
9662 if (!readBoundedDwgHandle(hBuff, handleEndBit, 0, false,
9663 parsedTableStyleHandle))
9664 return fail();
9665 m_tableStyleHandle = parsedTableStyleHandle.ref;
9666 if (!hBuff.isGood())
9667 return fail();
9668 m_content.m_tableStyleHandle = m_tableStyleHandle;
9669 m_semanticContentComplete = true;
9670 // Decode the cell grid on independent cursors, as the R2010+ path does for
9671 // TABLECONTENT. Everything read so far (base point, block record, owned
9672 // attributes, grid geometry, table style) is validated, so a cell this
9673 // reader cannot decode costs the semantic grid and nothing else: the
9674 // anonymous *T block that the INSERT part refers to still draws the table.
9675 // A failing cell used to invalidate the shared cursors, and the closing
9676 // isGood() check then rejected the whole entity, which fails the block
9677 // that owns it. The cursors are adopted only when the whole grid decoded.
9678 dwgBuffer contentBuf = buf->forkIndependent();
9679 dwgBuffer contentStringBuf = sBuf->forkIndependent();
9680 dwgBuffer contentHandleBuf = hBuff.forkIndependent();
9681 // For <=AC1018 (R2000/R2004) there is no separate R2007+ string stream:
9682 // DRW_Entity::parseDwg only seeks sBuf when version > AC1018 (see the
9683 // `strBuf != NULL && version > DRW::AC1018` guard there), so legacy cell
9684 // text is inline in `buf`. Passing the stale sBuf copy here would read
9685 // text from the wrong position and desync `buf`. Pass nullptr so the
9686 // cell readers' `textBuf = strBuf ? strBuf : buf` falls back to the
9687 // inline `buf`. R2007 (AC1021) keeps the separate sBuf stream.
9688 dwgBuffer *cellStrBuf = (version > DRW::AC1018) ? &contentStringBuf : nullptr;
9689 TableDwgBounds tableBounds;
9690 tableBounds.bodyEndBit = bodyEndBit;
9691 tableBounds.stringEndBit = cellStrBuf == nullptr
9692 ? bodyEndBit : proxyEntityEndBit(*cellStrBuf, 0);
9693 tableBounds.handleEndBit = handleEndBit;
9694 for (std::uint32_t row = 0; row < rows && m_semanticContentComplete; ++row) {
9695 for (std::uint32_t column = 0; column < columns; ++column) {
9696 if (!parseR2007TableCell(version, &contentBuf, cellStrBuf,
9697 &contentHandleBuf,
9698 m_content.m_rows[row].m_cells[column],
9699 &m_content.m_subrecordRanges,
9700 tableBounds)) {
9701 m_semanticContentComplete = false;
9702 break;
9703 }
9704 }
9705 }
9706
9707 if (m_semanticContentComplete)
9708 m_semanticContentComplete = skipR2007TableOverrides(
9709 version, &contentBuf, cellStrBuf, &contentHandleBuf,
9710 &m_content.m_subrecordRanges, tableBounds);
9711 if (m_semanticContentComplete
9712 && (!contentBuf.isGood()
9713 || (cellStrBuf != nullptr && !contentStringBuf.isGood())
9714 || !contentHandleBuf.isGood()))
9715 m_semanticContentComplete = false;
9716 if (m_semanticContentComplete) {
9717 *buf = contentBuf;
9718 if (cellStrBuf != nullptr)
9719 *sBuf = contentStringBuf;
9720 hBuff = contentHandleBuf;
9721 } else {
9722 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"
)
;
9723 }
9724
9725 if (!buf->isGood() || !sBuf->isGood() || !hBuff.isGood())
9726 return fail();
9727 m_valueFlag = parsedValueFlag;
9728 m_horizontalDirection = parsedHorizontalDirection;
9729 return commit();
9730}
9731
9732bool DRW_TableContentObject::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
9733 reset();
9734 const auto fail = [this, buf]() {
9735 if (buf != nullptr)
9736 buf->invalidate();
9737 reset();
9738 return false;
9739 };
9740 if (buf == nullptr || version <= DRW::AC1018)
9741 return fail();
9742
9743 dwgBuffer sBuff = *buf;
9744 sBuff.setVariableTextByteLength(true);
9745 dwgBuffer *sBuf = &sBuff;
9746 bool ret = DRW_TableEntry::parseDwg(version, buf, sBuf, bs);
9747 DRW_DBG("\n************************** parsing table content object ************************\n")DRW_dbg::dbg("\n************************** parsing table content object ************************\n"
)
;
9748 if (!ret)
9749 return fail();
9750
9751 dwgBuffer bodyBuff = *buf;
9752 dwgBuffer *bodyBuf = &bodyBuff;
9753
9754 dwgBuffer hBuff = *buf;
9755 seekTableObjectHandleStream(version, &hBuff, objSize);
9756 std::uint64_t handleEndBit = 0;
9757 if (!dwgHandleStreamEndBit(*buf, version, objSize, bs, handleEndBit))
9758 return fail();
9759 std::uint32_t parsedParentHandle = 0;
9760 std::vector<std::uint32_t> parsedReactors;
9761 std::uint32_t parsedXDictHandle = 0;
9762 if (!readTableObjectCommonHandles(&hBuff, handle, numReactors, xDictFlag,
9763 &parsedParentHandle, &parsedReactors,
9764 &parsedXDictHandle, handleEndBit))
9765 return fail();
9766
9767 DRW_TableContent parsedContent;
9768 TableDwgBounds tableBounds;
9769 tableBounds.bodyEndBit = tableBodyEndBit(version, sBuf, objSize);
9770 tableBounds.stringEndBit = proxyEntityEndBit(*sBuf, 0);
9771 tableBounds.handleEndBit = handleEndBit;
9772 const bool parseComplete = parseTableContent(
9773 version, bodyBuf, sBuf, &hBuff, parsedContent, tableBounds);
9774 const std::uint64_t bodyBitOffset =
9775 bodyBuf->getPosition() * 8u + bodyBuf->getBitPos();
9776 if (!parseComplete || !bodyBuf->isGood() || !sBuf->isGood()
9777 || !hBuff.isGood() || bodyBitOffset > objSize)
9778 return fail();
9779
9780 m_content = std::move(parsedContent);
9781 m_parseComplete = true;
9782 parentHandle = parsedParentHandle;
9783 reactorHandles = std::move(parsedReactors);
9784 xDictHandle = parsedXDictHandle;
9785 return true;
9786}
9787
9788void DRW_LWPolyline::applyExtrusion(){
9789 if (haveExtrusion) {
9790 calculateAxis(extPoint);
9791 for (unsigned int i=0; i<vertlist.size(); i++) {
9792 auto& vert = vertlist.at(i);
9793 DRW_Coord v(vert->x, vert->y, elevation);
9794 extrudePoint(extPoint, &v);
9795 vert->x = v.x;
9796 vert->y = v.y;
9797 }
9798 }
9799}
9800
9801bool DRW_LWPolyline::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
9802 switch (code) {
9803 case 10: {
9804 if (vertlist.size() >= static_cast<std::size_t>(kMaxLWPolylineVertices)
9805 || (m_dxfVertexCountSeen
9806 && vertlist.size() >= static_cast<std::size_t>(vertexnum)))
9807 return false;
9808 vertex = std::make_shared<DRW_Vertex2D>();
9809 vertlist.push_back(vertex);
9810 vertex->x = reader->getDouble();
9811 break; }
9812 case 20:
9813 if(vertex)
9814 vertex->y = reader->getDouble();
9815 break;
9816 case 40:
9817 if(vertex)
9818 vertex->stawidth = reader->getDouble();
9819 break;
9820 case 41:
9821 if(vertex)
9822 vertex->endwidth = reader->getDouble();
9823 break;
9824 case 42:
9825 if(vertex)
9826 vertex->bulge = reader->getDouble();
9827 break;
9828 case 91:
9829 if (vertex)
9830 vertex->identifier = reader->getInt32();
9831 break;
9832 case 38:
9833 elevation = reader->getDouble();
9834 break;
9835 case 39:
9836 thickness = reader->getDouble();
9837 break;
9838 case 43:
9839 width = reader->getDouble();
9840 break;
9841 case 70:
9842 flags = reader->getInt32();
9843 break;
9844 case 90:
9845 if (m_dxfVertexCountSeen)
9846 return false;
9847 vertexnum = reader->getInt32();
9848 if (!isValidCount(vertexnum, kMaxLWPolylineVertices))
9849 return false;
9850 m_dxfVertexCountSeen = true;
9851 return DRW::reserve(vertlist, vertexnum);
9852 case 210:
9853 haveExtrusion = true;
9854 extPoint.x = reader->getDouble();
9855 break;
9856 case 220:
9857 extPoint.y = reader->getDouble();
9858 break;
9859 case 230:
9860 extPoint.z = reader->getDouble();
9861 break;
9862 default:
9863 return DRW_Entity::parseCode(code, reader);
9864 }
9865
9866 return true;
9867}
9868
9869bool DRW_LWPolyline::validateDxf() const {
9870 return (!m_dxfVertexCountSeen
9871 || vertlist.size() == static_cast<std::size_t>(vertexnum))
9872 && validatePayloadFields();
9873}
9874
9875bool DRW_LWPolyline::validatePayloadFields() const {
9876 const auto finite = [](double value) { return std::isfinite(value); };
9877 const auto finiteVertex = [&finite](const auto& vertex) {
9878 return vertex != nullptr && finite(vertex->x) && finite(vertex->y)
9879 && finite(vertex->stawidth) && finite(vertex->endwidth)
9880 && finite(vertex->bulge)
9881 && vertex->identifier >= std::numeric_limits<std::int32_t>::min()
9882 && vertex->identifier <= std::numeric_limits<std::int32_t>::max();
9883 };
9884
9885 // LWPOLYLINE group 70 has only the closed and linetype-generation bits.
9886 return flags >= 0 && (flags & ~0x81) == 0
9887 && finite(width) && finite(elevation) && finite(thickness)
9888 && finite(extPoint.x) && finite(extPoint.y) && finite(extPoint.z)
9889 && vertlist.size() <= static_cast<std::size_t>(kMaxLWPolylineVertices)
9890 && vertlist.size()
9891 <= static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max())
9892 && std::all_of(vertlist.cbegin(), vertlist.cend(), finiteVertex);
9893}
9894
9895bool DRW_LWPolyline::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
9896 const auto resetState = [this]() {
9897 DRW_Entity::reset();
9898 vertexnum = 0;
9899 flags = 0;
9900 width = 0.0;
9901 elevation = 0.0;
9902 thickness = 0.0;
9903 extPoint = DRW_Coord{0.0, 0.0, 1.0};
9904 vertlist.clear();
9905 vertex.reset();
9906 m_dxfVertexCountSeen = false;
9907 };
9908 resetState();
9909 dwgBuffer *sourceBuf = buf;
9910 const auto fail = [sourceBuf, resetState]() {
9911 if (sourceBuf != nullptr)
9912 sourceBuf->invalidate();
9913 resetState();
9914 return false;
9915 };
9916 if (sourceBuf == nullptr)
9917 return fail();
9918
9919 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
9920 buf = &bodyProbe;
9921 bool ret = DRW_Entity::parseDwgCommon(version, buf, bs);
9922 if (!ret || !buf->isGood())
9923 return fail();
9924 DRW_DBG("\n***************************** parsing LWPolyline *******************************************\n")DRW_dbg::dbg("\n***************************** parsing LWPolyline *******************************************\n"
)
;
9925
9926 const std::uint64_t bodyEndBit = dwgDataEndBit;
9927 std::uint16_t rawFlagsValue = 0;
9928 if (!readBoundedBitShort(*buf, bodyEndBit, rawFlagsValue))
9929 return fail();
9930 const int rawFlags = static_cast<int>(rawFlagsValue);
9931 double parsedWidth = 0.0;
9932 double parsedElevation = 0.0;
9933 double parsedThickness = 0.0;
9934 DRW_Coord parsedExtPoint{0.0, 0.0, 1.0};
9935 if ((rawFlags & 4)
9936 && !readBoundedBitDouble(*buf, bodyEndBit, parsedWidth))
9937 return fail();
9938 if ((rawFlags & 8)
9939 && !readBoundedBitDouble(*buf, bodyEndBit, parsedElevation))
9940 return fail();
9941 if ((rawFlags & 2)
9942 && !readBoundedBitDouble(*buf, bodyEndBit, parsedThickness))
9943 return fail();
9944 if ((rawFlags & 1)
9945 && !readBoundedExtrusion(*buf, bodyEndBit, false, parsedExtPoint))
9946 return fail();
9947 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(rawFlags)DRW_dbg::dbg(rawFlags);
9948
9949 const std::uint32_t minimumVertexBits = version < DRW::AC1015 ? 128u : 4u;
9950 std::uint32_t parsedVertexCount = 0;
9951 if (!readTableBodyCount(version, buf, objSize,
9952 kMaxLWPolylineVertices, minimumVertexBits,
9953 parsedVertexCount)) {
9954 return fail();
9955 }
9956
9957 std::uint64_t vertexBits = 0;
9958 if (parsedVertexCount > 0) {
9959 if (version < DRW::AC1015) {
9960 if (!dwgSafety::multiply(parsedVertexCount, 128, vertexBits))
9961 return fail();
9962 } else {
9963 std::uint64_t deltaBits = 0;
9964 if (!dwgSafety::multiply(parsedVertexCount - 1, 4, deltaBits)
9965 || !dwgSafety::add(128, deltaBits, vertexBits))
9966 return fail();
9967 }
9968 }
9969 if (!proxyEntityHasBits(*buf, bodyEndBit, vertexBits))
9970 return fail();
9971
9972 std::vector<std::shared_ptr<DRW_Vertex2D>> parsedVertices;
9973 if (!DRW::reserve(parsedVertices, parsedVertexCount))
9974 return fail();
9975
9976 std::uint32_t bulgesnum = 0;
9977 if (rawFlags & 16) {
9978 if (!readTableBodyCount(version, buf, objSize,
9979 kMaxLWPolylineVertices, 2,
9980 bulgesnum))
9981 return fail();
9982 }
9983 std::uint32_t vertexIdCount = 0;
9984 if (version > DRW::AC1021) {//2010+
9985 if (rawFlags & 1024) {
9986 if (!readTableBodyCount(version, buf, objSize,
9987 kMaxLWPolylineVertices, 2,
9988 vertexIdCount))
9989 return fail();
9990 }
9991 }
9992 std::uint32_t widthsnum = 0;
9993 if (rawFlags & 32) {
9994 if (!readTableBodyCount(version, buf, objSize,
9995 kMaxLWPolylineVertices, 4,
9996 widthsnum))
9997 return fail();
9998 }
9999 std::uint64_t bulgeBits = 0;
10000 std::uint64_t vertexIdBits = 0;
10001 std::uint64_t widthBits = 0;
10002 std::uint64_t optionalBits = 0;
10003 if (!dwgSafety::multiply(bulgesnum, 2, bulgeBits)
10004 || !dwgSafety::multiply(vertexIdCount, 2, vertexIdBits)
10005 || !dwgSafety::multiply(widthsnum, 4, widthBits)
10006 || !dwgSafety::add(bulgeBits, vertexIdBits, optionalBits)
10007 || !dwgSafety::add(optionalBits, widthBits, optionalBits)) {
10008 return fail();
10009 }
10010 if (!proxyEntityHasBits(*buf, bodyEndBit, optionalBits))
10011 return fail();
10012 DRW_DBG("\nvertex num: ")DRW_dbg::dbg("\nvertex num: "); DRW_DBG(parsedVertexCount)DRW_dbg::dbg(parsedVertexCount); DRW_DBG(" bulges num: ")DRW_dbg::dbg(" bulges num: "); DRW_DBG(bulgesnum)DRW_dbg::dbg(bulgesnum);
10013 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);
10014
10015 if (parsedVertexCount > 0) { //verify if is lwpol without vertex (empty)
10016 // add vertexes
10017 auto parsedVertex = std::make_shared<DRW_Vertex2D>();
10018 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedVertex->x)
10019 || !readBoundedRawDouble(*buf, bodyEndBit, parsedVertex->y))
10020 return fail();
10021 parsedVertices.push_back(parsedVertex);
10022 auto pv = parsedVertex;
10023 for (std::uint32_t i = 1; i < parsedVertexCount; ++i){
10024 parsedVertex = std::make_shared<DRW_Vertex2D>();
10025 if (version < DRW::AC1015) {//14-
10026 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedVertex->x)
10027 || !readBoundedRawDouble(*buf, bodyEndBit, parsedVertex->y))
10028 return fail();
10029 } else {
10030 if (!readBoundedDefaultDouble(*buf, bodyEndBit, pv->x,
10031 parsedVertex->x)
10032 || !readBoundedDefaultDouble(*buf, bodyEndBit, pv->y,
10033 parsedVertex->y))
10034 return fail();
10035 }
10036 pv = parsedVertex;
10037 parsedVertices.push_back(parsedVertex);
10038 }
10039 }
10040 // The optional vectors are independent DWG fields, not per-point
10041 // conditionals. Consume them even when num_points is zero so the handle
10042 // stream remains aligned; values without a corresponding vertex are
10043 // intentionally discarded by the existing model.
10044 for (std::uint32_t i = 0; i < bulgesnum; i++){
10045 double bulge = 0.0;
10046 if (!readBoundedBitDouble(*buf, bodyEndBit, bulge))
10047 return fail();
10048 if (i < parsedVertices.size())
10049 parsedVertices.at(i)->bulge = bulge;
10050 }
10051 //add vertexId
10052 if (version > DRW::AC1021) {//2010+
10053 for (std::uint32_t i = 0; i < vertexIdCount; i++){
10054 std::int32_t vertexId = 0;
10055 if (!readBoundedBitLong(*buf, bodyEndBit, vertexId))
10056 return fail();
10057 if (i < parsedVertices.size())
10058 parsedVertices.at(i)->identifier = vertexId;
10059 }
10060 }
10061 //add widths
10062 for (std::uint32_t i = 0; i < widthsnum; i++){
10063 double staW = 0.0;
10064 double endW = 0.0;
10065 if (!readBoundedBitDouble(*buf, bodyEndBit, staW)
10066 || !readBoundedBitDouble(*buf, bodyEndBit, endW))
10067 return fail();
10068 if (i < parsedVertices.size()) {
10069 parsedVertices.at(i)->stawidth = staW;
10070 parsedVertices.at(i)->endwidth = endW;
10071 }
10072 }
10073 if (DRW_DBGGLDRW_dbg::getInstance()->getLevel() == DRW_dbg::Level::Debug){
10074 DRW_DBG("\nVertex list: ")DRW_dbg::dbg("\nVertex list: ");
10075 for (auto& pv: parsedVertices) {
10076 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);
10077 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);
10078 DRW_DBG(" identifier: ")DRW_dbg::dbg(" identifier: "); DRW_DBG(pv->identifier)DRW_dbg::dbg(pv->identifier);
10079 }
10080 }
10081
10082 const auto finite = [](const DRW_Coord& point) {
10083 return std::isfinite(point.x) && std::isfinite(point.y)
10084 && std::isfinite(point.z);
10085 };
10086 if (!finite(parsedExtPoint) || !std::isfinite(parsedWidth)
10087 || !std::isfinite(parsedElevation) || !std::isfinite(parsedThickness)
10088 || !std::all_of(parsedVertices.begin(), parsedVertices.end(),
10089 [](const std::shared_ptr<DRW_Vertex2D>& point) {
10090 return point != nullptr
10091 && std::isfinite(point->x)
10092 && std::isfinite(point->y)
10093 && std::isfinite(point->bulge)
10094 && std::isfinite(point->stawidth)
10095 && std::isfinite(point->endwidth);
10096 }))
10097 return fail();
10098
10099 DRW_DBG("\n")DRW_dbg::dbg("\n");
10100 /* Common Entity Handle Data */
10101 std::uint64_t handleEndBit = 0;
10102 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
10103 handleEndBit))
10104 return fail();
10105 dwgBuffer handleProbe = buf->forkIndependent();
10106 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
10107 handleEndBit);
10108 if (!ret || !handleProbe.isGood())
10109 return fail();
10110
10111 // Translate DWG LWPLINE flag bits to DXF group 70 bits.
10112 int dxfFlags = 0;
10113 if (rawFlags & 512)
10114 dxfFlags |= 1;
10115 if (rawFlags & 256)
10116 dxfFlags |= 128;
10117 vertexnum = static_cast<std::int32_t>(parsedVertexCount);
10118 flags = dxfFlags;
10119 width = parsedWidth;
10120 elevation = parsedElevation;
10121 thickness = parsedThickness;
10122 extPoint = parsedExtPoint;
10123 vertlist = std::move(parsedVertices);
10124 *sourceBuf = handleProbe;
10125 DRW_DBG("end flags value: ")DRW_dbg::dbg("end flags value: "); DRW_DBG(flags)DRW_dbg::dbg(flags);
10126 /* CRC X --- */
10127 return true;
10128}
10129
10130
10131// ----------------------------------------------------------------------------
10132// DRW_MLine — multiline entity (ODA §19.4.78, fixed type 0x2F = 47).
10133// ----------------------------------------------------------------------------
10134
10135bool DRW_MLine::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
10136 switch (code) {
10137 case 2:
10138 styleName = reader->getUtf8String();
10139 break;
10140 case 340:
10141 styleHandle = static_cast<std::uint32_t>(reader->getHandleString());
10142 break;
10143 case 40:
10144 scale = reader->getDouble();
10145 break;
10146 case 70:
10147 {
10148 int value = 0;
10149 if (!readDxfIntInRange(reader, 0, 2, value))
10150 return false;
10151 justification = static_cast<std::uint8_t>(value);
10152 }
10153 break;
10154 case 71:
10155 openClosed = reader->getInt32();
10156 break;
10157 case 72:
10158 if (m_dxfVertexCountSeen)
10159 return false;
10160 {
10161 const std::int32_t count = reader->getInt32();
10162 if (!isValidCount(count, kMaxDxfVertices))
10163 return false;
10164 numVerts = static_cast<std::uint16_t>(count);
10165 m_dxfVertexCountSeen = true;
10166 }
10167 break;
10168 case 73:
10169 if (m_dxfLineCountSeen)
10170 return false;
10171 {
10172 const std::int32_t count = reader->getInt32();
10173 if (!isValidCount(count, kMaxDxfLines))
10174 return false;
10175 numLines = static_cast<std::uint8_t>(count);
10176 m_dxfLineCountSeen = true;
10177 }
10178 break;
10179 case 10:
10180 basePoint.x = reader->getDouble();
10181 break;
10182 case 20:
10183 basePoint.y = reader->getDouble();
10184 break;
10185 case 30:
10186 basePoint.z = reader->getDouble();
10187 break;
10188 case 210:
10189 extPoint.x = reader->getDouble();
10190 break;
10191 case 220:
10192 extPoint.y = reader->getDouble();
10193 break;
10194 case 230:
10195 extPoint.z = reader->getDouble();
10196 break;
10197 // Per-vertex block: code 11 starts a new vertex; 12/13 follow.
10198 case 11:
10199 if (m_currentSegExpected >= 0 || m_currentAreaExpected >= 0)
10200 return false;
10201 ++m_currentVertexIdx;
10202 m_currentElementIdx = 0;
10203 if (m_currentVertexIdx >= kMaxDxfVertices
10204 || (m_dxfVertexCountSeen
10205 && m_currentVertexIdx >= static_cast<int>(numVerts)))
10206 return false;
10207 vertlist.emplace_back();
10208 vertlist[m_currentVertexIdx].position.x = reader->getDouble();
10209 break;
10210 case 21:
10211 if (m_currentVertexIdx >= 0)
10212 vertlist[m_currentVertexIdx].position.y = reader->getDouble();
10213 break;
10214 case 31:
10215 if (m_currentVertexIdx >= 0)
10216 vertlist[m_currentVertexIdx].position.z = reader->getDouble();
10217 break;
10218 case 12:
10219 if (m_currentVertexIdx >= 0)
10220 vertlist[m_currentVertexIdx].vertexDir.x = reader->getDouble();
10221 break;
10222 case 22:
10223 if (m_currentVertexIdx >= 0)
10224 vertlist[m_currentVertexIdx].vertexDir.y = reader->getDouble();
10225 break;
10226 case 32:
10227 if (m_currentVertexIdx >= 0)
10228 vertlist[m_currentVertexIdx].vertexDir.z = reader->getDouble();
10229 break;
10230 case 13:
10231 if (m_currentVertexIdx >= 0)
10232 vertlist[m_currentVertexIdx].miterDir.x = reader->getDouble();
10233 break;
10234 case 23:
10235 if (m_currentVertexIdx >= 0)
10236 vertlist[m_currentVertexIdx].miterDir.y = reader->getDouble();
10237 break;
10238 case 33:
10239 if (m_currentVertexIdx >= 0)
10240 vertlist[m_currentVertexIdx].miterDir.z = reader->getDouble();
10241 break;
10242 // 74 = segment-param count for current element. Sets up the inner
10243 // vector and resets the running param count. 41 reads each param.
10244 // 75 = fill-param count; 42 reads each. After fills are consumed,
10245 // advance to the next element. AutoCAD emits 74/41*/75/42* per element.
10246 case 74:
10247 if (m_currentVertexIdx < 0
10248 || m_currentSegExpected >= 0 || m_currentAreaExpected >= 0)
10249 return false;
10250 {
10251 auto& v = vertlist[m_currentVertexIdx];
10252 if (m_currentElementIdx != static_cast<int>(v.segParms.size())
10253 || m_currentElementIdx >= kMaxDxfLines
10254 || (m_dxfLineCountSeen
10255 && m_currentElementIdx >= static_cast<int>(numLines)))
10256 return false;
10257 const std::int32_t count = reader->getInt32();
10258 if (!isValidCount(count, kMaxDxfParameters))
10259 return false;
10260 v.segParms.emplace_back();
10261 v.areaFillParms.emplace_back();
10262 m_currentSegExpected = count;
10263 m_currentSegFillCount = 0;
10264 }
10265 break;
10266 case 41:
10267 if (m_currentVertexIdx < 0 || m_currentSegExpected < 0
10268 || m_currentElementIdx >= static_cast<int>(vertlist[m_currentVertexIdx].segParms.size()))
10269 return false;
10270 if (static_cast<std::int32_t>(
10271 vertlist[m_currentVertexIdx].segParms[m_currentElementIdx].size())
10272 >= m_currentSegExpected)
10273 return false;
10274 vertlist[m_currentVertexIdx].segParms[m_currentElementIdx]
10275 .push_back(reader->getDouble());
10276 break;
10277 case 75:
10278 if (m_currentVertexIdx < 0 || m_currentSegExpected < 0
10279 || m_currentAreaExpected >= 0
10280 || m_currentElementIdx >= static_cast<int>(vertlist[m_currentVertexIdx].segParms.size()))
10281 return false;
10282 if (static_cast<std::int32_t>(
10283 vertlist[m_currentVertexIdx].segParms[m_currentElementIdx].size())
10284 != m_currentSegExpected)
10285 return false;
10286 m_currentSegExpected = -1;
10287 m_currentSegFillCount = reader->getInt32();
10288 if (!isValidCount(m_currentSegFillCount, kMaxDxfParameters))
10289 return false;
10290 m_currentAreaExpected = m_currentSegFillCount;
10291 if (m_currentAreaExpected == 0) {
10292 m_currentAreaExpected = -1;
10293 ++m_currentElementIdx;
10294 }
10295 break;
10296 case 42:
10297 if (m_currentVertexIdx < 0 || m_currentAreaExpected < 0
10298 || m_currentElementIdx >= static_cast<int>(vertlist[m_currentVertexIdx].areaFillParms.size()))
10299 return false;
10300 if (static_cast<std::int32_t>(
10301 vertlist[m_currentVertexIdx].areaFillParms[m_currentElementIdx].size())
10302 >= m_currentAreaExpected)
10303 return false;
10304 vertlist[m_currentVertexIdx].areaFillParms[m_currentElementIdx]
10305 .push_back(reader->getDouble());
10306 if (static_cast<std::int32_t>(
10307 vertlist[m_currentVertexIdx].areaFillParms[m_currentElementIdx].size())
10308 == m_currentAreaExpected) {
10309 m_currentAreaExpected = -1;
10310 ++m_currentElementIdx;
10311 }
10312 break;
10313 default:
10314 return DRW_Entity::parseCode(code, reader);
10315 }
10316 return true;
10317}
10318
10319bool DRW_MLine::validateDxf() const {
10320 if (m_currentSegExpected >= 0 || m_currentAreaExpected >= 0)
10321 return false;
10322 if (m_dxfVertexCountSeen && vertlist.size() != numVerts)
10323 return false;
10324 if (!m_dxfLineCountSeen)
10325 return true;
10326 for (const DRW_MLineVertex& vertex : vertlist) {
10327 if (vertex.segParms.size() != numLines
10328 || vertex.areaFillParms.size() != numLines)
10329 return false;
10330 }
10331 return validatePayloadFields();
10332}
10333
10334bool DRW_MLine::validatePayloadFields() const {
10335 const auto finite = [](const DRW_Coord& point) {
10336 return std::isfinite(point.x) && std::isfinite(point.y)
10337 && std::isfinite(point.z);
10338 };
10339 if (!finite(basePoint) || !finite(extPoint) || !std::isfinite(scale)
10340 || openClosed < 0 || openClosed > 0xffff
10341 || numVerts > static_cast<std::uint16_t>(kMaxDxfVertices)
10342 || vertlist.size() > static_cast<std::size_t>(kMaxDxfVertices)
10343 || vertlist.size() != static_cast<std::size_t>(numVerts))
10344 return false;
10345
10346 for (const DRW_MLineVertex& vertex : vertlist) {
10347 if (!finite(vertex.position) || !finite(vertex.vertexDir)
10348 || !finite(vertex.miterDir)
10349 || vertex.segParms.size() != numLines
10350 || vertex.areaFillParms.size() != numLines)
10351 return false;
10352 const auto validParameters = [](const auto& parameterLists) {
10353 return std::all_of(
10354 parameterLists.cbegin(), parameterLists.cend(),
10355 [](const std::vector<double>& values) {
10356 return values.size() <= static_cast<std::size_t>(
10357 DRW_MLine::kMaxDxfParameters)
10358 && std::all_of(values.cbegin(), values.cend(),
10359 [](double value) {
10360 return std::isfinite(value);
10361 });
10362 });
10363 };
10364 if (!validParameters(vertex.segParms)
10365 || !validParameters(vertex.areaFillParms))
10366 return false;
10367 }
10368 return true;
10369}
10370
10371void DRW_MLine::resetDwgState() {
10372 DRW_Entity::reset();
10373 scale = 1.0;
10374 justification = 0;
10375 basePoint = DRW_Coord{0.0, 0.0, 0.0};
10376 extPoint = DRW_Coord{0.0, 0.0, 1.0};
10377 openClosed = 1;
10378 numLines = 0;
10379 numVerts = 0;
10380 styleName.clear();
10381 styleHandle = 0;
10382 vertlist.clear();
10383 m_currentVertexIdx = -1;
10384 m_currentElementIdx = 0;
10385 m_currentSegFillCount = 0;
10386 m_currentSegExpected = -1;
10387 m_currentAreaExpected = -1;
10388 m_dxfVertexCountSeen = false;
10389 m_dxfLineCountSeen = false;
10390}
10391
10392bool DRW_MLine::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
10393 resetDwgState();
10394 dwgBuffer *sourceBuf = buf;
10395 auto fail = [this, sourceBuf]() {
10396 if (sourceBuf != nullptr)
10397 sourceBuf->invalidate();
10398 resetDwgState();
10399 return false;
10400 };
10401 if (sourceBuf == nullptr)
10402 return fail();
10403
10404 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
10405 buf = &bodyProbe;
10406
10407 if (!DRW_Entity::parseDwgCommon(version, buf, bs)) return fail();
10408 DRW_DBG("\n***************************** parsing MLINE *********************\n")DRW_dbg::dbg("\n***************************** parsing MLINE *********************\n"
)
;
10409 // Per ODA §19.4.78 / libreDWG dwg_decode_MLINE:
10410 // BD scale, RC justification, 3BD basePoint, BE extrusion,
10411 // BS open/closed flag, RC num_lines, BS num_verts,
10412 // then per-vertex: 3BD pos, 3BD vdir, 3BD mdir,
10413 // per-line: BS num_segparms × BD parm, BS num_areafillparms × BD parm.
10414 const std::uint64_t bodyEndBit = dwgDataEndBit;
10415 double parsedScale = 0.0;
10416 std::uint8_t parsedJustification = 0;
10417 DRW_Coord parsedBasePoint;
10418 DRW_Coord parsedExtPoint;
10419 std::uint16_t parsedOpenClosed = 0;
10420 std::uint8_t parsedNumLines = 0;
10421 std::uint16_t parsedNumVerts = 0;
10422 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedScale)
10423 || !readBoundedRawChar8(*buf, bodyEndBit, parsedJustification)
10424 || !readBoundedBitCoord(*buf, bodyEndBit, parsedBasePoint)
10425 || !readBoundedExtrusion(*buf, bodyEndBit, false, parsedExtPoint)
10426 || !readBoundedBitShort(*buf, bodyEndBit, parsedOpenClosed)
10427 || !readBoundedRawChar8(*buf, bodyEndBit, parsedNumLines)
10428 || !readBoundedBitShort(*buf, bodyEndBit, parsedNumVerts))
10429 return fail();
10430 if (!std::isfinite(parsedScale)
10431 || !std::isfinite(parsedBasePoint.x)
10432 || !std::isfinite(parsedBasePoint.y)
10433 || !std::isfinite(parsedBasePoint.z)
10434 || !std::isfinite(parsedExtPoint.x)
10435 || !std::isfinite(parsedExtPoint.y)
10436 || !std::isfinite(parsedExtPoint.z))
10437 return fail();
10438 DRW_DBG(" mline scale: ")DRW_dbg::dbg(" mline scale: "); DRW_DBG(parsedScale)DRW_dbg::dbg(parsedScale);
10439 DRW_DBG(" just: ")DRW_dbg::dbg(" just: "); DRW_DBG(static_cast<int>(parsedJustification))DRW_dbg::dbg(static_cast<int>(parsedJustification));
10440 DRW_DBG(" openClosed: ")DRW_dbg::dbg(" openClosed: "); DRW_DBG(parsedOpenClosed)DRW_dbg::dbg(parsedOpenClosed);
10441 DRW_DBG(" lines: ")DRW_dbg::dbg(" lines: "); DRW_DBG(static_cast<int>(parsedNumLines))DRW_dbg::dbg(static_cast<int>(parsedNumLines));
10442 DRW_DBG(" verts: ")DRW_dbg::dbg(" verts: "); DRW_DBG(parsedNumVerts)DRW_dbg::dbg(parsedNumVerts); DRW_DBG("\n")DRW_dbg::dbg("\n");
10443 if (!buf->isGood()
10444 || !isValidCount(static_cast<std::int32_t>(parsedNumVerts), kMaxDxfVertices)
10445 || !isValidCount(static_cast<std::int32_t>(parsedNumLines), kMaxDxfLines))
10446 return fail();
10447
10448 // Each vertex always contains three minimum-width 3BD values and one
10449 // minimum-width BS for each segment/fill parameter count. Check this
10450 // before allocating the per-vertex/per-line vector grid.
10451 const std::uint64_t minimumBitsPerVertex =
10452 18u + static_cast<std::uint64_t>(parsedNumLines) * 4u;
10453 std::uint64_t minimumVertexBits = 0;
10454 if (!dwgSafety::multiply(static_cast<std::uint64_t>(parsedNumVerts),
10455 minimumBitsPerVertex, minimumVertexBits)
10456 || !proxyEntityHasBits(*buf, bodyEndBit, minimumVertexBits))
10457 return fail();
10458
10459 // The per-vertex parameter lists are also capped by the DWG spec. Keep a
10460 // combined budget so a valid per-list maximum cannot multiply into an
10461 // unbounded number of vector allocations on hostile input.
10462 constexpr std::uint64_t kMaxDwgParameters = 1000000;
10463 std::uint64_t totalParameters = 0;
10464 std::vector<DRW_MLineVertex> parsedVertices;
10465 if (!DRW::reserve(parsedVertices, parsedNumVerts))
10466 return fail();
10467 for (int vi = 0; vi < parsedNumVerts; ++vi) {
10468 DRW_MLineVertex vtx;
10469 if (!readBoundedBitCoord(*buf, bodyEndBit, vtx.position)
10470 || !readBoundedBitCoord(*buf, bodyEndBit, vtx.vertexDir)
10471 || !readBoundedBitCoord(*buf, bodyEndBit, vtx.miterDir))
10472 return fail();
10473 if (!DRW::reserve(vtx.segParms, parsedNumLines)
10474 || !DRW::reserve(vtx.areaFillParms, parsedNumLines))
10475 return fail();
10476 if (!DRW::resize(vtx.segParms, parsedNumLines)
10477 || !DRW::resize(vtx.areaFillParms, parsedNumLines))
10478 return fail();
10479 for (int li = 0; li < parsedNumLines; ++li) {
10480 std::uint16_t nSeg = 0;
10481 if (!readBoundedBitShort(*buf, bodyEndBit, nSeg)
10482 || !proxyEntityHasBits(*buf, bodyEndBit,
10483 static_cast<std::uint64_t>(nSeg) * 2u))
10484 return fail();
10485 if (!buf->isGood()
10486 || !isValidCount(static_cast<std::int32_t>(nSeg),
10487 kMaxDxfParameters)
10488 || totalParameters > kMaxDwgParameters - nSeg)
10489 return fail();
10490 totalParameters += nSeg;
10491 if (!DRW::reserve(vtx.segParms[li], nSeg))
10492 return fail();
10493 for (int s = 0; s < nSeg; ++s) {
10494 double value = 0.0;
10495 if (!readBoundedBitDouble(*buf, bodyEndBit, value)
10496 || !std::isfinite(value))
10497 return fail();
10498 vtx.segParms[li].push_back(value);
10499 }
10500 std::uint16_t nFill = 0;
10501 if (!readBoundedBitShort(*buf, bodyEndBit, nFill)
10502 || !proxyEntityHasBits(*buf, bodyEndBit,
10503 static_cast<std::uint64_t>(nFill) * 2u))
10504 return fail();
10505 if (!buf->isGood()
10506 || !isValidCount(static_cast<std::int32_t>(nFill),
10507 kMaxDxfParameters)
10508 || totalParameters > kMaxDwgParameters - nFill)
10509 return fail();
10510 totalParameters += nFill;
10511 if (!DRW::reserve(vtx.areaFillParms[li], nFill))
10512 return fail();
10513 for (int f = 0; f < nFill; ++f) {
10514 double value = 0.0;
10515 if (!readBoundedBitDouble(*buf, bodyEndBit, value)
10516 || !std::isfinite(value))
10517 return fail();
10518 vtx.areaFillParms[li].push_back(value);
10519 }
10520 }
10521 const auto finite = [](const DRW_Coord& point) {
10522 return std::isfinite(point.x) && std::isfinite(point.y)
10523 && std::isfinite(point.z);
10524 };
10525 if (!finite(vtx.position) || !finite(vtx.vertexDir)
10526 || !finite(vtx.miterDir))
10527 return fail();
10528 parsedVertices.push_back(std::move(vtx));
10529 }
10530 if (!buf->isGood())
10531 return fail();
10532
10533 std::uint64_t handleEndBit = 0;
10534 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
10535 handleEndBit))
10536 return fail();
10537 dwgBuffer handleProbe = buf->forkIndependent();
10538 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
10539 handleEndBit))
10540 return fail();
10541 // MLINE has one extra handle in the handle stream after the standard
10542 // entity handles: the MLINESTYLE reference. Read if available — some
10543 // older files (R14) store the style name inline instead.
10544 std::uint32_t parsedStyleHandle = 0;
10545 if (version > DRW::AC1014
10546 && proxyEntityHasBits(handleProbe, handleEndBit, 8)) {
10547 dwgHandle styleH;
10548 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
10549 styleH))
10550 return fail();
10551 if (!handleProbe.isGood())
10552 return fail();
10553 parsedStyleHandle = styleH.ref;
10554 DRW_DBG(" MLINE style handle: ")DRW_dbg::dbg(" MLINE style handle: ");
10555 DRW_DBGHL(styleH.code, styleH.size, styleH.ref)DRW_dbg::dbgHL(styleH.code, styleH.size, styleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
10556 }
10557 if (!handleProbe.isGood())
10558 return fail();
10559 *sourceBuf = handleProbe;
10560 scale = parsedScale;
10561 justification = parsedJustification;
10562 basePoint = parsedBasePoint;
10563 extPoint = parsedExtPoint;
10564 openClosed = parsedOpenClosed;
10565 numLines = parsedNumLines;
10566 numVerts = parsedNumVerts;
10567 styleHandle = parsedStyleHandle;
10568 vertlist = std::move(parsedVertices);
10569 return true;
10570}
10571
10572
10573// ----------------------------------------------------------------------------
10574// DRW_Underlay — UNDERLAY entity (PDFUNDERLAY/DGNUNDERLAY/DWFUNDERLAY).
10575// libreDWG UNDERLAYREFERENCE.spec field order:
10576// extrusion (BE) -> position (3BD) -> angle (BD radians) -> scale (BD x 3)
10577// -> flags (RC) -> contrast (RC) -> fade (RC) -> num_clip (BL)
10578// -> clip_verts (2RD × num_clip).
10579// Handle stream after standard entity handles: definition_id (H).
10580// ----------------------------------------------------------------------------
10581
10582bool DRW_Underlay::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
10583 switch (code) {
10584 case 340:
10585 definitionHandle = static_cast<std::uint32_t>(reader->getHandleString());
10586 break;
10587 case 10: position.x = reader->getDouble(); break;
10588 case 20: position.y = reader->getDouble(); break;
10589 case 30: position.z = reader->getDouble(); break;
10590 case 41: scale.x = reader->getDouble(); break;
10591 case 42: scale.y = reader->getDouble(); break;
10592 case 43: scale.z = reader->getDouble(); break;
10593 case 50: rotation = reader->getDouble(); break; // degrees in DXF
10594 case 210: extPoint.x = reader->getDouble(); break;
10595 case 220: extPoint.y = reader->getDouble(); break;
10596 case 230: extPoint.z = reader->getDouble(); break;
10597 case 280: {
10598 int value = 0;
10599 if (!readDxfIntInRange(reader, 0, 0xFF, value))
10600 return false;
10601 flags = static_cast<std::uint8_t>(value);
10602 break;
10603 }
10604 case 281: {
10605 int value = 0;
10606 if (!readDxfIntInRange(reader, 0, 100, value))
10607 return false;
10608 contrast = static_cast<std::uint8_t>(value);
10609 break;
10610 }
10611 case 282: {
10612 int value = 0;
10613 if (!readDxfIntInRange(reader, 0, 100, value))
10614 return false;
10615 fade = static_cast<std::uint8_t>(value);
10616 break;
10617 }
10618 case 11: {
10619 if (clipBoundary.size() >= kMaxClipVertices)
10620 return false;
10621 ++m_currentClipVertexIdx;
10622 if (m_currentClipVertexIdx >= static_cast<int>(clipBoundary.size())) {
10623 if (!DRW::resize(clipBoundary, m_currentClipVertexIdx + 1))
10624 return false;
10625 }
10626 clipBoundary[m_currentClipVertexIdx].x = reader->getDouble();
10627 break;
10628 }
10629 case 21:
10630 if (m_currentClipVertexIdx >= 0
10631 && m_currentClipVertexIdx < static_cast<int>(clipBoundary.size())) {
10632 clipBoundary[m_currentClipVertexIdx].y = reader->getDouble();
10633 }
10634 break;
10635 case 170: {
10636 const int count = reader->getInt32();
10637 if (!isValidCount(count, static_cast<std::int32_t>(kMaxClipVertices)))
10638 return false;
10639 m_inverseClipVertexCount = static_cast<std::uint32_t>(count);
10640 m_inverseClipCountSeen = true;
10641 if (!DRW::reserve(inverseClipBoundary, count))
10642 return false;
10643 break;
10644 }
10645 case 12: {
10646 if (inverseClipBoundary.size() >= kMaxClipVertices
10647 || (m_inverseClipCountSeen
10648 && inverseClipBoundary.size() >= m_inverseClipVertexCount))
10649 return false;
10650 ++m_currentInverseClipVertexIdx;
10651 if (m_currentInverseClipVertexIdx
10652 >= static_cast<int>(inverseClipBoundary.size())) {
10653 if (!DRW::resize(inverseClipBoundary,
10654 m_currentInverseClipVertexIdx + 1))
10655 return false;
10656 }
10657 inverseClipBoundary[m_currentInverseClipVertexIdx].x = reader->getDouble();
10658 break;
10659 }
10660 case 22:
10661 if (m_currentInverseClipVertexIdx >= 0
10662 && m_currentInverseClipVertexIdx
10663 < static_cast<int>(inverseClipBoundary.size())) {
10664 inverseClipBoundary[m_currentInverseClipVertexIdx].y = reader->getDouble();
10665 }
10666 break;
10667 default:
10668 return DRW_Entity::parseCode(code, reader);
10669 }
10670 return true;
10671}
10672
10673void DRW_Underlay::resetDwgState() {
10674 DRW_Entity::reset();
10675 // Entity carriers can be reused by callers. Clip payloads and their
10676 // parser counters belong to this record, not to the carrier lifetime.
10677 position = DRW_Coord{0.0, 0.0, 0.0};
10678 scale = DRW_Coord{1.0, 1.0, 1.0};
10679 rotation = 0.0;
10680 extPoint = DRW_Coord{0.0, 0.0, 1.0};
10681 flags = 2;
10682 contrast = 100;
10683 fade = 0;
10684 clipBoundary.clear();
10685 inverseClipBoundary.clear();
10686 m_currentClipVertexIdx = -1;
10687 m_currentInverseClipVertexIdx = -1;
10688 m_inverseClipVertexCount = 0;
10689 m_inverseClipCountSeen = false;
10690 definitionHandle = 0;
10691}
10692
10693bool DRW_Underlay::validatePayloadFields() const {
10694 const auto finite = [](const DRW_Coord& point) {
10695 return std::isfinite(point.x) && std::isfinite(point.y)
10696 && std::isfinite(point.z);
10697 };
10698 const auto finiteClipPoint = [](const DRW_Coord& point) {
10699 return std::isfinite(point.x) && std::isfinite(point.y);
10700 };
10701 return finite(position) && finite(scale) && finite(extPoint)
10702 && std::isfinite(rotation)
10703 && std::all_of(clipBoundary.cbegin(), clipBoundary.cend(),
10704 finiteClipPoint)
10705 && std::all_of(inverseClipBoundary.cbegin(),
10706 inverseClipBoundary.cend(), finiteClipPoint);
10707}
10708
10709bool DRW_Underlay::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
10710 resetDwgState();
10711 dwgBuffer *sourceBuf = buf;
10712 auto fail = [this, sourceBuf]() {
10713 if (sourceBuf != nullptr)
10714 sourceBuf->invalidate();
10715 resetDwgState();
10716 return false;
10717 };
10718 if (sourceBuf == nullptr)
10719 return fail();
10720
10721 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
10722 buf = &bodyProbe;
10723
10724 if (!DRW_Entity::parseDwgCommon(version, buf, bs)) return fail();
10725 DRW_DBG("\n***************************** parsing UNDERLAY ***************\n")DRW_dbg::dbg("\n***************************** parsing UNDERLAY ***************\n"
)
;
10726 const std::uint64_t bodyEndBit = dwgDataEndBit;
10727 DRW_Coord parsedExtPoint;
10728 DRW_Coord parsedPosition;
10729 DRW_Coord parsedScale;
10730 double parsedRotation = 0.0;
10731 std::uint8_t parsedFlags = 0;
10732 std::uint8_t parsedContrast = 0;
10733 std::uint8_t parsedFade = 0;
10734 if (!readBoundedExtrusion(*buf, bodyEndBit, false, parsedExtPoint)
10735 || !readBoundedBitCoord(*buf, bodyEndBit, parsedPosition)
10736 || !readBoundedBitDouble(*buf, bodyEndBit, parsedRotation)
10737 || !readBoundedBitDouble(*buf, bodyEndBit, parsedScale.x)
10738 || !readBoundedBitDouble(*buf, bodyEndBit, parsedScale.y)
10739 || !readBoundedBitDouble(*buf, bodyEndBit, parsedScale.z)
10740 || !readBoundedRawChar8(*buf, bodyEndBit, parsedFlags)
10741 || !readBoundedRawChar8(*buf, bodyEndBit, parsedContrast)
10742 || !readBoundedRawChar8(*buf, bodyEndBit, parsedFade))
10743 return fail();
10744 const auto finite = [](const DRW_Coord& point) {
10745 return std::isfinite(point.x) && std::isfinite(point.y)
10746 && std::isfinite(point.z);
10747 };
10748 if (!finite(parsedExtPoint) || !finite(parsedPosition)
10749 || !finite(parsedScale) || !std::isfinite(parsedRotation))
10750 return fail();
10751 std::uint32_t nClip = 0;
10752 DRW_DBG(" UNDERLAY pos: ")DRW_dbg::dbg(" UNDERLAY pos: "); DRW_DBG(parsedPosition.x)DRW_dbg::dbg(parsedPosition.x); DRW_DBG(",")DRW_dbg::dbg(",");
10753 DRW_DBG(parsedPosition.y)DRW_dbg::dbg(parsedPosition.y); DRW_DBG(" rot: ")DRW_dbg::dbg(" rot: "); DRW_DBG(parsedRotation)DRW_dbg::dbg(parsedRotation);
10754 DRW_DBG(" flags: ")DRW_dbg::dbg(" flags: "); DRW_DBGH(parsedFlags)DRW_dbg::dbgH(parsedFlags); DRW_DBG("\n")DRW_dbg::dbg("\n");
10755 if (!readTableBodyCount(version, buf, objSize, kMaxClipVertices, 128,
10756 nClip)
10757 || !proxyEntityHasBits(*buf, bodyEndBit,
10758 static_cast<std::uint64_t>(nClip) * 128u))
10759 return fail();
10760 std::vector<DRW_Coord> parsedClipBoundary;
10761 if (!DRW::reserve(parsedClipBoundary, static_cast<std::size_t>(nClip)))
10762 return fail();
10763 for (std::uint32_t i = 0; i < nClip; ++i) {
10764 DRW_Coord p;
10765 if (!readBoundedRawDouble(*buf, bodyEndBit, p.x)
10766 || !readBoundedRawDouble(*buf, bodyEndBit, p.y)
10767 || !std::isfinite(p.x) || !std::isfinite(p.y))
10768 return fail();
10769 p.z = 0.0;
10770 parsedClipBoundary.push_back(p);
10771 }
10772 std::vector<DRW_Coord> parsedInverseClipBoundary;
10773 if (version > DRW::AC1021 && (parsedFlags & 0x10) != 0) {
10774 std::uint16_t nInverse = 0;
10775 if (!readBoundedBitShort(*buf, bodyEndBit, nInverse)
10776 || nInverse > kMaxClipVertices
10777 || !proxyEntityHasBits(
10778 *buf, bodyEndBit,
10779 static_cast<std::uint64_t>(nInverse) * 128u)
10780 || !DRW::reserve(parsedInverseClipBoundary,
10781 static_cast<std::size_t>(nInverse)))
10782 return fail();
10783 for (std::uint16_t i = 0; i < nInverse; ++i) {
10784 DRW_Coord p;
10785 if (!readBoundedRawDouble(*buf, bodyEndBit, p.x)
10786 || !readBoundedRawDouble(*buf, bodyEndBit, p.y)
10787 || !std::isfinite(p.x) || !std::isfinite(p.y))
10788 return fail();
10789 p.z = 0.0;
10790 parsedInverseClipBoundary.push_back(p);
10791 }
10792 }
10793 if (!buf->isGood())
10794 return fail();
10795
10796 std::uint64_t handleEndBit = 0;
10797 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
10798 handleEndBit))
10799 return fail();
10800 dwgBuffer handleProbe = buf->forkIndependent();
10801 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
10802 handleEndBit))
10803 return fail();
10804 // The definition follows the common entity handles in every version: R13
10805 // and R14 keep both behind the data of the same record.
10806 std::uint32_t parsedDefinitionHandle = 0;
10807 if (proxyEntityHasBits(handleProbe, handleEndBit, 16)) {
10808 dwgHandle defH;
10809 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
10810 defH))
10811 return fail();
10812 if (!handleProbe.isGood())
10813 return fail();
10814 parsedDefinitionHandle = defH.ref;
10815 DRW_DBG(" UNDERLAY definitionHandle: ")DRW_dbg::dbg(" UNDERLAY definitionHandle: ");
10816 DRW_DBGHL(defH.code, defH.size, defH.ref)DRW_dbg::dbgHL(defH.code, defH.size, defH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
10817 }
10818 if (!handleProbe.isGood())
10819 return fail();
10820 *sourceBuf = handleProbe;
10821 extPoint = parsedExtPoint;
10822 position = parsedPosition;
10823 rotation = parsedRotation;
10824 scale = parsedScale;
10825 flags = parsedFlags;
10826 contrast = parsedContrast;
10827 fade = parsedFade;
10828 clipBoundary = std::move(parsedClipBoundary);
10829 inverseClipBoundary = std::move(parsedInverseClipBoundary);
10830 definitionHandle = parsedDefinitionHandle;
10831 return true;
10832}
10833
10834bool DRW_Underlay::encodeDwg(DRW::Version version, dwgBufferW *buf,
10835 std::uint32_t bs, dwgBufferW *strBuf,
10836 dwgBufferW *handleBuf) {
10837 (void)bs; (void)strBuf;
10838 if (!validatePayloadFields()
10839 || clipBoundary.size() > kMaxClipVertices
10840 || inverseClipBoundary.size() > kMaxClipVertices
10841 || (version <= DRW::AC1021 && !inverseClipBoundary.empty()))
10842 return false;
10843
10844 switch (kind) {
10845 case DGN:
10846 oType = kDwgClassNumDgn;
10847 break;
10848 case DWF:
10849 oType = kDwgClassNumDwf;
10850 break;
10851 case PDF:
10852 default:
10853 oType = kDwgClassNumPdf;
10854 break;
10855 }
10856 if (!encodeDwgCommon(version, buf))
10857 return false;
10858
10859 const bool hasInverseClip = !inverseClipBoundary.empty() || (flags & 0x10) != 0;
10860 const std::uint8_t wireFlags =
10861 hasInverseClip ? static_cast<std::uint8_t>(flags | 0x10) : flags;
10862
10863 buf->putExtrusion(extPoint, false);
10864 buf->put3BitDouble(position);
10865 buf->putBitDouble(rotation);
10866 buf->putBitDouble(scale.x);
10867 buf->putBitDouble(scale.y);
10868 buf->putBitDouble(scale.z);
10869 buf->putRawChar8(wireFlags);
10870 buf->putRawChar8(contrast);
10871 buf->putRawChar8(fade);
10872 buf->putBitLong(static_cast<std::int32_t>(clipBoundary.size()));
10873 for (const auto& vertex : clipBoundary) {
10874 buf->putRawDouble(vertex.x);
10875 buf->putRawDouble(vertex.y);
10876 }
10877 if (version > DRW::AC1021 && hasInverseClip) {
10878 buf->putBitShort(static_cast<std::uint16_t>(inverseClipBoundary.size()));
10879 for (const auto& vertex : inverseClipBoundary) {
10880 buf->putRawDouble(vertex.x);
10881 buf->putRawDouble(vertex.y);
10882 }
10883 }
10884
10885 if (!encodeDwgEntHandle(version, buf, handleBuf))
10886 return false;
10887 putNullableHardPointerHandle(handleBuf ? handleBuf : buf, definitionHandle);
10888 return true;
10889}
10890
10891
10892bool DRW_Text::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
10893 switch (code) {
10894 case 40:
10895 height = reader->getDouble();
10896 break;
10897 case 41:
10898 widthscale = reader->getDouble();
10899 break;
10900 case 50:
10901 angle = reader->getDouble();
10902 break;
10903 case 51:
10904 oblique = reader->getDouble();
10905 break;
10906 case 71:
10907 textgen = reader->getInt32();
10908 break;
10909 case 72:
10910 alignH = (HAlign)reader->getInt32();
10911 break;
10912 case 73:
10913 alignV = (VAlign)reader->getInt32();
10914 break;
10915 case 1:
10916 text = reader->getUtf8String();
10917 break;
10918 case 7:
10919 style = reader->getUtf8String();
10920 break;
10921 default:
10922 return DRW_Line::parseCode(code, reader);
10923 }
10924
10925 return true;
10926}
10927
10928void DRW_Text::resetDwgState() {
10929 DRW_Entity::reset();
10930 basePoint = DRW_Coord{0.0, 0.0, 0.0};
10931 secPoint = DRW_Coord{0.0, 0.0, 0.0};
10932 thickness = 0.0;
10933 extPoint = DRW_Coord{0.0, 0.0, 1.0};
10934 xAxisAngle = 0.0;
10935 height = 0.0;
10936 text.clear();
10937 angle = 0.0;
10938 widthscale = 1.0;
10939 oblique = 0.0;
10940 style = "STANDARD";
10941 textgen = 0;
10942 alignH = HLeft;
10943 alignV = VBaseLine;
10944 styleH = dwgHandle{};
10945}
10946
10947bool DRW_Text::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
10948 resetDwgState();
10949 dwgBuffer *sourceBuf = buf;
10950 auto fail = [this, sourceBuf]() {
10951 if (sourceBuf != nullptr)
10952 sourceBuf->invalidate();
10953 resetDwgState();
10954 return false;
10955 };
10956 if (sourceBuf == nullptr)
10957 return fail();
10958
10959 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
10960 buf = &bodyProbe;
10961 dwgBuffer sBuff = buf->forkIndependent();
10962 dwgBuffer *sBuf = buf;
10963 if (version > DRW::AC1018) {//2007+
10964 sBuf = &sBuff; //separate buffer for strings
10965 }
10966 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
10967 if (!ret)
10968 return fail();
10969 DRW_DBG("\n***************************** parsing text *********************************************\n")DRW_dbg::dbg("\n***************************** parsing text *********************************************\n"
)
;
10970 const std::uint64_t bodyEndBit = dwgDataEndBit;
10971
10972 // DataFlags RC Used to determine presence of subsequent data, set to 0xFF for R14-
10973 std::uint8_t data_flags = 0x00;
10974 if (version > DRW::AC1014) {//2000+
10975 if (!readBoundedRawChar8(*buf, bodyEndBit, data_flags))
10976 return fail(); /* DataFlags RC Used to determine presence of subsequent data */
10977 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");
10978 if ( !(data_flags & 0x01) ) { /* Elevation RD --- present if !(DataFlags & 0x01) */
10979 if (!readBoundedRawDouble(*buf, bodyEndBit, basePoint.z))
10980 return fail();
10981 }
10982 } else {//14-
10983 if (!readBoundedBitDouble(*buf, bodyEndBit, basePoint.z))
10984 return fail(); /* Elevation BD --- */
10985 }
10986 if (!readBoundedRawDouble(*buf, bodyEndBit, basePoint.x)
10987 || !readBoundedRawDouble(*buf, bodyEndBit, basePoint.y))
10988 return fail(); /* Insertion pt 2RD 10 */
10989 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");
10990 if (version > DRW::AC1014) {//2000+
10991 if ( !(data_flags & 0x02) ) { /* Alignment pt 2DD 11 present if !(DataFlags & 0x02), use 10 & 20 values for 2 default values.*/
10992 if (!readBoundedDefaultDouble(*buf, bodyEndBit, basePoint.x,
10993 secPoint.x)
10994 || !readBoundedDefaultDouble(*buf, bodyEndBit, basePoint.y,
10995 secPoint.y))
10996 return fail();
10997 } else {
10998 secPoint = basePoint;
10999 }
11000 } else {//14-
11001 if (!readBoundedRawDouble(*buf, bodyEndBit, secPoint.x)
11002 || !readBoundedRawDouble(*buf, bodyEndBit, secPoint.y))
11003 return fail(); /* Alignment pt 2RD 11 */
11004 }
11005 secPoint.z = basePoint.z;
11006 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");
11007 if (!readBoundedExtrusion(*buf, bodyEndBit, version > DRW::AC1014,
11008 extPoint))
11009 return fail();
11010 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");
11011 if (!readBoundedThickness(*buf, bodyEndBit, version > DRW::AC1014,
11012 thickness))
11013 return fail(); /* Thickness BD 39 */
11014
11015 if (version > DRW::AC1014) {//2000+
11016 if ( !(data_flags & 0x04) ) { /* Oblique ang RD 51 present if !(DataFlags & 0x04) */
11017 if (!readBoundedRawDouble(*buf, bodyEndBit, oblique))
11018 return fail();
11019 }
11020 if ( !(data_flags & 0x08) ) { /* Rotation ang RD 50 present if !(DataFlags & 0x08) */
11021 if (!readBoundedRawDouble(*buf, bodyEndBit, angle))
11022 return fail();
11023 }
11024 if (!readBoundedRawDouble(*buf, bodyEndBit, height))
11025 return fail(); /* Height RD 40 */
11026 if ( !(data_flags & 0x10) ) { /* Width factor RD 41 present if !(DataFlags & 0x10) */
11027 if (!readBoundedRawDouble(*buf, bodyEndBit, widthscale))
11028 return fail();
11029 }
11030 } else {//14-
11031 if (!readBoundedBitDouble(*buf, bodyEndBit, oblique)
11032 || !readBoundedBitDouble(*buf, bodyEndBit, angle)
11033 || !readBoundedBitDouble(*buf, bodyEndBit, height)
11034 || !readBoundedBitDouble(*buf, bodyEndBit, widthscale))
11035 return fail(); /* Oblique/rotation/height/width BD */
11036 }
11037 angle *= ARAD57.29577951308232;
11038 if (!std::isfinite(basePoint.x) || !std::isfinite(basePoint.y)
11039 || !std::isfinite(basePoint.z) || !std::isfinite(secPoint.x)
11040 || !std::isfinite(secPoint.y) || !std::isfinite(secPoint.z)
11041 || !std::isfinite(extPoint.x) || !std::isfinite(extPoint.y)
11042 || !std::isfinite(extPoint.z) || !std::isfinite(thickness)
11043 || !std::isfinite(oblique) || !std::isfinite(angle)
11044 || !std::isfinite(height) || !std::isfinite(widthscale))
11045 return fail();
11046 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: ");
11047 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");
11048 std::uint64_t stringEndBit = bodyEndBit;
11049 if (version > DRW::AC1021) {
11050 std::uint64_t ignoredBodyEndBit = 0;
11051 if (!entityBodyDataEndBit(*sBuf, version, objSize,
11052 ignoredBodyEndBit, &stringEndBit))
11053 return fail();
11054 }
11055 UTF8STRINGstd::string parsedText;
11056 if (version > DRW::AC1018
11057 && currentDwgBit(sBuf) >= stringEndBit) {
11058 parsedText.clear();
11059 } else if (!readBoundedVariableText(*sBuf, stringEndBit, version,
11060 parsedText)) {
11061 return fail();
11062 }
11063 DRW_DBG("text string: ")DRW_dbg::dbg("text string: "); DRW_DBG(parsedText.c_str())DRW_dbg::dbg(parsedText.c_str());DRW_DBG("\n")DRW_dbg::dbg("\n");
11064 //textgen, alignH, alignV always present in R14-, data_flags set in initialisation
11065 if ( !(data_flags & 0x20) ) { /* Generation BS 71 present if !(DataFlags & 0x20) */
11066 std::uint16_t parsedTextgen = 0;
11067 if (!readBoundedBitShort(*buf, bodyEndBit, parsedTextgen))
11068 return fail();
11069 textgen = parsedTextgen;
11070 DRW_DBG("textgen: ")DRW_dbg::dbg("textgen: "); DRW_DBG(textgen)DRW_dbg::dbg(textgen);
11071 }
11072 if ( !(data_flags & 0x40) ) { /* Horiz align. BS 72 present if !(DataFlags & 0x40) */
11073 std::uint16_t parsedAlignH = 0;
11074 if (!readBoundedBitShort(*buf, bodyEndBit, parsedAlignH))
11075 return fail();
11076 alignH = static_cast<HAlign>(parsedAlignH);
11077 DRW_DBG(", alignH: ")DRW_dbg::dbg(", alignH: "); DRW_DBG(alignH)DRW_dbg::dbg(alignH);
11078 }
11079 if ( !(data_flags & 0x80) ) { /* Vert align. BS 73 present if !(DataFlags & 0x80) */
11080 std::uint16_t parsedAlignV = 0;
11081 if (!readBoundedBitShort(*buf, bodyEndBit, parsedAlignV))
11082 return fail();
11083 alignV = static_cast<VAlign>(parsedAlignV);
11084 DRW_DBG(", alignV: ")DRW_dbg::dbg(", alignV: "); DRW_DBG(alignV)DRW_dbg::dbg(alignV);
11085 }
11086 DRW_DBG("\n")DRW_dbg::dbg("\n");
11087
11088 if (!buf->isGood() || !sBuf->isGood()
11089 || currentDwgBit(buf) > bodyEndBit
11090 || currentDwgBit(sBuf) > stringEndBit)
11091 return fail();
11092
11093 /* Common Entity Handle Data */
11094 std::uint64_t handleEndBit = 0;
11095 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
11096 handleEndBit))
11097 return fail();
11098 dwgBuffer handleProbe = buf->forkIndependent();
11099 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
11100 handleEndBit);
11101 if (!ret)
11102 return fail();
11103
11104 dwgHandle parsedStyleH;
11105 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
11106 parsedStyleH)
11107 || !handleProbe.isGood() || !sBuf->isGood())
11108 return fail();
11109
11110 *sourceBuf = handleProbe;
11111 text = std::move(parsedText);
11112 styleH = parsedStyleH;
11113 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");
11114 /* CRC X --- */
11115 return true;
11116}
11117
11118// ---------------------------------------------------------------------------
11119// RTEXT (RText, Express Tools) — read-only, mapped onto DRW_Text.
11120// ---------------------------------------------------------------------------
11121bool DRW_RText::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
11122 // RTEXT's DXF layout is a TEXT subset (1 text, 7 style, 10/20/30 insertion,
11123 // 40 height, 50 rotation deg, 210/220/230 extrusion) plus a flags long (70)
11124 // that plain TEXT does not carry.
11125 if (70 == code) {
11126 m_rTextFlags = reader->getInt32();
11127 return true;
11128 }
11129 return DRW_Text::parseCode(code, reader);
11130}
11131
11132bool DRW_RText::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
11133 resetDwgState();
11134 m_rTextFlags = 0;
11135 dwgBuffer *sourceBuf = buf;
11136 auto fail = [this, sourceBuf]() {
11137 if (sourceBuf != nullptr)
11138 sourceBuf->invalidate();
11139 resetDwgState();
11140 m_rTextFlags = 0;
11141 return false;
11142 };
11143 if (sourceBuf == nullptr)
11144 return fail();
11145
11146 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
11147 buf = &bodyProbe;
11148 dwgBuffer sBuff = buf->forkIndependent();
11149 dwgBuffer *sBuf = buf;
11150 if (version > DRW::AC1018) // 2007+ strings live in a separate stream
11151 sBuf = &sBuff;
11152 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
11153 if (!ret)
11154 return fail();
11155 DRW_DBG("\n***************************** parsing rtext ********************************************\n")DRW_dbg::dbg("\n***************************** parsing rtext ********************************************\n"
)
;
11156
11157 const std::uint64_t bodyEndBit = dwgDataEndBit;
11158 DRW_Coord parsedBasePoint;
11159 DRW_Coord parsedExtrusion;
11160 double parsedAngle = 0.0;
11161 double parsedHeight = 0.0;
11162 std::uint16_t parsedFlags = 0;
11163 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedBasePoint)
11164 || !readBoundedBitCoord(*buf, bodyEndBit, parsedExtrusion)
11165 || !readBoundedBitDouble(*buf, bodyEndBit, parsedAngle)
11166 || !readBoundedBitDouble(*buf, bodyEndBit, parsedHeight)
11167 || !readBoundedBitShort(*buf, bodyEndBit, parsedFlags)
11168 || !std::isfinite(parsedBasePoint.x)
11169 || !std::isfinite(parsedBasePoint.y)
11170 || !std::isfinite(parsedBasePoint.z)
11171 || !std::isfinite(parsedExtrusion.x)
11172 || !std::isfinite(parsedExtrusion.y)
11173 || !std::isfinite(parsedExtrusion.z)
11174 || !std::isfinite(parsedAngle)
11175 || !std::isfinite(parsedHeight))
11176 return fail();
11177 basePoint = parsedBasePoint; // insertion 3BD
11178 secPoint = parsedBasePoint; // no separate alignment point
11179 extPoint = parsedExtrusion; // extrusion 3BD
11180 angle = parsedAngle * ARAD57.29577951308232; // rotation BD (radians) -> degrees
11181 height = parsedHeight; // height BD
11182 m_rTextFlags = parsedFlags; // flags BS
11183 if (!std::isfinite(angle))
11184 return fail();
11185 std::uint64_t stringEndBit = bodyEndBit;
11186 if (version > DRW::AC1021) {
11187 std::uint64_t ignoredBodyEndBit = 0;
11188 if (!entityBodyDataEndBit(*sBuf, version, objSize,
11189 ignoredBodyEndBit, &stringEndBit))
11190 return fail();
11191 }
11192 UTF8STRINGstd::string parsedText;
11193 if (version > DRW::AC1018
11194 && currentDwgBit(sBuf) >= stringEndBit) {
11195 parsedText.clear();
11196 } else if (!readBoundedVariableText(*sBuf, stringEndBit, version,
11197 parsedText)) {
11198 return fail();
11199 }
11200 DRW_DBG("rtext string: ")DRW_dbg::dbg("rtext string: "); DRW_DBG(parsedText.c_str())DRW_dbg::dbg(parsedText.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
11201
11202 if (!buf->isGood() || !sBuf->isGood()
11203 || currentDwgBit(buf) > bodyEndBit
11204 || currentDwgBit(sBuf) > stringEndBit)
11205 return fail();
11206
11207 std::uint64_t handleEndBit = 0;
11208 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
11209 handleEndBit))
11210 return fail();
11211 dwgBuffer handleProbe = buf->forkIndependent();
11212 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
11213 handleEndBit);
11214 if (!ret)
11215 return fail();
11216 dwgHandle parsedStyleH;
11217 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
11218 parsedStyleH)
11219 || !handleProbe.isGood() || !sBuf->isGood())
11220 return fail();
11221
11222 *sourceBuf = handleProbe;
11223 text = std::move(parsedText);
11224 styleH = parsedStyleH;
11225 return true;
11226}
11227
11228// ---------------------------------------------------------------------------
11229// ARCALIGNEDTEXT (AcDbArcAlignedText, Express Tools) — read-only, mapped onto
11230// DRW_Text as a 2D approximation (text at the arc mid-point, tangent baseline).
11231// ---------------------------------------------------------------------------
11232bool DRW_RText::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
11233 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
11234 (void)bs;
11235 oType = kDwgClassNum;
11236 if (!encodeDwgCommon(version, buf)) return false;
11237
11238 buf->put3BitDouble(basePoint);
11239 buf->put3BitDouble(extPoint);
11240 buf->putBitDouble(angle / ARAD57.29577951308232);
11241 buf->putBitDouble(height);
11242 buf->putBitShort(bitShortFromInt(m_rTextFlags));
11243 (strBuf ? strBuf : buf)->putVariableText(version, text);
11244
11245 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
11246 putHardPointerHandle(handleBuf ? handleBuf : buf,
11247 (styleH.ref == 0) ? 0x13 : styleH.ref);
11248 return true;
11249}
11250
11251void DRW_ArcAlignedText::applyArcApproximation(){
11252 const double mid = 0.5 * (m_startAngle + m_endAngle);
11253 basePoint.x = m_center.x + m_radius * std::cos(mid);
11254 basePoint.y = m_center.y + m_radius * std::sin(mid);
11255 basePoint.z = m_center.z;
11256 secPoint = basePoint;
11257 // Baseline tangent to the arc at the mid-point; angle stored in degrees to
11258 // match DRW_Text (which the DWG path fills via `angle *= ARAD`).
11259 angle = (mid + M_PI_21.57079632679489661923) * ARAD57.29577951308232;
11260 // Height from the text-size D2T string when parseable, else a fraction of
11261 // the radius so the approximation is at least visible.
11262 double h = 0.0;
11263 try { h = std::stod(m_textSize); } catch (...) { h = 0.0; }
11264 if (h > 0.0)
11265 height = h;
11266 else if (height <= 0.0)
11267 height = 0.1 * m_radius;
11268}
11269
11270// Format a D2T (double-to-text) field the way the ARCALIGNEDTEXT model stores
11271// it: the DWG body carries these as text ("2.5", "1", "0"), while the DXF path
11272// reads them as doubles (group codes 41-46 fall in the double range, so the
11273// reader populates doubleData and leaves strData stale — getString() is unsafe
11274// here). %g reproduces the same compact textual form.
11275static std::string arcAlignedD2T(double v){
11276 char buf[32];
11277 std::snprintf(buf, sizeof(buf), "%g", v);
11278 return std::string(buf);
11279}
11280
11281static std::string arcAlignedStringOrDefault(const UTF8STRINGstd::string& value,
11282 const std::string& fallback) {
11283 return value.empty() ? fallback : value;
11284}
11285
11286bool DRW_ArcAlignedText::encodeDwg(DRW::Version version, dwgBufferW *buf,
11287 std::uint32_t bs, dwgBufferW *strBuf,
11288 dwgBufferW *handleBuf) {
11289 (void)bs;
11290 oType = kDwgClassNum;
11291 if (!encodeDwgCommon(version, buf)) return false;
11292
11293 dwgBufferW *sb = strBuf ? strBuf : buf;
11294 sb->putVariableText(version, arcAlignedStringOrDefault(
11295 m_textSize, arcAlignedD2T(height > 0.0 ? height : 0.0)));
11296 sb->putVariableText(version, arcAlignedStringOrDefault(
11297 m_xScale, arcAlignedD2T(widthscale > 0.0 ? widthscale : 1.0)));
11298 sb->putVariableText(version, arcAlignedStringOrDefault(m_charSpacing, "1"));
11299 sb->putVariableText(version, style.empty() ? "Standard" : style);
11300 sb->putVariableText(version, m_fontName);
11301 sb->putVariableText(version, m_bigFontName);
11302 sb->putVariableText(version, text);
11303 sb->putVariableText(version, arcAlignedStringOrDefault(m_offsetFromArc, "0"));
11304 sb->putVariableText(version, arcAlignedStringOrDefault(m_rightOffset, "0"));
11305 sb->putVariableText(version, arcAlignedStringOrDefault(m_leftOffset, "0"));
11306
11307 buf->put3BitDouble(m_center);
11308 buf->putBitDouble(m_radius);
11309 buf->putBitDouble(m_startAngle);
11310 buf->putBitDouble(m_endAngle);
11311 buf->put3BitDouble(extPoint);
11312 buf->putBitLong(static_cast<std::int32_t>(m_rawColor));
11313 buf->putBitShort(bitShortFromInt(m_characterSet));
11314 buf->putBitShort(bitShortFromInt(m_pitchAndFamily));
11315 buf->putBitShort(bitShortFromInt(m_isShx));
11316 buf->putBitShort(bitShortFromInt(m_isBold));
11317 buf->putBitShort(bitShortFromInt(m_isItalic));
11318 buf->putBitShort(bitShortFromInt(m_isUnderlined));
11319 buf->putBitShort(bitShortFromInt(m_alignment));
11320 buf->putBitShort(bitShortFromInt(m_isReverse));
11321 buf->putBitShort(bitShortFromInt(m_wizardFlag));
11322 buf->putBitShort(bitShortFromInt(m_textPosition));
11323 buf->putBitShort(bitShortFromInt(m_textDirection));
11324
11325 if (version <= DRW::AC1018)
11326 putNullableHardPointerHandle(buf, m_arcHandle);
11327 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
11328 if (version > DRW::AC1018)
11329 putNullableHardPointerHandle(handleBuf ? handleBuf : buf, m_arcHandle);
11330 return true;
11331}
11332
11333bool DRW_ArcAlignedText::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
11334 // ARCALIGNEDTEXT repurposes several TEXT codes (2/10/40/41/50/51/70…), so it
11335 // must not delegate those to DRW_Text; unknown codes fall through to the
11336 // AcDbEntity common parser. Angles (50/51) are DXF degrees -> radians.
11337 switch (code) {
11338 case 1: text = reader->getUtf8String(); break;
11339 case 2: m_fontName = reader->getUtf8String(); break;
11340 case 3: m_bigFontName = reader->getUtf8String(); break;
11341 case 7: style = reader->getUtf8String(); break;
11342 case 10: m_center.x = reader->getDouble(); break;
11343 case 20: m_center.y = reader->getDouble(); break;
11344 case 30: m_center.z = reader->getDouble(); break;
11345 case 40: m_radius = reader->getDouble(); break;
11346 case 41: m_xScale = arcAlignedD2T(reader->getDouble()); break;
11347 case 42: m_textSize = arcAlignedD2T(reader->getDouble()); break;
11348 case 43: m_charSpacing = arcAlignedD2T(reader->getDouble()); break;
11349 case 44: m_offsetFromArc = arcAlignedD2T(reader->getDouble()); break;
11350 case 45: m_rightOffset = arcAlignedD2T(reader->getDouble()); break;
11351 case 46: m_leftOffset = arcAlignedD2T(reader->getDouble()); break;
11352 case 50: m_startAngle = reader->getDouble() / ARAD57.29577951308232; break;
11353 case 51: m_endAngle = reader->getDouble() / ARAD57.29577951308232; break;
11354 case 70: m_isReverse = reader->getInt32(); break;
11355 case 71: m_textDirection = reader->getInt32(); break;
11356 case 72: m_alignment = reader->getInt32(); break;
11357 case 73: m_textPosition = reader->getInt32(); break;
11358 case 74: m_isBold = reader->getInt32(); break;
11359 case 75: m_isItalic = reader->getInt32(); break;
11360 case 76: m_isUnderlined = reader->getInt32(); break;
11361 case 77: m_characterSet = reader->getInt32(); break;
11362 case 78: m_pitchAndFamily = reader->getInt32(); break;
11363 case 79: m_isShx = reader->getInt32(); break;
11364 case 90: m_rawColor = reader->getInt32(); break;
11365 case 210: extPoint.x = reader->getDouble(); break;
11366 case 220: extPoint.y = reader->getDouble(); break;
11367 case 230: extPoint.z = reader->getDouble(); break;
11368 case 280: m_wizardFlag = reader->getInt32(); break;
11369 default: return DRW_Entity::parseCode(code, reader);
11370 }
11371 return true;
11372}
11373
11374void DRW_ArcAlignedText::resetDwgState() {
11375 DRW_Text::resetDwgState();
11376 m_center = DRW_Coord{0.0, 0.0, 0.0};
11377 m_radius = 0.0;
11378 m_startAngle = 0.0;
11379 m_endAngle = 0.0;
11380 m_textSize.clear();
11381 m_xScale.clear();
11382 m_charSpacing.clear();
11383 m_offsetFromArc.clear();
11384 m_rightOffset.clear();
11385 m_leftOffset.clear();
11386 m_fontName.clear();
11387 m_bigFontName.clear();
11388 m_rawColor = 0;
11389 m_characterSet = 0;
11390 m_pitchAndFamily = 0;
11391 m_isShx = 0;
11392 m_isBold = 0;
11393 m_isItalic = 0;
11394 m_isUnderlined = 0;
11395 m_alignment = 0;
11396 m_isReverse = 0;
11397 m_wizardFlag = 0;
11398 m_textPosition = 0;
11399 m_textDirection = 0;
11400 m_arcHandle = 0;
11401}
11402
11403bool DRW_ArcAlignedText::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
11404 // The arc reference is an optional trailer whose position depends on the
11405 // DWG version. Never carry it across a reused or truncated record.
11406 resetDwgState();
11407 dwgBuffer *sourceBuf = buf;
11408 auto fail = [this, sourceBuf]() {
11409 if (sourceBuf != nullptr)
11410 sourceBuf->invalidate();
11411 resetDwgState();
11412 return false;
11413 };
11414 if (sourceBuf == nullptr)
11415 return fail();
11416
11417 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
11418 buf = &bodyProbe;
11419
11420 dwgBuffer sBuff = buf->forkIndependent();
11421 dwgBuffer *sBuf = buf;
11422 if (version > DRW::AC1018) // 2007+ strings live in a separate stream
11423 sBuf = &sBuff;
11424 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
11425 if (!ret)
11426 return fail();
11427 DRW_DBG("\n***************************** parsing arcalignedtext **********************************\n")DRW_dbg::dbg("\n***************************** parsing arcalignedtext **********************************\n"
)
;
11428
11429 const std::uint64_t bodyEndBit = dwgDataEndBit;
11430 std::uint64_t stringEndBit = bodyEndBit;
11431 if (version > DRW::AC1021) {
11432 std::uint64_t ignoredBodyEndBit = 0;
11433 if (!entityBodyDataEndBit(*sBuf, version, objSize,
11434 ignoredBodyEndBit, &stringEndBit))
11435 return fail();
11436 }
11437
11438 UTF8STRINGstd::string parsedTextSize;
11439 UTF8STRINGstd::string parsedXScale;
11440 UTF8STRINGstd::string parsedCharSpacing;
11441 UTF8STRINGstd::string parsedStyle;
11442 UTF8STRINGstd::string parsedFontName;
11443 UTF8STRINGstd::string parsedBigFontName;
11444 UTF8STRINGstd::string parsedText;
11445 UTF8STRINGstd::string parsedOffsetFromArc;
11446 UTF8STRINGstd::string parsedRightOffset;
11447 UTF8STRINGstd::string parsedLeftOffset;
11448 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
11449 parsedTextSize)
11450 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11451 parsedXScale)
11452 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11453 parsedCharSpacing)
11454 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11455 parsedStyle)
11456 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11457 parsedFontName)
11458 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11459 parsedBigFontName)
11460 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11461 parsedText)
11462 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11463 parsedOffsetFromArc)
11464 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11465 parsedRightOffset)
11466 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11467 parsedLeftOffset))
11468 return fail();
11469
11470 DRW_Coord parsedCenter;
11471 double parsedRadius = 0.0;
11472 double parsedStartAngle = 0.0;
11473 double parsedEndAngle = 0.0;
11474 DRW_Coord parsedExtPoint;
11475 std::int32_t parsedRawColor = 0;
11476 std::uint16_t parsedCharacterSet = 0;
11477 std::uint16_t parsedPitchAndFamily = 0;
11478 std::uint16_t parsedIsShx = 0;
11479 std::uint16_t parsedIsBold = 0;
11480 std::uint16_t parsedIsItalic = 0;
11481 std::uint16_t parsedIsUnderlined = 0;
11482 std::uint16_t parsedAlignment = 0;
11483 std::uint16_t parsedIsReverse = 0;
11484 std::uint16_t parsedWizardFlag = 0;
11485 std::uint16_t parsedTextPosition = 0;
11486 std::uint16_t parsedTextDirection = 0;
11487 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedCenter)
11488 || !readBoundedBitDouble(*buf, bodyEndBit, parsedRadius)
11489 || !readBoundedBitDouble(*buf, bodyEndBit, parsedStartAngle)
11490 || !readBoundedBitDouble(*buf, bodyEndBit, parsedEndAngle)
11491 || !readBoundedBitCoord(*buf, bodyEndBit, parsedExtPoint)
11492 || !readBoundedBitLong(*buf, bodyEndBit, parsedRawColor)
11493 || !readBoundedBitShort(*buf, bodyEndBit, parsedCharacterSet)
11494 || !readBoundedBitShort(*buf, bodyEndBit, parsedPitchAndFamily)
11495 || !readBoundedBitShort(*buf, bodyEndBit, parsedIsShx)
11496 || !readBoundedBitShort(*buf, bodyEndBit, parsedIsBold)
11497 || !readBoundedBitShort(*buf, bodyEndBit, parsedIsItalic)
11498 || !readBoundedBitShort(*buf, bodyEndBit, parsedIsUnderlined)
11499 || !readBoundedBitShort(*buf, bodyEndBit, parsedAlignment)
11500 || !readBoundedBitShort(*buf, bodyEndBit, parsedIsReverse)
11501 || !readBoundedBitShort(*buf, bodyEndBit, parsedWizardFlag)
11502 || !readBoundedBitShort(*buf, bodyEndBit, parsedTextPosition)
11503 || !readBoundedBitShort(*buf, bodyEndBit, parsedTextDirection))
11504 return fail();
11505 DRW_DBG("arcalignedtext string: ")DRW_dbg::dbg("arcalignedtext string: "); DRW_DBG(parsedText.c_str())DRW_dbg::dbg(parsedText.c_str());
11506 DRW_DBG("\n")DRW_dbg::dbg("\n");
11507
11508 if (!buf->isGood() || !sBuf->isGood()
11509 || currentDwgBit(buf) > bodyEndBit
11510 || currentDwgBit(sBuf) > stringEndBit
11511 || !std::isfinite(parsedCenter.x) || !std::isfinite(parsedCenter.y)
11512 || !std::isfinite(parsedCenter.z) || !std::isfinite(parsedRadius)
11513 || parsedRadius < 0.0 || !std::isfinite(parsedStartAngle)
11514 || !std::isfinite(parsedEndAngle) || !std::isfinite(parsedExtPoint.x)
11515 || !std::isfinite(parsedExtPoint.y)
11516 || !std::isfinite(parsedExtPoint.z))
11517 return fail();
11518
11519 std::uint64_t handleEndBit = 0;
11520 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
11521 handleEndBit))
11522 return fail();
11523 // R2004- keeps the arc handle before the common handle stream; R2007+ after.
11524 dwgBuffer handleProbe = buf->forkIndependent();
11525 std::uint32_t parsedArcHandle = 0;
11526 if (version <= DRW::AC1018
11527 && proxyEntityHasBits(handleProbe, handleEndBit, 8)) {
11528 dwgHandle arcHandle;
11529 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
11530 arcHandle))
11531 return fail();
11532 parsedArcHandle = arcHandle.ref;
11533 }
11534
11535 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
11536 handleEndBit);
11537 if (!ret)
11538 return fail();
11539 if (version > DRW::AC1018
11540 && proxyEntityHasBits(handleProbe, handleEndBit, 8)) {
11541 dwgHandle arcHandle;
11542 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
11543 arcHandle))
11544 return fail();
11545 parsedArcHandle = arcHandle.ref;
11546 }
11547 if (!handleProbe.isGood())
11548 return fail();
11549
11550 *sourceBuf = handleProbe;
11551 m_textSize = std::move(parsedTextSize);
11552 m_xScale = std::move(parsedXScale);
11553 m_charSpacing = std::move(parsedCharSpacing);
11554 style = std::move(parsedStyle);
11555 m_fontName = std::move(parsedFontName);
11556 m_bigFontName = std::move(parsedBigFontName);
11557 text = std::move(parsedText);
11558 m_offsetFromArc = std::move(parsedOffsetFromArc);
11559 m_rightOffset = std::move(parsedRightOffset);
11560 m_leftOffset = std::move(parsedLeftOffset);
11561 m_center = parsedCenter;
11562 m_radius = parsedRadius;
11563 m_startAngle = parsedStartAngle;
11564 m_endAngle = parsedEndAngle;
11565 extPoint = parsedExtPoint;
11566 m_rawColor = parsedRawColor;
11567 m_characterSet = parsedCharacterSet;
11568 m_pitchAndFamily = parsedPitchAndFamily;
11569 m_isShx = parsedIsShx;
11570 m_isBold = parsedIsBold;
11571 m_isItalic = parsedIsItalic;
11572 m_isUnderlined = parsedIsUnderlined;
11573 m_alignment = parsedAlignment;
11574 m_isReverse = parsedIsReverse;
11575 m_wizardFlag = parsedWizardFlag;
11576 m_textPosition = parsedTextPosition;
11577 m_textDirection = parsedTextDirection;
11578 m_arcHandle = parsedArcHandle;
11579 applyArcApproximation();
11580 return true;
11581}
11582
11583// Out-of-line special members: required because mtext is a unique_ptr<DRW_MText>
11584// declared with a forward-declared element type in the header.
11585DRW_Attrib::~DRW_Attrib() = default;
11586DRW_Attrib::DRW_Attrib(const DRW_Attrib& o)
11587 : DRW_Text(o), tag(o.tag), attribFlags(o.attribFlags),
11588 m_fieldLength(o.m_fieldLength),
11589 lockPosition(o.lockPosition), attVersion(o.attVersion),
11590 m_attributeType(o.m_attributeType),
11591 keepDuplicateRecords(o.keepDuplicateRecords),
11592 mtext(o.mtext ? std::make_unique<DRW_MText>(*o.mtext) : nullptr),
11593 dxfInAttributeSubclass(o.dxfInAttributeSubclass),
11594 dxfEmbeddedMarker(o.dxfEmbeddedMarker) {}
11595DRW_Attrib& DRW_Attrib::operator=(const DRW_Attrib& o) {
11596 if (this != &o) {
11597 DRW_Text::operator=(o);
11598 tag = o.tag;
11599 attribFlags = o.attribFlags;
11600 m_fieldLength = o.m_fieldLength;
11601 lockPosition = o.lockPosition;
11602 attVersion = o.attVersion;
11603 m_attributeType = o.m_attributeType;
11604 keepDuplicateRecords = o.keepDuplicateRecords;
11605 mtext = o.mtext ? std::make_unique<DRW_MText>(*o.mtext) : nullptr;
11606 dxfInAttributeSubclass = o.dxfInAttributeSubclass;
11607 dxfEmbeddedMarker = o.dxfEmbeddedMarker;
11608 }
11609 return *this;
11610}
11611DRW_Attrib::DRW_Attrib(DRW_Attrib&&) noexcept = default;
11612DRW_Attrib& DRW_Attrib::operator=(DRW_Attrib&&) noexcept = default;
11613
11614bool DRW_Attrib::hasAmbiguousLegacyEmbeddedXData() const {
11615 return dxfEmbeddedMarker == EmbeddedDxfMarker::Legacy100
11616 && mtext != nullptr && !mtext->extData.empty();
11617}
11618
11619DRW_GeoPositionMarker::~DRW_GeoPositionMarker() = default;
11620DRW_GeoPositionMarker::DRW_GeoPositionMarker(
11621 const DRW_GeoPositionMarker& o)
11622 : DRW_Entity(o), m_classVersion(o.m_classVersion),
11623 m_position(o.m_position), m_radius(o.m_radius), m_notes(o.m_notes),
11624 m_landingGap(o.m_landingGap), m_mtextVisible(o.m_mtextVisible),
11625 m_textAlignment(o.m_textAlignment),
11626 m_enableFrameText(o.m_enableFrameText),
11627 mtext(o.mtext ? std::make_unique<DRW_MText>(*o.mtext) : nullptr),
11628 m_dxfDouble40Count(o.m_dxfDouble40Count),
11629 m_dxfBool290Count(o.m_dxfBool290Count) {}
11630DRW_GeoPositionMarker& DRW_GeoPositionMarker::operator=(
11631 const DRW_GeoPositionMarker& o) {
11632 if (this != &o) {
11633 DRW_Entity::operator=(o);
11634 m_classVersion = o.m_classVersion;
11635 m_position = o.m_position;
11636 m_radius = o.m_radius;
11637 m_notes = o.m_notes;
11638 m_landingGap = o.m_landingGap;
11639 m_mtextVisible = o.m_mtextVisible;
11640 m_textAlignment = o.m_textAlignment;
11641 m_enableFrameText = o.m_enableFrameText;
11642 mtext = o.mtext ? std::make_unique<DRW_MText>(*o.mtext) : nullptr;
11643 m_dxfDouble40Count = o.m_dxfDouble40Count;
11644 m_dxfBool290Count = o.m_dxfBool290Count;
11645 }
11646 return *this;
11647}
11648DRW_GeoPositionMarker::DRW_GeoPositionMarker(
11649 DRW_GeoPositionMarker&&) noexcept = default;
11650DRW_GeoPositionMarker& DRW_GeoPositionMarker::operator=(
11651 DRW_GeoPositionMarker&&) noexcept = default;
11652
11653namespace {
11654static bool parseEmbeddedMTextEntityMode(DRW::Version version, dwgBuffer *buf,
11655 std::uint64_t bodyEndBit,
11656 EmbeddedMTextHandleInfo& info,
11657 DRW_MText& mtext) {
11658 std::uint8_t entmode = 0;
11659 if (!readBounded2Bits(*buf, bodyEndBit, entmode))
11660 return false;
11661 info.m_ownerHandle = entmode == 0;
11662 if (!readBoundedBitLong(*buf, bodyEndBit, info.m_numReactors)
11663 || !dwgSafety::validReactorCount(info.m_numReactors))
11664 return false;
11665 if (version > DRW::AC1015) {
11666 bool xDictFlag = false;
11667 if (!readBoundedBit(*buf, bodyEndBit, xDictFlag))
11668 return false;
11669 info.m_xDictFlag = xDictFlag ? 1 : 0;
11670 }
11671 if (version > DRW::AC1024) {
11672 bool hasDataStorage = false;
11673 if (!readBoundedBit(*buf, bodyEndBit, hasDataStorage))
11674 return false;
11675 mtext.setHasDataStorageBinaryData(hasDataStorage);
11676 } else if (version < DRW::AC1018) {
11677 bool noLinks = false;
11678 if (!readBoundedBit(*buf, bodyEndBit, noLinks))
11679 return false; // nolinks / have-next-links
11680 }
11681 std::uint32_t color = 0;
11682 if (!readBoundedEnColor(*buf, bodyEndBit, version, color))
11683 return false;
11684 mtext.color = static_cast<int>(color);
11685 info.m_hasAcDbColorHandle = buf->lastEnColorHadDbColorRef;
11686 mtext.color24 = buf->lastEnColorRgb;
11687 if (!buf->lastEnColorName.empty()) {
11688 mtext.colorName = buf->lastEnColorBookName.empty()
11689 ? buf->lastEnColorName
11690 : (buf->lastEnColorBookName + "$" + buf->lastEnColorName);
11691 }
11692 if (buf->lastEnColorAlphaRaw != 0)
11693 mtext.transparency = static_cast<int>(buf->lastEnColorAlphaRaw);
11694 if (!readBoundedBitDouble(*buf, bodyEndBit, mtext.ltypeScale))
11695 return false;
11696 if (version > DRW::AC1014) {
11697 std::uint8_t ltFlags = 0;
11698 std::uint8_t plotFlags = 0;
11699 if (!readBounded2Bits(*buf, bodyEndBit, ltFlags)
11700 || !readBounded2Bits(*buf, bodyEndBit, plotFlags))
11701 return false;
11702 info.m_ltFlags = ltFlags;
11703 info.m_plotFlags = plotFlags;
11704 switch (info.m_ltFlags) {
11705 case 0:
11706 mtext.lineType = "BYLAYER";
11707 break;
11708 case 1:
11709 mtext.lineType = "BYBLOCK";
11710 break;
11711 case 2:
11712 mtext.lineType = "CONTINUOUS";
11713 break;
11714 default:
11715 mtext.lineType.clear();
11716 break;
11717 }
11718 }
11719 if (version > DRW::AC1018) {
11720 std::uint8_t materialFlag = 0;
11721 std::uint8_t shadowFlag = 0;
11722 if (!readBounded2Bits(*buf, bodyEndBit, materialFlag)
11723 || !readBoundedRawChar8(*buf, bodyEndBit, shadowFlag))
11724 return false;
11725 info.m_materialFlag = materialFlag;
11726 info.m_shadowFlag = shadowFlag;
11727 mtext.shadow = static_cast<DRW::ShadowMode>(info.m_shadowFlag & 0x3);
11728 }
11729 if (version > DRW::AC1021) {
11730 if (!readBoundedBit(*buf, bodyEndBit, info.m_hasFullVisualStyle)
11731 || !readBoundedBit(*buf, bodyEndBit, info.m_hasFaceVisualStyle)
11732 || !readBoundedBit(*buf, bodyEndBit, info.m_hasEdgeVisualStyle))
11733 return false;
11734 }
11735 std::uint16_t invisibleFlag = 0;
11736 if (!readBoundedBitShort(*buf, bodyEndBit, invisibleFlag))
11737 return false;
11738 mtext.visible = (invisibleFlag & 1) == 0;
11739 if (version > DRW::AC1014) {
11740 std::uint8_t lineWeight = 0;
11741 if (!readBoundedRawChar8(*buf, bodyEndBit, lineWeight))
11742 return false;
11743 mtext.lWeight = DRW_LW_Conv::dwgInt2lineWidth(lineWeight);
11744 }
11745 return buf->isGood() && currentDwgBit(buf) <= bodyEndBit;
11746}
11747
11748static bool parseEmbeddedMTextDwgFields(
11749 DRW::Version version, dwgBuffer *buf, dwgBuffer *sBuf,
11750 DRW_MText& mtext, EmbeddedMTextHandleInfo& info, EmbeddedMTextForm form,
11751 std::uint64_t bodyEndBit, std::uint64_t stringEndBit) {
11752 if (!parseEmbeddedMTextEntityMode(version, buf, bodyEndBit, info, mtext))
11753 return false;
11754
11755 if (!readBoundedBitCoord(*buf, bodyEndBit, mtext.basePoint)
11756 || !readBoundedBitCoord(*buf, bodyEndBit, mtext.extPoint)
11757 || !readBoundedBitCoord(*buf, bodyEndBit, mtext.secPoint))
11758 return false;
11759 mtext.angle = atan2(mtext.secPoint.y, mtext.secPoint.x) * ARAD57.29577951308232;
11760 if (!readBoundedBitDouble(*buf, bodyEndBit, mtext.widthscale))
11761 return false;
11762 // The writer puts these three back where they came from, so keep them:
11763 // an annotative MTEXT has no R2018 block to repeat them in.
11764 if (version > DRW::AC1018
11765 && !readBoundedBitDouble(*buf, bodyEndBit, mtext.m_r2018RectHeight))
11766 return false;
11767 if (!readBoundedBitDouble(*buf, bodyEndBit, mtext.height))
11768 return false;
11769 std::uint16_t textgen = 0;
11770 std::uint16_t alignH = 0;
11771 if (!readBoundedBitShort(*buf, bodyEndBit, textgen)
11772 || !readBoundedBitShort(*buf, bodyEndBit, alignH)
11773 || !readBoundedBitDouble(*buf, bodyEndBit,
11774 mtext.m_r2018ExtentsHeight)
11775 || !readBoundedBitDouble(*buf, bodyEndBit,
11776 mtext.m_r2018ExtentsWidth))
11777 return false;
11778 mtext.textgen = textgen;
11779 mtext.alignH = static_cast<DRW_Text::HAlign>(alignH);
11780 if (!readBoundedVariableText(*sBuf, stringEndBit, version, mtext.text))
11781 return false;
11782
11783 if (version > DRW::AC1014) {
11784 bool ignoredLineSpacingUnknown = false;
11785 if (!readBoundedBitShort(*buf, bodyEndBit,
11786 mtext.linespacingStyle)
11787 || !readBoundedBitDouble(*buf, bodyEndBit, mtext.interlin)
11788 || !readBoundedBit(*buf, bodyEndBit,
11789 ignoredLineSpacingUnknown))
11790 return false;
11791 mtext.alignV = static_cast<DRW_Text::VAlign>(mtext.linespacingStyle);
11792 }
11793 if (version > DRW::AC1015) {
11794 if (!readBoundedBitLong(*buf, bodyEndBit, mtext.m_backgroundFlags))
11795 return false;
11796 if ((mtext.m_backgroundFlags & 0x01)
11797 || (version >= DRW::AC1032 && (mtext.m_backgroundFlags & 0x10))) {
11798 std::uint32_t backgroundColor = 0;
11799 if (!readBoundedBitDouble(*buf, bodyEndBit,
11800 mtext.m_backgroundScale)
11801 || !readBoundedCmColor(*buf, sBuf, bodyEndBit, version,
11802 backgroundColor, nullptr, nullptr,
11803 nullptr, nullptr, stringEndBit)
11804 || !readBoundedBitLong(*buf, bodyEndBit,
11805 mtext.m_backgroundTransparency))
11806 return false;
11807 mtext.m_backgroundColor = static_cast<int>(backgroundColor);
11808 }
11809 }
11810
11811 if (version >= DRW::AC1032) {
11812 mtext.m_r2018ColumnHeights.clear();
11813 if (!readBoundedBit(*buf, bodyEndBit,
11814 mtext.m_r2018IsNotAnnotative))
11815 return false;
11816 info.m_hasR2018AppIdHandle = mtext.m_r2018IsNotAnnotative;
11817 if (form == EmbeddedMTextForm::Short) {
11818 if (!readBoundedBit(*buf, bodyEndBit,
11819 mtext.m_r2018ReallyLocked))
11820 return false;
11821 } else if (mtext.m_r2018IsNotAnnotative) {
11822 // Where the block repeats a field read above, that one stays.
11823 DwgMTextR2018Block block;
11824 if (!readBoundedDwgMTextR2018Block(*buf, bodyEndBit, block))
11825 return false;
11826 block.m_rectHeight = mtext.m_r2018RectHeight;
11827 block.m_extentsWidth = mtext.m_r2018ExtentsWidth;
11828 block.m_extentsHeight = mtext.m_r2018ExtentsHeight;
11829 storeDwgMTextR2018Block(std::move(block), mtext);
11830 }
11831 }
11832
11833 std::uint16_t annotativeSize = 0;
11834 if (!readBoundedBitShort(*buf, bodyEndBit, annotativeSize))
11835 return false;
11836 if (annotativeSize > 0) {
11837 std::vector<std::uint8_t> annotativeData;
11838 if (!DRW::reserve(annotativeData, annotativeSize))
11839 return false;
11840 for (std::uint16_t i = 0; i < annotativeSize; ++i) {
11841 std::uint8_t value = 0;
11842 if (!readBoundedRawChar8(*buf, bodyEndBit, value))
11843 return false;
11844 annotativeData.push_back(value);
11845 }
11846 mtext.m_r2018AnnotativeData = std::move(annotativeData);
11847 info.m_hasAnnotativeAppHandle = true;
11848 if (!readBoundedBitShort(*buf, bodyEndBit,
11849 mtext.m_r2018AnnotativeUnknown))
11850 return false;
11851 }
11852 return buf->isGood() && sBuf->isGood()
11853 && currentDwgBit(buf) <= bodyEndBit
11854 && currentDwgBit(sBuf) <= stringEndBit;
11855}
11856
11857static bool parseEmbeddedMTextDwg(
11858 DRW::Version version, dwgBuffer *buf, dwgBuffer *sBuf,
11859 DRW_MText& mtext, EmbeddedMTextHandleInfo& info, EmbeddedMTextForm form,
11860 std::uint64_t bodyEndBit, std::uint64_t stringEndBit) {
11861 if (buf == nullptr || sBuf == nullptr || !buf->isGood()
11862 || !sBuf->isGood())
11863 return false;
11864
11865 const std::uint64_t bodyStartBit = currentDwgBit(buf);
11866 const std::uint64_t stringStartBit = currentDwgBit(sBuf);
11867 const auto totalBits = [](const dwgBuffer& buffer) {
11868 std::uint64_t bits = 0;
11869 return dwgSafety::multiply(buffer.size(), 8, bits) ? bits : 0;
11870 };
11871 if (bodyEndBit == std::numeric_limits<std::uint64_t>::max())
11872 bodyEndBit = totalBits(*buf);
11873 if (stringEndBit == std::numeric_limits<std::uint64_t>::max())
11874 stringEndBit = totalBits(*sBuf);
11875 if (bodyStartBit > bodyEndBit || stringStartBit > stringEndBit)
11876 return false;
11877
11878 dwgBuffer bodyProbe = buf->forkIndependent();
11879 std::unique_ptr<dwgBuffer> separateStringProbe;
11880 dwgBuffer *stringProbe = &bodyProbe;
11881 if (sBuf != buf) {
11882 separateStringProbe = std::make_unique<dwgBuffer>(
11883 sBuf->forkIndependent());
11884 stringProbe = separateStringProbe.get();
11885 }
11886 if (!parseEmbeddedMTextDwgFields(version, &bodyProbe, stringProbe,
11887 mtext, info, form, bodyEndBit,
11888 stringEndBit))
11889 return false;
11890
11891 const std::uint64_t bodyConsumed = currentDwgBit(&bodyProbe);
11892 const std::uint64_t stringConsumed = currentDwgBit(stringProbe);
11893 if (bodyConsumed < bodyStartBit || stringConsumed < stringStartBit
11894 || bodyConsumed > bodyEndBit || stringConsumed > stringEndBit)
11895 return false;
11896
11897 const auto seekBits = [](dwgBuffer& buffer, std::uint64_t bit) {
11898 if (!buffer.setPosition(bit >> 3))
11899 return false;
11900 buffer.setBitPos(static_cast<std::uint8_t>(bit & 7u));
11901 return buffer.isGood() && currentDwgBit(&buffer) == bit;
11902 };
11903 if (!seekBits(*buf, bodyConsumed))
11904 return false;
11905 if (sBuf != buf && !seekBits(*sBuf, stringConsumed))
11906 return false;
11907 return true;
11908}
11909
11910static bool consumeEmbeddedMTextHandles(
11911 DRW::Version version, dwgBuffer *buf,
11912 const EmbeddedMTextHandleInfo& info, DRW_MText *mtext,
11913 std::uint64_t handleEndBit) {
11914 if (buf == nullptr || !buf->isGood())
11915 return false;
11916
11917 if (handleEndBit == std::numeric_limits<std::uint64_t>::max()) {
11918 if (!dwgSafety::multiply(buf->size(), 8, handleEndBit))
11919 return false;
11920 }
11921 dwgBuffer probe = buf->forkIndependent();
11922 auto readHandle = [&](bool offset, dwgHandle& value) {
11923 return readBoundedDwgHandle(probe, handleEndBit, 0, offset, value);
11924 };
11925
11926 dwgHandle owner;
11927 if (info.m_ownerHandle && !readHandle(false, owner))
11928 return false;
11929
11930 std::vector<std::uint32_t> reactors;
11931 if (info.m_numReactors < 0
11932 || !dwgSafety::validReactorCount(info.m_numReactors))
11933 return false;
11934 if (!DRW::reserve(reactors, info.m_numReactors))
11935 return false;
11936 for (int i = 0; i < info.m_numReactors; ++i) {
11937 dwgHandle reactor;
11938 if (!readHandle(false, reactor))
11939 return false;
11940 if (reactor.ref != 0)
11941 reactors.push_back(reactor.ref);
11942 }
11943
11944 dwgHandle xDict;
11945 const bool hasXDict = version <= DRW::AC1015 || info.m_xDictFlag != 1;
11946 if (hasXDict && !readHandle(false, xDict))
11947 return false;
11948
11949 dwgHandle color;
11950 if (info.m_hasAcDbColorHandle && !readHandle(false, color))
11951 return false;
11952
11953 dwgHandle layer;
11954 const bool hasLayer = version > DRW::AC1014;
11955 if (hasLayer && !readHandle(false, layer))
11956 return false;
11957
11958 dwgHandle lineType;
11959 const bool hasLineType = version > DRW::AC1014 && info.m_ltFlags == 3;
11960 if (hasLineType && !readHandle(false, lineType))
11961 return false;
11962
11963 dwgHandle material;
11964 const bool hasMaterial = version > DRW::AC1018 && info.m_materialFlag == 3;
11965 if (hasMaterial && !readHandle(false, material))
11966 return false;
11967
11968 dwgHandle shadow;
11969 const bool hasShadow = version > DRW::AC1018 && info.m_shadowFlag == 3;
11970 if (hasShadow && !readHandle(false, shadow))
11971 return false;
11972
11973 dwgHandle plotStyle;
11974 const bool hasPlotStyle = info.m_plotFlags == 3;
11975 if (hasPlotStyle && !readHandle(false, plotStyle))
11976 return false;
11977
11978 dwgHandle fullVisualStyle;
11979 const bool hasFullVisualStyle = version > DRW::AC1021
11980 && info.m_hasFullVisualStyle;
11981 if (hasFullVisualStyle && !readHandle(false, fullVisualStyle))
11982 return false;
11983
11984 dwgHandle faceVisualStyle;
11985 const bool hasFaceVisualStyle = version > DRW::AC1021
11986 && info.m_hasFaceVisualStyle;
11987 if (hasFaceVisualStyle && !readHandle(false, faceVisualStyle))
11988 return false;
11989
11990 dwgHandle edgeVisualStyle;
11991 const bool hasEdgeVisualStyle = version > DRW::AC1021
11992 && info.m_hasEdgeVisualStyle;
11993 if (hasEdgeVisualStyle && !readHandle(false, edgeVisualStyle))
11994 return false;
11995
11996 dwgHandle style;
11997 if (info.m_hasStyleHandle && !readHandle(false, style))
11998 return false;
11999
12000 dwgHandle appId;
12001 if (info.m_hasR2018AppIdHandle && !readHandle(false, appId))
12002 return false;
12003
12004 dwgHandle annotativeApp;
12005 if (info.m_hasAnnotativeAppHandle && !readHandle(false, annotativeApp))
12006 return false;
12007
12008 if (mtext != nullptr) {
12009 if (info.m_ownerHandle)
12010 mtext->parentHandle = owner.ref;
12011 mtext->reactorHandles = std::move(reactors);
12012 if (hasXDict)
12013 mtext->xDictHandle = xDict.ref;
12014 if (info.m_hasAcDbColorHandle)
12015 mtext->setDwgAcDbColorHandle(true, color.ref);
12016 if (hasLayer)
12017 mtext->setDwgLayerHandle(layer.ref);
12018 if (hasLineType)
12019 mtext->setDwgLinetypeHandle(lineType.ref);
12020 if (hasMaterial)
12021 mtext->material = material.ref;
12022 if (hasShadow)
12023 mtext->shadowHandle = shadow.ref;
12024 if (hasPlotStyle)
12025 mtext->plotStyle = static_cast<int>(plotStyle.ref);
12026 if (hasFullVisualStyle)
12027 mtext->fullVisualStyleHandle = fullVisualStyle.ref;
12028 if (hasFaceVisualStyle)
12029 mtext->faceVisualStyleHandle = faceVisualStyle.ref;
12030 if (hasEdgeVisualStyle)
12031 mtext->edgeVisualStyleHandle = edgeVisualStyle.ref;
12032 if (info.m_hasStyleHandle)
12033 mtext->styleH = style;
12034 if (info.m_hasR2018AppIdHandle)
12035 mtext->m_r2018AppIdHandle = appId.ref;
12036 if (info.m_hasAnnotativeAppHandle)
12037 mtext->m_r2018AnnotativeAppHandle = annotativeApp.ref;
12038 }
12039 *buf = probe;
12040 return true;
12041}
12042
12043static bool encodeEmbeddedMTextEntityMode(DRW::Version version, dwgBufferW *buf,
12044 const DRW_MText& mtext,
12045 bool hasOwnerHandle) {
12046 if (version < DRW::AC1032)
12047 return false;
12048
12049 // Embedded MTEXT begins at AcDbEntity mode, not with an object type,
12050 // object size, own handle, EED, or graphics data.
12051 if (mtext.reactorHandles.size()
12052 > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()))
12053 return false;
12054 buf->put2Bits(hasOwnerHandle ? 0 : 2);
12055 buf->putBitLong(static_cast<std::int32_t>(mtext.reactorHandles.size()));
12056 buf->putBit(mtext.xDictHandle == 0 ? 1 : 0);
12057 buf->putBit(mtext.hasDataStorageBinaryData() ? 1 : 0);
12058 UTF8STRINGstd::string colorName = mtext.colorName;
12059 UTF8STRINGstd::string bookName;
12060 const std::string::size_type separator = colorName.find('$');
12061 if (separator != std::string::npos) {
12062 bookName = colorName.substr(0, separator);
12063 colorName.erase(0, separator + 1);
12064 }
12065 buf->putEnColor(version, static_cast<std::uint16_t>(mtext.color),
12066 mtext.color24, colorName, bookName,
12067 static_cast<std::uint32_t>(mtext.transparency),
12068 mtext.hasDwgAcDbColorHandle());
12069 buf->putBitDouble(mtext.ltypeScale);
12070 std::uint8_t ltFlags = 0;
12071 if (mtext.dwgLinetypeHandle() != 0 || mtext.lineType.empty())
12072 ltFlags = 3;
12073 else if (mtext.lineType == "BYBLOCK")
12074 ltFlags = 1;
12075 else if (mtext.lineType == "CONTINUOUS")
12076 ltFlags = 2;
12077 buf->put2Bits(ltFlags);
12078 buf->put2Bits(mtext.plotStyle == DRW::DefaultPlotStyle ? 0 : 3);
12079 buf->put2Bits(mtext.material == DRW::MaterialByLayer ? 0 : 3);
12080 buf->putRawChar8(static_cast<std::uint8_t>(mtext.shadow));
12081 buf->putBit(mtext.fullVisualStyleHandle != 0);
12082 buf->putBit(mtext.faceVisualStyleHandle != 0);
12083 buf->putBit(mtext.edgeVisualStyleHandle != 0);
12084 buf->putBitShort(mtext.visible ? 0 : 1);
12085 buf->putRawChar8(static_cast<std::uint8_t>(mtext.lWeight));
12086 return true;
12087}
12088
12089// parentLayer and parentStyle are the handles the owning ATTRIB / ATTDEF
12090// writes for itself; the embedded MTEXT falls back to them, and from there to
12091// the writer's layer "0" and text style Standard.
12092static bool encodeEmbeddedMTextDwg(DRW::Version version, dwgBufferW *buf,
12093 dwgBufferW *strBuf, dwgBufferW *handleBuf,
12094 const DRW_MText& mtext,
12095 EmbeddedMTextForm form,
12096 std::uint32_t parentLayer = 0,
12097 std::uint32_t parentStyle = 0) {
12098 // AutoCAD always stores an owner handle here, null when there is none.
12099 const bool hasOwnerHandle = form == EmbeddedMTextForm::Full
12100 || mtext.parentHandle != DRW::NoHandle;
12101 if (!encodeEmbeddedMTextEntityMode(version, buf, mtext, hasOwnerHandle))
12102 return false;
12103
12104 buf->put3BitDouble(mtext.basePoint);
12105 buf->put3BitDouble(mtext.extPoint);
12106 buf->put3BitDouble(mtext.secPoint);
12107 buf->putBitDouble(mtext.widthscale);
12108 buf->putBitDouble(mtext.m_r2018RectHeight);
12109 buf->putBitDouble(mtext.height);
12110 buf->putBitShort(static_cast<std::uint16_t>(mtext.textgen));
12111 buf->putBitShort(static_cast<std::uint16_t>(mtext.alignH));
12112 buf->putBitDouble(mtext.m_r2018ExtentsHeight);
12113 buf->putBitDouble(mtext.m_r2018ExtentsWidth);
12114 (strBuf ? strBuf : buf)->putVariableText(version, mtext.text);
12115
12116 buf->putBitShort(mtext.linespacingStyle);
12117 buf->putBitDouble(mtext.interlin);
12118 buf->putBit(0);
12119 buf->putBitLong(mtext.m_backgroundFlags);
12120 if ((mtext.m_backgroundFlags & 0x01)
12121 || (mtext.m_backgroundFlags & 0x10)) {
12122 buf->putBitDouble(mtext.m_backgroundScale); // BitDouble, not BitLong
12123 buf->putCmColor(version, static_cast<std::uint16_t>(mtext.m_backgroundColor));
12124 buf->putBitLong(mtext.m_backgroundTransparency);
12125 }
12126
12127 buf->putBit(mtext.m_r2018IsNotAnnotative ? 1 : 0);
12128 if (form == EmbeddedMTextForm::Short) {
12129 buf->putBit(mtext.m_r2018ReallyLocked ? 1 : 0);
12130 } else if (mtext.m_r2018IsNotAnnotative) {
12131 // The block repeats fields written above. Take them from there, so an
12132 // MTEXT that never had the block, or was edited since it was read,
12133 // still gets a consistent one.
12134 DwgMTextR2018Block block = dwgMTextR2018BlockOf(mtext);
12135 block.m_attachment = mtext.textgen;
12136 block.m_xAxisDir = mtext.secPoint;
12137 block.m_insertionPoint = mtext.basePoint;
12138 block.m_rectWidth = mtext.widthscale;
12139 if (!putDwgMTextR2018Block(buf, block))
12140 return false;
12141 }
12142
12143 if (mtext.m_r2018AnnotativeData.size() > std::numeric_limits<std::uint16_t>::max())
12144 return false;
12145 buf->putBitShort(static_cast<std::uint16_t>(mtext.m_r2018AnnotativeData.size()));
12146 for (std::uint8_t byte : mtext.m_r2018AnnotativeData)
12147 buf->putRawChar8(byte);
12148 if (!mtext.m_r2018AnnotativeData.empty()) {
12149 buf->putBitShort(mtext.m_r2018AnnotativeUnknown);
12150 }
12151
12152 dwgBufferW *hb = handleBuf ? handleBuf : buf;
12153 if (form == EmbeddedMTextForm::Full)
12154 putDwgReference(hb, DRW::DwgSoftPointer, mtext.parentHandle);
12155 else if (hasOwnerHandle)
12156 putHardPointerHandle(hb, mtext.parentHandle);
12157 for (const std::uint32_t reactor : mtext.reactorHandles)
12158 putHardPointerHandle(hb, reactor);
12159 if (mtext.xDictHandle != 0)
12160 putHardPointerHandle(hb, mtext.xDictHandle);
12161 if (mtext.hasDwgAcDbColorHandle())
12162 putHardPointerHandle(hb, mtext.dwgAcDbColorHandle());
12163 if (parentLayer == 0)
12164 parentLayer = 0x12;
12165 putHardPointerHandle(hb, mtext.dwgLayerHandle() == 0
12166 ? parentLayer : mtext.dwgLayerHandle());
12167 if (mtext.dwgLinetypeHandle() != 0 || mtext.lineType.empty())
12168 putHardPointerHandle(hb, mtext.dwgLinetypeHandle());
12169 if (mtext.material != DRW::MaterialByLayer)
12170 putHardPointerHandle(hb, mtext.material);
12171 if (mtext.shadow == DRW::IgnoreShadows)
12172 putHardPointerHandle(hb, mtext.shadowHandle);
12173 if (mtext.plotStyle != DRW::DefaultPlotStyle)
12174 putHardPointerHandle(hb, static_cast<std::uint32_t>(mtext.plotStyle));
12175 if (mtext.fullVisualStyleHandle != 0)
12176 putHardPointerHandle(hb, mtext.fullVisualStyleHandle);
12177 if (mtext.faceVisualStyleHandle != 0)
12178 putHardPointerHandle(hb, mtext.faceVisualStyleHandle);
12179 if (mtext.edgeVisualStyleHandle != 0)
12180 putHardPointerHandle(hb, mtext.edgeVisualStyleHandle);
12181 if (parentStyle == 0)
12182 parentStyle = 0x13;
12183 putHardPointerHandle(hb, (mtext.styleH.ref == 0) ? parentStyle
12184 : mtext.styleH.ref);
12185 if (mtext.m_r2018IsNotAnnotative) {
12186 // AutoCAD leaves the registered application of an attribute's MTEXT
12187 // null.
12188 const bool defaultsToAcad = mtext.m_r2018AppIdHandle == 0
12189 && form == EmbeddedMTextForm::Short;
12190 putHardPointerHandle(hb, defaultsToAcad ? 0x14
12191 : mtext.m_r2018AppIdHandle);
12192 }
12193 if (!mtext.m_r2018AnnotativeData.empty())
12194 putHardPointerHandle(hb, (mtext.m_r2018AnnotativeAppHandle == 0)
12195 ? 0x14 : mtext.m_r2018AnnotativeAppHandle);
12196 return true;
12197}
12198
12199// What follows the attribute type in an ATTRIB or ATTDEF.
12200struct DwgAttributeTail {
12201 std::unique_ptr<DRW_MText> m_mtext;
12202 EmbeddedMTextHandleInfo m_embeddedHandles;
12203 EmbeddedMTextForm m_embeddedForm = EmbeddedMTextForm::Full;
12204 UTF8STRINGstd::string m_tag;
12205 std::uint16_t m_fieldLength = 0;
12206 std::uint8_t m_attribFlags = 0;
12207 bool m_lockPosition = false;
12208 UTF8STRINGstd::string m_prompt;
12209};
12210
12211// Everything an ATTRIB and an ATTDEF hold between the common entity data and
12212// the handle stream.
12213struct DwgAttributeBody {
12214 DwgTextBodyData m_text;
12215 std::uint8_t m_attVersion = 0;
12216 std::uint8_t m_attributeType = 1;
12217 DwgAttributeTail m_tail;
12218};
12219
12220bool readBoundedDwgAttributeTail(
12221 DRW::Version version, dwgBuffer& body, dwgBuffer& strings,
12222 std::uint64_t bodyEndBit, std::uint64_t stringStartBit,
12223 std::uint64_t stringEndBit, bool isAttdef, bool multiLine,
12224 EmbeddedMTextForm form, DwgAttributeTail& tail) {
12225 if (multiLine) {
12226 tail.m_mtext = std::make_unique<DRW_MText>();
12227 tail.m_embeddedForm = form;
12228 if (!parseEmbeddedMTextDwg(version, &body, &strings, *tail.m_mtext,
12229 tail.m_embeddedHandles, form, bodyEndBit,
12230 stringEndBit))
12231 return false;
12232 }
12233
12234 const bool hasStrings = version <= DRW::AC1018
12235 || stringStartBit < stringEndBit;
12236 if (hasStrings && !readBoundedVariableText(strings, stringEndBit, version,
12237 tail.m_tag))
12238 return false;
12239 if (!readBoundedBitShort(body, bodyEndBit, tail.m_fieldLength)
12240 || !readBoundedRawChar8(body, bodyEndBit, tail.m_attribFlags))
12241 return false;
12242 // lockPosition (DXF 280) is a bit in R2007+ DWG data.
12243 if (version >= DRW::AC1021
12244 && !readBoundedBit(body, bodyEndBit, tail.m_lockPosition))
12245 return false;
12246
12247 if (isAttdef) {
12248 // An R2010+ ATTDEF repeats the version byte before its prompt.
12249 // LibreCAD used to leave it out, so read it only when the body has
12250 // room for it.
12251 std::uint8_t repeatedVersion = 0;
12252 if (version >= DRW::AC1024 && form == EmbeddedMTextForm::Full
12253 && bodyEndBit - currentDwgBit(&body) >= 8
12254 && !readBoundedRawChar8(body, bodyEndBit, repeatedVersion))
12255 return false;
12256 if (hasStrings && !readBoundedVariableText(strings, stringEndBit,
12257 version, tail.m_prompt))
12258 return false;
12259 }
12260 return body.isGood() && strings.isGood()
12261 && currentDwgBit(&body) <= bodyEndBit;
12262}
12263
12264bool readBoundedDwgAttributeBody(
12265 DRW::Version version, dwgBuffer& body, dwgBuffer& strings,
12266 std::uint64_t bodyEndBit, std::uint64_t stringStartBit,
12267 std::uint64_t stringEndBit, bool isAttdef, DwgAttributeBody& data) {
12268 if (!readBoundedDwgTextBody(version, body, strings, bodyEndBit,
12269 stringStartBit, stringEndBit, data.m_text))
12270 return false;
12271 // R2010+ ATTRIB version follows the common TEXT data. R2018 adds the
12272 // attribute type immediately after it.
12273 if (version >= DRW::AC1024
12274 && !readBoundedRawChar8(body, bodyEndBit, data.m_attVersion))
12275 return false;
12276 if (version >= DRW::AC1032
12277 && !readBoundedRawChar8(body, bodyEndBit, data.m_attributeType))
12278 return false;
12279
12280 const bool multiLine = version >= DRW::AC1032
12281 && data.m_attributeType != 0 && data.m_attributeType != 1;
12282 if (!multiLine)
12283 return readBoundedDwgAttributeTail(
12284 version, body, strings, bodyEndBit, stringStartBit, stringEndBit,
12285 isAttdef, false, EmbeddedMTextForm::Full, data.m_tail);
12286
12287 // A multi-line attribute is in AutoCAD's form unless only the form of
12288 // older LibreCAD files accounts for the whole body: both writers end the
12289 // body right before the string stream, give or take the padding to a
12290 // byte. The streams are separate here, the embedded MTEXT is R2018 only.
12291 struct Attempt {
12292 dwgBuffer m_body;
12293 dwgBuffer m_strings;
12294 DwgAttributeTail m_tail;
12295 bool m_read = false;
12296 bool m_fits = false;
12297 };
12298 const auto read = [&](EmbeddedMTextForm form) {
12299 Attempt attempt{body.forkIndependent(), strings.forkIndependent(),
12300 DwgAttributeTail{}};
12301 attempt.m_read = readBoundedDwgAttributeTail(
12302 version, attempt.m_body, attempt.m_strings, bodyEndBit,
12303 stringStartBit, stringEndBit, isAttdef, true, form,
12304 attempt.m_tail);
12305 attempt.m_fits = attempt.m_read
12306 && bodyEndBit - currentDwgBit(&attempt.m_body) < 8;
12307 return attempt;
12308 };
12309 const auto accept = [&](Attempt& attempt) {
12310 body = attempt.m_body;
12311 strings = attempt.m_strings;
12312 data.m_tail = std::move(attempt.m_tail);
12313 return true;
12314 };
12315 Attempt full = read(EmbeddedMTextForm::Full);
12316 if (full.m_fits)
12317 return accept(full);
12318 Attempt legacy = read(EmbeddedMTextForm::Short);
12319 if (legacy.m_fits)
12320 return accept(legacy);
12321 return full.m_read && accept(full);
12322}
12323
12324// The attribute's own text style comes before what its embedded MTEXT refers
12325// to; older LibreCAD files have the two the other way round.
12326bool readBoundedDwgAttributeHandles(
12327 DRW::Version version, dwgBuffer& handles, std::uint64_t handleEndBit,
12328 DwgAttributeTail& tail, dwgHandle& styleH) {
12329 const bool embeddedFirst = tail.m_mtext != nullptr
12330 && tail.m_embeddedForm == EmbeddedMTextForm::Short;
12331 if (embeddedFirst
12332 && !consumeEmbeddedMTextHandles(version, &handles,
12333 tail.m_embeddedHandles,
12334 tail.m_mtext.get(), handleEndBit))
12335 return false;
12336 if (!readBoundedDwgHandle(handles, handleEndBit, 0, false, styleH))
12337 return false;
12338 if (tail.m_mtext != nullptr && !embeddedFirst
12339 && !consumeEmbeddedMTextHandles(version, &handles,
12340 tail.m_embeddedHandles,
12341 tail.m_mtext.get(), handleEndBit))
12342 return false;
12343 return handles.isGood();
12344}
12345
12346// Writes what readBoundedDwgAttributeBody reads, always in AutoCAD's form.
12347// prompt is null for an ATTRIB. The handles of an embedded MTEXT go to
12348// embeddedHandles, for the caller to append after the text style handle.
12349bool putDwgAttributeBody(DRW::Version version, dwgBufferW *buf,
12350 dwgBufferW *strBuf, const DRW_Attrib& attrib,
12351 const UTF8STRINGstd::string *prompt,
12352 dwgBufferW& embeddedHandles) {
12353 const std::uint8_t attributeType =
12354 (attrib.m_attributeType == 0) ? 1 : attrib.m_attributeType;
12355 const bool hasEmbeddedMText = version >= DRW::AC1032 && attributeType != 1;
12356 if (hasEmbeddedMText && !attrib.mtext)
12357 return false;
12358
12359 // TEXT-body section.
12360 // data_flags=0: emit every optional field unconditionally (same
12361 // strategy as DRW_Text::encodeDwg — simpler encoder, ~30 bytes larger).
12362 buf->putRawChar8(0); // data_flags=0
12363 buf->putRawDouble(attrib.basePoint.z); // elevation RD
12364 buf->putRawDouble(attrib.basePoint.x); // insertion 2RD
12365 buf->putRawDouble(attrib.basePoint.y);
12366 buf->putDefaultDouble(attrib.basePoint.x, attrib.secPoint.x); // alignment 2DD
12367 buf->putDefaultDouble(attrib.basePoint.y, attrib.secPoint.y);
12368 buf->putExtrusion(attrib.extPoint, /*b_R2000_style=*/true);
12369 buf->putThickness(attrib.thickness, /*b_R2000_style=*/true);
12370 buf->putRawDouble(attrib.oblique); // oblique angle RD
12371 buf->putRawDouble(attrib.angle / ARAD57.29577951308232); // angle in radians RD
12372 buf->putRawDouble(attrib.height); // text height RD
12373 buf->putRawDouble(attrib.widthscale); // width factor RD
12374 dwgBufferW *sb = strBuf ? strBuf : buf;
12375 sb->putVariableText(version, attrib.text); // text string TV
12376 buf->putBitShort(static_cast<std::uint16_t>(attrib.textgen)); // generation flags BS
12377 buf->putBitShort(static_cast<std::uint16_t>(attrib.alignH)); // horiz align BS
12378 buf->putBitShort(static_cast<std::uint16_t>(attrib.alignV)); // vert align BS
12379
12380 // AutoCAD has version 0 on multi-line attributes too.
12381 if (version >= DRW::AC1024)
12382 buf->putRawChar8(attrib.attVersion);
12383 if (version >= DRW::AC1032)
12384 buf->putRawChar8(attributeType);
12385
12386 if (hasEmbeddedMText
12387 && !encodeEmbeddedMTextDwg(version, buf, strBuf, &embeddedHandles,
12388 *attrib.mtext, EmbeddedMTextForm::Full,
12389 attrib.dwgLayerHandle(),
12390 attrib.styleH.ref))
12391 return false;
12392
12393 sb->putVariableText(version, attrib.tag); // tag TV
12394 buf->putBitShort(static_cast<std::uint16_t>(attrib.m_fieldLength)); // fieldLen BS
12395 buf->putRawChar8(attrib.attribFlags); // flags RC
12396 if (version >= DRW::AC1021)
12397 buf->putBit(attrib.lockPosition ? 1 : 0); // lock position B (R2007+)
12398
12399 if (prompt != nullptr) {
12400 if (version >= DRW::AC1024)
12401 buf->putRawChar8(attrib.attVersion); // version RC, repeated
12402 sb->putVariableText(version, *prompt); // prompt TV
12403 }
12404 return true;
12405}
12406}
12407
12408bool DRW_GeoPositionMarker::encodeDwg(DRW::Version v, dwgBufferW *buf,
12409 std::uint32_t bs, dwgBufferW *strBuf,
12410 dwgBufferW *handleBuf) {
12411 (void)bs;
12412 if (v < DRW::AC1027 || buf == nullptr)
12413 return false;
12414 // The embedded AcDbMTextObjectEmbedded wire form was introduced with
12415 // AC1032. Reject an AC1027 framed marker rather than emitting a body that
12416 // a reader will interpret as the parent handle stream.
12417 if (m_enableFrameText && (v < DRW::AC1032 || mtext == nullptr))
12418 return false;
12419
12420 oType = kDwgType;
12421 if (!encodeDwgCommon(v, buf, strBuf))
12422 return false;
12423
12424 dwgBufferW *stringBuffer = strBuf ? strBuf : buf;
12425 buf->putBitLong(m_classVersion);
12426 buf->put3BitDouble(m_position);
12427 buf->putBitDouble(m_radius);
12428 stringBuffer->putVariableText(v, m_notes);
12429 buf->putBitDouble(m_landingGap);
12430 buf->putBit(m_mtextVisible ? 1 : 0);
12431 buf->putRawChar8(m_textAlignment);
12432 buf->putBit(m_enableFrameText ? 1 : 0);
12433
12434 dwgBufferW embeddedHandles;
12435 if (m_enableFrameText
12436 && !encodeEmbeddedMTextDwg(v, buf, strBuf, &embeddedHandles, *mtext,
12437 EmbeddedMTextForm::Short))
12438 return false;
12439 if (!encodeDwgEntHandle(v, buf, handleBuf))
12440 return false;
12441
12442 dwgBufferW *resolvedHandles = handleBuf ? handleBuf : buf;
12443 appendBitBuffer(resolvedHandles, embeddedHandles);
12444 return true;
12445}
12446
12447bool DRW_Attrib::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
12448 // Multi-line ATTRIB / ATTDEF (R2018+). AutoCAD ends the AcDbAttribute
12449 // groups with the attribute type (71), a flag (72) and a copy of the
12450 // alignment point (11/21/31), then opens the MTEXT with the marker
12451 // `101 / Embedded Object`. From there to the XDATA, or to the end of the
12452 // record, every group is the embedded object's: the AcDbMText groups
12453 // 10/20/30 insertion, 40 height, 41 width, 46 defined height, 71
12454 // attachment, 72 direction, 3/1 text, 7 style, 210 extrusion, 11/21/31
12455 // x-axis, 50 rotation, 73/44 line spacing and the background groups.
12456 // ezdxf and ACadSharp read the record the same way. LibreCAD wrote the
12457 // marker as group 100 until this was corrected; that spelling is still
12458 // read.
12459 if (code == 100 || code == 101) {
12460 const std::string sub = reader->getString();
12461 if (sub == "Embedded Object") {
12462 // A further embedded object (ACadSharp appends the MTEXT column
12463 // data this way) is not modelled: the MTEXT skips its groups.
12464 if (mtext)
12465 return mtext->parseCode(101, reader);
12466 mtext = std::make_unique<DRW_MText>();
12467 // Which spelling the record had; both are read the same way.
12468 dxfEmbeddedMarker = code == 101 ? EmbeddedDxfMarker::Canonical101
12469 : EmbeddedDxfMarker::Legacy100;
12470 if (attVersion == 0) attVersion = 1;
12471 // The marker makes the attribute multi-line even in a record that
12472 // has no group 71; the DWG encoder drops the MTEXT of a type 1.
12473 if (m_attributeType <= 1)
12474 m_attributeType = eType == DRW::ATTDEF ? 4 : 2;
12475 return true;
12476 }
12477 if (code == 100)
12478 dxfInAttributeSubclass = sub == "AcDbAttribute"
12479 || sub == "AcDbAttributeDefinition";
12480 return true;
12481 }
12482 if (dxfInAttributeSubclass && !mtext) {
12483 if (code == 71) {
12484 const int value = reader->getInt32();
12485 if (value < 0 || value > std::numeric_limits<std::uint8_t>::max())
12486 return false;
12487 m_attributeType = static_cast<std::uint8_t>(value);
12488 return true;
12489 }
12490 if (code == 72) {
12491 (void)reader->getInt32();
12492 return true;
12493 }
12494 }
12495 // Past the marker everything short of XDATA goes to the embedded MTEXT,
12496 // group 3 (more text) included; AutoCAD, ezdxf and LibreCAD all write the
12497 // attribute's own groups before the marker. XDATA is the ATTRIB's.
12498 if (mtext && code < 1000) {
12499 const bool parsed = mtext->parseCode(code, reader);
12500 // No end-of-entity hook runs for an embedded MTEXT, so its rotation is
12501 // settled as the groups arrive. The last of 11/21/31 (WCS x-axis) and
12502 // 50 prevails, as for MTEXT, and the other is not kept: earlier
12503 // LibreCAD builds wrote a stale axis in front of the angle, and the
12504 // writer emits whichever of the two is set.
12505 if (!mtext->m_dxfInEmbeddedObject) {
12506 if (code == 11 || code == 21)
12507 mtext->updateAngle();
12508 else if (code == 50)
12509 mtext->secPoint = DRW_Coord{0.0, 0.0, 0.0};
12510 }
12511 return parsed;
12512 }
12513 switch (code) {
12514 case 2:
12515 tag = reader->getUtf8String();
12516 break;
12517 case 70:
12518 attribFlags = reader->getInt32();
12519 break;
12520 case 73:
12521 // AcDbAttribute code 73 = field length (obsolete); NOT the vertical
12522 // alignment from AcDbText (which is code 73 in TEXT but 74 in ATTRIB).
12523 m_fieldLength = reader->getInt32();
12524 break;
12525 case 74:
12526 // AcDbAttribute vertical alignment (code 74); TEXT uses code 73 for
12527 // this but ATTRIB moves it here to free code 73 for field length.
12528 alignV = (VAlign)reader->getInt32();
12529 break;
12530 case 280:
12531 // Lock position flag (R2010+ DXF group code)
12532 lockPosition = reader->getInt32() != 0;
12533 break;
12534 default:
12535 return DRW_Text::parseCode(code, reader);
12536 }
12537 return true;
12538}
12539
12540void DRW_Attrib::resetAttribDwgState() {
12541 resetDwgState();
12542 tag.clear();
12543 attribFlags = 0;
12544 m_fieldLength = 0;
12545 lockPosition = false;
12546 attVersion = 0;
12547 m_attributeType = 1;
12548 keepDuplicateRecords = 0;
12549 mtext.reset();
12550 dxfInAttributeSubclass = false;
12551 dxfEmbeddedMarker = EmbeddedDxfMarker::None;
12552}
12553
12554bool DRW_Attrib::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
12555 resetAttribDwgState();
12556 dwgBuffer *sourceBuf = buf;
12557 auto fail = [this, sourceBuf]() {
12558 if (sourceBuf != nullptr)
12559 sourceBuf->invalidate();
12560 resetAttribDwgState();
12561 return false;
12562 };
12563 if (sourceBuf == nullptr)
12564 return fail();
12565
12566 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
12567 buf = &bodyProbe;
12568 dwgBuffer sBuff = bodyProbe.forkIndependent();
12569 dwgBuffer *sBuf = &bodyProbe;
12570 if (version > DRW::AC1018) {//2007+
12571 sBuf = &sBuff; //separate buffer for strings
12572 }
12573 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
12574 if (!ret)
12575 return fail();
12576 DRW_DBG("\n***************************** parsing attrib *********************************************\n")DRW_dbg::dbg("\n***************************** parsing attrib *********************************************\n"
)
;
12577
12578 const std::uint64_t bodyEndBit = dwgDataEndBit;
12579 std::uint64_t stringStartBit = bodyEndBit;
12580 std::uint64_t stringEndBit = bodyEndBit;
12581 if (version > DRW::AC1018
12582 && (!buf->getR2007StringStreamBounds(objSize, stringStartBit,
12583 stringEndBit)
12584 || currentDwgBit(sBuf) > stringEndBit))
12585 return fail();
12586
12587 DwgAttributeBody parsed;
12588 if (!readBoundedDwgAttributeBody(version, *buf, *sBuf, bodyEndBit,
12589 stringStartBit, stringEndBit,
12590 /*isAttdef=*/false, parsed)) {
12591 DRW_DBG("ATTRIB payload failed\n")DRW_dbg::dbg("ATTRIB payload failed\n");
12592 return fail();
12593 }
12594
12595 /* Common Entity Handle Data */
12596 // The parent ATTRIB handle stream starts at objSize; its text style and
12597 // the handles of an embedded MTEXT follow the common ones.
12598 std::uint64_t handleEndBit = 0;
12599 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
12600 handleEndBit))
12601 return fail();
12602 dwgBuffer handleProbe = buf->forkIndependent();
12603 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
12604 handleEndBit);
12605 if (!ret)
12606 return fail();
12607
12608 dwgHandle parsedStyleH;
12609 if (!readBoundedDwgAttributeHandles(version, handleProbe, handleEndBit,
12610 parsed.m_tail, parsedStyleH)
12611 || !sBuf->isGood()
12612 || currentDwgBit(buf) > bodyEndBit
12613 || currentDwgBit(sBuf) > stringEndBit)
12614 return fail();
12615
12616 *sourceBuf = handleProbe;
12617 basePoint = parsed.m_text.basePoint;
12618 secPoint = parsed.m_text.secPoint;
12619 extPoint = parsed.m_text.extPoint;
12620 thickness = parsed.m_text.thickness;
12621 oblique = parsed.m_text.oblique;
12622 angle = parsed.m_text.angle;
12623 height = parsed.m_text.height;
12624 widthscale = parsed.m_text.widthscale;
12625 text = std::move(parsed.m_text.text);
12626 textgen = parsed.m_text.textgen;
12627 alignH = parsed.m_text.alignH;
12628 alignV = parsed.m_text.alignV;
12629 attVersion = parsed.m_attVersion;
12630 m_attributeType = parsed.m_attributeType;
12631 mtext = std::move(parsed.m_tail.m_mtext);
12632 tag = std::move(parsed.m_tail.m_tag);
12633 m_fieldLength = parsed.m_tail.m_fieldLength;
12634 attribFlags = parsed.m_tail.m_attribFlags;
12635 lockPosition = parsed.m_tail.m_lockPosition;
12636 styleH = parsedStyleH;
12637 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");
12638 return true;
12639}
12640
12641bool DRW_Attrib::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
12642 (void)bs;
12643 if (version >= DRW::AC1024 && version < DRW::AC1032 && (attVersion != 0 || mtext))
12644 return false;
12645
12646 oType = 2; // ATTRIB class id — see dwgreader.cpp:1148
12647 if (!encodeDwgCommon(version, buf)) return false;
12648
12649 dwgBufferW embeddedHandles;
12650 if (!putDwgAttributeBody(version, buf, strBuf, *this, /*prompt=*/nullptr,
12651 embeddedHandles))
12652 return false;
12653
12654 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
12655
12656 dwgBufferW *sb = strBuf ? strBuf : buf;
12657 dwgBufferW *hb = handleBuf ? handleBuf : buf;
12658 putHardPointerHandle(hb, (styleH.ref == 0) ? 0x13 : styleH.ref);
12659 appendBitBuffer(hb, embeddedHandles);
12660 return buf->isGood() && sb->isGood() && hb->isGood();
12661}
12662
12663bool DRW_Attdef::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
12664 // Group 3 is the prompt only up to the embedded-object marker; after it
12665 // the group carries MTEXT content.
12666 if (code == 3 && !mtext) {
12667 prompt = reader->getUtf8String();
12668 return true;
12669 }
12670 return DRW_Attrib::parseCode(code, reader);
12671}
12672
12673bool DRW_Attdef::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
12674 // ATTDEF mirrors ATTRIB layout but adds a prompt string after the tag.
12675 resetAttribDwgState();
12676 prompt.clear();
12677 dwgBuffer *sourceBuf = buf;
12678 auto fail = [this, sourceBuf]() {
12679 if (sourceBuf != nullptr)
12680 sourceBuf->invalidate();
12681 resetAttribDwgState();
12682 prompt.clear();
12683 return false;
12684 };
12685 if (sourceBuf == nullptr)
12686 return fail();
12687
12688 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
12689 buf = &bodyProbe;
12690 dwgBuffer sBuff = bodyProbe.forkIndependent();
12691 dwgBuffer *sBuf = &bodyProbe;
12692 if (version > DRW::AC1018) {
12693 sBuf = &sBuff;
12694 }
12695 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
12696 if (!ret)
12697 return fail();
12698 DRW_DBG("\n***************************** parsing attdef *********************************************\n")DRW_dbg::dbg("\n***************************** parsing attdef *********************************************\n"
)
;
12699 const std::uint64_t bodyEndBit = dwgDataEndBit;
12700 std::uint64_t stringStartBit = bodyEndBit;
12701 std::uint64_t stringEndBit = bodyEndBit;
12702 if (version > DRW::AC1018
12703 && (!buf->getR2007StringStreamBounds(objSize, stringStartBit,
12704 stringEndBit)
12705 || currentDwgBit(sBuf) > stringEndBit))
12706 return fail();
12707
12708 DwgAttributeBody parsed;
12709 if (!readBoundedDwgAttributeBody(version, *buf, *sBuf, bodyEndBit,
12710 stringStartBit, stringEndBit,
12711 /*isAttdef=*/true, parsed)) {
12712 DRW_DBG("ATTDEF payload failed\n")DRW_dbg::dbg("ATTDEF payload failed\n");
12713 return fail();
12714 }
12715
12716 // The parent ATTDEF handle stream; its text style and the handles of an
12717 // embedded MTEXT follow the common ones.
12718 std::uint64_t handleEndBit = 0;
12719 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
12720 handleEndBit))
12721 return fail();
12722 dwgBuffer handleProbe = buf->forkIndependent();
12723 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
12724 handleEndBit);
12725 if (!ret)
12726 return fail();
12727
12728 dwgHandle parsedStyleH;
12729 if (!readBoundedDwgAttributeHandles(version, handleProbe, handleEndBit,
12730 parsed.m_tail, parsedStyleH)
12731 || !sBuf->isGood()
12732 || currentDwgBit(buf) > bodyEndBit
12733 || currentDwgBit(sBuf) > stringEndBit)
12734 return fail();
12735
12736 *sourceBuf = handleProbe;
12737 basePoint = parsed.m_text.basePoint;
12738 secPoint = parsed.m_text.secPoint;
12739 extPoint = parsed.m_text.extPoint;
12740 thickness = parsed.m_text.thickness;
12741 oblique = parsed.m_text.oblique;
12742 angle = parsed.m_text.angle;
12743 height = parsed.m_text.height;
12744 widthscale = parsed.m_text.widthscale;
12745 text = std::move(parsed.m_text.text);
12746 textgen = parsed.m_text.textgen;
12747 alignH = parsed.m_text.alignH;
12748 alignV = parsed.m_text.alignV;
12749 attVersion = parsed.m_attVersion;
12750 m_attributeType = parsed.m_attributeType;
12751 mtext = std::move(parsed.m_tail.m_mtext);
12752 tag = std::move(parsed.m_tail.m_tag);
12753 m_fieldLength = parsed.m_tail.m_fieldLength;
12754 attribFlags = parsed.m_tail.m_attribFlags;
12755 lockPosition = parsed.m_tail.m_lockPosition;
12756 prompt = std::move(parsed.m_tail.m_prompt);
12757 styleH = parsedStyleH;
12758 return true;
12759}
12760
12761bool DRW_Attdef::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
12762 (void)bs;
12763 if (version >= DRW::AC1024 && version < DRW::AC1032 && (attVersion != 0 || mtext))
12764 return false;
12765
12766 oType = 3; // ATTDEF class id — see dwgreader.cpp:1185
12767 if (!encodeDwgCommon(version, buf)) return false;
12768
12769 dwgBufferW embeddedHandles;
12770 if (!putDwgAttributeBody(version, buf, strBuf, *this, &prompt,
12771 embeddedHandles))
12772 return false;
12773
12774 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
12775
12776 dwgBufferW *sb = strBuf ? strBuf : buf;
12777 dwgBufferW *hb = handleBuf ? handleBuf : buf;
12778 putHardPointerHandle(hb, (styleH.ref == 0) ? 0x13 : styleH.ref);
12779 appendBitBuffer(hb, embeddedHandles);
12780 return buf->isGood() && sb->isGood() && hb->isGood();
12781}
12782
12783bool DRW_MText::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
12784 // R2018 appends the column data of an MTEXT as `101 / Embedded Object`.
12785 // That object reuses the MTEXT group codes with other meanings (10 is the
12786 // x-axis, 11 the insertion point, 40 the width, 71 the column type, 73 the
12787 // auto-height flag ...), so none of it may reach the fields below. XDATA
12788 // follows the object and is still the entity's.
12789 if (code == 101) {
12790 if (reader->getString() == "Embedded Object")
12791 m_dxfInEmbeddedObject = true;
12792 return true;
12793 }
12794 if (m_dxfInEmbeddedObject && code < 1000)
12795 return true;
12796 switch (code) {
12797 case 1:
12798 text += reader->getString();
12799 text = reader->toUtf8String(text);
12800 break;
12801 case 11:
12802 hasXAxisVec = true;
12803 return DRW_Text::parseCode(code, reader);
12804 case 3:
12805 text += reader->getString();
12806 break;
12807 case 44:
12808 interlin = reader->getDouble();
12809 break;
12810 case 50: // djm: per dxf docs, last of code 11 or code 50 prevails
12811 hasXAxisVec = false;
12812 angle = reader->getDouble();
12813 break;
12814 case 73: {
12815 const int value = reader->getInt32();
12816 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
12817 return false;
12818 linespacingStyle = static_cast<std::uint16_t>(value);
12819 break;
12820 }
12821 case 42:
12822 m_r2018ExtentsWidth = reader->getDouble();
12823 break;
12824 case 43:
12825 m_r2018ExtentsHeight = reader->getDouble();
12826 break;
12827 case 46:
12828 m_r2018RectHeight = reader->getDouble();
12829 break;
12830 case 45:
12831 m_backgroundScale = reader->getDouble();
12832 break;
12833 case 63:
12834 m_backgroundColor = reader->getInt32();
12835 break;
12836 case 90:
12837 m_backgroundFlags = reader->getInt32();
12838 break;
12839 case 421:
12840 // True colour of the background fill; 63 keeps its ACI.
12841 m_backgroundTrueColor = reader->getInt32();
12842 break;
12843 case 441:
12844 m_backgroundTransparency = reader->getInt32();
12845 break;
12846 default:
12847 return DRW_Text::parseCode(code, reader);
12848 }
12849
12850 return true;
12851}
12852
12853void DRW_MText::resetDwgState() {
12854 DRW_Text::resetDwgState();
12855 interlin = 1.0;
12856 linespacingStyle = 1;
12857 m_backgroundFlags = 0;
12858 m_backgroundScale = 0.0;
12859 m_backgroundColor = 0;
12860 m_backgroundTrueColor = -1;
12861 m_backgroundTransparency = 0;
12862 hasXAxisVec = false;
12863 m_dxfInEmbeddedObject = false;
12864 m_r2018IsNotAnnotative = false;
12865 m_r2018ReallyLocked = false;
12866 m_r2018Version = 0;
12867 m_r2018DefaultFlag = false;
12868 m_r2018AppIdHandle = 0;
12869 m_r2018Attachment = 0;
12870 m_r2018XAxisDir = {};
12871 m_r2018InsertionPoint = {};
12872 m_r2018RectWidth = 0.0;
12873 m_r2018RectHeight = 0.0;
12874 m_r2018ExtentsHeight = 0.0;
12875 m_r2018ExtentsWidth = 0.0;
12876 m_r2018ColumnType = 0;
12877 m_r2018ColumnCount = 0;
12878 m_r2018ColumnWidth = 0.0;
12879 m_r2018ColumnGutter = 0.0;
12880 m_r2018ColumnAutoHeight = false;
12881 m_r2018ColumnFlowReversed = false;
12882 m_r2018ColumnHeights.clear();
12883 m_r2018AnnotativeData.clear();
12884 m_r2018AnnotativeUnknown = 0;
12885 m_r2018AnnotativeAppHandle = 0;
12886 textgen = 1;
12887 alignV = static_cast<VAlign>(TopLeft);
12888}
12889
12890bool DRW_MText::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
12891 // A reader may reuse an entity carrier while walking a compound stream.
12892 // R2018 context data is version-gated, so clear it before parsing an
12893 // older MTEXT or stale column/annotative state can leak into the result.
12894 resetDwgState();
12895 dwgBuffer *sourceBuf = buf;
12896 auto fail = [this, sourceBuf]() {
12897 if (sourceBuf != nullptr)
12898 sourceBuf->invalidate();
12899 resetDwgState();
12900 return false;
12901 };
12902 if (sourceBuf == nullptr)
12903 return fail();
12904
12905 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
12906 buf = &bodyProbe;
12907
12908 dwgBuffer sBuff = buf->forkIndependent();
12909 dwgBuffer *sBuf = buf;
12910 if (version > DRW::AC1018) {//2007+
12911 sBuf = &sBuff; //separate buffer for strings
12912 }
12913 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
12914 if (!ret)
12915 return fail();
12916 DRW_DBG("\n***************************** parsing mtext *********************************************\n")DRW_dbg::dbg("\n***************************** parsing mtext *********************************************\n"
)
;
12917
12918 const std::uint64_t bodyEndBit = dwgDataEndBit;
12919 std::uint64_t stringStartBit = bodyEndBit;
12920 std::uint64_t stringEndBit = bodyEndBit;
12921 if (version > DRW::AC1018) {
12922 if (!buf->getR2007StringStreamBounds(objSize, stringStartBit,
12923 stringEndBit)
12924 || currentDwgBit(sBuf) > stringEndBit)
12925 return fail();
12926 }
12927
12928 const bool hasStringStream = stringStartBit < stringEndBit;
12929 const auto readText = [&](UTF8STRINGstd::string& value) {
12930 if (version > DRW::AC1018 && !hasStringStream) {
12931 value.clear();
12932 return true;
12933 }
12934 const std::uint64_t endBit = version > DRW::AC1018
12935 ? stringEndBit : bodyEndBit;
12936 return readBoundedVariableText(*sBuf, endBit, version, value);
12937 };
12938
12939 DRW_Coord parsedBasePoint;
12940 DRW_Coord parsedExtrusion;
12941 DRW_Coord parsedXAxisDirection;
12942 double parsedWidthScale = 0.0;
12943 double parsedRectangleHeight = 0.0;
12944 double parsedHeight = 0.0;
12945 std::uint16_t parsedAttachment = 1;
12946 std::uint16_t parsedDrawingDirection = 0;
12947 double ignoredExtentsHeight = 0.0;
12948 double ignoredExtentsWidth = 0.0;
12949 UTF8STRINGstd::string parsedText;
12950 std::uint16_t parsedLineSpacingStyle = 1;
12951 double parsedInterlin = 1.0;
12952 std::int32_t parsedBackgroundFlags = 0;
12953 double parsedBackgroundScale = 0.0;
12954 std::uint32_t parsedBackgroundColor = 0;
12955 std::int32_t parsedBackgroundTransparency = 0;
12956 bool parsedIsNotAnnotative = false;
12957 DwgMTextR2018Block parsedR2018;
12958
12959 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedBasePoint)
12960 || !readBoundedBitCoord(*buf, bodyEndBit, parsedExtrusion)
12961 || !readBoundedBitCoord(*buf, bodyEndBit, parsedXAxisDirection)
12962 || !readBoundedBitDouble(*buf, bodyEndBit, parsedWidthScale)
12963 || (version > DRW::AC1018
12964 && !readBoundedBitDouble(*buf, bodyEndBit,
12965 parsedRectangleHeight))
12966 || !readBoundedBitDouble(*buf, bodyEndBit, parsedHeight)
12967 || !readBoundedBitShort(*buf, bodyEndBit, parsedAttachment)
12968 || !readBoundedBitShort(*buf, bodyEndBit, parsedDrawingDirection)
12969 || !readBoundedBitDouble(*buf, bodyEndBit, ignoredExtentsHeight)
12970 || !readBoundedBitDouble(*buf, bodyEndBit, ignoredExtentsWidth)
12971 || !readText(parsedText))
12972 return fail();
12973
12974 if (version > DRW::AC1014
12975 && (!readBoundedBitShort(*buf, bodyEndBit, parsedLineSpacingStyle)
12976 || !readBoundedBitDouble(*buf, bodyEndBit, parsedInterlin)))
12977 return fail();
12978 bool ignoredBit = false;
12979 if (version > DRW::AC1014 && !readBoundedBit(*buf, bodyEndBit,
12980 ignoredBit))
12981 return fail();
12982
12983 if (version > DRW::AC1015) {
12984 if (!readBoundedBitLong(*buf, bodyEndBit, parsedBackgroundFlags))
12985 return fail();
12986 const bool hasBackgroundDetails = (parsedBackgroundFlags & 0x01) != 0
12987 || (version >= DRW::AC1032
12988 && (parsedBackgroundFlags & 0x10) != 0);
12989 if (hasBackgroundDetails
12990 && (!readBoundedBitDouble(*buf, bodyEndBit,
12991 parsedBackgroundScale)
12992 || !readBoundedCmColor(*buf, sBuf, bodyEndBit, version,
12993 parsedBackgroundColor, nullptr,
12994 nullptr, nullptr, nullptr,
12995 stringEndBit)
12996 || !readBoundedBitLong(*buf, bodyEndBit,
12997 parsedBackgroundTransparency)))
12998 return fail();
12999 }
13000
13001 bool hasR2018AppId = false;
13002 if (version >= DRW::AC1032) {
13003 if (!readBoundedBit(*buf, bodyEndBit, parsedIsNotAnnotative))
13004 return fail();
13005 if (parsedIsNotAnnotative) {
13006 hasR2018AppId = true;
13007 if (!readBoundedDwgMTextR2018Block(*buf, bodyEndBit, parsedR2018))
13008 return fail();
13009 }
13010 }
13011
13012 const auto finite = [](const DRW_Coord& point) {
13013 return std::isfinite(point.x) && std::isfinite(point.y)
13014 && std::isfinite(point.z);
13015 };
13016 if (!buf->isGood() || !sBuf->isGood()
13017 || !finite(parsedBasePoint) || !finite(parsedExtrusion)
13018 || !finite(parsedXAxisDirection)
13019 || !std::isfinite(parsedWidthScale)
13020 || !std::isfinite(parsedRectangleHeight)
13021 || !std::isfinite(parsedHeight)
13022 || !std::isfinite(ignoredExtentsHeight)
13023 || !std::isfinite(ignoredExtentsWidth)
13024 || !std::isfinite(parsedInterlin))
13025 return fail();
13026
13027 std::uint64_t handleEndBit = 0;
13028 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
13029 handleEndBit))
13030 return fail();
13031 dwgBuffer handleProbe = buf->forkIndependent();
13032 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
13033 handleEndBit))
13034 return fail();
13035
13036 dwgHandle parsedStyleH;
13037 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
13038 parsedStyleH))
13039 return fail();
13040 dwgHandle parsedAppIdH;
13041 if (hasR2018AppId
13042 && !readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
13043 parsedAppIdH))
13044 return fail();
13045 if (!handleProbe.isGood() || !sBuf->isGood())
13046 return fail();
13047
13048 *sourceBuf = handleProbe;
13049 basePoint = parsedBasePoint;
13050 extPoint = parsedExtrusion;
13051 secPoint = parsedXAxisDirection;
13052 hasXAxisVec = true;
13053 widthscale = parsedWidthScale;
13054 height = parsedHeight;
13055 angle = atan2(secPoint.y, secPoint.x) * ARAD57.29577951308232;
13056 textgen = parsedAttachment;
13057 alignH = static_cast<HAlign>(parsedDrawingDirection);
13058 text = std::move(parsedText);
13059 linespacingStyle = parsedLineSpacingStyle;
13060 interlin = parsedInterlin;
13061 m_backgroundFlags = parsedBackgroundFlags;
13062 m_backgroundScale = parsedBackgroundScale;
13063 m_backgroundColor = static_cast<int>(parsedBackgroundColor);
13064 m_backgroundTransparency = parsedBackgroundTransparency;
13065 m_r2018IsNotAnnotative = parsedIsNotAnnotative;
13066 m_r2018AppIdHandle = hasR2018AppId ? parsedAppIdH.ref : 0;
13067 storeDwgMTextR2018Block(std::move(parsedR2018), *this);
13068 styleH = parsedStyleH;
13069 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(".");
13070 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");
13071 return true;
13072}
13073
13074void DRW_MText::updateAngle() {
13075 if (hasXAxisVec) {
13076 angle = atan2(secPoint.y, secPoint.x) * ARAD57.29577951308232;
13077 }
13078}
13079
13080bool DRW_Polyline::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
13081 switch (code) {
13082 case 70:
13083 flags = reader->getInt32();
13084 break;
13085 case 40:
13086 defstawidth = reader->getDouble();
13087 break;
13088 case 41:
13089 defendwidth = reader->getDouble();
13090 break;
13091 case 71:
13092 vertexcount = reader->getInt32();
13093 break;
13094 case 72:
13095 facecount = reader->getInt32();
13096 break;
13097 case 73:
13098 smoothM = reader->getInt32();
13099 break;
13100 case 74:
13101 smoothN = reader->getInt32();
13102 break;
13103 case 75:
13104 curvetype = reader->getInt32();
13105 break;
13106 default:
13107 return DRW_Point::parseCode(code, reader);
13108 }
13109
13110 return true;
13111}
13112
13113//0x0F polyline 2D bit 4(8) & 5(16) NOT set
13114//0x10 polyline 3D bit 4(8) set
13115//0x1D PFACE bit 5(16) set
13116void DRW_Polyline::resetDwgState() {
13117 DRW_Point::resetDwgState();
13118 flags = 0;
13119 defstawidth = 0.0;
13120 defendwidth = 0.0;
13121 vertexcount = 0;
13122 facecount = 0;
13123 smoothM = 0;
13124 smoothN = 0;
13125 curvetype = 0;
13126 vertlist.clear();
13127 hadlesList.clear();
13128 firstEH = 0;
13129 lastEH = 0;
13130 seqEndH = dwgHandle{};
13131}
13132
13133bool DRW_Polyline::validatePayloadFields() const {
13134 const auto finite = [](const DRW_Coord& point) {
13135 return std::isfinite(point.x) && std::isfinite(point.y)
13136 && std::isfinite(point.z);
13137 };
13138 const auto validBitShort = [](int value) {
13139 return value >= 0
13140 && value <= std::numeric_limits<std::uint16_t>::max();
13141 };
13142 return flags >= 0 && flags <= std::numeric_limits<std::uint8_t>::max()
13143 && validBitShort(curvetype) && validBitShort(vertexcount)
13144 && validBitShort(facecount) && validBitShort(smoothM)
13145 && validBitShort(smoothN) && finite(basePoint)
13146 && finite(extPoint) && std::isfinite(defstawidth)
13147 && std::isfinite(defendwidth) && std::isfinite(thickness)
13148 && vertlist.size() <= dwgSafety::MaxOwnedObjectCount
13149 && vertlist.size()
13150 <= static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max())
13151 && std::all_of(vertlist.cbegin(), vertlist.cend(),
13152 [](const auto& vertex) { return vertex != nullptr; });
13153}
13154
13155bool DRW_Polyline::isDwgVertexCompatible(
13156 const DRW_Vertex& vertex) const noexcept {
13157 if ((flags & 64) != 0) {
13158 return vertex.dwgSubtype() == DRW_Vertex::DwgSubtype::Polyface
13159 || vertex.dwgSubtype() == DRW_Vertex::DwgSubtype::PolyfaceFace;
13160 }
13161 if ((flags & 16) != 0)
13162 return vertex.dwgSubtype() == DRW_Vertex::DwgSubtype::Mesh;
13163 if ((flags & 8) != 0)
13164 return vertex.dwgSubtype() == DRW_Vertex::DwgSubtype::Vertex3D;
13165 return vertex.dwgSubtype() == DRW_Vertex::DwgSubtype::Vertex2D;
13166}
13167
13168bool DRW_Polyline::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
13169 resetDwgState();
13170 dwgBuffer *sourceBuf = buf;
13171 auto fail = [this, sourceBuf]() {
13172 if (sourceBuf != nullptr)
13173 sourceBuf->invalidate();
13174 resetDwgState();
13175 return false;
13176 };
13177
13178 if (sourceBuf == nullptr)
13179 return fail();
13180
13181 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
13182 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
13183 || !bodyProbe.isGood())
13184 return fail();
13185 DRW_DBG("\n***************************** parsing polyline *********************************************\n")DRW_dbg::dbg("\n***************************** parsing polyline *********************************************\n"
)
;
13186
13187 const std::uint64_t bodyEndBit = dwgDataEndBit;
13188 int parsedFlags = 0;
13189 double parsedDefStartWidth = 0.0;
13190 double parsedDefEndWidth = 0.0;
13191 int parsedVertexCount = 0;
13192 int parsedFaceCount = 0;
13193 int parsedSmoothM = 0;
13194 int parsedSmoothN = 0;
13195 int parsedCurveType = 0;
13196 double parsedThickness = 0.0;
13197 DRW_Coord parsedBasePoint;
13198 DRW_Coord parsedExtrusion;
13199 std::int32_t ooCount = 0;
13200 if (oType == 0x0F) { //pline 2D
13201 std::uint16_t value = 0;
13202 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13203 return fail();
13204 parsedFlags = value;
13205 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(parsedFlags)DRW_dbg::dbg(parsedFlags);
13206 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13207 return fail();
13208 parsedCurveType = value;
13209 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, parsedDefStartWidth)
13210 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedDefEndWidth)
13211 || !readBoundedThickness(bodyProbe, bodyEndBit,
13212 version > DRW::AC1014, parsedThickness)
13213 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedBasePoint.z)
13214 || !readBoundedExtrusion(bodyProbe, bodyEndBit,
13215 version > DRW::AC1014, parsedExtrusion))
13216 return fail();
13217 } else if (oType == 0x10) { //pline 3D
13218 std::uint8_t tmpFlag = 0;
13219 if (!readBoundedRawChar8(bodyProbe, bodyEndBit, tmpFlag))
13220 return fail();
13221 DRW_DBG("flags 1 value: ")DRW_dbg::dbg("flags 1 value: "); DRW_DBG(tmpFlag)DRW_dbg::dbg(tmpFlag);
13222 if (tmpFlag & 1)
13223 parsedCurveType = 5; // quadratic B-spline
13224 else if (tmpFlag & 2)
13225 parsedCurveType = 6; // cubic B-spline
13226 if (tmpFlag & 3)
13227 parsedFlags |= 4; // splined (bit 2); do NOT overwrite curvetype to 8
13228 if (!readBoundedRawChar8(bodyProbe, bodyEndBit, tmpFlag))
13229 return fail();
13230 if (tmpFlag & 1)
13231 parsedFlags |= 1;
13232 parsedFlags |= 8; //indicate 3DPOL
13233 DRW_DBG("flags 2 value: ")DRW_dbg::dbg("flags 2 value: "); DRW_DBG(tmpFlag)DRW_dbg::dbg(tmpFlag);
13234 } else if (oType == 0x1D) { //PFACE
13235 std::uint16_t vertexValue = 0;
13236 std::uint16_t faceValue = 0;
13237 if (!readBoundedBitShort(bodyProbe, bodyEndBit, vertexValue)
13238 || !readBoundedBitShort(bodyProbe, bodyEndBit, faceValue))
13239 return fail();
13240 parsedFlags = 64;
13241 parsedVertexCount = vertexValue;
13242 parsedFaceCount = faceValue;
13243 DRW_DBG("vertex count: ")DRW_dbg::dbg("vertex count: "); DRW_DBG(parsedVertexCount)DRW_dbg::dbg(parsedVertexCount);
13244 DRW_DBG("face count: ")DRW_dbg::dbg("face count: "); DRW_DBG(parsedFaceCount)DRW_dbg::dbg(parsedFaceCount);
13245 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(parsedFlags)DRW_dbg::dbg(parsedFlags);
13246 } else if (oType == 0x1E) { //POLYLINE_MESH per ODA spec sec 19.4.31
13247 std::uint16_t value = 0;
13248 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13249 return fail();
13250 parsedFlags = value | 16; //bit 4 = 3D polygon mesh
13251 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(parsedFlags)DRW_dbg::dbg(parsedFlags);
13252 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13253 return fail();
13254 parsedCurveType = value;
13255 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13256 return fail();
13257 parsedVertexCount = value; //M-count
13258 DRW_DBG(" M count: ")DRW_dbg::dbg(" M count: "); DRW_DBG(parsedVertexCount)DRW_dbg::dbg(parsedVertexCount);
13259 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13260 return fail();
13261 parsedFaceCount = value; //N-count
13262 DRW_DBG(" N count: ")DRW_dbg::dbg(" N count: "); DRW_DBG(parsedFaceCount)DRW_dbg::dbg(parsedFaceCount);
13263 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13264 return fail();
13265 parsedSmoothM = value; //M smooth-surface density, DXF 73
13266 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13267 return fail();
13268 parsedSmoothN = value; //N smooth-surface density, DXF 74
13269 DRW_DBG(" M/N density: ")DRW_dbg::dbg(" M/N density: "); DRW_DBG(parsedSmoothM)DRW_dbg::dbg(parsedSmoothM); DRW_DBG("/")DRW_dbg::dbg("/"); DRW_DBG(parsedSmoothN)DRW_dbg::dbg(parsedSmoothN);
13270 } else {
13271 return fail();
13272 }
13273 if (version > DRW::AC1015){ //2004+
13274 if (!readBoundedBitLong(bodyProbe, bodyEndBit, ooCount)
13275 || !dwgSafety::validOwnedObjectCount(
13276 ooCount, bodyProbe.numRemainingBytes())) {
13277 DRW_DBG("\nWARNING: Invalid POLYLINE owned-object count\n")DRW_dbg::dbg("\nWARNING: Invalid POLYLINE owned-object count\n"
)
;
13278 return fail();
13279 }
13280 }
13281
13282 std::uint64_t handleEndBit = 0;
13283 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
13284 handleEndBit))
13285 return fail();
13286 dwgBuffer handleProbe = bodyProbe.forkIndependent();
13287 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
13288 handleEndBit)
13289 || !handleProbe.isGood())
13290 return fail();
13291
13292 std::list<std::uint32_t> parsedHandles;
13293 std::uint32_t parsedFirstEH = 0;
13294 std::uint32_t parsedLastEH = 0;
13295
13296 if (version < DRW::AC1018){ //2000-
13297 dwgHandle objectH;
13298 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
13299 objectH))
13300 return fail();
13301 parsedFirstEH = objectH.ref;
13302 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");
13303 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
13304 objectH))
13305 return fail();
13306 parsedLastEH = objectH.ref;
13307 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");
13308 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
13309 } else {
13310 for (std::int32_t i = 0; i < ooCount; ++i){
13311 dwgHandle objectH;
13312 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle,
13313 true, objectH))
13314 return fail();
13315 try {
13316 parsedHandles.push_back(objectH.ref);
13317 } catch (...) {
13318 return fail();
13319 }
13320 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");
13321 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
13322 }
13323 }
13324 dwgHandle parsedSeqEndH;
13325 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
13326 parsedSeqEndH))
13327 return fail();
13328 if (!handleProbe.isGood())
13329 return fail();
13330 *sourceBuf = handleProbe;
13331 flags = parsedFlags;
13332 defstawidth = parsedDefStartWidth;
13333 defendwidth = parsedDefEndWidth;
13334 vertexcount = parsedVertexCount;
13335 facecount = parsedFaceCount;
13336 smoothM = parsedSmoothM;
13337 smoothN = parsedSmoothN;
13338 curvetype = parsedCurveType;
13339 thickness = parsedThickness;
13340 basePoint = parsedBasePoint;
13341 extPoint = parsedExtrusion;
13342 firstEH = parsedFirstEH;
13343 lastEH = parsedLastEH;
13344 seqEndH = parsedSeqEndH;
13345 hadlesList = std::move(parsedHandles);
13346 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");
13347 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");
13348
13349// RS crc; //RS */
13350 return buf->isGood();
13351}
13352
13353bool DRW_Vertex::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
13354 switch (code) {
13355 case 70:
13356 flags = reader->getInt32();
13357 break;
13358 case 40:
13359 stawidth = reader->getDouble();
13360 break;
13361 case 41:
13362 endwidth = reader->getDouble();
13363 break;
13364 case 42:
13365 bulge = reader->getDouble();
13366 break;
13367 case 50:
13368 tgdir = reader->getDouble();
13369 break;
13370 case 71:
13371 vindex1 = reader->getInt32();
13372 break;
13373 case 72:
13374 vindex2 = reader->getInt32();
13375 break;
13376 case 73:
13377 vindex3 = reader->getInt32();
13378 break;
13379 case 74:
13380 vindex4 = reader->getInt32();
13381 break;
13382 case 91:
13383 identifier = reader->getInt32();
13384 break;
13385 default:
13386 return DRW_Point::parseCode(code, reader);
13387 }
13388
13389 return true;
13390}
13391
13392//0x0A vertex 2D
13393//0x0B vertex 3D
13394//0x0C MESH
13395//0x0D PFACE
13396//0x0E PFACE FACE
13397void DRW_Vertex::resetDwgState() {
13398 DRW_Point::resetDwgState();
13399 stawidth = 0.0;
13400 endwidth = 0.0;
13401 bulge = 0.0;
13402 flags = 0;
13403 tgdir = 0.0;
13404 vindex1 = vindex2 = vindex3 = vindex4 = 0;
13405 identifier = 0;
13406 m_dwgSubtype = DwgSubtype::Auto;
13407}
13408
13409bool DRW_Vertex::validatePayloadFields() const {
13410 const auto finite = [](const DRW_Coord& point) {
13411 return std::isfinite(point.x) && std::isfinite(point.y)
13412 && std::isfinite(point.z);
13413 };
13414 const auto validFaceIndex = [](int value) {
13415 return value >= std::numeric_limits<std::int16_t>::min()
13416 && value <= std::numeric_limits<std::int16_t>::max();
13417 };
13418 const auto validInt32 = [](int value) {
13419 return value >= std::numeric_limits<std::int32_t>::min()
13420 && value <= std::numeric_limits<std::int32_t>::max();
13421 };
13422 return flags >= 0 && flags <= std::numeric_limits<std::uint8_t>::max()
13423 && validFaceIndex(vindex1) && validFaceIndex(vindex2)
13424 && validFaceIndex(vindex3) && validFaceIndex(vindex4)
13425 && validInt32(identifier) && finite(basePoint)
13426 && std::isfinite(stawidth) && std::isfinite(endwidth)
13427 && std::isfinite(bulge) && std::isfinite(tgdir);
13428}
13429
13430bool DRW_Vertex::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs, double el){
13431 resetDwgState();
13432 dwgBuffer *sourceBuf = buf;
13433 auto fail = [this, sourceBuf]() {
13434 if (sourceBuf != nullptr)
13435 sourceBuf->invalidate();
13436 resetDwgState();
13437 return false;
13438 };
13439
13440 if (sourceBuf == nullptr)
13441 return fail();
13442
13443 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
13444 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
13445 || !bodyProbe.isGood())
13446 return fail();
13447 DRW_DBG("\n***************************** parsing pline Vertex *********************************************\n")DRW_dbg::dbg("\n***************************** parsing pline Vertex *********************************************\n"
)
;
13448
13449 const std::uint64_t bodyEndBit = dwgDataEndBit;
13450 DRW_Vertex::DwgSubtype parsedSubtype = DRW_Vertex::DwgSubtype::Auto;
13451 std::uint8_t parsedFlags = 0;
13452 DRW_Coord parsedBasePoint;
13453 double parsedStartWidth = 0.0;
13454 double parsedEndWidth = 0.0;
13455 double parsedBulge = 0.0;
13456 double parsedTangentDirection = 0.0;
13457 int parsedIndex1 = 0;
13458 int parsedIndex2 = 0;
13459 int parsedIndex3 = 0;
13460 int parsedIndex4 = 0;
13461 int parsedIdentifier = 0;
13462 if (oType == 0x0A) { //pline 2D, needed example
13463 parsedSubtype = DwgSubtype::Vertex2D;
13464 if (!readBoundedRawChar8(bodyProbe, bodyEndBit, parsedFlags)
13465 || !readBoundedBitCoord(bodyProbe, bodyEndBit, parsedBasePoint)
13466 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedStartWidth))
13467 return fail();
13468 parsedBasePoint.z = el;
13469 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(parsedFlags)DRW_dbg::dbg(parsedFlags);
13470 DRW_DBG("basePoint: ")DRW_dbg::dbg("basePoint: "); DRW_DBGPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint.z)DRW_dbg::dbgPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint
.z)
;
13471 if (parsedStartWidth < 0.0) {
13472 parsedEndWidth = parsedStartWidth = std::fabs(parsedStartWidth);
13473 } else if (!readBoundedBitDouble(bodyProbe, bodyEndBit,
13474 parsedEndWidth)) {
13475 return fail();
13476 }
13477 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, parsedBulge))
13478 return fail();
13479 if (version > DRW::AC1021) { //2010+
13480 if (!readBoundedBitLong(bodyProbe, bodyEndBit, parsedIdentifier))
13481 return fail();
13482 DRW_DBG("Vertex ID: ")DRW_dbg::dbg("Vertex ID: "); DRW_DBG(parsedIdentifier)DRW_dbg::dbg(parsedIdentifier);
13483 }
13484 if (!readBoundedBitDouble(bodyProbe, bodyEndBit,
13485 parsedTangentDirection))
13486 return fail();
13487 } else if (oType == 0x0B || oType == 0x0C || oType == 0x0D) { //PFACE
13488 if (oType == 0x0B)
13489 parsedSubtype = DwgSubtype::Vertex3D;
13490 else if (oType == 0x0C)
13491 parsedSubtype = DwgSubtype::Mesh;
13492 else
13493 parsedSubtype = DwgSubtype::Polyface;
13494 if (!readBoundedRawChar8(bodyProbe, bodyEndBit, parsedFlags)
13495 || !readBoundedBitCoord(bodyProbe, bodyEndBit, parsedBasePoint))
13496 return fail();
13497 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(parsedFlags)DRW_dbg::dbg(parsedFlags);
13498 DRW_DBG("basePoint: ")DRW_dbg::dbg("basePoint: "); DRW_DBGPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint.z)DRW_dbg::dbgPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint
.z)
;
13499 } else if (oType == 0x0E) { //PFACE FACE
13500 parsedSubtype = DwgSubtype::PolyfaceFace;
13501 auto signedIndex = [](std::uint16_t value) {
13502 return value > 32767 ? value - 65536 : value;
13503 };
13504 std::uint16_t value = 0;
13505 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13506 return fail();
13507 parsedIndex1 = signedIndex(value);
13508 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13509 return fail();
13510 parsedIndex2 = signedIndex(value);
13511 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13512 return fail();
13513 parsedIndex3 = signedIndex(value);
13514 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13515 return fail();
13516 parsedIndex4 = signedIndex(value);
13517 } else {
13518 return fail();
13519 }
13520
13521 const auto finite = [](const DRW_Coord& point) {
13522 return std::isfinite(point.x) && std::isfinite(point.y)
13523 && std::isfinite(point.z);
13524 };
13525 if (!finite(parsedBasePoint) || !std::isfinite(parsedStartWidth)
13526 || !std::isfinite(parsedEndWidth) || !std::isfinite(parsedBulge)
13527 || !std::isfinite(parsedTangentDirection)
13528 || !std::isfinite(el))
13529 return fail();
13530
13531 std::uint64_t handleEndBit = 0;
13532 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
13533 handleEndBit))
13534 return fail();
13535 dwgBuffer handleProbe = bodyProbe.forkIndependent();
13536 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
13537 handleEndBit)
13538 || !handleProbe.isGood())
13539 return fail();
13540 // RS crc; //RS */
13541 *sourceBuf = handleProbe;
13542 m_dwgSubtype = parsedSubtype;
13543 flags = parsedFlags;
13544 basePoint = parsedBasePoint;
13545 stawidth = parsedStartWidth;
13546 endwidth = parsedEndWidth;
13547 bulge = parsedBulge;
13548 tgdir = parsedTangentDirection;
13549 vindex1 = parsedIndex1;
13550 vindex2 = parsedIndex2;
13551 vindex3 = parsedIndex3;
13552 vindex4 = parsedIndex4;
13553 identifier = parsedIdentifier;
13554 return true;
13555}
13556
13557bool DRW_Hatch::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
13558 try {
13559 switch (code) {
13560 case 2:
13561 name = reader->getUtf8String();
13562 break;
13563 case 70:
13564 solid = reader->getInt32();
13565 break;
13566 case 71:
13567 associative = reader->getInt32();
13568 break;
13569 case 72: { /*edge type*/
13570 if (ispol){ // polyline path: 72 is the has-bulge flag. Do NOT fold it
13571 // into pline->flags — bit 0 there is the *closed* flag (set by code
13572 // 73), and the per-vertex bulges arrive via code 42 regardless. Some
13573 // writers (e.g. ezdxf MPOLYGON) emit 73 before 72; the old code let
13574 // 72 clear the closed bit 73 had just set, leaving the boundary open
13575 // so RS_Hatch::validate() rejected the area.
13576 break;
13577 }
13578 if (spline && !validateCurrentSplineDxf())
13579 return false;
13580 if (!loop
13581 || loop->objlist.size() >= static_cast<std::size_t>(kMaxHatchItems)
13582 || (m_dxfLoopEdgeCountExpected >= 0
13583 && loop->objlist.size()
13584 >= static_cast<std::size_t>(m_dxfLoopEdgeCountExpected)))
13585 return false;
13586 const int edgeType = reader->getInt32();
13587 if (edgeType == 1){ //line
13588 addLine();
13589 } else if (edgeType == 2){ //arc
13590 addArc();
13591 } else if (edgeType == 3){ //elliptic arc
13592 addEllipse();
13593 } else if (edgeType == 4){ //spline
13594 addSpline();
13595 } else {
13596 return false;
13597 }
13598 break;
13599 }
13600 case 10:
13601 // Spline edge: 10 is a control-point x-coord.
13602 if (spline) {
13603 if (spline->controllist.size() >= static_cast<std::size_t>(kMaxHatchItems)
13604 || (m_dxfSplineControlCountExpected >= 0
13605 && spline->controllist.size()
13606 >= static_cast<std::size_t>(m_dxfSplineControlCountExpected)))
13607 return false;
13608 spline->controllist.push_back(std::make_shared<DRW_Coord>(reader->getDouble(), 0.0, 0.0));
13609 break;
13610 }
13611 if (pt) pt->basePoint.x = reader->getDouble();
13612 else if (pline) {
13613 if (pline->vertlist.size() >= static_cast<std::size_t>(kMaxHatchItems)
13614 || (m_dxfPolylineVertexCountExpected >= 0
13615 && pline->vertlist.size()
13616 >= static_cast<std::size_t>(m_dxfPolylineVertexCountExpected)))
13617 return false;
13618 plvert = pline->addVertex();
13619 plvert->x = reader->getDouble();
13620 } else if (m_dxfSeedPointsExpected < 0) {
13621 return DRW_Point::parseCode(code, reader);
13622 } else {
13623 // After group 98 the boundary path is closed; seed-point
13624 // coords arrive as group-10/20 pairs.
13625 if (seedPoints.size() >= static_cast<std::size_t>(m_dxfSeedPointsExpected))
13626 return false;
13627 DRW_Coord seed;
13628 seed.x = reader->getDouble();
13629 seedPoints.push_back(seed);
13630 }
13631 break;
13632 case 20:
13633 if (spline && !spline->controllist.empty()) {
13634 spline->controllist.back()->y = reader->getDouble();
13635 break;
13636 }
13637 if (pt) pt->basePoint.y = reader->getDouble();
13638 else if (plvert) plvert ->y = reader->getDouble();
13639 else if (m_dxfSeedPointsExpected >= 0) {
13640 if (seedPoints.empty())
13641 return false;
13642 seedPoints.back().y = reader->getDouble();
13643 } else {
13644 return DRW_Point::parseCode(code, reader);
13645 }
13646 break;
13647 case 11:
13648 // Spline edge: 11 is a fit-point x-coord.
13649 if (spline) {
13650 if (spline->fitlist.size() >= static_cast<std::size_t>(kMaxHatchItems)
13651 || (m_dxfSplineFitCountExpected >= 0
13652 && spline->fitlist.size()
13653 >= static_cast<std::size_t>(m_dxfSplineFitCountExpected)))
13654 return false;
13655 spline->fitlist.push_back(std::make_shared<DRW_Coord>(reader->getDouble(), 0.0, 0.0));
13656 break;
13657 }
13658 if (line) line->secPoint.x = reader->getDouble();
13659 else if (ellipse) ellipse->secPoint.x = reader->getDouble();
13660 break;
13661 case 21:
13662 if (spline && !spline->fitlist.empty()) {
13663 spline->fitlist.back()->y = reader->getDouble();
13664 break;
13665 }
13666 if (line) line->secPoint.y = reader->getDouble();
13667 else if (ellipse) ellipse->secPoint.y = reader->getDouble();
13668 break;
13669 case 12:
13670 if (spline) { spline->tgStart.x = reader->getDouble(); break; }
13671 break;
13672 case 22:
13673 if (spline) { spline->tgStart.y = reader->getDouble(); break; }
13674 break;
13675 case 13:
13676 if (spline) { spline->tgEnd.x = reader->getDouble(); break; }
13677 break;
13678 case 23:
13679 if (spline) { spline->tgEnd.y = reader->getDouble(); break; }
13680 break;
13681 case 40:
13682 // Spline edge: 40 is a knot value (occurs nknots times).
13683 if (spline) {
13684 if (spline->knotslist.size() >= static_cast<std::size_t>(kMaxHatchItems)
13685 || (m_dxfSplineKnotCountExpected >= 0
13686 && spline->knotslist.size()
13687 >= static_cast<std::size_t>(m_dxfSplineKnotCountExpected)))
13688 return false;
13689 spline->knotslist.push_back(reader->getDouble());
13690 break;
13691 }
13692 if (arc) arc->radious = reader->getDouble();
13693 else if (ellipse) ellipse->ratio = reader->getDouble();
13694 break;
13695 case 41:
13696 scale = reader->getDouble();
13697 break;
13698 case 42:
13699 // Spline edge: 42 is a per-control-point weight.
13700 if (spline) {
13701 if (spline->weightlist.size() >= static_cast<std::size_t>(kMaxHatchItems)
13702 || (m_dxfSplineControlCountExpected >= 0
13703 && spline->weightlist.size()
13704 >= static_cast<std::size_t>(m_dxfSplineControlCountExpected)))
13705 return false;
13706 spline->weightlist.push_back(reader->getDouble());
13707 break;
13708 }
13709 if (plvert) plvert ->bulge = reader->getDouble();
13710 break;
13711 case 50:
13712 if (arc) arc->staangle = reader->getDouble()/ARAD57.29577951308232;
13713 else if (ellipse) ellipse->staparam = reader->getDouble()/ARAD57.29577951308232;
13714 break;
13715 case 51:
13716 if (arc) arc->endangle = reader->getDouble()/ARAD57.29577951308232;
13717 else if (ellipse) ellipse->endparam = reader->getDouble()/ARAD57.29577951308232;
13718 break;
13719 case 47:
13720 pixelSize = reader->getDouble();
13721 break;
13722 case 52:
13723 angle = reader->getDouble() / ARAD57.29577951308232;
13724 break;
13725 case 53: // pattern line angle — starts a new PatternLine record
13726 if (!validateCurrentPatternLineDxf())
13727 return false;
13728 if (m_dxfPatternLineCountExpected >= 0
13729 && patternLines.size() >= static_cast<std::size_t>(m_dxfPatternLineCountExpected))
13730 return false;
13731 if (patternLines.size() >= static_cast<std::size_t>(kMaxHatchItems))
13732 return false;
13733 patternLines.push_back(PatternLine());
13734 patternLines.back().angle = reader->getDouble() / ARAD57.29577951308232;
13735 m_dxfPatternDashCountExpected = -1;
13736 m_dxfPatternDashCountSeen = false;
13737 break;
13738 case 43:
13739 if (!patternLines.empty()) patternLines.back().baseX = reader->getDouble();
13740 break;
13741 case 44:
13742 if (!patternLines.empty()) patternLines.back().baseY = reader->getDouble();
13743 break;
13744 case 45:
13745 if (!patternLines.empty()) patternLines.back().offsetX = reader->getDouble();
13746 break;
13747 case 46:
13748 if (!patternLines.empty()) patternLines.back().offsetY = reader->getDouble();
13749 break;
13750 case 79: // dash count — the 49s that follow will accumulate
13751 if (patternLines.empty() || m_dxfPatternDashCountSeen)
13752 return false;
13753 m_dxfPatternDashCountExpected = reader->getInt32();
13754 if (!isValidCount(m_dxfPatternDashCountExpected, kMaxHatchItems))
13755 return false;
13756 m_dxfPatternDashCountSeen = true;
13757 return DRW::reserve(patternLines.back().dashList, m_dxfPatternDashCountExpected);
13758 case 49:
13759 if (patternLines.empty()
13760 || patternLines.back().dashList.size() >= static_cast<std::size_t>(kMaxHatchItems)
13761 || (m_dxfPatternDashCountExpected >= 0
13762 && patternLines.back().dashList.size()
13763 >= static_cast<std::size_t>(m_dxfPatternDashCountExpected)))
13764 return false;
13765 patternLines.back().dashList.push_back(reader->getDouble());
13766 break;
13767 case 73:
13768 // Spline edge: 73 is the rational flag (1 = rational).
13769 if (spline) {
13770 if (reader->getInt32()) spline->flags |= 0x4;
13771 break;
13772 }
13773 if (arc) arc->isccw = reader->getInt32();
13774 // polyline path: 73 is the is-closed flag -> set bit 0 only, leaving the
13775 // rest of pline->flags untouched (order-independent vs code 72).
13776 else if (pline) pline->flags = (pline->flags & ~1) | (reader->getInt32() ? 1 : 0);
13777 break;
13778 case 74:
13779 // Spline edge: 74 is the periodic flag (1 = periodic/closed).
13780 if (spline) {
13781 if (reader->getInt32()) spline->flags |= 0x2;
13782 }
13783 break;
13784 case 94:
13785 // Spline edge degree.
13786 if (spline) {
13787 spline->degree = reader->getInt32();
13788 return isValidCount(spline->degree, kMaxSplineDegree)
13789 && spline->degree != 0;
13790 }
13791 break;
13792 case 95:
13793 // Spline edge number of knots.
13794 if (spline) {
13795 if (m_dxfSplineKnotCountExpected >= 0)
13796 return false;
13797 spline->nknots = reader->getInt32();
13798 if (!isValidCount(spline->nknots, kMaxHatchItems))
13799 return false;
13800 m_dxfSplineKnotCountExpected = spline->nknots;
13801 return DRW::reserve(spline->knotslist, spline->nknots);
13802 }
13803 break;
13804 case 96:
13805 // Spline edge number of control points.
13806 if (spline) {
13807 if (m_dxfSplineControlCountExpected >= 0)
13808 return false;
13809 spline->ncontrol = reader->getInt32();
13810 if (!isValidCount(spline->ncontrol, kMaxHatchItems))
13811 return false;
13812 m_dxfSplineControlCountExpected = spline->ncontrol;
13813 return DRW::reserve(spline->controllist, spline->ncontrol);
13814 }
13815 break;
13816 case 97:
13817 if (spline) {
13818 if (!m_splineNfitSet) {
13819 // First 97 in this spline edge = fit-point count (nfit).
13820 spline->nfit = reader->getInt32();
13821 if (!isValidCount(spline->nfit, kMaxHatchItems))
13822 return false;
13823 if (m_dxfSplineFitCountExpected >= 0)
13824 return false;
13825 m_dxfSplineFitCountExpected = spline->nfit;
13826 if (!DRW::reserve(spline->fitlist, spline->nfit))
13827 return false;
13828 if (spline->nfit == 0) {
13829 if (!validateCurrentSplineDxf())
13830 return false;
13831 // No fit points or tangents follow; safe to clear spline
13832 // so the next code-97 (loop boundary count) is not
13833 // misinterpreted as another nfit.
13834 spline.reset();
13835 } else {
13836 m_splineNfitSet = true;
13837 }
13838 } else {
13839 // Second 97 while spline is active = loop boundary handle count.
13840 if (!validateCurrentSplineDxf())
13841 return false;
13842 spline.reset();
13843 m_splineNfitSet = false;
13844 m_boundaryHandleCount = reader->getInt32();
13845 if (!isValidCount(m_boundaryHandleCount, kMaxHatchItems))
13846 return false;
13847 if (m_dxfBoundaryHandleCountSeen)
13848 return false;
13849 m_dxfBoundaryHandleCountSeen = true;
13850 if (m_boundaryHandleCount > 0 && loop
13851 && !DRW::reserve(loop->m_boundaryHandles, m_boundaryHandleCount))
13852 return false;
13853 }
13854 break;
13855 }
13856 // No active spline: this is the loop boundary handle count.
13857 m_splineNfitSet = false;
13858 if (m_dxfBoundaryHandleCountSeen)
13859 return false;
13860 m_boundaryHandleCount = reader->getInt32();
13861 if (!isValidCount(m_boundaryHandleCount, kMaxHatchItems))
13862 return false;
13863 m_dxfBoundaryHandleCountSeen = true;
13864 if (m_boundaryHandleCount > 0 && loop
13865 && !DRW::reserve(loop->m_boundaryHandles, m_boundaryHandleCount))
13866 return false;
13867 break;
13868 case 330:
13869 if (m_boundaryHandleCount > 0 && loop) {
13870 // getHandleString() converts the hex string to int for us.
13871 loop->m_boundaryHandles.push_back(
13872 static_cast<std::uint32_t>(reader->getHandleString()));
13873 --m_boundaryHandleCount;
13874 break;
13875 }
13876 if (m_dxfBoundaryHandleCountSeen)
13877 return false;
13878 return DRW_Point::parseCode(code, reader);
13879 case 75:
13880 hstyle = reader->getInt32();
13881 break;
13882 case 76:
13883 hpattern = reader->getInt32();
13884 break;
13885 case 77:
13886 doubleflag = reader->getInt32();
13887 break;
13888 case 78:
13889 if (m_dxfPatternLineCountSeen)
13890 return false;
13891 deflines = reader->getInt32();
13892 if (!isValidCount(deflines, kMaxHatchItems))
13893 return false;
13894 m_dxfPatternLineCountExpected = deflines;
13895 m_dxfPatternLineCountSeen = true;
13896 if (patternLines.size() > static_cast<std::size_t>(deflines))
13897 return false;
13898 break;
13899 case 91:
13900 if (m_dxfLoopCountSeen)
13901 return false;
13902 loopsnum = reader->getInt32();
13903 if (!isValidCount(loopsnum, kMaxHatchItems))
13904 return false;
13905 m_dxfLoopCountExpected = loopsnum;
13906 m_dxfLoopCountSeen = true;
13907 return DRW::reserve( looplist, loopsnum);
13908 case 92: {
13909 if (!validateCurrentBoundaryPathDxf())
13910 return false;
13911 if (m_dxfLoopCountExpected >= 0
13912 && looplist.size() >= static_cast<std::size_t>(m_dxfLoopCountExpected))
13913 return false;
13914 clearEntities();
13915 m_dxfLoopEdgeCountExpected = -1;
13916 m_dxfLoopEdgeCountSeen = false;
13917 m_dxfPolylineVertexCountExpected = -1;
13918 m_dxfPolylineVertexCountSeen = false;
13919 m_dxfBoundaryHandleCountSeen = false;
13920 m_boundaryHandleCount = 0;
13921 loop = std::make_shared<DRW_HatchLoop>(reader->getInt32());
13922 looplist.push_back(loop);
13923 if (reader->getInt32() & 2) {
13924 ispol = true;
13925 clearEntities();
13926 pline = std::make_shared<DRW_LWPolyline>();
13927 loop->objlist.push_back(pline);
13928 } else ispol = false;
13929 break;
13930 }
13931 case 93:
13932 if (pline) {
13933 if (m_dxfPolylineVertexCountSeen)
13934 return false;
13935 pline->vertexnum = reader->getInt32();
13936 if (!isValidCount(pline->vertexnum, kMaxHatchItems))
13937 return false;
13938 m_dxfPolylineVertexCountExpected = pline->vertexnum;
13939 m_dxfPolylineVertexCountSeen = true;
13940 } else if (loop) {
13941 if (m_dxfLoopEdgeCountSeen)
13942 return false;
13943 loop->numedges = reader->getInt32();//aqui reserve
13944 if (!isValidCount(loop->numedges, kMaxHatchItems))
13945 return false;
13946 if (loop->objlist.size() > static_cast<std::size_t>(loop->numedges))
13947 return false;
13948 m_dxfLoopEdgeCountExpected = loop->numedges;
13949 m_dxfLoopEdgeCountSeen = true;
13950 }
13951 break;
13952 case 98: { // seed-point count; coords follow as group-10/20 pairs
13953 if (!validateCurrentBoundaryPathDxf() || !validateCurrentPatternLineDxf())
13954 return false;
13955 if (m_dxfSeedPointCountSeen)
13956 return false;
13957 clearEntities();
13958 const int count = reader->getInt32();
13959 if (!isValidCount(count, kMaxHatchItems)
13960 || !DRW::reserve(seedPoints, count))
13961 return false;
13962 m_dxfSeedPointsExpected = count;
13963 m_dxfSeedPointCountSeen = true;
13964 break;
13965 }
13966 case 450:
13967 isGradient = reader->getInt32();
13968 break;
13969 case 451:
13970 gradReserved = reader->getInt32();
13971 break;
13972 case 452:
13973 singleColor = reader->getInt32();
13974 break;
13975 case 453: {
13976 if (m_dxfGradientColorCountSeen)
13977 return false;
13978 const int n = reader->getInt32();
13979 if (!isValidCount(n, kMaxHatchItems)
13980 || !DRW::reserve(gradColors, n))
13981 return false;
13982 m_dxfGradientColorCountExpected = n;
13983 m_dxfGradientColorCountSeen = true;
13984 break;
13985 }
13986 case 460:
13987 gradAngle = reader->getDouble();
13988 break;
13989 case 461:
13990 gradShift = reader->getDouble();
13991 break;
13992 case 462:
13993 gradTint = reader->getDouble();
13994 break;
13995 case 463: {
13996 if (m_dxfGradientColorCountExpected >= 0
13997 && gradColors.size() >= static_cast<std::size_t>(m_dxfGradientColorCountExpected))
13998 return false;
13999 if (gradColors.size() >= static_cast<std::size_t>(kMaxHatchItems))
14000 return false;
14001 DRW_Hatch::GradientStop stop;
14002 stop.value = reader->getDouble();
14003 gradColors.push_back(stop);
14004 break;
14005 }
14006 case 421:
14007 if (!gradColors.empty())
14008 gradColors.back().rgb = reader->getInt32();
14009 break;
14010 case 63:
14011 if (!gradColors.empty())
14012 gradColors.back().aciColor = reader->getInt32();
14013 else
14014 return DRW_Point::parseCode(code, reader);
14015 break;
14016 case 431:
14017 if (!gradColors.empty())
14018 gradColors.back().colorMethod = reader->getInt32();
14019 break;
14020 case 432:
14021 if (!gradColors.empty())
14022 gradColors.back().colorName = reader->getUtf8String();
14023 break;
14024 case 433:
14025 if (!gradColors.empty())
14026 gradColors.back().colorBookName = reader->getUtf8String();
14027 break;
14028 case 470:
14029 gradName = reader->getUtf8String();
14030 break;
14031 default:
14032 return DRW_Point::parseCode(code, reader);
14033 }
14034
14035 return true;
14036 }
14037 catch (...) {
14038 return false;
14039 }
14040}
14041
14042bool DRW_Hatch::validateCurrentSplineDxf() const {
14043 if (!spline)
14044 return true;
14045 if (m_dxfSplineKnotCountExpected >= 0
14046 && spline->knotslist.size()
14047 != static_cast<std::size_t>(m_dxfSplineKnotCountExpected))
14048 return false;
14049 if (m_dxfSplineControlCountExpected >= 0
14050 && spline->controllist.size()
14051 != static_cast<std::size_t>(m_dxfSplineControlCountExpected))
14052 return false;
14053 if (m_dxfSplineFitCountExpected >= 0
14054 && spline->fitlist.size()
14055 != static_cast<std::size_t>(m_dxfSplineFitCountExpected))
14056 return false;
14057 if (spline->flags & 0x4)
14058 return m_dxfSplineControlCountExpected < 0
14059 || spline->weightlist.size()
14060 == static_cast<std::size_t>(m_dxfSplineControlCountExpected);
14061 return spline->weightlist.empty();
14062}
14063
14064bool DRW_Hatch::validateCurrentPatternLineDxf() const {
14065 if (patternLines.empty() || m_dxfPatternDashCountExpected < 0)
14066 return true;
14067 return patternLines.back().dashList.size()
14068 == static_cast<std::size_t>(m_dxfPatternDashCountExpected);
14069}
14070
14071bool DRW_Hatch::validateCurrentBoundaryPathDxf() const {
14072 if (m_boundaryHandleCount != 0 || !validateCurrentSplineDxf())
14073 return false;
14074 if (!loop)
14075 return true;
14076 if (ispol) {
14077 std::shared_ptr<DRW_LWPolyline> polyline = pline;
14078 if (!polyline && loop->objlist.size() == 1)
14079 polyline = std::dynamic_pointer_cast<DRW_LWPolyline>(loop->objlist.front());
14080 if (!polyline)
14081 return false;
14082 return !m_dxfPolylineVertexCountSeen
14083 || polyline->vertlist.size()
14084 == static_cast<std::size_t>(m_dxfPolylineVertexCountExpected);
14085 }
14086 return !m_dxfLoopEdgeCountSeen
14087 || loop->objlist.size()
14088 == static_cast<std::size_t>(m_dxfLoopEdgeCountExpected);
14089}
14090
14091bool DRW_Hatch::validateDxf() const {
14092 if (!validateCurrentBoundaryPathDxf()
14093 || !validateCurrentPatternLineDxf())
14094 return false;
14095 if (m_dxfLoopCountExpected >= 0
14096 && looplist.size() != static_cast<std::size_t>(m_dxfLoopCountExpected))
14097 return false;
14098 if (m_dxfPatternLineCountExpected >= 0
14099 && patternLines.size()
14100 != static_cast<std::size_t>(m_dxfPatternLineCountExpected))
14101 return false;
14102 if (m_dxfGradientColorCountExpected >= 0
14103 && gradColors.size()
14104 != static_cast<std::size_t>(m_dxfGradientColorCountExpected))
14105 return false;
14106 return m_dxfSeedPointsExpected < 0
14107 || seedPoints.size() == static_cast<std::size_t>(m_dxfSeedPointsExpected);
14108}
14109
14110bool DRW_MPolygon::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
14111 // MPOLYGON shares HATCH's boundary/pattern/gradient codes but overloads
14112 // several of them: 70 is the version (HATCH stores it as `solid`), 71 is
14113 // the solid flag (HATCH stores it as `associative`), the after-loop 73
14114 // is annotatedBoundary, and 11/21 is the boundary x-direction vector that
14115 // DRW_Hatch would otherwise feed into a line-edge's end point. m_loopsDone
14116 // flips once the loop list is behind us so late 73/11/21 go to the trailer
14117 // instead of the current loop record.
14118 switch (code) {
14119 case 70:
14120 mpVersion = reader->getInt32();
14121 return mpVersion >= 0 && mpVersion <= 32767;
14122 case 71:
14123 solid = reader->getInt32() != 0 ? 1 : 0;
14124 return true;
14125 case 76:
14126 // Flip into "trailer" mode first; DRW_Hatch::parseCode stores hpattern.
14127 m_loopsDone = true;
14128 break;
14129 case 73:
14130 if (m_loopsDone) {
14131 annotatedBoundary = reader->getInt32();
14132 return true;
14133 }
14134 break; // loop's closed bit, let DRW_Hatch::parseCode handle it
14135 case 11:
14136 if (m_loopsDone) {
14137 xDirX = reader->getDouble();
14138 return true;
14139 }
14140 break; // line-edge end inside an open loop
14141 case 21:
14142 if (m_loopsDone) {
14143 xDirY = reader->getDouble();
14144 return true;
14145 }
14146 break;
14147 case 63:
14148 if (gradColors.empty()) { fillColorAci = reader->getInt32(); return true; }
14149 break;
14150 case 421:
14151 if (gradColors.empty()) { fillColorRgb = reader->getInt32(); return true; }
14152 break;
14153 case 430:
14154 fillColorName = reader->getUtf8String();
14155 return true;
14156 case 99:
14157 degenerateLoops = reader->getInt32();
14158 return true;
14159 default:
14160 break;
14161 }
14162 return DRW_Hatch::parseCode(code, reader);
14163}
14164
14165bool DRW_MPolygon::polylineLoop(const DRW_HatchLoop& loop, DRW_LWPolyline& out) {
14166 out.vertlist.clear();
14167 out.flags = 1; // closed
14168 // A polyline-loop record (bit 2) carries its single DRW_LWPolyline as the
14169 // only object. Copy it as-is.
14170 if ((loop.type & 2) == 2) {
14171 if (loop.objlist.empty())
14172 return false;
14173 const DRW_LWPolyline* pl = dynamic_cast<const DRW_LWPolyline*>(loop.objlist.at(0).get());
14174 if (pl == nullptr)
14175 return false;
14176 for (const std::shared_ptr<DRW_Vertex2D>& v : pl->vertlist) {
14177 if (!v)
14178 return false;
14179 out.vertlist.push_back(std::make_shared<DRW_Vertex2D>(*v));
14180 }
14181 out.flags = (pl->flags & 1) ? 1 : 0;
14182 return !out.vertlist.empty();
14183 }
14184 if (loop.objlist.empty())
14185 return false;
14186 struct Seg {
14187 DRW_Coord p;
14188 DRW_Coord q;
14189 double bulge;
14190 };
14191 std::vector<Seg> segs;
14192 segs.reserve(loop.objlist.size());
14193 double bboxMinX = 0.0, bboxMinY = 0.0, bboxMaxX = 0.0, bboxMaxY = 0.0;
14194 bool bboxInit = false;
14195 auto track = [&](const DRW_Coord& c) {
14196 if (!bboxInit) {
14197 bboxMinX = bboxMaxX = c.x;
14198 bboxMinY = bboxMaxY = c.y;
14199 bboxInit = true;
14200 } else {
14201 bboxMinX = std::min(bboxMinX, c.x);
14202 bboxMaxX = std::max(bboxMaxX, c.x);
14203 bboxMinY = std::min(bboxMinY, c.y);
14204 bboxMaxY = std::max(bboxMaxY, c.y);
14205 }
14206 };
14207 for (const auto& ownedEdge : loop.objlist) {
14208 const DRW_Entity* edge = ownedEdge.get();
14209 if (edge == nullptr)
14210 return false;
14211 if (edge->eType == DRW::LINE) {
14212 const DRW_Line* ln = static_cast<const DRW_Line*>(edge);
14213 Seg s{ln->basePoint, ln->secPoint, 0.0};
14214 track(s.p);
14215 track(s.q);
14216 segs.push_back(s);
14217 } else if (edge->eType == DRW::ARC) {
14218 // Hatch arc convention: isccw selects between (a0, a1) and
14219 // (2*pi - a0, 2*pi - a1); the sweep is signed by direction.
14220 const DRW_Arc* a = static_cast<const DRW_Arc*>(edge);
14221 const int ccw = a->isccw;
14222 double s = ccw ? a->staangle : (2.0 * M_PI3.14159265358979323846 - a->staangle);
14223 double t = ccw ? a->endangle : (2.0 * M_PI3.14159265358979323846 - a->endangle);
14224 const double rawSweep = std::fabs(a->endangle - a->staangle);
14225 double sweep;
14226 if (rawSweep >= 2.0 * M_PI3.14159265358979323846 - 1e-12) {
14227 sweep = 2.0 * M_PI3.14159265358979323846;
14228 } else {
14229 double d = ccw ? (t - s) : (s - t);
14230 while (d < 0.0) d += 2.0 * M_PI3.14159265358979323846;
14231 while (d > 2.0 * M_PI3.14159265358979323846) d -= 2.0 * M_PI3.14159265358979323846;
14232 sweep = d;
14233 }
14234 const double sign = ccw ? +1.0 : -1.0;
14235 if (std::fabs(sweep - 2.0 * M_PI3.14159265358979323846) < 1e-12) {
14236 // Represent a full circle as two half-sweep bulges.
14237 DRW_Coord ps{a->basePoint.x + a->radious * std::cos(s),
14238 a->basePoint.y + a->radious * std::sin(s),
14239 0.0};
14240 DRW_Coord mid{2.0 * a->basePoint.x - ps.x,
14241 2.0 * a->basePoint.y - ps.y, 0.0};
14242 segs.push_back({ps, mid, sign * 1.0});
14243 segs.push_back({mid, ps, sign * 1.0});
14244 track(ps);
14245 track(mid);
14246 } else {
14247 DRW_Coord ps{a->basePoint.x + a->radious * std::cos(s),
14248 a->basePoint.y + a->radious * std::sin(s),
14249 0.0};
14250 DRW_Coord qs{a->basePoint.x + a->radious * std::cos(t),
14251 a->basePoint.y + a->radious * std::sin(t),
14252 0.0};
14253 segs.push_back({ps, qs, sign * std::tan(sweep / 4.0)});
14254 track(ps);
14255 track(qs);
14256 }
14257 } else {
14258 // ELLIPSE / SPLINE / unknown: refused, as ezdxf does.
14259 return false;
14260 }
14261 }
14262 const double diag = std::hypot(bboxMaxX - bboxMinX, bboxMaxY - bboxMinY);
14263 const double tol = 1e-9 * std::max(1.0, diag);
14264 for (size_t i = 0; i < segs.size(); ++i) {
14265 const Seg& cur = segs[i];
14266 const Seg& nxt = segs[(i + 1) % segs.size()];
14267 if (std::hypot(cur.q.x - nxt.p.x, cur.q.y - nxt.p.y) > tol)
14268 return false;
14269 }
14270 for (const Seg& s : segs) {
14271 auto v = std::make_shared<DRW_Vertex2D>();
14272 v->x = s.p.x;
14273 v->y = s.p.y;
14274 v->bulge = s.bulge;
14275 out.vertlist.push_back(v);
14276 }
14277 return !out.vertlist.empty();
14278}
14279
14280// Reset MPOLYGON-only carriers alongside DRW_Hatch::resetDwgState(). Also used
14281// at the start of parseDwg and inside its fail lambda so a reused entity never
14282// publishes stale additions after a failed parse.
14283void DRW_MPolygon::resetMPolygonState() {
14284 fillColorAci = 0;
14285 fillColorRgb = -1;
14286 fillColorName.clear();
14287 xDirX = 0.0;
14288 xDirY = 0.0;
14289 degenerateLoops = 0;
14290 mpVersion = 1;
14291 annotatedBoundary = 0;
14292 m_loopsDone = false;
14293}
14294
14295// DRW_MPolygon::parseDwg — AcDbMPolygon DWG body in ODA's layout.
14296//
14297// AcDbMPolygon is HATCH-derived but has a different DWG body. The leading BS
14298// is the version (DXF 70), not a style; there is no associative bit and no
14299// second style BS after the loops; each loop is a `is_closed`/`hasBulge`/BL
14300// count/vertices record (no derived-boundary bit, no edge paths); and only
14301// the common entity handles follow — no boundary-source-handle list. The
14302// trailer is HATCH-foreign too: fill CMC, 2RD x-direction and BL degenerate
14303// path count instead of HATCH's pixel size and seed points.
14304//
14305// Decoded from ODA File Converter output: 8 AC1015 objects accounted to the
14306// RL bit size, and 24 R2004/R2007/R2010/R2018 objects (including the repo
14307// fixture) accounted to 0 bits by `mpoly_bitsize_check.py`,
14308// `mpoly_generic_decode.py` and `mpoly_r2004plus_accounting.py`.
14309bool DRW_MPolygon::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
14310 resetDwgState();
14311 resetMPolygonState();
14312 dwgBuffer *sourceBuf = buf;
14313 auto fail = [this, sourceBuf]() {
14314 if (sourceBuf != nullptr)
14315 sourceBuf->invalidate();
14316 resetDwgState();
14317 resetMPolygonState();
14318 return false;
14319 };
14320 if (sourceBuf == nullptr)
14321 return fail();
14322
14323 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
14324 dwgBuffer stringProbe = sourceBuf->forkIndependent();
14325 buf = &bodyProbe;
14326 dwgBuffer *sBuf = version > DRW::AC1018 ? &stringProbe : buf;
14327 if (!DRW_Entity::parseDwg(
14328 version, buf, version > DRW::AC1018 ? &stringProbe : nullptr, bs)
14329 || !buf->isGood() || !sBuf->isGood())
14330 return fail();
14331 DRW_DBG("\n***************************** parsing mpolygon *********************************************\n")DRW_dbg::dbg("\n***************************** parsing mpolygon *********************************************\n"
)
;
14332 const std::uint64_t bodyEndBit = dwgDataEndBit;
14333 std::uint64_t stringStartBit = bodyEndBit;
14334 std::uint64_t stringEndBit = bodyEndBit;
14335 if (version > DRW::AC1018) {
14336 stringStartBit = currentDwgBit(sBuf);
14337 if (version > DRW::AC1021) {
14338 std::uint64_t ignoredBodyEndBit = 0;
14339 if (!entityBodyDataEndBit(*sBuf, version, objSize,
14340 ignoredBodyEndBit, &stringEndBit))
14341 return fail();
14342 }
14343 }
14344
14345 // Leading BS is MPOLYGON's version (DXF code 70), not HATCH's style. ODA
14346 // writes 1; a value above 32767 would overflow DXF's unsigned-16 field, so
14347 // refuse it before taking the parse any further.
14348 std::uint16_t parsedMpVersion = 0;
14349 if (!readBoundedBitShort(*buf, bodyEndBit, parsedMpVersion))
14350 return fail();
14351 if (parsedMpVersion > 32767u)
14352 return fail();
14353 mpVersion = static_cast<int>(parsedMpVersion);
14354
14355 if (version > DRW::AC1015) { //2004+ gradient (same layout as HATCH)
14356 if (!readBoundedHatchGradient(
14357 *buf, sBuf, bodyEndBit, version, isGradient, gradReserved,
14358 gradAngle, gradShift, singleColor, gradTint, gradName,
14359 gradColors, stringEndBit))
14360 return fail();
14361 }
14362 if (!readBoundedBitDouble(*buf, bodyEndBit, basePoint.z)
14363 || !readBoundedBitCoord(*buf, bodyEndBit, extPoint)
14364 || !std::isfinite(basePoint.z)
14365 || !std::isfinite(extPoint.x)
14366 || !std::isfinite(extPoint.y)
14367 || !std::isfinite(extPoint.z))
14368 return fail();
14369 UTF8STRINGstd::string parsedName;
14370 if (version > DRW::AC1018 && stringStartBit >= stringEndBit) {
14371 parsedName.clear();
14372 } else if (!readBoundedVariableText(*sBuf, stringEndBit, version,
14373 parsedName)) {
14374 return fail();
14375 }
14376 bool parsedSolid = false;
14377 // MPOLYGON writes only the solid bit — there is no associative bit between
14378 // the name and the loop count in ODA's layout. associative stays 0.
14379 if (!sBuf->isGood()
14380 || currentDwgBit(sBuf) > stringEndBit
14381 || !readBoundedBit(*buf, bodyEndBit, parsedSolid))
14382 return fail();
14383 solid = parsedSolid;
14384 associative = 0;
14385 name = std::move(parsedName);
14386
14387 // Each MPOLYGON loop is an is_closed/hasBulge/vertex-count record; no
14388 // derived-boundary bit, no edge paths, no boundary-source-handle count.
14389 std::uint32_t parsedLoopCount = 0;
14390 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 4,
14391 parsedLoopCount))
14392 return fail();
14393 if (!DRW::reserve(looplist, parsedLoopCount))
14394 return fail();
14395 loopsnum = static_cast<std::int32_t>(parsedLoopCount);
14396 for (std::uint32_t i = 0; i < parsedLoopCount; ++i) {
14397 bool closed = false;
14398 bool asBulge = false;
14399 if (!proxyEntityHasBits(*buf, bodyEndBit, 2)
14400 || !readBoundedBit(*buf, bodyEndBit, closed)
14401 || !readBoundedBit(*buf, bodyEndBit, asBulge))
14402 return fail();
14403 std::uint32_t numVert = 0;
14404 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems,
14405 asBulge ? 130u : 128u, numVert))
14406 return fail();
14407 auto polyline = std::make_shared<DRW_LWPolyline>();
14408 polyline->flags = closed ? 1 : 0;
14409 polyline->vertexnum = static_cast<int>(numVert);
14410 for (std::uint32_t j = 0; j < numVert; ++j) {
14411 DRW_Vertex2D v;
14412 if (!readBoundedRawDouble(*buf, bodyEndBit, v.x)
14413 || !readBoundedRawDouble(*buf, bodyEndBit, v.y)
14414 || !std::isfinite(v.x) || !std::isfinite(v.y))
14415 return fail();
14416 if (asBulge) {
14417 if (!readBoundedBitDouble(*buf, bodyEndBit, v.bulge)
14418 || !std::isfinite(v.bulge))
14419 return fail();
14420 }
14421 polyline->addVertex(v);
14422 }
14423 auto hatchLoop = std::make_shared<DRW_HatchLoop>(2); // polyline path
14424 hatchLoop->objlist.push_back(polyline);
14425 hatchLoop->update();
14426 looplist.push_back(hatchLoop);
14427 }
14428
14429 std::uint16_t parsedPattern = 0;
14430 if (!readBoundedBitShort(*buf, bodyEndBit, parsedPattern))
14431 return fail();
14432 hpattern = parsedPattern;
14433 hstyle = 0; // MPOLYGON has no second style BS; HATCH's second style is absent.
14434
14435 if (!solid) {
14436 double parsedAngle = 0.0;
14437 double parsedScale = 0.0;
14438 bool parsedDoubleFlag = false;
14439 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedAngle)
14440 || !readBoundedBitDouble(*buf, bodyEndBit, parsedScale)
14441 || !readBoundedBit(*buf, bodyEndBit, parsedDoubleFlag)
14442 || !std::isfinite(parsedAngle)
14443 || !std::isfinite(parsedScale))
14444 return fail();
14445 angle = parsedAngle;
14446 scale = parsedScale;
14447 doubleflag = parsedDoubleFlag;
14448 std::uint32_t parsedDeflines = 0;
14449 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 12,
14450 parsedDeflines))
14451 return fail();
14452 if (!DRW::reserve(patternLines, parsedDeflines))
14453 return fail();
14454 deflines = static_cast<int>(parsedDeflines);
14455 for (std::uint32_t i = 0; i < parsedDeflines; ++i) {
14456 PatternLine patternLine;
14457 if (!readBoundedBitDouble(*buf, bodyEndBit, patternLine.angle)
14458 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.baseX)
14459 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.baseY)
14460 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.offsetX)
14461 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.offsetY)
14462 || !std::isfinite(patternLine.angle)
14463 || !std::isfinite(patternLine.baseX)
14464 || !std::isfinite(patternLine.baseY)
14465 || !std::isfinite(patternLine.offsetX)
14466 || !std::isfinite(patternLine.offsetY))
14467 return fail();
14468 std::uint32_t numDashL = 0;
14469 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 2,
14470 numDashL)
14471 || !proxyEntityHasBits(
14472 *buf, bodyEndBit, static_cast<std::uint64_t>(numDashL) * 2u)
14473 || !DRW::reserve(patternLine.dashList, numDashL))
14474 return fail();
14475 for (std::uint32_t d = 0; d < numDashL; ++d) {
14476 double dash = 0.0;
14477 if (!readBoundedBitDouble(*buf, bodyEndBit, dash)
14478 || !std::isfinite(dash))
14479 return fail();
14480 patternLine.dashList.push_back(dash);
14481 }
14482 patternLines.push_back(std::move(patternLine));
14483 }
14484 }
14485
14486 // MPOLYGON trailer (differs from HATCH): fill CMC + x-direction + degenerate
14487 // path count. No pixel size / seed points here.
14488 std::int32_t rgb = -1;
14489 UTF8STRINGstd::string colName;
14490 std::uint32_t parsedFillColor = 0;
14491 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version,
14492 parsedFillColor, &rgb, nullptr, &colName))
14493 return fail();
14494 fillColorAci = static_cast<int>(parsedFillColor);
14495 fillColorRgb = rgb;
14496 fillColorName = colName;
14497 DRW_Coord xdir;
14498 if (!readBounded2RawDouble(*buf, bodyEndBit, xdir)
14499 || !std::isfinite(xdir.x) || !std::isfinite(xdir.y))
14500 return fail();
14501 xDirX = xdir.x;
14502 xDirY = xdir.y;
14503 std::uint32_t parsedDegenerateLoops = 0;
14504 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 0,
14505 parsedDegenerateLoops))
14506 return fail();
14507 degenerateLoops = static_cast<std::int32_t>(parsedDegenerateLoops);
14508
14509 // MPOLYGON carries only the common entity handles; HATCH's trailing
14510 // boundary-source-handle list is absent.
14511 std::uint64_t handleEndBit = 0;
14512 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
14513 handleEndBit))
14514 return fail();
14515 dwgBuffer handleProbe = buf->forkIndependent();
14516 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
14517 handleEndBit)
14518 || !handleProbe.isGood())
14519 return fail();
14520
14521 *sourceBuf = handleProbe;
14522 return true;
14523}
14524
14525// Shared DWG boundary-loop reader for HATCH and MPOLYGON (ODA §20.4.36).
14526// Reads the loop count and, per loop, the derived-boundary flag plus its edge
14527// list or polyline. Accumulates the running boundary-source-handle total and
14528// whether any loop is derived (needs a trailing pixel size). Extracted from
14529// DRW_Hatch::parseDwg so DRW_MPolygon::parseDwg reuses the identical body while
14530// supplying its own differing leading (BS style) and trailing (fill CMC +
14531// x-direction + degenerate count) field order.
14532bool validateDwgHatchData(const DRW_Hatch& hatch) {
14533 if (hatch.looplist.size() > static_cast<std::size_t>(kMaxHatchItems)
14534 || hatch.patternLines.size() > static_cast<std::size_t>(kMaxHatchItems)
14535 || hatch.gradColors.size() > static_cast<std::size_t>(kMaxHatchItems)
14536 || hatch.seedPoints.size() > static_cast<std::size_t>(kMaxHatchItems))
14537 return false;
14538
14539 for (const auto& loop : hatch.looplist) {
14540 if (!loop
14541 || loop->objlist.size() > static_cast<std::size_t>(kMaxHatchItems)
14542 || loop->m_boundaryHandles.size() > static_cast<std::size_t>(kMaxHatchItems))
14543 return false;
14544
14545 if ((loop->type & 2) != 0) {
14546 if (loop->objlist.size() != 1)
14547 return false;
14548 const auto* polyline =
14549 dynamic_cast<const DRW_LWPolyline*>(loop->objlist.front().get());
14550 if (!polyline || polyline->vertlist.size() > static_cast<std::size_t>(kMaxHatchItems))
14551 return false;
14552 for (const auto& vertex : polyline->vertlist) {
14553 if (!vertex)
14554 return false;
14555 }
14556 continue;
14557 }
14558
14559 for (const auto& entity : loop->objlist) {
14560 if (dynamic_cast<const DRW_Line*>(entity.get())
14561 || dynamic_cast<const DRW_Arc*>(entity.get())
14562 || dynamic_cast<const DRW_Ellipse*>(entity.get())) {
14563 continue;
14564 }
14565 const auto* spline = dynamic_cast<const DRW_Spline*>(entity.get());
14566 if (!spline
14567 || spline->knotslist.size() > static_cast<std::size_t>(kMaxHatchItems)
14568 || spline->controllist.size() > static_cast<std::size_t>(kMaxHatchItems)
14569 || spline->fitlist.size() > static_cast<std::size_t>(kMaxHatchItems))
14570 return false;
14571 for (const auto& point : spline->controllist) {
14572 if (!point)
14573 return false;
14574 }
14575 for (const auto& point : spline->fitlist) {
14576 if (!point)
14577 return false;
14578 }
14579 }
14580 }
14581
14582 for (const auto& line : hatch.patternLines) {
14583 if (line.dashList.size() > static_cast<std::size_t>(kMaxHatchItems))
14584 return false;
14585 }
14586 return true;
14587}
14588
14589bool DRW_MPolygon::encodeDwg(DRW::Version version, dwgBufferW *buf,
14590 std::uint32_t bs, dwgBufferW *strBuf,
14591 dwgBufferW *handleBuf) {
14592 (void)bs;
14593 if (!validateDwgHatchData(*this)
14594 || !isValidCount(degenerateLoops, kMaxHatchItems)
14595 || mpVersion < 0 || mpVersion > 32767
14596 || annotatedBoundary != 0)
14597 return false;
14598 // Every loop must flatten into a bulge polyline (LINE / ARC only); refuse
14599 // ellipse, spline or any other edge up front so the file stays loss-free.
14600 for (const auto& loop : looplist) {
14601 if (!loop)
14602 return false;
14603 DRW_LWPolyline probe;
14604 if (!polylineLoop(*loop, probe))
14605 return false;
14606 }
14607 oType = kDwgClassNum;
14608 if (!encodeDwgCommon(version, buf, strBuf))
14609 return false;
14610
14611 dwgBufferW *sb = strBuf ? strBuf : buf;
14612
14613 // AcDbMPolygon writes the version BS first (DXF code 70), not a style.
14614 buf->putBitShort(static_cast<std::uint16_t>(mpVersion));
14615 encodeDwgGradientData(version, buf, sb);
14616
14617 buf->putBitDouble(basePoint.z);
14618 buf->put3BitDouble(extPoint);
14619 sb->putVariableText(version, name);
14620 buf->putBit(static_cast<std::uint8_t>(solid));
14621 // No associative bit: MPOLYGON is never associative in the ODA layout.
14622
14623 // Each loop writes as is_closed/hasBulge/vertex-count/vertices; no derived
14624 // bit, no edge paths, no boundary-source-handle count.
14625 buf->putBitLong(static_cast<std::int32_t>(looplist.size()));
14626 for (const auto& loop : looplist) {
14627 DRW_LWPolyline polyline;
14628 if (!polylineLoop(*loop, polyline))
14629 return false;
14630 const bool closed = (polyline.flags & 1) != 0;
14631 bool asBulge = false;
14632 for (const auto& v : polyline.vertlist) {
14633 if (v && v->bulge != 0.0) { asBulge = true; break; }
14634 }
14635 buf->putBit(static_cast<std::uint8_t>(closed));
14636 buf->putBit(static_cast<std::uint8_t>(asBulge));
14637 buf->putBitLong(static_cast<std::int32_t>(polyline.vertlist.size()));
14638 for (const auto& v : polyline.vertlist) {
14639 buf->putRawDouble(v->x);
14640 buf->putRawDouble(v->y);
14641 if (asBulge)
14642 buf->putBitDouble(v->bulge);
14643 }
14644 }
14645
14646 buf->putBitShort(static_cast<std::uint16_t>(hpattern));
14647 // No second style BS: hstyle is held locally at 0 and never emitted again.
14648
14649 if (!solid) {
14650 buf->putBitDouble(angle);
14651 buf->putBitDouble(scale);
14652 buf->putBit(static_cast<std::uint8_t>(doubleflag));
14653 buf->putBitShort(static_cast<std::uint16_t>(patternLines.size()));
14654 for (const PatternLine& pl : patternLines) {
14655 buf->putBitDouble(pl.angle);
14656 buf->putBitDouble(pl.baseX);
14657 buf->putBitDouble(pl.baseY);
14658 buf->putBitDouble(pl.offsetX);
14659 buf->putBitDouble(pl.offsetY);
14660 buf->putBitShort(static_cast<std::uint16_t>(pl.dashList.size()));
14661 for (double dash : pl.dashList)
14662 buf->putBitDouble(dash);
14663 }
14664 }
14665
14666 buf->putCmColor(version,
14667 static_cast<std::uint16_t>(fillColorAci),
14668 fillColorRgb,
14669 fillColorName,
14670 {},
14671 sb);
14672 buf->put2RawDouble(DRW_Coord{xDirX, xDirY, 0.0});
14673 buf->putBitLong(degenerateLoops);
14674
14675 return encodeDwgEntHandle(version, buf, handleBuf);
14676}
14677
14678bool DRW_Hatch::parseDwgBoundaryData(DRW::Version version, dwgBuffer *buf,
14679 std::uint32_t &totalBoundItems, bool &havePixelSize) {
14680 try {
14681 const std::uint64_t bodyEndBit = dwgDataEndBit;
14682 std::uint32_t parsedLoopCount = 0;
14683 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 4,
14684 parsedLoopCount))
14685 return false;
14686 if (!DRW::reserve(looplist, parsedLoopCount))
14687 return false;
14688 loopsnum = static_cast<std::int32_t>(parsedLoopCount);
14689 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);
14690 DRW_DBG(" loopsnum: ")DRW_dbg::dbg(" loopsnum: "); DRW_DBG(loopsnum)DRW_dbg::dbg(loopsnum); DRW_DBG("\n")DRW_dbg::dbg("\n");
14691
14692 //read loops
14693 for (std::uint32_t i = 0 ; i < parsedLoopCount; ++i){
14694 std::int32_t loopType = 0;
14695 if (!readBoundedBitLong(*buf, bodyEndBit, loopType)
14696 || loopType < 0)
14697 return false;
14698 loop = std::make_shared<DRW_HatchLoop>(loopType);
14699 havePixelSize = havePixelSize || ((loop->type & 4) != 0);
14700 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);
14701 if (!(loop->type & 2)){ //Not polyline
14702 std::uint32_t numPathSeg = 0;
14703 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 8,
14704 numPathSeg))
14705 return false;
14706 if (!DRW::reserve(loop->objlist, numPathSeg))
14707 return false;
14708 DRW_DBG(" numPathSeg: ")DRW_dbg::dbg(" numPathSeg: "); DRW_DBG(numPathSeg)DRW_dbg::dbg(numPathSeg); DRW_DBG("\n")DRW_dbg::dbg("\n");
14709 for (std::uint32_t j = 0; j<numPathSeg;++j){
14710 std::uint8_t typePath = 0;
14711 if (!readBoundedRawChar8(*buf, bodyEndBit, typePath)
14712 || typePath < 1 || typePath > 4)
14713 return false;
14714 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");
14715 if (typePath == 1){ //line
14716 DRW_Coord basePoint;
14717 DRW_Coord secPoint;
14718 if (!readBounded2RawDouble(*buf, bodyEndBit, basePoint)
14719 || !readBounded2RawDouble(*buf, bodyEndBit, secPoint))
14720 return false;
14721 addLine();
14722 line->basePoint = basePoint;
14723 line->secPoint = secPoint;
14724 } else if (typePath == 2){ //circle arc
14725 DRW_Coord basePoint;
14726 double radius = 0.0;
14727 double startAngle = 0.0;
14728 double endAngle = 0.0;
14729 bool isCcw = false;
14730 if (!readBounded2RawDouble(*buf, bodyEndBit, basePoint)
14731 || !readBoundedBitDouble(*buf, bodyEndBit, radius)
14732 || !readBoundedBitDouble(*buf, bodyEndBit, startAngle)
14733 || !readBoundedBitDouble(*buf, bodyEndBit, endAngle)
14734 || !readBoundedBit(*buf, bodyEndBit, isCcw)
14735 || !std::isfinite(basePoint.x)
14736 || !std::isfinite(basePoint.y)
14737 || !std::isfinite(radius)
14738 || !std::isfinite(startAngle)
14739 || !std::isfinite(endAngle))
14740 return false;
14741 addArc();
14742 arc->basePoint = basePoint;
14743 arc->radious = radius;
14744 arc->staangle = startAngle;
14745 arc->endangle = endAngle;
14746 arc->isccw = isCcw;
14747 } else if (typePath == 3){ //ellipse arc
14748 DRW_Coord basePoint;
14749 DRW_Coord secPoint;
14750 double ratio = 0.0;
14751 double startParam = 0.0;
14752 double endParam = 0.0;
14753 bool isCcw = false;
14754 if (!readBounded2RawDouble(*buf, bodyEndBit, basePoint)
14755 || !readBounded2RawDouble(*buf, bodyEndBit, secPoint)
14756 || !readBoundedBitDouble(*buf, bodyEndBit, ratio)
14757 || !readBoundedBitDouble(*buf, bodyEndBit, startParam)
14758 || !readBoundedBitDouble(*buf, bodyEndBit, endParam)
14759 || !readBoundedBit(*buf, bodyEndBit, isCcw)
14760 || !std::isfinite(basePoint.x)
14761 || !std::isfinite(basePoint.y)
14762 || !std::isfinite(secPoint.x)
14763 || !std::isfinite(secPoint.y)
14764 || !std::isfinite(ratio)
14765 || !std::isfinite(startParam)
14766 || !std::isfinite(endParam))
14767 return false;
14768 addEllipse();
14769 ellipse->basePoint = basePoint;
14770 ellipse->secPoint = secPoint;
14771 ellipse->ratio = ratio;
14772 ellipse->staparam = startParam;
14773 ellipse->endparam = endParam;
14774 ellipse->isccw = isCcw;
14775 } else if (typePath == 4){ //spline
14776 std::int32_t degree = 0;
14777 bool isRational = false;
14778 bool isPeriodic = false;
14779 if (!readBoundedBitLong(*buf, bodyEndBit, degree)
14780 || !isValidSplineDegree(degree)
14781 || !readBoundedBit(*buf, bodyEndBit, isRational)
14782 || !readBoundedBit(*buf, bodyEndBit, isPeriodic))
14783 return false;
14784 std::uint32_t knotCount = 0;
14785 if (!readTableBodyCount(version, buf, objSize,
14786 kMaxHatchItems, 2, knotCount)) {
14787 return false;
14788 }
14789 std::uint32_t controlCount = 0;
14790 if (!readTableBodyCount(version, buf, objSize,
14791 kMaxHatchItems, 128, controlCount)) {
14792 return false;
14793 }
14794 DRW_Spline parsedSpline;
14795 parsedSpline.degree = degree;
14796 parsedSpline.nknots = static_cast<std::int32_t>(knotCount);
14797 parsedSpline.ncontrol = static_cast<std::int32_t>(controlCount);
14798 parsedSpline.flags = (isRational ? 4 : 0)
14799 | (isPeriodic ? 2 : 0);
14800 if (!DRW::reserve(parsedSpline.knotslist, knotCount)
14801 || !DRW::reserve(parsedSpline.controllist, controlCount))
14802 return false;
14803 for (std::uint32_t j = 0; j < knotCount;++j){
14804 double knot = 0.0;
14805 if (!readBoundedBitDouble(*buf, bodyEndBit, knot)
14806 || !std::isfinite(knot))
14807 return false;
14808 parsedSpline.knotslist.push_back(knot);
14809 }
14810 for (std::uint32_t j = 0; j < controlCount;++j){
14811 DRW_Coord control;
14812 if (!readBounded2RawDouble(*buf, bodyEndBit, control)
14813 || !std::isfinite(control.x)
14814 || !std::isfinite(control.y))
14815 return false;
14816 if (isRational) {
14817 if (!readBoundedBitDouble(*buf, bodyEndBit, control.z)
14818 || !std::isfinite(control.z))
14819 return false;
14820 }
14821 parsedSpline.controllist.push_back(
14822 std::make_shared<DRW_Coord>(control));
14823 }
14824 if (version > DRW::AC1021) { //2010+
14825 std::uint32_t fitCount = 0;
14826 if (!readTableBodyCount(version, buf, objSize,
14827 kMaxHatchItems, 128, fitCount))
14828 return false;
14829 parsedSpline.nfit = static_cast<std::int32_t>(fitCount);
14830 // Fit points AND the start/end tangents are present only
14831 // when nfit > 0 (matches ACadSharp's `if (nfitPoints > 0)`).
14832 // Reading the two tangents unconditionally on an nfit==0
14833 // spline edge over-runs the entity body and fails the parse
14834 // (e.g. svg/export_sample.dwg: a degree-3 non-rational
14835 // spline boundary edge with 9 control points / 0 fit points).
14836 if (parsedSpline.nfit > 0) {
14837 if (!DRW::reserve(parsedSpline.fitlist, fitCount)) {
14838 return false;
14839 }
14840 for (std::uint32_t j = 0; j < fitCount;++j){
14841 DRW_Coord fit;
14842 if (!readBounded2RawDouble(*buf, bodyEndBit, fit)
14843 || !std::isfinite(fit.x)
14844 || !std::isfinite(fit.y))
14845 return false;
14846 parsedSpline.fitlist.push_back(
14847 std::make_shared<DRW_Coord>(fit));
14848 }
14849 if (!readBounded2RawDouble(*buf, bodyEndBit,
14850 parsedSpline.tgStart)
14851 || !readBounded2RawDouble(*buf, bodyEndBit,
14852 parsedSpline.tgEnd)
14853 || !std::isfinite(parsedSpline.tgStart.x)
14854 || !std::isfinite(parsedSpline.tgStart.y)
14855 || !std::isfinite(parsedSpline.tgEnd.x)
14856 || !std::isfinite(parsedSpline.tgEnd.y))
14857 return false;
14858 }
14859 }
14860 addSpline();
14861 *spline = std::move(parsedSpline);
14862 }
14863 }
14864 } else { //end not pline, start polyline
14865 if (!proxyEntityHasBits(*buf, bodyEndBit, 2))
14866 return false;
14867 bool asBulge = false;
14868 bool closed = false;
14869 if (!readBoundedBit(*buf, bodyEndBit, asBulge)
14870 || !readBoundedBit(*buf, bodyEndBit, closed))
14871 return false;
14872 std::uint32_t numVert = 0;
14873 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems,
14874 128, numVert))
14875 return false;
14876 if (!DRW::reserve(loop->objlist, 1))
14877 return false;
14878 DRW_DBG(" asBulge: ")DRW_dbg::dbg(" asBulge: "); DRW_DBG(asBulge)DRW_dbg::dbg(asBulge); DRW_DBG(" closed: ")DRW_dbg::dbg(" closed: "); DRW_DBG(closed)DRW_dbg::dbg(closed);
14879 DRW_DBG(" numVert: ")DRW_dbg::dbg(" numVert: "); DRW_DBG(numVert)DRW_dbg::dbg(numVert); DRW_DBG("\n")DRW_dbg::dbg("\n");
14880 std::vector<DRW_Vertex2D> vertices;
14881 if (!DRW::reserve(vertices, numVert))
14882 return false;
14883 for (std::uint32_t j = 0; j<numVert;++j){
14884 DRW_Vertex2D v;
14885 if (!readBoundedRawDouble(*buf, bodyEndBit, v.x)
14886 || !readBoundedRawDouble(*buf, bodyEndBit, v.y)
14887 || !std::isfinite(v.x) || !std::isfinite(v.y))
14888 return false;
14889 if (asBulge
14890 && (!readBoundedBitDouble(*buf, bodyEndBit, v.bulge)
14891 || !std::isfinite(v.bulge)))
14892 return false;
14893 vertices.push_back(v);
14894 }
14895 pline = std::make_shared<DRW_LWPolyline>();
14896 pline->flags = closed;
14897 for (const DRW_Vertex2D& vertex : vertices)
14898 pline->addVertex(vertex);
14899 loop->objlist.push_back(pline);
14900 }//end polyline
14901 loop->update();
14902 std::uint32_t boundaryHandleCount = 0;
14903 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 0,
14904 boundaryHandleCount)
14905 || totalBoundItems > kMaxHatchItems - boundaryHandleCount)
14906 return false;
14907 looplist.push_back(loop);
14908 totalBoundItems += boundaryHandleCount;
14909 DRW_DBG(" totalBoundItems: ")DRW_dbg::dbg(" totalBoundItems: "); DRW_DBG(totalBoundItems)DRW_dbg::dbg(totalBoundItems);
14910 } //end read loops
14911 return buf->isGood();
14912 } catch (...) {
14913 return false;
14914 }
14915}
14916
14917bool DRW_Hatch::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
14918 resetDwgState();
14919 dwgBuffer *sourceBuf = buf;
14920 auto fail = [this, sourceBuf]() {
14921 if (sourceBuf != nullptr)
14922 sourceBuf->invalidate();
14923 resetDwgState();
14924 return false;
14925 };
14926 if (sourceBuf == nullptr)
14927 return fail();
14928
14929 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
14930 dwgBuffer stringProbe = sourceBuf->forkIndependent();
14931 buf = &bodyProbe;
14932 dwgBuffer *sBuf = version > DRW::AC1018 ? &stringProbe : buf;
14933 std::uint32_t totalBoundItems = 0;
14934 bool havePixelSize = false;
14935
14936 bool ret = DRW_Entity::parseDwg(
14937 version, buf, version > DRW::AC1018 ? &stringProbe : nullptr, bs);
14938 if (!ret || !buf->isGood() || !sBuf->isGood())
14939 return fail();
14940 DRW_DBG("\n***************************** parsing hatch *********************************************\n")DRW_dbg::dbg("\n***************************** parsing hatch *********************************************\n"
)
;
14941 const std::uint64_t bodyEndBit = dwgDataEndBit;
14942 std::uint64_t stringStartBit = bodyEndBit;
14943 std::uint64_t stringEndBit = bodyEndBit;
14944 if (version > DRW::AC1018) {
14945 stringStartBit = currentDwgBit(sBuf);
14946 if (version > DRW::AC1021) {
14947 std::uint64_t ignoredBodyEndBit = 0;
14948 if (!entityBodyDataEndBit(*sBuf, version, objSize,
14949 ignoredBodyEndBit, &stringEndBit))
14950 return fail();
14951 }
14952 }
14953
14954 if (version > DRW::AC1015) { //2004+
14955 if (!readBoundedHatchGradient(
14956 *buf, sBuf, bodyEndBit, version, isGradient, gradReserved,
14957 gradAngle, gradShift, singleColor, gradTint, gradName,
14958 gradColors, stringEndBit))
14959 return fail();
14960 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");
14961 }
14962 DRW_Coord parsedBasePoint = basePoint;
14963 DRW_Coord parsedExtPoint;
14964 UTF8STRINGstd::string parsedName;
14965 bool parsedSolid = false;
14966 bool parsedAssociative = false;
14967 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedBasePoint.z)
14968 || !readBoundedBitCoord(*buf, bodyEndBit, parsedExtPoint)
14969 || !std::isfinite(parsedBasePoint.z)
14970 || !std::isfinite(parsedExtPoint.x)
14971 || !std::isfinite(parsedExtPoint.y)
14972 || !std::isfinite(parsedExtPoint.z))
14973 return fail();
14974 DRW_DBG("base point: ")DRW_dbg::dbg("base point: "); DRW_DBGPT(parsedBasePoint.x, parsedBasePoint.y,DRW_dbg::dbgPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint
.z)
14975 parsedBasePoint.z)DRW_dbg::dbgPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint
.z)
;
14976 DRW_DBG("\nextrusion: ")DRW_dbg::dbg("\nextrusion: "); DRW_DBGPT(parsedExtPoint.x, parsedExtPoint.y,DRW_dbg::dbgPT(parsedExtPoint.x, parsedExtPoint.y, parsedExtPoint
.z)
14977 parsedExtPoint.z)DRW_dbg::dbgPT(parsedExtPoint.x, parsedExtPoint.y, parsedExtPoint
.z)
;
14978 if (version > DRW::AC1018 && stringStartBit >= stringEndBit) {
14979 parsedName.clear();
14980 } else if (!readBoundedVariableText(*sBuf, stringEndBit, version,
14981 parsedName)) {
14982 return fail();
14983 }
14984 if (!sBuf->isGood()
14985 || currentDwgBit(sBuf) > stringEndBit
14986 || !readBoundedBit(*buf, bodyEndBit, parsedSolid)
14987 || !readBoundedBit(*buf, bodyEndBit, parsedAssociative))
14988 return fail();
14989 basePoint = parsedBasePoint;
14990 extPoint = parsedExtPoint;
14991 name = std::move(parsedName);
14992 solid = parsedSolid;
14993 associative = parsedAssociative;
14994 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");
14995 if (!parseDwgBoundaryData(version, buf, totalBoundItems, havePixelSize))
14996 return fail();
14997
14998 std::uint16_t parsedStyle = 0;
14999 std::uint16_t parsedPattern = 0;
15000 if (!readBoundedBitShort(*buf, bodyEndBit, parsedStyle)
15001 || !readBoundedBitShort(*buf, bodyEndBit, parsedPattern))
15002 return fail();
15003 hstyle = parsedStyle;
15004 hpattern = parsedPattern;
15005 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);
15006 if (!solid){
15007 double parsedAngle = 0.0;
15008 double parsedScale = 0.0;
15009 bool parsedDoubleFlag = false;
15010 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedAngle)
15011 || !readBoundedBitDouble(*buf, bodyEndBit, parsedScale)
15012 || !readBoundedBit(*buf, bodyEndBit, parsedDoubleFlag)
15013 || !std::isfinite(parsedAngle)
15014 || !std::isfinite(parsedScale))
15015 return fail();
15016 angle = parsedAngle;
15017 scale = parsedScale;
15018 doubleflag = parsedDoubleFlag;
15019 std::uint32_t parsedDeflines = 0;
15020 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 12,
15021 parsedDeflines))
15022 return fail();
15023 deflines = static_cast<int>(parsedDeflines);
15024 for (std::uint32_t i = 0 ; i < parsedDeflines; ++i){
15025 PatternLine patternLine;
15026 if (!readBoundedBitDouble(*buf, bodyEndBit, patternLine.angle)
15027 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.baseX)
15028 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.baseY)
15029 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.offsetX)
15030 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.offsetY)
15031 || !std::isfinite(patternLine.angle)
15032 || !std::isfinite(patternLine.baseX)
15033 || !std::isfinite(patternLine.baseY)
15034 || !std::isfinite(patternLine.offsetX)
15035 || !std::isfinite(patternLine.offsetY))
15036 return fail();
15037 std::uint32_t numDashL = 0;
15038 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 2,
15039 numDashL)
15040 || !proxyEntityHasBits(
15041 *buf, bodyEndBit, static_cast<std::uint64_t>(numDashL) * 2u)
15042 || !DRW::reserve(patternLine.dashList, numDashL))
15043 return fail();
15044 DRW_DBG("\ndef line: ")DRW_dbg::dbg("\ndef line: "); DRW_DBG(patternLine.angle)DRW_dbg::dbg(patternLine.angle); DRW_DBG(",")DRW_dbg::dbg(",");
15045 DRW_DBG(patternLine.baseX)DRW_dbg::dbg(patternLine.baseX); DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(patternLine.baseY)DRW_dbg::dbg(patternLine.baseY);
15046 DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(patternLine.offsetX)DRW_dbg::dbg(patternLine.offsetX); DRW_DBG(",")DRW_dbg::dbg(",");
15047 DRW_DBG(patternLine.offsetY)DRW_dbg::dbg(patternLine.offsetY);
15048 for (std::uint32_t i = 0 ; i < numDashL; ++i){
15049 double lengthL = 0.0;
15050 if (!readBoundedBitDouble(*buf, bodyEndBit, lengthL)
15051 || !std::isfinite(lengthL))
15052 return fail();
15053 patternLine.dashList.push_back(lengthL);
15054 DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(lengthL)DRW_dbg::dbg(lengthL);
15055 }
15056 patternLines.push_back(std::move(patternLine));
15057 }//end deflines
15058 } //end not solid
15059
15060 if (havePixelSize){
15061 if (!readBoundedBitDouble(*buf, bodyEndBit, pixelSize)
15062 || !std::isfinite(pixelSize))
15063 return fail();
15064 DRW_DBG("\npixel size: ")DRW_dbg::dbg("\npixel size: "); DRW_DBG(pixelSize)DRW_dbg::dbg(pixelSize);
15065 }
15066 std::uint32_t numSeedPoints = 0;
15067 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 128,
15068 numSeedPoints)
15069 || !proxyEntityHasBits(
15070 *buf, bodyEndBit, static_cast<std::uint64_t>(numSeedPoints) * 128u)
15071 || !DRW::reserve(seedPoints, numSeedPoints))
15072 return fail();
15073 DRW_DBG("\nnum Seed Points ")DRW_dbg::dbg("\nnum Seed Points "); DRW_DBG(numSeedPoints)DRW_dbg::dbg(numSeedPoints);
15074 for (std::uint32_t i = 0 ; i < numSeedPoints; ++i){
15075 DRW_Coord seedPt;
15076 if (!readBounded2RawDouble(*buf, bodyEndBit, seedPt)
15077 || !std::isfinite(seedPt.x)
15078 || !std::isfinite(seedPt.y))
15079 return fail();
15080 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);
15081 seedPoints.push_back(seedPt);
15082 }
15083
15084 DRW_DBG("\n")DRW_dbg::dbg("\n");
15085 std::uint64_t handleEndBit = 0;
15086 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
15087 handleEndBit))
15088 return fail();
15089 dwgBuffer handleProbe = buf->forkIndependent();
15090 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
15091 handleEndBit);
15092 if (!ret || !handleProbe.isGood())
15093 return fail();
15094 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
15095
15096 for (std::uint32_t i = 0 ; i < totalBoundItems; ++i){
15097 dwgHandle biH;
15098 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
15099 biH))
15100 return fail();
15101 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);
15102 }
15103 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
15104// RS crc; //RS */
15105 if (!handleProbe.isGood())
15106 return fail();
15107 *sourceBuf = handleProbe;
15108 return true;
15109}
15110
15111void DRW_Hatch::encodeDwgGradientData(DRW::Version version, dwgBufferW *buf,
15112 dwgBufferW *strBuf) const {
15113 if (version <= DRW::AC1015)
8
Assuming 'version' is > AC1015
15114 return;
15115
15116 dwgBufferW *sb = strBuf ? strBuf : buf;
9
Taking false branch
10
Assuming 'strBuf' is null
11
'?' condition is false
15117 buf->putBitLong(isGradient);
12
Called C++ object pointer is null
15118 buf->putBitLong(gradReserved);
15119 buf->putBitDouble(gradAngle);
15120 buf->putBitDouble(gradShift);
15121 buf->putBitLong(singleColor);
15122 buf->putBitDouble(gradTint);
15123 buf->putBitLong(static_cast<std::int32_t>(gradColors.size()));
15124 for (const GradientStop& stop : gradColors) {
15125 buf->putBitDouble(stop.value);
15126 buf->putBitShort(static_cast<std::uint16_t>(stop.aciColor));
15127 buf->putBitLong(static_cast<std::uint32_t>(stop.rgb));
15128 buf->putRawChar8(0);
15129 }
15130 sb->putVariableText(version, gradName);
15131}
15132
15133bool DRW_Hatch::encodeDwgBoundaryData(DRW::Version version, dwgBufferW *buf,
15134 bool includePixelSize) const {
15135 buf->putBitLong(static_cast<std::int32_t>(looplist.size()));
15136
15137 for (const auto& lp : looplist) {
15138 buf->putBitLong(includePixelSize ? lp->type : lp->type & ~4);
15139
15140 if (!(lp->type & 2)) {
15141 buf->putBitLong(static_cast<std::int32_t>(lp->objlist.size()));
15142 for (const auto& seg : lp->objlist) {
15143 if (const auto* ln = dynamic_cast<const DRW_Line*>(seg.get())) {
15144 buf->putRawChar8(1); // line
15145 buf->put2RawDouble(ln->basePoint);
15146 buf->put2RawDouble(ln->secPoint);
15147 } else if (const auto* arc = dynamic_cast<const DRW_Arc*>(seg.get())) {
15148 buf->putRawChar8(2); // circular arc
15149 buf->put2RawDouble(arc->basePoint);
15150 buf->putBitDouble(arc->radious);
15151 buf->putBitDouble(arc->staangle);
15152 buf->putBitDouble(arc->endangle);
15153 buf->putBit(static_cast<std::uint8_t>(arc->isccw));
15154 } else if (const auto* el = dynamic_cast<const DRW_Ellipse*>(seg.get())) {
15155 buf->putRawChar8(3); // ellipse arc
15156 buf->put2RawDouble(el->basePoint);
15157 buf->put2RawDouble(el->secPoint);
15158 buf->putBitDouble(el->ratio);
15159 buf->putBitDouble(el->staparam);
15160 buf->putBitDouble(el->endparam);
15161 buf->putBit(static_cast<std::uint8_t>(el->isccw));
15162 } else if (const auto* sp = dynamic_cast<const DRW_Spline*>(seg.get())) {
15163 buf->putRawChar8(4); // spline
15164 buf->putBitLong(sp->degree);
15165 bool isRational = (sp->flags & 4) != 0;
15166 bool isPeriodic = (sp->flags & 2) != 0;
15167 buf->putBit(static_cast<std::uint8_t>(isRational));
15168 buf->putBit(static_cast<std::uint8_t>(isPeriodic));
15169 buf->putBitLong(static_cast<std::int32_t>(sp->knotslist.size()));
15170 buf->putBitLong(static_cast<std::int32_t>(sp->controllist.size()));
15171 for (double k : sp->knotslist)
15172 buf->putBitDouble(k);
15173 for (const auto& cp : sp->controllist) {
15174 DRW_Coord c2{cp->x, cp->y, 0.0};
15175 buf->put2RawDouble(c2);
15176 if (isRational)
15177 buf->putBitDouble(cp->z);
15178 }
15179 if (version > DRW::AC1021) {
15180 buf->putBitLong(static_cast<std::int32_t>(sp->fitlist.size()));
15181 for (const auto& fp : sp->fitlist) {
15182 DRW_Coord f2{fp->x, fp->y, 0.0};
15183 buf->put2RawDouble(f2);
15184 }
15185 buf->put2RawDouble(sp->tgStart);
15186 buf->put2RawDouble(sp->tgEnd);
15187 }
15188 } else {
15189 return false;
15190 }
15191 }
15192 } else {
15193 const DRW_LWPolyline* pl = nullptr;
15194 if (!lp->objlist.empty())
15195 pl = dynamic_cast<const DRW_LWPolyline*>(lp->objlist[0].get());
15196 if (!pl)
15197 return false;
15198
15199 bool asBulge = false;
15200 for (const auto& v : pl->vertlist)
15201 if (v->bulge != 0.0) { asBulge = true; break; }
15202
15203 buf->putBit(static_cast<std::uint8_t>(asBulge));
15204 buf->putBit(static_cast<std::uint8_t>(pl->flags & 1));
15205 buf->putBitLong(static_cast<std::int32_t>(pl->vertlist.size()));
15206 for (const auto& v : pl->vertlist) {
15207 buf->putRawDouble(v->x);
15208 buf->putRawDouble(v->y);
15209 if (asBulge)
15210 buf->putBitDouble(v->bulge);
15211 }
15212 }
15213
15214 buf->putBitLong(0); // numBoundHandles for this loop (0 = non-associative)
15215 }
15216
15217 return true;
15218}
15219
15220bool DRW_Hatch::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
15221 (void)bs;
15222 if (!validateDwgHatchData(*this))
1
Taking false branch
15223 return false;
15224 oType = 78; // HATCH class id — see dwgreader.cpp:1380
15225 if (!encodeDwgCommon(version, buf)) return false;
2
Assuming the condition is false
15226
15227 dwgBufferW *sb = strBuf ? strBuf : buf;
3
Taking false branch
4
Assuming 'strBuf' is null
5
'?' condition is false
15228 encodeDwgGradientData(version, buf, sb);
6
Passing 'sb' via 2nd parameter 'buf'
7
Calling 'DRW_Hatch::encodeDwgGradientData'
15229
15230 buf->putBitDouble(basePoint.z); // BD: elevation
15231 buf->put3BitDouble(extPoint); // 3BD: extrusion (NOT BE-style for HATCH)
15232 sb->putVariableText(version, name); // TV: hatch pattern name
15233 buf->putBit(static_cast<std::uint8_t>(solid));
15234 buf->putBit(static_cast<std::uint8_t>(associative));
15235 if (!encodeDwgBoundaryData(version, buf, true)) return false;
15236
15237 buf->putBitShort(static_cast<std::uint16_t>(hstyle));
15238 buf->putBitShort(static_cast<std::uint16_t>(hpattern));
15239
15240 if (!solid) {
15241 buf->putBitDouble(angle);
15242 buf->putBitDouble(scale);
15243 buf->putBit(static_cast<std::uint8_t>(doubleflag));
15244 buf->putBitShort(static_cast<std::uint16_t>(patternLines.size()));
15245 for (const PatternLine& patternLine : patternLines) {
15246 buf->putBitDouble(patternLine.angle);
15247 buf->putBitDouble(patternLine.baseX);
15248 buf->putBitDouble(patternLine.baseY);
15249 buf->putBitDouble(patternLine.offsetX);
15250 buf->putBitDouble(patternLine.offsetY);
15251 buf->putBitShort(static_cast<std::uint16_t>(patternLine.dashList.size()));
15252 for (double dash : patternLine.dashList)
15253 buf->putBitDouble(dash);
15254 }
15255 }
15256
15257 bool havePixelSize = false;
15258 for (const auto& lp : looplist)
15259 havePixelSize = havePixelSize || (lp && ((lp->type & 4) != 0));
15260 if (havePixelSize)
15261 buf->putBitDouble(pixelSize);
15262
15263 buf->putBitLong(static_cast<std::int32_t>(seedPoints.size()));
15264 for (const auto& sp : seedPoints) {
15265 buf->putRawDouble(sp.x);
15266 buf->putRawDouble(sp.y);
15267 }
15268
15269 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
15270 return true;
15271}
15272
15273bool DRW_Spline::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
15274 switch (code) {
15275 case 210:
15276 normalVec.x = reader->getDouble();
15277 break;
15278 case 220:
15279 normalVec.y = reader->getDouble();
15280 break;
15281 case 230:
15282 normalVec.z = reader->getDouble();
15283 break;
15284 case 12:
15285 tgStart.x = reader->getDouble();
15286 break;
15287 case 22:
15288 tgStart.y = reader->getDouble();
15289 break;
15290 case 32:
15291 tgStart.z = reader->getDouble();
15292 break;
15293 case 13:
15294 tgEnd.x = reader->getDouble();
15295 break;
15296 case 23:
15297 tgEnd.y = reader->getDouble();
15298 break;
15299 case 33:
15300 tgEnd.z = reader->getDouble();
15301 break;
15302 case 70:
15303 flags = reader->getInt32();
15304 break;
15305 case 71:
15306 degree = reader->getInt32();
15307 if (!isValidCount(degree, kMaxDxfDegree) || degree == 0)
15308 return false;
15309 m_dxfDegreeSeen = true;
15310 break;
15311 case 72:
15312 if (m_dxfKnotCountSeen)
15313 return false;
15314 nknots = reader->getInt32();
15315 if (!isValidCount(nknots, kMaxDxfItems))
15316 return false;
15317 m_dxfKnotCountSeen = true;
15318 break;
15319 case 73:
15320 if (m_dxfControlCountSeen)
15321 return false;
15322 ncontrol = reader->getInt32();
15323 if (!isValidCount(ncontrol, kMaxDxfItems))
15324 return false;
15325 m_dxfControlCountSeen = true;
15326 break;
15327 case 74:
15328 if (m_dxfFitCountSeen)
15329 return false;
15330 nfit = reader->getInt32();
15331 if (!isValidCount(nfit, kMaxDxfItems))
15332 return false;
15333 m_dxfFitCountSeen = true;
15334 break;
15335 case 42:
15336 tolknot = reader->getDouble();
15337 break;
15338 case 43:
15339 tolcontrol = reader->getDouble();
15340 break;
15341 case 44:
15342 tolfit = reader->getDouble();
15343 break;
15344 case 10: {
15345 if (controllist.size() >= static_cast<std::size_t>(kMaxDxfItems)
15346 || (m_dxfControlCountSeen
15347 && controllist.size() >= static_cast<std::size_t>(ncontrol)))
15348 return false;
15349 controlpoint = std::make_shared<DRW_Coord>();
15350 controllist.push_back(controlpoint);
15351 controlpoint->x = reader->getDouble();
15352 break; }
15353 case 20:
15354 if(controlpoint)
15355 controlpoint->y = reader->getDouble();
15356 break;
15357 case 30:
15358 if(controlpoint)
15359 controlpoint->z = reader->getDouble();
15360 break;
15361 case 11: {
15362 if (fitlist.size() >= static_cast<std::size_t>(kMaxDxfItems)
15363 || (m_dxfFitCountSeen
15364 && fitlist.size() >= static_cast<std::size_t>(nfit)))
15365 return false;
15366 fitpoint = std::make_shared<DRW_Coord>();
15367 fitlist.push_back(fitpoint);
15368 fitpoint->x = reader->getDouble();
15369 break; }
15370 case 21:
15371 if(fitpoint)
15372 fitpoint->y = reader->getDouble();
15373 break;
15374 case 31:
15375 if(fitpoint)
15376 fitpoint->z = reader->getDouble();
15377 break;
15378 case 40:
15379 if (knotslist.size() >= static_cast<std::size_t>(kMaxDxfItems)
15380 || (m_dxfKnotCountSeen
15381 && knotslist.size() >= static_cast<std::size_t>(nknots)))
15382 return false;
15383 knotslist.push_back(reader->getDouble());
15384 break;
15385 case 41:
15386 if (weightlist.size() >= static_cast<std::size_t>(kMaxDxfItems)
15387 || (m_dxfControlCountSeen
15388 && weightlist.size() >= static_cast<std::size_t>(ncontrol)))
15389 return false;
15390 weightlist.push_back(reader->getDouble());
15391 break;
15392 default:
15393 return DRW_Entity::parseCode(code, reader);
15394 }
15395
15396 return true;
15397}
15398
15399bool DRW_Spline::validateDxf() const {
15400 if (m_dxfKnotCountSeen
15401 && knotslist.size() != static_cast<std::size_t>(nknots))
15402 return false;
15403 if (m_dxfControlCountSeen
15404 && controllist.size() != static_cast<std::size_t>(ncontrol))
15405 return false;
15406 if (m_dxfFitCountSeen
15407 && fitlist.size() != static_cast<std::size_t>(nfit))
15408 return false;
15409 return validatePayloadFields(/*allowMixedLists=*/true);
15410}
15411
15412bool DRW_Spline::validatePayloadFields(bool allowMixedLists) const {
15413 const auto finite = [](double value) { return std::isfinite(value); };
15414 const auto finiteCoord = [&finite](const DRW_Coord& point) {
15415 return finite(point.x) && finite(point.y) && finite(point.z);
15416 };
15417 const auto validCount = [](std::size_t count) {
15418 return count <= static_cast<std::size_t>(kMaxSplineItems)
15419 && count <= static_cast<std::size_t>(
15420 std::numeric_limits<std::int32_t>::max());
15421 };
15422 const auto validPointList = [&finiteCoord, &validCount](const auto& points) {
15423 return validCount(points.size())
15424 && std::all_of(points.cbegin(), points.cend(),
15425 [&finiteCoord](const auto& point) {
15426 return point != nullptr && finiteCoord(*point);
15427 });
15428 };
15429
15430 if (!isValidSplineDegree(degree)
15431 // Bits above 16 (AutoCAD 2013+ sets 32 and 1024 on fit splines) are
15432 // not used, but a 16-bit value is accepted.
15433 || flags < 0 || flags > 0xFFFF
15434 || !finiteCoord(normalVec) || !finiteCoord(tgStart)
15435 || !finiteCoord(tgEnd) || !finite(tolknot)
15436 || !finite(tolcontrol) || !finite(tolfit)
15437 || !validCount(knotslist.size())
15438 || !std::all_of(knotslist.cbegin(), knotslist.cend(), finite)
15439 || !validCount(weightlist.size())
15440 || !std::all_of(weightlist.cbegin(), weightlist.cend(), finite)
15441 || !validPointList(controllist) || !validPointList(fitlist)
15442 || weightlist.size() > controllist.size())
15443 return false;
15444
15445 // The DWG body has disjoint control-point and fit-point scenarios. When
15446 // both lists are present, the encoder selects control points and would
15447 // silently discard the fit points, so reject that lossy representation.
15448 if (!allowMixedLists && !fitlist.empty() && !controllist.empty())
15449 return false;
15450 if (!fitlist.empty() && !controllist.empty())
15451 return isValidControlSplineLayout(
15452 degree, static_cast<std::int32_t>(knotslist.size()),
15453 static_cast<std::int32_t>(controllist.size()))
15454 && isValidFitSplineLayout(
15455 degree, static_cast<std::int32_t>(fitlist.size()));
15456 if (!fitlist.empty())
15457 return isValidFitSplineLayout(
15458 degree, static_cast<std::int32_t>(fitlist.size()));
15459 return isValidControlSplineLayout(
15460 degree, static_cast<std::int32_t>(knotslist.size()),
15461 static_cast<std::int32_t>(controllist.size()));
15462}
15463
15464bool DRW_Helix::validateDxf() const {
15465 const auto finite = [](const DRW_Coord& point) {
15466 return std::isfinite(point.x) && std::isfinite(point.y)
15467 && std::isfinite(point.z);
15468 };
15469 return DRW_Spline::validateDxf()
15470 && finite(axisBasePt) && finite(startPt) && finite(axisVector)
15471 && std::isfinite(radius) && std::isfinite(turns)
15472 && std::isfinite(turnHeight);
15473}
15474
15475bool DRW_Helix::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
15476 if (code == 100) {
15477 const std::string subclass = reader->getString();
15478 m_parsingHelixSubclass = (subclass == "AcDbHelix");
15479 return true;
15480 }
15481
15482 if (!m_parsingHelixSubclass)
15483 return DRW_Spline::parseCode(code, reader);
15484
15485 switch (code) {
15486 case 90:
15487 m_majorVersion = reader->getInt32();
15488 break;
15489 case 91:
15490 m_maintVersion = reader->getInt32();
15491 break;
15492 case 10:
15493 axisBasePt.x = reader->getDouble();
15494 break;
15495 case 20:
15496 axisBasePt.y = reader->getDouble();
15497 break;
15498 case 30:
15499 axisBasePt.z = reader->getDouble();
15500 break;
15501 case 11:
15502 startPt.x = reader->getDouble();
15503 break;
15504 case 21:
15505 startPt.y = reader->getDouble();
15506 break;
15507 case 31:
15508 startPt.z = reader->getDouble();
15509 break;
15510 case 12:
15511 axisVector.x = reader->getDouble();
15512 break;
15513 case 22:
15514 axisVector.y = reader->getDouble();
15515 break;
15516 case 32:
15517 axisVector.z = reader->getDouble();
15518 break;
15519 case 40:
15520 radius = reader->getDouble();
15521 break;
15522 case 41:
15523 turns = reader->getDouble();
15524 break;
15525 case 42:
15526 turnHeight = reader->getDouble();
15527 break;
15528 case 290:
15529 {
15530 int value = 0;
15531 if (!readDxfIntInRange(reader, 0, 1, value))
15532 return false;
15533 handedness = value != 0;
15534 }
15535 break;
15536 case 280:
15537 {
15538 int value = 0;
15539 if (!readDxfIntInRange(reader, 0, 0xFF, value))
15540 return false;
15541 constraintType = static_cast<std::uint8_t>(value);
15542 }
15543 break;
15544 default:
15545 return DRW_Entity::parseCode(code, reader);
15546 }
15547
15548 return true;
15549}
15550
15551void DRW_Spline::resetDwgState() {
15552 DRW_Entity::reset();
15553 normalVec = DRW_Coord{0.0, 0.0, 0.0};
15554 tgStart = DRW_Coord{0.0, 0.0, 0.0};
15555 tgEnd = DRW_Coord{0.0, 0.0, 0.0};
15556 flags = 0;
15557 degree = 0;
15558 m_scenario = 0;
15559 m_splineFlags1 = 0;
15560 m_knotParam = 15;
15561 nknots = 0;
15562 ncontrol = 0;
15563 nfit = 0;
15564 tolknot = 0.0000001;
15565 tolcontrol = 0.0000001;
15566 tolfit = 0.0000001;
15567 knotslist.clear();
15568 weightlist.clear();
15569 controllist.clear();
15570 fitlist.clear();
15571 controlpoint.reset();
15572 fitpoint.reset();
15573 m_dxfDegreeSeen = false;
15574 m_dxfKnotCountSeen = false;
15575 m_dxfControlCountSeen = false;
15576 m_dxfFitCountSeen = false;
15577}
15578
15579bool DRW_Spline::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
15580 resetDwgState();
15581 dwgBuffer *sourceBuf = buf;
15582 auto fail = [this, sourceBuf]() {
15583 if (sourceBuf != nullptr)
15584 sourceBuf->invalidate();
15585 resetDwgState();
15586 return false;
15587 };
15588 if (sourceBuf == nullptr)
15589 return fail();
15590
15591 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
15592 buf = &bodyProbe;
15593 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
15594 if (!ret)
15595 return fail();
15596 if (!parseDwgSplineBody(version, buf))
15597 return fail();
15598
15599 std::uint64_t handleEndBit = 0;
15600 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
15601 handleEndBit))
15602 return fail();
15603 /* Common Entity Handle Data */
15604 dwgBuffer handleProbe = buf->forkIndependent();
15605 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
15606 handleEndBit);
15607 if (!ret || !handleProbe.isGood())
15608 return fail();
15609// RS crc; //RS */
15610 *sourceBuf = handleProbe;
15611 return true;
15612}
15613
15614// Spline body decode: the scenario/degree/knots/ctrl/fit section, WITHOUT
15615// the leading DRW_Entity::parseDwg(common) or the trailing parseDwgEntHandle.
15616// Factored out so DRW_Helix can reuse the identical spline payload before its
15617// AcDbHelix trailer (Phase 8a-1).
15618bool DRW_Spline::parseDwgSplineBody(DRW::Version version, dwgBuffer *buf){
15619 try {
15620 DRW_DBG("\n***************************** parsing spline *********************************************\n")DRW_dbg::dbg("\n***************************** parsing spline *********************************************\n"
)
;
15621 if (buf == nullptr || !buf->isGood())
15622 return false;
15623 std::uint8_t weight = 0; // RLZ ??? flags, weight, code 70, bit 4 (16)
15624 const std::uint64_t bodyEndBit = dwgDataEndBit;
15625 std::uint64_t controlBits = 0;
15626
15627 std::int32_t scenario = 0;
15628 if (!readBoundedBitLong(*buf, bodyEndBit, scenario))
15629 return false;
15630 m_scenario = scenario;
15631 DRW_DBG("scenario: ")DRW_dbg::dbg("scenario: "); DRW_DBG(scenario)DRW_dbg::dbg(scenario);
15632 if (version > DRW::AC1024) {
15633 std::int32_t splFlag1 = 0;
15634 std::int32_t knotParam = 0;
15635 if (!readBoundedBitLong(*buf, bodyEndBit, splFlag1)
15636 || !readBoundedBitLong(*buf, bodyEndBit, knotParam))
15637 return false;
15638 m_splineFlags1 = splFlag1;
15639 m_knotParam = knotParam;
15640 if (knotParam == kSplineKnotParamCustom || !(splFlag1 & kSplineFlagUseKnotParameter)) {
15641 scenario = 1;
15642 } else if (splFlag1 & kSplineFlagMethodFitPoints) {
15643 scenario = 2;
15644 }
15645 m_scenario = scenario;
15646 DRW_DBG(" 2013 splFlag1: ")DRW_dbg::dbg(" 2013 splFlag1: "); DRW_DBG(splFlag1)DRW_dbg::dbg(splFlag1);
15647 DRW_DBG(" 2013 knotParam: ")DRW_dbg::dbg(" 2013 knotParam: "); DRW_DBG(knotParam)DRW_dbg::dbg(knotParam);
15648// DRW_DBG("unk bit: "); DRW_DBG(buf->getBit());
15649 }
15650 if (!readBoundedBitLong(*buf, bodyEndBit, degree))
15651 return false;
15652 DRW_DBG(" degree: ")DRW_dbg::dbg(" degree: "); DRW_DBG(degree)DRW_dbg::dbg(degree); DRW_DBG("\n")DRW_dbg::dbg("\n");
15653 if (!isValidSplineDegree(degree)) {
15654 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");
15655 return false;
15656 }
15657 if (scenario == 2) {
15658 flags = 8;//scenario 2 = not rational & planar
15659 if (m_splineFlags1 & kSplineFlagClosed)
15660 flags |= 1;
15661 if (!readBoundedBitDouble(*buf, bodyEndBit, tolfit)
15662 || !readBoundedBitCoord(*buf, bodyEndBit, tgStart)
15663 || !readBoundedBitCoord(*buf, bodyEndBit, tgEnd))
15664 return false;
15665 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);
15666 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);
15667 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);
15668 std::uint32_t parsedFitCount = 0;
15669 if (!readTableBodyCount(version, buf, objSize, kMaxSplineItems, 6,
15670 parsedFitCount)) {
15671 return false;
15672 }
15673 nfit = static_cast<std::int32_t>(parsedFitCount);
15674 if (!isValidFitSplineLayout(degree, nfit)) {
15675 DRW_DBG("\ndwg Spline, invalid fit layout degree/count: ")DRW_dbg::dbg("\ndwg Spline, invalid fit layout degree/count: "
)
;
15676 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");
15677 return false;
15678 }
15679 DRW_DBG("\nnumber of fit points: ")DRW_dbg::dbg("\nnumber of fit points: "); DRW_DBG(nfit)DRW_dbg::dbg(nfit);
15680 } else if (scenario == 1) {
15681 flags = 8;//scenario 1 = rational & planar
15682 bool rational = false;
15683 bool closed = false;
15684 bool periodic = false;
15685 if (!readBoundedBit(*buf, bodyEndBit, rational)
15686 || !readBoundedBit(*buf, bodyEndBit, closed)
15687 || !readBoundedBit(*buf, bodyEndBit, periodic)
15688 || !readBoundedBitDouble(*buf, bodyEndBit, tolknot)
15689 || !readBoundedBitDouble(*buf, bodyEndBit, tolcontrol))
15690 return false;
15691 flags |= rational ? 4 : 0;
15692 flags |= closed ? 1 : 0;
15693 flags |= periodic ? 2 : 0;
15694 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);
15695 DRW_DBG(" control point tolerance: ")DRW_dbg::dbg(" control point tolerance: "); DRW_DBG(tolcontrol)DRW_dbg::dbg(tolcontrol);
15696 std::uint32_t parsedKnotCount = 0;
15697 if (!readTableBodyCount(version, buf, objSize, kMaxSplineItems, 2,
15698 parsedKnotCount)) {
15699 return false;
15700 }
15701 nknots = static_cast<std::int32_t>(parsedKnotCount);
15702 std::uint32_t parsedControlCount = 0;
15703 if (!readTableBodyCount(version, buf, objSize, kMaxSplineItems, 6,
15704 parsedControlCount)) {
15705 return false;
15706 }
15707 ncontrol = static_cast<std::int32_t>(parsedControlCount);
15708 if (!isValidControlSplineLayout(degree, nknots, ncontrol)) {
15709 DRW_DBG("\ndwg Spline, invalid control layout degree/knots/control: ")DRW_dbg::dbg("\ndwg Spline, invalid control layout degree/knots/control: "
)
;
15710 DRW_DBG(degree)DRW_dbg::dbg(degree); DRW_DBG("/")DRW_dbg::dbg("/"); DRW_DBG(nknots)DRW_dbg::dbg(nknots); DRW_DBG("/")DRW_dbg::dbg("/");
15711 DRW_DBG(ncontrol)DRW_dbg::dbg(ncontrol); DRW_DBG("\n")DRW_dbg::dbg("\n");
15712 return false;
15713 }
15714 bool hasWeights = false;
15715 if (!readBoundedBit(*buf, bodyEndBit, hasWeights))
15716 return false;
15717 weight = hasWeights ? 1 : 0; // flags bit 4: weights present (code 70)
15718 if (weight) flags |= 0x10;
15719 if (!dwgSafety::multiply(static_cast<std::uint64_t>(ncontrol),
15720 weight ? 8u : 6u, controlBits))
15721 return false;
15722 if (!proxyEntityHasBits(*buf, bodyEndBit, controlBits)) {
15723 return false;
15724 }
15725 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: ");
15726 DRW_DBG(ncontrol)DRW_dbg::dbg(ncontrol); DRW_DBG(" weight bit: ")DRW_dbg::dbg(" weight bit: "); DRW_DBG(weight)DRW_dbg::dbg(weight);
15727 } else {
15728 DRW_DBG("\ndwg Spline, unknown scenario ")DRW_dbg::dbg("\ndwg Spline, unknown scenario "); DRW_DBG(scenario)DRW_dbg::dbg(scenario);
15729 DRW_DBG(" (expected 1 or 2)\n")DRW_dbg::dbg(" (expected 1 or 2)\n");
15730 return false; //RLZ: from doc only 1 or 2 are ok ?
15731 }
15732
15733 std::vector<double> parsedKnots;
15734 std::vector<double> parsedWeights;
15735 std::vector<std::shared_ptr<DRW_Coord>> parsedControls;
15736 std::vector<std::shared_ptr<DRW_Coord>> parsedFits;
15737 std::uint64_t knotBits = 0;
15738 std::uint64_t fitBits = 0;
15739 std::uint64_t requiredBits = 0;
15740 if (!dwgSafety::multiply(static_cast<std::uint64_t>(nknots), 2,
15741 knotBits)
15742 || !dwgSafety::multiply(static_cast<std::uint64_t>(nfit), 6,
15743 fitBits)
15744 || !dwgSafety::add(knotBits, controlBits, requiredBits)
15745 || !dwgSafety::add(requiredBits, fitBits, requiredBits)
15746 || !proxyEntityHasBits(*buf, bodyEndBit, requiredBits)) {
15747 return false;
15748 }
15749 if (!DRW::reserve(parsedKnots, nknots)) {
15750 return false;
15751 }
15752 for (std::int32_t i= 0; i<nknots; ++i){
15753 double knot = 0.0;
15754 if (!readBoundedBitDouble(*buf, bodyEndBit, knot)
15755 || !std::isfinite(knot))
15756 return false;
15757 parsedKnots.push_back(knot);
15758 }
15759 if (!DRW::reserve(parsedControls, ncontrol)) {
15760 return false;
15761 }
15762 if (weight && !DRW::reserve(parsedWeights, ncontrol)) {
15763 return false;
15764 }
15765 for (std::int32_t i= 0; i<ncontrol; ++i){
15766 DRW_Coord control;
15767 if (!readBoundedBitCoord(*buf, bodyEndBit, control)
15768 || !std::isfinite(control.x)
15769 || !std::isfinite(control.y)
15770 || !std::isfinite(control.z))
15771 return false;
15772 parsedControls.push_back(std::make_shared<DRW_Coord>(control));
15773 if (weight) {
15774 //per-control-point weight; required for hyperbola/parabola
15775 //conic detection in consumers (e.g. LibreCAD addSpline)
15776 double w = 0.0; //RLZ Warning: D (BD or RD)
15777 if (!readBoundedBitDouble(*buf, bodyEndBit, w)
15778 || !std::isfinite(w))
15779 return false;
15780 parsedWeights.push_back(w);
15781 DRW_DBG("\n w: ")DRW_dbg::dbg("\n w: "); DRW_DBG(w)DRW_dbg::dbg(w);
15782 }
15783 }
15784 if (!DRW::reserve(parsedFits, nfit)) {
15785 return false;
15786 }
15787 for (std::int32_t i= 0; i<nfit; ++i) {
15788 DRW_Coord fit;
15789 if (!readBoundedBitCoord(*buf, bodyEndBit, fit)
15790 || !std::isfinite(fit.x)
15791 || !std::isfinite(fit.y)
15792 || !std::isfinite(fit.z))
15793 return false;
15794 parsedFits.push_back(std::make_shared<DRW_Coord>(fit));
15795 }
15796
15797 if (DRW_DBGGLDRW_dbg::getInstance()->getLevel() == DRW_dbg::Level::Debug) {
15798 DRW_DBG("\nknots list: ")DRW_dbg::dbg("\nknots list: ");
15799 for (auto const& v: parsedKnots) {
15800 DRW_DBG("\n")DRW_dbg::dbg("\n"); DRW_DBG(v)DRW_dbg::dbg(v);
15801 }
15802 DRW_DBG("\ncontrol point list: ")DRW_dbg::dbg("\ncontrol point list: ");
15803 for (auto const& v: parsedControls) {
15804 DRW_DBG("\n")DRW_dbg::dbg("\n"); DRW_DBGPT(v->x, v->y, v->z)DRW_dbg::dbgPT(v->x, v->y, v->z);
15805 }
15806 DRW_DBG("\nfit point list: ")DRW_dbg::dbg("\nfit point list: ");
15807 for (auto const& v: parsedFits) {
15808 DRW_DBG("\n")DRW_dbg::dbg("\n"); DRW_DBGPT(v->x, v->y, v->z)DRW_dbg::dbgPT(v->x, v->y, v->z);
15809 }
15810 }
15811
15812 if (!buf->isGood())
15813 return false;
15814 knotslist = std::move(parsedKnots);
15815 weightlist = std::move(parsedWeights);
15816 controllist = std::move(parsedControls);
15817 fitlist = std::move(parsedFits);
15818 return true;
15819 } catch (...) {
15820 return false;
15821 }
15822}
15823
15824// AcDbHelix trailer order (libreDWG dwg2.spec:2493-2503):
15825// major_version BL, maint_version BL, axis_base_pt 3BD, start_pt 3BD,
15826// axis_vector 3BD, radius BD, turns BD, turn_height BD, handedness B,
15827// constraint_type RC.
15828void DRW_Helix::resetDwgState() {
15829 DRW_Spline::resetDwgState();
15830 m_majorVersion = 0;
15831 m_maintVersion = 0;
15832 axisBasePt = DRW_Coord{0.0, 0.0, 0.0};
15833 startPt = DRW_Coord{0.0, 0.0, 0.0};
15834 axisVector = DRW_Coord{0.0, 0.0, 0.0};
15835 radius = 0.0;
15836 turns = 0.0;
15837 turnHeight = 0.0;
15838 handedness = false;
15839 constraintType = 0;
15840 m_parsingHelixSubclass = false;
15841}
15842
15843bool DRW_Helix::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
15844 resetDwgState();
15845 dwgBuffer *sourceBuf = buf;
15846 auto fail = [this, sourceBuf]() {
15847 if (sourceBuf != nullptr)
15848 sourceBuf->invalidate();
15849 resetDwgState();
15850 return false;
15851 };
15852 if (sourceBuf == nullptr)
15853 return fail();
15854
15855 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
15856 buf = &bodyProbe;
15857 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
15858 if (!ret)
15859 return fail();
15860 DRW_DBG("\n***************************** parsing helix *********************************************\n")DRW_dbg::dbg("\n***************************** parsing helix *********************************************\n"
)
;
15861 if (!parseDwgSplineBody(version, buf))
15862 return fail();
15863
15864 // AcDbHelix trailer (see field order above).
15865 const std::uint64_t bodyEndBit = dwgDataEndBit;
15866 std::int32_t parsedMajorVersion = 0;
15867 std::int32_t parsedMaintVersion = 0;
15868 DRW_Coord parsedAxisBasePt;
15869 DRW_Coord parsedStartPt;
15870 DRW_Coord parsedAxisVector;
15871 double parsedRadius = 0.0;
15872 double parsedTurns = 0.0;
15873 double parsedTurnHeight = 0.0;
15874 bool parsedHandedness = false;
15875 std::uint8_t parsedConstraintType = 0;
15876 if (!readBoundedBitLong(*buf, bodyEndBit, parsedMajorVersion)
15877 || !readBoundedBitLong(*buf, bodyEndBit, parsedMaintVersion)
15878 || !readBoundedBitCoord(*buf, bodyEndBit, parsedAxisBasePt)
15879 || !readBoundedBitCoord(*buf, bodyEndBit, parsedStartPt)
15880 || !readBoundedBitCoord(*buf, bodyEndBit, parsedAxisVector)
15881 || !readBoundedBitDouble(*buf, bodyEndBit, parsedRadius)
15882 || !readBoundedBitDouble(*buf, bodyEndBit, parsedTurns)
15883 || !readBoundedBitDouble(*buf, bodyEndBit, parsedTurnHeight)
15884 || !readBoundedBit(*buf, bodyEndBit, parsedHandedness)
15885 || !readBoundedRawChar8(*buf, bodyEndBit, parsedConstraintType)
15886 || !std::isfinite(parsedAxisBasePt.x)
15887 || !std::isfinite(parsedAxisBasePt.y)
15888 || !std::isfinite(parsedAxisBasePt.z)
15889 || !std::isfinite(parsedStartPt.x)
15890 || !std::isfinite(parsedStartPt.y)
15891 || !std::isfinite(parsedStartPt.z)
15892 || !std::isfinite(parsedAxisVector.x)
15893 || !std::isfinite(parsedAxisVector.y)
15894 || !std::isfinite(parsedAxisVector.z)
15895 || !std::isfinite(parsedRadius)
15896 || !std::isfinite(parsedTurns)
15897 || !std::isfinite(parsedTurnHeight))
15898 return fail();
15899 DRW_DBG("\nhelix radius: ")DRW_dbg::dbg("\nhelix radius: "); DRW_DBG(parsedRadius)DRW_dbg::dbg(parsedRadius); DRW_DBG(" turns: ")DRW_dbg::dbg(" turns: "); DRW_DBG(parsedTurns)DRW_dbg::dbg(parsedTurns);
15900
15901 std::uint64_t handleEndBit = 0;
15902 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
15903 handleEndBit))
15904 return fail();
15905 /* Common Entity Handle Data */
15906 dwgBuffer handleProbe = buf->forkIndependent();
15907 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
15908 handleEndBit);
15909 if (!ret || !handleProbe.isGood())
15910 return fail();
15911 // RS crc; //RS */
15912 m_majorVersion = parsedMajorVersion;
15913 m_maintVersion = parsedMaintVersion;
15914 axisBasePt = parsedAxisBasePt;
15915 startPt = parsedStartPt;
15916 axisVector = parsedAxisVector;
15917 radius = parsedRadius;
15918 turns = parsedTurns;
15919 turnHeight = parsedTurnHeight;
15920 handedness = parsedHandedness;
15921 constraintType = parsedConstraintType;
15922 *sourceBuf = handleProbe;
15923 return true;
15924}
15925
15926bool DRW_Image::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
15927 switch (code) {
15928 case 12:
15929 vVector.x = reader->getDouble();
15930 break;
15931 case 22:
15932 vVector.y = reader->getDouble();
15933 break;
15934 case 32:
15935 vVector.z = reader->getDouble();
15936 break;
15937 case 13:
15938 sizeu = reader->getDouble();
15939 break;
15940 case 23:
15941 sizev = reader->getDouble();
15942 break;
15943 case 70:
15944 {
15945 int value = 0;
15946 if (!readDxfIntInRange(reader, 0, 0xFFFF, value))
15947 return false;
15948 m_displayProps = value;
15949 }
15950 break;
15951 case 90: {
15952 const std::int32_t value = reader->getInt32();
15953 if (value < 0 || value > kMaxClassVersion)
15954 return false;
15955 m_classVersion = value;
15956 break;
15957 }
15958 case 340:
15959 ref = reader->getHandleString();
15960 break;
15961 case 360:
15962 m_imageDefReactorHandle = reader->getHandleString();
15963 break;
15964 case 280:
15965 {
15966 int value = 0;
15967 if (!readDxfIntInRange(reader, 0, 1, value))
15968 return false;
15969 clip = value;
15970 }
15971 break;
15972 case 281:
15973 {
15974 int value = 0;
15975 if (!readDxfIntInRange(reader, 0, 100, value))
15976 return false;
15977 brightness = value;
15978 }
15979 break;
15980 case 282:
15981 {
15982 int value = 0;
15983 if (!readDxfIntInRange(reader, 0, 100, value))
15984 return false;
15985 contrast = value;
15986 }
15987 break;
15988 case 283:
15989 {
15990 int value = 0;
15991 if (!readDxfIntInRange(reader, 0, 100, value))
15992 return false;
15993 fade = value;
15994 }
15995 break;
15996 case 71:
15997 {
15998 int value = 0;
15999 if (!readDxfIntInRange(reader, 0, 2, value))
16000 return false;
16001 m_clipBoundaryType = value;
16002 }
16003 break;
16004 case 91:
16005 // The declared count is a structural invariant: reject negative or
16006 // implausibly large values before reserve() can allocate unboundedly.
16007 {
16008 const std::int32_t count = reader->getInt32();
16009 if (count < 0 || count > static_cast<std::int32_t>(DRW_Image::kMaxClipVertices))
16010 return false;
16011 clipPath.clear();
16012 if (!DRW::reserve(clipPath, count))
16013 return false;
16014 m_declaredClipVertexCount = count;
16015 m_clipPathHasOpenVertex = false;
16016 }
16017 break;
16018 case 14:
16019 // WIPEOUT polygon vertex x — start a new vertex. Group 24 (y) follows.
16020 if (m_clipPathHasOpenVertex)
16021 return false;
16022 clipPath.emplace_back(reader->getDouble(), 0.0);
16023 m_clipPathHasOpenVertex = true;
16024 break;
16025 case 24:
16026 // WIPEOUT polygon vertex y — complete the most recently started vertex.
16027 if (!m_clipPathHasOpenVertex || clipPath.empty())
16028 return false;
16029 clipPath.back().y = reader->getDouble();
16030 m_clipPathHasOpenVertex = false;
16031 break;
16032 case 290:
16033 // R2010+ Clip mode (IMAGE/WIPEOUT, ODA spec §20.4.80):
16034 // 0 = mask outside the polygon, 1 = mask inside.
16035 clipMode = reader->getBool();
16036 break;
16037 default:
16038 return DRW_Line::parseCode(code, reader);
16039 }
16040
16041 return true;
16042}
16043
16044bool DRW_Image::hasValidClipBoundary() const {
16045 if (m_clipPathHasOpenVertex
16046 || (m_declaredClipVertexCount >= 0
16047 && static_cast<std::size_t>(m_declaredClipVertexCount) != clipPath.size())) {
16048 return false;
16049 }
16050 switch (m_clipBoundaryType) {
16051 case 0:
16052 return clipPath.empty();
16053 case 1:
16054 return clipPath.size() == 2;
16055 case 2:
16056 return clipPath.size() >= 3;
16057 default:
16058 return false;
16059 }
16060}
16061
16062bool DRW_Image::validatePayloadFields() const {
16063 const auto finite = [](const DRW_Coord& point) {
16064 return std::isfinite(point.x) && std::isfinite(point.y)
16065 && std::isfinite(point.z);
16066 };
16067 return m_classVersion >= 0 && m_classVersion <= kMaxClassVersion
16068 && m_displayProps >= 0 && m_displayProps <= 0xffff
16069 && clip >= 0 && clip <= 1
16070 && brightness >= 0 && brightness <= 0xff
16071 && contrast >= 0 && contrast <= 0xff
16072 && fade >= 0 && fade <= 0xff
16073 && std::isfinite(sizeu) && std::isfinite(sizev)
16074 && finite(basePoint) && finite(secPoint) && finite(vVector)
16075 && clipPath.size() <= kMaxClipVertices
16076 && std::all_of(clipPath.cbegin(), clipPath.cend(),
16077 [](const DRW_Coord& point) {
16078 return std::isfinite(point.x)
16079 && std::isfinite(point.y);
16080 });
16081}
16082
16083void DRW_Image::resetDwgState() {
16084 DRW_Line::resetDwgState();
16085 m_classVersion = 0;
16086 ref = 0;
16087 m_imageDefReactorHandle = 0;
16088 m_displayProps = 0;
16089 vVector = DRW_Coord{0.0, 0.0, 0.0};
16090 sizeu = 0.0;
16091 sizev = 0.0;
16092 dz = 0.0;
16093 clip = 0;
16094 brightness = 50;
16095 contrast = 50;
16096 fade = 0;
16097 m_clipBoundaryType = 0;
16098 clipPath.clear();
16099 clipMode = false;
16100 m_declaredClipVertexCount = -1;
16101 m_clipPathHasOpenVertex = false;
16102}
16103
16104bool DRW_Image::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
16105 resetDwgState();
16106 dwgBuffer *sourceBuf = buf;
16107 auto fail = [this, sourceBuf]() {
16108 if (sourceBuf != nullptr)
16109 sourceBuf->invalidate();
16110 resetDwgState();
16111 return false;
16112 };
16113 if (sourceBuf == nullptr)
16114 return fail();
16115
16116 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
16117 buf = &bodyProbe;
16118 dwgBuffer sBuff = buf->forkIndependent();
16119 dwgBuffer *sBuf = buf;
16120 if (version > DRW::AC1018) {//2007+
16121 sBuf = &sBuff; //separate buffer for strings
16122 }
16123 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
16124 if (!ret)
16125 return fail();
16126 DRW_DBG("\n***************************** parsing image *********************************************\n")DRW_dbg::dbg("\n***************************** parsing image *********************************************\n"
)
;
16127
16128 const auto finite = [](const DRW_Coord& point) {
16129 return std::isfinite(point.x) && std::isfinite(point.y)
16130 && std::isfinite(point.z);
16131 };
16132 const std::uint64_t bodyEndBit = dwgDataEndBit;
16133 std::int32_t parsedClassVersion = 0;
16134 DRW_Coord parsedBasePoint;
16135 DRW_Coord parsedSecPoint;
16136 DRW_Coord parsedVVector;
16137 double parsedSizeU = 0.0;
16138 double parsedSizeV = 0.0;
16139 std::uint16_t parsedDisplayProps = 0;
16140 bool parsedClip = false;
16141 std::uint8_t parsedBrightness = 0;
16142 std::uint8_t parsedContrast = 0;
16143 std::uint8_t parsedFade = 0;
16144 bool parsedClipMode = false;
16145 if (!readBoundedBitLong(*buf, bodyEndBit, parsedClassVersion)
16146 || parsedClassVersion < 0 || parsedClassVersion > kMaxClassVersion
16147 || !readBoundedBitCoord(*buf, bodyEndBit, parsedBasePoint)
16148 || !readBoundedBitCoord(*buf, bodyEndBit, parsedSecPoint)
16149 || !readBoundedBitCoord(*buf, bodyEndBit, parsedVVector)
16150 || !readBoundedRawDouble(*buf, bodyEndBit, parsedSizeU)
16151 || !readBoundedRawDouble(*buf, bodyEndBit, parsedSizeV)
16152 || !readBoundedBitShort(*buf, bodyEndBit, parsedDisplayProps)
16153 || !readBoundedBit(*buf, bodyEndBit, parsedClip)
16154 || !readBoundedRawChar8(*buf, bodyEndBit, parsedBrightness)
16155 || !readBoundedRawChar8(*buf, bodyEndBit, parsedContrast)
16156 || !readBoundedRawChar8(*buf, bodyEndBit, parsedFade)
16157 || (version > DRW::AC1021
16158 && !readBoundedBit(*buf, bodyEndBit, parsedClipMode))
16159 || !finite(parsedBasePoint) || !finite(parsedSecPoint)
16160 || !finite(parsedVVector) || !std::isfinite(parsedSizeU)
16161 || !std::isfinite(parsedSizeV))
16162 return fail();
16163 DRW_DBG("class Version: ")DRW_dbg::dbg("class Version: "); DRW_DBG(parsedClassVersion)DRW_dbg::dbg(parsedClassVersion);
16164 DRW_DBG("\nbase point: ")DRW_dbg::dbg("\nbase point: "); DRW_DBGPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint.z)DRW_dbg::dbgPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint
.z)
;
16165 DRW_DBG("\nU vector: ")DRW_dbg::dbg("\nU vector: "); DRW_DBGPT(parsedSecPoint.x, parsedSecPoint.y, parsedSecPoint.z)DRW_dbg::dbgPT(parsedSecPoint.x, parsedSecPoint.y, parsedSecPoint
.z)
;
16166 DRW_DBG("\nV vector: ")DRW_dbg::dbg("\nV vector: "); DRW_DBGPT(parsedVVector.x, parsedVVector.y, parsedVVector.z)DRW_dbg::dbgPT(parsedVVector.x, parsedVVector.y, parsedVVector
.z)
;
16167 DRW_DBG("\nsize U: ")DRW_dbg::dbg("\nsize U: "); DRW_DBG(parsedSizeU)DRW_dbg::dbg(parsedSizeU); DRW_DBG("\nsize V: ")DRW_dbg::dbg("\nsize V: "); DRW_DBG(parsedSizeV)DRW_dbg::dbg(parsedSizeV);
16168 DRW_DBG("\ndisplay props: ")DRW_dbg::dbg("\ndisplay props: "); DRW_DBG(parsedDisplayProps)DRW_dbg::dbg(parsedDisplayProps);
16169
16170 std::uint16_t parsedClipBoundaryType = 0;
16171 if (!readBoundedBitShort(*buf, bodyEndBit, parsedClipBoundaryType))
16172 return fail();
16173 std::vector<DRW_Coord> parsedClipPath;
16174 std::int32_t parsedDeclaredClipVertexCount = -1;
16175 if (parsedClipBoundaryType == 0) {
16176 // No clip boundary payload.
16177 } else if (parsedClipBoundaryType == 1){
16178 // Rectangles are encoded as exactly two opposite corners. Keep that
16179 // canonical payload intact; rendering expands it independently.
16180 DRW_Coord ll;
16181 DRW_Coord ur;
16182 if (!readBoundedRawDouble(*buf, bodyEndBit, ll.x)
16183 || !readBoundedRawDouble(*buf, bodyEndBit, ll.y)
16184 || !readBoundedRawDouble(*buf, bodyEndBit, ur.x)
16185 || !readBoundedRawDouble(*buf, bodyEndBit, ur.y)
16186 || !finite(ll) || !finite(ur))
16187 return fail();
16188 parsedClipPath.push_back(ll);
16189 parsedClipPath.push_back(ur);
16190 parsedDeclaredClipVertexCount = 2;
16191 } else if (parsedClipBoundaryType == 2) {
16192 std::uint32_t numVerts = 0;
16193 if (!readTableBodyCount(version, buf, objSize,
16194 static_cast<std::uint32_t>(kMaxClipVertices),
16195 128, numVerts))
16196 return fail();
16197 std::uint64_t vertexBits = 0;
16198 if (!dwgSafety::multiply(static_cast<std::uint64_t>(numVerts), 128,
16199 vertexBits)
16200 || !proxyEntityHasBits(*buf, bodyEndBit, vertexBits)
16201 || !DRW::reserve(parsedClipPath, numVerts))
16202 return fail();
16203 for (std::uint32_t i = 0; i < numVerts; ++i) {
16204 DRW_Coord point;
16205 if (!readBoundedRawDouble(*buf, bodyEndBit, point.x)
16206 || !readBoundedRawDouble(*buf, bodyEndBit, point.y)
16207 || !finite(point))
16208 return fail();
16209 parsedClipPath.push_back(point);
16210 }
16211 parsedDeclaredClipVertexCount = static_cast<std::int32_t>(numVerts);
16212 } else {
16213 DRW_DBG("unsupported image clip type: ")DRW_dbg::dbg("unsupported image clip type: "); DRW_DBG(parsedClipBoundaryType)DRW_dbg::dbg(parsedClipBoundaryType); DRW_DBG("\n")DRW_dbg::dbg("\n");
16214 return fail();
16215 }
16216
16217 if ((parsedClipBoundaryType == 0 && !parsedClipPath.empty())
16218 || (parsedClipBoundaryType == 2 && parsedClipPath.size() < 3))
16219 return fail();
16220
16221 if (!buf->isGood())
16222 return fail();
16223 std::uint64_t handleEndBit = 0;
16224 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
16225 handleEndBit))
16226 return fail();
16227 dwgBuffer handleProbe = buf->forkIndependent();
16228 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
16229 handleEndBit);
16230 if (!ret)
16231 return fail();
16232 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
16233
16234 if (!proxyEntityHasBits(handleProbe, handleEndBit, 8))
16235 return fail();
16236 dwgHandle biH;
16237 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false, biH))
16238 return fail();
16239 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);
16240 if (!proxyEntityHasBits(handleProbe, handleEndBit, 8))
16241 return fail();
16242 dwgHandle reactorH;
16243 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
16244 reactorH))
16245 return fail();
16246 DRW_DBG("ImageDefReactor Handle: ")DRW_dbg::dbg("ImageDefReactor Handle: "); DRW_DBGHL(reactorH.code, reactorH.size, reactorH.ref)DRW_dbg::dbgHL(reactorH.code, reactorH.size, reactorH.ref);
16247 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
16248 if (!handleProbe.isGood() || !sBuf->isGood())
16249 return fail();
16250 m_classVersion = parsedClassVersion;
16251 basePoint = parsedBasePoint;
16252 secPoint = parsedSecPoint;
16253 vVector = parsedVVector;
16254 sizeu = parsedSizeU;
16255 sizev = parsedSizeV;
16256 m_displayProps = parsedDisplayProps;
16257 clip = parsedClip ? 1 : 0;
16258 brightness = parsedBrightness;
16259 contrast = parsedContrast;
16260 fade = parsedFade;
16261 clipMode = parsedClipMode;
16262 m_clipBoundaryType = parsedClipBoundaryType;
16263 clipPath = std::move(parsedClipPath);
16264 m_declaredClipVertexCount = parsedDeclaredClipVertexCount;
16265 m_clipPathHasOpenVertex = false;
16266 *sourceBuf = handleProbe;
16267 ref = biH.ref;
16268 m_imageDefReactorHandle = reactorH.ref;
16269 return true;
16270}
16271
16272// DRW_Image::encodeDwg — inverse of DRW_Image::parseDwg above (libreDWG
16273// dwg.spec:5533-5563). Body field order: BL class_version, 3 x 3BD
16274// (base/uvec/vvec), 2 x RD (sizeu/sizev), BS display_props, B clip,
16275// 3 x RC (brightness/contrast/fade), [R2010+ B clip_mode], BS
16276// clip_boundary_type + verts. Both handles (imagedef code 5 + reactor
16277// code 3) are emitted UNCONDITIONALLY at the END of the handle stream,
16278// matching parseDwg's order — NOT the spec's interleaved mid-stream slots.
16279bool DRW_Image::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
16280 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
16281 (void)bs; (void)strBuf;
16282 if (!validatePayloadFields()) {
16283 DRW_DBG("IMAGE has an invalid scalar or clip field\n")DRW_dbg::dbg("IMAGE has an invalid scalar or clip field\n");
16284 return false;
16285 }
16286 // Callers sometimes populate clipPath without setting m_clipBoundaryType
16287 // (DXF import historically stored only the vertices). Infer a coherent type
16288 // so encode does not reject a well-formed polygon/rectangle path.
16289 if (m_clipBoundaryType == 0 && !clipPath.empty()) {
16290 if (clipPath.size() == 2)
16291 m_clipBoundaryType = 1;
16292 else if (clipPath.size() >= 3)
16293 m_clipBoundaryType = 2;
16294 }
16295 if (!hasValidClipBoundary()) {
16296 DRW_DBG("IMAGE has invalid clip boundary\n")DRW_dbg::dbg("IMAGE has invalid clip boundary\n");
16297 return false;
16298 }
16299 oType = 101; // IMAGE class id — see dwgreader.cpp case 101
16300 if (!encodeDwgCommon(version, buf)) return false;
16301
16302 buf->putBitLong(m_classVersion);
16303 buf->putBitDouble(basePoint.x); buf->putBitDouble(basePoint.y); buf->putBitDouble(basePoint.z);
16304 buf->putBitDouble(secPoint.x); buf->putBitDouble(secPoint.y); buf->putBitDouble(secPoint.z); // uvec
16305 buf->putBitDouble(vVector.x); buf->putBitDouble(vVector.y); buf->putBitDouble(vVector.z);
16306 buf->putRawDouble(sizeu);
16307 buf->putRawDouble(sizev);
16308 buf->putBitShort(static_cast<std::uint16_t>(m_displayProps));
16309 buf->putBit(static_cast<std::uint8_t>(clip & 1));
16310 buf->putRawChar8(static_cast<std::uint8_t>(brightness));
16311 buf->putRawChar8(static_cast<std::uint8_t>(contrast));
16312 buf->putRawChar8(static_cast<std::uint8_t>(fade));
16313 if (version > DRW::AC1021) { // 2010+ clip mode
16314 buf->putBit(clipMode ? 1 : 0);
16315 }
16316 if (m_clipBoundaryType == 0) {
16317 buf->putBitShort(0); // clip_boundary_type 0 = none
16318 } else if (m_clipBoundaryType == 1) {
16319 buf->putBitShort(1);
16320 buf->put2RawDouble(clipPath[0]);
16321 buf->put2RawDouble(clipPath[1]);
16322 } else {
16323 buf->putBitShort(2);
16324 buf->putBitLong(static_cast<std::int32_t>(clipPath.size()));
16325 for (std::size_t i = 0; i < clipPath.size(); ++i)
16326 buf->put2RawDouble(clipPath[i]);
16327 }
16328
16329 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
16330
16331 // Emit both trailing handles UNCONDITIONALLY in parseDwg order:
16332 // imagedef (hard pointer, code 5) then imagedefreactor (hard owner, code 3).
16333 dwgBufferW *hb = handleBuf ? handleBuf : buf;
16334 auto makeHandle = [](std::uint8_t code, std::uint32_t r) {
16335 dwgHandle h;
16336 h.code = (r == 0) ? 0 : code;
16337 h.ref = r;
16338 h.size = 0;
16339 if (r != 0) { std::uint32_t t = r; while (t != 0) { t >>= 8; ++h.size; } }
16340 return h;
16341 };
16342 hb->putHandle(makeHandle(5, ref)); // imagedef (340)
16343 hb->putHandle(makeHandle(3, m_imageDefReactorHandle)); // imagedefreactor (360)
16344 return true;
16345}
16346
16347bool DRW_Wipeout::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
16348 return DRW_Image::parseCode(code, reader);
16349}
16350
16351bool DRW_Wipeout::hasValidBoundary() const {
16352 return m_clipBoundaryType != 0 && hasValidClipBoundary();
16353}
16354
16355bool DRW_Wipeout::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
16356 if (buf == nullptr)
16357 return false;
16358
16359 // Image parsing is transactional; keep it on a second cursor because a
16360 // valid image payload is still invalid for WIPEOUT when it has no clip
16361 // boundary. The wrapper must preserve the same failure contract.
16362 dwgBuffer probe = buf->forkIndependent();
16363 if (!DRW_Image::parseDwg(version, &probe, bs)) {
16364 buf->invalidate();
16365 resetDwgState();
16366 return false;
16367 }
16368 if (!hasValidBoundary()) {
16369 buf->invalidate();
16370 resetDwgState();
16371 return false;
16372 }
16373 *buf = probe;
16374 return true;
16375}
16376
16377bool DRW_Wipeout::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
16378 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
16379 (void)bs; (void)strBuf;
16380 if (!validatePayloadFields()) {
16381 DRW_DBG("WIPEOUT has an invalid scalar or clip field\n")DRW_dbg::dbg("WIPEOUT has an invalid scalar or clip field\n");
16382 return false;
16383 }
16384 if (clipPath.size() > DRW_Image::kMaxClipVertices) {
16385 DRW_DBG("WIPEOUT clip vertices exceed DWG limit\n")DRW_dbg::dbg("WIPEOUT clip vertices exceed DWG limit\n");
16386 return false;
16387 }
16388 if (!hasValidBoundary()) {
16389 DRW_DBG("WIPEOUT has invalid clip boundary\n")DRW_dbg::dbg("WIPEOUT has invalid clip boundary\n");
16390 return false;
16391 }
16392 // WIPEOUT is fixed DWG entity type 1109. It is not a custom class and
16393 // must not depend on a file-local CLASSES ordinal.
16394 oType = dwgType::WIPEOUT;
16395 if (!encodeDwgCommon(version, buf)) return false;
16396
16397 buf->putBitLong(0);
16398 buf->putBitDouble(basePoint.x); buf->putBitDouble(basePoint.y); buf->putBitDouble(basePoint.z);
16399 buf->putBitDouble(secPoint.x); buf->putBitDouble(secPoint.y); buf->putBitDouble(secPoint.z);
16400 buf->putBitDouble(vVector.x); buf->putBitDouble(vVector.y); buf->putBitDouble(vVector.z);
16401 buf->putRawDouble(sizeu);
16402 buf->putRawDouble(sizev);
16403 buf->putBitShort(static_cast<std::uint16_t>(m_displayProps));
16404 buf->putBit(static_cast<std::uint8_t>(clip & 1));
16405 buf->putRawChar8(static_cast<std::uint8_t>(brightness));
16406 buf->putRawChar8(static_cast<std::uint8_t>(contrast));
16407 buf->putRawChar8(static_cast<std::uint8_t>(fade));
16408 if (version > DRW::AC1021) {
16409 buf->putBit(clipMode ? 1 : 0);
16410 }
16411 if (m_clipBoundaryType == 1) {
16412 buf->putBitShort(1);
16413 buf->put2RawDouble(clipPath[0]);
16414 buf->put2RawDouble(clipPath[1]);
16415 } else {
16416 buf->putBitShort(2);
16417 buf->putBitLong(static_cast<std::int32_t>(clipPath.size()));
16418 for (std::size_t i = 0; i < clipPath.size(); ++i)
16419 buf->put2RawDouble(clipPath[i]);
16420 }
16421
16422 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
16423
16424 dwgBufferW *hb = handleBuf ? handleBuf : buf;
16425 auto makeHandle = [](std::uint8_t code, std::uint32_t r) {
16426 dwgHandle h;
16427 h.code = (r == 0) ? 0 : code;
16428 h.ref = r;
16429 h.size = 0;
16430 if (r != 0) { std::uint32_t t = r; while (t != 0) { t >>= 8; ++h.size; } }
16431 return h;
16432 };
16433 hb->putHandle(makeHandle(5, ref));
16434 hb->putHandle(makeHandle(3, m_imageDefReactorHandle));
16435 return true;
16436}
16437
16438bool DRW_NavisworksModel::parseCode(
16439 int code, const std::unique_ptr<dxfReader>& reader) {
16440 if (code == 100) {
16441 const std::string subclass = reader->getString();
16442 if (subclass == "AcDbNavisworksModel")
16443 m_dxfInBody = true;
16444 return true;
16445 }
16446 if (!m_dxfInBody)
16447 return DRW_Entity::parseCode(code, reader);
16448
16449 switch (code) {
16450 case 70: {
16451 const std::int32_t value = reader->getInt32();
16452 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
16453 return false;
16454 flags = static_cast<std::uint16_t>(value);
16455 m_dxfSawFlags = true;
16456 return true;
16457 }
16458 case 340:
16459 definitionHandle = reader->getHandleString();
16460 m_dxfSawDefinition = true;
16461 return true;
16462 case 40:
16463 if (m_dxfTransformCount < kTransformSize) {
16464 transform[m_dxfTransformCount++] = reader->getDouble();
16465 return true;
16466 }
16467 if (m_dxfSawUnitFactor)
16468 return false;
16469 unitFactor = reader->getDouble();
16470 m_dxfSawUnitFactor = true;
16471 return true;
16472 default:
16473 return DRW_Entity::parseCode(code, reader);
16474 }
16475}
16476
16477bool DRW_NavisworksModel::finalizeDxf() {
16478 return m_dxfTransformCount == kTransformSize && m_dxfSawUnitFactor
16479 && std::isfinite(unitFactor)
16480 && std::all_of(transform.begin(), transform.end(),
16481 [](double value) { return std::isfinite(value); });
16482}
16483
16484void DRW_NavisworksModel::resetDwgState() {
16485 DRW_Entity::reset();
16486 flags = 0;
16487 definitionHandle = 0;
16488 transform = {1.0, 0.0, 0.0, 0.0,
16489 0.0, 1.0, 0.0, 0.0,
16490 0.0, 0.0, 1.0, 0.0,
16491 0.0, 0.0, 0.0, 1.0};
16492 unitFactor = 1.0;
16493}
16494
16495bool DRW_NavisworksModel::parseDwg(DRW::Version version, dwgBuffer *buf,
16496 std::uint32_t bs) {
16497 resetDwgState();
16498 dwgBuffer *sourceBuf = buf;
16499 auto fail = [this, sourceBuf]() {
16500 if (sourceBuf != nullptr)
16501 sourceBuf->invalidate();
16502 resetDwgState();
16503 return false;
16504 };
16505 if (sourceBuf == nullptr)
16506 return fail();
16507 if (version < DRW::AC1015)
16508 return fail();
16509
16510 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
16511 dwgBuffer stringBuffer = bodyProbe.forkIndependent();
16512 dwgBuffer *stringBuf = version > DRW::AC1018 ? &stringBuffer : &bodyProbe;
16513 if (!DRW_Entity::parseDwg(version, &bodyProbe, stringBuf, bs))
16514 return fail();
16515
16516 const std::uint64_t bodyEndBit = dwgDataEndBit;
16517 std::uint64_t handleEndBit = 0;
16518 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
16519 handleEndBit))
16520 return fail();
16521 // BS is an unsigned bit-short at the codec boundary. The flags are a
16522 // mask, so values with bit 15 set must survive instead of being rejected
16523 // through an intermediate signed conversion.
16524 std::uint16_t parsedFlags = 0;
16525 if (!readBoundedBitShort(bodyProbe, bodyEndBit, parsedFlags))
16526 return fail();
16527
16528 std::uint32_t parsedDefinitionHandle = 0;
16529 if (version <= DRW::AC1018) {
16530 dwgHandle definition;
16531 if (!readBoundedDwgHandle(bodyProbe, handleEndBit, 0, false,
16532 definition))
16533 return fail();
16534 parsedDefinitionHandle = definition.ref;
16535 }
16536
16537 std::array<double, 16> parsedTransform{};
16538 for (double& value : parsedTransform) {
16539 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, value))
16540 return fail();
16541 }
16542 double parsedUnitFactor = 0.0;
16543 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, parsedUnitFactor))
16544 return fail();
16545 if (!std::all_of(parsedTransform.begin(), parsedTransform.end(),
16546 [](double value) { return std::isfinite(value); })
16547 || !std::isfinite(parsedUnitFactor)) {
16548 return fail();
16549 }
16550
16551 if (!DRW_Entity::parseDwgEntHandle(version, &bodyProbe, true,
16552 handleEndBit))
16553 return fail();
16554 dwgBuffer handleProbe = bodyProbe.forkIndependent();
16555 if (version > DRW::AC1018) {
16556 dwgHandle definition;
16557 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
16558 definition))
16559 return fail();
16560 parsedDefinitionHandle = definition.ref;
16561 }
16562 if (!handleProbe.isGood() || !stringBuf->isGood())
16563 return fail();
16564 flags = parsedFlags;
16565 transform = parsedTransform;
16566 unitFactor = parsedUnitFactor;
16567 definitionHandle = parsedDefinitionHandle;
16568 *sourceBuf = handleProbe;
16569 return true;
16570}
16571
16572bool DRW_NavisworksModel::encodeDwg(DRW::Version version, dwgBufferW *buf,
16573 std::uint32_t bs, dwgBufferW *strBuf,
16574 dwgBufferW *handleBuf) {
16575 (void)bs;
16576 if (version < DRW::AC1015
16577 || !std::isfinite(unitFactor)
16578 || !std::all_of(transform.begin(), transform.end(),
16579 [](double value) { return std::isfinite(value); }))
16580 return false;
16581
16582 oType = kDwgClassNum;
16583 if (!encodeDwgCommon(version, buf, strBuf))
16584 return false;
16585
16586 buf->putBitShort(flags);
16587 dwgBufferW *hb = handleBuf ? handleBuf : buf;
16588 auto makeHandle = [](std::uint8_t code, std::uint32_t ref) {
16589 dwgHandle handle;
16590 handle.code = ref == 0 ? 0 : code;
16591 handle.ref = ref;
16592 handle.size = 0;
16593 for (std::uint32_t value = ref; value != 0; value >>= 8)
16594 ++handle.size;
16595 return handle;
16596 };
16597
16598 // AC1015/AC1018 place this entity-specific handle before the matrix in
16599 // the legacy data stream. AC1021+ places it after the common handles.
16600 if (version <= DRW::AC1018)
16601 buf->putHandle(makeHandle(2, definitionHandle));
16602 for (double value : transform)
16603 buf->putBitDouble(value);
16604 buf->putBitDouble(unitFactor);
16605
16606 if (!DRW_Entity::encodeDwgEntHandle(version, buf, handleBuf))
16607 return false;
16608 if (version > DRW::AC1018)
16609 hb->putHandle(makeHandle(2, definitionHandle));
16610 return true;
16611}
16612
16613bool DRW_PointCloud::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
16614 if (code == 100) {
16615 m_dxfInBody = reader->getString() == "AcDbPointCloud";
16616 return true;
16617 }
16618 if (!m_dxfInBody)
16619 return DRW_Entity::parseCode(code, reader);
16620 switch (code) {
16621 case 70:
16622 m_dxfSawClassVersion = true;
16623 classVersion = reader->getInt32();
16624 break;
16625 case 10: origin.x = reader->getDouble(); break;
16626 case 20: origin.y = reader->getDouble(); break;
16627 case 30: origin.z = reader->getDouble(); break;
16628 case 1: savedFilename = reader->getUtf8String(); break;
16629 case 90:
16630 if (m_dxfSawSourceCount)
16631 return false;
16632 m_dxfSawSourceCount = true;
16633 sourceFileCount = reader->getInt32();
16634 if (!isValidCount(sourceFileCount,
16635 static_cast<std::int32_t>(kMaxItems))) {
16636 return false;
16637 }
16638 sourceFiles.clear();
16639 if (!DRW::reserve(sourceFiles, sourceFileCount))
16640 return false;
16641 break;
16642 case 2:
16643 if (sourceFiles.size() >= static_cast<std::size_t>(sourceFileCount))
16644 return false;
16645 sourceFiles.push_back(reader->getUtf8String());
16646 break;
16647 case 3: ucsName = reader->getUtf8String(); break;
16648 case 11: extentsMin.x = reader->getDouble(); break;
16649 case 21: extentsMin.y = reader->getDouble(); break;
16650 case 31: extentsMin.z = reader->getDouble(); break;
16651 case 12: extentsMax.x = reader->getDouble(); break;
16652 case 22: extentsMax.y = reader->getDouble(); break;
16653 case 32: extentsMax.z = reader->getDouble(); break;
16654 case 92: {
16655 if (m_dxfSawPointCount)
16656 return false;
16657 m_dxfSawPointCount = true;
16658 const std::int32_t count = reader->getInt32();
16659 if (count < 0)
16660 return false;
16661 pointCount = static_cast<std::uint64_t>(count);
16662 break;
16663 }
16664 case 13: ucsOrigin.x = reader->getDouble(); break;
16665 case 23: ucsOrigin.y = reader->getDouble(); break;
16666 case 33: ucsOrigin.z = reader->getDouble(); break;
16667 case 210: ucsXDirection.x = reader->getDouble(); break;
16668 case 220: ucsXDirection.y = reader->getDouble(); break;
16669 case 230: ucsXDirection.z = reader->getDouble(); break;
16670 case 211: ucsYDirection.x = reader->getDouble(); break;
16671 case 221: ucsYDirection.y = reader->getDouble(); break;
16672 case 231: ucsYDirection.z = reader->getDouble(); break;
16673 case 212: ucsZDirection.x = reader->getDouble(); break;
16674 case 222: ucsZDirection.y = reader->getDouble(); break;
16675 case 232: ucsZDirection.z = reader->getDouble(); break;
16676 case 330:
16677 if (!m_dxfInBody)
16678 return DRW_Entity::parseCode(code, reader);
16679 definitionHandle = static_cast<std::uint32_t>(reader->getHandleString());
16680 break;
16681 case 360: reactorHandle = static_cast<std::uint32_t>(reader->getHandleString()); break;
16682 case 71: intensityScheme = reader->getInt32(); break;
16683 case 40: intensityStyle.minIntensity = reader->getDouble(); break;
16684 case 41: intensityStyle.maxIntensity = reader->getDouble(); break;
16685 case 42: intensityStyle.lowThreshold = reader->getDouble(); break;
16686 case 43: intensityStyle.highThreshold = reader->getDouble(); break;
16687 default:
16688 return DRW_Entity::parseCode(code, reader);
16689 }
16690 return true;
16691}
16692
16693bool DRW_PointCloud::finalizeDxf() {
16694 return m_dxfSawClassVersion && m_dxfSawSourceCount && m_dxfSawPointCount
16695 && isValidCount(sourceFileCount,
16696 static_cast<std::int32_t>(kMaxItems))
16697 && sourceFiles.size() == static_cast<std::size_t>(sourceFileCount);
16698}
16699
16700void DRW_PointCloud::resetDwgState() {
16701 DRW_Entity::reset();
16702 classVersion = 0;
16703 origin = DRW_Coord{0.0, 0.0, 0.0};
16704 savedFilename.clear();
16705 sourceFileCount = 0;
16706 sourceFiles.clear();
16707 extentsMin = DRW_Coord{0.0, 0.0, 0.0};
16708 extentsMax = DRW_Coord{0.0, 0.0, 0.0};
16709 pointCount = 0;
16710 ucsName.clear();
16711 ucsOrigin = DRW_Coord{0.0, 0.0, 0.0};
16712 ucsXDirection = DRW_Coord{1.0, 0.0, 0.0};
16713 ucsYDirection = DRW_Coord{0.0, 1.0, 0.0};
16714 ucsZDirection = DRW_Coord{0.0, 0.0, 1.0};
16715 definitionHandle = 0;
16716 reactorHandle = 0;
16717 showIntensity = false;
16718 intensityScheme = 0;
16719 intensityStyle = DRW_PointCloudIntensityStyle{};
16720 showClipping = false;
16721 clippingCount = 0;
16722 clippings.clear();
16723 m_dxfInBody = false;
16724 m_dxfSawClassVersion = false;
16725 m_dxfSawSourceCount = false;
16726 m_dxfSawPointCount = false;
16727}
16728
16729bool DRW_PointCloud::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
16730 resetDwgState();
16731 dwgBuffer *sourceBuf = buf;
16732 auto fail = [this, sourceBuf]() {
16733 if (sourceBuf != nullptr)
16734 sourceBuf->invalidate();
16735 resetDwgState();
16736 return false;
16737 };
16738 if (sourceBuf == nullptr || version <= DRW::AC1018)
16739 return fail();
16740
16741 const auto finite = [](const DRW_Coord& point) {
16742 return std::isfinite(point.x) && std::isfinite(point.y)
16743 && std::isfinite(point.z);
16744 };
16745
16746 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
16747 dwgBuffer stringProbe = sourceBuf->forkIndependent();
16748 if (!DRW_Entity::parseDwg(version, &bodyProbe, &stringProbe, bs))
16749 return fail();
16750
16751 const std::uint64_t bodyEndBit = dwgDataEndBit;
16752 std::uint64_t stringEndBit = bodyEndBit;
16753 if (version > DRW::AC1021) {
16754 std::uint64_t ignoredBodyEndBit = 0;
16755 if (!entityBodyDataEndBit(stringProbe, version, objSize,
16756 ignoredBodyEndBit, &stringEndBit))
16757 return fail();
16758 }
16759 std::uint16_t parsedClassVersion = 0;
16760 if (!readBoundedBitShort(bodyProbe, bodyEndBit, parsedClassVersion)
16761 || !readBoundedBitCoord(bodyProbe, bodyEndBit, origin))
16762 return fail();
16763 classVersion = parsedClassVersion;
16764 if (!finite(origin))
16765 return fail();
16766 if (!readBoundedVariableText(stringProbe, stringEndBit, version,
16767 savedFilename))
16768 return fail();
16769 std::uint32_t declaredSourceFiles = 0;
16770 if (!readTableBodyCount(
16771 version, &bodyProbe, objSize,
16772 static_cast<std::uint32_t>(kMaxItems), 2,
16773 declaredSourceFiles)) {
16774 return fail();
16775 }
16776 sourceFileCount = static_cast<int>(declaredSourceFiles);
16777 sourceFiles.clear();
16778
16779 if (sourceFileCount == 0) {
16780 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, extentsMin)
16781 || !readBoundedBitCoord(bodyProbe, bodyEndBit, extentsMax)
16782 || !readBoundedRawLong64(bodyProbe, bodyEndBit, pointCount))
16783 return fail();
16784 if (!finite(extentsMin) || !finite(extentsMax))
16785 return fail();
16786 if (!readBoundedVariableText(stringProbe, stringEndBit, version,
16787 ucsName))
16788 return fail();
16789 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, ucsOrigin)
16790 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsXDirection)
16791 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsYDirection)
16792 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsZDirection))
16793 return fail();
16794 if (!finite(ucsOrigin) || !finite(ucsXDirection)
16795 || !finite(ucsYDirection) || !finite(ucsZDirection)) {
16796 return fail();
16797 }
16798
16799 if (version > DRW::AC1024) {
16800 std::uint16_t parsedIntensityScheme = 0;
16801 if (!readBoundedBit(bodyProbe, bodyEndBit, showIntensity)
16802 || !readBoundedBitShort(bodyProbe, bodyEndBit,
16803 parsedIntensityScheme)
16804 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
16805 intensityStyle.minIntensity)
16806 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
16807 intensityStyle.maxIntensity)
16808 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
16809 intensityStyle.lowThreshold)
16810 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
16811 intensityStyle.highThreshold))
16812 return fail();
16813 intensityScheme = static_cast<int>(parsedIntensityScheme);
16814 if (!std::isfinite(intensityStyle.minIntensity)
16815 || !std::isfinite(intensityStyle.maxIntensity)
16816 || !std::isfinite(intensityStyle.lowThreshold)
16817 || !std::isfinite(intensityStyle.highThreshold)) {
16818 return fail();
16819 }
16820 if (!readBoundedBit(bodyProbe, bodyEndBit, showClipping))
16821 return fail();
16822
16823 std::uint32_t declaredClippings = 0;
16824 if (!readTableBodyCount(
16825 version, &bodyProbe, objSize,
16826 static_cast<std::uint32_t>(kMaxItems), 2,
16827 declaredClippings)) {
16828 return fail();
16829 }
16830 clippingCount = static_cast<int>(declaredClippings);
16831 clippings.clear();
16832 if (!DRW::reserve(clippings, clippingCount))
16833 return fail();
16834 for (std::int32_t i = 0; i < clippingCount; ++i) {
16835 DRW_PointCloudClipping clipping;
16836 if (!readBoundedBit(bodyProbe, bodyEndBit,
16837 clipping.isInverted))
16838 return fail();
16839 std::uint16_t clippingType = 0;
16840 if (!readBoundedBitShort(bodyProbe, bodyEndBit, clippingType))
16841 return fail();
16842 clipping.type = clippingType;
16843 if (clipping.type < 1 || clipping.type > 3)
16844 return fail();
16845 std::uint32_t vertexCount = 2;
16846 if (clipping.type == 3
16847 && !readTableBodyCount(
16848 version, &bodyProbe, objSize,
16849 static_cast<std::uint32_t>(kMaxItems), 128,
16850 vertexCount)) {
16851 return fail();
16852 }
16853 clipping.vertexCount = static_cast<int>(vertexCount);
16854 if (!DRW::reserve(clipping.vertices, clipping.vertexCount))
16855 return fail();
16856 for (std::int32_t j = 0; j < clipping.vertexCount; ++j) {
16857 DRW_Coord vertex;
16858 if (!readBounded2RawDouble(bodyProbe, bodyEndBit, vertex))
16859 return fail();
16860 clipping.vertices.push_back(vertex);
16861 }
16862 if (clipping.type == 1) {
16863 if (!readBoundedBitDouble(bodyProbe, bodyEndBit,
16864 clipping.zMin)
16865 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
16866 clipping.zMax))
16867 return fail();
16868 }
16869 if (!std::all_of(
16870 clipping.vertices.begin(), clipping.vertices.end(),
16871 finite)
16872 || !std::isfinite(clipping.zMin)
16873 || !std::isfinite(clipping.zMax)) {
16874 return fail();
16875 }
16876 clippings.push_back(std::move(clipping));
16877 }
16878 } else {
16879 showIntensity = false;
16880 intensityScheme = 0;
16881 showClipping = false;
16882 clippingCount = 0;
16883 clippings.clear();
16884 }
16885 }
16886
16887 if (!DRW::reserve(sourceFiles, sourceFileCount))
16888 return fail();
16889 for (std::int32_t i = 0; i < sourceFileCount; ++i) {
16890 UTF8STRINGstd::string sourceFile;
16891 if (!readBoundedVariableText(stringProbe, stringEndBit, version,
16892 sourceFile))
16893 return fail();
16894 sourceFiles.push_back(std::move(sourceFile));
16895 }
16896
16897 if (!bodyProbe.isGood() || !stringProbe.isGood()
16898 || currentDwgBit(&bodyProbe) > bodyEndBit
16899 || currentDwgBit(&stringProbe) > stringEndBit)
16900 return fail();
16901
16902 std::uint64_t handleEndBit = 0;
16903 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
16904 handleEndBit))
16905 return fail();
16906 dwgBuffer handleProbe = bodyProbe.forkIndependent();
16907 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
16908 handleEndBit)
16909 || !handleProbe.isGood())
16910 return fail();
16911 std::uint32_t parsedDefinitionHandle = 0;
16912 std::uint32_t parsedReactorHandle = 0;
16913 if (version > DRW::AC1024 && sourceFileCount == 0) {
16914 dwgHandle definition;
16915 dwgHandle reactor;
16916 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
16917 definition)
16918 || !readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
16919 reactor))
16920 return fail();
16921 parsedDefinitionHandle = definition.ref;
16922 parsedReactorHandle = reactor.ref;
16923 }
16924 if (!handleProbe.isGood() || !stringProbe.isGood())
16925 return fail();
16926 definitionHandle = parsedDefinitionHandle;
16927 reactorHandle = parsedReactorHandle;
16928 *sourceBuf = handleProbe;
16929 return true;
16930}
16931
16932bool DRW_PointCloud::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
16933 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
16934 (void)bs;
16935 const auto finite = [](const DRW_Coord& point) {
16936 return std::isfinite(point.x) && std::isfinite(point.y)
16937 && std::isfinite(point.z);
16938 };
16939 if (version <= DRW::AC1018
16940 || classVersion > std::numeric_limits<std::uint16_t>::max()
16941 || sourceFileCount < 0
16942 || sourceFileCount > static_cast<int>(kMaxItems)
16943 || sourceFiles.size() != static_cast<std::size_t>(sourceFileCount)
16944 || clippingCount < 0
16945 || (sourceFileCount == 0 && clippings.size() > kMaxItems)
16946 || (sourceFileCount != 0
16947 && (clippingCount != 0 || !clippings.empty()
16948 || showIntensity || showClipping))
16949 || intensityScheme < 0
16950 || intensityScheme > std::numeric_limits<std::uint16_t>::max()
16951 || clippings.size() != static_cast<std::size_t>(clippingCount)) {
16952 return false;
16953 }
16954 if (!finite(origin) || !finite(extentsMin) || !finite(extentsMax)
16955 || !finite(ucsOrigin) || !finite(ucsXDirection)
16956 || !finite(ucsYDirection) || !finite(ucsZDirection)
16957 || !std::isfinite(intensityStyle.minIntensity)
16958 || !std::isfinite(intensityStyle.maxIntensity)
16959 || !std::isfinite(intensityStyle.lowThreshold)
16960 || !std::isfinite(intensityStyle.highThreshold)) {
16961 return false;
16962 }
16963 for (const DRW_PointCloudClipping& clipping : clippings) {
16964 if (clipping.type < 1 || clipping.type > 3
16965 || clipping.vertexCount < 0
16966 || clipping.vertexCount > static_cast<int>(kMaxItems)
16967 || clipping.vertices.size() != static_cast<std::size_t>(clipping.vertexCount)
16968 || (clipping.type != 3 && clipping.vertexCount != 2)
16969 || !std::isfinite(clipping.zMin)
16970 || !std::isfinite(clipping.zMax)) {
16971 return false;
16972 }
16973 for (const DRW_Coord& vertex : clipping.vertices) {
16974 if (!finite(vertex))
16975 return false;
16976 }
16977 }
16978
16979 oType = kDwgClassNum;
16980 if (!encodeDwgCommon(version, buf, strBuf))
16981 return false;
16982 dwgBufferW *sBuf = strBuf ? strBuf : buf;
16983 buf->putBitShort(static_cast<std::uint16_t>(classVersion));
16984 buf->put3BitDouble(origin);
16985 sBuf->putVariableText(version, savedFilename);
16986 buf->putBitLong(sourceFileCount);
16987 if (sourceFileCount == 0) {
16988 buf->put3BitDouble(extentsMin);
16989 buf->put3BitDouble(extentsMax);
16990 buf->putRawLong64(pointCount);
16991 sBuf->putVariableText(version, ucsName);
16992 buf->put3BitDouble(ucsOrigin);
16993 buf->put3BitDouble(ucsXDirection);
16994 buf->put3BitDouble(ucsYDirection);
16995 buf->put3BitDouble(ucsZDirection);
16996
16997 if (version > DRW::AC1024) {
16998 buf->putBit(showIntensity ? 1 : 0);
16999 buf->putBitShort(static_cast<std::uint16_t>(intensityScheme));
17000 buf->putBitDouble(intensityStyle.minIntensity);
17001 buf->putBitDouble(intensityStyle.maxIntensity);
17002 buf->putBitDouble(intensityStyle.lowThreshold);
17003 buf->putBitDouble(intensityStyle.highThreshold);
17004 buf->putBit(showClipping ? 1 : 0);
17005 buf->putBitLong(clippingCount);
17006 for (const DRW_PointCloudClipping& clipping : clippings) {
17007 buf->putBit(clipping.isInverted ? 1 : 0);
17008 buf->putBitShort(static_cast<std::uint16_t>(clipping.type));
17009 if (clipping.type == 3)
17010 buf->putBitLong(clipping.vertexCount);
17011 for (const DRW_Coord& vertex : clipping.vertices)
17012 buf->put2RawDouble(vertex);
17013 if (clipping.type == 1) {
17014 buf->putBitDouble(clipping.zMin);
17015 buf->putBitDouble(clipping.zMax);
17016 }
17017 }
17018 }
17019 }
17020 for (const UTF8STRINGstd::string& sourceFile : sourceFiles)
17021 sBuf->putVariableText(version, sourceFile);
17022
17023 if (!encodeDwgEntHandle(version, buf, handleBuf))
17024 return false;
17025 if (version > DRW::AC1024 && sourceFileCount == 0) {
17026 dwgBufferW *hBuf = handleBuf ? handleBuf : buf;
17027 auto putHandle = [hBuf](std::uint8_t code, std::uint32_t ref) {
17028 dwgHandle handle;
17029 handle.code = ref == 0 ? 0 : code;
17030 handle.ref = ref;
17031 handle.size = 0;
17032 for (std::uint32_t value = ref; value != 0; value >>= 8)
17033 ++handle.size;
17034 hBuf->putHandle(handle);
17035 };
17036 putHandle(5, definitionHandle);
17037 putHandle(3, reactorHandle);
17038 }
17039 return true;
17040}
17041
17042bool DRW_PointCloudEx::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
17043 if (code == 100) {
17044 m_dxfInBody = reader->getString() == "AcDbPointCloud";
17045 return true;
17046 }
17047 if (!m_dxfInBody)
17048 return DRW_Entity::parseCode(code, reader);
17049 switch (code) {
17050 case 70:
17051 if (m_dxfSeen70++ != 0) return false;
17052 classVersion = reader->getInt32();
17053 break;
17054 case 10: extentsMin.x = reader->getDouble(); break;
17055 case 20: extentsMin.y = reader->getDouble(); break;
17056 case 30: extentsMin.z = reader->getDouble(); break;
17057 case 11: extentsMax.x = reader->getDouble(); break;
17058 case 21: extentsMax.y = reader->getDouble(); break;
17059 case 31: extentsMax.z = reader->getDouble(); break;
17060 case 12: ucsOrigin.x = reader->getDouble(); break;
17061 case 22: ucsOrigin.y = reader->getDouble(); break;
17062 case 32: ucsOrigin.z = reader->getDouble(); break;
17063 case 210: ucsXDirection.x = reader->getDouble(); break;
17064 case 220: ucsXDirection.y = reader->getDouble(); break;
17065 case 230: ucsXDirection.z = reader->getDouble(); break;
17066 case 211: ucsYDirection.x = reader->getDouble(); break;
17067 case 221: ucsYDirection.y = reader->getDouble(); break;
17068 case 231: ucsYDirection.z = reader->getDouble(); break;
17069 case 212: ucsZDirection.x = reader->getDouble(); break;
17070 case 222: ucsZDirection.y = reader->getDouble(); break;
17071 case 232: ucsZDirection.z = reader->getDouble(); break;
17072 case 290:
17073 if (m_dxfHasCropping) {
17074 if (m_dxfSeen290 % 2 == 0)
17075 m_dxfCurrentCropping.isInside = reader->getBool();
17076 else
17077 m_dxfCurrentCropping.isInverted = reader->getBool();
17078 ++m_dxfSeen290;
17079 } else {
17080 if (m_dxfSeen290++ != 0) return false;
17081 isLocked = reader->getBool();
17082 }
17083 break;
17084 case 330:
17085 if (!m_dxfInBody)
17086 return DRW_Entity::parseCode(code, reader);
17087 definitionHandle = static_cast<std::uint32_t>(reader->getHandleString());
17088 break;
17089 case 360:
17090 reactorHandle = static_cast<std::uint32_t>(reader->getHandleString());
17091 break;
17092 case 1:
17093 switch (m_dxfTextCount++) {
17094 case 0: name = reader->getUtf8String(); break;
17095 case 1: intensityColorScheme = reader->getUtf8String(); break;
17096 case 2: currentColorScheme = reader->getUtf8String(); break;
17097 case 3: classificationColorScheme = reader->getUtf8String(); break;
17098 default: return false;
17099 }
17100 break;
17101 case 291: showIntensity = reader->getBool(); break;
17102 case 295: showCropping = reader->getBool(); break;
17103 case 71:
17104 if (m_dxfSeen71++ == 0)
17105 stylizationType = reader->getInt32();
17106 else if (m_dxfSeen71 == 2)
17107 intensityOutOfRangeBehavior = reader->getInt32();
17108 else
17109 return false;
17110 break;
17111 case 72: elevationOutOfRangeBehavior = reader->getInt32(); break;
17112 case 40:
17113 if (m_dxfSeen40++ == 0)
17114 elevationMin = reader->getDouble();
17115 else if (m_dxfSeen40 == 2)
17116 elevationMax = reader->getDouble();
17117 else
17118 return false;
17119 break;
17120 case 41: elevationMax = reader->getDouble(); break;
17121 case 90:
17122 if (m_dxfSeen90++ != 0) return false;
17123 intensityMin = reader->getInt32();
17124 break;
17125 case 91:
17126 if (m_dxfSeen91++ != 0) return false;
17127 intensityMax = reader->getInt32();
17128 break;
17129 case 292: elevationApplyToFixedRange = reader->getBool(); break;
17130 case 293: intensityAsGradient = reader->getBool(); break;
17131 case 294: elevationAsGradient = reader->getBool(); break;
17132 case 92: {
17133 if (m_dxfSawCroppingCount) return false;
17134 const std::int32_t count = reader->getInt32();
17135 if (!isValidCount(count, static_cast<std::int32_t>(kMaxItems)))
17136 return false;
17137 croppingCount = count;
17138 m_dxfSawCroppingCount = true;
17139 break;
17140 }
17141 case 93:
17142 if (m_dxfHasCropping) {
17143 if (m_dxfSeen93++ != 0) return false;
17144 m_dxfCurrentCropping.pointCount = reader->getInt32();
17145 if (!isValidCount(m_dxfCurrentCropping.pointCount,
17146 static_cast<std::int32_t>(kMaxItems))) {
17147 return false;
17148 }
17149 m_dxfCurrentCropping.points.clear();
17150 if (!DRW::reserve(m_dxfCurrentCropping.points,
17151 m_dxfCurrentCropping.pointCount))
17152 return false;
17153 } else if (m_dxfSawCroppingCount && croppingCount == 0) {
17154 if (m_dxfSeen93++ == 0)
17155 unknownInt0 = reader->getInt32();
17156 else if (m_dxfSeen93 == 2)
17157 unknownInt1 = reader->getInt32();
17158 else
17159 return false;
17160 } else {
17161 return false;
17162 }
17163 break;
17164 case 280:
17165 if (!m_dxfSawCroppingCount || croppingCount == 0
17166 || !finishDxfCropping()) {
17167 return false;
17168 }
17169 m_dxfCurrentCropping = DRW_PointCloudExCropping{};
17170 m_dxfCurrentCropping.type = reader->getInt32();
17171 if (m_dxfCurrentCropping.type < 0
17172 || m_dxfCurrentCropping.type > std::numeric_limits<std::uint16_t>::max()) {
17173 return false;
17174 }
17175 m_dxfHasCropping = true;
17176 m_dxfSeen290 = 0;
17177 m_dxfSeen93 = 0;
17178 m_dxfDirectionComponent = 0;
17179 m_dxfCropPlaneComponent = 0;
17180 m_dxfCropPointOpen = false;
17181 break;
17182 case 13:
17183 case 23:
17184 case 33:
17185 if (!m_dxfHasCropping) {
17186 if (code == 13) ucsOrigin.x = reader->getDouble();
17187 else if (code == 23) ucsOrigin.y = reader->getDouble();
17188 else ucsOrigin.z = reader->getDouble();
17189 } else if (m_dxfCropPlaneComponent < 3) {
17190 if (code == 13 && m_dxfCropPlaneComponent != 0) return false;
17191 if (code == 23 && m_dxfCropPlaneComponent != 1) return false;
17192 if (code == 33 && m_dxfCropPlaneComponent != 2) return false;
17193 const double value = reader->getDouble();
17194 if (code == 13) m_dxfCurrentCropping.cropPlane.x = value;
17195 else if (code == 23) m_dxfCurrentCropping.cropPlane.y = value;
17196 else m_dxfCurrentCropping.cropPlane.z = value;
17197 ++m_dxfCropPlaneComponent;
17198 } else {
17199 if (code == 13) {
17200 if (m_dxfCropPointOpen) return false;
17201 m_dxfCropPoint = DRW_Coord{};
17202 m_dxfCropPointOpen = true;
17203 } else if (!m_dxfCropPointOpen) {
17204 return false;
17205 }
17206 const double value = reader->getDouble();
17207 if (code == 13) m_dxfCropPoint.x = value;
17208 else if (code == 23) m_dxfCropPoint.y = value;
17209 else m_dxfCropPoint.z = value;
17210 if (code == 33) {
17211 m_dxfCurrentCropping.points.push_back(m_dxfCropPoint);
17212 m_dxfCropPointOpen = false;
17213 }
17214 }
17215 break;
17216 case 213:
17217 case 223:
17218 case 233: {
17219 if (!m_dxfHasCropping || m_dxfDirectionComponent >= 6)
17220 return false;
17221 const double value = reader->getDouble();
17222 DRW_Coord *direction = m_dxfDirectionComponent < 3
17223 ? &m_dxfCurrentCropping.cropXDirection
17224 : &m_dxfCurrentCropping.cropYDirection;
17225 const int component = static_cast<int>(m_dxfDirectionComponent % 3);
17226 if ((component == 0 && code != 213)
17227 || (component == 1 && code != 223)
17228 || (component == 2 && code != 233)) {
17229 return false;
17230 }
17231 if (component == 0) direction->x = value;
17232 else if (component == 1) direction->y = value;
17233 else direction->z = value;
17234 ++m_dxfDirectionComponent;
17235 break;
17236 }
17237 default:
17238 return DRW_Entity::parseCode(code, reader);
17239 }
17240 return true;
17241}
17242
17243bool DRW_PointCloudEx::finishDxfCropping() {
17244 if (!m_dxfHasCropping)
17245 return true;
17246 if (m_dxfSeen290 != 2 || m_dxfDirectionComponent != 6
17247 || m_dxfCropPlaneComponent != 3 || m_dxfCropPointOpen
17248 || m_dxfSeen93 != 1
17249 || m_dxfCurrentCropping.points.size()
17250 != static_cast<std::size_t>(m_dxfCurrentCropping.pointCount)) {
17251 return false;
17252 }
17253 croppings.push_back(m_dxfCurrentCropping);
17254 m_dxfHasCropping = false;
17255 return true;
17256}
17257
17258bool DRW_PointCloudEx::finalizeDxf() {
17259 if (!finishDxfCropping())
17260 return false;
17261 if (m_dxfSeen70 != 1 || !m_dxfSawCroppingCount)
17262 return false;
17263 if (croppingCount == 0)
17264 return m_dxfSeen93 == 2 && croppings.empty();
17265 return croppings.size() == static_cast<std::size_t>(croppingCount);
17266}
17267
17268void DRW_PointCloudEx::resetDwgState() {
17269 DRW_Entity::reset();
17270 classVersion = 0;
17271 extentsMin = DRW_Coord{0.0, 0.0, 0.0};
17272 extentsMax = DRW_Coord{0.0, 0.0, 0.0};
17273 ucsOrigin = DRW_Coord{0.0, 0.0, 0.0};
17274 ucsXDirection = DRW_Coord{1.0, 0.0, 0.0};
17275 ucsYDirection = DRW_Coord{0.0, 1.0, 0.0};
17276 ucsZDirection = DRW_Coord{0.0, 0.0, 1.0};
17277 isLocked = false;
17278 definitionHandle = 0;
17279 reactorHandle = 0;
17280 name.clear();
17281 showIntensity = false;
17282 showCropping = false;
17283 croppingCount = 0;
17284 unknownInt0 = 0;
17285 unknownInt1 = 0;
17286 stylizationType = 0;
17287 intensityColorScheme.clear();
17288 currentColorScheme.clear();
17289 classificationColorScheme.clear();
17290 elevationMin = 0.0;
17291 elevationMax = 0.0;
17292 intensityMin = 0.0;
17293 intensityMax = 0.0;
17294 intensityOutOfRangeBehavior = 0;
17295 elevationOutOfRangeBehavior = 0;
17296 elevationApplyToFixedRange = false;
17297 intensityAsGradient = false;
17298 elevationAsGradient = false;
17299 croppings.clear();
17300 m_dxfCurrentCropping = DRW_PointCloudExCropping{};
17301 m_dxfTextCount = 0;
17302 m_dxfSeen70 = 0;
17303 m_dxfSeen71 = 0;
17304 m_dxfSeen90 = 0;
17305 m_dxfSeen91 = 0;
17306 m_dxfSeen93 = 0;
17307 m_dxfSeen290 = 0;
17308 m_dxfSeen40 = 0;
17309 m_dxfDirectionComponent = 0;
17310 m_dxfCropPlaneComponent = 0;
17311 m_dxfSawCroppingCount = false;
17312 m_dxfInBody = false;
17313 m_dxfHasCropping = false;
17314 m_dxfCropPointOpen = false;
17315 m_dxfCropPoint = DRW_Coord{0.0, 0.0, 0.0};
17316}
17317
17318bool DRW_PointCloudEx::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
17319 resetDwgState();
17320 dwgBuffer *sourceBuf = buf;
17321 auto fail = [this, sourceBuf]() {
17322 if (sourceBuf != nullptr)
17323 sourceBuf->invalidate();
17324 resetDwgState();
17325 return false;
17326 };
17327 if (sourceBuf == nullptr || version <= DRW::AC1024)
17328 return fail();
17329
17330 const auto finite = [](const DRW_Coord& point) {
17331 return std::isfinite(point.x) && std::isfinite(point.y)
17332 && std::isfinite(point.z);
17333 };
17334 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
17335 dwgBuffer stringProbe = sourceBuf->forkIndependent();
17336 if (!DRW_Entity::parseDwg(version, &bodyProbe, &stringProbe, bs))
17337 return fail();
17338
17339 const std::uint64_t bodyEndBit = dwgDataEndBit;
17340 std::uint64_t stringEndBit = bodyEndBit;
17341 if (version > DRW::AC1021) {
17342 std::uint64_t ignoredBodyEndBit = 0;
17343 if (!entityBodyDataEndBit(stringProbe, version, objSize,
17344 ignoredBodyEndBit, &stringEndBit))
17345 return fail();
17346 }
17347 std::uint16_t parsedClassVersion = 0;
17348 if (!readBoundedBitShort(bodyProbe, bodyEndBit, parsedClassVersion)
17349 || !readBoundedBitCoord(bodyProbe, bodyEndBit, extentsMin)
17350 || !readBoundedBitCoord(bodyProbe, bodyEndBit, extentsMax)
17351 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsOrigin)
17352 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsXDirection)
17353 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsYDirection)
17354 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsZDirection))
17355 return fail();
17356 classVersion = parsedClassVersion;
17357 if (!finite(extentsMin) || !finite(extentsMax) || !finite(ucsOrigin)
17358 || !finite(ucsXDirection) || !finite(ucsYDirection)
17359 || !finite(ucsZDirection)) {
17360 return fail();
17361 }
17362 if (!readBoundedBit(bodyProbe, bodyEndBit, isLocked))
17363 return fail();
17364 if (!readBoundedVariableText(stringProbe, stringEndBit, version, name))
17365 return fail();
17366 if (!readBoundedBit(bodyProbe, bodyEndBit, showIntensity)
17367 || !readBoundedBit(bodyProbe, bodyEndBit, showCropping))
17368 return fail();
17369 std::uint32_t declaredCroppings = 0;
17370 if (!readTableBodyCount(
17371 version, &bodyProbe, objSize,
17372 static_cast<std::uint32_t>(kMaxItems), 24,
17373 declaredCroppings)) {
17374 return fail();
17375 }
17376 croppingCount = static_cast<int>(declaredCroppings);
17377 croppings.clear();
17378 if (croppingCount == 0) {
17379 std::uint16_t parsedStylizationType = 0;
17380 if (!readBoundedBitLong(bodyProbe, bodyEndBit, unknownInt0)
17381 || !readBoundedBitLong(bodyProbe, bodyEndBit, unknownInt1)
17382 || !readBoundedBitShort(bodyProbe, bodyEndBit,
17383 parsedStylizationType))
17384 return fail();
17385 stylizationType = parsedStylizationType;
17386 if (!readBoundedVariableText(stringProbe, stringEndBit, version,
17387 intensityColorScheme)
17388 || !readBoundedVariableText(stringProbe, stringEndBit, version,
17389 currentColorScheme)
17390 || !readBoundedVariableText(stringProbe, stringEndBit, version,
17391 classificationColorScheme))
17392 return fail();
17393 std::int32_t parsedIntensityMin = 0;
17394 std::int32_t parsedIntensityMax = 0;
17395 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, elevationMin)
17396 || !readBoundedBitDouble(bodyProbe, bodyEndBit, elevationMax)
17397 || !readBoundedBitLong(bodyProbe, bodyEndBit, parsedIntensityMin)
17398 || !readBoundedBitLong(bodyProbe, bodyEndBit, parsedIntensityMax))
17399 return fail();
17400 intensityMin = static_cast<double>(parsedIntensityMin);
17401 intensityMax = static_cast<double>(parsedIntensityMax);
17402 if (!std::isfinite(elevationMin) || !std::isfinite(elevationMax))
17403 return fail();
17404 std::uint16_t parsedIntensityBehavior = 0;
17405 std::uint16_t parsedElevationBehavior = 0;
17406 if (!readBoundedBitShort(bodyProbe, bodyEndBit,
17407 parsedIntensityBehavior)
17408 || !readBoundedBitShort(bodyProbe, bodyEndBit,
17409 parsedElevationBehavior)
17410 || !readBoundedBit(bodyProbe, bodyEndBit,
17411 elevationApplyToFixedRange)
17412 || !readBoundedBit(bodyProbe, bodyEndBit, intensityAsGradient)
17413 || !readBoundedBit(bodyProbe, bodyEndBit, elevationAsGradient))
17414 return fail();
17415 intensityOutOfRangeBehavior = parsedIntensityBehavior;
17416 elevationOutOfRangeBehavior = parsedElevationBehavior;
17417 } else {
17418 unknownInt0 = 0;
17419 unknownInt1 = 0;
17420 }
17421 if (!bodyProbe.isGood() || !stringProbe.isGood()
17422 || currentDwgBit(&bodyProbe) > bodyEndBit
17423 || currentDwgBit(&stringProbe) > stringEndBit)
17424 return fail();
17425
17426 if (!DRW::reserve(croppings, croppingCount))
17427 return fail();
17428 for (std::uint32_t i = 0; i < declaredCroppings; ++i) {
17429 DRW_PointCloudExCropping cropping;
17430 std::uint16_t croppingType = 0;
17431 if (!readBoundedBitShort(bodyProbe, bodyEndBit, croppingType)
17432 || !readBoundedBit(bodyProbe, bodyEndBit, cropping.isInside)
17433 || !readBoundedBit(bodyProbe, bodyEndBit, cropping.isInverted)
17434 || !readBoundedBitCoord(bodyProbe, bodyEndBit,
17435 cropping.cropPlane)
17436 || !readBoundedBitCoord(bodyProbe, bodyEndBit,
17437 cropping.cropXDirection)
17438 || !readBoundedBitCoord(bodyProbe, bodyEndBit,
17439 cropping.cropYDirection)) {
17440 return fail();
17441 }
17442 cropping.type = static_cast<int>(croppingType);
17443 if (!finite(cropping.cropPlane) || !finite(cropping.cropXDirection)
17444 || !finite(cropping.cropYDirection)) {
17445 return fail();
17446 }
17447 std::uint32_t declaredPoints = 0;
17448 if (!readTableBodyCount(
17449 version, &bodyProbe, objSize,
17450 static_cast<std::uint32_t>(kMaxItems),
17451 6, declaredPoints)) {
17452 return fail();
17453 }
17454 std::uint64_t pointBits = 0;
17455 if (!dwgSafety::multiply(declaredPoints, 6, pointBits)
17456 || !proxyEntityHasBits(bodyProbe, bodyEndBit, pointBits)) {
17457 return fail();
17458 }
17459 cropping.pointCount = static_cast<int>(declaredPoints);
17460 if (!DRW::reserve(cropping.points, cropping.pointCount))
17461 return fail();
17462 for (std::uint32_t j = 0; j < declaredPoints; ++j) {
17463 DRW_Coord point;
17464 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, point))
17465 return fail();
17466 cropping.points.push_back(point);
17467 }
17468 if (!std::all_of(cropping.points.begin(), cropping.points.end(), finite))
17469 return fail();
17470 croppings.push_back(std::move(cropping));
17471 }
17472
17473 std::uint64_t handleEndBit = 0;
17474 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
17475 handleEndBit))
17476 return fail();
17477 dwgBuffer handleProbe = bodyProbe.forkIndependent();
17478 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
17479 handleEndBit)
17480 || !handleProbe.isGood())
17481 return fail();
17482 dwgHandle definition;
17483 dwgHandle reactor;
17484 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
17485 definition)
17486 || !readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
17487 reactor))
17488 return fail();
17489 const std::uint32_t parsedDefinitionHandle = definition.ref;
17490 const std::uint32_t parsedReactorHandle = reactor.ref;
17491 if (!handleProbe.isGood() || !stringProbe.isGood())
17492 return fail();
17493 definitionHandle = parsedDefinitionHandle;
17494 reactorHandle = parsedReactorHandle;
17495 *sourceBuf = handleProbe;
17496 return true;
17497}
17498
17499bool DRW_PointCloudEx::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
17500 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
17501 (void)bs;
17502 const auto finite = [](const DRW_Coord& point) {
17503 return std::isfinite(point.x) && std::isfinite(point.y)
17504 && std::isfinite(point.z);
17505 };
17506 const auto fitsUint16 = [](int value) {
17507 return value >= 0
17508 && value <= std::numeric_limits<std::uint16_t>::max();
17509 };
17510 if (version <= DRW::AC1024
17511 || classVersion > std::numeric_limits<std::uint16_t>::max()
17512 || croppingCount < 0
17513 || croppingCount > static_cast<int>(kMaxItems)
17514 || croppings.size() != static_cast<std::size_t>(croppingCount)) {
17515 return false;
17516 }
17517 for (const DRW_PointCloudExCropping& cropping : croppings) {
17518 if (cropping.pointCount < 0
17519 || cropping.pointCount > static_cast<int>(kMaxItems)
17520 || cropping.points.size() != static_cast<std::size_t>(cropping.pointCount)) {
17521 return false;
17522 }
17523 if (!fitsUint16(cropping.type) || !finite(cropping.cropPlane)
17524 || !finite(cropping.cropXDirection)
17525 || !finite(cropping.cropYDirection)) {
17526 return false;
17527 }
17528 for (const DRW_Coord& point : cropping.points) {
17529 if (!finite(point))
17530 return false;
17531 }
17532 }
17533 if (!finite(extentsMin) || !finite(extentsMax) || !finite(ucsOrigin)
17534 || !finite(ucsXDirection) || !finite(ucsYDirection)
17535 || !finite(ucsZDirection) || !std::isfinite(elevationMin)
17536 || !std::isfinite(elevationMax)
17537 || !fitsUint16(stylizationType)
17538 || !fitsUint16(intensityOutOfRangeBehavior)
17539 || !fitsUint16(elevationOutOfRangeBehavior)) {
17540 return false;
17541 }
17542 const auto integral32 = [](double value) {
17543 return std::isfinite(value)
17544 && value >= static_cast<double>(std::numeric_limits<std::int32_t>::min())
17545 && value <= static_cast<double>(std::numeric_limits<std::int32_t>::max())
17546 && std::floor(value) == value;
17547 };
17548 if (croppingCount == 0
17549 && (!integral32(intensityMin) || !integral32(intensityMax))) {
17550 return false;
17551 }
17552
17553 oType = kDwgClassNum;
17554 if (!encodeDwgCommon(version, buf, strBuf))
17555 return false;
17556 dwgBufferW *sBuf = strBuf ? strBuf : buf;
17557 buf->putBitShort(static_cast<std::uint16_t>(classVersion));
17558 buf->put3BitDouble(extentsMin);
17559 buf->put3BitDouble(extentsMax);
17560 buf->put3BitDouble(ucsOrigin);
17561 buf->put3BitDouble(ucsXDirection);
17562 buf->put3BitDouble(ucsYDirection);
17563 buf->put3BitDouble(ucsZDirection);
17564 buf->putBit(isLocked ? 1 : 0);
17565 sBuf->putVariableText(version, name);
17566 buf->putBit(showIntensity ? 1 : 0);
17567 buf->putBit(showCropping ? 1 : 0);
17568 buf->putBitLong(croppingCount);
17569 if (croppingCount == 0) {
17570 buf->putBitLong(unknownInt0);
17571 buf->putBitLong(unknownInt1);
17572 buf->putBitShort(static_cast<std::uint16_t>(stylizationType));
17573 sBuf->putVariableText(version, intensityColorScheme);
17574 sBuf->putVariableText(version, currentColorScheme);
17575 sBuf->putVariableText(version, classificationColorScheme);
17576 buf->putBitDouble(elevationMin);
17577 buf->putBitDouble(elevationMax);
17578 buf->putBitLong(static_cast<std::int32_t>(intensityMin));
17579 buf->putBitLong(static_cast<std::int32_t>(intensityMax));
17580 buf->putBitShort(static_cast<std::uint16_t>(intensityOutOfRangeBehavior));
17581 buf->putBitShort(static_cast<std::uint16_t>(elevationOutOfRangeBehavior));
17582 buf->putBit(elevationApplyToFixedRange ? 1 : 0);
17583 buf->putBit(intensityAsGradient ? 1 : 0);
17584 buf->putBit(elevationAsGradient ? 1 : 0);
17585 }
17586 for (const DRW_PointCloudExCropping& cropping : croppings) {
17587 buf->putBitShort(static_cast<std::uint16_t>(cropping.type));
17588 buf->putBit(cropping.isInside ? 1 : 0);
17589 buf->putBit(cropping.isInverted ? 1 : 0);
17590 buf->put3BitDouble(cropping.cropPlane);
17591 buf->put3BitDouble(cropping.cropXDirection);
17592 buf->put3BitDouble(cropping.cropYDirection);
17593 buf->putBitLong(cropping.pointCount);
17594 for (const DRW_Coord& point : cropping.points)
17595 buf->put3BitDouble(point);
17596 }
17597 if (!encodeDwgEntHandle(version, buf, handleBuf))
17598 return false;
17599
17600 dwgBufferW *hBuf = handleBuf ? handleBuf : buf;
17601 auto putHandle = [hBuf](std::uint8_t code, std::uint32_t ref) {
17602 dwgHandle handle;
17603 handle.code = ref == 0 ? 0 : code;
17604 handle.ref = ref;
17605 handle.size = 0;
17606 for (std::uint32_t value = ref; value != 0; value >>= 8)
17607 ++handle.size;
17608 hBuf->putHandle(handle);
17609 };
17610 putHandle(5, definitionHandle);
17611 putHandle(3, reactorHandle);
17612 return true;
17613}
17614
17615bool DRW_Surface::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
17616 switch (code) {
17617 case 1:
17618 case 3:
17619 // SURFACE ACIS payloads use text groups in the AcDbModelerGeometry
17620 // subclass. Keep the bytes verbatim; group 3 is a continuation of
17621 // the same payload and must not be interpreted as a surface field.
17622 if (!appendTextBytesChecked(rawAcisData, reader->getString(),
17623 dwgSafety::MaxBufferSize))
17624 return false;
17625 break;
17626 case 70:
17627 modelerFormatVersion = reader->getInt32();
17628 break;
17629 case 71:
17630 uIsolines = reader->getInt32();
17631 break;
17632 case 72:
17633 vIsolines = reader->getInt32();
17634 break;
17635 case 310:
17636 // AcDbEntity proxy graphics precede the AcDbModelerGeometry subclass,
17637 // so the announced bytes belong to the entity, not to the ACIS body.
17638 if (numProxyGraph > 0
17639 && proxyGraphics.size() < static_cast<std::size_t>(numProxyGraph))
17640 return DRW_Entity::parseCode(code, reader);
17641 {
17642 std::vector<std::uint8_t> decoded;
17643 if (!decodeHexBytes(reader->getString(), decoded))
17644 return false;
17645 if (!appendBytesChecked(rawAcisData, decoded,
17646 dwgSafety::MaxBufferSize))
17647 return false;
17648 }
17649 break;
17650 default:
17651 return DRW_Entity::parseCode(code, reader);
17652 }
17653 return true;
17654}
17655
17656bool DRW_ExtrudedSurface::parseCode(
17657 int code, const std::unique_ptr<dxfReader>& reader) {
17658 switch (code) {
17659 case 100: {
17660 const std::string marker = reader->getString();
17661 if (marker == "AcDbExtrudedSurface")
17662 m_dxfInSubtype = true;
17663 else if (marker == "AcDbModelerGeometry"
17664 || marker == "AcDbSurface")
17665 m_dxfInSubtype = false;
17666 break;
17667 }
17668 case 90:
17669 {
17670 // The class id, then the size of the 310 data that follows.
17671 const std::int32_t value = reader->getInt32();
17672 if (value < 0 || m_dxfDataSizeSeen)
17673 return false;
17674 if (m_dxfClassIdSeen) {
17675 m_dxfDataSizeSeen = true;
17676 break;
17677 }
17678 classId = static_cast<std::uint32_t>(value);
17679 m_dxfClassIdSeen = true;
17680 }
17681 break;
17682 case 310:
17683 // Extrusion data is not kept; it must not join the ACIS body.
17684 if (!m_dxfInSubtype)
17685 return DRW_Surface::parseCode(code, reader);
17686 break;
17687 case 10:
17688 sweepVector.x = reader->getDouble();
17689 break;
17690 case 20:
17691 sweepVector.y = reader->getDouble();
17692 break;
17693 case 30:
17694 sweepVector.z = reader->getDouble();
17695 break;
17696 case 11:
17697 referenceVector.x = reader->getDouble();
17698 break;
17699 case 21:
17700 referenceVector.y = reader->getDouble();
17701 break;
17702 case 31:
17703 referenceVector.z = reader->getDouble();
17704 break;
17705 case 40:
17706 if (m_dxfExtrudedTransformCount >= extrudedTransform.size())
17707 return false;
17708 extrudedTransform[m_dxfExtrudedTransformCount++] = reader->getDouble();
17709 break;
17710 case 41:
17711 // Code 41 is not part of the AcDbExtrudedSurface mapping. Keep an
17712 // unexpected value out of the common entity state.
17713 return false;
17714 case 42:
17715 if (!m_dxfInSubtype)
17716 return DRW_Surface::parseCode(code, reader);
17717 draftAngle = reader->getDouble();
17718 break;
17719 case 43:
17720 draftStartDistance = reader->getDouble();
17721 break;
17722 case 44:
17723 draftEndDistance = reader->getDouble();
17724 break;
17725 case 45:
17726 twistAngle = reader->getDouble();
17727 break;
17728 case 46:
17729 if (m_dxfSweepTransformCount >= sweepEntityTransform.size())
17730 return false;
17731 sweepEntityTransform[m_dxfSweepTransformCount++] = reader->getDouble();
17732 break;
17733 case 47:
17734 if (m_dxfPathTransformCount >= pathEntityTransform.size())
17735 return false;
17736 pathEntityTransform[m_dxfPathTransformCount++] = reader->getDouble();
17737 break;
17738 case 48:
17739 scaleFactor = reader->getDouble();
17740 break;
17741 case 49:
17742 alignAngle = reader->getDouble();
17743 break;
17744 case 70:
17745 if (!m_dxfInSubtype)
17746 return DRW_Surface::parseCode(code, reader);
17747 {
17748 const std::int32_t value = reader->getInt32();
17749 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
17750 return false;
17751 sweepAlignmentFlags = value;
17752 }
17753 break;
17754 case 71:
17755 if (!m_dxfInSubtype)
17756 return DRW_Surface::parseCode(code, reader);
17757 {
17758 const std::int32_t value = reader->getInt32();
17759 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
17760 return false;
17761 pathFlags = value;
17762 }
17763 break;
17764 case 290:
17765 solid = reader->getInt32() != 0;
17766 break;
17767 case 292:
17768 alignStart = reader->getInt32() != 0;
17769 break;
17770 case 293:
17771 bank = reader->getInt32() != 0;
17772 break;
17773 case 294:
17774 basePointSet = reader->getInt32() != 0;
17775 break;
17776 case 295:
17777 sweepEntityTransformComputed = reader->getInt32() != 0;
17778 break;
17779 case 296:
17780 pathEntityTransformComputed = reader->getInt32() != 0;
17781 break;
17782 default:
17783 return DRW_Surface::parseCode(code, reader);
17784 }
17785 return true;
17786}
17787
17788bool DRW_ExtrudedSurface::finalizeDxf() const {
17789 if (!m_dxfClassIdSeen)
17790 return true;
17791 return m_dxfInSubtype
17792 && m_dxfExtrudedTransformCount == extrudedTransform.size()
17793 && m_dxfSweepTransformCount == sweepEntityTransform.size()
17794 && m_dxfPathTransformCount == pathEntityTransform.size();
17795}
17796
17797bool DRW_SweptSurface::parseCode(
17798 int code, const std::unique_ptr<dxfReader>& reader) {
17799 switch (code) {
17800 case 100: {
17801 const std::string marker = reader->getString();
17802 if (marker == "AcDbSweptSurface")
17803 m_dxfInSubtype = true;
17804 else if (marker == "AcDbModelerGeometry"
17805 || marker == "AcDbSurface")
17806 m_dxfInSubtype = false;
17807 break;
17808 }
17809 case 90:
17810 if (!m_dxfInSubtype)
17811 return DRW_Surface::parseCode(code, reader);
17812 m_dxfTypedFieldSeen = true;
17813 {
17814 // Each entity id may be followed by the size of its 310 data.
17815 const std::int32_t value = reader->getInt32();
17816 if (value < 0)
17817 return false;
17818 if (m_dxfSweepEntityIdSeen) {
17819 if (m_dxfDataSizeCount >= (m_dxfPathEntityIdSeen ? 2 : 1))
17820 return false;
17821 ++m_dxfDataSizeCount;
17822 break;
17823 }
17824 sweepEntityId = static_cast<std::uint32_t>(value);
17825 m_dxfSweepEntityIdSeen = true;
17826 }
17827 break;
17828 case 91:
17829 if (!m_dxfInSubtype)
17830 return DRW_Surface::parseCode(code, reader);
17831 if (m_dxfPathEntityIdSeen)
17832 return false;
17833 m_dxfTypedFieldSeen = true;
17834 {
17835 const std::int32_t value = reader->getInt32();
17836 if (value < 0)
17837 return false;
17838 pathEntityId = static_cast<std::uint32_t>(value);
17839 m_dxfPathEntityIdSeen = true;
17840 }
17841 break;
17842 case 310: {
17843 if (!m_dxfInSubtype)
17844 return DRW_Surface::parseCode(code, reader);
17845 m_dxfTypedFieldSeen = true;
17846 std::vector<std::uint8_t> decoded;
17847 if (!decodeHexBytes(reader->getString(), decoded))
17848 return false;
17849 auto& destination = m_dxfPathEntityIdSeen ? pathData : sweepData;
17850 if (!appendBytesChecked(destination, decoded, kMaxSweepDataSize))
17851 return false;
17852 break;
17853 }
17854 case 11:
17855 if (!m_dxfInSubtype)
17856 return DRW_Surface::parseCode(code, reader);
17857 m_dxfTypedFieldSeen = true;
17858 referenceVector.x = reader->getDouble();
17859 break;
17860 case 21:
17861 if (!m_dxfInSubtype)
17862 return DRW_Surface::parseCode(code, reader);
17863 m_dxfTypedFieldSeen = true;
17864 referenceVector.y = reader->getDouble();
17865 break;
17866 case 31:
17867 if (!m_dxfInSubtype)
17868 return DRW_Surface::parseCode(code, reader);
17869 m_dxfTypedFieldSeen = true;
17870 referenceVector.z = reader->getDouble();
17871 break;
17872 case 40:
17873 if (!m_dxfInSubtype)
17874 return DRW_Surface::parseCode(code, reader);
17875 m_dxfTypedFieldSeen = true;
17876 if (m_dxfSweepTransformCount >= sweepEntityTransformed.size())
17877 return false;
17878 sweepEntityTransformed[m_dxfSweepTransformCount++] = reader->getDouble();
17879 break;
17880 case 41:
17881 if (!m_dxfInSubtype)
17882 return DRW_Surface::parseCode(code, reader);
17883 m_dxfTypedFieldSeen = true;
17884 if (m_dxfPathTransformCount >= pathEntityTransformed.size())
17885 return false;
17886 pathEntityTransformed[m_dxfPathTransformCount++] = reader->getDouble();
17887 break;
17888 case 42:
17889 if (!m_dxfInSubtype)
17890 return DRW_Surface::parseCode(code, reader);
17891 m_dxfTypedFieldSeen = true;
17892 draftAngle = reader->getDouble();
17893 break;
17894 case 43:
17895 if (!m_dxfInSubtype)
17896 return DRW_Surface::parseCode(code, reader);
17897 m_dxfTypedFieldSeen = true;
17898 draftStartDistance = reader->getDouble();
17899 break;
17900 case 44:
17901 if (!m_dxfInSubtype)
17902 return DRW_Surface::parseCode(code, reader);
17903 m_dxfTypedFieldSeen = true;
17904 draftEndDistance = reader->getDouble();
17905 break;
17906 case 45:
17907 if (!m_dxfInSubtype)
17908 return DRW_Surface::parseCode(code, reader);
17909 m_dxfTypedFieldSeen = true;
17910 twistAngle = reader->getDouble();
17911 break;
17912 case 46:
17913 if (!m_dxfInSubtype)
17914 return DRW_Surface::parseCode(code, reader);
17915 m_dxfTypedFieldSeen = true;
17916 if (m_dxfSweepEntityTransformedCount >= sweepEntityTransform.size())
17917 return false;
17918 sweepEntityTransform[m_dxfSweepEntityTransformedCount++] =
17919 reader->getDouble();
17920 break;
17921 case 47:
17922 if (!m_dxfInSubtype)
17923 return DRW_Surface::parseCode(code, reader);
17924 m_dxfTypedFieldSeen = true;
17925 if (m_dxfPathEntityTransformedCount >= pathEntityTransform.size())
17926 return false;
17927 pathEntityTransform[m_dxfPathEntityTransformedCount++] =
17928 reader->getDouble();
17929 break;
17930 case 48:
17931 if (!m_dxfInSubtype)
17932 return DRW_Surface::parseCode(code, reader);
17933 m_dxfTypedFieldSeen = true;
17934 scaleFactor = reader->getDouble();
17935 break;
17936 case 49:
17937 if (!m_dxfInSubtype)
17938 return DRW_Surface::parseCode(code, reader);
17939 m_dxfTypedFieldSeen = true;
17940 alignAngle = reader->getDouble();
17941 break;
17942 case 70:
17943 if (!m_dxfInSubtype)
17944 return DRW_Surface::parseCode(code, reader);
17945 m_dxfTypedFieldSeen = true;
17946 {
17947 const std::int32_t value = reader->getInt32();
17948 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
17949 return false;
17950 sweepAlignmentFlags = value;
17951 }
17952 break;
17953 case 71:
17954 if (!m_dxfInSubtype)
17955 return DRW_Surface::parseCode(code, reader);
17956 m_dxfTypedFieldSeen = true;
17957 {
17958 const std::int32_t value = reader->getInt32();
17959 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
17960 return false;
17961 pathFlags = value;
17962 }
17963 break;
17964 case 290:
17965 if (!m_dxfInSubtype)
17966 return DRW_Surface::parseCode(code, reader);
17967 m_dxfTypedFieldSeen = true;
17968 solid = reader->getInt32() != 0;
17969 break;
17970 case 292:
17971 if (!m_dxfInSubtype)
17972 return DRW_Surface::parseCode(code, reader);
17973 m_dxfTypedFieldSeen = true;
17974 alignStart = reader->getInt32() != 0;
17975 break;
17976 case 293:
17977 if (!m_dxfInSubtype)
17978 return DRW_Surface::parseCode(code, reader);
17979 m_dxfTypedFieldSeen = true;
17980 bank = reader->getInt32() != 0;
17981 break;
17982 case 294:
17983 if (!m_dxfInSubtype)
17984 return DRW_Surface::parseCode(code, reader);
17985 m_dxfTypedFieldSeen = true;
17986 basePointSet = reader->getInt32() != 0;
17987 break;
17988 case 295:
17989 if (!m_dxfInSubtype)
17990 return DRW_Surface::parseCode(code, reader);
17991 m_dxfTypedFieldSeen = true;
17992 sweepEntityTransformComputed = reader->getInt32() != 0;
17993 break;
17994 case 296:
17995 if (!m_dxfInSubtype)
17996 return DRW_Surface::parseCode(code, reader);
17997 m_dxfTypedFieldSeen = true;
17998 pathEntityTransformComputed = reader->getInt32() != 0;
17999 break;
18000 default:
18001 return DRW_Surface::parseCode(code, reader);
18002 }
18003 return true;
18004}
18005
18006bool DRW_SweptSurface::finalizeDxf() const {
18007 if (!m_dxfTypedFieldSeen)
18008 return true;
18009 return m_dxfInSubtype && m_dxfSweepEntityIdSeen
18010 && m_dxfPathEntityIdSeen
18011 && m_dxfSweepTransformCount == sweepEntityTransformed.size()
18012 && m_dxfPathTransformCount == pathEntityTransformed.size()
18013 && m_dxfSweepEntityTransformedCount == sweepEntityTransform.size()
18014 && m_dxfPathEntityTransformedCount == pathEntityTransform.size();
18015}
18016
18017bool DRW_LoftedSurface::parseCode(
18018 int code, const std::unique_ptr<dxfReader>& reader) {
18019 switch (code) {
18020 case 100: {
18021 const std::string marker = reader->getString();
18022 if (marker == "AcDbLoftedSurface")
18023 m_dxfInSubtype = true;
18024 else if (marker == "AcDbModelerGeometry"
18025 || marker == "AcDbSurface")
18026 m_dxfInSubtype = false;
18027 break;
18028 }
18029 case 40:
18030 if (!m_dxfInSubtype)
18031 return DRW_Surface::parseCode(code, reader);
18032 m_dxfTypedFieldSeen = true;
18033 if (m_dxfTransformCount >= loftEntityTransform.size())
18034 return false;
18035 loftEntityTransform[m_dxfTransformCount++] = reader->getDouble();
18036 break;
18037 case 70:
18038 if (!m_dxfInSubtype)
18039 return DRW_Surface::parseCode(code, reader);
18040 m_dxfTypedFieldSeen = true;
18041 planeNormalLoftingType = reader->getInt32();
18042 break;
18043 case 41:
18044 if (!m_dxfInSubtype)
18045 return DRW_Surface::parseCode(code, reader);
18046 m_dxfTypedFieldSeen = true;
18047 startDraftAngle = reader->getDouble();
18048 break;
18049 case 42:
18050 if (!m_dxfInSubtype)
18051 return DRW_Surface::parseCode(code, reader);
18052 m_dxfTypedFieldSeen = true;
18053 endDraftAngle = reader->getDouble();
18054 break;
18055 case 43:
18056 if (!m_dxfInSubtype)
18057 return DRW_Surface::parseCode(code, reader);
18058 m_dxfTypedFieldSeen = true;
18059 startDraftMagnitude = reader->getDouble();
18060 break;
18061 case 44:
18062 if (!m_dxfInSubtype)
18063 return DRW_Surface::parseCode(code, reader);
18064 m_dxfTypedFieldSeen = true;
18065 endDraftMagnitude = reader->getDouble();
18066 break;
18067 case 90: {
18068 if (!m_dxfInSubtype)
18069 return DRW_Surface::parseCode(code, reader);
18070 if (m_dxfReferenceTokenCount >= kMaxReferenceTokenCount)
18071 return false;
18072 const std::int32_t value = reader->getInt32();
18073 if (value < 0 || static_cast<std::size_t>(value)
18074 > kMaxReferenceTokenCount)
18075 return false;
18076 m_dxfTypedFieldSeen = true;
18077 dxfReferenceData.emplace_back(90, value);
18078 ++m_dxfReferenceTokenCount;
18079 break;
18080 }
18081 case 310: {
18082 if (!m_dxfInSubtype)
18083 return DRW_Surface::parseCode(code, reader);
18084 std::vector<std::uint8_t> decoded;
18085 if (!decodeHexBytes(reader->getString(), decoded))
18086 return false;
18087 if (m_dxfReferenceTokenCount >= kMaxReferenceTokenCount
18088 || decoded.size() > kMaxReferenceDataSize
18089 || m_dxfReferenceDataSize
18090 > kMaxReferenceDataSize - decoded.size())
18091 return false;
18092 m_dxfTypedFieldSeen = true;
18093 dxfReferenceData.emplace_back(310, std::move(decoded));
18094 m_dxfReferenceDataSize += dxfReferenceData.back().binary()->size();
18095 ++m_dxfReferenceTokenCount;
18096 break;
18097 }
18098 case 290:
18099 if (!m_dxfInSubtype)
18100 return DRW_Surface::parseCode(code, reader);
18101 m_dxfTypedFieldSeen = true;
18102 arcLengthParameterization = reader->getInt32() != 0;
18103 break;
18104 case 291:
18105 if (!m_dxfInSubtype)
18106 return DRW_Surface::parseCode(code, reader);
18107 m_dxfTypedFieldSeen = true;
18108 noTwist = reader->getInt32() != 0;
18109 break;
18110 case 292:
18111 if (!m_dxfInSubtype)
18112 return DRW_Surface::parseCode(code, reader);
18113 m_dxfTypedFieldSeen = true;
18114 alignDirection = reader->getInt32() != 0;
18115 break;
18116 case 293:
18117 if (!m_dxfInSubtype)
18118 return DRW_Surface::parseCode(code, reader);
18119 m_dxfTypedFieldSeen = true;
18120 simpleSurfaces = reader->getInt32() != 0;
18121 break;
18122 case 294:
18123 if (!m_dxfInSubtype)
18124 return DRW_Surface::parseCode(code, reader);
18125 m_dxfTypedFieldSeen = true;
18126 closedSurfaces = reader->getInt32() != 0;
18127 break;
18128 case 295:
18129 if (!m_dxfInSubtype)
18130 return DRW_Surface::parseCode(code, reader);
18131 m_dxfTypedFieldSeen = true;
18132 solid = reader->getInt32() != 0;
18133 break;
18134 case 296:
18135 if (!m_dxfInSubtype)
18136 return DRW_Surface::parseCode(code, reader);
18137 m_dxfTypedFieldSeen = true;
18138 ruledSurface = reader->getInt32() != 0;
18139 break;
18140 case 297:
18141 if (!m_dxfInSubtype)
18142 return DRW_Surface::parseCode(code, reader);
18143 m_dxfTypedFieldSeen = true;
18144 virtualGuide = reader->getInt32() != 0;
18145 break;
18146 case 5:
18147 if (!m_dxfInSubtype)
18148 return DRW_Surface::parseCode(code, reader);
18149 if (!reader->isValidHandleString())
18150 return false;
18151 m_dxfTypedFieldSeen = true;
18152 pathCurveHandle = reader->getHandleString();
18153 break;
18154 default:
18155 return DRW_Surface::parseCode(code, reader);
18156 }
18157 return true;
18158}
18159
18160bool DRW_LoftedSurface::finalizeDxf() const {
18161 if (!m_dxfTypedFieldSeen)
18162 return true;
18163 return m_dxfInSubtype
18164 && m_dxfTransformCount == loftEntityTransform.size()
18165 && std::all_of(loftEntityTransform.begin(), loftEntityTransform.end(),
18166 [](double value) { return std::isfinite(value); })
18167 && std::isfinite(startDraftAngle)
18168 && std::isfinite(endDraftAngle)
18169 && std::isfinite(startDraftMagnitude)
18170 && std::isfinite(endDraftMagnitude);
18171}
18172
18173bool DRW_NurbsSurface::parseCode(
18174 int code, const std::unique_ptr<dxfReader>& reader) {
18175 switch (code) {
18176 case 100: {
18177 const std::string marker = reader->getString();
18178 if (marker == "AcDbNurbSurface")
18179 m_dxfInSubtype = true;
18180 else if (marker == "AcDbModelerGeometry"
18181 || marker == "AcDbSurface")
18182 m_dxfInSubtype = false;
18183 break;
18184 }
18185 case 170:
18186 if (!m_dxfInSubtype)
18187 return DRW_Surface::parseCode(code, reader);
18188 {
18189 const std::int32_t value = reader->getInt32();
18190 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
18191 return false;
18192 short170 = static_cast<std::uint16_t>(value);
18193 m_dxfTypedFieldSeen = true;
18194 }
18195 break;
18196 case 290:
18197 if (!m_dxfInSubtype)
18198 return DRW_Surface::parseCode(code, reader);
18199 cvHullDisplay = reader->getInt32() != 0;
18200 m_dxfTypedFieldSeen = true;
18201 break;
18202 case 10:
18203 if (!m_dxfInSubtype)
18204 return DRW_Surface::parseCode(code, reader);
18205 uvec1.x = reader->getDouble();
18206 m_dxfCoordinateMask[0] |= 1;
18207 m_dxfTypedFieldSeen = true;
18208 break;
18209 case 20:
18210 if (!m_dxfInSubtype)
18211 return DRW_Surface::parseCode(code, reader);
18212 uvec1.y = reader->getDouble();
18213 m_dxfCoordinateMask[0] |= 2;
18214 m_dxfTypedFieldSeen = true;
18215 break;
18216 case 30:
18217 if (!m_dxfInSubtype)
18218 return DRW_Surface::parseCode(code, reader);
18219 uvec1.z = reader->getDouble();
18220 m_dxfCoordinateMask[0] |= 4;
18221 m_dxfTypedFieldSeen = true;
18222 break;
18223 case 11:
18224 if (!m_dxfInSubtype)
18225 return DRW_Surface::parseCode(code, reader);
18226 vvec1.x = reader->getDouble();
18227 m_dxfCoordinateMask[1] |= 1;
18228 m_dxfTypedFieldSeen = true;
18229 break;
18230 case 21:
18231 if (!m_dxfInSubtype)
18232 return DRW_Surface::parseCode(code, reader);
18233 vvec1.y = reader->getDouble();
18234 m_dxfCoordinateMask[1] |= 2;
18235 m_dxfTypedFieldSeen = true;
18236 break;
18237 case 31:
18238 if (!m_dxfInSubtype)
18239 return DRW_Surface::parseCode(code, reader);
18240 vvec1.z = reader->getDouble();
18241 m_dxfCoordinateMask[1] |= 4;
18242 m_dxfTypedFieldSeen = true;
18243 break;
18244 case 12:
18245 if (!m_dxfInSubtype)
18246 return DRW_Surface::parseCode(code, reader);
18247 uvec2.x = reader->getDouble();
18248 m_dxfCoordinateMask[2] |= 1;
18249 m_dxfTypedFieldSeen = true;
18250 break;
18251 case 22:
18252 if (!m_dxfInSubtype)
18253 return DRW_Surface::parseCode(code, reader);
18254 uvec2.y = reader->getDouble();
18255 m_dxfCoordinateMask[2] |= 2;
18256 m_dxfTypedFieldSeen = true;
18257 break;
18258 case 32:
18259 if (!m_dxfInSubtype)
18260 return DRW_Surface::parseCode(code, reader);
18261 uvec2.z = reader->getDouble();
18262 m_dxfCoordinateMask[2] |= 4;
18263 m_dxfTypedFieldSeen = true;
18264 break;
18265 case 13:
18266 if (!m_dxfInSubtype)
18267 return DRW_Surface::parseCode(code, reader);
18268 vvec2.x = reader->getDouble();
18269 m_dxfCoordinateMask[3] |= 1;
18270 m_dxfTypedFieldSeen = true;
18271 break;
18272 case 23:
18273 if (!m_dxfInSubtype)
18274 return DRW_Surface::parseCode(code, reader);
18275 vvec2.y = reader->getDouble();
18276 m_dxfCoordinateMask[3] |= 2;
18277 m_dxfTypedFieldSeen = true;
18278 break;
18279 case 33:
18280 if (!m_dxfInSubtype)
18281 return DRW_Surface::parseCode(code, reader);
18282 vvec2.z = reader->getDouble();
18283 m_dxfCoordinateMask[3] |= 4;
18284 m_dxfTypedFieldSeen = true;
18285 break;
18286 default:
18287 return DRW_Surface::parseCode(code, reader);
18288 }
18289 return true;
18290}
18291
18292bool DRW_NurbsSurface::finalizeDxf() const {
18293 if (!m_dxfTypedFieldSeen)
18294 return true;
18295 const bool coordinatesSeen = std::any_of(
18296 m_dxfCoordinateMask.begin(), m_dxfCoordinateMask.end(),
18297 [](std::uint8_t mask) { return mask != 0; });
18298 if (!m_dxfInSubtype
18299 || (coordinatesSeen && std::any_of(
18300 m_dxfCoordinateMask.begin(), m_dxfCoordinateMask.end(),
18301 [](std::uint8_t mask) { return mask != 7; }))) {
18302 return false;
18303 }
18304 return std::all_of(m_dxfCoordinateMask.begin(), m_dxfCoordinateMask.end(),
18305 [](std::uint8_t mask) { return mask == 0 || mask == 7; })
18306 && std::isfinite(uvec1.x) && std::isfinite(uvec1.y)
18307 && std::isfinite(uvec1.z) && std::isfinite(vvec1.x)
18308 && std::isfinite(vvec1.y) && std::isfinite(vvec1.z)
18309 && std::isfinite(uvec2.x) && std::isfinite(uvec2.y)
18310 && std::isfinite(uvec2.z) && std::isfinite(vvec2.x)
18311 && std::isfinite(vvec2.y) && std::isfinite(vvec2.z);
18312}
18313
18314bool DRW_RevolvedSurface::parseCode(
18315 int code, const std::unique_ptr<dxfReader>& reader) {
18316 switch (code) {
18317 case 90:
18318 if (!m_dxfClassIdSeen) {
18319 const std::int32_t value = reader->getInt32();
18320 if (value < 0)
18321 return false;
18322 classId = static_cast<std::uint32_t>(value);
18323 m_dxfClassIdSeen = true;
18324 } else {
18325 const std::int32_t value = reader->getInt32();
18326 if (value < 0)
18327 return false;
18328 id = static_cast<std::uint32_t>(value);
18329 }
18330 break;
18331 case 310:
18332 // Revolve data follows the subclass id; it must not join the ACIS body.
18333 if (!m_dxfClassIdSeen)
18334 return DRW_Surface::parseCode(code, reader);
18335 break;
18336 case 10:
18337 axisPoint.x = reader->getDouble();
18338 break;
18339 case 20:
18340 axisPoint.y = reader->getDouble();
18341 break;
18342 case 30:
18343 axisPoint.z = reader->getDouble();
18344 break;
18345 case 11:
18346 axisVector.x = reader->getDouble();
18347 break;
18348 case 21:
18349 axisVector.y = reader->getDouble();
18350 break;
18351 case 31:
18352 axisVector.z = reader->getDouble();
18353 break;
18354 case 40:
18355 revolveAngle = reader->getDouble();
18356 break;
18357 case 41:
18358 startAngle = reader->getDouble();
18359 break;
18360 case 42:
18361 if (m_dxfTransformCount >= transform.size())
18362 return false;
18363 transform[m_dxfTransformCount++] = reader->getDouble();
18364 break;
18365 case 43:
18366 draftAngle = reader->getDouble();
18367 break;
18368 case 44:
18369 draftStartDistance = reader->getDouble();
18370 break;
18371 case 45:
18372 draftEndDistance = reader->getDouble();
18373 break;
18374 case 46:
18375 twistAngle = reader->getDouble();
18376 break;
18377 case 290:
18378 solid = reader->getInt32() != 0;
18379 break;
18380 case 291:
18381 closeToAxis = reader->getInt32() != 0;
18382 break;
18383 default:
18384 return DRW_Surface::parseCode(code, reader);
18385 }
18386 return true;
18387}
18388
18389void DRW_Surface::resetDwgState() {
18390 DRW_Entity::reset();
18391 uIsolines = 0;
18392 vIsolines = 0;
18393 modelerFormatVersion = 0;
18394 acisEmpty = false;
18395 acisVersion = 0;
18396 rawAcisData.clear();
18397 hasRawDwgBody = false;
18398 rawDwgBodyBitSize = 0;
18399 rawDwgBodyVersion = DRW::UNKNOWNV;
18400 crossSectionHandles.clear();
18401 guideCurveHandles.clear();
18402 dwgClassNum = 0;
18403 dwgPayloadDecoded = false;
18404 m_wireframe = DRW_AcisBrep();
18405 m_wireframeDecoded = false;
18406
18407 const auto identity = [] {
18408 return std::array<double, DRW_ExtrudedSurface::kTransformSize>{
18409 1.0, 0.0, 0.0, 0.0,
18410 0.0, 1.0, 0.0, 0.0,
18411 0.0, 0.0, 1.0, 0.0,
18412 0.0, 0.0, 0.0, 1.0};
18413 };
18414 switch (eType) {
18415 case DRW::EXTRUDEDSURFACE: {
18416 auto *surface = static_cast<DRW_ExtrudedSurface *>(this);
18417 surface->classId = 0;
18418 surface->sweepVector = DRW_Coord{0.0, 0.0, 0.0};
18419 surface->extrudedTransform = identity();
18420 surface->sweepEntityTransform = identity();
18421 surface->pathEntityTransform = identity();
18422 surface->draftAngle = 0.0;
18423 surface->draftStartDistance = 0.0;
18424 surface->draftEndDistance = 0.0;
18425 surface->twistAngle = 0.0;
18426 surface->scaleFactor = 0.0;
18427 surface->alignAngle = 0.0;
18428 surface->solid = false;
18429 surface->sweepAlignmentFlags = 0;
18430 surface->pathFlags = 0;
18431 surface->alignStart = false;
18432 surface->bank = false;
18433 surface->basePointSet = false;
18434 surface->sweepEntityTransformComputed = false;
18435 surface->pathEntityTransformComputed = false;
18436 surface->referenceVector = DRW_Coord{0.0, 0.0, 0.0};
18437 break;
18438 }
18439 case DRW::REVOLVEDSURFACE: {
18440 auto *surface = static_cast<DRW_RevolvedSurface *>(this);
18441 surface->classId = 0;
18442 surface->id = 0;
18443 surface->axisPoint = DRW_Coord{0.0, 0.0, 0.0};
18444 surface->axisVector = DRW_Coord{0.0, 0.0, 0.0};
18445 surface->revolveAngle = 0.0;
18446 surface->startAngle = 0.0;
18447 surface->transform = identity();
18448 surface->draftAngle = 0.0;
18449 surface->draftStartDistance = 0.0;
18450 surface->draftEndDistance = 0.0;
18451 surface->twistAngle = 0.0;
18452 surface->solid = false;
18453 surface->closeToAxis = false;
18454 break;
18455 }
18456 case DRW::SWEPTSURFACE: {
18457 auto *surface = static_cast<DRW_SweptSurface *>(this);
18458 surface->classVersion = 0;
18459 surface->sweepEntityId = 0;
18460 surface->sweepData.clear();
18461 surface->pathEntityId = 0;
18462 surface->pathData.clear();
18463 surface->sweepEntityTransform = identity();
18464 surface->pathEntityTransform = identity();
18465 surface->draftAngle = 0.0;
18466 surface->draftStartDistance = 0.0;
18467 surface->draftEndDistance = 0.0;
18468 surface->twistAngle = 0.0;
18469 surface->scaleFactor = 1.0;
18470 surface->alignAngle = 0.0;
18471 surface->sweepEntityTransformed = identity();
18472 surface->pathEntityTransformed = identity();
18473 surface->solid = false;
18474 surface->sweepAlignmentFlags = 0;
18475 surface->pathFlags = 0;
18476 surface->alignStart = false;
18477 surface->bank = false;
18478 surface->basePointSet = false;
18479 surface->sweepEntityTransformComputed = false;
18480 surface->pathEntityTransformComputed = false;
18481 surface->referenceVector = DRW_Coord{0.0, 0.0, 0.0};
18482 break;
18483 }
18484 case DRW::LOFTEDSURFACE: {
18485 auto *surface = static_cast<DRW_LoftedSurface *>(this);
18486 surface->loftEntityTransform = identity();
18487 surface->planeNormalLoftingType = 0;
18488 surface->startDraftAngle = 0.0;
18489 surface->endDraftAngle = 0.0;
18490 surface->startDraftMagnitude = 0.0;
18491 surface->endDraftMagnitude = 0.0;
18492 surface->arcLengthParameterization = false;
18493 surface->noTwist = true;
18494 surface->alignDirection = true;
18495 surface->simpleSurfaces = true;
18496 surface->closedSurfaces = false;
18497 surface->solid = false;
18498 surface->ruledSurface = false;
18499 surface->virtualGuide = false;
18500 surface->numCrossSections = 0;
18501 surface->numGuideCurves = 0;
18502 surface->dxfReferenceData.clear();
18503 surface->pathCurveHandle = 0;
18504 break;
18505 }
18506 case DRW::NURBSURFACE: {
18507 auto *surface = static_cast<DRW_NurbsSurface *>(this);
18508 surface->short170 = 0;
18509 surface->cvHullDisplay = false;
18510 surface->uvec1 = DRW_Coord{0.0, 0.0, 0.0};
18511 surface->vvec1 = DRW_Coord{0.0, 0.0, 0.0};
18512 surface->uvec2 = DRW_Coord{0.0, 0.0, 0.0};
18513 surface->vvec2 = DRW_Coord{0.0, 0.0, 0.0};
18514 break;
18515 }
18516 default:
18517 break;
18518 }
18519}
18520
18521bool DRW_Surface::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
18522 resetDwgState();
18523 dwgBuffer *sourceBuf = buf;
18524 auto fail = [this, sourceBuf]() {
18525 if (sourceBuf != nullptr)
18526 sourceBuf->invalidate();
18527 resetDwgState();
18528 return false;
18529 };
18530 if (sourceBuf == nullptr)
18531 return fail();
18532
18533 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
18534 dwgBuffer stringProbe = sourceBuf->forkIndependent();
18535 dwgBuffer *stringStream = version > DRW::AC1018 ? &stringProbe : nullptr;
18536 if (!DRW_Entity::parseDwg(version, &bodyProbe, stringStream, bs))
18537 return fail();
18538 buf = &bodyProbe;
18539 const std::uint64_t bodyStartBit = currentDwgBit(buf);
18540
18541 // Surface bodies are opaque here, but their bounded bytes still carry the
18542 // ACIS payload and must be available for rendering and metadata replay.
18543 std::uint64_t bodyEndBit = bodyStartBit;
18544 if (!entityBodyDataEndBit(
18545 stringStream != nullptr ? *stringStream : bodyProbe,
18546 version, objSize, bodyEndBit))
18547 return fail();
18548 if (bodyEndBit < bodyStartBit)
18549 return fail();
18550 if (bodyEndBit > bodyStartBit
18551 && bodyEndBit - bodyStartBit > std::numeric_limits<std::uint32_t>::max())
18552 return fail();
18553 hasRawDwgBody = bodyEndBit > bodyStartBit;
18554 rawDwgBodyBitSize = static_cast<std::uint32_t>(
18555 bodyEndBit >= bodyStartBit ? bodyEndBit - bodyStartBit : 0);
18556 rawDwgBodyVersion = version;
18557 dwgClassNum = oType >= 0 ? static_cast<std::uint16_t>(oType) : 0;
18558 dwgPayloadDecoded = false;
18559 crossSectionHandles.clear();
18560 guideCurveHandles.clear();
18561 if (bodyEndBit > bodyStartBit
18562 && !copyDwgBitRange(*buf, bodyStartBit, bodyEndBit, rawAcisData)) {
18563 return fail();
18564 }
18565
18566 // When has_ds_data is set, the bounded body is an inline DataStorage/SAB
18567 // payload. Keep it opaque, matching the cross-project reader contract;
18568 // interpreting its bytes as subtype fields can fabricate counts and make
18569 // a valid surface fail while reading a nonexistent handle tail.
18570 if (bodyEndBit > bodyStartBit && !hasDsData) {
18571 SurfaceDwgPayloadInfo payload;
18572 if (readSurfaceDwgPayload(
18573 rawAcisData, bodyEndBit - bodyStartBit, version, eType,
18574 surfaceHasModelerFormatVersion(eType),
18575 surfaceModelerFormatVersionLimit(eType), payload)) {
18576 acisEmpty = payload.acisEmpty;
18577 acisVersion = payload.acisVersion;
18578 modelerFormatVersion = payload.modelerFormatVersion;
18579 uIsolines = payload.uIsolines;
18580 vIsolines = payload.vIsolines;
18581 if (payload.extrudedFieldsValid
18582 && eType == DRW::EXTRUDEDSURFACE) {
18583 auto *extruded = static_cast<DRW_ExtrudedSurface *>(this);
18584 extruded->sweepVector = payload.extrudedSweepVector;
18585 extruded->extrudedTransform = payload.extrudedTransform;
18586 extruded->sweepEntityTransform =
18587 payload.extrudedSweepEntityTransform;
18588 extruded->pathEntityTransform =
18589 payload.extrudedPathEntityTransform;
18590 extruded->draftAngle = payload.extrudedDraftAngle;
18591 extruded->draftStartDistance =
18592 payload.extrudedDraftStartDistance;
18593 extruded->draftEndDistance = payload.extrudedDraftEndDistance;
18594 extruded->twistAngle = payload.extrudedTwistAngle;
18595 extruded->scaleFactor = payload.extrudedScaleFactor;
18596 extruded->alignAngle = payload.extrudedAlignAngle;
18597 extruded->solid = payload.extrudedSolid;
18598 extruded->sweepAlignmentFlags =
18599 payload.extrudedSweepAlignmentFlags;
18600 extruded->pathFlags = payload.extrudedPathFlags;
18601 extruded->alignStart = payload.extrudedAlignStart;
18602 extruded->bank = payload.extrudedBank;
18603 extruded->basePointSet = payload.extrudedBasePointSet;
18604 extruded->sweepEntityTransformComputed =
18605 payload.extrudedSweepEntityTransformComputed;
18606 extruded->pathEntityTransformComputed =
18607 payload.extrudedPathEntityTransformComputed;
18608 extruded->referenceVector = payload.extrudedReferenceVector;
18609 }
18610 if (payload.sweptFieldsValid && eType == DRW::SWEPTSURFACE) {
18611 auto *swept = static_cast<DRW_SweptSurface *>(this);
18612 swept->classVersion = payload.sweptClassVersion;
18613 swept->sweepEntityId = payload.sweptEntityId;
18614 swept->sweepData = payload.sweptData;
18615 swept->pathEntityId = payload.sweptPathEntityId;
18616 swept->pathData = payload.sweptPathData;
18617 swept->sweepEntityTransform = payload.sweptEntityTransform;
18618 swept->pathEntityTransform = payload.sweptPathEntityTransform;
18619 swept->draftAngle = payload.sweptDraftAngle;
18620 swept->draftStartDistance = payload.sweptDraftStartDistance;
18621 swept->draftEndDistance = payload.sweptDraftEndDistance;
18622 swept->twistAngle = payload.sweptTwistAngle;
18623 swept->scaleFactor = payload.sweptScaleFactor;
18624 swept->alignAngle = payload.sweptAlignAngle;
18625 swept->solid = payload.sweptSolid;
18626 swept->sweepAlignmentFlags = payload.sweptAlignmentFlags;
18627 swept->pathFlags = payload.sweptPathFlags;
18628 swept->alignStart = payload.sweptAlignStart;
18629 swept->bank = payload.sweptBank;
18630 swept->basePointSet = payload.sweptBasePointSet;
18631 swept->sweepEntityTransformComputed =
18632 payload.sweptEntityTransformComputed;
18633 swept->pathEntityTransformComputed =
18634 payload.sweptPathEntityTransformComputed;
18635 swept->referenceVector = payload.sweptReferenceVector;
18636 }
18637 if (payload.loftedFieldsValid && eType == DRW::LOFTEDSURFACE) {
18638 auto *lofted = static_cast<DRW_LoftedSurface *>(this);
18639 lofted->loftEntityTransform = payload.loftedEntityTransform;
18640 lofted->planeNormalLoftingType =
18641 payload.loftedPlaneNormalLoftingType;
18642 lofted->startDraftAngle = payload.loftedStartDraftAngle;
18643 lofted->endDraftAngle = payload.loftedEndDraftAngle;
18644 lofted->startDraftMagnitude =
18645 payload.loftedStartDraftMagnitude;
18646 lofted->endDraftMagnitude = payload.loftedEndDraftMagnitude;
18647 lofted->arcLengthParameterization =
18648 payload.loftedArcLengthParameterization;
18649 lofted->noTwist = payload.loftedNoTwist;
18650 lofted->alignDirection = payload.loftedAlignDirection;
18651 lofted->simpleSurfaces = payload.loftedSimpleSurfaces;
18652 lofted->closedSurfaces = payload.loftedClosedSurfaces;
18653 lofted->solid = payload.loftedSolid;
18654 lofted->ruledSurface = payload.loftedRuledSurface;
18655 lofted->virtualGuide = payload.loftedVirtualGuide;
18656 lofted->numCrossSections = payload.loftedNumCrossSections;
18657 lofted->numGuideCurves = payload.loftedNumGuideCurves;
18658 }
18659 if (payload.nurbsFieldsValid && eType == DRW::NURBSURFACE) {
18660 auto *nurbs = static_cast<DRW_NurbsSurface *>(this);
18661 nurbs->short170 = payload.nurbsShort170;
18662 nurbs->cvHullDisplay = payload.nurbsCvHullDisplay;
18663 nurbs->uvec1 = payload.nurbsUvec1;
18664 nurbs->vvec1 = payload.nurbsVvec1;
18665 nurbs->uvec2 = payload.nurbsUvec2;
18666 nurbs->vvec2 = payload.nurbsVvec2;
18667 }
18668 dwgPayloadDecoded = true;
18669 if (payload.revolvedFieldsValid
18670 && eType == DRW::REVOLVEDSURFACE) {
18671 auto *revolved = static_cast<DRW_RevolvedSurface *>(this);
18672 revolved->classId = payload.revolvedClassId;
18673 revolved->id = payload.revolvedId;
18674 revolved->axisPoint = payload.revolvedAxisPoint;
18675 revolved->axisVector = payload.revolvedAxisVector;
18676 revolved->revolveAngle = payload.revolvedAngle;
18677 revolved->startAngle = payload.revolvedStartAngle;
18678 revolved->transform = payload.revolvedTransform;
18679 revolved->draftAngle = payload.revolvedDraftAngle;
18680 revolved->draftStartDistance = payload.revolvedDraftStartDistance;
18681 revolved->draftEndDistance = payload.revolvedDraftEndDistance;
18682 revolved->twistAngle = payload.revolvedTwistAngle;
18683 revolved->solid = payload.revolvedSolid;
18684 revolved->closeToAxis = payload.revolvedCloseToAxis;
18685 }
18686 }
18687 }
18688
18689 // R13-R2004 keep the handle stream inline; seek to the documented body
18690 // boundary before the common handle parser. R2007+ performs that seek in
18691 // parseDwgEntHandle() itself.
18692 dwgBuffer handleProbe = bodyProbe.forkIndependent();
18693 if (version <= DRW::AC1018 && bodyEndBit > bodyStartBit) {
18694 if (!handleProbe.setPosition(bodyEndBit >> 3))
18695 return fail();
18696 handleProbe.setBitPos(static_cast<std::uint8_t>(bodyEndBit & 7u));
18697 if (!handleProbe.isGood())
18698 return fail();
18699 }
18700 std::uint64_t handleEndBit = 0;
18701 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
18702 handleEndBit))
18703 return fail();
18704 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
18705 handleEndBit)
18706 || !handleProbe.isGood())
18707 return fail();
18708 std::vector<std::uint32_t> parsedCrossSectionHandles;
18709 std::vector<std::uint32_t> parsedGuideCurveHandles;
18710 std::uint32_t parsedPathCurveHandle = 0;
18711 const auto readHandleList = [&](std::int32_t count,
18712 std::vector<std::uint32_t>& values) {
18713 if (count < 0
18714 || count > static_cast<std::int32_t>(
18715 DRW_LoftedSurface::kMaxReferenceTokenCount)
18716 || !DRW::reserve(values, count))
18717 return false;
18718 for (std::int32_t i = 0; i < count; ++i) {
18719 dwgHandle value;
18720 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
18721 value))
18722 return false;
18723 values.push_back(value.ref);
18724 }
18725 return true;
18726 };
18727 if (eType == DRW::LOFTEDSURFACE && dwgPayloadDecoded) {
18728 auto *lofted = static_cast<DRW_LoftedSurface *>(this);
18729 if (!readHandleList(lofted->numCrossSections,
18730 parsedCrossSectionHandles)
18731 || !readHandleList(lofted->numGuideCurves,
18732 parsedGuideCurveHandles))
18733 return fail();
18734 dwgHandle pathCurve;
18735 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
18736 pathCurve))
18737 return fail();
18738 parsedPathCurveHandle = pathCurve.ref;
18739 }
18740 if (!handleProbe.isGood())
18741 return fail();
18742 crossSectionHandles = std::move(parsedCrossSectionHandles);
18743 guideCurveHandles = std::move(parsedGuideCurveHandles);
18744 if (eType == DRW::LOFTEDSURFACE)
18745 static_cast<DRW_LoftedSurface *>(this)->pathCurveHandle =
18746 parsedPathCurveHandle;
18747 *sourceBuf = handleProbe;
18748 return true;
18749}
18750
18751bool DRW_Surface::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
18752 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
18753 (void)bs;
18754 if (buf == nullptr || version < DRW::AC1021 || dwgClassNum == 0)
18755 return false;
18756
18757 const auto validBitShort = [](int value) {
18758 return value >= 0
18759 && value <= std::numeric_limits<std::uint16_t>::max();
18760 };
18761 const auto validCount = [](std::int32_t value, std::size_t limit) {
18762 return value >= 0 && static_cast<std::size_t>(value) <= limit;
18763 };
18764 const auto validTypedPayload = [&]() {
18765 if (!acisEmpty && !rawAcisData.empty())
18766 return false;
18767 if (surfaceHasModelerFormatVersion(eType)
18768 && (!validBitShort(modelerFormatVersion)
18769 || (eType == DRW::LOFTEDSURFACE
18770 && modelerFormatVersion
18771 > surfaceModelerFormatVersionLimit(eType))))
18772 return false;
18773 if (!validBitShort(uIsolines) || !validBitShort(vIsolines))
18774 return false;
18775
18776 switch (eType) {
18777 case DRW::EXTRUDEDSURFACE: {
18778 const auto *surface = static_cast<const DRW_ExtrudedSurface *>(this);
18779 return std::isfinite(surface->draftAngle)
18780 && std::isfinite(surface->draftStartDistance)
18781 && std::isfinite(surface->draftEndDistance)
18782 && std::isfinite(surface->twistAngle)
18783 && std::isfinite(surface->scaleFactor)
18784 && std::isfinite(surface->alignAngle)
18785 && finiteSurfaceCoord(surface->referenceVector)
18786 && finiteSurfaceCoord(surface->sweepVector)
18787 && validBitShort(surface->sweepAlignmentFlags)
18788 && validBitShort(surface->pathFlags)
18789 && finiteSurfaceMatrix(surface->extrudedTransform)
18790 && finiteSurfaceMatrix(surface->sweepEntityTransform)
18791 && finiteSurfaceMatrix(surface->pathEntityTransform);
18792 }
18793 case DRW::LOFTEDSURFACE: {
18794 const auto *surface = static_cast<const DRW_LoftedSurface *>(this);
18795 return std::isfinite(surface->startDraftAngle)
18796 && std::isfinite(surface->endDraftAngle)
18797 && std::isfinite(surface->startDraftMagnitude)
18798 && std::isfinite(surface->endDraftMagnitude)
18799 && validCount(surface->numCrossSections,
18800 DRW_LoftedSurface::kMaxReferenceTokenCount)
18801 && validCount(surface->numGuideCurves,
18802 DRW_LoftedSurface::kMaxReferenceTokenCount)
18803 && surface->crossSectionHandles.size()
18804 == static_cast<std::size_t>(surface->numCrossSections)
18805 && surface->guideCurveHandles.size()
18806 == static_cast<std::size_t>(surface->numGuideCurves)
18807 && finiteSurfaceMatrix(surface->loftEntityTransform);
18808 }
18809 case DRW::REVOLVEDSURFACE: {
18810 const auto *surface = static_cast<const DRW_RevolvedSurface *>(this);
18811 return surface->classId <= std::numeric_limits<std::int32_t>::max()
18812 && surface->id <= std::numeric_limits<std::int32_t>::max()
18813 && finiteSurfaceCoord(surface->axisPoint)
18814 && finiteSurfaceCoord(surface->axisVector)
18815 && std::isfinite(surface->revolveAngle)
18816 && std::isfinite(surface->startAngle)
18817 && std::isfinite(surface->draftAngle)
18818 && std::isfinite(surface->draftStartDistance)
18819 && std::isfinite(surface->draftEndDistance)
18820 && std::isfinite(surface->twistAngle)
18821 && finiteSurfaceMatrix(surface->transform);
18822 }
18823 case DRW::SWEPTSURFACE: {
18824 const auto *surface = static_cast<const DRW_SweptSurface *>(this);
18825 return surface->classVersion <= 10
18826 && surface->sweepEntityId
18827 <= std::numeric_limits<std::int32_t>::max()
18828 && surface->pathEntityId
18829 <= std::numeric_limits<std::int32_t>::max()
18830 && surface->sweepData.size()
18831 <= DRW_SweptSurface::kMaxSweepDataSize
18832 && surface->pathData.size()
18833 <= DRW_SweptSurface::kMaxSweepDataSize
18834 && std::isfinite(surface->draftAngle)
18835 && std::isfinite(surface->draftStartDistance)
18836 && std::isfinite(surface->draftEndDistance)
18837 && std::isfinite(surface->twistAngle)
18838 && std::isfinite(surface->scaleFactor)
18839 && std::isfinite(surface->alignAngle)
18840 && finiteSurfaceCoord(surface->referenceVector)
18841 && validBitShort(surface->sweepAlignmentFlags)
18842 && validBitShort(surface->pathFlags)
18843 && finiteSurfaceMatrix(surface->sweepEntityTransform)
18844 && finiteSurfaceMatrix(surface->pathEntityTransform);
18845 }
18846 case DRW::NURBSURFACE:
18847 if (version < DRW::AC1027)
18848 return true;
18849 {
18850 const auto *surface = static_cast<const DRW_NurbsSurface *>(this);
18851 return finiteSurfaceCoord(surface->uvec1)
18852 && finiteSurfaceCoord(surface->vvec1)
18853 && finiteSurfaceCoord(surface->uvec2)
18854 && finiteSurfaceCoord(surface->vvec2);
18855 }
18856 case DRW::PLANESURFACE:
18857 return true;
18858 default:
18859 return false;
18860 }
18861 };
18862
18863 if (hasRawDwgBody && rawDwgBodyVersion == version) {
18864 if (rawDwgBodyBitSize == 0
18865 || rawDwgBodyBitSize > surfaceRawBitCapacity(rawAcisData))
18866 return false;
18867 } else if (!validTypedPayload()) {
18868 return false;
18869 }
18870 if (!encodeDwgCommon(version, buf, strBuf))
18871 return false;
18872
18873 if (hasRawDwgBody && rawDwgBodyVersion == version) {
18874 if (!putSurfaceRawBits(buf, rawAcisData, rawDwgBodyBitSize))
18875 return false;
18876 } else {
18877 // DXF ACIS/SAB bytes are not the bit-packed DWG ACTION_3DSOLID body.
18878 // Do not place them directly into a DWG object. Native ACIS emission
18879 // remains a separate conversion step; empty modeler bodies are valid
18880 // and allow metadata-only surfaces to round-trip safely.
18881 if (!acisEmpty && !rawAcisData.empty())
18882 return false;
18883 buf->putBit(1); // ACTION_3DSOLID: no ACIS body
18884
18885 const bool hasModelerFormat = surfaceHasModelerFormatVersion(eType);
18886 if (hasModelerFormat) {
18887 if (modelerFormatVersion < 0
18888 || modelerFormatVersion > 0xFFFF
18889 || (eType == DRW::LOFTEDSURFACE && modelerFormatVersion > 3))
18890 return false;
18891 buf->putBitShort(static_cast<std::uint16_t>(modelerFormatVersion));
18892 }
18893 if (uIsolines < 0 || uIsolines > 0xFFFF
18894 || vIsolines < 0 || vIsolines > 0xFFFF)
18895 return false;
18896 buf->putBitShort(static_cast<std::uint16_t>(uIsolines));
18897 buf->putBitShort(static_cast<std::uint16_t>(vIsolines));
18898
18899 auto putCoord = [&](const DRW_Coord& value) {
18900 if (!finiteSurfaceCoord(value))
18901 return false;
18902 buf->put3BitDouble(value);
18903 return true;
18904 };
18905 auto putBool = [&](bool value) { buf->putBit(value ? 1 : 0); };
18906 auto putCount = [&](std::int32_t value, std::size_t limit) {
18907 return value >= 0 && static_cast<std::size_t>(value) <= limit;
18908 };
18909
18910 switch (eType) {
18911 case DRW::EXTRUDEDSURFACE: {
18912 const auto *surface = static_cast<const DRW_ExtrudedSurface *>(this);
18913 if (!std::isfinite(surface->draftAngle)
18914 || !std::isfinite(surface->draftStartDistance)
18915 || !std::isfinite(surface->draftEndDistance)
18916 || !std::isfinite(surface->twistAngle)
18917 || !std::isfinite(surface->scaleFactor)
18918 || !std::isfinite(surface->alignAngle)
18919 || !finiteSurfaceCoord(surface->referenceVector)
18920 || !finiteSurfaceCoord(surface->sweepVector)
18921 || surface->sweepAlignmentFlags < 0
18922 || surface->sweepAlignmentFlags > 0xFFFF
18923 || surface->pathFlags < 0 || surface->pathFlags > 0xFFFF
18924 || !finiteSurfaceMatrix(surface->sweepEntityTransform)
18925 || !finiteSurfaceMatrix(surface->pathEntityTransform))
18926 return false;
18927 buf->putBitDouble(surface->draftAngle);
18928 buf->putBitDouble(surface->draftStartDistance);
18929 buf->putBitDouble(surface->draftEndDistance);
18930 buf->putBitDouble(surface->twistAngle);
18931 buf->putBitDouble(surface->scaleFactor);
18932 buf->putBitDouble(surface->alignAngle);
18933 // The matrices were validated above; emit them in their specified
18934 // position after SweepOptions' scalar fields.
18935 for (double value : surface->sweepEntityTransform)
18936 buf->putBitDouble(value);
18937 for (double value : surface->pathEntityTransform)
18938 buf->putBitDouble(value);
18939 putBool(surface->solid);
18940 buf->putBitShort(static_cast<std::uint16_t>(surface->sweepAlignmentFlags));
18941 buf->putBitShort(static_cast<std::uint16_t>(surface->pathFlags));
18942 putBool(surface->alignStart);
18943 putBool(surface->bank);
18944 putBool(surface->basePointSet);
18945 putBool(surface->sweepEntityTransformComputed);
18946 putBool(surface->pathEntityTransformComputed);
18947 if (!finiteSurfaceCoord(surface->referenceVector)
18948 || !finiteSurfaceCoord(surface->sweepVector)
18949 || !finiteSurfaceMatrix(surface->extrudedTransform))
18950 return false;
18951 putCoord(surface->referenceVector);
18952 putCoord(surface->sweepVector);
18953 for (double value : surface->extrudedTransform)
18954 buf->putBitDouble(value);
18955 break;
18956 }
18957 case DRW::LOFTEDSURFACE: {
18958 const auto *surface = static_cast<const DRW_LoftedSurface *>(this);
18959 if (!std::isfinite(surface->startDraftAngle)
18960 || !std::isfinite(surface->endDraftAngle)
18961 || !std::isfinite(surface->startDraftMagnitude)
18962 || !std::isfinite(surface->endDraftMagnitude)
18963 || !putCount(surface->numCrossSections,
18964 DRW_LoftedSurface::kMaxReferenceTokenCount)
18965 || !putCount(surface->numGuideCurves,
18966 DRW_LoftedSurface::kMaxReferenceTokenCount)
18967 || !finiteSurfaceMatrix(surface->loftEntityTransform))
18968 return false;
18969 for (double value : surface->loftEntityTransform)
18970 buf->putBitDouble(value);
18971 buf->putBitLong(surface->planeNormalLoftingType);
18972 buf->putBitDouble(surface->startDraftAngle);
18973 buf->putBitDouble(surface->endDraftAngle);
18974 buf->putBitDouble(surface->startDraftMagnitude);
18975 buf->putBitDouble(surface->endDraftMagnitude);
18976 putBool(surface->arcLengthParameterization);
18977 putBool(surface->noTwist);
18978 putBool(surface->alignDirection);
18979 putBool(surface->simpleSurfaces);
18980 putBool(surface->closedSurfaces);
18981 putBool(surface->solid);
18982 putBool(surface->ruledSurface);
18983 putBool(surface->virtualGuide);
18984 buf->putBitShort(static_cast<std::uint16_t>(surface->numCrossSections));
18985 buf->putBitShort(static_cast<std::uint16_t>(surface->numGuideCurves));
18986 break;
18987 }
18988 case DRW::REVOLVEDSURFACE: {
18989 const auto *surface = static_cast<const DRW_RevolvedSurface *>(this);
18990 if (surface->classId > std::numeric_limits<std::int32_t>::max()
18991 || surface->id > std::numeric_limits<std::int32_t>::max()
18992 || !finiteSurfaceCoord(surface->axisPoint)
18993 || !finiteSurfaceCoord(surface->axisVector)
18994 || !std::isfinite(surface->revolveAngle)
18995 || !std::isfinite(surface->startAngle)
18996 || !std::isfinite(surface->draftAngle)
18997 || !std::isfinite(surface->draftStartDistance)
18998 || !std::isfinite(surface->draftEndDistance)
18999 || !std::isfinite(surface->twistAngle)
19000 || !finiteSurfaceMatrix(surface->transform))
19001 return false;
19002 buf->putBitLong(static_cast<std::int32_t>(surface->classId));
19003 buf->putBitLong(static_cast<std::int32_t>(surface->id));
19004 buf->put3BitDouble(surface->axisPoint);
19005 buf->put3BitDouble(surface->axisVector);
19006 buf->putBitDouble(surface->revolveAngle);
19007 buf->putBitDouble(surface->startAngle);
19008 for (double value : surface->transform)
19009 buf->putBitDouble(value);
19010 buf->putBitDouble(surface->draftAngle);
19011 buf->putBitDouble(surface->draftStartDistance);
19012 buf->putBitDouble(surface->draftEndDistance);
19013 buf->putBitDouble(surface->twistAngle);
19014 putBool(surface->solid);
19015 putBool(surface->closeToAxis);
19016 break;
19017 }
19018 case DRW::SWEPTSURFACE: {
19019 const auto *surface = static_cast<const DRW_SweptSurface *>(this);
19020 if (surface->classVersion > 10
19021 || surface->sweepEntityId > std::numeric_limits<std::int32_t>::max()
19022 || surface->pathEntityId > std::numeric_limits<std::int32_t>::max()
19023 || surface->sweepData.size() > DRW_SweptSurface::kMaxSweepDataSize
19024 || surface->pathData.size() > DRW_SweptSurface::kMaxSweepDataSize
19025 || !std::isfinite(surface->draftAngle)
19026 || !std::isfinite(surface->draftStartDistance)
19027 || !std::isfinite(surface->draftEndDistance)
19028 || !std::isfinite(surface->twistAngle)
19029 || !std::isfinite(surface->scaleFactor)
19030 || !std::isfinite(surface->alignAngle)
19031 || !finiteSurfaceCoord(surface->referenceVector)
19032 || surface->sweepAlignmentFlags < 0
19033 || surface->sweepAlignmentFlags > 0xFFFF
19034 || surface->pathFlags < 0 || surface->pathFlags > 0xFFFF
19035 || !finiteSurfaceMatrix(surface->sweepEntityTransform)
19036 || !finiteSurfaceMatrix(surface->pathEntityTransform))
19037 return false;
19038 buf->putBitLong(static_cast<std::int32_t>(surface->classVersion));
19039 buf->putBitLong(static_cast<std::int32_t>(surface->sweepEntityId));
19040 buf->putBitLong(static_cast<std::int32_t>(surface->sweepData.size()));
19041 if (!surface->sweepData.empty())
19042 buf->putBytes(surface->sweepData.data(), surface->sweepData.size());
19043 buf->putBitLong(static_cast<std::int32_t>(surface->pathEntityId));
19044 buf->putBitLong(static_cast<std::int32_t>(surface->pathData.size()));
19045 if (!surface->pathData.empty())
19046 buf->putBytes(surface->pathData.data(), surface->pathData.size());
19047 buf->putBitDouble(surface->draftAngle);
19048 buf->putBitDouble(surface->draftStartDistance);
19049 buf->putBitDouble(surface->draftEndDistance);
19050 buf->putBitDouble(surface->twistAngle);
19051 buf->putBitDouble(surface->scaleFactor);
19052 buf->putBitDouble(surface->alignAngle);
19053 for (double value : surface->sweepEntityTransform)
19054 buf->putBitDouble(value);
19055 for (double value : surface->pathEntityTransform)
19056 buf->putBitDouble(value);
19057 putBool(surface->solid);
19058 buf->putBitShort(static_cast<std::uint16_t>(surface->sweepAlignmentFlags));
19059 buf->putBitShort(static_cast<std::uint16_t>(surface->pathFlags));
19060 putBool(surface->alignStart);
19061 putBool(surface->bank);
19062 putBool(surface->basePointSet);
19063 putBool(surface->sweepEntityTransformComputed);
19064 putBool(surface->pathEntityTransformComputed);
19065 if (!finiteSurfaceCoord(surface->referenceVector))
19066 return false;
19067 putCoord(surface->referenceVector);
19068 break;
19069 }
19070 case DRW::NURBSURFACE: {
19071 if (version < DRW::AC1027)
19072 break;
19073 const auto *surface = static_cast<const DRW_NurbsSurface *>(this);
19074 if (!finiteSurfaceCoord(surface->uvec1)
19075 || !finiteSurfaceCoord(surface->vvec1)
19076 || !finiteSurfaceCoord(surface->uvec2)
19077 || !finiteSurfaceCoord(surface->vvec2))
19078 return false;
19079 buf->putBitShort(surface->short170);
19080 putBool(surface->cvHullDisplay);
19081 buf->put3BitDouble(surface->uvec1);
19082 buf->put3BitDouble(surface->vvec1);
19083 buf->put3BitDouble(surface->uvec2);
19084 buf->put3BitDouble(surface->vvec2);
19085 break;
19086 }
19087 case DRW::PLANESURFACE:
19088 break;
19089 default:
19090 return false;
19091 }
19092 }
19093
19094 if (!encodeDwgEntHandle(version, buf, handleBuf))
19095 return false;
19096 if (eType == DRW::LOFTEDSURFACE) {
19097 dwgBufferW *hb = handleBuf != nullptr ? handleBuf : buf;
19098 for (std::uint32_t ref : crossSectionHandles)
19099 putSurfaceHandle(hb, ref);
19100 for (std::uint32_t ref : guideCurveHandles)
19101 putSurfaceHandle(hb, ref);
19102 putSurfaceHandle(hb,
19103 static_cast<const DRW_LoftedSurface *>(this)
19104 ->pathCurveHandle);
19105 }
19106 return true;
19107}
19108
19109bool DRW_Dimension::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
19110 switch (code) {
19111 case 1:
19112 text = reader->getUtf8String();
19113 break;
19114 case 2:
19115 name = reader->getUtf8String();
19116 break;
19117 case 3:
19118 style = reader->getUtf8String();
19119 break;
19120 case 70:
19121 type = reader->getInt32();
19122 break;
19123 case 71:
19124 align = reader->getInt32();
19125 break;
19126 case 72:
19127 linesty = reader->getInt32();
19128 break;
19129 case 10:
19130 defPoint.x = reader->getDouble();
19131 break;
19132 case 20:
19133 defPoint.y = reader->getDouble();
19134 break;
19135 case 30:
19136 defPoint.z = reader->getDouble();
19137 break;
19138 case 11:
19139 textPoint.x = reader->getDouble();
19140 break;
19141 case 21:
19142 textPoint.y = reader->getDouble();
19143 break;
19144 case 31:
19145 textPoint.z = reader->getDouble();
19146 break;
19147 case 12:
19148 clonePoint.x = reader->getDouble();
19149 break;
19150 case 22:
19151 clonePoint.y = reader->getDouble();
19152 break;
19153 case 32:
19154 clonePoint.z = reader->getDouble();
19155 break;
19156 case 13:
19157 def1.x = reader->getDouble();
19158 break;
19159 case 23:
19160 def1.y = reader->getDouble();
19161 break;
19162 case 33:
19163 def1.z = reader->getDouble();
19164 break;
19165 case 14:
19166 def2.x = reader->getDouble();
19167 break;
19168 case 24:
19169 def2.y = reader->getDouble();
19170 break;
19171 case 34:
19172 def2.z = reader->getDouble();
19173 break;
19174 case 15:
19175 circlePoint.x = reader->getDouble();
19176 break;
19177 case 25:
19178 circlePoint.y = reader->getDouble();
19179 break;
19180 case 35:
19181 circlePoint.z = reader->getDouble();
19182 break;
19183 case 16:
19184 arcPoint.x = reader->getDouble();
19185 break;
19186 case 26:
19187 arcPoint.y = reader->getDouble();
19188 break;
19189 case 36:
19190 arcPoint.z = reader->getDouble();
19191 break;
19192 case 41:
19193 linefactor = reader->getDouble();
19194 break;
19195 case 53:
19196 rot = reader->getDouble();
19197 break;
19198 case 50:
19199 angle = reader->getDouble();
19200 break;
19201 case 52:
19202 oblique = reader->getDouble();
19203 break;
19204 case 40:
19205 length = reader->getDouble();
19206 break;
19207 case 51:
19208 hdir = reader->getDouble();
19209 break;
19210 case 42:
19211 measureValue = reader->getDouble();
19212 break;
19213 case 74:
19214 flipArrow1 = reader->getInt32() != 0;
19215 break;
19216 case 75:
19217 flipArrow2 = reader->getInt32() != 0;
19218 break;
19219 case 76:
19220 genTol = reader->getInt32() != 0;
19221 break;
19222 case 77:
19223 limGen = reader->getInt32() != 0;
19224 break;
19225 case 43:
19226 tolPlus = reader->getDouble();
19227 break;
19228 case 44:
19229 tolMinus = reader->getDouble();
19230 break;
19231 case 45:
19232 tolScale = reader->getDouble();
19233 break;
19234 case 78:
19235 tolDecimals = reader->getInt32();
19236 break;
19237 case 79:
19238 tolAlign = reader->getInt32();
19239 break;
19240 case 80:
19241 tolZero = reader->getInt32();
19242 break;
19243 case 81:
19244 altTolDecimals = reader->getInt32();
19245 break;
19246 case 82:
19247 altZero = reader->getInt32();
19248 break;
19249 case 83:
19250 altTolZero = reader->getInt32();
19251 break;
19252 case 84:
19253 textMove = reader->getInt32();
19254 break;
19255 default:
19256 return DRW_Entity::parseCode(code, reader);
19257 }
19258
19259 return true;
19260}
19261
19262bool DRW_Dimension::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs)
19263{
19264 DRW_UNUSED( version)(void)version;
19265 DRW_UNUSED( buf)(void)buf;
19266 DRW_UNUSED( bs)(void)bs;
19267
19268 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"
)
;
19269
19270 return false;
19271}
19272
19273void DRW_Dimension::resetDwgState() {
19274 DRW_Entity::reset();
19275 type = 0;
19276 name.clear();
19277 defPoint = DRW_Coord{0.0, 0.0, 0.0};
19278 textPoint = DRW_Coord{0.0, 0.0, 0.0};
19279 text.clear();
19280 style = "STANDARD";
19281 align = 5;
19282 linesty = 1;
19283 linefactor = 1.0;
19284 rot = 0.0;
19285 extPoint = DRW_Coord{0.0, 0.0, 1.0};
19286 hdir = 0.0;
19287 clonePoint = DRW_Coord{0.0, 0.0, 0.0};
19288 def1 = DRW_Coord{0.0, 0.0, 0.0};
19289 def2 = DRW_Coord{0.0, 0.0, 0.0};
19290 angle = 0.0;
19291 oblique = 0.0;
19292 circlePoint = DRW_Coord{0.0, 0.0, 0.0};
19293 arcPoint = DRW_Coord{0.0, 0.0, 0.0};
19294 length = 0.0;
19295 measureValue = 0.0;
19296 flipArrow1 = false;
19297 flipArrow2 = false;
19298 genTol = false;
19299 limGen = false;
19300 tolPlus = 0.0;
19301 tolMinus = 0.0;
19302 tolScale = 0.0;
19303 tolDecimals = 0;
19304 tolAlign = 0;
19305 tolZero = 0;
19306 altTolDecimals = 0;
19307 altZero = 0;
19308 altTolZero = 0;
19309 textMove = 0;
19310 dimStyleH = dwgHandle{};
19311 blockH = dwgHandle{};
19312}
19313
19314bool DRW_Dimension::parseDwg(DRW::Version version, dwgBuffer *buf, dwgBuffer *sBuf, std::uint32_t bs /*= 0*/) {
19315 (void)sBuf;
19316 resetDwgState();
19317 if (buf == nullptr)
19318 return false;
19319
19320 dwgBuffer sBuff = *buf;
19321 dwgBuffer *commonStringBuffer = buf;
19322 if (version > DRW::AC1018) {//2007+
19323 commonStringBuffer = &sBuff; //separate buffer for strings
19324 }
19325
19326 if (!DRW_Entity::parseDwg(version, buf, commonStringBuffer, bs))
19327 return false;
19328
19329 DRW_DBG("\n***************************** parsing dimension *********************************************")DRW_dbg::dbg("\n***************************** parsing dimension *********************************************"
)
;
19330 const std::uint64_t bodyEndBit = dwgDataEndBit;
19331 dwgBuffer bodyProbe = buf->forkIndependent();
19332 dwgBuffer stringProbe = sBuff;
19333 dwgBuffer *textBuffer = &bodyProbe;
19334 if (version > DRW::AC1018)
19335 textBuffer = &stringProbe;
19336 std::uint64_t stringStartBit = bodyEndBit;
19337 std::uint64_t stringEndBit = bodyEndBit;
19338 if (version > DRW::AC1018) {
19339 stringStartBit = currentDwgBit(textBuffer);
19340 if (version > DRW::AC1021) {
19341 std::uint64_t ignoredBodyEndBit = 0;
19342 if (!entityBodyDataEndBit(*textBuffer, version, objSize,
19343 ignoredBodyEndBit, &stringEndBit))
19344 return false;
19345 }
19346 }
19347
19348 std::uint8_t parsedDimVersion = 0;
19349 if (version > DRW::AC1021) { //2010+
19350 if (!readBoundedRawChar8(bodyProbe, bodyEndBit, parsedDimVersion))
19351 return false;
19352 DRW_DBG("\ndimVersion: ")DRW_dbg::dbg("\ndimVersion: "); DRW_DBG(parsedDimVersion)DRW_dbg::dbg(parsedDimVersion);
19353 }
19354
19355 DRW_Coord parsedExtrusion;
19356 DRW_Coord parsedTextPoint;
19357 DRW_Coord ignoredInsertionPoint;
19358 double parsedTextPointZ = 0.0;
19359 double ignoredInsertionRotation = 0.0;
19360 std::uint8_t parsedType = 0;
19361 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedExtrusion)
19362 || !readBoundedRawDouble(bodyProbe, bodyEndBit, parsedTextPoint.x)
19363 || !readBoundedRawDouble(bodyProbe, bodyEndBit, parsedTextPoint.y)
19364 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedTextPointZ)
19365 || !readBoundedRawChar8(bodyProbe, bodyEndBit, parsedType)) {
19366 return false;
19367 }
19368 parsedTextPoint.z = parsedTextPointZ;
19369
19370 // ODA §20.4.22: Extrusion is plain 3BD (NOT BE) — confirmed by libreDWG dwg_spec_shared.h
19371 DRW_DBG("\nextPoint: ")DRW_dbg::dbg("\nextPoint: "); DRW_DBGPT(parsedExtrusion.x, parsedExtrusion.y,DRW_dbg::dbgPT(parsedExtrusion.x, parsedExtrusion.y, parsedExtrusion
.z)
19372 parsedExtrusion.z)DRW_dbg::dbgPT(parsedExtrusion.x, parsedExtrusion.y, parsedExtrusion
.z)
;
19373 DRW_DBG("\ntextPoint: ")DRW_dbg::dbg("\ntextPoint: "); DRW_DBGPT(parsedTextPoint.x, parsedTextPoint.y,DRW_dbg::dbgPT(parsedTextPoint.x, parsedTextPoint.y, parsedTextPoint
.z)
19374 parsedTextPoint.z)DRW_dbg::dbgPT(parsedTextPoint.x, parsedTextPoint.y, parsedTextPoint
.z)
;
19375 DRW_DBG("\ntype (70) read: ")DRW_dbg::dbg("\ntype (70) read: "); DRW_DBG(parsedType)DRW_dbg::dbg(parsedType);
19376 const std::uint8_t parsedDimensionType =
19377 static_cast<std::uint8_t>((parsedType & 1) ? parsedType & 0x7F
19378 : parsedType | 0x80);
19379 int parsedTypeValue = (parsedDimensionType & 2) ? parsedDimensionType | 0x20
19380 : parsedDimensionType & 0xDF;
19381 DRW_DBG(" type (70) set: ")DRW_dbg::dbg(" type (70) set: "); DRW_DBG(parsedTypeValue)DRW_dbg::dbg(parsedTypeValue);
19382 //clear last 3 bits to set integer dim type
19383 parsedTypeValue &= 0xF8;
19384 UTF8STRINGstd::string parsedText;
19385 if (version > DRW::AC1018 && stringStartBit >= stringEndBit) {
19386 parsedText.clear();
19387 } else if (!readBoundedVariableText(*textBuffer, stringEndBit, version,
19388 parsedText)) {
19389 return false;
19390 }
19391 DRW_DBG("\nforced dim text: ")DRW_dbg::dbg("\nforced dim text: "); DRW_DBG(parsedText.c_str())DRW_dbg::dbg(parsedText.c_str());
19392 double parsedRot = 0.0;
19393 double parsedHDir = 0.0;
19394 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, parsedRot)
19395 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedHDir)
19396 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ignoredInsertionPoint)
19397 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
19398 ignoredInsertionRotation)) {
19399 return false;
19400 }
19401 DRW_DBG("\ninspoint: ")DRW_dbg::dbg("\ninspoint: "); DRW_DBGPT(ignoredInsertionPoint.x,DRW_dbg::dbgPT(ignoredInsertionPoint.x, ignoredInsertionPoint
.y, ignoredInsertionPoint.z)
19402 ignoredInsertionPoint.y,DRW_dbg::dbgPT(ignoredInsertionPoint.x, ignoredInsertionPoint
.y, ignoredInsertionPoint.z)
19403 ignoredInsertionPoint.z)DRW_dbg::dbgPT(ignoredInsertionPoint.x, ignoredInsertionPoint
.y, ignoredInsertionPoint.z)
;
19404 DRW_DBG(" insRot_code54: ")DRW_dbg::dbg(" insRot_code54: "); DRW_DBG(ignoredInsertionRotation)DRW_dbg::dbg(ignoredInsertionRotation);
19405
19406 std::uint16_t parsedAlign = 5;
19407 std::uint16_t parsedLineStyle = 1;
19408 double parsedLineFactor = 1.0;
19409 double parsedMeasureValue = 0.0;
19410 bool parsedFlipArrow1 = false;
19411 bool parsedFlipArrow2 = false;
19412 if (version > DRW::AC1014) { //2000+
19413 if (!readBoundedBitShort(bodyProbe, bodyEndBit, parsedAlign)
19414 || !readBoundedBitShort(bodyProbe, bodyEndBit, parsedLineStyle)
19415 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedLineFactor)
19416 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
19417 parsedMeasureValue))
19418 return false;
19419 DRW_DBG("\n actMeas_code42: ")DRW_dbg::dbg("\n actMeas_code42: "); DRW_DBG(parsedMeasureValue)DRW_dbg::dbg(parsedMeasureValue);
19420 if (version > DRW::AC1018) { //2007+
19421 bool ignoredBit = false;
19422 if (!readBoundedBit(bodyProbe, bodyEndBit, ignoredBit)
19423 || !readBoundedBit(bodyProbe, bodyEndBit, parsedFlipArrow1)
19424 || !readBoundedBit(bodyProbe, bodyEndBit, parsedFlipArrow2))
19425 return false;
19426 DRW_DBG("\n2007, unk, flip1, flip2: ")DRW_dbg::dbg("\n2007, unk, flip1, flip2: "); DRW_DBG(ignoredBit)DRW_dbg::dbg(ignoredBit);
19427 DRW_DBG(parsedFlipArrow1)DRW_dbg::dbg(parsedFlipArrow1); DRW_DBG(parsedFlipArrow2)DRW_dbg::dbg(parsedFlipArrow2);
19428 }
19429 }
19430 DRW_Coord parsedClonePoint;
19431 if (!readBoundedRawDouble(bodyProbe, bodyEndBit, parsedClonePoint.x)
19432 || !readBoundedRawDouble(bodyProbe, bodyEndBit, parsedClonePoint.y))
19433 return false;
19434 parsedClonePoint.z = 0.0;
19435 if (!bodyProbe.isGood() || !textBuffer->isGood()
19436 || currentDwgBit(&bodyProbe) > bodyEndBit
19437 || currentDwgBit(textBuffer) > stringEndBit
19438 || !std::isfinite(parsedExtrusion.x)
19439 || !std::isfinite(parsedExtrusion.y)
19440 || !std::isfinite(parsedExtrusion.z)
19441 || !std::isfinite(parsedTextPoint.x)
19442 || !std::isfinite(parsedTextPoint.y)
19443 || !std::isfinite(parsedTextPoint.z)
19444 || !std::isfinite(ignoredInsertionPoint.x)
19445 || !std::isfinite(ignoredInsertionPoint.y)
19446 || !std::isfinite(ignoredInsertionPoint.z)
19447 || !std::isfinite(parsedRot)
19448 || !std::isfinite(parsedHDir)
19449 || !std::isfinite(ignoredInsertionRotation)
19450 || !std::isfinite(parsedLineFactor)
19451 || !std::isfinite(parsedMeasureValue)
19452 || !std::isfinite(parsedClonePoint.x)
19453 || !std::isfinite(parsedClonePoint.y))
19454 return false;
19455
19456 extPoint = parsedExtrusion;
19457 textPoint = parsedTextPoint;
19458 type = parsedTypeValue;
19459 text = parsedText;
19460 rot = parsedRot;
19461 hdir = parsedHDir;
19462 align = parsedAlign;
19463 linesty = parsedLineStyle;
19464 linefactor = parsedLineFactor;
19465 measureValue = parsedMeasureValue;
19466 flipArrow1 = parsedFlipArrow1;
19467 flipArrow2 = parsedFlipArrow2;
19468 clonePoint = parsedClonePoint;
19469 *buf = bodyProbe;
19470 return true;
19471}
19472
19473bool DRW_Dimension::parseDwgDimensionHandles(DRW::Version version,
19474 dwgBuffer *buf,
19475 std::uint64_t handleEndBit) {
19476 if (buf == nullptr)
19477 return false;
19478 dwgBuffer probe = buf->forkIndependent();
19479 if (!parseDwgEntHandle(version, &probe, true, handleEndBit))
19480 return false;
19481 dwgHandle parsedDimStyle;
19482 dwgHandle parsedBlock;
19483 if (!readBoundedDwgHandle(probe, handleEndBit, 0, false, parsedDimStyle)
19484 || !readBoundedDwgHandle(probe, handleEndBit, 0, false, parsedBlock))
19485 return false;
19486 dimStyleH = parsedDimStyle;
19487 blockH = parsedBlock;
19488 *buf = probe;
19489 return true;
19490}
19491
19492bool DRW_DimAligned::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19493 DRW_Dimension::resetDwgState();
19494 dwgBuffer *sourceBuf = buf;
19495 auto fail = [this, sourceBuf]() {
19496 if (sourceBuf != nullptr)
19497 sourceBuf->invalidate();
19498 DRW_Dimension::resetDwgState();
19499 return false;
19500 };
19501 if (sourceBuf == nullptr)
19502 return fail();
19503
19504 dwgBuffer probe = sourceBuf->forkIndependent();
19505 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19506 return fail();
19507 buf = &probe;
19508
19509 if (oType == 0x15)
19510 DRW_DBG("\n***************************** parsing dim linear *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim linear *********************************************\n"
)
;
19511 else
19512 DRW_DBG("\n***************************** parsing dim aligned *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim aligned *********************************************\n"
)
;
19513 const std::uint64_t bodyEndBit = dwgDataEndBit;
19514 DRW_Coord parsedDef1;
19515 DRW_Coord parsedDef2;
19516 DRW_Coord parsedDimPoint;
19517 double parsedOblique = 0.0;
19518 double parsedAngle = 0.0;
19519 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedDef1)
19520 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef2)
19521 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDimPoint)
19522 || !readBoundedBitDouble(*buf, bodyEndBit, parsedOblique))
19523 return fail();
19524 if (oType == 0x15
19525 && !readBoundedBitDouble(*buf, bodyEndBit, parsedAngle))
19526 return fail();
19527 const auto finite = [](const DRW_Coord& point) {
19528 return std::isfinite(point.x) && std::isfinite(point.y)
19529 && std::isfinite(point.z);
19530 };
19531 if (!std::isfinite(parsedOblique) || !std::isfinite(parsedAngle)
19532 || !finite(parsedDef1) || !finite(parsedDef2)
19533 || !finite(parsedDimPoint)) {
19534 return fail();
19535 }
19536 setPt3(parsedDef1); //def1
19537 setPt4(parsedDef2);
19538 setDefPoint(parsedDimPoint);
19539 setOb52(parsedOblique * ARAD57.29577951308232); // radians → degrees
19540 if (oType == 0x15)
19541 setAn50(parsedAngle * ARAD57.29577951308232);
19542 else
19543 type |= 1;
19544 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");
19545
19546 std::uint64_t handleEndBit = 0;
19547 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19548 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19549 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimAligned::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimAligned::parseDwg()\n"
)
;
19550 return fail();
19551 }
19552 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");
19553 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");
19554
19555 // RS crc; //RS */
19556 if (!buf->isGood())
19557 return fail();
19558 *sourceBuf = probe;
19559 return true;
19560 }
19561
19562bool DRW_DimRadial::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19563 DRW_Dimension::resetDwgState();
19564 dwgBuffer *sourceBuf = buf;
19565 auto fail = [this, sourceBuf]() {
19566 if (sourceBuf != nullptr)
19567 sourceBuf->invalidate();
19568 DRW_Dimension::resetDwgState();
19569 return false;
19570 };
19571 if (sourceBuf == nullptr)
19572 return fail();
19573
19574 dwgBuffer probe = sourceBuf->forkIndependent();
19575 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19576 return fail();
19577 buf = &probe;
19578
19579 DRW_DBG("\n***************************** parsing dim radial *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim radial *********************************************\n"
)
;
19580 const std::uint64_t bodyEndBit = dwgDataEndBit;
19581 DRW_Coord parsedDefPoint;
19582 DRW_Coord parsedCenter;
19583 double parsedLength = 0.0;
19584 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint)
19585 || !readBoundedBitCoord(*buf, bodyEndBit, parsedCenter)
19586 || !readBoundedBitDouble(*buf, bodyEndBit, parsedLength)
19587 || !std::isfinite(parsedDefPoint.x)
19588 || !std::isfinite(parsedDefPoint.y)
19589 || !std::isfinite(parsedDefPoint.z)
19590 || !std::isfinite(parsedCenter.x)
19591 || !std::isfinite(parsedCenter.y)
19592 || !std::isfinite(parsedCenter.z)
19593 || !std::isfinite(parsedLength))
19594 return fail();
19595 setDefPoint(parsedDefPoint); //code 10
19596 DRW_DBG("defPoint: ")DRW_dbg::dbg("defPoint: "); DRW_DBGPT(parsedDefPoint.x, parsedDefPoint.y,DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
19597 parsedDefPoint.z)DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
;
19598 setPt5(parsedCenter); //center pt code 15
19599 DRW_DBG("\ncenter point: ")DRW_dbg::dbg("\ncenter point: "); DRW_DBGPT(parsedCenter.x, parsedCenter.y,DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
19600 parsedCenter.z)DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
;
19601 setRa40(parsedLength); //leader length code 40
19602 DRW_DBG("\nleader length: ")DRW_dbg::dbg("\nleader length: "); DRW_DBG(getRa40())DRW_dbg::dbg(getRa40());
19603 type |= 4;
19604 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");
19605 if (!buf->isGood())
19606 return fail();
19607
19608 std::uint64_t handleEndBit = 0;
19609 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19610 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19611 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimRadial::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimRadial::parseDwg()\n"
)
;
19612 return fail();
19613 }
19614 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");
19615 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");
19616
19617 // RS crc; //RS */
19618 if (!buf->isGood())
19619 return fail();
19620 *sourceBuf = probe;
19621 return true;
19622}
19623
19624// DRW_DimLargeRadial (AcDbRadialDimensionLarge, LARGE_RADIAL_DIMENSION).
19625// DXF group-code parser: the AcDbRadialDimensionLarge subclass overloads codes
19626// 13/14/15/40 (chord / override center / jog point / jog angle), so gate them on
19627// the subclass marker (like DRW_DimArc). The chord point is stored as the radial
19628// diameter point so the existing addDimRadial consumer renders center→chord.
19629bool DRW_DimLargeRadial::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
19630 if (code == 100) {
19631 std::string s = reader->getString();
19632 if (s == "AcDbRadialDimensionLarge") {
19633 m_largeRadialSubclassSeen = true;
19634 return true;
19635 }
19636 return DRW_Dimension::parseCode(code, reader);
19637 }
19638 if (m_largeRadialSubclassSeen) {
19639 DRW_Coord chord;
19640 switch (code) {
19641 case 13: chord = getPt5(); chord.x = reader->getDouble(); setPt5(chord); return true;
19642 case 23: chord = getPt5(); chord.y = reader->getDouble(); setPt5(chord); return true;
19643 case 33: chord = getPt5(); chord.z = reader->getDouble(); setPt5(chord); return true;
19644 case 14: overrideCenterPoint.x = reader->getDouble(); return true;
19645 case 24: overrideCenterPoint.y = reader->getDouble(); return true;
19646 case 34: overrideCenterPoint.z = reader->getDouble(); return true;
19647 case 15: jogPoint.x = reader->getDouble(); return true;
19648 case 25: jogPoint.y = reader->getDouble(); return true;
19649 case 35: jogPoint.z = reader->getDouble(); return true;
19650 case 40: jogAngle = reader->getDouble(); return true;
19651 default: break;
19652 }
19653 }
19654 return DRW_Dimension::parseCode(code, reader);
19655}
19656
19657// DRW_DimLargeRadial DWG body: five subclass reads then the dim-style and
19658// anon-block handles. The three subclass points are ordered
19659// definition point, JOG point, jog angle, CHORD point, OVERRIDDEN center
19660// so that the decoded fields match the DXF group codes (chord=13, override=14,
19661// jog=15) and libdxfrw's own DXF parseCode. The read-only reference parser
19662// (parseLargeRadialDimension) labels the 2nd/4th/5th reads chord/override/jog,
19663// i.e. a cyclic rotation of the point roles; that is inconsistent with the DXF
19664// semantics and with an ODA File Converter DXF↔DWG round-trip (which preserves
19665// codes 13/14/15 exactly). Verified by large_radial_dim_dwg_tests.cpp against
19666// an ODA-synthesized fixture, cross-checked with the dwg-parser's DXF read.
19667// Only the field labels change vs. the reference parser — the read sizes/order
19668// (3BD, 3BD, BD, 3BD, 3BD) are identical, so buffer alignment is unchanged.
19669bool DRW_DimLargeRadial::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19670 DRW_Dimension::resetDwgState();
19671 overrideCenterPoint = DRW_Coord{0.0, 0.0, 0.0};
19672 jogPoint = DRW_Coord{0.0, 0.0, 0.0};
19673 jogAngle = 0.0;
19674 m_largeRadialSubclassSeen = false;
19675 dwgBuffer *sourceBuf = buf;
19676 auto fail = [this, sourceBuf]() {
19677 if (sourceBuf != nullptr)
19678 sourceBuf->invalidate();
19679 DRW_Dimension::resetDwgState();
19680 overrideCenterPoint = DRW_Coord{0.0, 0.0, 0.0};
19681 jogPoint = DRW_Coord{0.0, 0.0, 0.0};
19682 jogAngle = 0.0;
19683 m_largeRadialSubclassSeen = false;
19684 return false;
19685 };
19686 if (sourceBuf == nullptr)
19687 return fail();
19688
19689 dwgBuffer probe = sourceBuf->forkIndependent();
19690 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19691 return fail();
19692 buf = &probe;
19693 const std::uint64_t bodyEndBit = dwgDataEndBit;
19694 DRW_Coord parsedDefPoint;
19695 DRW_Coord parsedJogPoint;
19696 DRW_Coord parsedChordPoint;
19697 DRW_Coord parsedOverrideCenter;
19698 double parsedJogAngle = 0.0;
19699 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint)
19700 || !readBoundedBitCoord(*buf, bodyEndBit, parsedJogPoint)
19701 || !readBoundedBitDouble(*buf, bodyEndBit, parsedJogAngle)
19702 || !readBoundedBitCoord(*buf, bodyEndBit, parsedChordPoint)
19703 || !readBoundedBitCoord(*buf, bodyEndBit, parsedOverrideCenter)
19704 || !std::isfinite(parsedDefPoint.x)
19705 || !std::isfinite(parsedDefPoint.y)
19706 || !std::isfinite(parsedDefPoint.z)
19707 || !std::isfinite(parsedJogPoint.x)
19708 || !std::isfinite(parsedJogPoint.y)
19709 || !std::isfinite(parsedJogPoint.z)
19710 || !std::isfinite(parsedChordPoint.x)
19711 || !std::isfinite(parsedChordPoint.y)
19712 || !std::isfinite(parsedChordPoint.z)
19713 || !std::isfinite(parsedOverrideCenter.x)
19714 || !std::isfinite(parsedOverrideCenter.y)
19715 || !std::isfinite(parsedOverrideCenter.z)
19716 || !std::isfinite(parsedJogAngle))
19717 return fail();
19718 setDefPoint(parsedDefPoint); // definition point (code 10)
19719 jogPoint = parsedJogPoint; // jog vertex (code 15)
19720 jogAngle = parsedJogAngle; // jog transverse angle (code 40)
19721 setPt5(parsedChordPoint); // chord point (code 13)
19722 overrideCenterPoint = parsedOverrideCenter; // overridden center (code 14)
19723 type |= 4; // radial dimension type bit
19724 if (!buf->isGood())
19725 return fail();
19726 std::uint64_t handleEndBit = 0;
19727 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19728 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19729 return fail();
19730 }
19731 *sourceBuf = probe;
19732 return true;
19733}
19734
19735bool DRW_DimDiametric::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19736 DRW_Dimension::resetDwgState();
19737 dwgBuffer *sourceBuf = buf;
19738 auto fail = [this, sourceBuf]() {
19739 if (sourceBuf != nullptr)
19740 sourceBuf->invalidate();
19741 DRW_Dimension::resetDwgState();
19742 return false;
19743 };
19744 if (sourceBuf == nullptr)
19745 return fail();
19746
19747 dwgBuffer probe = sourceBuf->forkIndependent();
19748 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19749 return fail();
19750 buf = &probe;
19751
19752 DRW_DBG("\n***************************** parsing dim diametric *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim diametric *********************************************\n"
)
;
19753 const std::uint64_t bodyEndBit = dwgDataEndBit;
19754 DRW_Coord parsedCenter;
19755 DRW_Coord parsedDefPoint;
19756 double parsedLength = 0.0;
19757 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedCenter)
19758 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint)
19759 || !readBoundedBitDouble(*buf, bodyEndBit, parsedLength)
19760 || !std::isfinite(parsedCenter.x)
19761 || !std::isfinite(parsedCenter.y)
19762 || !std::isfinite(parsedCenter.z)
19763 || !std::isfinite(parsedDefPoint.x)
19764 || !std::isfinite(parsedDefPoint.y)
19765 || !std::isfinite(parsedDefPoint.z)
19766 || !std::isfinite(parsedLength))
19767 return fail();
19768 setPt5(parsedCenter); //center pt code 15
19769 DRW_DBG("center point: ")DRW_dbg::dbg("center point: "); DRW_DBGPT(parsedCenter.x, parsedCenter.y,DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
19770 parsedCenter.z)DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
;
19771 setDefPoint(parsedDefPoint); //code 10
19772 DRW_DBG("\ndefPoint: ")DRW_dbg::dbg("\ndefPoint: "); DRW_DBGPT(parsedDefPoint.x, parsedDefPoint.y,DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
19773 parsedDefPoint.z)DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
;
19774 setRa40(parsedLength); //leader length code 40
19775 DRW_DBG("\nleader length: ")DRW_dbg::dbg("\nleader length: "); DRW_DBG(getRa40())DRW_dbg::dbg(getRa40());
19776 type |= 3;
19777 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");
19778 if (!buf->isGood())
19779 return fail();
19780
19781 std::uint64_t handleEndBit = 0;
19782 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19783 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19784 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimDiametric::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimDiametric::parseDwg()\n"
)
;
19785 return fail();
19786 }
19787 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");
19788 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");
19789
19790 // RS crc; //RS */
19791 if (!buf->isGood())
19792 return fail();
19793 *sourceBuf = probe;
19794 return true;
19795}
19796
19797bool DRW_DimAngular::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19798 DRW_Dimension::resetDwgState();
19799 dwgBuffer *sourceBuf = buf;
19800 auto fail = [this, sourceBuf]() {
19801 if (sourceBuf != nullptr)
19802 sourceBuf->invalidate();
19803 DRW_Dimension::resetDwgState();
19804 return false;
19805 };
19806 if (sourceBuf == nullptr)
19807 return fail();
19808
19809 dwgBuffer probe = sourceBuf->forkIndependent();
19810 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19811 return fail();
19812 buf = &probe;
19813
19814 DRW_DBG("\n***************************** parsing dim angular *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim angular *********************************************\n"
)
;
19815 const std::uint64_t bodyEndBit = dwgDataEndBit;
19816 DRW_Coord parsedArcPoint;
19817 DRW_Coord parsedDef1;
19818 DRW_Coord parsedDef2;
19819 DRW_Coord parsedCenter;
19820 DRW_Coord parsedDefPoint;
19821 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedArcPoint.x)
19822 || !readBoundedRawDouble(*buf, bodyEndBit, parsedArcPoint.y)
19823 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef1)
19824 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef2)
19825 || !readBoundedBitCoord(*buf, bodyEndBit, parsedCenter)
19826 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint))
19827 return fail();
19828 parsedArcPoint.z = 0.0;
19829 const auto finite = [](const DRW_Coord& point) {
19830 return std::isfinite(point.x) && std::isfinite(point.y)
19831 && std::isfinite(point.z);
19832 };
19833 if (!finite(parsedArcPoint) || !finite(parsedDef1) || !finite(parsedDef2)
19834 || !finite(parsedCenter) || !finite(parsedDefPoint))
19835 return fail();
19836 setPt6(parsedArcPoint); //code 16
19837 DRW_DBG("arc Point: ")DRW_dbg::dbg("arc Point: "); DRW_DBGPT(parsedArcPoint.x, parsedArcPoint.y,DRW_dbg::dbgPT(parsedArcPoint.x, parsedArcPoint.y, parsedArcPoint
.z)
19838 parsedArcPoint.z)DRW_dbg::dbgPT(parsedArcPoint.x, parsedArcPoint.y, parsedArcPoint
.z)
;
19839 setPt3(parsedDef1); //def1 code 13
19840 DRW_DBG("\ndef1: ")DRW_dbg::dbg("\ndef1: "); DRW_DBGPT(parsedDef1.x, parsedDef1.y, parsedDef1.z)DRW_dbg::dbgPT(parsedDef1.x, parsedDef1.y, parsedDef1.z);
19841 setPt4(parsedDef2); //def2 code 14
19842 DRW_DBG("\ndef2: ")DRW_dbg::dbg("\ndef2: "); DRW_DBGPT(parsedDef2.x, parsedDef2.y, parsedDef2.z)DRW_dbg::dbgPT(parsedDef2.x, parsedDef2.y, parsedDef2.z);
19843 setPt5(parsedCenter); //center pt code 15
19844 DRW_DBG("\ncenter point: ")DRW_dbg::dbg("\ncenter point: "); DRW_DBGPT(parsedCenter.x, parsedCenter.y,DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
19845 parsedCenter.z)DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
;
19846 setDefPoint(parsedDefPoint); //code 10
19847 DRW_DBG("\ndefPoint: ")DRW_dbg::dbg("\ndefPoint: "); DRW_DBGPT(parsedDefPoint.x, parsedDefPoint.y,DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
19848 parsedDefPoint.z)DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
;
19849 type |= 0x02;
19850 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");
19851 if (!buf->isGood())
19852 return fail();
19853
19854 std::uint64_t handleEndBit = 0;
19855 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19856 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19857 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimAngular::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimAngular::parseDwg()\n"
)
;
19858 return fail();
19859 }
19860 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");
19861 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");
19862
19863 // RS crc; //RS */
19864 if (!buf->isGood())
19865 return fail();
19866 *sourceBuf = probe;
19867 return true;
19868}
19869
19870bool DRW_DimAngular3p::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19871 DRW_Dimension::resetDwgState();
19872 dwgBuffer *sourceBuf = buf;
19873 auto fail = [this, sourceBuf]() {
19874 if (sourceBuf != nullptr)
19875 sourceBuf->invalidate();
19876 DRW_Dimension::resetDwgState();
19877 return false;
19878 };
19879 if (sourceBuf == nullptr)
19880 return fail();
19881
19882 dwgBuffer probe = sourceBuf->forkIndependent();
19883 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19884 return fail();
19885 buf = &probe;
19886
19887 DRW_DBG("\n***************************** parsing dim angular3p *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim angular3p *********************************************\n"
)
;
19888 const std::uint64_t bodyEndBit = dwgDataEndBit;
19889 DRW_Coord parsedDefPoint;
19890 DRW_Coord parsedDef1;
19891 DRW_Coord parsedDef2;
19892 DRW_Coord parsedCenter;
19893 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint)
19894 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef1)
19895 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef2)
19896 || !readBoundedBitCoord(*buf, bodyEndBit, parsedCenter))
19897 return fail();
19898 const auto finite = [](const DRW_Coord& point) {
19899 return std::isfinite(point.x) && std::isfinite(point.y)
19900 && std::isfinite(point.z);
19901 };
19902 if (!finite(parsedDefPoint) || !finite(parsedDef1) || !finite(parsedDef2)
19903 || !finite(parsedCenter))
19904 return fail();
19905 setDefPoint(parsedDefPoint); //code 10
19906 DRW_DBG("defPoint: ")DRW_dbg::dbg("defPoint: "); DRW_DBGPT(parsedDefPoint.x, parsedDefPoint.y,DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
19907 parsedDefPoint.z)DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
;
19908 setPt3(parsedDef1); //def1 code 13
19909 DRW_DBG("\ndef1: ")DRW_dbg::dbg("\ndef1: "); DRW_DBGPT(parsedDef1.x, parsedDef1.y, parsedDef1.z)DRW_dbg::dbgPT(parsedDef1.x, parsedDef1.y, parsedDef1.z);
19910 setPt4(parsedDef2); //def2 code 14
19911 DRW_DBG("\ndef2: ")DRW_dbg::dbg("\ndef2: "); DRW_DBGPT(parsedDef2.x, parsedDef2.y, parsedDef2.z)DRW_dbg::dbgPT(parsedDef2.x, parsedDef2.y, parsedDef2.z);
19912 setPt5(parsedCenter); //center pt code 15
19913 DRW_DBG("\ncenter point: ")DRW_dbg::dbg("\ncenter point: "); DRW_DBGPT(parsedCenter.x, parsedCenter.y,DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
19914 parsedCenter.z)DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
;
19915 type |= 0x05;
19916 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");
19917 if (!buf->isGood())
19918 return fail();
19919
19920 std::uint64_t handleEndBit = 0;
19921 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19922 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19923 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimAngular3p::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimAngular3p::parseDwg()\n"
)
;
19924 return fail();
19925 }
19926 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");
19927 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");
19928
19929 // RS crc; //RS */
19930 if (!buf->isGood())
19931 return fail();
19932 *sourceBuf = probe;
19933 return true;
19934}
19935
19936bool DRW_DimOrdinate::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19937 DRW_Dimension::resetDwgState();
19938 dwgBuffer *sourceBuf = buf;
19939 auto fail = [this, sourceBuf]() {
19940 if (sourceBuf != nullptr)
19941 sourceBuf->invalidate();
19942 DRW_Dimension::resetDwgState();
19943 return false;
19944 };
19945 if (sourceBuf == nullptr)
19946 return fail();
19947
19948 dwgBuffer probe = sourceBuf->forkIndependent();
19949 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19950 return fail();
19951 buf = &probe;
19952
19953 DRW_DBG("\n***************************** parsing dim ordinate *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim ordinate *********************************************\n"
)
;
19954 const std::uint64_t bodyEndBit = dwgDataEndBit;
19955 DRW_Coord parsedDefPoint;
19956 DRW_Coord parsedDef1;
19957 DRW_Coord parsedDef2;
19958 std::uint8_t type2 = 0;
19959 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint)
19960 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef1)
19961 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef2)
19962 || !readBoundedRawChar8(*buf, bodyEndBit, type2))
19963 return fail();
19964 const auto finite = [](const DRW_Coord& point) {
19965 return std::isfinite(point.x) && std::isfinite(point.y)
19966 && std::isfinite(point.z);
19967 };
19968 if (!finite(parsedDefPoint) || !finite(parsedDef1) || !finite(parsedDef2))
19969 return fail();
19970 setDefPoint(parsedDefPoint);
19971 DRW_DBG("defPoint: ")DRW_dbg::dbg("defPoint: "); DRW_DBGPT(parsedDefPoint.x, parsedDefPoint.y,DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
19972 parsedDefPoint.z)DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
;
19973 setPt3(parsedDef1); //def1
19974 DRW_DBG("\ndef1: ")DRW_dbg::dbg("\ndef1: "); DRW_DBGPT(parsedDef1.x, parsedDef1.y, parsedDef1.z)DRW_dbg::dbgPT(parsedDef1.x, parsedDef1.y, parsedDef1.z);
19975 setPt4(parsedDef2);
19976 DRW_DBG("\ndef2: ")DRW_dbg::dbg("\ndef2: "); DRW_DBGPT(parsedDef2.x, parsedDef2.y, parsedDef2.z)DRW_dbg::dbgPT(parsedDef2.x, parsedDef2.y, parsedDef2.z);
19977 DRW_DBG("type2 (70) read: ")DRW_dbg::dbg("type2 (70) read: "); DRW_DBG(type2)DRW_dbg::dbg(type2);
19978 // 0B.1: x-vs-y ordinate flag is DXF group-70 bit 6 (0x40), matching the
19979 // filter (rs_filterdxfrw.cpp `type & 64`) and the DWG parseCode path.
19980 // (Previously set bit 7/0x80, which the filter never checks.) The clear
19981 // mask 0xBF already clears 0x40. The DIMENSION base type byte (bit 7) is
19982 // a separate field — see :6141/:6409/:6660, NOT touched here.
19983 int parsedType = type;
19984 parsedType = (type2 & 1) ? parsedType | 0x40 : parsedType & 0xBF;
19985 DRW_DBG(" type (70) set: ")DRW_dbg::dbg(" type (70) set: "); DRW_DBG(parsedType)DRW_dbg::dbg(parsedType);
19986 parsedType |= 6;
19987 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(parsedType)DRW_dbg::dbg(parsedType); DRW_DBG("\n")DRW_dbg::dbg("\n");
19988
19989 std::uint64_t handleEndBit = 0;
19990 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19991 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19992 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimAligned::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimAligned::parseDwg()\n"
)
;
19993 return fail();
19994 }
19995 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");
19996 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");
19997
19998 // RS crc; //RS */
19999 if (!buf->isGood())
20000 return fail();
20001 type = parsedType;
20002 *sourceBuf = probe;
20003 return true;
20004}
20005
20006// ----------------------------------------------------------------------------
20007// DRW_Dimension shared base encoder (R2000 / AC1015)
20008// ----------------------------------------------------------------------------
20009bool DRW_Dimension::encodeDwgDimBase(DRW::Version version, dwgBufferW *buf,
20010 dwgBufferW *strBuf) const {
20011 // ODA §20.4.22: version RC present for R2010+ (mirrors parseDwg read at version > AC1021)
20012 if (version > DRW::AC1021)
20013 buf->putRawChar8(0);
20014 // R2007+: the dim text below is routed to strBuf (the separate string
20015 // stream) via the (strBuf ? strBuf : buf) selector in putVariableText.
20016 buf->put3BitDouble(extPoint); // 3BD per ODA §20.4.22 (NOT BE, NO padding bits)
20017 buf->putRawDouble(textPoint.x);
20018 buf->putRawDouble(textPoint.y);
20019 buf->putBitDouble(textPoint.z);
20020 // Reverse the parseDwg type-byte transformation:
20021 // parseDwg bit0=0 → type bit7 set; bit0=1 → type bit7 clear
20022 // parseDwg bit1=1 → type bit5 set; bit1=0 → type bit5 clear
20023 std::uint8_t rawByte = static_cast<std::uint8_t>(
20024 ((type & 0x80) ? 0 : 1) | ((type & 0x20) ? 2 : 0));
20025 buf->putRawChar8(rawByte);
20026 (strBuf ? strBuf : buf)->putVariableText(version, text);
20027 buf->putBitDouble(rot);
20028 buf->putBitDouble(hdir);
20029 // ins_scale (3BD) of the dimension's anonymous block — not stored by the
20030 // reader, but ODA/libreDWG default it to (1,1,1) (dwg.spec FIELD_3BD_1), not
20031 // (0,0,0). A zero scale is degenerate for ODA consumers. (write-review #46)
20032 const DRW_Coord insScale{1.0, 1.0, 1.0};
20033 buf->put3BitDouble(insScale);
20034 buf->putBitDouble(0.0); // ins_rotation (code 54) — default 0, not stored
20035 // R2000 (version > AC1014): alignment, spacing, line factor, measure
20036 buf->putBitShort(static_cast<std::uint16_t>(align));
20037 buf->putBitShort(static_cast<std::uint16_t>(linesty));
20038 buf->putBitDouble(linefactor);
20039 buf->putBitDouble(measureValue);
20040 if (version > DRW::AC1018) {
20041 buf->putBit(0); // unknown R2007+ bit
20042 buf->putBit(flipArrow1 ? 1 : 0);
20043 buf->putBit(flipArrow2 ? 1 : 0);
20044 }
20045 buf->putRawDouble(clonePoint.x);
20046 buf->putRawDouble(clonePoint.y);
20047 return true;
20048}
20049
20050// Helper: emit dimStyleH (defaults to STANDARD=0x15) and blockH.
20051static void putDimHandles(dwgBufferW *buf, const dwgHandle& dimStyleH, const dwgHandle& blockH,
20052 dwgBufferW *hBuf = nullptr) {
20053 dwgBufferW *hb = hBuf ? hBuf : buf;
20054 dwgHandle dsH;
20055 dsH.code = 5;
20056 dsH.ref = (dimStyleH.ref == 0) ? 0x15 : dimStyleH.ref;
20057 dsH.size = 0;
20058 if (dsH.ref != 0) { std::uint32_t t = dsH.ref; while (t != 0) { t >>= 8; ++dsH.size; } }
20059 hb->putHandle(dsH);
20060
20061 dwgHandle bhH;
20062 bhH.code = (blockH.ref == 0) ? 0 : 5;
20063 bhH.ref = blockH.ref;
20064 bhH.size = 0;
20065 if (bhH.ref != 0) { std::uint32_t t = bhH.ref; while (t != 0) { t >>= 8; ++bhH.size; } }
20066 hb->putHandle(bhH);
20067}
20068
20069// ----------------------------------------------------------------------------
20070// DRW_DimAligned::encodeDwg (oType=22)
20071// ----------------------------------------------------------------------------
20072bool DRW_DimAligned::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
20073 (void)bs;
20074 oType = 22;
20075 if (!encodeDwgCommon(version, buf)) return false;
20076 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
20077 buf->put3BitDouble(getPt3()); // def1
20078 buf->put3BitDouble(getPt4()); // def2
20079 buf->put3BitDouble(getDefPoint()); // defPoint
20080 buf->putBitDouble(getOb52() / ARAD57.29577951308232); // oblique: degrees → radians
20081 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
20082 putDimHandles(buf, dimStyleH, blockH, handleBuf);
20083 return true;
20084}
20085
20086// ----------------------------------------------------------------------------
20087// DRW_DimLinear::encodeDwg (oType=21)
20088// ----------------------------------------------------------------------------
20089bool DRW_DimLinear::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
20090 (void)bs;
20091 oType = 21;
20092 if (!encodeDwgCommon(version, buf)) return false;
20093 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
20094 buf->put3BitDouble(getPt3()); // def1
20095 buf->put3BitDouble(getPt4()); // def2
20096 buf->put3BitDouble(getDefPoint()); // defPoint
20097 buf->putBitDouble(getOb52() / ARAD57.29577951308232); // oblique: degrees → radians
20098 buf->putBitDouble(getAn50() / ARAD57.29577951308232); // rotation angle: degrees → radians
20099 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
20100 putDimHandles(buf, dimStyleH, blockH, handleBuf);
20101 return true;
20102}
20103
20104// ----------------------------------------------------------------------------
20105// DRW_DimRadial::encodeDwg (oType=25)
20106// ----------------------------------------------------------------------------
20107bool DRW_DimRadial::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
20108 (void)bs;
20109 oType = 25;
20110 if (!encodeDwgCommon(version, buf)) return false;
20111 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
20112 buf->put3BitDouble(getDefPoint()); // center point (code 10)
20113 buf->put3BitDouble(getPt5()); // diameter point (code 15)
20114 buf->putBitDouble(getRa40()); // leader length (code 40)
20115 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
20116 putDimHandles(buf, dimStyleH, blockH, handleBuf);
20117 return true;
20118}
20119
20120// ----------------------------------------------------------------------------
20121// DRW_DimLargeRadial::encodeDwg (oType=519, custom AcDbRadialDimensionLarge)
20122// ----------------------------------------------------------------------------
20123bool DRW_DimLargeRadial::encodeDwg(DRW::Version version, dwgBufferW *buf,
20124 std::uint32_t bs, dwgBufferW *strBuf,
20125 dwgBufferW *handleBuf) {
20126 (void)bs;
20127 oType = kDwgClassNum;
20128 if (!encodeDwgCommon(version, buf)) return false;
20129 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
20130 buf->put3BitDouble(getCenterPoint()); // definition point (code 10)
20131 buf->put3BitDouble(jogPoint); // jog vertex (code 15)
20132 buf->putBitDouble(jogAngle); // jog transverse angle (code 40)
20133 buf->put3BitDouble(getChordPoint()); // chord point (code 13)
20134 buf->put3BitDouble(overrideCenterPoint); // overridden center (code 14)
20135 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
20136 putDimHandles(buf, dimStyleH, blockH, handleBuf);
20137 return true;
20138}
20139
20140// ----------------------------------------------------------------------------
20141// DRW_DimDiametric::encodeDwg (oType=26)
20142// ----------------------------------------------------------------------------
20143bool DRW_DimDiametric::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
20144 (void)bs;
20145 oType = 26;
20146 if (!encodeDwgCommon(version, buf)) return false;
20147 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
20148 buf->put3BitDouble(getPt5()); // first diameter point (code 15) — matches parseDwg order
20149 buf->put3BitDouble(getDefPoint()); // opposite point (code 10)
20150 buf->putBitDouble(getRa40()); // leader length (code 40)
20151 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
20152 putDimHandles(buf, dimStyleH, blockH, handleBuf);
20153 return true;
20154}
20155
20156// ----------------------------------------------------------------------------
20157// DRW_DimAngular::encodeDwg (oType=24, 2-line angular)
20158// ----------------------------------------------------------------------------
20159bool DRW_DimAngular::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
20160 (void)bs;
20161 oType = 24;
20162 if (!encodeDwgCommon(version, buf)) return false;
20163 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
20164 // arcPoint is 2RD (not 3BD) in parseDwg — only x and y
20165 buf->putRawDouble(getPt6().x);
20166 buf->putRawDouble(getPt6().y);
20167 buf->put3BitDouble(getPt3()); // def1 (line 1 start)
20168 buf->put3BitDouble(getPt4()); // def2 (line 1 end)
20169 buf->put3BitDouble(getPt5()); // circlePoint (center)
20170 buf->put3BitDouble(getDefPoint()); // defPoint
20171 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
20172 putDimHandles(buf, dimStyleH, blockH, handleBuf);
20173 return true;
20174}
20175
20176// ----------------------------------------------------------------------------
20177// DRW_DimAngular3p::encodeDwg (oType=23, 3-point angular)
20178// ----------------------------------------------------------------------------
20179bool DRW_DimAngular3p::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
20180 (void)bs;
20181 oType = 23;
20182 if (!encodeDwgCommon(version, buf)) return false;
20183 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
20184 buf->put3BitDouble(getDefPoint()); // defPoint (code 10)
20185 buf->put3BitDouble(getPt3()); // def1 (code 13)
20186 buf->put3BitDouble(getPt4()); // def2 (code 14)
20187 buf->put3BitDouble(getPt5()); // circlePoint / vertex (code 15)
20188 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
20189 putDimHandles(buf, dimStyleH, blockH, handleBuf);
20190 return true;
20191}
20192
20193// ----------------------------------------------------------------------------
20194// DRW_DimArc::parseCode (DXF group-code parser)
20195// ----------------------------------------------------------------------------
20196bool DRW_DimArc::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
20197 if (code == 100) {
20198 std::string s = reader->getString();
20199 if (s == "AcDbArcDimension") {
20200 m_arcSubclassSeen = true;
20201 return true;
20202 }
20203 // Fall through for AcDbEntity / AcDbDimension so base classes see them
20204 return DRW_Dimension::parseCode(code, reader);
20205 }
20206 if (m_arcSubclassSeen) {
20207 switch (code) {
20208 case 40: arcStartAngle = reader->getDouble(); return true;
20209 case 41: arcEndAngle = reader->getDouble(); return true;
20210 case 70: arcSymbol = reader->getInt32(); return true;
20211 case 71: isPartial = reader->getInt32() != 0; return true;
20212 }
20213 }
20214 switch (code) {
20215 case 17: leaderPt2.x = reader->getDouble(); return true;
20216 case 27: leaderPt2.y = reader->getDouble(); return true;
20217 case 37: leaderPt2.z = reader->getDouble(); return true;
20218 }
20219 return DRW_Dimension::parseCode(code, reader);
20220}
20221
20222// ----------------------------------------------------------------------------
20223// DRW_DimArc::parseDwg (ODA DWG spec §20.4.19)
20224// ----------------------------------------------------------------------------
20225bool DRW_DimArc::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
20226 DRW_Dimension::resetDwgState();
20227 leaderPt2 = DRW_Coord{0.0, 0.0, 0.0};
20228 arcStartAngle = 0.0;
20229 arcEndAngle = 0.0;
20230 arcSymbol = 0;
20231 isPartial = false;
20232 hasLeader = false;
20233 m_arcSubclassSeen = false;
20234 dwgBuffer *sourceBuf = buf;
20235 auto fail = [this, sourceBuf]() {
20236 if (sourceBuf != nullptr)
20237 sourceBuf->invalidate();
20238 DRW_Dimension::resetDwgState();
20239 leaderPt2 = DRW_Coord{0.0, 0.0, 0.0};
20240 arcStartAngle = 0.0;
20241 arcEndAngle = 0.0;
20242 arcSymbol = 0;
20243 isPartial = false;
20244 hasLeader = false;
20245 m_arcSubclassSeen = false;
20246 return false;
20247 };
20248 if (sourceBuf == nullptr)
20249 return fail();
20250
20251 dwgBuffer probe = sourceBuf->forkIndependent();
20252 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
20253 return fail();
20254 buf = &probe;
20255 const std::uint64_t bodyEndBit = dwgDataEndBit;
20256 DRW_Coord parsedDefPoint;
20257 DRW_Coord parsedDef1;
20258 DRW_Coord parsedDef2;
20259 DRW_Coord parsedCenter;
20260 DRW_Coord parsedLeaderPoint1;
20261 DRW_Coord parsedLeaderPoint2;
20262 bool parsedPartial = false;
20263 double parsedStartAngle = 0.0;
20264 double parsedEndAngle = 0.0;
20265 bool parsedHasLeader = false;
20266 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint)
20267 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef1)
20268 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef2)
20269 || !readBoundedBitCoord(*buf, bodyEndBit, parsedCenter)
20270 || !readBoundedBit(*buf, bodyEndBit, parsedPartial)
20271 || !readBoundedBitDouble(*buf, bodyEndBit, parsedStartAngle)
20272 || !readBoundedBitDouble(*buf, bodyEndBit, parsedEndAngle)
20273 || !readBoundedBit(*buf, bodyEndBit, parsedHasLeader)
20274 // ODA §20.4.19: leader points are unconditional in the stream.
20275 || !readBoundedBitCoord(*buf, bodyEndBit, parsedLeaderPoint1)
20276 || !readBoundedBitCoord(*buf, bodyEndBit, parsedLeaderPoint2))
20277 return fail();
20278 const auto finite = [](const DRW_Coord& point) {
20279 return std::isfinite(point.x) && std::isfinite(point.y)
20280 && std::isfinite(point.z);
20281 };
20282 if (!finite(parsedDefPoint) || !finite(parsedDef1) || !finite(parsedDef2)
20283 || !finite(parsedCenter) || !finite(parsedLeaderPoint1)
20284 || !finite(parsedLeaderPoint2) || !std::isfinite(parsedStartAngle)
20285 || !std::isfinite(parsedEndAngle))
20286 return fail();
20287 setDefPoint(parsedDefPoint);
20288 setPt3(parsedDef1);
20289 setPt4(parsedDef2);
20290 setPt5(parsedCenter);
20291 setPt6(parsedLeaderPoint1);
20292 leaderPt2 = parsedLeaderPoint2;
20293 isPartial = parsedPartial;
20294 arcStartAngle = parsedStartAngle;
20295 arcEndAngle = parsedEndAngle;
20296 hasLeader = parsedHasLeader;
20297 std::uint64_t handleEndBit = 0;
20298 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
20299 || !parseDwgDimensionHandles(version, buf, handleEndBit))
20300 return fail();
20301 *sourceBuf = probe;
20302 return true;
20303}
20304
20305// ----------------------------------------------------------------------------
20306// DRW_DimArc::encodeDwg (oType=500 — dynamic class, classNum from writeDwgClasses)
20307// ----------------------------------------------------------------------------
20308bool DRW_DimArc::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
20309 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
20310 (void)bs;
20311 oType = DRW_DimArc::kDwgClassNum; // assigned in writeDwgClasses; reader resolves via classesmap
20312 if (!encodeDwgCommon(version, buf)) return false;
20313 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
20314 buf->put3BitDouble(getDefPoint()); // arc dim-line arc point (code 10)
20315 buf->put3BitDouble(getPt3()); // extension line 1 (code 13)
20316 buf->put3BitDouble(getPt4()); // extension line 2 (code 14)
20317 buf->put3BitDouble(getPt5()); // arc center (code 15)
20318 buf->putBit(isPartial ? 1 : 0);
20319 buf->putBitDouble(arcStartAngle);
20320 buf->putBitDouble(arcEndAngle);
20321 buf->putBit(hasLeader ? 1 : 0);
20322 // ODA §20.4.19: leader points are UNCONDITIONAL — always written; default to ext-line pts
20323 DRW_Coord lp1 = hasLeader ? getPt6() : getPt3();
20324 DRW_Coord lp2 = hasLeader ? leaderPt2 : getPt4();
20325 buf->put3BitDouble(lp1); // leader point 1 (code 16)
20326 buf->put3BitDouble(lp2); // leader point 2 (code 17)
20327 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
20328 putDimHandles(buf, dimStyleH, blockH, handleBuf);
20329 return true;
20330}
20331
20332// ----------------------------------------------------------------------------
20333// DRW_DimOrdinate::encodeDwg (oType=20)
20334// ----------------------------------------------------------------------------
20335bool DRW_DimOrdinate::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
20336 (void)bs;
20337 oType = 20;
20338 if (!encodeDwgCommon(version, buf)) return false;
20339 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
20340 buf->put3BitDouble(getDefPoint()); // origin/definition point (code 10)
20341 buf->put3BitDouble(getPt3()); // feature location point (code 13)
20342 buf->put3BitDouble(getPt4()); // leader end point (code 14)
20343 // type2 byte encodes the x-vs-y ordinate flag (bit 6 / 0x40 of type, per
20344 // 0B.1) — keeps the DWG byte round-trip self-consistent with the parse
20345 // side while making the filter's `type & 64` check fire.
20346 std::uint8_t type2byte = (type & 0x40) ? 1 : 0;
20347 buf->putRawChar8(type2byte);
20348 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
20349 putDimHandles(buf, dimStyleH, blockH, handleBuf);
20350 return true;
20351}
20352
20353bool DRW_Leader::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
20354 switch (code) {
20355 case 3:
20356 style = reader->getUtf8String();
20357 break;
20358 case 71:
20359 arrow = reader->getInt32();
20360 break;
20361 case 72:
20362 leadertype = reader->getInt32();
20363 break;
20364 case 73:
20365 flag = reader->getInt32();
20366 break;
20367 case 74:
20368 hookline = reader->getInt32();
20369 break;
20370 case 75:
20371 hookflag = reader->getInt32();
20372 break;
20373 case 76:
20374 vertnum = reader->getInt32();
20375 hasVertexCount = true;
20376 break;
20377 case 77:
20378 coloruse = reader->getInt32();
20379 break;
20380 case 40:
20381 textheight = reader->getDouble();
20382 break;
20383 case 41:
20384 textwidth = reader->getDouble();
20385 break;
20386 case 10:
20387 vertexpoint= std::make_shared<DRW_Coord>();
20388 vertexlist.push_back(vertexpoint);
20389 vertexpoint->x = reader->getDouble();
20390 break;
20391 case 20:
20392 if(vertexpoint)
20393 vertexpoint->y = reader->getDouble();
20394 break;
20395 case 30:
20396 if(vertexpoint)
20397 vertexpoint->z = reader->getDouble();
20398 break;
20399 case 340:
20400 annotHandle = reader->getHandleString();
20401 break;
20402 case 210:
20403 extrusionPoint.x = reader->getDouble();
20404 break;
20405 case 220:
20406 extrusionPoint.y = reader->getDouble();
20407 break;
20408 case 230:
20409 extrusionPoint.z = reader->getDouble();
20410 break;
20411 case 211:
20412 horizdir.x = reader->getDouble();
20413 break;
20414 case 221:
20415 horizdir.y = reader->getDouble();
20416 break;
20417 case 231:
20418 horizdir.z = reader->getDouble();
20419 break;
20420 case 212:
20421 offsetblock.x = reader->getDouble();
20422 break;
20423 case 222:
20424 offsetblock.y = reader->getDouble();
20425 break;
20426 case 232:
20427 offsetblock.z = reader->getDouble();
20428 break;
20429 case 213:
20430 offsettext.x = reader->getDouble();
20431 break;
20432 case 223:
20433 offsettext.y = reader->getDouble();
20434 break;
20435 case 233:
20436 offsettext.z = reader->getDouble();
20437 break;
20438 default:
20439 return DRW_Entity::parseCode(code, reader);
20440 }
20441
20442 return true;
20443}
20444
20445bool DRW_Leader::validatePayloadFields() const {
20446 const auto finite = [](const DRW_Coord& point) {
20447 return std::isfinite(point.x) && std::isfinite(point.y)
20448 && std::isfinite(point.z);
20449 };
20450 if ((arrow != 0 && arrow != 1) || (hookline != 0 && hookline != 1)
20451 || (hookflag != 0 && hookflag != 1) || leadertype < 0
20452 || leadertype > 1 || flag < 0 || flag > 3
20453 || coloruse < 0
20454 || coloruse > static_cast<int>(std::numeric_limits<std::uint16_t>::max())
20455 || vertexlist.size() > static_cast<std::size_t>(kMaxLeaderVertices)
20456 || !std::isfinite(textheight) || !std::isfinite(textwidth)
20457 || !finite(origin) || !finite(extrusionPoint) || !finite(horizdir)
20458 || !finite(offsetblock) || !finite(offsettext)) {
20459 return false;
20460 }
20461 return std::all_of(vertexlist.begin(), vertexlist.end(),
20462 [&finite](const std::shared_ptr<DRW_Coord>& point) {
20463 return point != nullptr && finite(*point);
20464 });
20465}
20466
20467bool DRW_Leader::validateDxf() const {
20468 if (!validatePayloadFields())
20469 return false;
20470 if (!hasVertexCount)
20471 return true;
20472 return vertnum >= 0
20473 && vertnum <= kMaxLeaderVertices
20474 && static_cast<std::size_t>(vertnum) == vertexlist.size();
20475}
20476
20477void DRW_Leader::resetDwgState() {
20478 DRW_Entity::reset();
20479 style.clear();
20480 arrow = 1;
20481 leadertype = 0;
20482 flag = 3;
20483 hookline = 1;
20484 hookflag = 1;
20485 textheight = 1.0;
20486 textwidth = 1.0;
20487 vertnum = 0;
20488 coloruse = 7;
20489 annotHandle = 0;
20490 origin = DRW_Coord{0.0, 0.0, 0.0};
20491 extrusionPoint = DRW_Coord{0.0, 0.0, 1.0};
20492 horizdir = DRW_Coord{1.0, 0.0, 0.0};
20493 offsetblock = DRW_Coord{0.0, 0.0, 0.0};
20494 offsettext = DRW_Coord{0.0, 0.0, 0.0};
20495 vertexlist.clear();
20496 vertexpoint.reset();
20497 hasVertexCount = false;
20498 dimStyleH = dwgHandle{};
20499 AnnotH = dwgHandle{};
20500}
20501
20502bool DRW_Leader::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
20503 resetDwgState();
20504 dwgBuffer *sourceBuf = buf;
20505 auto fail = [this, sourceBuf]() {
20506 if (sourceBuf != nullptr)
20507 sourceBuf->invalidate();
20508 resetDwgState();
20509 return false;
20510 };
20511 if (sourceBuf == nullptr)
20512 return fail();
20513
20514 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
20515 buf = &bodyProbe;
20516
20517 dwgBuffer sBuff = buf->forkIndependent();
20518 dwgBuffer *sBuf = buf;
20519 if (version > DRW::AC1018) {//2007+
20520 sBuf = &sBuff; //separate buffer for strings
20521 }
20522 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
20523 if (!ret || !buf->isGood() || !sBuf->isGood())
20524 return fail();
20525 DRW_DBG("\n***************************** parsing leader *********************************************\n")DRW_dbg::dbg("\n***************************** parsing leader *********************************************\n"
)
;
20526 const std::uint64_t bodyEndBit = dwgDataEndBit;
20527 bool ignoredBit = false;
20528 std::uint16_t ignoredShort = 0;
20529 std::uint16_t parsedAnnotationType = 0;
20530 std::uint16_t parsedLeaderType = 0;
20531 if (!readBoundedBit(*buf, bodyEndBit, ignoredBit)
20532 || !readBoundedBitShort(*buf, bodyEndBit, parsedAnnotationType)
20533 || !readBoundedBitShort(*buf, bodyEndBit, parsedLeaderType))
20534 return fail();
20535 if (parsedAnnotationType > 3 || parsedLeaderType > 1)
20536 return fail();
20537 DRW_DBG(" Path type ")DRW_dbg::dbg(" Path type "); DRW_DBG(parsedLeaderType)DRW_dbg::dbg(parsedLeaderType);
20538 std::uint32_t parsedPointCount = 0;
20539 if (!readTableBodyCount(version, buf, objSize, kMaxLeaderVertices, 6,
20540 parsedPointCount))
20541 return fail();
20542 const std::int32_t nPt = static_cast<std::int32_t>(parsedPointCount);
20543 DRW_DBG(" Num pts ")DRW_dbg::dbg(" Num pts "); DRW_DBG(nPt)DRW_dbg::dbg(nPt);
20544 std::uint64_t pointBits = 0;
20545 if (!dwgSafety::multiply(parsedPointCount, 6, pointBits)
20546 || !proxyEntityHasBits(*buf, bodyEndBit, pointBits))
20547 return fail();
20548
20549 // add vertexes
20550 std::vector<std::shared_ptr<DRW_Coord>> parsedVertices;
20551 if (!DRW::reserve(parsedVertices, nPt))
20552 return fail();
20553 for (std::uint32_t i = 0; i < parsedPointCount; i++){
20554 DRW_Coord vertex;
20555 if (!readBoundedBitCoord(*buf, bodyEndBit, vertex))
20556 return fail();
20557 parsedVertices.push_back(std::make_shared<DRW_Coord>(vertex));
20558 DRW_DBG("\nvertex ")DRW_dbg::dbg("\nvertex "); DRW_DBGPT(vertex.x, vertex.y, vertex.z)DRW_dbg::dbgPT(vertex.x, vertex.y, vertex.z);
20559 }
20560 DRW_Coord parsedOrigin;
20561 DRW_Coord parsedExtrusion;
20562 DRW_Coord parsedHorizdir;
20563 DRW_Coord parsedOffsetblock;
20564 DRW_Coord parsedOffsettext{0.0, 0.0, 0.0};
20565 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedOrigin)
20566 || !readBoundedBitCoord(*buf, bodyEndBit, parsedExtrusion)
20567 || !readBoundedBitCoord(*buf, bodyEndBit, parsedHorizdir)
20568 || !readBoundedBitCoord(*buf, bodyEndBit, parsedOffsetblock))
20569 return fail();
20570 DRW_DBG("\norigin ")DRW_dbg::dbg("\norigin "); DRW_DBGPT(parsedOrigin.x, parsedOrigin.y, parsedOrigin.z)DRW_dbg::dbgPT(parsedOrigin.x, parsedOrigin.y, parsedOrigin.z
)
;
20571 // ODA §20.4.47: Extrusion is plain 3DPOINT (3BD), not BE — confirmed by libreDWG dwg.spec:3439
20572 if (leaderHasEndpointProjection(version)) {
20573 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedOffsettext))
20574 return fail();
20575 DRW_DBG("\nEndptproj ")DRW_dbg::dbg("\nEndptproj "); DRW_DBGPT(parsedOffsettext.x, parsedOffsettext.y, parsedOffsettext.z)DRW_dbg::dbgPT(parsedOffsettext.x, parsedOffsettext.y, parsedOffsettext
.z)
;
20576 }
20577 if (version < DRW::AC1015) { //R14 -
20578 double ignoredDouble = 0.0;
20579 if (!readBoundedBitDouble(*buf, bodyEndBit, ignoredDouble))
20580 return fail();
20581 }
20582 double parsedTextheight = 0.0;
20583 double parsedTextwidth = 0.0;
20584 bool parsedHookline = false;
20585 bool parsedArrow = false;
20586 std::uint16_t parsedColoruse = 7;
20587 if ((leaderHasTextBox(version)
20588 && (!readBoundedBitDouble(*buf, bodyEndBit, parsedTextheight)
20589 || !readBoundedBitDouble(*buf, bodyEndBit, parsedTextwidth)))
20590 || !readBoundedBit(*buf, bodyEndBit, parsedHookline)
20591 || !readBoundedBit(*buf, bodyEndBit, parsedArrow))
20592 return fail();
20593 DRW_DBG("\ntextheight ")DRW_dbg::dbg("\ntextheight "); DRW_DBG(parsedTextheight)DRW_dbg::dbg(parsedTextheight); DRW_DBG(" textwidth ")DRW_dbg::dbg(" textwidth "); DRW_DBG(parsedTextwidth)DRW_dbg::dbg(parsedTextwidth);
20594 DRW_DBG(" hookline ")DRW_dbg::dbg(" hookline "); DRW_DBG(parsedHookline)DRW_dbg::dbg(parsedHookline); DRW_DBG(" arrow flag ")DRW_dbg::dbg(" arrow flag "); DRW_DBG(parsedArrow)DRW_dbg::dbg(parsedArrow);
20595
20596 // ODA §20.4.47 places arrowhead type immediately after the hook and
20597 // arrow flags in every DWG version. R2000+ keeps the field even though
20598 // the remaining legacy arrowhead data is absent.
20599 if (!readBoundedBitShort(*buf, bodyEndBit, ignoredShort))
20600 return fail();
20601 if (version < DRW::AC1015) { //R14 -
20602 double ignoredDimSize = 0.0;
20603 if (!readBoundedBitDouble(*buf, bodyEndBit, ignoredDimSize)
20604 || !readBoundedBit(*buf, bodyEndBit, ignoredBit)
20605 || !readBoundedBit(*buf, bodyEndBit, ignoredBit)
20606 || !readBoundedBitShort(*buf, bodyEndBit, ignoredShort)
20607 || !readBoundedBitShort(*buf, bodyEndBit, parsedColoruse)
20608 || !readBoundedBit(*buf, bodyEndBit, ignoredBit)
20609 || !readBoundedBit(*buf, bodyEndBit, ignoredBit))
20610 return fail();
20611 } else { //R2000+
20612 if (!readBoundedBit(*buf, bodyEndBit, ignoredBit)
20613 || !readBoundedBit(*buf, bodyEndBit, ignoredBit))
20614 return fail();
20615 }
20616 DRW_DBG("\n")DRW_dbg::dbg("\n");
20617 std::uint64_t handleEndBit = 0;
20618 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
20619 handleEndBit))
20620 return fail();
20621
20622 dwgBuffer handleProbe = buf->forkIndependent();
20623 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
20624 handleEndBit);
20625 if (!ret || !handleProbe.isGood())
20626 return fail();
20627 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
20628 dwgHandle parsedAnnotH;
20629 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
20630 parsedAnnotH))
20631 return fail();
20632 dwgHandle parsedDimStyleH;
20633 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
20634 parsedDimStyleH))
20635 return fail(); /* H 7 STYLE (hard pointer) */
20636 DRW_DBG("annot block Handle: ")DRW_dbg::dbg("annot block Handle: "); DRW_DBGHL(parsedAnnotH.code, parsedAnnotH.size, parsedAnnotH.ref)DRW_dbg::dbgHL(parsedAnnotH.code, parsedAnnotH.size, parsedAnnotH
.ref)
; DRW_DBG("\n")DRW_dbg::dbg("\n");
20637 DRW_DBG("dim style Handle: ")DRW_dbg::dbg("dim style Handle: "); DRW_DBGHL(parsedDimStyleH.code, parsedDimStyleH.size, parsedDimStyleH.ref)DRW_dbg::dbgHL(parsedDimStyleH.code, parsedDimStyleH.size, parsedDimStyleH
.ref)
; DRW_DBG("\n")DRW_dbg::dbg("\n");
20638 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
20639// RS crc; //RS */
20640 const auto finite = [](const DRW_Coord& point) {
20641 return std::isfinite(point.x) && std::isfinite(point.y)
20642 && std::isfinite(point.z);
20643 };
20644 if (!handleProbe.isGood() || !sBuf->isGood()
20645 || !finite(parsedOrigin) || !finite(parsedExtrusion)
20646 || !finite(parsedHorizdir) || !finite(parsedOffsetblock)
20647 || !finite(parsedOffsettext) || !std::isfinite(parsedTextheight)
20648 || !std::isfinite(parsedTextwidth)
20649 || !std::all_of(parsedVertices.begin(), parsedVertices.end(),
20650 [](const std::shared_ptr<DRW_Coord>& point) {
20651 return point != nullptr
20652 && std::isfinite(point->x)
20653 && std::isfinite(point->y)
20654 && std::isfinite(point->z);
20655 }))
20656 return fail();
20657 *sourceBuf = handleProbe;
20658 flag = static_cast<int>(parsedAnnotationType);
20659 leadertype = parsedLeaderType;
20660 vertnum = nPt;
20661 origin = parsedOrigin;
20662 extrusionPoint = parsedExtrusion;
20663 horizdir = parsedHorizdir;
20664 offsetblock = parsedOffsetblock;
20665 offsettext = parsedOffsettext;
20666 textheight = parsedTextheight;
20667 textwidth = parsedTextwidth;
20668 hookline = parsedHookline;
20669 arrow = parsedArrow;
20670 coloruse = static_cast<int>(parsedColoruse);
20671 AnnotH = parsedAnnotH;
20672 annotHandle = AnnotH.ref;
20673 dimStyleH = parsedDimStyleH;
20674 vertexlist = std::move(parsedVertices);
20675 return true;
20676}
20677
20678// DXF CONTEXT_DATA{} nested-block state machine (§20.4.86). The nested blocks
20679// open with 300 "CONTEXT_DATA{" / 302 "LEADER{" / 304 "LEADER_LINE{" and close
20680// with the distinct codes 301 / 303 / 305, so the open block is tracked with a
20681// single state int (no stack needed). The numeric group codes are overloaded
20682// by block — e.g. 40 is the overall scale in CONTEXT, the landing distance in
20683// LEADER and the arrow size in LEADER_LINE; 10/20/30 are the content base point,
20684// the connection point and a polyline vertex respectively — so they are routed
20685// per state into `context`. Returns true when the code belongs to the context
20686// block (consumed); false at entity level so parseCode handles it.
20687bool DRW_MLeader::parseDxfContextCode(int code, const std::unique_ptr<dxfReader>& reader){
20688 try {
20689 switch (code) { // block open/close markers
20690 case 300:
20691 if (m_dxfCtxState != 0 || reader->getString() != "CONTEXT_DATA{")
20692 return false;
20693 m_dxfCtxState = 1;
20694 return true;
20695 case 301:
20696 if (m_dxfCtxState != 1 || reader->getString() != "}")
20697 return false;
20698 m_dxfCtxState = 0;
20699 return true;
20700 case 302:
20701 if (m_dxfCtxState != 1 || reader->getString() != "LEADER{"
20702 || context.roots.size() >= static_cast<std::size_t>(kMaxMLeaderItems))
20703 return false;
20704 context.roots.emplace_back();
20705 m_dxfCtxState = 2;
20706 return true;
20707 case 303:
20708 if (m_dxfCtxState != 2 || reader->getString() != "}")
20709 return false;
20710 m_dxfCtxState = 1;
20711 return true;
20712 case 304:
20713 if (m_dxfCtxState == 1) {
20714 context.textLabel = reader->getUtf8String();
20715 return true;
20716 }
20717 if (m_dxfCtxState != 2 || reader->getString() != "LEADER_LINE{"
20718 || context.roots.empty()
20719 || context.roots.back().leaderLines.size()
20720 >= static_cast<std::size_t>(kMaxMLeaderItems))
20721 return false;
20722 context.roots.back().leaderLines.emplace_back();
20723 m_dxfCtxState = 3;
20724 return true;
20725 case 305:
20726 if (m_dxfCtxState != 3 || reader->getString() != "}")
20727 return false;
20728 m_dxfCtxState = 2;
20729 return true;
20730 default: break;
20731 }
20732
20733 if (m_dxfCtxState == 0)
20734 return false; // entity level — parseCode handles it
20735
20736 if (m_dxfCtxState == 3) { // LEADER_LINE{}: a polyline + overrides
20737 DRW_MLeaderRoot* root = context.roots.empty() ? nullptr : &context.roots.back();
20738 DRW_MLeaderLeaderLine* line =
20739 (root && !root->leaderLines.empty()) ? &root->leaderLines.back() : nullptr;
20740 if (line) switch (code) {
20741 case 10:
20742 if (line->points.size() >= static_cast<std::size_t>(kMaxLeaderVertices))
20743 return false;
20744 line->points.emplace_back(reader->getDouble(), 0.0, 0.0);
20745 return true;
20746 case 20: if (!line->points.empty()) line->points.back().y = reader->getDouble(); return true;
20747 case 30: if (!line->points.empty()) line->points.back().z = reader->getDouble(); return true;
20748 case 40: line->arrowSize = reader->getDouble(); return true;
20749 case 90: line->segmentIndex = reader->getInt32(); return true;
20750 case 91: line->leaderLineIndex = reader->getInt32(); return true;
20751 case 92: line->color = reader->getInt32(); return true;
20752 case 93: line->overrideFlags = reader->getInt32(); return true;
20753 case 170: line->leaderType = reader->getInt32(); return true;
20754 case 171: line->lineWeight = reader->getInt32(); return true;
20755 default: break;
20756 }
20757 return true; // swallow other line codes
20758 }
20759
20760 if (m_dxfCtxState == 2) { // LEADER{}: one root attachment
20761 DRW_MLeaderRoot* root = context.roots.empty() ? nullptr : &context.roots.back();
20762 if (root) switch (code) {
20763 case 290: root->isContentValid = (reader->getInt32() != 0); return true;
20764 case 291: root->unknown291 = (reader->getInt32() != 0); return true;
20765 case 10: root->connectionPoint.x = reader->getDouble(); return true;
20766 case 20: root->connectionPoint.y = reader->getDouble(); return true;
20767 case 30: root->connectionPoint.z = reader->getDouble(); return true;
20768 case 11: root->direction.x = reader->getDouble(); return true;
20769 case 21: root->direction.y = reader->getDouble(); return true;
20770 case 31: root->direction.z = reader->getDouble(); return true;
20771 case 90: root->leaderIndex = reader->getInt32(); return true;
20772 case 40: root->landingDistance = reader->getDouble(); return true;
20773 case 271: root->attachmentDirection = reader->getInt32(); return true;
20774 default: break;
20775 }
20776 return true; // swallow other leader codes
20777 }
20778
20779 switch (code) { // m_dxfCtxState == 1: CONTEXT_DATA{}
20780 case 40: context.overallScale = reader->getDouble(); return true;
20781 case 10: context.contentBasePoint.x = reader->getDouble(); return true;
20782 case 20: context.contentBasePoint.y = reader->getDouble(); return true;
20783 case 30: context.contentBasePoint.z = reader->getDouble(); return true;
20784 case 41: context.textHeight = reader->getDouble(); return true;
20785 case 140: context.arrowHeadSize = reader->getDouble(); return true;
20786 case 145: context.landingGap = reader->getDouble(); return true;
20787 case 174: context.styleLeftAttach = reader->getInt32(); return true;
20788 case 175: context.styleRightAttach = reader->getInt32(); return true;
20789 case 176: context.textAlignType = reader->getInt32(); return true;
20790 case 177: context.attachmentType = reader->getInt32(); return true;
20791 case 290: context.hasTextContents = (reader->getInt32() != 0); return true;
20792 /* text-content branch */
20793 case 11: context.textNormal.x = reader->getDouble(); return true;
20794 case 21: context.textNormal.y = reader->getDouble(); return true;
20795 case 31: context.textNormal.z = reader->getDouble(); return true;
20796 case 12: context.textLocation.x = reader->getDouble(); return true;
20797 case 22: context.textLocation.y = reader->getDouble(); return true;
20798 case 32: context.textLocation.z = reader->getDouble(); return true;
20799 case 13: context.textDirection.x = reader->getDouble(); return true;
20800 case 23: context.textDirection.y = reader->getDouble(); return true;
20801 case 33: context.textDirection.z = reader->getDouble(); return true;
20802 case 42: context.textRotation = reader->getDouble(); return true;
20803 case 43: context.boundaryWidth = reader->getDouble(); return true;
20804 case 44: context.boundaryHeight = reader->getDouble(); return true;
20805 case 45: context.lineSpacingFactor = reader->getDouble(); return true;
20806 case 170: context.lineSpacingStyle = reader->getInt32(); return true;
20807 case 90: context.textColor = reader->getInt32(); return true;
20808 case 171: context.alignment = reader->getInt32(); return true;
20809 case 172: context.flowDirection = reader->getInt32(); return true;
20810 case 91: context.bgFillColor = reader->getInt32(); return true;
20811 case 141: context.bgScaleFactor = reader->getDouble(); return true;
20812 case 92: context.bgTransparency = reader->getInt32(); return true;
20813 case 291: context.bgFillEnabled = (reader->getInt32() != 0); return true;
20814 case 292: context.bgMaskFillOn = (reader->getInt32() != 0); return true;
20815 case 173: context.columnType = reader->getInt32(); return true;
20816 case 293: context.textHeightAuto = (reader->getInt32() != 0); return true;
20817 case 142: context.columnWidth = reader->getDouble(); return true;
20818 case 143: context.columnGutter = reader->getDouble(); return true;
20819 case 294: context.columnFlowReversed = (reader->getInt32() != 0); return true;
20820 case 144:
20821 if (context.columnSizes.size() >= static_cast<std::size_t>(kMaxMLeaderItems))
20822 return false;
20823 context.columnSizes.push_back(reader->getDouble());
20824 return true;
20825 case 295: context.wordBreak = (reader->getInt32() != 0); return true;
20826 /* block-content branch */
20827 case 296: context.hasContentsBlock = (reader->getInt32() != 0); return true;
20828 case 14: context.blockNormal.x = reader->getDouble(); return true;
20829 case 24: context.blockNormal.y = reader->getDouble(); return true;
20830 case 34: context.blockNormal.z = reader->getDouble(); return true;
20831 case 15: context.blockLocation.x = reader->getDouble(); return true;
20832 case 25: context.blockLocation.y = reader->getDouble(); return true;
20833 case 35: context.blockLocation.z = reader->getDouble(); return true;
20834 case 16: context.blockScale.x = reader->getDouble(); return true;
20835 case 26: context.blockScale.y = reader->getDouble(); return true;
20836 case 36: context.blockScale.z = reader->getDouble(); return true;
20837 case 46: context.blockRotation = reader->getDouble(); return true;
20838 case 93: context.blockColor = reader->getInt32(); return true;
20839 /* common tail */
20840 case 110: case 120: case 130:
20841 case 111: case 121: case 131:
20842 case 112: case 122: case 132:
20843 // The helper answers true for every code in this set, so this
20844 // matches the nine `return true` arms it replaces.
20845 return readCoordTripletCode(code, reader, context.basePoint,
20846 context.baseDirection,
20847 context.baseVertical);
20848 case 297: context.isNormalReversed = (reader->getInt32() != 0); return true;
20849 case 272: context.styleBottomAttach = reader->getInt32(); return true;
20850 case 273: context.styleTopAttach = reader->getInt32(); return true;
20851 default: return true; // swallow any other context code
20852 }
20853 } catch (...) {
20854 return false;
20855 }
20856}
20857
20858bool DRW_MLeader::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
20859 // The embedded CONTEXT_DATA{} block (§20.4.86) is routed by the nested-block
20860 // state machine; the remaining (entity-level) fields are read below and
20861 // mirror the DWG body parser.
20862 if (parseDxfContextCode(code, reader))
20863 return true;
20864 // A failed nested parse must not fall through to the permissive base
20865 // parser: that would silently discard malformed controls or over-limit
20866 // collections and still publish the entity.
20867 if (m_dxfCtxState != 0 || (code >= 300 && code <= 305))
20868 return false;
20869 switch (code) {
20870 case 170: leaderType = reader->getInt32(); break;
20871 case 171: leaderLineWeight = reader->getInt32(); break;
20872 case 172: styleContentType = reader->getInt32(); break;
20873 case 173: styleLeftAttach = reader->getInt32(); break;
20874 case 95: styleRightAttach = reader->getInt32(); break;
20875 case 174: styleTextAngleType = reader->getInt32(); break;
20876 case 175: unknown175 = reader->getInt32(); break;
20877 case 176: styleAttachmentType = reader->getInt32(); break;
20878 case 178: ipeAlign = reader->getInt32(); break;
20879 case 179: justification = reader->getInt32(); break;
20880 case 271: attachmentDirection = reader->getInt32(); break;
20881 case 272: styleBottomAttach = reader->getInt32(); break;
20882 case 273: styleTopAttach = reader->getInt32(); break;
20883 case 90: overrideFlags = reader->getInt32(); break;
20884 case 91: leaderColor = reader->getInt32(); break;
20885 case 92: styleTextColor = reader->getInt32(); break;
20886 case 93: styleBlockColor = reader->getInt32(); break;
20887 case 41: landingDistance = reader->getDouble(); break;
20888 case 42: defaultArrowHeadSize = reader->getDouble(); break;
20889 case 43: styleBlockRotation = reader->getDouble(); break;
20890 case 45: scaleFactor = reader->getDouble(); break;
20891 case 290: landingEnabled = (reader->getInt32() != 0); break;
20892 case 291: doglegEnabled = (reader->getInt32() != 0); break;
20893 case 292: styleTextFrameEnabled = (reader->getInt32() != 0); break;
20894 case 293: isAnnotative = (reader->getInt32() != 0); break;
20895 case 294: isTextDirectionNegative = (reader->getInt32() != 0); break;
20896 case 295: leaderExtendedToText = (reader->getInt32() != 0); break;
20897 default:
20898 return DRW_Entity::parseCode(code, reader);
20899 }
20900 return true;
20901}
20902
20903// Helper: parse one AcDbMLeaderObjectContextData::LeaderRoot entry (§20.4.86).
20904//
20905// Each root has: connection point + direction, optional break pairs, leader
20906// index, landing distance, then a count-and-list of leader lines. Lines
20907// themselves carry: point list, break-info pairs, and (R2010+) per-line
20908// style overrides. The handles inside (line-type / arrow per leader line)
20909// are deferred to the entity-level handle stream and not stored here.
20910static bool parseMLeaderRoot(DRW::Version version, dwgBuffer *buf,
20911 std::uint32_t objectSize,
20912 std::uint64_t bodyEndBit,
20913 DRW_MLeaderRoot& root) {
20914 // Layout per libreDWG dwg2.spec:1316-1366 (Dwg_LEADER_Node + Dwg_LEADER_Line).
20915 // The two 3BD coords at the head of the node are conditional on the
20916 // preceding B flags; reading them unconditionally drifts the bit stream
20917 // when either flag is 0.
20918 bool hasLastPt = false;
20919 bool hasDogleg = false;
20920 if (!readBoundedBit(*buf, bodyEndBit, hasLastPt)
20921 || !readBoundedBit(*buf, bodyEndBit, hasDogleg))
20922 return false; // 290 has_lastleaderlinepoint, 291 has_dogleg
20923 root.isContentValid = hasLastPt;
20924 root.unknown291 = hasDogleg;
20925 if (hasLastPt) {
20926 if (!readBoundedBitCoord(*buf, bodyEndBit, root.connectionPoint))
20927 return false;
20928 }
20929 if (hasDogleg) {
20930 if (!readBoundedBitCoord(*buf, bodyEndBit, root.direction))
20931 return false;
20932 }
20933
20934 std::uint32_t nBreaks = 0;
20935 if (!readTableBodyCount(version, buf, objectSize, kMaxMLeaderItems, 12,
20936 nBreaks))
20937 return false;
20938 if (!DRW::reserve(root.breaks, static_cast<int>(nBreaks)))
20939 return false;
20940 for (std::uint32_t i = 0; i < nBreaks; ++i) {
20941 DRW_Coord a;
20942 DRW_Coord b;
20943 if (!readBoundedBitCoord(*buf, bodyEndBit, a)
20944 || !readBoundedBitCoord(*buf, bodyEndBit, b))
20945 return false;
20946 root.breaks.emplace_back(a, b);
20947 }
20948
20949 if (!readBoundedBitLong(*buf, bodyEndBit, root.leaderIndex)
20950 || !readBoundedBitDouble(*buf, bodyEndBit, root.landingDistance))
20951 return false;
20952
20953 std::uint32_t nLines = 0;
20954 if (!readTableBodyCount(version, buf, objectSize, kMaxMLeaderItems, 6,
20955 nLines))
20956 return false;
20957 if (!DRW::reserve(root.leaderLines, static_cast<int>(nLines)))
20958 return false;
20959 for (std::uint32_t i = 0; i < nLines; ++i) {
20960 DRW_MLeaderLeaderLine line;
20961 // Per libreDWG: BL num_points, points, BL num_breaks, breaks, BL line_index.
20962 // The previous 5-BL layout (brkInfoCount + segmentIndex + nPairs +
20963 // pairs + leaderLineIndex) inserted two spurious BL reads, drifting
20964 // every subsequent entity-level field (overallScale, contentType, …).
20965 std::uint32_t nPts = 0;
20966 if (!readTableBodyCount(version, buf, objectSize, kMaxMLeaderItems, 6,
20967 nPts))
20968 return false;
20969 if (!DRW::reserve(line.points, static_cast<int>(nPts)))
20970 return false;
20971 for (std::uint32_t j = 0; j < nPts; ++j) {
20972 DRW_Coord point;
20973 if (!readBoundedBitCoord(*buf, bodyEndBit, point))
20974 return false;
20975 line.points.push_back(point);
20976 }
20977 std::uint32_t nLineBreaks = 0;
20978 if (!readTableBodyCount(version, buf, objectSize, kMaxMLeaderItems,
20979 12, nLineBreaks))
20980 return false;
20981 for (std::uint32_t j = 0; j < nLineBreaks; ++j) {
20982 DRW_Coord a;
20983 DRW_Coord b;
20984 if (!readBoundedBitCoord(*buf, bodyEndBit, a)
20985 || !readBoundedBitCoord(*buf, bodyEndBit, b))
20986 return false;
20987 line.breaks.emplace_back(a, b);
20988 }
20989 if (!readBoundedBitLong(*buf, bodyEndBit, line.leaderLineIndex))
20990 return false;
20991
20992 // R2010+ per-line override block. The spec marks this block "R2010"
20993 // (§20.4.86 page 215); the override flags BL 93 says which fields
20994 // were overridden. The handle fields (340 line-type, 341 arrow) are
20995 // deferred to the trailing handle stream.
20996 if (version >= DRW::AC1024) {
20997 if (!readBoundedBitShort(*buf, bodyEndBit, line.leaderType))
20998 return false;
20999 std::uint32_t parsedColor = 0;
21000 if (!readBoundedCmColor(*buf, nullptr, bodyEndBit, version,
21001 parsedColor))
21002 return false;
21003 line.color = static_cast<int>(parsedColor);
21004 // line type handle 340 — read from handles section later
21005 if (!readBoundedBitLong(*buf, bodyEndBit, line.lineWeight)
21006 || !readBoundedBitDouble(*buf, bodyEndBit, line.arrowSize)
21007 || !readBoundedBitLong(*buf, bodyEndBit,
21008 line.overrideFlags))
21009 return false;
21010 // arrow handle 341 — handles section
21011 }
21012 root.leaderLines.push_back(std::move(line));
21013 }
21014
21015 if (version >= DRW::AC1024) {
21016 if (!readBoundedBitShort(*buf, bodyEndBit, root.attachmentDirection))
21017 return false;
21018 }
21019
21020 return buf->isGood();
21021}
21022
21023// Helper: parse the AcDbMLeaderObjectContextData (§20.4.86) payload, the
21024// large embedded block at the start of the MLEADER body that carries the
21025// leader geometry plus either text or block content.
21026static bool parseMLeaderAnnotContext(DRW::Version version, dwgBuffer *buf,
21027 dwgBuffer *sBuf,
21028 std::uint32_t objectSize,
21029 std::uint64_t bodyEndBit,
21030 std::uint64_t stringEndBit,
21031 DRW_MLeaderAnnotContext& ctx) {
21032 // NOTE: when AcDbMLeaderObjectContextData is embedded INSIDE the MLEADER
21033 // entity body (rather than serialized as a standalone object), the
21034 // AcDbObjectContextData base preamble (BS version, B has-file-ext-dict,
21035 // B default-flag) does NOT appear in the bit stream — those fields are
21036 // standalone-object metadata. The embedded AnnotContext starts directly
21037 // with the leader-roots count. Its standalone-object scale handle is
21038 // likewise absent from this embedded payload.
21039
21040 // Number of leader roots.
21041 std::int32_t nRoots = 0;
21042 if (!readBoundedBitLong(*buf, bodyEndBit, nRoots))
21043 return false;
21044 if (nRoots == 0) {
21045 if (!proxyEntityHasBits(*buf, bodyEndBit, 7))
21046 return false;
21047 bool rootCountBits[7] = {};
21048 for (bool& rootCountBit : rootCountBits) {
21049 if (!readBoundedBit(*buf, bodyEndBit, rootCountBit))
21050 return false;
21051 }
21052 nRoots = rootCountBits[5] ? 2 : 1;
21053 }
21054 if (!isValidCount(nRoots, kMaxMLeaderItems)) return false;
21055 std::uint64_t minimumRootBits = 0;
21056 if (!dwgSafety::multiply(static_cast<std::uint64_t>(nRoots), 10,
21057 minimumRootBits)
21058 || !proxyEntityHasBits(*buf, bodyEndBit, minimumRootBits)) {
21059 return false;
21060 }
21061 ctx.roots.clear();
21062 if (!DRW::reserve(ctx.roots, nRoots))
21063 return false;
21064 for (std::int32_t i = 0; i < nRoots; ++i) {
21065 DRW_MLeaderRoot root;
21066 if (!parseMLeaderRoot(version, buf, objectSize, bodyEndBit, root))
21067 return false;
21068 ctx.roots.push_back(std::move(root));
21069 }
21070
21071 // Common content fields.
21072 if (!readBoundedBitDouble(*buf, bodyEndBit, ctx.overallScale)
21073 || !readBoundedBitCoord(*buf, bodyEndBit, ctx.contentBasePoint)
21074 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.textHeight)
21075 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.arrowHeadSize)
21076 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.landingGap)
21077 || !readBoundedBitShort(*buf, bodyEndBit, ctx.styleLeftAttach)
21078 || !readBoundedBitShort(*buf, bodyEndBit, ctx.styleRightAttach)
21079 || !readBoundedBitShort(*buf, bodyEndBit, ctx.textAlignType)
21080 || !readBoundedBitShort(*buf, bodyEndBit, ctx.attachmentType)
21081 || !readBoundedBit(*buf, bodyEndBit, ctx.hasTextContents))
21082 return false;
21083
21084 if (ctx.hasTextContents) {
21085 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
21086 ctx.textLabel))
21087 return false;
21088 if (!readBoundedBitCoord(*buf, bodyEndBit, ctx.textNormal))
21089 return false;
21090 // text style handle 340 — handles section
21091 if (!readBoundedBitCoord(*buf, bodyEndBit, ctx.textLocation)
21092 || !readBoundedBitCoord(*buf, bodyEndBit, ctx.textDirection)
21093 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.textRotation)
21094 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.boundaryWidth)
21095 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.boundaryHeight)
21096 || !readBoundedBitDouble(*buf, bodyEndBit,
21097 ctx.lineSpacingFactor)
21098 || !readBoundedBitShort(*buf, bodyEndBit, ctx.lineSpacingStyle))
21099 return false;
21100 std::uint32_t parsedTextColor = 0;
21101 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version,
21102 parsedTextColor, nullptr, nullptr, nullptr,
21103 nullptr, stringEndBit))
21104 return false;
21105 ctx.textColor = static_cast<int>(parsedTextColor);
21106 if (!readBoundedBitShort(*buf, bodyEndBit, ctx.alignment)
21107 || !readBoundedBitShort(*buf, bodyEndBit, ctx.flowDirection))
21108 return false;
21109 std::uint32_t parsedBgFillColor = 0;
21110 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version,
21111 parsedBgFillColor, nullptr, nullptr, nullptr,
21112 nullptr, stringEndBit))
21113 return false;
21114 ctx.bgFillColor = static_cast<int>(parsedBgFillColor);
21115 if (!readBoundedBitDouble(*buf, bodyEndBit, ctx.bgScaleFactor)
21116 || !readBoundedBitLong(*buf, bodyEndBit, ctx.bgTransparency)
21117 || !readBoundedBit(*buf, bodyEndBit, ctx.bgFillEnabled)
21118 || !readBoundedBit(*buf, bodyEndBit, ctx.bgMaskFillOn)
21119 || !readBoundedBitShort(*buf, bodyEndBit, ctx.columnType)
21120 || !readBoundedBit(*buf, bodyEndBit, ctx.textHeightAuto)
21121 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.columnWidth)
21122 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.columnGutter)
21123 || !readBoundedBit(*buf, bodyEndBit, ctx.columnFlowReversed))
21124 return false;
21125 std::uint32_t nColSizes = 0;
21126 if (!readTableBodyCount(version, buf, objectSize, kMaxMLeaderItems, 2,
21127 nColSizes))
21128 return false;
21129 if (!DRW::reserve(ctx.columnSizes, static_cast<int>(nColSizes)))
21130 return false;
21131 for (std::uint32_t i = 0; i < nColSizes; ++i) {
21132 double columnSize = 0.0;
21133 if (!readBoundedBitDouble(*buf, bodyEndBit, columnSize))
21134 return false;
21135 ctx.columnSizes.push_back(columnSize);
21136 }
21137 bool ignoredBit = false;
21138 if (!readBoundedBit(*buf, bodyEndBit, ctx.wordBreak)
21139 || !readBoundedBit(*buf, bodyEndBit, ignoredBit))
21140 return false; // unknown trailing bit
21141 } else {
21142 if (!readBoundedBit(*buf, bodyEndBit, ctx.hasContentsBlock))
21143 return false;
21144 if (ctx.hasContentsBlock) {
21145 // BlockTableRecord handle 341 — deferred
21146 if (!readBoundedBitCoord(*buf, bodyEndBit, ctx.blockNormal)
21147 || !readBoundedBitCoord(*buf, bodyEndBit, ctx.blockLocation)
21148 || !readBoundedBitCoord(*buf, bodyEndBit, ctx.blockScale)
21149 || !readBoundedBitDouble(*buf, bodyEndBit,
21150 ctx.blockRotation))
21151 return false;
21152 std::uint32_t parsedBlockColor = 0;
21153 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version,
21154 parsedBlockColor, nullptr, nullptr, nullptr,
21155 nullptr, stringEndBit))
21156 return false;
21157 ctx.blockColor = static_cast<int>(parsedBlockColor);
21158 for (size_t i = 0; i < 16; ++i) {
21159 if (!readBoundedBitDouble(*buf, bodyEndBit,
21160 ctx.blockTransform[i]))
21161 return false;
21162 }
21163 }
21164 }
21165
21166 // Common tail.
21167 if (!readBoundedBitCoord(*buf, bodyEndBit, ctx.basePoint)
21168 || !readBoundedBitCoord(*buf, bodyEndBit, ctx.baseDirection)
21169 || !readBoundedBitCoord(*buf, bodyEndBit, ctx.baseVertical)
21170 || !readBoundedBit(*buf, bodyEndBit, ctx.isNormalReversed))
21171 return false;
21172
21173 if (version >= DRW::AC1024) {
21174 if (!readBoundedBitShort(*buf, bodyEndBit, ctx.styleTopAttach)
21175 || !readBoundedBitShort(*buf, bodyEndBit, ctx.styleBottomAttach))
21176 return false;
21177 }
21178
21179 return buf->isGood() && sBuf->isGood();
21180}
21181
21182static bool encodeMLeaderRoot(DRW::Version version, dwgBufferW *buf,
21183 const DRW_MLeaderRoot& root) {
21184 if (root.breaks.size() > DRW_MLeader::kMaxLeaderPoints
21185 || root.leaderLines.size() > DRW_MLeader::kMaxLeaderLines)
21186 return false;
21187
21188 buf->putBit(root.isContentValid ? 1 : 0);
21189 buf->putBit(root.unknown291 ? 1 : 0);
21190 if (root.isContentValid)
21191 buf->put3BitDouble(root.connectionPoint);
21192 if (root.unknown291)
21193 buf->put3BitDouble(root.direction);
21194
21195 buf->putBitLong(static_cast<std::int32_t>(root.breaks.size()));
21196 for (const auto& brk : root.breaks) {
21197 buf->put3BitDouble(brk.first);
21198 buf->put3BitDouble(brk.second);
21199 }
21200
21201 buf->putBitLong(root.leaderIndex);
21202 buf->putBitDouble(root.landingDistance);
21203
21204 buf->putBitLong(static_cast<std::int32_t>(root.leaderLines.size()));
21205 for (const DRW_MLeaderLeaderLine& line : root.leaderLines) {
21206 if (line.points.size() > DRW_MLeader::kMaxLeaderPoints
21207 || line.breaks.size() > DRW_MLeader::kMaxLeaderPoints)
21208 return false;
21209 buf->putBitLong(static_cast<std::int32_t>(line.points.size()));
21210 for (const DRW_Coord& point : line.points)
21211 buf->put3BitDouble(point);
21212
21213 buf->putBitLong(static_cast<std::int32_t>(line.breaks.size()));
21214 for (const auto& brk : line.breaks) {
21215 buf->put3BitDouble(brk.first);
21216 buf->put3BitDouble(brk.second);
21217 }
21218 buf->putBitLong(line.leaderLineIndex);
21219
21220 if (version >= DRW::AC1024) {
21221 buf->putBitShort(line.leaderType);
21222 buf->putCmColor(version, static_cast<std::uint16_t>(line.color));
21223 buf->putBitLong(line.lineWeight);
21224 buf->putBitDouble(line.arrowSize);
21225 buf->putBitLong(line.overrideFlags);
21226 }
21227 }
21228
21229 if (version >= DRW::AC1024)
21230 buf->putBitShort(root.attachmentDirection);
21231
21232 return true;
21233}
21234
21235static bool encodeMLeaderAnnotContext(DRW::Version version, dwgBufferW *buf,
21236 dwgBufferW *strBuf,
21237 const DRW_MLeaderAnnotContext& ctx) {
21238 if (ctx.roots.size() > DRW_MLeader::kMaxRoots
21239 || ctx.columnSizes.size() > DRW_MLeader::kMaxLeaderPoints)
21240 return false;
21241 if (ctx.hasContentsBlock)
21242 return false;
21243
21244 buf->putBitLong(static_cast<std::int32_t>(ctx.roots.size()));
21245 for (const DRW_MLeaderRoot& root : ctx.roots) {
21246 if (!encodeMLeaderRoot(version, buf, root))
21247 return false;
21248 }
21249
21250 buf->putBitDouble(ctx.overallScale);
21251 buf->put3BitDouble(ctx.contentBasePoint);
21252 buf->putBitDouble(ctx.textHeight);
21253 buf->putBitDouble(ctx.arrowHeadSize);
21254 buf->putBitDouble(ctx.landingGap);
21255 buf->putBitShort(ctx.styleLeftAttach);
21256 buf->putBitShort(ctx.styleRightAttach);
21257 buf->putBitShort(ctx.textAlignType);
21258 buf->putBitShort(ctx.attachmentType);
21259 buf->putBit(ctx.hasTextContents ? 1 : 0);
21260
21261 if (ctx.hasTextContents) {
21262 (strBuf ? strBuf : buf)->putVariableText(version, ctx.textLabel);
21263 buf->put3BitDouble(ctx.textNormal);
21264 buf->put3BitDouble(ctx.textLocation);
21265 buf->put3BitDouble(ctx.textDirection);
21266 buf->putBitDouble(ctx.textRotation);
21267 buf->putBitDouble(ctx.boundaryWidth);
21268 buf->putBitDouble(ctx.boundaryHeight);
21269 buf->putBitDouble(ctx.lineSpacingFactor);
21270 buf->putBitShort(ctx.lineSpacingStyle);
21271 buf->putCmColor(version, static_cast<std::uint16_t>(ctx.textColor));
21272 buf->putBitShort(ctx.alignment);
21273 buf->putBitShort(ctx.flowDirection);
21274 buf->putCmColor(version, static_cast<std::uint16_t>(ctx.bgFillColor));
21275 buf->putBitDouble(ctx.bgScaleFactor);
21276 buf->putBitLong(ctx.bgTransparency);
21277 buf->putBit(ctx.bgFillEnabled ? 1 : 0);
21278 buf->putBit(ctx.bgMaskFillOn ? 1 : 0);
21279 buf->putBitShort(ctx.columnType);
21280 buf->putBit(ctx.textHeightAuto ? 1 : 0);
21281 buf->putBitDouble(ctx.columnWidth);
21282 buf->putBitDouble(ctx.columnGutter);
21283 buf->putBit(ctx.columnFlowReversed ? 1 : 0);
21284 buf->putBitLong(static_cast<std::int32_t>(ctx.columnSizes.size()));
21285 for (double columnSize : ctx.columnSizes)
21286 buf->putBitDouble(columnSize);
21287 buf->putBit(ctx.wordBreak ? 1 : 0);
21288 buf->putBit(0);
21289 } else {
21290 buf->putBit(0); // hasContentsBlock
21291 }
21292
21293 buf->put3BitDouble(ctx.basePoint);
21294 buf->put3BitDouble(ctx.baseDirection);
21295 buf->put3BitDouble(ctx.baseVertical);
21296 buf->putBit(ctx.isNormalReversed ? 1 : 0);
21297
21298 if (version >= DRW::AC1024) {
21299 buf->putBitShort(ctx.styleTopAttach);
21300 buf->putBitShort(ctx.styleBottomAttach);
21301 }
21302
21303 return true;
21304}
21305
21306void DRW_MLeader::resetDwgState() {
21307 DRW_Entity::reset();
21308 context = DRW_MLeaderAnnotContext{};
21309 classVersion = 2;
21310 styleHandle = dwgHandle{};
21311 overrideFlags = 0;
21312 leaderType = 1;
21313 leaderColor = 0;
21314 leaderLineTypeHandle = dwgHandle{};
21315 leaderLineWeight = 0;
21316 landingEnabled = true;
21317 doglegEnabled = true;
21318 landingDistance = 0.0;
21319 arrowHeadHandle = dwgHandle{};
21320 defaultArrowHeadSize = 0.0;
21321 styleContentType = 2;
21322 styleTextStyleHandle = dwgHandle{};
21323 styleLeftAttach = 0;
21324 styleRightAttach = 0;
21325 styleTextAngleType = 0;
21326 unknown175 = 0;
21327 styleTextColor = 0;
21328 styleTextFrameEnabled = false;
21329 styleBlockHandle = dwgHandle{};
21330 styleBlockColor = 0;
21331 styleBlockScale = DRW_Coord{1.0, 1.0, 1.0};
21332 styleBlockRotation = 0.0;
21333 styleAttachmentType = 0;
21334 isAnnotative = false;
21335 arrowHeads.clear();
21336 blockLabels.clear();
21337 isTextDirectionNegative = false;
21338 ipeAlign = 0;
21339 justification = 0;
21340 scaleFactor = 1.0;
21341 attachmentDirection = 0;
21342 styleTopAttach = 0;
21343 styleBottomAttach = 0;
21344 leaderExtendedToText = false;
21345 m_dxfCtxState = 0;
21346}
21347
21348bool DRW_MLeader::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
21349 resetDwgState();
21350 dwgBuffer *sourceBuf = buf;
21351 auto fail = [this, sourceBuf]() {
21352 if (sourceBuf != nullptr)
21353 sourceBuf->invalidate();
21354 resetDwgState();
21355 return false;
21356 };
21357 if (sourceBuf == nullptr)
21358 return fail();
21359
21360 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
21361 buf = &bodyProbe;
21362
21363 dwgBuffer sBuff = buf->forkIndependent();
21364 dwgBuffer *sBuf = buf;
21365 if (version > DRW::AC1018) { // 2007+
21366 sBuf = &sBuff;
21367 }
21368 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
21369 if (!ret) return fail();
21370 DRW_DBG("\n***************************** parsing MLEADER ***************\n")DRW_dbg::dbg("\n***************************** parsing MLEADER ***************\n"
)
;
21371 const std::uint64_t bodyEndBit = dwgDataEndBit;
21372 std::uint64_t stringEndBit = bodyEndBit;
21373 if (version > DRW::AC1021) {
21374 std::uint64_t ignoredBodyEndBit = 0;
21375 if (!entityBodyDataEndBit(*sBuf, version, objSize,
21376 ignoredBodyEndBit, &stringEndBit))
21377 return fail();
21378 }
21379
21380 // R2010b+ class version (BS, default 2; <=R2004 was 1). libreDWG
21381 // dwg2.spec:1303-1306. Absent in R2007 streams; reading it would drift.
21382 if (version >= DRW::AC1024) {
21383 if (!readBoundedBitShort(*buf, bodyEndBit, classVersion))
21384 return fail();
21385 if (classVersion > 10) {
21386 DRW_DBG("\nMLEADER: implausible classVersion=")DRW_dbg::dbg("\nMLEADER: implausible classVersion=");
21387 DRW_DBG(static_cast<int>(classVersion))DRW_dbg::dbg(static_cast<int>(classVersion));
21388 DRW_DBG(", aborting body\n")DRW_dbg::dbg(", aborting body\n");
21389 return fail();
21390 }
21391 }
21392
21393 // Phase 4 — embedded AcDbMLeaderObjectContextData / MLeaderAnnotContext.
21394 if (!parseMLeaderAnnotContext(version, buf, sBuf, objSize, bodyEndBit,
21395 stringEndBit, context)) {
21396 DRW_DBG("\nMLEADER: AnnotContext parse failed\n")DRW_dbg::dbg("\nMLEADER: AnnotContext parse failed\n");
21397 return fail();
21398 }
21399
21400 // Phase 3 — entity-level fields per §20.4.48 (after the AnnotContext).
21401 // Many handle slots are deferred to the trailing handle stream and not
21402 // stored here yet (resolution comes in Phase 7).
21403 if (!readBoundedBitLong(*buf, bodyEndBit, overrideFlags)
21404 || !readBoundedBitShort(*buf, bodyEndBit, leaderType))
21405 return fail();
21406 std::uint32_t parsedColor = 0;
21407 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version, parsedColor,
21408 nullptr, nullptr, nullptr, nullptr,
21409 stringEndBit))
21410 return fail();
21411 leaderColor = static_cast<int>(parsedColor);
21412 // leader line type handle 341 — handle stream
21413 if (!readBoundedBitLong(*buf, bodyEndBit, leaderLineWeight)
21414 || !readBoundedBit(*buf, bodyEndBit, landingEnabled)
21415 || !readBoundedBit(*buf, bodyEndBit, doglegEnabled)
21416 || !readBoundedBitDouble(*buf, bodyEndBit, landingDistance))
21417 return fail();
21418 // arrow head handle 342 — handle stream
21419 if (!readBoundedBitDouble(*buf, bodyEndBit, defaultArrowHeadSize)
21420 || !readBoundedBitShort(*buf, bodyEndBit, styleContentType))
21421 return fail();
21422 // text style handle 343 — handle stream
21423 if (!readBoundedBitShort(*buf, bodyEndBit, styleLeftAttach)
21424 || !readBoundedBitShort(*buf, bodyEndBit, styleRightAttach)
21425 || !readBoundedBitShort(*buf, bodyEndBit, styleTextAngleType)
21426 || !readBoundedBitShort(*buf, bodyEndBit, unknown175))
21427 return fail();
21428 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version, parsedColor,
21429 nullptr, nullptr, nullptr, nullptr,
21430 stringEndBit))
21431 return fail();
21432 styleTextColor = static_cast<int>(parsedColor);
21433 if (!readBoundedBit(*buf, bodyEndBit, styleTextFrameEnabled))
21434 return fail();
21435 // style block handle 344 — handle stream (optional)
21436 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version, parsedColor,
21437 nullptr, nullptr, nullptr, nullptr,
21438 stringEndBit))
21439 return fail();
21440 styleBlockColor = static_cast<int>(parsedColor);
21441 if (!readBoundedBitCoord(*buf, bodyEndBit, styleBlockScale)
21442 || !readBoundedBitDouble(*buf, bodyEndBit, styleBlockRotation)
21443 || !readBoundedBitShort(*buf, bodyEndBit, styleAttachmentType)
21444 || !readBoundedBit(*buf, bodyEndBit, isAnnotative))
21445 return fail();
21446
21447 // R2007 arrays (pre-R2010 only): per spec §20.4.48. Bounds-check the
21448 // counts; a misaligned bit stream would produce huge nonsense values.
21449 if (version < DRW::AC1024) {
21450 std::uint32_t nArrows = 0;
21451 if (!readTableBodyCount(version, buf, objSize, kMaxMLeaderItems, 1,
21452 nArrows))
21453 return fail();
21454 if (!DRW::reserve(arrowHeads, static_cast<int>(nArrows)))
21455 return fail();
21456 for (std::uint32_t i = 0; i < nArrows; ++i) {
21457 ArrowHeadEntry e;
21458 if (!readBoundedBit(*buf, bodyEndBit, e.isDefault))
21459 return fail();
21460 arrowHeads.push_back(e);
21461 }
21462 std::uint32_t nLabels = 0;
21463 if (!readTableBodyCount(version, buf, objSize, kMaxMLeaderItems, 4,
21464 nLabels))
21465 return fail();
21466 if (!DRW::reserve(blockLabels, static_cast<int>(nLabels)))
21467 return fail();
21468 for (std::uint32_t i = 0; i < nLabels; ++i) {
21469 BlockLabelEntry e;
21470 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
21471 e.labelText))
21472 return fail();
21473 if (!readBoundedBitShort(*buf, bodyEndBit, e.uiIndex)
21474 || !readBoundedBitDouble(*buf, bodyEndBit, e.width))
21475 return fail();
21476 blockLabels.push_back(std::move(e));
21477 }
21478 if (!readBoundedBit(*buf, bodyEndBit, isTextDirectionNegative)
21479 || !readBoundedBitShort(*buf, bodyEndBit, ipeAlign)
21480 || !readBoundedBitShort(*buf, bodyEndBit, justification)
21481 || !readBoundedBitDouble(*buf, bodyEndBit, scaleFactor))
21482 return fail();
21483 } else { // R2010+
21484 if (!readBoundedBitShort(*buf, bodyEndBit, attachmentDirection)
21485 || !readBoundedBitShort(*buf, bodyEndBit, styleTopAttach)
21486 || !readBoundedBitShort(*buf, bodyEndBit, styleBottomAttach))
21487 return fail();
21488 }
21489 if (version >= DRW::AC1027) { // R2013+
21490 if (!readBoundedBit(*buf, bodyEndBit, leaderExtendedToText))
21491 return fail();
21492 }
21493
21494 if (!buf->isGood() || !sBuf->isGood()
21495 || currentDwgBit(buf) > bodyEndBit
21496 || currentDwgBit(sBuf) > stringEndBit)
21497 return fail();
21498
21499 std::uint64_t handleEndBit = 0;
21500 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
21501 handleEndBit))
21502 return fail();
21503 // Common entity handles first (owner/reactors/xdic/layer/ltype/...) —
21504 // entity-specific handles follow in declared order from libreDWG
21505 // dwg2.spec. Read order in the trailing handle stream:
21506 // 1. (R2010b+ only) per-leader-line ltype + arrow handles, in the
21507 // same iteration order as the body block.
21508 // 2. AnnotContext content handle (text_style 340 if hasTextContents,
21509 // else block_table 341 if hasContentsBlock).
21510 // 3. mleaderstyle (340), line_ltype (341), arrow_handle (342),
21511 // text_style (343), block_style (344) entity-level handles.
21512 // 4. (R14-R2007 only) per-arrowhead + per-blocklabel handles.
21513 ret = DRW_Entity::parseDwgEntHandle(version, buf, true, handleEndBit);
21514 if (!ret)
21515 return fail();
21516
21517 auto safeHandle = [&](dwgHandle& slot, const char* tag) {
21518 if (!readBoundedDwgHandle(*buf, handleEndBit, 0, false, slot))
21519 return false;
21520 DRW_DBG(" ")DRW_dbg::dbg(" "); DRW_DBG(tag)DRW_dbg::dbg(tag); DRW_DBG(": ")DRW_dbg::dbg(": ");
21521 DRW_DBGHL(slot.code, slot.size, slot.ref)DRW_dbg::dbgHL(slot.code, slot.size, slot.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
21522 return true;
21523 };
21524
21525 // 1. R2010b+ per-line handles, in body iteration order.
21526 if (version >= DRW::AC1024) {
21527 for (auto& root : context.roots) {
21528 for (auto& line : root.leaderLines) {
21529 if (!safeHandle(line.lineTypeHandle, "line.ltype")) return fail();
21530 if (!safeHandle(line.arrowHandle, "line.arrow")) return fail();
21531 }
21532 }
21533 }
21534
21535 // 2. AnnotContext content handle.
21536 if (context.hasTextContents) {
21537 if (!safeHandle(context.textStyleHandle, "ctx.text_style")) return fail();
21538 } else if (context.hasContentsBlock) {
21539 if (!safeHandle(context.blockTableRecordHandle, "ctx.block_table")) return fail();
21540 }
21541
21542 // 3. Entity-level handles.
21543 if (!safeHandle(styleHandle, "mleaderstyle")) return fail();
21544 if (!safeHandle(leaderLineTypeHandle, "line_ltype")) return fail();
21545 if (!safeHandle(arrowHeadHandle, "arrow_handle")) return fail();
21546 if (!safeHandle(styleTextStyleHandle, "text_style")) return fail();
21547 if (!safeHandle(styleBlockHandle, "block_style")) return fail();
21548
21549 // 4. R14-R2007 per-arrowhead + per-blocklabel handles (counts came
21550 // from the body-side arrays read earlier).
21551 if (version < DRW::AC1024) {
21552 for (auto& a : arrowHeads)
21553 if (!safeHandle(a.handle, "arrowheads.handle")) return fail();
21554 for (auto& bl : blockLabels)
21555 if (!safeHandle(bl.attDefHandle, "blocklabels.attdef")) return fail();
21556 }
21557
21558 const int rb = buf->numRemainingBytes();
21559 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");
21560 if (rb > 4) {
21561 DRW_DBG("MLEADER: handle-stream tail ")DRW_dbg::dbg("MLEADER: handle-stream tail "); DRW_DBG(rb)DRW_dbg::dbg(rb);
21562 DRW_DBG(" bytes unconsumed (handle ")DRW_dbg::dbg(" bytes unconsumed (handle ");
21563 DRW_DBGH(handle)DRW_dbg::dbgH(handle); DRW_DBG(") — review tail handle list\n")DRW_dbg::dbg(") — review tail handle list\n");
21564 }
21565
21566 if (!buf->isGood() || !sBuf->isGood())
21567 return fail();
21568 *sourceBuf = *buf;
21569 return true;
21570}
21571
21572bool DRW_MLeader::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
21573 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
21574 (void)bs;
21575 if (version < DRW::AC1024)
21576 return false;
21577
21578 oType = kDwgClassNum;
21579 if (!encodeDwgCommon(version, buf, strBuf))
21580 return false;
21581
21582 buf->putBitShort(classVersion == 0 ? 2 : classVersion);
21583 if (!encodeMLeaderAnnotContext(version, buf, strBuf, context))
21584 return false;
21585
21586 buf->putBitLong(overrideFlags);
21587 buf->putBitShort(leaderType);
21588 buf->putCmColor(version, static_cast<std::uint16_t>(leaderColor));
21589 buf->putBitLong(leaderLineWeight);
21590 buf->putBit(landingEnabled ? 1 : 0);
21591 buf->putBit(doglegEnabled ? 1 : 0);
21592 buf->putBitDouble(landingDistance);
21593 buf->putBitDouble(defaultArrowHeadSize);
21594 buf->putBitShort(styleContentType);
21595 buf->putBitShort(styleLeftAttach);
21596 buf->putBitShort(styleRightAttach);
21597 buf->putBitShort(styleTextAngleType);
21598 buf->putBitShort(unknown175);
21599 buf->putCmColor(version, static_cast<std::uint16_t>(styleTextColor));
21600 buf->putBit(styleTextFrameEnabled ? 1 : 0);
21601 buf->putCmColor(version, static_cast<std::uint16_t>(styleBlockColor));
21602 buf->put3BitDouble(styleBlockScale);
21603 buf->putBitDouble(styleBlockRotation);
21604 buf->putBitShort(styleAttachmentType);
21605 buf->putBit(isAnnotative ? 1 : 0);
21606 buf->putBitShort(attachmentDirection);
21607 buf->putBitShort(styleTopAttach);
21608 buf->putBitShort(styleBottomAttach);
21609 if (version >= DRW::AC1027)
21610 buf->putBit(leaderExtendedToText ? 1 : 0);
21611
21612 if (!encodeDwgEntHandle(version, buf, handleBuf))
21613 return false;
21614
21615 dwgBufferW *hb = handleBuf ? handleBuf : buf;
21616 for (const DRW_MLeaderRoot& root : context.roots) {
21617 for (const DRW_MLeaderLeaderLine& line : root.leaderLines) {
21618 putHardPointerHandle(hb, line.lineTypeHandle.ref);
21619 putHardPointerHandle(hb, line.arrowHandle.ref);
21620 }
21621 }
21622
21623 if (context.hasTextContents) {
21624 putHardPointerHandle(hb, context.textStyleHandle.ref);
21625 } else if (context.hasContentsBlock) {
21626 putHardPointerHandle(hb, context.blockTableRecordHandle.ref);
21627 }
21628
21629 putHardPointerHandle(hb, styleHandle.ref);
21630 putHardPointerHandle(hb, leaderLineTypeHandle.ref);
21631 putHardPointerHandle(hb, arrowHeadHandle.ref);
21632 putHardPointerHandle(hb, styleTextStyleHandle.ref);
21633 putHardPointerHandle(hb, styleBlockHandle.ref);
21634
21635 return true;
21636}
21637
21638bool DRW_Viewport::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
21639 switch (code) {
21640 case 40:
21641 pswidth = reader->getDouble();
21642 break;
21643 case 41:
21644 psheight = reader->getDouble();
21645 break;
21646 case 68:
21647 vpstatus = reader->getInt32();
21648 break;
21649 case 69:
21650 vpID = reader->getInt32();
21651 break;
21652 case 12:
21653 centerPX = reader->getDouble();
21654 break;
21655 case 22:
21656 centerPY = reader->getDouble();
21657 break;
21658 case 13:
21659 snapPX = reader->getDouble();
21660 break;
21661 case 23:
21662 snapPY = reader->getDouble();
21663 break;
21664 case 14:
21665 snapSpPX = reader->getDouble();
21666 break;
21667 case 24:
21668 snapSpPY = reader->getDouble();
21669 break;
21670 case 15:
21671 gridSpX = reader->getDouble();
21672 break;
21673 case 25:
21674 gridSpY = reader->getDouble();
21675 break;
21676 case 16:
21677 viewDir.x = reader->getDouble();
21678 break;
21679 case 26:
21680 viewDir.y = reader->getDouble();
21681 break;
21682 case 36:
21683 viewDir.z = reader->getDouble();
21684 break;
21685 case 17:
21686 viewTarget.x = reader->getDouble();
21687 break;
21688 case 27:
21689 viewTarget.y = reader->getDouble();
21690 break;
21691 case 37:
21692 viewTarget.z = reader->getDouble();
21693 break;
21694 case 42:
21695 viewLength = reader->getDouble();
21696 break;
21697 case 43:
21698 frontClip = reader->getDouble();
21699 break;
21700 case 44:
21701 backClip = reader->getDouble();
21702 break;
21703 case 45:
21704 viewHeight = reader->getDouble();
21705 break;
21706 case 50:
21707 snapAngle = reader->getDouble();
21708 break;
21709 case 51:
21710 twistAngle = reader->getDouble();
21711 break;
21712 case 46:
21713 circleZoom = reader->getInt32();
21714 break;
21715 case 61:
21716 majorGridLines = reader->getInt32();
21717 break;
21718 case 63:
21719 ambientColor = static_cast<std::uint32_t>(reader->getInt32());
21720 break;
21721 case 72:
21722 circleZoom = reader->getInt32();
21723 break;
21724 case 90:
21725 statusFlags = reader->getInt32();
21726 break;
21727 case 1:
21728 styleSheet = reader->getUtf8String();
21729 break;
21730 case 281:
21731 renderMode = reader->getInt32();
21732 break;
21733 case 71:
21734 ucsPerViewport = reader->getInt32() != 0;
21735 break;
21736 case 74:
21737 ucsAtOrigin = reader->getInt32() != 0;
21738 break;
21739 case 110: case 120: case 130:
21740 case 111: case 121: case 131:
21741 case 112: case 122: case 132:
21742 readCoordTripletCode(code, reader, ucsOrigin, ucsXAxis, ucsYAxis);
21743 break;
21744 case 146:
21745 ucsElevation = reader->getDouble();
21746 break;
21747 case 76:
21748 break;
21749 case 79:
21750 ucsOrthographicType = reader->getInt32();
21751 break;
21752 case 148:
21753 shadePlotMode = reader->getInt32();
21754 break;
21755 case 170:
21756 shadePlotMode = reader->getInt32();
21757 break;
21758 case 292:
21759 useDefaultLighting = reader->getInt32() != 0;
21760 break;
21761 case 282:
21762 defaultLightingType = reader->getInt32();
21763 break;
21764 case 451:
21765 brightness = reader->getDouble();
21766 break;
21767 case 452:
21768 contrast = reader->getDouble();
21769 break;
21770 case 141:
21771 brightness = reader->getDouble();
21772 break;
21773 case 142:
21774 contrast = reader->getDouble();
21775 break;
21776 case 421:
21777 ambientColorRgb = reader->getInt32();
21778 break;
21779 case 431:
21780 ambientColorName = reader->getUtf8String();
21781 break;
21782 case 331:
21783 frozenLayerHandles.push_back(
21784 static_cast<std::uint32_t>(reader->getHandleString()));
21785 break;
21786 case 332:
21787 backgroundHandle = static_cast<std::uint32_t>(reader->getHandleString());
21788 break;
21789 case 333:
21790 shadePlotHandle = static_cast<std::uint32_t>(reader->getHandleString());
21791 break;
21792 case 340:
21793 clipBoundaryHandle = static_cast<std::uint32_t>(reader->getHandleString());
21794 break;
21795 case 345:
21796 namedUcsHandle = static_cast<std::uint32_t>(reader->getHandleString());
21797 break;
21798 case 346:
21799 baseUcsHandle = static_cast<std::uint32_t>(reader->getHandleString());
21800 break;
21801 case 361:
21802 m_sunHandle = static_cast<std::uint32_t>(reader->getHandleString());
21803 break;
21804 case 348:
21805 visualStyleHandle = static_cast<std::uint32_t>(reader->getHandleString());
21806 fullVisualStyleHandle = visualStyleHandle;
21807 break;
21808 case 349:
21809 shadePlotHandle = static_cast<std::uint32_t>(reader->getHandleString());
21810 break;
21811 default:
21812 return DRW_Point::parseCode(code, reader);
21813 }
21814
21815 return true;
21816}
21817
21818bool DRW_Viewport::validatePayloadFields() const {
21819 const auto finite = [](const DRW_Coord& point) {
21820 return std::isfinite(point.x) && std::isfinite(point.y)
21821 && std::isfinite(point.z);
21822 };
21823 const auto fitsSignedShort = [](int value) {
21824 return value >= static_cast<int>(std::numeric_limits<std::int16_t>::min())
21825 && value <= static_cast<int>(std::numeric_limits<std::int16_t>::max());
21826 };
21827 const auto fitsByte = [](int value) {
21828 return value >= 0
21829 && value <= static_cast<int>(std::numeric_limits<std::uint8_t>::max());
21830 };
21831 const auto fitsBitShort = [](int value) {
21832 return value >= 0
21833 && value <= static_cast<int>(std::numeric_limits<std::uint16_t>::max());
21834 };
21835 const bool integralCircleZoom = std::isfinite(circleZoom)
21836 && std::trunc(circleZoom) == circleZoom;
21837 return finite(basePoint) && finite(viewTarget) && finite(viewDir)
21838 && finite(ucsOrigin) && finite(ucsXAxis) && finite(ucsYAxis)
21839 && std::isfinite(pswidth) && std::isfinite(psheight)
21840 && std::isfinite(centerPX) && std::isfinite(centerPY)
21841 && std::isfinite(snapPX) && std::isfinite(snapPY)
21842 && std::isfinite(snapSpPX) && std::isfinite(snapSpPY)
21843 && std::isfinite(gridSpX) && std::isfinite(gridSpY)
21844 && std::isfinite(viewLength) && std::isfinite(frontClip)
21845 && std::isfinite(backClip) && std::isfinite(viewHeight)
21846 && std::isfinite(snapAngle) && std::isfinite(twistAngle)
21847 && std::isfinite(ucsElevation) && std::isfinite(brightness)
21848 && std::isfinite(contrast) && fitsSignedShort(vpstatus)
21849 && fitsSignedShort(vpID) && integralCircleZoom
21850 && circleZoom >= 0.0
21851 && circleZoom <= static_cast<double>(std::numeric_limits<std::uint16_t>::max())
21852 && fitsBitShort(majorGridLines) && fitsByte(renderMode)
21853 && fitsBitShort(ucsOrthographicType) && fitsBitShort(shadePlotMode)
21854 && fitsByte(defaultLightingType)
21855 && ambientColor <= std::numeric_limits<std::uint16_t>::max()
21856 && ambientColorRgb >= -1 && ambientColorRgb <= 0xFFFFFF
21857 && frozenLayerHandles.size() <= kMaxViewportFrozenLayers;
21858}
21859
21860bool DRW_Viewport::validateDxf() const {
21861 return validatePayloadFields();
21862}
21863//ex 22 dec 34
21864void DRW_Viewport::resetDwgState() {
21865 DRW_Point::resetDwgState();
21866 pswidth = 205.0;
21867 psheight = 156.0;
21868 vpstatus = 0;
21869 vpID = 0;
21870 centerPX = 128.5;
21871 centerPY = 97.5;
21872 snapPX = snapPY = 0.0;
21873 snapSpPX = snapSpPY = 10.0;
21874 gridSpX = gridSpY = 10.0;
21875 viewDir = DRW_Coord{0.0, 0.0, 1.0};
21876 viewTarget = DRW_Coord{0.0, 0.0, 0.0};
21877 viewLength = 50.0;
21878 frontClip = backClip = 0.0;
21879 viewHeight = 1.0;
21880 snapAngle = twistAngle = 0.0;
21881 circleZoom = 100.0;
21882 majorGridLines = 0;
21883 statusFlags = 0;
21884 styleSheet.clear();
21885 renderMode = 0;
21886 ucsAtOrigin = false;
21887 ucsPerViewport = false;
21888 ucsOrigin = DRW_Coord{0.0, 0.0, 0.0};
21889 ucsXAxis = DRW_Coord{1.0, 0.0, 0.0};
21890 ucsYAxis = DRW_Coord{0.0, 1.0, 0.0};
21891 ucsElevation = 0.0;
21892 ucsOrthographicType = 0;
21893 shadePlotMode = 0;
21894 useDefaultLighting = true;
21895 defaultLightingType = 1;
21896 brightness = contrast = 0.0;
21897 ambientColor = 250;
21898 ambientColorRgb = -1;
21899 ambientColorMethod = 0;
21900 ambientColorName.clear();
21901 vpHeaderHandle = 0;
21902 clipBoundaryHandle = 0;
21903 namedUcsHandle = 0;
21904 baseUcsHandle = 0;
21905 backgroundHandle = 0;
21906 visualStyleHandle = 0;
21907 shadePlotHandle = 0;
21908 m_sunHandle = 0;
21909 frozenLayerHandles.clear();
21910 frozenLyCount = 0;
21911}
21912
21913bool DRW_Viewport::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
21914 resetDwgState();
21915 dwgBuffer *sourceBuf = buf;
21916 auto fail = [this, sourceBuf]() {
21917 if (sourceBuf != nullptr)
21918 sourceBuf->invalidate();
21919 resetDwgState();
21920 return false;
21921 };
21922 if (sourceBuf == nullptr)
21923 return fail();
21924
21925 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
21926 buf = &bodyProbe;
21927 dwgBuffer sBuff = bodyProbe.forkIndependent();
21928 dwgBuffer *sBuf = buf;
21929 if (version > DRW::AC1018) {//2007+
21930 sBuf = &sBuff; //separate buffer for strings
21931 }
21932 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
21933 if (!ret)
21934 return fail();
21935 DRW_DBG("\n***************************** parsing viewport *****************************************\n")DRW_dbg::dbg("\n***************************** parsing viewport *****************************************\n"
)
;
21936 const std::uint64_t bodyEndBit = dwgDataEndBit;
21937 std::uint64_t stringEndBit = bodyEndBit;
21938 if (version > DRW::AC1021) {
21939 std::uint64_t ignoredBodyEndBit = 0;
21940 if (!entityBodyDataEndBit(*sBuf, version, objSize,
21941 ignoredBodyEndBit, &stringEndBit))
21942 return fail();
21943 }
21944 if (!readBoundedBitCoord(*buf, bodyEndBit, basePoint)
21945 || !readBoundedBitDouble(*buf, bodyEndBit, pswidth)
21946 || !readBoundedBitDouble(*buf, bodyEndBit, psheight))
21947 return fail();
21948 DRW_DBG("center ")DRW_dbg::dbg("center "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
21949 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");
21950 //RLZ TODO: complete in dxf
21951 if (version > DRW::AC1014) {//2000+
21952 if (!readBoundedBitCoord(*buf, bodyEndBit, viewTarget)
21953 || !readBoundedBitCoord(*buf, bodyEndBit, viewDir)
21954 || !readBoundedBitDouble(*buf, bodyEndBit, twistAngle)
21955 || !readBoundedBitDouble(*buf, bodyEndBit, viewHeight)
21956 || !readBoundedBitDouble(*buf, bodyEndBit, viewLength)
21957 || !readBoundedBitDouble(*buf, bodyEndBit, frontClip)
21958 || !readBoundedBitDouble(*buf, bodyEndBit, backClip)
21959 || !readBoundedBitDouble(*buf, bodyEndBit, snapAngle)
21960 || !readBoundedRawDouble(*buf, bodyEndBit, centerPX)
21961 || !readBoundedRawDouble(*buf, bodyEndBit, centerPY)
21962 || !readBoundedRawDouble(*buf, bodyEndBit, snapPX)
21963 || !readBoundedRawDouble(*buf, bodyEndBit, snapPY)
21964 || !readBoundedRawDouble(*buf, bodyEndBit, snapSpPX)
21965 || !readBoundedRawDouble(*buf, bodyEndBit, snapSpPY)
21966 || !readBoundedRawDouble(*buf, bodyEndBit, gridSpX)
21967 || !readBoundedRawDouble(*buf, bodyEndBit, gridSpY))
21968 return fail();
21969 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);
21970 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);
21971 DRW_DBG("\nView twist Angle: ")DRW_dbg::dbg("\nView twist Angle: "); DRW_DBG(twistAngle)DRW_dbg::dbg(twistAngle);
21972 DRW_DBG("\nview Height: ")DRW_dbg::dbg("\nview Height: "); DRW_DBG(viewHeight)DRW_dbg::dbg(viewHeight);
21973 DRW_DBG(" Lens Length: ")DRW_dbg::dbg(" Lens Length: "); DRW_DBG(viewLength)DRW_dbg::dbg(viewLength);
21974 DRW_DBG("\nfront Clip Z: ")DRW_dbg::dbg("\nfront Clip Z: "); DRW_DBG(frontClip)DRW_dbg::dbg(frontClip);
21975 DRW_DBG(" back Clip Z: ")DRW_dbg::dbg(" back Clip Z: "); DRW_DBG(backClip)DRW_dbg::dbg(backClip);
21976 DRW_DBG("\n snap Angle: ")DRW_dbg::dbg("\n snap Angle: "); DRW_DBG(snapAngle)DRW_dbg::dbg(snapAngle);
21977 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);
21978 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);
21979 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);
21980 std::uint16_t parsedCircleZoom = 0;
21981 if (!readBoundedBitShort(*buf, bodyEndBit, parsedCircleZoom))
21982 return fail();
21983 circleZoom = parsedCircleZoom;
21984 }
21985 if (version > DRW::AC1018) {//2007+
21986 std::uint16_t parsedMajorGridLines = 0;
21987 if (!readBoundedBitShort(*buf, bodyEndBit, parsedMajorGridLines))
21988 return fail();
21989 majorGridLines = parsedMajorGridLines;
21990 }
21991 if (version > DRW::AC1014) {//2000+
21992 std::int32_t parsedFrozenLyCount = 0;
21993 if (!readBoundedBitLong(*buf, bodyEndBit, parsedFrozenLyCount))
21994 return fail();
21995 if (!isValidCount(parsedFrozenLyCount, kMaxViewportFrozenLayers))
21996 return fail();
21997 frozenLyCount = static_cast<std::uint32_t>(parsedFrozenLyCount);
21998 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");
21999 if (!readBoundedBitLong(*buf, bodyEndBit, statusFlags))
22000 return fail();
22001 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
22002 styleSheet))
22003 return fail();
22004 std::uint8_t parsedRenderMode = 0;
22005 if (!readBoundedRawChar8(*buf, bodyEndBit, parsedRenderMode))
22006 return fail();
22007 renderMode = parsedRenderMode;
22008 // ODA order is UCS-at-origin (74), then UCS-per-viewport (71).
22009 if (!readBoundedBit(*buf, bodyEndBit, ucsAtOrigin)
22010 || !readBoundedBit(*buf, bodyEndBit, ucsPerViewport)
22011 || !readBoundedBitCoord(*buf, bodyEndBit, ucsOrigin)
22012 || !readBoundedBitCoord(*buf, bodyEndBit, ucsXAxis)
22013 || !readBoundedBitCoord(*buf, bodyEndBit, ucsYAxis)
22014 || !readBoundedBitDouble(*buf, bodyEndBit, ucsElevation))
22015 return fail();
22016 std::uint16_t parsedUcsOrthographicType = 0;
22017 if (!readBoundedBitShort(*buf, bodyEndBit,
22018 parsedUcsOrthographicType))
22019 return fail();
22020 ucsOrthographicType = parsedUcsOrthographicType;
22021 }
22022 if (version > DRW::AC1015) {//2004+
22023 std::uint16_t parsedShadePlotMode = 0;
22024 if (!readBoundedBitShort(*buf, bodyEndBit, parsedShadePlotMode))
22025 return fail();
22026 shadePlotMode = parsedShadePlotMode;
22027 }
22028 if (version > DRW::AC1018) {//2007+
22029 std::uint8_t parsedDefaultLightingType = 0;
22030 if (!readBoundedBit(*buf, bodyEndBit, useDefaultLighting)
22031 || !readBoundedRawChar8(*buf, bodyEndBit,
22032 parsedDefaultLightingType)
22033 || !readBoundedBitDouble(*buf, bodyEndBit, brightness)
22034 || !readBoundedBitDouble(*buf, bodyEndBit, contrast))
22035 return fail();
22036 defaultLightingType = parsedDefaultLightingType;
22037 // ODA §20.4.38: ambient color is CMC, not ENC — confirmed by libreDWG dwg.spec:2512
22038 bool hasRgbColor = false;
22039 int parsedAmbientColorRgb = -1;
22040 UTF8STRINGstd::string parsedAmbientColorName;
22041 UTF8STRINGstd::string parsedAmbientColorBookName;
22042 dwgBuffer colorProbe = buf->forkIndependent();
22043 dwgBuffer colorStringProbe = sBuf->forkIndependent();
22044 const std::uint32_t parsedAmbientColor = colorProbe.getCmColor(
22045 version, &parsedAmbientColorRgb, &colorStringProbe,
22046 &parsedAmbientColorName, &parsedAmbientColorBookName,
22047 &hasRgbColor);
22048 if (!colorProbe.isGood() || !colorStringProbe.isGood()
22049 || currentDwgBit(&colorProbe) > bodyEndBit
22050 || currentDwgBit(&colorStringProbe) > stringEndBit)
22051 return fail();
22052 *buf = colorProbe;
22053 *sBuf = colorStringProbe;
22054 ambientColor = parsedAmbientColor;
22055 ambientColorRgb = parsedAmbientColorRgb;
22056 ambientColorName = parsedAmbientColorBookName.empty()
22057 ? std::move(parsedAmbientColorName)
22058 : (parsedAmbientColorBookName + "$"
22059 + parsedAmbientColorName);
22060 if (!hasRgbColor)
22061 ambientColorRgb = -1;
22062 }
22063 const auto finite = [](const DRW_Coord& point) {
22064 return std::isfinite(point.x) && std::isfinite(point.y)
22065 && std::isfinite(point.z);
22066 };
22067 if (!finite(basePoint) || !finite(viewTarget) || !finite(viewDir)
22068 || !finite(ucsOrigin) || !finite(ucsXAxis) || !finite(ucsYAxis)
22069 || !std::isfinite(pswidth) || !std::isfinite(psheight)
22070 || !std::isfinite(centerPX) || !std::isfinite(centerPY)
22071 || !std::isfinite(snapPX) || !std::isfinite(snapPY)
22072 || !std::isfinite(snapSpPX) || !std::isfinite(snapSpPY)
22073 || !std::isfinite(gridSpX) || !std::isfinite(gridSpY)
22074 || !std::isfinite(viewLength) || !std::isfinite(frontClip)
22075 || !std::isfinite(backClip) || !std::isfinite(viewHeight)
22076 || !std::isfinite(snapAngle) || !std::isfinite(twistAngle)
22077 || !std::isfinite(ucsElevation) || !std::isfinite(brightness)
22078 || !std::isfinite(contrast))
22079 return fail();
22080 if (!validatePayloadFields())
22081 return fail();
22082 std::uint64_t handleEndBit = 0;
22083 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
22084 handleEndBit))
22085 return fail();
22086 ret = DRW_Entity::parseDwgEntHandle(version, buf, true, handleEndBit);
22087 if (!ret || !buf->isGood() || !sBuf->isGood())
22088 return fail();
22089
22090 // R13-R2004 keep the handle stream inline (sBuf aliases buf), so by this
22091 // point sBuf has necessarily moved past stringEndBit into the handle
22092 // data; only R2007+ has a detached string stream to bound here (see the
22093 // identical guard in DRW_Block::parseDwg).
22094 if (!buf->isGood() || !sBuf->isGood()
22095 || (version > DRW::AC1018 && currentDwgBit(sBuf) > stringEndBit))
22096 return fail();
22097
22098 dwgBuffer handleProbe = buf->forkIndependent();
22099 auto readHandleRef = [&handleProbe, handleEndBit](std::uint32_t& target) {
22100 dwgHandle parsed;
22101 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
22102 parsed)) {
22103 return false;
22104 }
22105 target = parsed.ref;
22106 return true;
22107 };
22108 if (version < DRW::AC1015) {//R13 & R14 only
22109 if (!readHandleRef(vpHeaderHandle))
22110 return fail();
22111 }
22112 if (version > DRW::AC1014) {//2000+
22113 if (!DRW::reserve(frozenLayerHandles, static_cast<int>(frozenLyCount)))
22114 return fail();
22115 for (std::uint32_t i = 0; i < frozenLyCount; ++i) {
22116 std::uint32_t ref = 0;
22117 if (!readHandleRef(ref))
22118 return fail();
22119 frozenLayerHandles.push_back(ref);
22120 }
22121 if (!readHandleRef(clipBoundaryHandle))
22122 return fail();
22123 if (version == DRW::AC1015) {//2000 only
22124 if (!readHandleRef(vpHeaderHandle))
22125 return fail();
22126 }
22127 if (!readHandleRef(namedUcsHandle) || !readHandleRef(baseUcsHandle))
22128 return fail();
22129 }
22130 if (version > DRW::AC1018) {//2007+
22131 if (!readHandleRef(backgroundHandle)
22132 || !readHandleRef(visualStyleHandle)
22133 || !readHandleRef(shadePlotHandle))
22134 return fail();
22135 }
22136 if (version > DRW::AC1018 && !readHandleRef(m_sunHandle))
22137 return fail();
22138 if (!handleProbe.isGood() || !sBuf->isGood())
22139 return fail();
22140 *sourceBuf = handleProbe;
22141 return true;
22142}
22143
22144// ---------------------------------------------------------------------------
22145// Write helpers shared by the new encoders below.
22146// ---------------------------------------------------------------------------
22147
22148namespace {
22149// ODA handle reference codes: 3 hard owner, 4 soft pointer, 5 hard pointer.
22150static void putTypedHandle(dwgBufferW *hb, std::uint32_t ref,
22151 std::uint8_t code) {
22152 dwgHandle h;
22153 h.code = ref == 0 ? 0 : code;
22154 h.ref = ref;
22155 h.ref64 = ref;
22156 h.size = 0;
22157 if (h.ref != 0) {
22158 std::uint32_t t = h.ref;
22159 while (t != 0) { t >>= 8; ++h.size; }
22160 }
22161 hb->putHandle(h);
22162}
22163
22164static void putAbsHandle(dwgBufferW *hb, std::uint32_t ref) {
22165 putTypedHandle(hb, ref, 5);
22166}
22167} // namespace
22168
22169// ---------------------------------------------------------------------------
22170// DRW_MLine::encodeDwg — OT=47 (AC1015/AC1018/AC1024)
22171// ---------------------------------------------------------------------------
22172
22173bool DRW_MLine::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
22174 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
22175 (void)bs; (void)strBuf;
22176 if (!validatePayloadFields())
22177 return false;
22178 oType = 47;
22179 if (!encodeDwgCommon(version, buf)) return false;
22180
22181 buf->putBitDouble(scale);
22182 buf->putRawChar8(justification);
22183 buf->put3BitDouble(basePoint);
22184 buf->putExtrusion(extPoint, false); // false = pre-2000 style (getExtrusion(false) in parser)
22185 buf->putBitShort(static_cast<std::uint16_t>(openClosed));
22186 buf->putRawChar8(numLines);
22187 buf->putBitShort(numVerts);
22188
22189 for (const auto& vtx : vertlist) {
22190 buf->put3BitDouble(vtx.position);
22191 buf->put3BitDouble(vtx.vertexDir);
22192 buf->put3BitDouble(vtx.miterDir);
22193 for (int li = 0; li < static_cast<int>(numLines); ++li) {
22194 const auto& segs = (li < static_cast<int>(vtx.segParms.size()))
22195 ? vtx.segParms[li] : std::vector<double>{};
22196 const auto& fills = (li < static_cast<int>(vtx.areaFillParms.size()))
22197 ? vtx.areaFillParms[li] : std::vector<double>{};
22198 buf->putBitShort(static_cast<std::uint16_t>(segs.size()));
22199 for (double s : segs) buf->putBitDouble(s);
22200 buf->putBitShort(static_cast<std::uint16_t>(fills.size()));
22201 for (double f : fills) buf->putBitDouble(f);
22202 }
22203 }
22204
22205 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
22206
22207 // MLINE style handle — extra handle after standard entity handles.
22208 if (version > DRW::AC1014) {
22209 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
22210 putAbsHandle(hb, styleHandle);
22211 }
22212 return true;
22213}
22214
22215// ---------------------------------------------------------------------------
22216// DRW_Vertex::encodeDwg — OT varies by flags
22217// ---------------------------------------------------------------------------
22218
22219bool DRW_Vertex::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
22220 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
22221 (void)bs; (void)strBuf;
22222 if (buf == nullptr || !validatePayloadFields())
22223 return false;
22224 switch (m_dwgSubtype) {
22225 case DwgSubtype::Vertex2D: oType = 0x0A; break;
22226 case DwgSubtype::Vertex3D: oType = 0x0B; break;
22227 case DwgSubtype::Mesh: oType = 0x0C; break;
22228 case DwgSubtype::Polyface: oType = 0x0D; break;
22229 case DwgSubtype::PolyfaceFace: oType = 0x0E; break;
22230 case DwgSubtype::Auto:
22231 if ((flags & 64) != 0)
22232 oType = 0x0D; // VERTEX_PFACE coordinate vertex
22233 else if ((flags & 128) != 0)
22234 oType = 0x0E; // VERTEX_PFACE_FACE
22235 else if ((flags & 16) != 0)
22236 oType = 0x0C; // VERTEX_MESH
22237 else if ((flags & 32) != 0 || (flags & 8) != 0)
22238 oType = 0x0B; // VERTEX_3D
22239 else
22240 oType = 0x0A; // VERTEX_2D
22241 break;
22242 }
22243
22244 if (!encodeDwgCommon(version, buf)) return false;
22245
22246 if (oType == 0x0A) {
22247 buf->putRawChar8(static_cast<std::uint8_t>(flags));
22248 buf->put3BitDouble(basePoint);
22249 buf->putBitDouble(stawidth);
22250 buf->putBitDouble(endwidth);
22251 buf->putBitDouble(bulge);
22252 if (version > DRW::AC1021)
22253 buf->putBitLong(static_cast<std::int32_t>(identifier));
22254 buf->putBitDouble(tgdir);
22255 } else if (oType == 0x0B || oType == 0x0C || oType == 0x0D) {
22256 buf->putRawChar8(static_cast<std::uint8_t>(flags));
22257 buf->put3BitDouble(basePoint);
22258 } else { // 0x0E pface face
22259 buf->putBitShort(static_cast<std::uint16_t>(vindex1));
22260 buf->putBitShort(static_cast<std::uint16_t>(vindex2));
22261 buf->putBitShort(static_cast<std::uint16_t>(vindex3));
22262 buf->putBitShort(static_cast<std::uint16_t>(vindex4));
22263 }
22264
22265 return encodeDwgEntHandle(version, buf, handleBuf);
22266}
22267
22268bool DRW_SeqEnd::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
22269 DRW_Entity::reset();
22270 if (buf == nullptr)
22271 return false;
22272
22273 auto fail = [this, buf]() {
22274 buf->invalidate();
22275 DRW_Entity::reset();
22276 return false;
22277 };
22278 dwgBuffer probe = buf->forkIndependent();
22279 if (!DRW_Entity::parseDwgCommon(version, &probe, bs)
22280 || !probe.isGood())
22281 return fail();
22282 std::uint64_t handleEndBit = 0;
22283 if (!dwgHandleStreamEndBit(*buf, version, objSize, bs, handleEndBit)
22284 || !DRW_Entity::parseDwgEntHandle(version, &probe, true,
22285 handleEndBit)
22286 || !probe.isGood())
22287 return fail();
22288
22289 *buf = probe;
22290 return true;
22291}
22292
22293bool DRW_SeqEnd::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
22294 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
22295 (void)bs; (void)strBuf;
22296 oType = 0x06;
22297 if (!encodeDwgCommon(version, buf))
22298 return false;
22299 return encodeDwgEntHandle(version, buf, handleBuf);
22300}
22301
22302// ---------------------------------------------------------------------------
22303// DRW_Polyline::encodeDwg — OT varies by flags; vertex handles emitted here.
22304// ---------------------------------------------------------------------------
22305
22306bool DRW_Polyline::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
22307 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
22308 (void)bs; (void)strBuf;
22309 if (buf == nullptr || !validatePayloadFields())
22310 return false;
22311 // Determine object type from stored flags (mirror of parseDwg dispatch).
22312 if (flags & 64) oType = 0x1D; // POLYLINE_PFACE
22313 else if (flags & 16) oType = 0x1E; // POLYLINE_MESH
22314 else if (flags & 8) oType = 0x10; // POLYLINE_3D
22315 else oType = 0x0F; // POLYLINE_2D
22316
22317 if (!encodeDwgCommon(version, buf)) return false;
22318
22319 if (oType == 0x0F) {
22320 buf->putBitShort(static_cast<std::uint16_t>(flags));
22321 buf->putBitShort(static_cast<std::uint16_t>(curvetype));
22322 buf->putBitDouble(defstawidth);
22323 buf->putBitDouble(defendwidth);
22324 buf->putThickness(thickness, version > DRW::AC1014);
22325 buf->putBitDouble(basePoint.z);
22326 buf->putExtrusion(extPoint, version > DRW::AC1014);
22327 } else if (oType == 0x10) {
22328 // curvetype → 2 RC flag bytes (mirror of parser decode)
22329 std::uint8_t rc1 = 0;
22330 if (curvetype == 5) rc1 = 1;
22331 else if (curvetype == 6) rc1 = 2;
22332 else if (curvetype == 8) rc1 = 3;
22333 buf->putRawChar8(rc1);
22334 buf->putRawChar8(static_cast<std::uint8_t>(flags & 1)); // bit 0 = closed
22335 } else if (oType == 0x1D) {
22336 buf->putBitShort(static_cast<std::uint16_t>(vertexcount));
22337 buf->putBitShort(static_cast<std::uint16_t>(facecount));
22338 } else { // 0x1E MESH
22339 buf->putBitShort(static_cast<std::uint16_t>(flags & ~16)); // strip reader-added bit 4
22340 buf->putBitShort(static_cast<std::uint16_t>(curvetype));
22341 buf->putBitShort(static_cast<std::uint16_t>(vertexcount)); // M count
22342 buf->putBitShort(static_cast<std::uint16_t>(facecount)); // N count
22343 buf->putBitShort(static_cast<std::uint16_t>(smoothM)); // mDensity, DXF 73
22344 buf->putBitShort(static_cast<std::uint16_t>(smoothN)); // nDensity, DXF 74
22345 }
22346
22347 // AC2004+ (>AC1015): emit vertex count before the handle section.
22348 std::int32_t ooCount = static_cast<std::int32_t>(vertlist.size());
22349 if (version > DRW::AC1015)
22350 buf->putBitLong(ooCount);
22351
22352 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
22353
22354 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
22355
22356 if (version < DRW::AC1018) {
22357 // R2000-: first/last vertex handles (absolute hard pointers).
22358 putAbsHandle(hb, vertlist.empty() ? 0u : vertlist.front()->handle);
22359 putAbsHandle(hb, vertlist.empty() ? 0u : vertlist.back()->handle);
22360 } else {
22361 // R2004+: one handle per vertex.
22362 for (const auto& v : vertlist)
22363 putAbsHandle(hb, v ? v->handle : 0u);
22364 }
22365 putAbsHandle(hb, seqEndH.ref);
22366
22367 return true;
22368}
22369
22370// ---------------------------------------------------------------------------
22371// DRW_Leader::encodeDwg — OT=45 (AC1015/AC1018/AC1024)
22372// ---------------------------------------------------------------------------
22373
22374bool DRW_Leader::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
22375 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
22376 (void)bs; (void)strBuf;
22377 if (buf == nullptr || !validatePayloadFields())
22378 return false;
22379 oType = 45;
22380 if (!encodeDwgCommon(version, buf)) return false;
22381
22382 buf->putBit(0); // unknown bit
22383 buf->putBitShort(static_cast<std::uint16_t>(flag)); // annotation type
22384 buf->putBitShort(static_cast<std::int16_t>(leadertype)); // pathType (DXF code 72)
22385 buf->putBitLong(static_cast<std::int32_t>(vertexlist.size()));
22386 for (const auto& vp : vertexlist)
22387 buf->put3BitDouble(*vp);
22388 buf->put3BitDouble(origin); // leader plane origin
22389 // ODA §20.4.47: Extrusion is plain 3DPOINT (3BD), not BE — matches parseDwg.
22390 buf->put3BitDouble(extrusionPoint);
22391
22392 buf->put3BitDouble(horizdir);
22393 buf->put3BitDouble(offsetblock);
22394
22395 if (leaderHasEndpointProjection(version))
22396 buf->put3BitDouble(offsettext); // endpoint projection
22397
22398 if (version < DRW::AC1015)
22399 buf->putBitDouble(0.0); // dimgap (pre-R2000)
22400
22401 if (leaderHasTextBox(version)) {
22402 buf->putBitDouble(textheight);
22403 buf->putBitDouble(textwidth);
22404 }
22405
22406 buf->putBit(static_cast<std::uint8_t>(hookline));
22407 buf->putBit(static_cast<std::uint8_t>(arrow));
22408
22409 buf->putBitShort(0); // arrowHeadType (present in every DWG version)
22410 if (version < DRW::AC1015) {
22411 buf->putBitDouble(0.0); // dimasz
22412 buf->putBit(0); // unk
22413 buf->putBit(0); // unk
22414 buf->putBitShort(0); // unk short
22415 buf->putBitShort(static_cast<std::uint16_t>(coloruse)); // byBlock color
22416 buf->putBit(0); // unk
22417 buf->putBit(0); // unk
22418 } else {
22419 buf->putBit(0); // unk
22420 buf->putBit(0); // unk
22421 }
22422
22423 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
22424
22425 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
22426 putAbsHandle(hb, 0); // AnnotH — null (no annotation entity)
22427 putAbsHandle(hb, 0x15); // dimStyleH — hard ptr to STANDARD (handle 0x15)
22428
22429 return buf->isGood() && hb->isGood();
22430}
22431
22432// ---------------------------------------------------------------------------
22433// DRW_Viewport::encodeDwg — OT=34 (AC1015/AC1018/AC1024)
22434// ---------------------------------------------------------------------------
22435
22436bool DRW_Viewport::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
22437 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
22438 (void)bs;
22439 if (buf == nullptr || !validatePayloadFields())
22440 return false;
22441 oType = 34;
22442 // Use strBuf for TV strings in AC1024; for AC1015/AC1018 strings go inline.
22443 dwgBufferW *sb = (strBuf && version > DRW::AC1018) ? strBuf : buf;
22444 if (!encodeDwgCommon(version, buf)) return false;
22445
22446 buf->putBitDouble(basePoint.x);
22447 buf->putBitDouble(basePoint.y);
22448 buf->putBitDouble(basePoint.z);
22449 buf->putBitDouble(pswidth);
22450 buf->putBitDouble(psheight);
22451
22452 if (version > DRW::AC1014) {
22453 buf->putBitDouble(viewTarget.x);
22454 buf->putBitDouble(viewTarget.y);
22455 buf->putBitDouble(viewTarget.z);
22456 buf->putBitDouble(viewDir.x);
22457 buf->putBitDouble(viewDir.y);
22458 buf->putBitDouble(viewDir.z);
22459 buf->putBitDouble(twistAngle);
22460 buf->putBitDouble(viewHeight);
22461 buf->putBitDouble(viewLength); // lens length
22462 buf->putBitDouble(frontClip);
22463 buf->putBitDouble(backClip);
22464 buf->putBitDouble(snapAngle);
22465 buf->putRawDouble(centerPX);
22466 buf->putRawDouble(centerPY);
22467 buf->putRawDouble(snapPX);
22468 buf->putRawDouble(snapPY);
22469 buf->putRawDouble(snapSpPX);
22470 buf->putRawDouble(snapSpPY);
22471 buf->putRawDouble(gridSpX);
22472 buf->putRawDouble(gridSpY);
22473 buf->putBitShort(static_cast<std::uint16_t>(circleZoom));
22474 }
22475
22476 if (version > DRW::AC1018)
22477 buf->putBitShort(static_cast<std::uint16_t>(majorGridLines));
22478
22479 if (version > DRW::AC1014) {
22480 buf->putBitLong(static_cast<std::int32_t>(frozenLayerHandles.size()));
22481 buf->putBitLong(statusFlags);
22482 sb->putVariableText(version, styleSheet);
22483 buf->putRawChar8(static_cast<std::uint8_t>(renderMode));
22484 buf->putBit(ucsAtOrigin ? 1 : 0);
22485 buf->putBit(ucsPerViewport ? 1 : 0);
22486 buf->put3BitDouble(ucsOrigin);
22487 buf->put3BitDouble(ucsXAxis);
22488 buf->put3BitDouble(ucsYAxis);
22489 buf->putBitDouble(ucsElevation);
22490 buf->putBitShort(static_cast<std::uint16_t>(ucsOrthographicType));
22491 }
22492
22493 if (version > DRW::AC1015)
22494 buf->putBitShort(static_cast<std::uint16_t>(shadePlotMode));
22495
22496 if (version > DRW::AC1018) {
22497 buf->putBit(useDefaultLighting ? 1 : 0);
22498 buf->putRawChar8(defaultLightingType);
22499 buf->putBitDouble(brightness);
22500 buf->putBitDouble(contrast);
22501 if (ambientColorRgb >= 0) {
22502 buf->putCmColor(version, static_cast<std::uint16_t>(ambientColor),
22503 ambientColorRgb, ambientColorName, "", strBuf);
22504 } else {
22505 buf->putCmColor(version, static_cast<std::uint16_t>(ambientColor));
22506 }
22507 }
22508
22509 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
22510
22511 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
22512
22513 if (version < DRW::AC1015) {
22514 putTypedHandle(hb, vpHeaderHandle, 5);
22515 }
22516 if (version > DRW::AC1014) {
22517 const std::uint8_t frozenCode = version == DRW::AC1015 ? 5 : 4;
22518 for (const std::uint32_t ref : frozenLayerHandles)
22519 putTypedHandle(hb, ref, frozenCode);
22520 putTypedHandle(hb, clipBoundaryHandle, 4);
22521 if (version == DRW::AC1015)
22522 putTypedHandle(hb, vpHeaderHandle, 5);
22523 putTypedHandle(hb, namedUcsHandle, 5);
22524 putTypedHandle(hb, baseUcsHandle, 5);
22525 }
22526 if (version > DRW::AC1018) {
22527 putTypedHandle(hb, backgroundHandle, 4);
22528 putTypedHandle(hb, visualStyleHandle, 5);
22529 putTypedHandle(hb, shadePlotHandle, 4);
22530 putTypedHandle(hb, m_sunHandle, 3);
22531 }
22532
22533 return buf->isGood() && sb->isGood() && hb->isGood();
22534}