vfs: check userland buffers before reading them.
[haiku.git] / src / apps / haikudepot / textview / MarkupParser.cpp
blob5c11c14dc6199652af18f7e2c3dc2a4089730cc5
1 /*
2 * Copyright 2013, Stephan Aßmus <superstippi@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
6 #include "MarkupParser.h"
8 #include <new>
10 #include <math.h>
12 #include <utf8_functions.h>
15 MarkupParser::MarkupParser()
17 fNormalStyle(),
18 fBoldStyle(),
19 fItalicStyle(),
20 fBoldItalicStyle(),
21 fHeadingStyle(),
23 fParagraphStyle(),
24 fHeadingParagraphStyle(),
25 fBulletStyle(),
27 fCurrentCharacterStyle(&fNormalStyle),
28 fCurrentParagraphStyle(&fParagraphStyle),
29 fSpanStartOffset(0)
31 _InitStyles();
35 MarkupParser::MarkupParser(const CharacterStyle& characterStyle,
36 const ParagraphStyle& paragraphStyle)
38 fNormalStyle(characterStyle),
39 fBoldStyle(),
40 fItalicStyle(),
41 fBoldItalicStyle(),
42 fHeadingStyle(),
44 fParagraphStyle(paragraphStyle),
45 fHeadingParagraphStyle(),
46 fBulletStyle(),
48 fCurrentCharacterStyle(&fNormalStyle),
49 fCurrentParagraphStyle(&fParagraphStyle),
50 fSpanStartOffset(0)
52 _InitStyles();
56 void
57 MarkupParser::SetStyles(const CharacterStyle& characterStyle,
58 const ParagraphStyle& paragraphStyle)
60 fNormalStyle = characterStyle;
61 fParagraphStyle = paragraphStyle;
62 _InitStyles();
66 TextDocumentRef
67 MarkupParser::CreateDocumentFromMarkup(const BString& text)
69 TextDocumentRef document(new(std::nothrow) TextDocument(), true);
70 if (document.Get() == NULL)
71 return document;
73 AppendMarkup(document, text);
75 return document;
79 void
80 MarkupParser::AppendMarkup(const TextDocumentRef& document, const BString& text)
82 fTextDocument.SetTo(document);
84 fCurrentCharacterStyle = &fNormalStyle;
85 fCurrentParagraphStyle = &fParagraphStyle;
87 fCurrentParagraph = Paragraph(*fCurrentParagraphStyle);
88 fSpanStartOffset = 0;
90 _ParseText(text);
92 fTextDocument.Unset();
96 // #pragma mark - private
99 void
100 MarkupParser::_InitStyles()
102 fBoldStyle = fNormalStyle;
103 fBoldStyle.SetBold(true);
105 fItalicStyle = fNormalStyle;
106 fItalicStyle.SetItalic(true);
108 fBoldItalicStyle = fNormalStyle;
109 fBoldItalicStyle.SetBold(true);
110 fBoldItalicStyle.SetItalic(true);
112 float fontSize = fNormalStyle.Font().Size();
114 fHeadingStyle = fNormalStyle;
115 fHeadingStyle.SetFontSize(ceilf(fontSize * 1.15f));
116 fHeadingStyle.SetBold(true);
118 fHeadingParagraphStyle = fParagraphStyle;
119 fHeadingParagraphStyle.SetSpacingTop(ceilf(fontSize * 0.8f));
120 fHeadingParagraphStyle.SetSpacingBottom(ceilf(fontSize * 0.5f));
121 fHeadingParagraphStyle.SetJustify(false);
123 fBulletStyle = fParagraphStyle;
124 fBulletStyle.SetBullet(Bullet("•", fontSize));
125 fBulletStyle.SetLineInset(ceilf(fontSize * 0.8f));
129 void
130 MarkupParser::_ParseText(const BString& text)
132 int32 start = 0;
133 int32 offset = 0;
135 int32 charCount = text.CountChars();
136 const char* c = text.String();
138 while (offset <= charCount) {
139 uint32 nextChar = UTF8ToCharCode(&c);
141 switch (nextChar) {
142 // Requires two line-breaks to start a new paragraph, unles the current
143 // paragraph is already considered a bullet list item. Doesn't work well
144 // with current set of packages.
145 // case '\n':
146 // _CopySpan(text, start, offset);
147 // if (offset + 1 < charCount && c[0] == '\n') {
148 // _FinishParagraph();
149 // offset += 1;
150 // c += 1;
151 // } else if (fCurrentParagraph.Style() == fBulletStyle) {
152 // _FinishParagraph();
153 // }
154 // start = offset + 1;
155 // break;
157 case '\n':
158 _CopySpan(text, start, offset);
159 if (offset > 0 && c[-1] != ' ')
160 _FinishParagraph(offset >= charCount);
161 start = offset + 1;
162 break;
164 case '\0':
165 _CopySpan(text, start, offset);
166 _FinishParagraph(true);
167 start = offset + 1;
168 break;
170 case '\'':
171 if (offset + 2 < charCount && c[0] == '\'') {
172 int32 tickCount = 2;
173 if (c[1] == '\'')
174 tickCount = 3;
176 // Copy previous span using current style, excluding the
177 // ticks.
178 _CopySpan(text, start, offset);
180 if (tickCount == 2)
181 _ToggleStyle(fItalicStyle);
182 else if (tickCount == 3)
183 _ToggleStyle(fBoldStyle);
185 // Don't include the ticks in the next span.
186 offset += tickCount - 1;
187 start = offset + 1;
188 c += tickCount - 1;
190 break;
192 case '=':
193 // Detect headings
194 if (offset == start
195 && fCurrentParagraph.IsEmpty()
196 && offset + 2 < charCount
197 && c[0] == '=' && c[1] == ' ') {
199 fCurrentParagraph.SetStyle(fHeadingParagraphStyle);
200 fCurrentCharacterStyle = &fHeadingStyle;
202 offset += 2;
203 c += 2;
205 start = offset + 1;
206 } else if (offset > start
207 && offset + 2 < charCount
208 && c[0] == '=' && c[1] == '\n') {
210 _CopySpan(text, start, offset - 1);
212 offset += 2;
213 c += 2;
215 _FinishParagraph(offset >= charCount);
217 start = offset + 1;
219 break;
221 case ' ':
222 // Detect bullets at line starts (preceeding space)
223 if (offset == start
224 && fCurrentParagraph.IsEmpty()
225 && offset + 2 < charCount
226 && (c[0] == '*' || c[0] == '-') && c[1] == ' ') {
228 fCurrentParagraph.SetStyle(fBulletStyle);
230 offset += 2;
231 c += 2;
233 start = offset + 1;
235 break;
237 case '*':
238 case '-':
239 // Detect bullets at line starts (no preceeding space)
240 if (offset == start
241 && fCurrentParagraph.IsEmpty()
242 && offset + 1 < charCount
243 && c[0] == ' ') {
245 fCurrentParagraph.SetStyle(fBulletStyle);
247 offset += 1;
248 c += 1;
250 start = offset + 1;
252 break;
254 default:
255 break;
258 offset++;
263 void
264 MarkupParser::_CopySpan(const BString& text, int32& start, int32 end)
266 if (start >= end)
267 return;
269 BString subString;
270 text.CopyCharsInto(subString, start, end - start);
271 fCurrentParagraph.Append(TextSpan(subString, *fCurrentCharacterStyle));
273 start = end;
277 void
278 MarkupParser::_ToggleStyle(const CharacterStyle& style)
280 if (fCurrentCharacterStyle == &style)
281 fCurrentCharacterStyle = &fNormalStyle;
282 else
283 fCurrentCharacterStyle = &style;
287 void
288 MarkupParser::_FinishParagraph(bool isLast)
290 if (!isLast)
291 fCurrentParagraph.Append(TextSpan("\n", *fCurrentCharacterStyle));
293 if (fCurrentParagraph.IsEmpty()) {
294 // Append empty span
295 fCurrentParagraph.Append(TextSpan("", fNormalStyle));
298 fTextDocument->Append(fCurrentParagraph);
299 fCurrentParagraph.Clear();
300 fCurrentParagraph.SetStyle(fParagraphStyle);
301 fCurrentCharacterStyle = &fNormalStyle;