* Copyright 2013, Stephan Aßmus <superstippi@gmx.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "MarkupParser.h"
#include <new>
#include <math.h>
#include <utf8_functions.h>
MarkupParser::MarkupParser()
:
fNormalStyle(),
fBoldStyle(),
fItalicStyle(),
fBoldItalicStyle(),
fHeadingStyle(),
fParagraphStyle(),
fHeadingParagraphStyle(),
fBulletStyle(),
fCurrentCharacterStyle(&fNormalStyle),
fCurrentParagraphStyle(&fParagraphStyle),
fSpanStartOffset(0)
{
_InitStyles();
}
MarkupParser::MarkupParser(const CharacterStyle& characterStyle,
const ParagraphStyle& paragraphStyle)
:
fNormalStyle(characterStyle),
fBoldStyle(),
fItalicStyle(),
fBoldItalicStyle(),
fHeadingStyle(),
fParagraphStyle(paragraphStyle),
fHeadingParagraphStyle(),
fBulletStyle(),
fCurrentCharacterStyle(&fNormalStyle),
fCurrentParagraphStyle(&fParagraphStyle),
fSpanStartOffset(0)
{
_InitStyles();
}
void
MarkupParser::SetStyles(const CharacterStyle& characterStyle,
const ParagraphStyle& paragraphStyle)
{
fNormalStyle = characterStyle;
fParagraphStyle = paragraphStyle;
_InitStyles();
}
TextDocumentRef
MarkupParser::CreateDocumentFromMarkup(const BString& text)
{
TextDocumentRef document(new(std::nothrow) TextDocument(), true);
if (!document.IsSet())
return document;
AppendMarkup(document, text);
return document;
}
void
MarkupParser::AppendMarkup(const TextDocumentRef& document, const BString& text)
{
fTextDocument.SetTo(document);
fCurrentCharacterStyle = &fNormalStyle;
fCurrentParagraphStyle = &fParagraphStyle;
fCurrentParagraph = Paragraph(*fCurrentParagraphStyle);
fSpanStartOffset = 0;
_ParseText(text);
fTextDocument.Unset();
}
void
MarkupParser::_InitStyles()
{
fBoldStyle = fNormalStyle;
fBoldStyle.SetBold(true);
fItalicStyle = fNormalStyle;
fItalicStyle.SetItalic(true);
fBoldItalicStyle = fNormalStyle;
fBoldItalicStyle.SetBold(true);
fBoldItalicStyle.SetItalic(true);
float fontSize = fNormalStyle.Font().Size();
fHeadingStyle = fNormalStyle;
fHeadingStyle.SetFontSize(ceilf(fontSize * 1.15f));
fHeadingStyle.SetBold(true);
fHeadingParagraphStyle = fParagraphStyle;
fHeadingParagraphStyle.SetSpacingTop(ceilf(fontSize * 0.8f));
fHeadingParagraphStyle.SetSpacingBottom(ceilf(fontSize * 0.5f));
fHeadingParagraphStyle.SetJustify(false);
fBulletStyle = fParagraphStyle;
fBulletStyle.SetBullet(Bullet("•", fontSize));
fBulletStyle.SetLineInset(ceilf(fontSize * 0.8f));
}
void
MarkupParser::_ParseText(const BString& text)
{
int32 start = 0;
int32 offset = 0;
int32 charCount = text.CountChars();
const char* c = text.String();
while (offset <= charCount) {
uint32 nextChar = UTF8ToCharCode(&c);
switch (nextChar) {
case '\n':
_CopySpan(text, start, offset);
if (offset > 0 && c[-1] != ' ')
_FinishParagraph(offset >= charCount);
start = offset + 1;
break;
case '\0':
_CopySpan(text, start, offset);
_FinishParagraph(true);
start = offset + 1;
break;
case '\'':
if (offset + 2 < charCount && c[0] == '\'') {
int32 tickCount = 2;
if (c[1] == '\'')
tickCount = 3;
_CopySpan(text, start, offset);
if (tickCount == 2)
_ToggleStyle(fItalicStyle);
else if (tickCount == 3)
_ToggleStyle(fBoldStyle);
offset += tickCount - 1;
start = offset + 1;
c += tickCount - 1;
}
break;
case '=':
if (offset == start
&& fCurrentParagraph.IsEmpty()
&& offset + 2 < charCount
&& c[0] == '=' && c[1] == ' ') {
fCurrentParagraph.SetStyle(fHeadingParagraphStyle);
fCurrentCharacterStyle = &fHeadingStyle;
offset += 2;
c += 2;
start = offset + 1;
} else if (offset > start
&& offset + 2 < charCount
&& c[0] == '=' && c[1] == '\n') {
_CopySpan(text, start, offset - 1);
offset += 2;
c += 2;
_FinishParagraph(offset >= charCount);
start = offset + 1;
}
break;
case ' ':
if (offset == start
&& fCurrentParagraph.IsEmpty()
&& offset + 2 < charCount
&& (c[0] == '*' || c[0] == '-') && c[1] == ' ') {
fCurrentParagraph.SetStyle(fBulletStyle);
offset += 2;
c += 2;
start = offset + 1;
}
break;
case '*':
case '-':
if (offset == start
&& fCurrentParagraph.IsEmpty()
&& offset + 1 < charCount
&& c[0] == ' ') {
fCurrentParagraph.SetStyle(fBulletStyle);
offset += 1;
c += 1;
start = offset + 1;
}
break;
default:
break;
}
offset++;
}
}
void
MarkupParser::_CopySpan(const BString& text, int32& start, int32 end)
{
if (start >= end)
return;
BString subString;
text.CopyCharsInto(subString, start, end - start);
fCurrentParagraph.Append(TextSpan(subString, *fCurrentCharacterStyle));
start = end;
}
void
MarkupParser::_ToggleStyle(const CharacterStyle& style)
{
if (fCurrentCharacterStyle == &style)
fCurrentCharacterStyle = &fNormalStyle;
else
fCurrentCharacterStyle = &style;
}
void
MarkupParser::_FinishParagraph(bool isLast)
{
if (!isLast)
fCurrentParagraph.Append(TextSpan("\n", *fCurrentCharacterStyle));
if (fCurrentParagraph.IsEmpty()) {
fCurrentParagraph.Append(TextSpan("", fNormalStyle));
}
fTextDocument->Append(fCurrentParagraph);
fCurrentParagraph.Clear();
fCurrentParagraph.SetStyle(fParagraphStyle);
fCurrentCharacterStyle = &fNormalStyle;
}