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