Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
StyledTextIO.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Frameworks_Led_StyledTextIO_h_
5#define _Stroika_Frameworks_Led_StyledTextIO_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include <set>
10
11/*
12@MODULE: StyledTextIO
13@DESCRIPTION:
14 <p>A portable attempt at abstracting away the details of styled text file IO and all
15 the different formats for styled text.</p>
16 <p>This code defines APIs which should allow for reading any different styled text format.
17 And dumping it to any different output (eg. text buffer).</p>
18 <p>The only real LED-SPECIFIC parts of this are that I only provide concrete output (aka sinks)
19 implementations to Led StandardStyledTextImagers. And that some of the Src/Sink APIs are oriented towards what would be
20 helpful for a Led-based editor (in other words, features not supported by Led aren't communicated to/from the src/sinks).</p>
21 <p>The <em>big picture</em> for this module is that there are two main basic subdivisions. There are
22 @'StyledTextIOReader' subclasses, and @'StyledTextIOWriter' subclasses. The readers are for READING some file format,
23 and converting it to a stream of method calls (on a sink to be described later). And writers are for writing those
24 formatted data files, based on results of method calls on an abstract source class.</p>
25 <p>Though @'StyledTextIOReader' and @'StyledTextIOWriter' share no common base class, they <em>do</em> follow
26 a very similar design pattern. They both define abstract 'sources' and 'sinks' for their operation.</p>
27 <p>For a @'StyledTextIOReader', it reads its data from a @'StyledTextIOReader::SrcStream' (typically maybe a file),
28 and writes it to a @'StyledTextIOReader::SinkStream' (typically a Led-text-buffer/view).</p>
29 <p>A @'StyledTextIOWriter', writes data extracted from a @'StyledTextIOWriter::SrcStream'
30 (typically view/textstore, much like a @'StyledTextIOReader::SinkStream'),
31 and writes it to a @'StyledTextIOWriter::SinkStream' (typically an output file).</p>
32 <p>These abstract sources and sinks are defined to just the minimal pure virtual APIs needed to extract/write bare bytes,
33 or formatted text in a format Led can understand. Several concrete instantiations of each are provided by Led (some here, and
34 some in other modules, as appropriate).</p>
35 <p>Subclasses of @'StyledTextIOReader' and @'StyledTextIOWriter' are where the knowledge of particular file formats resides.
36 For example, the knowledge of how to read RTF is in @'StyledTextIOReader_RTF' and the knowledge of how to write HTML is in
37 @'StyledTextIOWriter_HTML'.</p>
38 */
39
41#include "Stroika/Foundation/Memory/Common.h"
42
43#include "Stroika/Frameworks/Led/StandardStyledTextImager.h" // For StyledInfoSummaryRecord declaration
44#include "Stroika/Frameworks/Led/StyledTextEmbeddedObjects.h"
45#include "Stroika/Frameworks/Led/StyledTextImager.h"
46#include "Stroika/Frameworks/Led/Support.h"
47
48namespace Stroika::Frameworks::Led::StyledTextIO {
49
50 /*
51 @CLASS: StyledTextIOReader
52 @DESCRIPTION: <p>Abstract base class for styled text reading. Subclasses know about various styled text file formats, and
53 take care of the details of mapping streams of bytes into Led internal data structures with that styled text.</p>
54 */
55 class StyledTextIOReader {
56 public:
57 /*
58 @DESCRIPTION: <p>A StyledTextIOReader needs a pointer to a function which is a source of raw bytes
59 to be interpretted as text which will be inserted into a text buffer. SrcStream is an abstract class
60 defining this API.</p>
61
62 \@todo THIS IS OBSOLETE - and should be switched to using Streams::InputStream<byte> --LGP 2024-02-22
63
64 */
65 class SrcStream {
66 public:
67 virtual ~SrcStream () = default;
68
69 public:
70 /*
71 @METHOD: StyledTextIOReader::SrcStream::current_offset
72 @DESCRIPTION: <p>Return the current seekPos</p>
73 */
74 virtual size_t current_offset () const = 0;
75
76 public:
77 /*
78 @METHOD: StyledTextIOReader::SrcStream::seek_to
79 @DESCRIPTION: <p>Sets the current read-pointer to the given position (often used to scan backwards, but can be any direction).
80 Note if 'to' is past end, this just pins one past end of buffer.</p>
81 */
82 virtual void seek_to (size_t to) = 0;
83
84 public:
85 /*
86 @METHOD: StyledTextIOReader::SrcStream::read
87 @DESCRIPTION: <p>Read the given number of bytes from the is source, and fill them into the buffer. Returns
88 the number of bytes read. Always return as many bytes as possible and block if they
89 are not yet available. Don't return partial reads (except on EOF). 'bytes' can be zero, and then
90 'read' will return immediately with a return value of zero.</p>
91 */
92 virtual size_t read (void* buffer, size_t bytes) = 0;
93
94 public:
95 /*
96 @METHOD: StyledTextIOReader::SrcStream::read1
97 @DESCRIPTION: <p>Read 1 character and return the number of characters read (0 or 1).
98 This is a trivial wrapper on @'StyledTextIOReader::SrcStream::read' which can be overriden
99 and is frequently used as a performance optimization.</p>
100 */
101 virtual size_t read1 (char* c)
102 {
103 return read (c, 1);
104 }
105 };
106
107 public:
108 class SinkStream;
109 /*
110 @CLASS: StyledTextIOReader::BadInputHandler
111 @DESCRIPTION:
112 <p>Abstract base class for styled text writing. Subclasses know about various styled text file formats, and
113 take care of the details of mapping Led internal data structures with styled text into streams of bytes in that format.</p>
114 */
115 class BadInputHandler {
116 public:
117 virtual ~BadInputHandler () = default;
118 virtual void HandleBadlyFormattedInput (const StyledTextIOReader& reader, bool unrecoverable);
119 };
120
121 protected:
122 StyledTextIOReader (SrcStream* srcStream, SinkStream* sinkStream,
123 const shared_ptr<BadInputHandler>& badInputHander = shared_ptr<BadInputHandler> ()); // callers responsability to destroy srcStream/sinkStream
124
125 // The Read() method must be overriden by one subclass to provide the format interpretation
126 public:
127 virtual void Read () = 0;
128 virtual bool QuickLookAppearsToBeRightFormat () = 0;
129
130 public:
131 /*
132 @CLASS: StyledTextIOReader::BufferedIndirectSrcStream
133 @BASES: @'StyledTextIOReader::SrcStream'
134 @DESCRIPTION: <p></p>
135 */
136 class BufferedIndirectSrcStream : public SrcStream {
137 public:
138 BufferedIndirectSrcStream (SrcStream& realSrcStream);
139
140 public:
141 virtual size_t current_offset () const override;
142 virtual void seek_to (size_t to) override;
143 virtual size_t read (void* buffer, size_t bytes) override;
144 virtual size_t read1 (char* c) override;
145
146 private:
147 nonvirtual void FillCache ();
148
149 private:
150 SrcStream& fRealSrcStream;
151 char fWindowTop_Data[4 * 1024]; // buffer for how much we buffer at a time...
152 size_t fWindowTop_Offset;
153 const char* fWindowBottom_Data;
154 size_t fWindowBottom_Offset;
155 const char* fCursor_Data;
156 size_t fCursor_Offset;
157 };
158
159 public:
160 nonvirtual SrcStream& GetSrcStream () const;
161 nonvirtual SinkStream& GetSinkStream () const;
162
163 private:
164 mutable BufferedIndirectSrcStream fSrcStream;
165 SinkStream* fSinkStream;
166
167 public:
168 nonvirtual shared_ptr<BadInputHandler> GetBadInputHandler () const;
169 nonvirtual void SetBadInputHandler (const shared_ptr<BadInputHandler>& badInputHandler);
170
171 private:
172 shared_ptr<BadInputHandler> fBadInputHandler;
173
174 public:
175 nonvirtual void HandleBadlyFormattedInput (bool unrecoverable = false) const;
176
177 protected:
178 class SrcStreamSeekSaver;
179
180 protected:
181 struct ReadEOFException {}; // not an error state necesserily. Just allows our Read helper routines
182 // to be simpler. Don't need to worry as much about this special case,
183 // and they can just return chars (and throw on eof).
184
185 nonvirtual void PutBackLastChar () const;
186 nonvirtual char GetNextChar () const;
187 nonvirtual char PeekNextChar () const;
188 nonvirtual void ConsumeNextChar () const;
189 nonvirtual string GrabString (size_t from, size_t to = size_t (-1)); // doesn't move seek_to () position (rather restores it)
190 // if no end specified (-1), then grab from to current seekpos
191 };
192
193 // utility to auto-scroll back to place in stream where we were created on DTOR
194 class StyledTextIOReader::SrcStreamSeekSaver {
195 public:
196 SrcStreamSeekSaver (SrcStream& srcStream);
197 ~SrcStreamSeekSaver ();
198
199 private:
200 SrcStream& fSrcStream;
201 size_t fSavedPos;
202 };
203
204 /*
205 @CLASS: StyledTextIOReader::SinkStream
206 @DESCRIPTION: <p>A StyledTextIOReader needs a pointer to a function which is a sink for all the styled text
207 and other information read. SinkStream is an abstract class defining this API.</p>
208 */
209 class StyledTextIOReader::SinkStream {
210 public:
211 virtual ~SinkStream ()
212 {
213 }
214
215 public:
216 /*
217 @METHOD: StyledTextIOReader::SinkStream::current_offset
218 @DESCRIPTION: <p>Return the current seekPos</p>
219 */
220 virtual size_t current_offset () const = 0; // current seekPos
221
222 public:
223 /*
224 @METHOD: StyledTextIOReader::SinkStream::AppendText
225 @DESCRIPTION: <p>Append the given text to the output text buffer. If fontSpec is nullptr, use default.
226 Probably later we will return and update the fontspec with @'StyledTextIOReader::SinkStream::ApplyStyle'. Note, this style
227 of API is defined cuz some format readers give us a bunch of text at once, and then later (elsewhere) store the style
228 information. And still others provide them together, hand-in-hand.</p>
229 */
230 virtual void AppendText (const Led_tChar* text, size_t nTChars, const FontSpecification* fontSpec) = 0;
231
232 public:
233 /*
234 @METHOD: StyledTextIOReader::SinkStream::ApplyStyle
235 @DESCRIPTION: <p>Apply the given style information to the given range of text. See @'StyledTextIOReader::SinkStream::AppendText'.</p>
236 */
237 virtual void ApplyStyle (size_t from, size_t to, const vector<StyledInfoSummaryRecord>& styleRuns) = 0;
238
239 public:
240 /*
241 @METHOD: StyledTextIOReader::SinkStream::GetDefaultFontSpec
242 @DESCRIPTION:
243 */
244 virtual FontSpecification GetDefaultFontSpec () const = 0;
245
246#if qStroika_Frameworks_Led_SupportGDI
247 public:
248 /*
249 @METHOD: StyledTextIOReader::SinkStream::InsertEmbeddingForExistingSentinel
250 @DESCRIPTION:
251 */
252 virtual void InsertEmbeddingForExistingSentinel (SimpleEmbeddedObjectStyleMarker* embedding, size_t at) = 0;
253
254 public:
255 /*
256 @METHOD: StyledTextIOReader::SinkStream::AppendEmbedding
257 @DESCRIPTION:
258 */
259 virtual void AppendEmbedding (SimpleEmbeddedObjectStyleMarker* embedding) = 0;
260#endif
261
262 public:
263 /*
264 @METHOD: StyledTextIOReader::SinkStream::AppendSoftLineBreak
265 @DESCRIPTION:
266 */
267 virtual void AppendSoftLineBreak () = 0;
268
269 public:
270 virtual void StartTable ();
271 virtual void EndTable ();
272 virtual void StartTableRow ();
273 virtual void EndTableRow ();
274 virtual void StartTableCell (size_t colSpan);
275 virtual void EndTableCell ();
276
277 public:
278 /*
279 @METHOD: StyledTextIOReader::SinkStream::InsertMarker
280 @DESCRIPTION:
281 */
282 virtual void InsertMarker (Marker* m, size_t at, size_t length, MarkerOwner* markerOwner) = 0;
283
284 public:
285 virtual void SetJustification (Justification justification);
286 virtual void SetStandardTabStopList (const StandardTabStopList& tabStops);
287 virtual void SetFirstIndent (TWIPS tx);
288 virtual void SetLeftMargin (TWIPS lhs);
289 virtual void SetRightMargin (TWIPS rhs);
290 virtual void SetSpaceBefore (TWIPS sb);
291 virtual void SetSpaceAfter (TWIPS sa);
292 virtual void SetLineSpacing (LineSpacing sl);
293 virtual void SetTextHidden (bool hidden);
294 virtual void SetListStyle (ListStyle listStyle);
295 virtual void SetListIndentLevel (unsigned char indentLevel);
296
297 public:
298 virtual void SetTableBorderColor (Color c);
299 virtual void SetTableBorderWidth (TWIPS bWidth);
300 virtual void SetCellWidths (const vector<TWIPS>& cellWidths);
301 virtual void SetCellBackColor (const Color c);
302 virtual void SetDefaultCellMarginsForCurrentRow (TWIPS top, TWIPS left, TWIPS bottom, TWIPS right);
303 virtual void SetDefaultCellSpacingForCurrentRow (TWIPS top, TWIPS left, TWIPS bottom, TWIPS right);
304
305 public:
306 /*
307 @METHOD: StyledTextIOReader::SinkStream::EndOfBuffer
308 @DESCRIPTION: <p>Called by StyledText IO readers when end of source buffer is encountered. This means that
309 the next Flush () call contains the last of the text.</p>
310 */
311 virtual void EndOfBuffer () {};
312
313 public:
314 /*
315 @METHOD: StyledTextIOReader::SinkStream::Flush
316 @DESCRIPTION:
317 */
318 virtual void Flush () = 0;
319
320 public:
321 nonvirtual size_t GetCountOfTCharsInserted () const;
322 };
323
324 /*
325 @CLASS: StyledTextIOWriter
326 @DESCRIPTION:
327 <p>Abstract base class for styled text writing. Subclasses know about various styled text file formats, and
328 take care of the details of mapping Led internal data structures with styled text into streams of bytes in that format.</p>
329 */
330 class StyledTextIOWriter {
331 public:
332 class SrcStream;
333 class SinkStream;
334
335 protected:
336 StyledTextIOWriter (SrcStream* srcStream, SinkStream* sinkStream); // callers responsability to destroy srcStream/sinkStream
337
338 // The Read() method must be overriden by one subclass to provide the format interpretation
339 public:
340 virtual void Write () = 0;
341
342 public:
343 nonvirtual SrcStream& GetSrcStream () const;
344 nonvirtual SinkStream& GetSinkStream () const;
345
346 private:
347 SrcStream* fSrcStream;
348 SinkStream* fSinkStream;
349
350 // Utilities
351 protected:
352 nonvirtual void write (const void* data, size_t nBytes);
353 nonvirtual void write (char c);
354 nonvirtual void write (const char* str);
355 nonvirtual void write (const string& str);
356 };
357
358 /*
359 @CLASS: StyledTextIOWriter::SrcStream
360 @DESCRIPTION: <p>Abstract base class for @'StyledTextIOWriter's to get their text content from.</p>
361 */
362 class StyledTextIOWriter::SrcStream {
363 public:
364 virtual ~SrcStream () = default;
365
366 public:
367 /*
368 @METHOD: StyledTextIOWriter::SrcStream::readNTChars
369 @DESCRIPTION: <p>readNTChars can retun less than maxTChars before end of buffer, only to make us end on even
370 mbyte char boundary.</p>
371 */
372 virtual size_t readNTChars (Led_tChar* intoBuf, size_t maxTChars) = 0;
373
374 public:
375 /*
376 @METHOD: StyledTextIOWriter::SrcStream::current_offset
377 @DESCRIPTION: <p>current seekPos</p>
378 */
379 virtual size_t current_offset () const = 0;
380
381 public:
382 /*
383 @METHOD: StyledTextIOWriter::SrcStream::seek_to
384 @DESCRIPTION: <p>'to' past end just pins one past end of buffer</p>
385 */
386 virtual void seek_to (size_t to) = 0;
387
388 public:
389 /*
390 @METHOD: StyledTextIOWriter::SrcStream::GetTotalTextLength
391 @DESCRIPTION: <p>Total # of tChars</p>
392 */
393 virtual size_t GetTotalTextLength () const = 0;
394
395 public:
396 /*
397 @METHOD: StyledTextIOWriter::SrcStream::GetStyleInfo
398 @DESCRIPTION:
399 */
400 virtual vector<StyledInfoSummaryRecord> GetStyleInfo (size_t from, size_t len) const = 0;
401
402#if qStroika_Frameworks_Led_SupportGDI
403 public:
404 /*
405 @METHOD: StyledTextIOWriter::SrcStream::CollectAllEmbeddingMarkersInRange
406 @DESCRIPTION:
407 */
408 virtual vector<SimpleEmbeddedObjectStyleMarker*> CollectAllEmbeddingMarkersInRange (size_t from, size_t to) const = 0;
409#endif
410
411 public:
412 class Table;
413
414 /*
415 @METHOD: StyledTextIOWriter::SrcStream::GetTableAt
416 @DESCRIPTION: Return a @'StyledTextIOWriter::SrcStream::Table' object. Note - this is not to be
417 confused with a @'WordProcessor::Table' object. Though they are closely related, this object
418 contains just the API required for writing tables to files.
419 */
420 virtual Table* GetTableAt (size_t at) const = 0;
421
422 public:
423 /*
424 @METHOD: StyledTextIOWriter::SrcStream::SummarizeFontAndColorTable
425 @DESCRIPTION: <p>Produce a list of all fontnames and colors used in the document. This is needed for some formats
426 like RTF which require a list of all font names and colors before writing any of the rest of the document.</p>
427 */
428 virtual void SummarizeFontAndColorTable (set<SDKString>* fontNames, set<Color>* colorsUsed) const = 0;
429
430 public:
431 /*
432 @METHOD: StyledTextIOWriter::SrcStream::GetEmbeddingMarkerPosOffset
433 @DESCRIPTION: <p>Since we maybe externalizing a subset of the buffer, and the marker positions in the embedding object
434 are absolute, we need to know this to relativize them in the externalized stream</p>
435 */
436 virtual size_t GetEmbeddingMarkerPosOffset () const = 0;
437
438 public:
439 virtual Justification GetJustification () const;
440 virtual StandardTabStopList GetStandardTabStopList () const;
441 virtual TWIPS GetFirstIndent () const;
442 virtual void GetMargins (TWIPS* lhs, TWIPS* rhs) const;
443 virtual TWIPS GetSpaceBefore () const;
444 virtual TWIPS GetSpaceAfter () const;
445 virtual LineSpacing GetLineSpacing () const;
446 virtual void GetListStyleInfo (ListStyle* listStyle, unsigned char* indentLevel) const;
447 virtual Led_tChar GetSoftLineBreakCharacter () const;
448 virtual DiscontiguousRun<bool> GetHidableTextRuns () const;
449 };
450
451 /*
452 @CLASS: StyledTextIOWriter::SrcStream::Table
453 @DESCRIPTION: <p>Simple abstract API so styled text IO code can ask key questions about a table object in order to persist it..</p>
454 */
455 class StyledTextIOWriter::SrcStream::Table {
456 public:
457 virtual ~Table () = default;
458
459 public:
460 struct CellInfo {
461 CellInfo ();
462 TWIPS f_cellx;
463 Color f_clcbpat; // cell background color
464 };
465
466 public:
467 /*
468 @METHOD: StyledTextIOWriter::SrcStream::Table::GetRows
469 @DESCRIPTION: <p>Get the number of rows in the given table.</p>
470 */
471 virtual size_t GetRows () const = 0;
472 /*
473 @METHOD: StyledTextIOWriter::SrcStream::Table::GetColumns
474 @DESCRIPTION: <p>Get the number of columns in the given row. Note that this can be
475 different from row to row within a given table.</p>
476 */
477 virtual size_t GetColumns (size_t row) const = 0;
478 /*
479 @METHOD: StyledTextIOWriter::SrcStream::Table::GetRowInfo
480 @DESCRIPTION: <p>Get the @'StyledTextIOWriter::SrcStream::Table::CellInfo's for the given
481 row. The number of these records is the same as the return value from
482 @'StyledTextIOWriter::SrcStream::Table::GetColumns'.
483 </p>
484 */
485 virtual void GetRowInfo (size_t row, vector<CellInfo>* cellInfos) = 0;
486 /*
487 @METHOD: StyledTextIOWriter::SrcStream::Table::MakeCellSubSrcStream
488 @DESCRIPTION: <p>Returns a @'StyledTextIOWriter::SrcStream' containing the data for the given table cell.
489 Note that this can return nullptr for 'merged' cells.</p>
490 */
491 virtual StyledTextIOWriter::SrcStream* MakeCellSubSrcStream (size_t row, size_t column) = 0;
492 /*
493 @METHOD: StyledTextIOWriter::SrcStream::Table::GetOffsetEnd
494 @DESCRIPTION: <p>Returns the source-doc offset of the given table end relative to its start. Readers should
495 skip ahead that many positions after writing the tables contents.</p>
496 */
497 virtual size_t GetOffsetEnd () const = 0;
498 /*
499 @METHOD: StyledTextIOWriter::SrcStream::Table::GetDefaultCellMarginsForRow
500 @DESCRIPTION: <p>Return the default cell margins for the given row. Note that the @'TWIPS_Rect'
501 is not a true rectangle, but just a handy way to return 4 values - a top/left/bottom/right.</p>
502 */
503 virtual TWIPS_Rect GetDefaultCellMarginsForRow (size_t row) const = 0;
504 /*
505 @METHOD: StyledTextIOWriter::SrcStream::Table::GetDefaultCellSpacingForRow
506 @DESCRIPTION: <p>Return the default cell spacing for the given row. Note that the @'TWIPS_Rect'
507 is not a true rectangle, but just a handy way to return 4 values - a top/left/bottom/right.</p>
508 */
509 virtual TWIPS_Rect GetDefaultCellSpacingForRow (size_t row) const = 0;
510 };
511
512 /*
513 <p>Abstract base class for @'StyledTextIOWriter's to dump their text content to.</p>
514
515 \@todo THIS IS OBSOLETE - and should be switched to using Streams::OutputStream<byte> --LGP 2024-02-22
516 */
517 class StyledTextIOWriter::SinkStream {
518 public:
519 virtual ~SinkStream ()
520 {
521 }
522
523 public:
524 /*
525 @METHOD: StyledTextIOWriter::SinkStream::current_offset
526 @DESCRIPTION: <p>Return the current seekPos.</p>
527 */
528 virtual size_t current_offset () const = 0;
529
530 public:
531 /*
532 @METHOD: StyledTextIOWriter::SinkStream::seek_to
533 @DESCRIPTION: <p>'to' past end just pins one past end of buffer</p>
534 */
535 virtual void seek_to (size_t to) = 0;
536
537 public:
538 /*
539 @METHOD: StyledTextIOWriter::SinkStream::write
540 @DESCRIPTION:
541 */
542 virtual void write (const void* buffer, size_t bytes) = 0;
543 };
544
545 /*
546 ********* Some StyledTextIOReader::SrcStream subclasses *********
547 */
548 /*
549 @CLASS: StyledTextIOSrcStream_Memory
550 @BASES: @'StyledTextIOReader::SrcStream'
551 @DESCRIPTION: <p>If you have a block of memory which contains the untyped contents which will be converted by some
552 reader (@'StyledTextIOReader'), you use this as the @'StyledTextIOReader::SrcStream'. Just initialize one of these
553 with the appropriate data, and pass this to the appropriate @'StyledTextIOReader'.</p>
554 <p>NB: This class doesn't free up, or copy the given pointer. It is up the the caller todo that, and only after
555 this SrcStream object has been destroyed. Typically, this follows trivially from a sequential, stack-based allocation
556 strategy, where the data comes from some object declared earlier on the stack.</p>
557 */
558 class StyledTextIOSrcStream_Memory : public StyledTextIOReader::SrcStream {
559 public:
560 StyledTextIOSrcStream_Memory (const void* data, size_t nBytes);
561
562 public:
563 virtual size_t current_offset () const override;
564 virtual void seek_to (size_t to) override;
565 virtual size_t read (void* buffer, size_t bytes) override;
566 virtual size_t read1 (char* c) override;
567
568 private:
569 const void* fData;
570 const void* fDataEnd;
571 size_t fBytesInBuffer;
572 const void* fCurPtr;
573 };
574
575 inline StyledTextIOReader::BufferedIndirectSrcStream::BufferedIndirectSrcStream (SrcStream& realSrcStream)
576 : fRealSrcStream (realSrcStream)
577 ,
578 //fWindowTop_Data (),
579 fWindowTop_Offset (size_t (-1))
580 , fWindowBottom_Data (nullptr)
581 , fWindowBottom_Offset (size_t (-1))
582 , fCursor_Data (nullptr)
583 , fCursor_Offset (0)
584 {
585 }
586 inline void StyledTextIOReader::BufferedIndirectSrcStream::FillCache ()
587 {
588 fWindowTop_Offset = fCursor_Offset;
589 fRealSrcStream.seek_to (fWindowTop_Offset); // probably could frequently optimize this call way if we were careful to cache last seek-offset from buffer
590 size_t bytesRead = fRealSrcStream.read (fWindowTop_Data, std::size (fWindowTop_Data));
591 fWindowBottom_Data = fWindowTop_Data + bytesRead;
592 fWindowBottom_Offset = fWindowTop_Offset + bytesRead;
593 Assert (fCursor_Offset >= fWindowTop_Offset and fCursor_Offset <= fWindowBottom_Offset); // should only call FillCache in that case?
594 Assert (fCursor_Offset == fWindowTop_Offset); // should only call FillCache in that case?
595 if (fCursor_Offset >= fWindowTop_Offset and fCursor_Offset <= fWindowBottom_Offset) {
596 fCursor_Data = fWindowTop_Data + (fCursor_Offset - fWindowTop_Offset);
597 }
598 Ensure (fWindowTop_Data <= fCursor_Data and fCursor_Data <= fWindowBottom_Data);
599 Ensure (fWindowTop_Offset <= fCursor_Offset and fCursor_Offset <= fWindowBottom_Offset);
600 }
601 inline size_t StyledTextIOReader::BufferedIndirectSrcStream::current_offset () const
602 {
603 return fCursor_Offset;
604 }
605 inline void StyledTextIOReader::BufferedIndirectSrcStream::seek_to (size_t to)
606 {
607 // If seekpos inside our window (at end of buffer counts as inside window even though next read may force a FillCache),
608 // just update offset(s), and otherwise - mark fCursor_Data as nullptr so we know cache invalid
609 if (fWindowTop_Offset <= to and to <= fWindowBottom_Offset) {
610 fCursor_Data = fWindowTop_Data + (to - fWindowTop_Offset);
611 }
612 else {
613 fCursor_Data = nullptr;
614 }
615 fCursor_Offset = to;
616 }
617 inline size_t StyledTextIOReader::BufferedIndirectSrcStream::read (void* buffer, size_t bytes)
618 {
619 RequireNotNull (buffer);
620
621 byte* destCursor = reinterpret_cast<byte*> (buffer);
622 size_t bytesReadSoFar = 0;
623
624 /*
625 * See if the initial part of this request can be satisfied by our current buffered data
626 * and updated 'bytesReadSoFar' to reflect how much read from that buffer.
627 */
628 if (fCursor_Data != nullptr and fWindowTop_Offset >= fCursor_Offset and fCursor_Offset < fWindowBottom_Offset) {
629 size_t bytesAvail = fWindowBottom_Offset - fCursor_Offset; // must be > 0 UNLESS we are at EOF
630 size_t thisReadCount = min (bytesAvail, bytes);
631 AssertNotNull (fCursor_Data);
632 (void)::memcpy (destCursor, fCursor_Data, thisReadCount);
633 destCursor += thisReadCount;
634 fCursor_Data += thisReadCount;
635 fCursor_Offset += thisReadCount;
636 bytesReadSoFar += thisReadCount;
637 }
638
639 /*
640 * If we've not completed the request, see if it can be accomodated by by filling the buffer,
641 * and trying to pull data out of that buffer. If not - then simply read the data directly.
642 */
643 if (bytesReadSoFar < bytes) {
644 size_t bytesLeftToRead = bytes - bytesReadSoFar;
645 if (bytesLeftToRead < std::size (fWindowTop_Data)) {
646 FillCache ();
647 size_t bytesAvail = fWindowBottom_Offset - fCursor_Offset; // must be > 0 UNLESS we are at EOF
648 size_t thisReadCount = min (bytesAvail, bytesLeftToRead);
649 AssertNotNull (fCursor_Data);
650 DISABLE_COMPILER_MSC_WARNING_START (6387) // fCursor_Data is not null because of assertion above
651 (void)::memcpy (destCursor, fCursor_Data, thisReadCount);
652 DISABLE_COMPILER_MSC_WARNING_END (6387)
653 destCursor += thisReadCount;
654 fCursor_Data += thisReadCount;
655 fCursor_Offset += thisReadCount;
656 bytesReadSoFar += thisReadCount;
657 }
658 else {
659 fRealSrcStream.seek_to (fCursor_Offset);
660 size_t bytesRead = fRealSrcStream.read (destCursor, bytesLeftToRead);
661 bytesReadSoFar += bytesRead;
662 fCursor_Offset += bytesRead;
663 // Cache is invalid - so mark it so...
664 fCursor_Data = nullptr;
665 }
666 }
667 return bytesReadSoFar;
668 }
669 inline size_t StyledTextIOReader::BufferedIndirectSrcStream::read1 (char* c)
670 {
671 RequireNotNull (c);
672 /*
673 * See if we can read ANY non-zero number of bytes out of our window. If yes - then just
674 * return those (even if thats less than the user requested - following standard UNIX read
675 * conventions). If we cannot read any bytes given our current window, refill the window, and
676 * try again.
677 */
678 if ((fCursor_Data == nullptr) or (fCursor_Offset < fWindowTop_Offset or fCursor_Offset >= fWindowBottom_Offset)) {
679 FillCache ();
680 }
681 Assert (fWindowTop_Offset <= fCursor_Offset and fCursor_Offset <= fWindowBottom_Offset);
682 if (fWindowBottom_Offset == fCursor_Offset) {
683 return 0;
684 }
685 else {
686 AssertNotNull (fCursor_Data);
687 *c = *fCursor_Data;
688 ++fCursor_Data;
689 ++fCursor_Offset;
690 return 1;
691 }
692 }
693
694 /*
695 ********* Some StyledTextIOReader::SrcStream subclasses *********
696 */
697 /*
698 @CLASS: StyledTextIOWriterSinkStream_Memory
699 @BASES: @'StyledTextIOWriter::SinkStream'
700 @DESCRIPTION:
701 */
702 class StyledTextIOWriterSinkStream_Memory : public StyledTextIOWriter::SinkStream {
703 public:
704 StyledTextIOWriterSinkStream_Memory ();
705 ~StyledTextIOWriterSinkStream_Memory ();
706
707 private: // prevent accidental copying
708 StyledTextIOWriterSinkStream_Memory (const StyledTextIOWriterSinkStream_Memory&) = delete;
709 void operator= (const StyledTextIOWriterSinkStream_Memory&) = delete;
710
711 public:
712 virtual size_t current_offset () const override;
713 virtual void seek_to (size_t to) override;
714 virtual void write (const void* buffer, size_t bytes) override;
715
716 nonvirtual const void* PeekAtData () const;
717 nonvirtual size_t GetLength () const;
718
719 private:
720 char* fData;
721 size_t fBytesUsed;
722 size_t fBytesAllocated;
723 char* fCurPtr;
724 };
725
726#if qStroika_Frameworks_Led_SupportGDI
727 /*
728 @CLASS: EmbeddingSinkStream
729 @BASES: @'SimpleEmbeddedObjectStyleMarker::SinkStream'
730 @DESCRIPTION:
731 */
732 class EmbeddingSinkStream : public SimpleEmbeddedObjectStyleMarker::SinkStream {
733 public:
734 EmbeddingSinkStream (StyledTextIOWriter::SinkStream& realSinkStream);
735
736 virtual void write (const void* buffer, size_t bytes) override;
737
738 private:
739 StyledTextIOWriter::SinkStream& fRealSinkStream;
740 };
741#endif
742
743 /*
744 ********************************************************************************
745 ***************************** Implementation Details ***************************
746 ********************************************************************************
747 */
748 // class StyledTextIOReader::SrcStreamSeekSaver
749 inline StyledTextIOReader::SrcStreamSeekSaver::SrcStreamSeekSaver (SrcStream& srcStream)
750 : fSrcStream (srcStream)
751 , fSavedPos (srcStream.current_offset ())
752 {
753 }
754 inline StyledTextIOReader::SrcStreamSeekSaver::~SrcStreamSeekSaver ()
755 {
756 try {
757 fSrcStream.seek_to (fSavedPos);
758 }
759 catch (...) {
760 // ignore errors here cuz throwing out of DTORs appears to cause havoc with
761 // MWERKS runtime?? Is it a MWERKS bug? Or mine - or has this been fixed?
762 // LGP 960906
763 }
764 }
765
766 // class StyledTextIOReader
767 inline StyledTextIOReader::StyledTextIOReader (SrcStream* srcStream, SinkStream* sinkStream, const shared_ptr<BadInputHandler>& badInputHander)
768 : fSrcStream (*srcStream)
769 , fSinkStream (sinkStream)
770 , fBadInputHandler (badInputHander)
771 {
772 RequireNotNull (srcStream);
773 if (fBadInputHandler.get () == nullptr) {
774 fBadInputHandler = Memory::MakeSharedPtr<BadInputHandler> ();
775 }
776 }
777 inline StyledTextIOReader::SrcStream& StyledTextIOReader::GetSrcStream () const
778 {
779 return fSrcStream;
780 }
781 inline StyledTextIOReader::SinkStream& StyledTextIOReader::GetSinkStream () const
782 {
783 EnsureNotNull (fSinkStream);
784 return *fSinkStream;
785 }
786 /*
787 @METHOD: StyledTextIOReader::GetBadInputHandler
788 @DESCRIPTION: <p>Each reader class has associated with it an error handler - of type @'StyledTextIOReader::BadInputHandler'. This is used
789 to handle syntactic or logical errors in the input. By default - this class is simple
790 @'StyledTextIOReader::BadInputHandler'.</p>
791 <p>See also @'StyledTextIOReader::SetBadInputHandler' and @'StyledTextIOReader::HandleBadlyFormattedInput'.</p>
792 */
793 inline shared_ptr<StyledTextIOReader::BadInputHandler> StyledTextIOReader::GetBadInputHandler () const
794 {
795 Ensure (fBadInputHandler.get () != nullptr);
796 return fBadInputHandler;
797 }
798 /*
799 @METHOD: StyledTextIOReader::SetBadInputHandler
800 @DESCRIPTION: <p>See @'StyledTextIOReader::GetBadInputHandler'</p>
801 */
802 inline void StyledTextIOReader::SetBadInputHandler (const shared_ptr<BadInputHandler>& badInputHandler)
803 {
804 fBadInputHandler = badInputHandler;
805 if (fBadInputHandler == nullptr) {
806 fBadInputHandler = Memory::MakeSharedPtr<BadInputHandler> ();
807 }
808 }
809 /*
810 @METHOD: StyledTextIOReader::HandleBadlyFormattedInput
811 @DESCRIPTION: <p>This routine is called whenever this is badly formatted input text to the reader.
812 This is a simple wrapper on the owned @'StyledTextIOReader::BadInputHandler', which can be gotten/set with
813 @'StyledTextIOReader::GetBadInputHandler' / @'StyledTextIOReader::SetBadInputHandler'</p>
814 */
815 inline void StyledTextIOReader::HandleBadlyFormattedInput (bool unrecoverable) const
816 {
817 GetBadInputHandler ()->HandleBadlyFormattedInput (*this, unrecoverable);
818 }
819 /*
820 @METHOD: StyledTextIOReader::PutBackLastChar
821 @DESCRIPTION: <p>Unread the last read character. Note - this can be done as many times as you want (allowing infinite unread)
822 but it is a bug/error if you ever unread characters that handn't been read in the first place</p>
823 */
824 inline void StyledTextIOReader::PutBackLastChar () const
825 {
826 Require (fSrcStream.current_offset () > 0);
827 fSrcStream.seek_to (fSrcStream.current_offset () - 1);
828 }
829 inline char StyledTextIOReader::GetNextChar () const
830 {
831 Stroika_ATTRIBUTE_INDETERMINATE char c; // Better to leave uninitialized for performance reasons - LGP 2003-03-17
832 if (fSrcStream.read1 (&c) == 1) {
833 return c;
834 }
835 else {
836 throw ReadEOFException{};
837 Assert (false);
838 return 0; // NOT REACHED
839 }
840 }
841 inline char StyledTextIOReader::PeekNextChar () const
842 {
843 Stroika_ATTRIBUTE_INDETERMINATE char c; // Better to leave uninitialized for performance reasons - LGP 2003-03-17
844 if (fSrcStream.read1 (&c) == 1) {
845 PutBackLastChar ();
846 return c;
847 }
848 else {
849 throw ReadEOFException ();
851 return 0; // NOT REACHED
852 }
853 }
854 inline void StyledTextIOReader::ConsumeNextChar () const
855 {
856 (void)GetNextChar ();
857 }
858
859 // class StyledTextIOWriter
860 inline StyledTextIOWriter::StyledTextIOWriter (SrcStream* srcStream, SinkStream* sinkStream)
861 : fSrcStream (srcStream)
862 , fSinkStream (sinkStream)
863 {
864 RequireNotNull (srcStream);
865 RequireNotNull (sinkStream);
866 }
867 inline StyledTextIOWriter::SrcStream& StyledTextIOWriter::GetSrcStream () const
868 {
869 EnsureNotNull (fSrcStream);
870 return *fSrcStream;
871 }
872 inline StyledTextIOWriter::SinkStream& StyledTextIOWriter::GetSinkStream () const
873 {
874 EnsureNotNull (fSinkStream);
875 return *fSinkStream;
876 }
877
878 // class StyledTextIOReader::SinkStream
879 /*
880 @METHOD: StyledTextIOReader::SinkStream::GetCountOfTCharsInserted
881 @DESCRIPTION:
882 */
883 inline size_t StyledTextIOReader::SinkStream::GetCountOfTCharsInserted () const
884 {
885 return current_offset ();
886 }
887
888 // class StyledTextIOWriter::SrcStream::Table::CellInfo
889 inline StyledTextIOWriter::SrcStream::Table::CellInfo::CellInfo ()
890 : f_cellx (TWIPS (0))
891 , f_clcbpat (Color::kWhite)
892 {
893 }
894
895 // class StyledTextIOWriterSinkStream_Memory
896 inline const void* StyledTextIOWriterSinkStream_Memory::PeekAtData () const
897 {
898 return fData;
899 }
900 inline size_t StyledTextIOWriterSinkStream_Memory::GetLength () const
901 {
902 return fBytesUsed;
903 }
904
905}
906
907#endif /*_Stroika_Frameworks_Led_StyledTextIO_h_*/
#define AssertNotNull(p)
Definition Assertions.h:336
#define EnsureNotNull(p)
Definition Assertions.h:343
#define RequireNotNull(p)
Definition Assertions.h:350
#define AssertNotReached()
Definition Assertions.h:358
#define Stroika_ATTRIBUTE_INDETERMINATE
Stroika_ATTRIBUTE_INDETERMINATE is used where you would use a C++ attribute for a variable that is in...
Definition StdCompat.h:428