Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
StyledTextIO_MIMETextEnriched.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4
5#include "Stroika/Frameworks/StroikaPreComp.h"
6
7#include <cctype>
8
9#include "Stroika/Foundation/Characters/LineEndings.h"
11
12#include "Stroika/Frameworks/Led/StyledTextIO/StyledTextIO_MIMETextEnriched.h"
13
14using namespace Stroika::Foundation;
15
16using namespace Stroika::Frameworks;
17using namespace Stroika::Frameworks::Led;
18using namespace Stroika::Frameworks::Led::StyledTextIO;
19
21
22/*
23 ********************************************************************************
24 ************************** StyledTextIOReader_MIMETextEnriched *****************
25 ********************************************************************************
26 */
27// SEE RFC 1563
28StyledTextIOReader_MIMETextEnriched::StyledTextIOReader_MIMETextEnriched (SrcStream* srcStream, SinkStream* sinkStream)
29 : StyledTextIOReader (srcStream, sinkStream)
30 , fBoldMode (0)
31 , fItalicMode (0)
32 , fUnderlineMode (0)
33 , fFixedWidthMode (0)
34 , fFontSizeAdjust (0)
35 , fNoFillMode (0)
36{
37}
38
39void StyledTextIOReader_MIMETextEnriched::Read ()
40{
41 SkipWhitespace ();
42 ScanFor ("Content-type:"); // must find since checked in QuickLookAppearsToBeRightFormat
43 SkipWhitespace ();
44 ScanFor ("text/enriched"); // ""
45 SkipOneLine ();
46 SkipOneLine (); // SB one blank line after Content-type...
47
48 /*
49 * Basiclly, we just scan for "<" since all command tokens inside these.
50 * Anything up to a "<" is plain text and we echo it - in current style -
51 * except for CR/LF mapping/handling
52 */
53 for (;;) {
54 size_t lastOffset = GetSrcStream ().current_offset ();
55 bool gotCmdStart = ScanFor ("<"); // side effect of scanning forward
56
57 size_t currentOffset = GetSrcStream ().current_offset ();
58 if (currentOffset == lastOffset and not gotCmdStart) {
59 break; // we must be past the end of the document
60 }
61
62 if (gotCmdStart) {
63 --currentOffset; // backup over <
64 }
65
66 FontSpecification fontSpec = GetAdjustedCurrentFontSpec ();
67
68 // Handle text before <
69 {
70 size_t len = currentOffset - lastOffset;
71 StackBuffer<Led_tChar> buf{Memory::eUninitialized, len};
72 GetSrcStream ().seek_to (lastOffset);
73 GetSrcStream ().read (buf.data (), len);
74 len = Characters::NormalizeTextToNL<Led_tChar> (buf.data (), len, buf.data (), len);
75 if (fNoFillMode == 0) {
76 // Strip xtra CRLF, and merge spaces - not really sure what should be done here.
77 // Just take a WILD STAB GUESS!!!
78 // LGP 951103
79 StackBuffer<Led_tChar> buf2{Memory::eUninitialized, len};
80 size_t i2 = 0;
81 for (size_t i = 0; i < len; ++i) {
82 Led_tChar prevChar = (i == 0) ? '\0' : buf[i - 1];
83 Led_tChar nextChar = (i == len - 1) ? '\0' : buf[i + 1];
84
85 if (buf[i] == '\n' and nextChar != '\n') {
86 buf[i] = ' '; // skip this NL
87 }
88
89 if (isspace (prevChar) and isspace (buf[i])) {
90 continue; // skip this char
91 }
92 buf2[i2++] = buf[i];
93 }
94 GetSinkStream ().AppendText (buf2.data (), i2, &fontSpec);
95 }
96 else {
97 GetSinkStream ().AppendText (buf.data (), len, &fontSpec);
98 }
99 }
100
101 if (gotCmdStart) {
102 // Handle "<" command
103
104 char c;
105 GetSrcStream ().seek_to (currentOffset + 1);
106 if (GetSrcStream ().read (&c, 1) != 0 and c == '<') {
107 // special case of << - not a real command - just echo <
108 Led_tChar wc = c; // tmp-hack - assume src is ASCII - LGP990308
109 GetSinkStream ().AppendText (&wc, 1, &fontSpec);
110 }
111 else {
112 // HANDLE REAL COMMAND
113 GetSrcStream ().seek_to (currentOffset);
114
115 ScanFor (">");
116 size_t len = GetSrcStream ().current_offset () - currentOffset;
117 StackBuffer<char> cmdBuf{Memory::eUninitialized, len + 1};
118 GetSrcStream ().seek_to (currentOffset);
119 GetSrcStream ().read (cmdBuf.data (), len);
120 cmdBuf[len] = '\0';
121 if (strcmp (cmdBuf.data (), "<bold>") == 0) {
122 ++fBoldMode;
123 }
124 else if (strcmp (cmdBuf.data (), "</bold>") == 0 and fBoldMode > 0) {
125 --fBoldMode;
126 }
127 else if (strcmp (cmdBuf.data (), "<italic>") == 0) {
128 ++fItalicMode;
129 }
130 else if (strcmp (cmdBuf.data (), "</italic>") == 0 and fItalicMode > 0) {
131 --fItalicMode;
132 }
133 else if (strcmp (cmdBuf.data (), "<underline>") == 0) {
134 ++fItalicMode;
135 }
136 else if (strcmp (cmdBuf.data (), "</underline>") == 0 and fUnderlineMode > 0) {
137 --fUnderlineMode;
138 }
139 else if (strcmp (cmdBuf.data (), "<fixed>") == 0) {
140 ++fFixedWidthMode;
141 }
142 else if (strcmp (cmdBuf.data (), "</fixed>") == 0 and fFixedWidthMode > 0) {
143 --fFixedWidthMode;
144 }
145 else if ((strcmp (cmdBuf.data (), "<smaller>") == 0) or (strcmp (cmdBuf.data (), "</bigger>") == 0)) {
146 fFontSizeAdjust -= 2;
147 }
148 else if ((strcmp (cmdBuf.data (), "</smaller>") == 0) or (strcmp (cmdBuf.data (), "<bigger>") == 0)) {
149 fFontSizeAdjust += 2;
150 }
151 else if (strcmp (cmdBuf.data (), "<nofill>") == 0) {
152 ++fNoFillMode;
153 }
154 else if (strcmp (cmdBuf.data (), "</nofill>") == 0 and fNoFillMode > 0) {
155 --fNoFillMode;
156 }
157 else if (strcmp (cmdBuf.data (), "<param>") == 0) {
158 ScanFor ("</param>"); // skip/ignore
159 }
160 }
161 }
162 }
163 GetSinkStream ().EndOfBuffer ();
164}
165
166bool StyledTextIOReader_MIMETextEnriched::QuickLookAppearsToBeRightFormat ()
167{
168 SrcStreamSeekSaver savePos (GetSrcStream ());
169 SkipWhitespace ();
170 if (LookingAt ("Content-type:")) {
171 SkipWhitespace ();
172 return LookingAt ("text/enriched");
173 }
174 else {
175 return false;
176 }
177 Assert (false);
178 return false; // silence compiler warning
179}
180
181void StyledTextIOReader_MIMETextEnriched::SkipWhitespace ()
182{
183 char c;
184 while (GetSrcStream ().read (&c, 1) != 0) {
185 if (not isascii (c) or not isspace (c)) {
186 // one char too far!!!
187 GetSrcStream ().seek_to (GetSrcStream ().current_offset () - 1);
188 return;
189 }
190 }
191}
192
193void StyledTextIOReader_MIMETextEnriched::SkipOneLine ()
194{
195 char c;
196 while (GetSrcStream ().read (&c, 1) != 0) {
197 if (c == '\r') {
198 char nextChar;
199 GetSrcStream ().read (&nextChar, 1);
200 if (nextChar != '\n') {
201 // mac format text - let legit, but we'll take it... put char back...
202 GetSrcStream ().seek_to (GetSrcStream ().current_offset () - 1);
203 }
204 return;
205 }
206 if (c == '\n') {
207 return;
208 }
209 }
210}
211
212bool StyledTextIOReader_MIMETextEnriched::ScanFor (const char* matchMe, bool ignoreCase)
213{
214 RequireNotNull (matchMe);
215
216 char c;
217 while (GetSrcStream ().read (&c, 1) != 0) {
218 if (Led_CasedCharsEqual (c, matchMe[0], ignoreCase)) {
219 size_t savedOffset = GetSrcStream ().current_offset ();
220 if (LookingAt (&matchMe[1], ignoreCase)) {
221 return true;
222 }
223 else {
224 GetSrcStream ().seek_to (savedOffset);
225 }
226 }
227 }
228 return false;
229}
230
231bool StyledTextIOReader_MIMETextEnriched::LookingAt (const char* matchMe, bool ignoreCase)
232{
233 RequireNotNull (matchMe);
234 size_t nBytesToMatch = ::strlen (matchMe);
235
236 if (nBytesToMatch == 0) {
237 return true;
238 }
239 else {
240 char c;
241 if (GetSrcStream ().read (&c, 1) != 0) {
242 if (Led_CasedCharsEqual (c, matchMe[0], ignoreCase)) {
243 size_t savedOffset = GetSrcStream ().current_offset ();
244 if (LookingAt (&matchMe[1], ignoreCase)) {
245 return true;
246 }
247 else {
248 GetSrcStream ().seek_to (savedOffset);
249 }
250 }
251 }
252 }
253 return false;
254}
255
256FontSpecification StyledTextIOReader_MIMETextEnriched::GetAdjustedCurrentFontSpec () const
257{
258 FontSpecification fsp = GetSinkStream ().GetDefaultFontSpec ();
259 if (fBoldMode > 0) {
260 fsp.SetStyle_Bold (true);
261 }
262 if (fItalicMode > 0) {
263 fsp.SetStyle_Italic (true);
264 }
265 if (fUnderlineMode > 0) {
266 fsp.SetStyle_Underline (true);
267 }
268 if (fFixedWidthMode > 0) {
269 Assert (false); // just ignore - NYI
270 }
271
272 {
273 int resultSize = fsp.GetPointSize () + fFontSizeAdjust;
274 if (resultSize < 5) {
275 resultSize = 5;
276 }
277 if (resultSize > 64) {
278 resultSize = 64;
279 }
280 fsp.SetPointSize (static_cast<uint8_t> (resultSize)); // pinned 5..64 so fits in byte
281 }
282 return fsp;
283}
#define RequireNotNull(p)
Definition Assertions.h:347
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...