Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
StyledTextIO_LedNative.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <cctype>
7#include <climits>
8#include <cstdio> // for a couple sprintf() calls - could pretty easily be avoided
9
13#include "Stroika/Foundation/Characters/LineEndings.h"
15#include "Stroika/Foundation/DataExchange/BadFormatException.h"
16#include "Stroika/Foundation/Execution/Exceptions.h"
17
18#include "Stroika/Frameworks/Led/StyledTextEmbeddedObjects.h"
19
20#include "StyledTextIO_LedNative.h"
21
22using namespace Stroika::Foundation;
24
25using namespace Stroika::Frameworks;
26using namespace Stroika::Frameworks::Led;
27using namespace Stroika::Frameworks::Led::StyledTextIO;
28
30
31namespace {
32 inline void OutputStandardToSinkStream_size_t_ (StyledTextIOWriter::SinkStream& sinkStream, size_t n)
33 {
34 uint32_t buf;
35 SizeTToBuf (n, &buf);
36 sinkStream.write (&buf, sizeof (buf));
37 }
38 inline unsigned long InputStandardFromSrcStream_ULONG (StyledTextIOReader::SrcStream& srcStream)
39 {
40 uint32_t buf;
41 if (srcStream.read (&buf, sizeof (buf)) != sizeof (buf)) {
42 Execution::Throw (DataExchange::BadFormatException::kThe);
43 }
44 return (BufToUInt32 (&buf));
45 }
46}
47
48/*
49 * Basic file format is:
50 * MAGIC COOKIE
51 * LENGTH OF TEXT (ULONG)
52 * TEXT (variable length)
53 * LENGTH OF STYLE RUN LIST (number of elemenbts - stored as ULONG)
54 * Actual Style run elements, stored as PortableStyleRunData
55 * MAGIC COOKIE - AGAIN - JUST FOR GOOD LUCK!!!
56 */
57using LedFormatMagicCookie = char[16];
58const LedFormatMagicCookie kLedPartFormatVersion_4_MagicNumber = "\01Version4"; // always keep this magic cookie the same size
59const LedFormatMagicCookie kLedPartFormatVersion_5_MagicNumber = "\01Version5"; // always keep this magic cookie the same size
60const LedFormatMagicCookie kLedPartFormatVersion_6_MagicNumber = "\01Version6"; // always keep this magic cookie the same size
61
62/*
63 * Version 4 font style info file format.
64 */
65struct PortableStyleRunData_Version4 {
66 char fFontName[256];
67 unsigned char fItalic;
68 unsigned char fBoldWeight; // use Windows definition here/4...
69 enum {
70 eBoldnessNormal = 400 / 4,
71 eBoldnessBold = 700 / 4
72 };
73 uint8_t fUnderline;
74 uint8_t fUnused1_; // so we get good cross-platform / cross-compiler alignment
75 uint16_t fFontSize; // note this size is a POINT-SIZE (not the tmHeight)
76 uint16_t fUnused2_;
77 uint32_t fLength;
78};
79inline void _DO_ALIGN_ASSERTS_Version4_ ()
80{
81 Assert (sizeof (PortableStyleRunData_Version4) == 268);
82 Assert (offsetof (PortableStyleRunData_Version4, fItalic) == 256);
83 Assert (offsetof (PortableStyleRunData_Version4, fBoldWeight) == 257);
84 Assert (offsetof (PortableStyleRunData_Version4, fUnderline) == 258);
85 Assert (offsetof (PortableStyleRunData_Version4, fFontSize) == 260);
86 Assert (offsetof (PortableStyleRunData_Version4, fLength) == 264);
87}
88inline StyledInfoSummaryRecord mkInfoSummaryRecordFromPortData (const PortableStyleRunData_Version4& srcData)
89{
90 _DO_ALIGN_ASSERTS_Version4_ ();
92 fsp.SetStyle_Plain (); // so all style valid bits set...
93 fsp.SetStyle_Bold (srcData.fBoldWeight >= PortableStyleRunData_Version4::eBoldnessBold);
94 fsp.SetStyle_Italic (!!srcData.fItalic);
95 fsp.SetStyle_Underline (!!srcData.fUnderline);
96 short fontSize = BufToUInt16 (&srcData.fFontSize);
97 if (fontSize < 3 or fontSize > 128) {
98 fontSize = 12; // a reasonable default for bogus/corrupt data
99 }
100 fsp.SetPointSize (fontSize);
101 fsp.SetFontName (String::FromNarrowSDKString (srcData.fFontName).AsSDKString ());
102 return (StyledInfoSummaryRecord (fsp, BufToUInt32 (&srcData.fLength)));
103}
104
105/*
106 * Version 5 font style info file format.
107 * <<Used for Led 2.1 release>>
108 */
109struct PortableStyleRunData_Version5 {
110 unsigned char fThisRecordLength; // nbytes in this record - cuz fFontName len is variable...
111 // note use of a char here effectively limits us to only font names of
112 // a little over 200, but that should be more than enuf (limit on PC is 32,
113 // and I don't know on mac).
114
115 // Really wanted to just use bit-fields for these guys, but that is non-portable.
116 // Sigh... I did that first and the code looks much more readable without all those
117 // damned shifts and ors etc... LGP 960531
118 enum BitFlagStyles {
119 eBold = 0,
120 eItalic,
121 eUnderline,
122 eOutline,
123 eShadow,
124 eCondensed,
125 eExtended,
126 eStrikeout
127 };
128 unsigned char fStyleSet; // or in BitFlagStyles
129 unsigned short fPointSize; // note this size is a POINT-SIZE (not the tmHeight)
130 uint32_t fLength;
131 char fFontName[256]; // to file, we really only write as many bytes as needed (no NUL char TERM - infer size from fThisRecordLength)
132
133 static size_t NameLenFromRecordLen (unsigned char len)
134 {
135 size_t xtra = offsetof (PortableStyleRunData_Version5, fFontName);
136 Assert (len > xtra);
137 return static_cast<unsigned char> (len - xtra);
138 }
139 static unsigned char RecordLenFromNameLen (size_t len)
140 {
141 if (len > 255) {
142 Execution::Throw (Execution::Exception{L"RecordLenFromNameLen too long"sv});
143 }
144 size_t xtra = offsetof (PortableStyleRunData_Version5, fFontName);
145 return (unsigned char)min (xtra + size_t (len), size_t (256));
146 }
147};
148inline void _DO_ALIGN_ASSERTS_Version5_ ()
149{
150 Assert (offsetof (PortableStyleRunData_Version5, fThisRecordLength) == 0);
151 Assert (offsetof (PortableStyleRunData_Version5, fStyleSet) == 1);
152 Assert (offsetof (PortableStyleRunData_Version5, fPointSize) == 2);
153 Assert (offsetof (PortableStyleRunData_Version5, fLength) == 4);
154 Assert (offsetof (PortableStyleRunData_Version5, fFontName) == 8);
155 //Assert (offsetof (PortableStyleRunData_Version5, fFontName) == 16);
156 //Assert (sizeof (PortableStyleRunData_Version5) == 16+256);
157 Assert (sizeof (PortableStyleRunData_Version5) == 8 + 256);
158}
159inline PortableStyleRunData_Version5 mkPortableStyleRunData_Version5 (const StyledInfoSummaryRecord& isr)
160{
161 _DO_ALIGN_ASSERTS_Version5_ ();
162 PortableStyleRunData_Version5 data;
163 (void)::memset (&data, 0, sizeof data); // A nice feature of data file formats, is that if you make no changes to the content
164 // and write it again, they 'diff' equal. Even though this memset isn't needed for
165 // accuracy, its nice todo anyhow, and pretty cheap - LGP 960531
166 string fontName = String::FromSDKString (isr.GetFontName ()).AsNarrowSDKString ();
167 Characters::CString::Copy (data.fFontName, Memory::NEltsOf (data.fFontName), fontName.c_str ());
168 data.fThisRecordLength = data.RecordLenFromNameLen (fontName.length ());
169
170 data.fStyleSet |= isr.GetStyle_Bold () ? (1 << data.eBold) : 0;
171 data.fStyleSet |= isr.GetStyle_Italic () ? (1 << data.eItalic) : 0;
172 data.fStyleSet |= isr.GetStyle_Underline () ? (1 << data.eUnderline) : 0;
173// note these set to zero in memset above so no need to set on other platforms
174#if qStroika_Foundation_Common_Platform_Windows
175 data.fStyleSet |= isr.GetStyle_Strikeout () ? (1 << data.eStrikeout) : 0;
176#endif
177 UInt16ToBuf (isr.GetPointSize (), &data.fPointSize);
178 SizeTToBuf (isr.fLength, &data.fLength);
179 return data;
180}
181inline StyledInfoSummaryRecord mkInfoSummaryRecordFromPortData (const PortableStyleRunData_Version5& srcData)
182{
183 _DO_ALIGN_ASSERTS_Version5_ ();
185 fsp.SetStyle_Plain (); // so all style valid bits set...
186 fsp.SetStyle_Bold (!!(srcData.fStyleSet & (1 << srcData.eBold)));
187 fsp.SetStyle_Italic (!!(srcData.fStyleSet & (1 << srcData.eItalic)));
188 fsp.SetStyle_Underline (!!(srcData.fStyleSet & (1 << srcData.eUnderline)));
189#if qStroika_Foundation_Common_Platform_Windows
190 fsp.SetStyle_Strikeout (!!(srcData.fStyleSet & (1 << srcData.eStrikeout)));
191#endif
192 short fontSize = BufToUInt16 (&srcData.fPointSize);
193 if (fontSize < 3 or fontSize > 128) {
194 fontSize = 12; // a reasonable default for bogus/corrupt data
195 }
196 fsp.SetPointSize (fontSize);
197 {
198 size_t fontNameLen = srcData.NameLenFromRecordLen (srcData.fThisRecordLength);
199 fsp.SetFontName (String::FromNarrowSDKString (string{srcData.fFontName, fontNameLen}).AsSDKString ());
200 }
201 return (StyledInfoSummaryRecord (fsp, BufToUInt32 (&srcData.fLength)));
202}
203
204/*
205 * Version 6 font style info file format.
206 * <<Introduced for Led 2.2a2, 970616 - to add color & sub/superscript support>>
207 */
208struct PortableStyleRunData_Version6 {
209 unsigned char fThisRecordLength; // nbytes in this record - cuz fFontName len is variable...
210 // note use of a char here effectively limits us to only font names of
211 // a little over 200, but that should be more than enuf (limit on PC is 32,
212 // and I don't know on mac).
213
214 // Really wanted to just use bit-fields for these guys, but that is non-portable.
215 // Sigh... I did that first and the code looks much more readable without all those
216 // damned shifts and ors etc... LGP 960531
217 enum BitFlagStyles {
218 eBold = 0,
219 eItalic,
220 eUnderline,
221 eOutline,
222 eShadow,
223 eCondensed,
224 eExtended,
225 eStrikeout,
226 eSubscript,
227 eSuperscript
228 };
229 uint16_t fStyleSet; // or in BitFlagStyles
230 uint16_t fPointSize; // note this size is a POINT-SIZE (not the tmHeight)
231 uint16_t fColor[3];
232 uint32_t fLength;
233 char fFontName[256]; // to file, we really only write as many bytes as needed (no NUL char TERM - infer size from fThisRecordLength)
234
235 static size_t NameLenFromRecordLen (unsigned char len)
236 {
237 size_t xtra = offsetof (PortableStyleRunData_Version6, fFontName);
238 Assert (len > xtra);
239 return static_cast<unsigned char> (len - xtra);
240 }
241 static unsigned char RecordLenFromNameLen (size_t len)
242 {
243 if (len > 255) {
244 Execution::Throw (Execution::Exception{L"RecordLenFromNameLen too long"sv});
245 }
246 size_t xtra = offsetof (PortableStyleRunData_Version6, fFontName);
247 return (unsigned char)min (xtra + size_t (len), size_t (256));
248 }
249};
250inline void _DO_ALIGN_ASSERTS_Version6_ ()
251{
252 Assert (offsetof (PortableStyleRunData_Version6, fThisRecordLength) == 0);
253 Assert (offsetof (PortableStyleRunData_Version6, fStyleSet) == 2);
254 Assert (offsetof (PortableStyleRunData_Version6, fPointSize) == 4);
255 Assert (offsetof (PortableStyleRunData_Version6, fColor) == 6);
256 Assert (offsetof (PortableStyleRunData_Version6, fLength) == 12);
257 Assert (offsetof (PortableStyleRunData_Version6, fFontName) == 16);
258 Assert (sizeof (PortableStyleRunData_Version6) == 16 + 256);
259}
260inline PortableStyleRunData_Version6 mkPortableStyleRunData_Version6 (const StyledInfoSummaryRecord& isr)
261{
262 _DO_ALIGN_ASSERTS_Version6_ ();
263 PortableStyleRunData_Version6 data;
264 (void)::memset (&data, 0, sizeof data); // A nice feature of data file formats, is that if you make no changes to the content
265 // and write it again, they 'diff' equal. Even though this memset isn't needed for
266 // accuracy, its nice todo anyhow, and pretty cheap - LGP 960531
267 string fontName = String::FromSDKString (isr.GetFontName ()).AsNarrowSDKString ();
268 Characters::CString::Copy (data.fFontName, Memory::NEltsOf (data.fFontName), fontName.c_str ());
269 data.fThisRecordLength = data.RecordLenFromNameLen (fontName.length ());
270
271 data.fStyleSet |= isr.GetStyle_Bold () ? (1 << data.eBold) : 0;
272 data.fStyleSet |= isr.GetStyle_Italic () ? (1 << data.eItalic) : 0;
273 data.fStyleSet |= isr.GetStyle_Underline () ? (1 << data.eUnderline) : 0;
274// note these set to zero in memset above so no need to set on other platforms
275#if qStroika_Foundation_Common_Platform_Windows
276 data.fStyleSet |= isr.GetStyle_Strikeout () ? (1 << data.eStrikeout) : 0;
277#endif
278 data.fStyleSet |= (isr.GetStyle_SubOrSuperScript () == FontSpecification::eSubscript) ? (1 << data.eSubscript) : 0;
279 data.fStyleSet |= (isr.GetStyle_SubOrSuperScript () == FontSpecification::eSuperscript) ? (1 << data.eSuperscript) : 0;
280 UInt16ToBuf (isr.GetTextColor ().GetRed (), &data.fColor[0]);
281 UInt16ToBuf (isr.GetTextColor ().GetGreen (), &data.fColor[1]);
282 UInt16ToBuf (isr.GetTextColor ().GetBlue (), &data.fColor[2]);
283 UInt16ToBuf (isr.GetPointSize (), &data.fPointSize);
284 SizeTToBuf (isr.fLength, &data.fLength);
285 return data;
286}
287inline StyledInfoSummaryRecord mkInfoSummaryRecordFromPortData (const PortableStyleRunData_Version6& srcData)
288{
289 _DO_ALIGN_ASSERTS_Version6_ ();
291 fsp.SetStyle_Plain (); // so all style valid bits set...
292 fsp.SetStyle_Bold (!!(srcData.fStyleSet & (1 << srcData.eBold)));
293 fsp.SetStyle_Italic (!!(srcData.fStyleSet & (1 << srcData.eItalic)));
294 fsp.SetStyle_Underline (!!(srcData.fStyleSet & (1 << srcData.eUnderline)));
295#if qStroika_Foundation_Common_Platform_Windows
296 fsp.SetStyle_Strikeout (!!(srcData.fStyleSet & (1 << srcData.eStrikeout)));
297#endif
298 if (srcData.fStyleSet & (1 << srcData.eSubscript)) {
299 fsp.SetStyle_SubOrSuperScript (FontSpecification::eSubscript);
300 }
301 if (srcData.fStyleSet & (1 << srcData.eSuperscript)) {
302 fsp.SetStyle_SubOrSuperScript (FontSpecification::eSuperscript);
303 }
304
305 fsp.SetTextColor (Color (BufToUInt16 (&srcData.fColor[0]), BufToUInt16 (&srcData.fColor[1]), BufToUInt16 (&srcData.fColor[2])));
306
307 short fontSize = BufToUInt16 (&srcData.fPointSize);
308 if (fontSize < 3 or fontSize > 128) {
309 fontSize = 12; // a reasonable default for bogus/corrupt data
310 }
311 fsp.SetPointSize (fontSize);
312 {
313 size_t fontNameLen = srcData.NameLenFromRecordLen (srcData.fThisRecordLength);
314 fsp.SetFontName (String::FromNarrowSDKString (string{srcData.fFontName, fontNameLen}).AsSDKString ());
315 }
316 return (StyledInfoSummaryRecord (fsp, BufToUInt32 (&srcData.fLength)));
317}
318
319/*
320 ********************************************************************************
321 ******************* StyledTextIOReader_LedNativeFileFormat *********************
322 ********************************************************************************
323 */
324StyledTextIOReader_LedNativeFileFormat::StyledTextIOReader_LedNativeFileFormat (SrcStream* srcStream, SinkStream* sinkStream)
325 : StyledTextIOReader (srcStream, sinkStream)
326{
327}
328
329void StyledTextIOReader_LedNativeFileFormat::Read ()
330{
331 _DO_ALIGN_ASSERTS_Version4_ (); // check here for all versions, just so we don't wait too long to discover
332 _DO_ALIGN_ASSERTS_Version5_ (); // a problem reading old files (I don't really test that).
333
334 LedFormatMagicCookie cookie;
335 if (GetSrcStream ().read (cookie, sizeof (cookie)) != sizeof (cookie)) {
336 Execution::Throw (DataExchange::BadFormatException::kThe);
337 }
338 bool isVersion4 = memcmp (cookie, kLedPartFormatVersion_4_MagicNumber, sizeof (kLedPartFormatVersion_4_MagicNumber)) == 0;
339 bool isVersion5 = memcmp (cookie, kLedPartFormatVersion_5_MagicNumber, sizeof (kLedPartFormatVersion_5_MagicNumber)) == 0;
340 bool isVersion6 = memcmp (cookie, kLedPartFormatVersion_6_MagicNumber, sizeof (kLedPartFormatVersion_6_MagicNumber)) == 0;
341 bool isFormatGood = isVersion4 or isVersion5 or isVersion6;
342 if (not isFormatGood) {
343 Execution::Throw (DataExchange::BadFormatException::kThe);
344 }
345 if (isVersion4) {
346 Read_Version4 (cookie);
347 }
348 if (isVersion5) {
349 Read_Version5 (cookie);
350 }
351 if (isVersion6) {
352 Read_Version6 (cookie);
353 }
354 GetSinkStream ().EndOfBuffer ();
355}
356
357bool StyledTextIOReader_LedNativeFileFormat::QuickLookAppearsToBeRightFormat ()
358{
359 SrcStreamSeekSaver savePos (GetSrcStream ());
360
361 LedFormatMagicCookie cookie;
362 if (GetSrcStream ().read (cookie, sizeof (cookie)) != sizeof (cookie)) {
363 return false;
364 }
365 bool isVersion4 = memcmp (cookie, kLedPartFormatVersion_4_MagicNumber, sizeof (kLedPartFormatVersion_4_MagicNumber)) == 0;
366 bool isVersion5 = memcmp (cookie, kLedPartFormatVersion_5_MagicNumber, sizeof (kLedPartFormatVersion_5_MagicNumber)) == 0;
367 bool isVersion6 = memcmp (cookie, kLedPartFormatVersion_6_MagicNumber, sizeof (kLedPartFormatVersion_6_MagicNumber)) == 0;
368 bool isFormatGood = isVersion4 or isVersion5 or isVersion6;
369 return isFormatGood;
370}
371
372void StyledTextIOReader_LedNativeFileFormat::Read_Version4 (const char* cookie)
373{
374 // quickie text read implementation - could loop, but turns out multiple calls to AppendText
375 // is slow (at least under OpenDoc - see why later and see if we can do piecewise read,
376 // so we don't need large contiguos buffer. This is OK though ...
377 size_t totalTextLength = 0;
378 {
379 uint32_t lenAsBuf;
380 if (GetSrcStream ().read (&lenAsBuf, sizeof (lenAsBuf)) != sizeof (lenAsBuf)) {
381 Execution::Throw (DataExchange::BadFormatException::kThe);
382 }
383 totalTextLength = BufToUInt32 (&lenAsBuf);
384 }
385 const size_t kMaxBufSize = 64 * 1024 * 1024; // not a REAL limit - just a sanity check input data not garbage,
386 // and so we don't get errors from our malloc for asking for huge number
387 // (OpenDoc 1.0 does this).
388 if (totalTextLength > kMaxBufSize) {
389 Execution::Throw (DataExchange::BadFormatException::kThe);
390 }
391 {
392 StackBuffer<char> buf{Memory::eUninitialized, totalTextLength};
393 if (GetSrcStream ().read (buf.data (), totalTextLength) != totalTextLength) {
394 Execution::Throw (DataExchange::BadFormatException::kThe);
395 }
396 size_t nChars = totalTextLength;
397 StackBuffer<Led_tChar> unicodeText{Memory::eUninitialized, nChars};
398#if qStroika_Foundation_Common_Platform_Windows
399 auto text = CodeCvt<Led_tChar>{static_cast<CodePage> (CP_ACP)}.Bytes2Characters (as_bytes (span{buf}), span{unicodeText});
400#else
401 auto text = CodeCvt<Led_tChar>{locale{}}.Bytes2Characters (as_bytes (span{buf}), span{unicodeText});
402#endif
403 GetSinkStream ().AppendText (text.data (), text.size (), nullptr);
404 }
405
406 // Read Style Runs
407 {
408 size_t nStyleRuns = InputStandardFromSrcStream_ULONG (GetSrcStream ());
409 if (nStyleRuns > totalTextLength + 1) {
410 Execution::Throw (DataExchange::BadFormatException::kThe);
411 }
412 vector<StyledInfoSummaryRecord> styleRunInfo;
413 {
414 StackBuffer<PortableStyleRunData_Version4> portableStyleRuns{Memory::eUninitialized, nStyleRuns};
415 if (GetSrcStream ().read (portableStyleRuns.data (), nStyleRuns * sizeof (PortableStyleRunData_Version4)) !=
416 nStyleRuns * sizeof (PortableStyleRunData_Version4)) {
417 Execution::Throw (DataExchange::BadFormatException::kThe);
418 }
419 for (size_t i = 0; i < nStyleRuns; ++i) {
420 styleRunInfo.push_back (mkInfoSummaryRecordFromPortData (portableStyleRuns[i]));
421 }
422 }
423 GetSinkStream ().ApplyStyle (0, totalTextLength, styleRunInfo);
424 }
425
426 // Read Embeddings
427#if qStroika_Frameworks_Led_SupportGDI
428 {
429 size_t nEmbeddings = InputStandardFromSrcStream_ULONG (GetSrcStream ());
430 if (nEmbeddings > totalTextLength) { // sanity check
431 Execution::Throw (DataExchange::BadFormatException::kThe);
432 }
433
434 for (size_t i = 0; i < nEmbeddings; ++i) {
435 size_t whereAt = InputStandardFromSrcStream_ULONG (GetSrcStream ());
436 size_t howManyBytes = InputStandardFromSrcStream_ULONG (GetSrcStream ());
437
438 size_t justBeforeEmbedding = GetSrcStream ().current_offset ();
439
440 Led_PrivateEmbeddingTag tag;
441 if (howManyBytes < sizeof (tag)) {
442 Execution::Throw (DataExchange::BadFormatException::kThe);
443 }
444 if (GetSrcStream ().read (tag, sizeof (tag)) != sizeof (tag)) {
445 Execution::Throw (DataExchange::BadFormatException::kThe);
446 }
447 SimpleEmbeddedObjectStyleMarker* embedding = InternalizeEmbedding (tag, howManyBytes - sizeof (tag));
448 if (embedding == NULL) {
449 // something unknown. In the future, we will cons up a special embedding for unknown
450 // items so they will appear in the text, and still get written back out later correctly.
451 // But SKIPPING THEM will have todo for now...
452 GetSrcStream ().seek_to (justBeforeEmbedding + howManyBytes);
453 }
454 else {
455 GetSinkStream ().InsertEmbeddingForExistingSentinel (embedding, whereAt);
456 }
457 }
458 }
459#endif
460
461 // check for extra cookie at the end...
462 LedFormatMagicCookie endCookie;
463 if (GetSrcStream ().read (endCookie, sizeof (endCookie)) != sizeof (endCookie)) {
464 Execution::Throw (DataExchange::BadFormatException::kThe);
465 }
466 if (memcmp (endCookie, cookie, sizeof (endCookie)) != 0) {
467 // maybe should warn - maybe an error? Who knows - we've read so much - its a shame to fail to open
468 // just cuz of this...
469 Execution::Throw (DataExchange::BadFormatException::kThe);
470 }
471}
472
473void StyledTextIOReader_LedNativeFileFormat::Read_Version5 (const char* cookie)
474{
475 // quickie text read implementation - could loop, but turns out multiple calls to AppendText
476 // is slow (at least under OpenDoc - see why later and see if we can do piecewise read,
477 // so we don't need large contiguos buffer. This is OK though ...
478 size_t totalTextLength = 0;
479 {
480 uint32_t lenAsBuf;
481 if (GetSrcStream ().read (&lenAsBuf, sizeof (lenAsBuf)) != sizeof (lenAsBuf)) {
482 Execution::Throw (DataExchange::BadFormatException::kThe);
483 }
484 totalTextLength = BufToUInt32 (&lenAsBuf);
485 }
486 const size_t kMaxBufSize = 64 * 1024 * 1024; // not a REAL limit - just a sanity check input data not garbage,
487 // and so we don't get errors from our malloc for asking for huge number
488 // (OpenDoc 1.0 does this).
489 if (totalTextLength > kMaxBufSize) {
490 Execution::Throw (DataExchange::BadFormatException::kThe);
491 }
492 {
493 StackBuffer<char> buf{Memory::eUninitialized, totalTextLength};
494 if (GetSrcStream ().read (buf.data (), totalTextLength) != totalTextLength) {
495 Execution::Throw (DataExchange::BadFormatException::kThe);
496 }
497 size_t nChars = totalTextLength;
498 StackBuffer<Led_tChar> unicodeText{Memory::eUninitialized, nChars};
499#if qStroika_Foundation_Common_Platform_Windows
500 auto text = CodeCvt<Led_tChar>{static_cast<CodePage> (CP_ACP)}.Bytes2Characters (as_bytes (span{buf}), span{unicodeText});
501#else
502 auto text = CodeCvt<Led_tChar>{locale{}}.Bytes2Characters (as_bytes (span{buf}), span{unicodeText});
503#endif
504 GetSinkStream ().AppendText (text.data (), text.size (), nullptr);
505 }
506
507 // Read Style Runs
508 {
509 size_t howManyBytesOfStyleInfo = InputStandardFromSrcStream_ULONG (GetSrcStream ());
510 StackBuffer<char> portableStyleRunsBuffer{Memory::eUninitialized, howManyBytesOfStyleInfo};
511 if (GetSrcStream ().read (portableStyleRunsBuffer.data (), howManyBytesOfStyleInfo) != howManyBytesOfStyleInfo) {
512 Execution::Throw (DataExchange::BadFormatException::kThe);
513 }
514
515 size_t offsetInText = 0;
516 size_t i = 0;
517 for (; i < howManyBytesOfStyleInfo;) {
518 const PortableStyleRunData_Version5* thisBucket = (PortableStyleRunData_Version5*)(((char*)portableStyleRunsBuffer) + i);
519 if (i + thisBucket->fThisRecordLength > howManyBytesOfStyleInfo) {
520 Execution::Throw (DataExchange::BadFormatException::kThe);
521 }
522 StyledInfoSummaryRecord isr = mkInfoSummaryRecordFromPortData (*thisBucket);
523
524 vector<StyledInfoSummaryRecord> list;
525 list.push_back (isr);
526 GetSinkStream ().ApplyStyle (offsetInText, offsetInText + isr.fLength, list); // silly this API REQUIRES a list...
527
528 offsetInText += isr.fLength;
529 i += thisBucket->fThisRecordLength;
530 }
531 if (i != howManyBytesOfStyleInfo) {
532 Execution::Throw (DataExchange::BadFormatException::kThe);
533 }
534 }
535
536#if qStroika_Frameworks_Led_SupportGDI
537 // Read Embeddings
538 {
539 size_t nEmbeddings = InputStandardFromSrcStream_ULONG (GetSrcStream ());
540 if (nEmbeddings > totalTextLength) { // sanity check
541 Execution::Throw (DataExchange::BadFormatException::kThe);
542 }
543
544 for (size_t i = 0; i < nEmbeddings; ++i) {
545 size_t whereAt = InputStandardFromSrcStream_ULONG (GetSrcStream ()) - 1;
546 size_t howManyBytes = InputStandardFromSrcStream_ULONG (GetSrcStream ());
547
548 size_t justBeforeEmbedding = GetSrcStream ().current_offset ();
549
550 Led_PrivateEmbeddingTag tag;
551 if (howManyBytes < sizeof (tag)) {
552 Execution::Throw (DataExchange::BadFormatException::kThe);
553 }
554 if (GetSrcStream ().read (tag, sizeof (tag)) != sizeof (tag)) {
555 Execution::Throw (DataExchange::BadFormatException::kThe);
556 }
557 SimpleEmbeddedObjectStyleMarker* embedding = InternalizeEmbedding (tag, howManyBytes - sizeof (tag));
558 if (embedding == nullptr) {
559 // something unknown. In the future, we will cons up a special embedding for unknown
560 // items so they will appear in the text, and still get written back out later correctly.
561 // But SKIPPING THEM will have todo for now...
562 GetSrcStream ().seek_to (justBeforeEmbedding + howManyBytes);
563 }
564 else {
565 GetSinkStream ().InsertEmbeddingForExistingSentinel (embedding, whereAt);
566 }
567 }
568 }
569#endif
570
571 // check for extra cookie at the end...
572 LedFormatMagicCookie endCookie;
573 if (GetSrcStream ().read (endCookie, sizeof (endCookie)) != sizeof (endCookie)) {
574 Execution::Throw (DataExchange::BadFormatException::kThe);
575 }
576 if (memcmp (endCookie, cookie, sizeof (endCookie)) != 0) {
577 // maybe should warn - maybe an error? Who knows - we've read so much - its a shame to fail to open
578 // just cuz of this...
579 Execution::Throw (DataExchange::BadFormatException::kThe);
580 }
581}
582
583void StyledTextIOReader_LedNativeFileFormat::Read_Version6 (const char* cookie)
584{
585 // quickie text read implementation - could loop, but turns out multiple calls to AppendText
586 // is slow (at least under OpenDoc - see why later and see if we can do piecewise read,
587 // so we don't need large contiguos buffer. This is OK though ...
588 size_t totalTextLength = 0;
589 {
590 uint32_t lenAsBuf;
591 if (GetSrcStream ().read (&lenAsBuf, sizeof (lenAsBuf)) != sizeof (lenAsBuf)) {
592 Execution::Throw (DataExchange::BadFormatException::kThe);
593 }
594 totalTextLength = BufToUInt32 (&lenAsBuf);
595 }
596 const size_t kMaxBufSize = 64 * 1024 * 1024; // not a REAL limit - just a sanity check input data not garbage,
597 // and so we don't get errors from our malloc for asking for huge number
598 // (OpenDoc 1.0 does this).
599 if (totalTextLength > kMaxBufSize) {
600 Execution::Throw (DataExchange::BadFormatException::kThe);
601 }
602 {
603 StackBuffer<char> buf{Memory::eUninitialized, totalTextLength};
604 if (GetSrcStream ().read (buf.data (), totalTextLength) != totalTextLength) {
605 Execution::Throw (DataExchange::BadFormatException::kThe);
606 }
607 size_t nChars = totalTextLength;
608 StackBuffer<Led_tChar> unicodeText{Memory::eUninitialized, nChars};
609#if qStroika_Foundation_Common_Platform_Windows
610 auto text = CodeCvt<Led_tChar>{static_cast<CodePage> (CP_ACP)}.Bytes2Characters (as_bytes (span{buf}), span{unicodeText});
611#else
612 auto text = CodeCvt<Led_tChar>{locale{}}.Bytes2Characters (as_bytes (span{buf}), span{unicodeText});
613#endif
614 GetSinkStream ().AppendText (text.data (), text.size (), nullptr);
615 }
616
617 // Read Style Runs
618 {
619 size_t howManyBytesOfStyleInfo = InputStandardFromSrcStream_ULONG (GetSrcStream ());
620 StackBuffer<char> portableStyleRunsBuffer{Memory::eUninitialized, howManyBytesOfStyleInfo};
621 if (GetSrcStream ().read (portableStyleRunsBuffer.data (), howManyBytesOfStyleInfo) != howManyBytesOfStyleInfo) {
622 Execution::Throw (DataExchange::BadFormatException::kThe);
623 }
624
625 size_t offsetInText = 0;
626 size_t i = 0;
627 for (; i < howManyBytesOfStyleInfo;) {
628 const PortableStyleRunData_Version6* thisBucket = (PortableStyleRunData_Version6*)(((char*)portableStyleRunsBuffer) + i);
629 if (i + thisBucket->fThisRecordLength > howManyBytesOfStyleInfo) {
630 Execution::Throw (DataExchange::BadFormatException::kThe);
631 }
632 StyledInfoSummaryRecord isr = mkInfoSummaryRecordFromPortData (*thisBucket);
633
634 vector<StyledInfoSummaryRecord> list;
635 list.push_back (isr);
636 GetSinkStream ().ApplyStyle (offsetInText, offsetInText + isr.fLength, list); // silly this API REQUIRES a list...
637
638 offsetInText += isr.fLength;
639 i += thisBucket->fThisRecordLength;
640 }
641 if (i != howManyBytesOfStyleInfo) {
642 Execution::Throw (DataExchange::BadFormatException::kThe);
643 }
644 }
645
646#if qStroika_Frameworks_Led_SupportGDI
647 // Read Embeddings
648 {
649 size_t nEmbeddings = InputStandardFromSrcStream_ULONG (GetSrcStream ());
650 if (nEmbeddings > totalTextLength) { // sanity check
651 Execution::Throw (DataExchange::BadFormatException::kThe);
652 }
653
654 for (size_t i = 0; i < nEmbeddings; ++i) {
655 size_t whereAt = InputStandardFromSrcStream_ULONG (GetSrcStream ());
656 size_t howManyBytes = InputStandardFromSrcStream_ULONG (GetSrcStream ());
657
658 size_t justBeforeEmbedding = GetSrcStream ().current_offset ();
659
660 Led_PrivateEmbeddingTag tag;
661 if (howManyBytes < sizeof (tag)) {
662 Execution::Throw (DataExchange::BadFormatException::kThe);
663 }
664 if (GetSrcStream ().read (tag, sizeof (tag)) != sizeof (tag)) {
665 Execution::Throw (DataExchange::BadFormatException::kThe);
666 }
667 SimpleEmbeddedObjectStyleMarker* embedding = InternalizeEmbedding (tag, howManyBytes - sizeof (tag));
668 if (embedding == NULL) {
669 // something unknown. In the future, we will cons up a special embedding for unknown
670 // items so they will appear in the text, and still get written back out later correctly.
671 // But SKIPPING THEM will have todo for now...
672 GetSrcStream ().seek_to (justBeforeEmbedding + howManyBytes);
673 }
674 else {
675 GetSinkStream ().InsertEmbeddingForExistingSentinel (embedding, whereAt);
676 }
677 }
678 }
679#endif
680
681 // check for extra cookie at the end...
682 LedFormatMagicCookie endCookie;
683 if (GetSrcStream ().read (endCookie, sizeof (endCookie)) != sizeof (endCookie)) {
684 Execution::Throw (DataExchange::BadFormatException::kThe);
685 }
686 if (memcmp (endCookie, cookie, sizeof (endCookie)) != 0) {
687 // maybe should warn - maybe an error? Who knows - we've read so much - its a shame to fail to open
688 // just cuz of this...
689 Execution::Throw (DataExchange::BadFormatException::kThe);
690 }
691}
692
693#if qStroika_Frameworks_Led_SupportGDI
694SimpleEmbeddedObjectStyleMarker* StyledTextIOReader_LedNativeFileFormat::InternalizeEmbedding (Led_PrivateEmbeddingTag tag, size_t howManyBytes)
695{
696 StackBuffer<char> dataBuf{Memory::eUninitialized, howManyBytes};
697 if (GetSrcStream ().read (dataBuf.data (), howManyBytes) != howManyBytes) {
698 Execution::Throw (DataExchange::BadFormatException::kThe);
699 }
700
701 const vector<EmbeddedObjectCreatorRegistry::Assoc>& types = EmbeddedObjectCreatorRegistry::Get ().GetAssocList ();
702 for (size_t i = 0; i < types.size (); ++i) {
703 EmbeddedObjectCreatorRegistry::Assoc assoc = types[i];
704 if (memcmp (assoc.fEmbeddingTag, tag, sizeof (assoc.fEmbeddingTag)) == 0) {
705 AssertNotNull (assoc.fReadFromMemory);
706 return (assoc.fReadFromMemory) (tag, dataBuf.data (), howManyBytes);
707 }
708 }
709 return new StandardUnknownTypeStyleMarker{0, tag, dataBuf.data (), howManyBytes};
710}
711#endif
712
713/*
714 ********************************************************************************
715 ******************* StyledTextIOWriter_LedNativeFileFormat *********************
716 ********************************************************************************
717 */
718StyledTextIOWriter_LedNativeFileFormat::StyledTextIOWriter_LedNativeFileFormat (SrcStream* srcStream, SinkStream* sinkStream)
719 : StyledTextIOWriter (srcStream, sinkStream)
720{
721}
722
723void StyledTextIOWriter_LedNativeFileFormat::Write ()
724{
725 Write_Version6 ();
726}
727
728void StyledTextIOWriter_LedNativeFileFormat::Write_Version6 ()
729{
730 // Write a magic cookie to identify this format
731 write (kLedPartFormatVersion_6_MagicNumber, sizeof (kLedPartFormatVersion_6_MagicNumber));
732
733 size_t totalTextLength = GetSrcStream ().GetTotalTextLength ();
734 {
735 // write a length-of-text count, and then the text
736 StackBuffer<Led_tChar> buf{Memory::eUninitialized, totalTextLength};
737 if (GetSrcStream ().readNTChars (buf.data (), totalTextLength) != totalTextLength) {
738 Execution::Throw (DataExchange::BadFormatException::kThe);
739 }
740 size_t nChars = totalTextLength * sizeof (wchar_t);
741 StackBuffer<char> result{Memory::eUninitialized, nChars};
742#if qStroika_Foundation_Common_Platform_Windows
743 nChars = CodeCvt<Led_tChar>{static_cast<CodePage> (CP_ACP)}.Characters2Bytes (span{buf}, as_writable_bytes (span{result})).size ();
744#else
745 nChars = CodeCvt<Led_tChar>{locale{}}.Characters2Bytes (span{buf}, as_writable_bytes (span{result})).size ();
746#endif
747 {
748 uint32_t encodedTL = 0;
749 SizeTToBuf (nChars, &encodedTL);
750 write (&encodedTL, sizeof (encodedTL));
751 }
752 write (result.data (), nChars);
753 }
754
755 // Write the style runs
756 {
757 vector<StyledInfoSummaryRecord> styleRunInfo = GetSrcStream ().GetStyleInfo (0, totalTextLength);
758
759 size_t howManyBytes = 0; // write place-holder, than then come back and patch this!
760 size_t styleRunInfoSectionCursor = GetSinkStream ().current_offset ();
761 OutputStandardToSinkStream_size_t_ (GetSinkStream (), howManyBytes);
762
763 size_t styleRuns = styleRunInfo.size ();
764 for (size_t i = 0; i < styleRuns; ++i) {
765 PortableStyleRunData_Version6 data = mkPortableStyleRunData_Version6 (styleRunInfo[i]);
766 write (&data, data.fThisRecordLength);
767 }
768
769 // Here we back-patch the new size of the embedded object, so we can read-over these things
770 // without knowing about the object type (exp cross-platform).
771 size_t here = GetSinkStream ().current_offset ();
772 howManyBytes = here - (styleRunInfoSectionCursor + 4); //+4 cuz don't count size marker length itself.
773 GetSinkStream ().seek_to (styleRunInfoSectionCursor);
774 OutputStandardToSinkStream_size_t_ (GetSinkStream (), howManyBytes);
775 GetSinkStream ().seek_to (here); // back to where we left off...
776 }
777
778 // Write the embedded objects
779#if qStroika_Frameworks_Led_SupportGDI
780 {
781 vector<SimpleEmbeddedObjectStyleMarker*> embeddings = GetSrcStream ().CollectAllEmbeddingMarkersInRange (0, totalTextLength);
782 OutputStandardToSinkStream_size_t_ (GetSinkStream (), embeddings.size ());
783 size_t markerPosOffset = GetSrcStream ().GetEmbeddingMarkerPosOffset ();
784 for (size_t i = 0; i < embeddings.size (); ++i) {
785 SimpleEmbeddedObjectStyleMarker* embedding = embeddings[i];
786 // Write where embedding is located in text, relative to beginning of text (being internalized/extenralized)
787 size_t whereAt = embedding->GetStart () - markerPosOffset;
788 OutputStandardToSinkStream_size_t_ (GetSinkStream (), whereAt);
789
790 // Note: this howManyBytes refers to the content-type specific portion.
791 size_t howManyBytes = 0; // write place-holder, than then come back and patch this!
792 size_t embeddingSizeCursor = GetSinkStream ().current_offset ();
793 OutputStandardToSinkStream_size_t_ (GetSinkStream (), howManyBytes);
794
795 size_t embeddingCursor = GetSinkStream ().current_offset ();
796 write (embedding->GetTag (), sizeof (Led_PrivateEmbeddingTag));
797 ExternalizeEmbedding (embedding);
798
799 // Here we back-patch the new size of the embedded object, so we can read-over these things
800 // without knowing about the object type (exp cross-platform).
801 size_t here = GetSinkStream ().current_offset ();
802 howManyBytes = here - embeddingCursor;
803 GetSinkStream ().seek_to (embeddingSizeCursor);
804 OutputStandardToSinkStream_size_t_ (GetSinkStream (), howManyBytes);
805 GetSinkStream ().seek_to (here); // back to where we left off...
806 }
807 }
808#endif
809
810 // Write a magic cookie - just as a validation/sanity check on the format
811 write (kLedPartFormatVersion_6_MagicNumber, sizeof (kLedPartFormatVersion_6_MagicNumber));
812}
813
814#if qStroika_Frameworks_Led_SupportGDI
815void StyledTextIOWriter_LedNativeFileFormat::ExternalizeEmbedding (SimpleEmbeddedObjectStyleMarker* embedding)
816{
817 EmbeddingSinkStream embeddingSinkStream (GetSinkStream ());
818 embedding->Write (embeddingSinkStream);
819}
820#endif
#define AssertNotNull(p)
Definition Assertions.h:333
CodeCvt unifies byte <-> unicode conversions, vaguely inspired by (and wraps) std::codecvt,...
Definition CodeCvt.h:118
nonvirtual size_t Bytes2Characters(span< const byte > from) const
convert span byte (external serialized format) parameters to characters (like std::codecvt<>::in () -...
Definition CodeCvt.inl:750
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
Definition Exceptions.h:157
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43