[fbreader] Refactored CSS and XHTML support
This significantly improves EPUB rendering.
This commit is contained in:
parent
ab68bef4e6
commit
c6e46b94a2
34 changed files with 1349 additions and 590 deletions
|
@ -1,24 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DefaultStyles>
|
||||
<style id="0" name="Regular Paragraph" firstLineIndentDelta="40" allowHyphenations="true"/>
|
||||
<style id="0" name="Regular Paragraph" firstLineIndentDelta="1em" allowHyphenations="true"/>
|
||||
<style id="1" name="Title" fontSizeDelta="10" bold="true" spaceBefore="2" spaceAfter="7" alignment="center" allowHyphenations="false"/>
|
||||
<style id="3" name="Poem Title" fontSizeDelta="2" bold="true" spaceBefore="6" spaceAfter="6" leftIndent="40" allowHyphenations="false"/>
|
||||
<style id="2" name="Section Title" fontSizeDelta="6" bold="true" spaceAfter="5" alignment="center" allowHyphenations="false"/>
|
||||
<style id="31" name="Header 1" fontSizeDelta="6" bold="true" spaceAfter="5" alignment="center" allowHyphenations="false"/>
|
||||
<style id="32" name="Header 2" fontSizeDelta="6" bold="true" spaceAfter="5" alignment="center" allowHyphenations="false"/>
|
||||
<style id="5" name="Annotation" fontSizeDelta="-2" firstLineIndentDelta="20" allowHyphenations="true"/>
|
||||
<style id="6" name="Epigraph" fontSizeDelta="-2" italic="true" leftIndent="80" allowHyphenations="true"/>
|
||||
<style id="4" name="Subtitle" bold="true" allowHyphenations="true"/>
|
||||
<style id="33" name="Header 3" bold="true" allowHyphenations="true"/>
|
||||
<style id="34" name="Header 4" bold="true" allowHyphenations="true"/>
|
||||
<style id="13" name="Author" leftIndent="20" allowHyphenations="false"/>
|
||||
<style id="14" name="Date" leftIndent="40" allowHyphenations="false"/>
|
||||
<style id="7" name="Stanza" spaceBefore="6" spaceAfter="6" alignment="linestart" allowHyphenations="false"/>
|
||||
<style id="8" name="Verse" leftIndent="20" alignment="linestart" allowHyphenations="false"/>
|
||||
<style id="10" name="Image" spaceBefore="8" alignment="center"/>
|
||||
<style id="10" name="Image" spaceBefore="8" spaceAfter="8" alignment="center"/>
|
||||
<style id="23" name="Contents Table" spaceAfter="7" leftIndent="20" firstLineIndentDelta="-20" alignment="linestart"/>
|
||||
<style id="25" name="Library Entry" alignment="linestart" allowHyphenations="false"/>
|
||||
<style id="9" name="Preformatted text" italic="true" alignment="linestart" allowHyphenations="false"/>
|
||||
<style id="9" name="Preformatted text" firstLineIndentDelta="0" alignment="linestart" allowHyphenations="false" family="monospace"/>
|
||||
<style id="31" name="Header 1" firstLineIndentDelta="0" fontSizeDelta="6" spaceAfter="1em" bold="true" allowHyphenations="false"/>
|
||||
<style id="32" name="Header 2" firstLineIndentDelta="0" fontSizeDelta="4" spaceAfter="1em" bold="true" allowHyphenations="false"/>
|
||||
<style id="33" name="Header 3" firstLineIndentDelta="0" spaceBefore="1em" spaceAfter="1em" bold="true" allowHyphenations="true"/>
|
||||
<style id="34" name="Header 4" firstLineIndentDelta="0" spaceBefore="1em" spaceAfter="1em" bold="true" allowHyphenations="true"/>
|
||||
<style id="35" name="Header 5" firstLineIndentDelta="0" spaceBefore="1em" spaceAfter="1em" bold="true"/>
|
||||
<style id="36" name="Header 6" firstLineIndentDelta="0" spaceBefore="1em" spaceAfter="1em" bold="true"/>
|
||||
<style id="39" name="Quote" spaceBefore="1em" spaceAfter="1em" leftIndent="2em" rightIndent="1em"/>
|
||||
|
||||
<style id="12" partial="true" name="Cite" italic="true"/>
|
||||
<style id="15" partial="true" name="Internal Hyperlink" allowHyphenations="false" hyperlink="internal"/>
|
||||
|
@ -27,14 +30,12 @@
|
|||
<style id="16" partial="true" name="Footnote" fontSizeDelta="-6" vShift="10" allowHyphenations="false" hyperlink="internal"/>
|
||||
<style id="17" partial="true" name="Emphasis" italic="true"/>
|
||||
<style id="18" partial="true" name="Strong" bold="true"/>
|
||||
<style id="35" name="Header 5" bold="true"/>
|
||||
<style id="36" name="Header 6" bold="true"/>
|
||||
<style id="19" partial="true" name="Subscript" fontSizeDelta="-4" vShift="-4" allowHyphenations="false"/>
|
||||
<style id="20" partial="true" name="Superscript" fontSizeDelta="-4" vShift="10" allowHyphenations="false"/>
|
||||
<style id="21" partial="true" name="Code" italic="false" allowHyphenations="false"/>
|
||||
<style id="21" partial="true" name="Code" firstLineIndentDelta="0" allowHyphenations="false" family="monospace"/>
|
||||
<style id="22" partial="true" name="StrikeThrough"/>
|
||||
<style id="27" partial="true" name="Italic" italic="true"/>
|
||||
<style id="28" partial="true" name="Bold" bold="true"/>
|
||||
<style id="29" partial="true" name="Definition" italic="true"/>
|
||||
<style id="30" partial="true" name="Definition Description" bold="true" italic="true"/>
|
||||
<style id="30" partial="true" name="Definition Description" bold="true" italic="true"/>
|
||||
</DefaultStyles>
|
||||
|
|
|
@ -65,7 +65,7 @@ public:
|
|||
|
||||
bool isDecorated() const;
|
||||
|
||||
const std::string &fontFamily() const;
|
||||
const std::vector<std::string> &fontFamilies() const;
|
||||
|
||||
int fontSize() const;
|
||||
bool bold() const;
|
||||
|
@ -102,10 +102,10 @@ BooksSettings::TextStyle::isDecorated() const
|
|||
return iDefaultStyle->isDecorated();
|
||||
}
|
||||
|
||||
const std::string&
|
||||
BooksSettings::TextStyle::fontFamily() const
|
||||
const std::vector<std::string>&
|
||||
BooksSettings::TextStyle::fontFamilies() const
|
||||
{
|
||||
return iDefaultStyle->fontFamily();
|
||||
return iDefaultStyle->fontFamilies();
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
@ -54,6 +54,11 @@ shared_ptr<ZLTextStyle> BooksTextStyle::defaults()
|
|||
return style;
|
||||
}
|
||||
|
||||
BooksTextStyle::BooksTextStyle()
|
||||
{
|
||||
iFontFamilies.push_back(Default::FONT_FAMILY);
|
||||
}
|
||||
|
||||
bool
|
||||
BooksTextStyle::equalLayout(
|
||||
shared_ptr<ZLTextStyle> aStyle1,
|
||||
|
@ -74,9 +79,9 @@ bool BooksTextStyle::isDecorated() const
|
|||
return false;
|
||||
}
|
||||
|
||||
const std::string& BooksTextStyle::fontFamily() const
|
||||
const std::vector<std::string>& BooksTextStyle::fontFamilies() const
|
||||
{
|
||||
return Default::FONT_FAMILY;
|
||||
return iFontFamilies;
|
||||
}
|
||||
|
||||
int BooksTextStyle::fontSize() const
|
||||
|
|
|
@ -49,18 +49,18 @@ private:
|
|||
static weak_ptr<ZLTextStyle> gInstance;
|
||||
|
||||
private:
|
||||
BooksTextStyle() {}
|
||||
BooksTextStyle();
|
||||
|
||||
public:
|
||||
bool isDecorated() const;
|
||||
|
||||
const std::string &fontFamily() const;
|
||||
const std::vector<std::string>& fontFamilies() const;
|
||||
|
||||
int fontSize() const;
|
||||
bool bold() const;
|
||||
bool italic() const;
|
||||
|
||||
const std::string &colorStyle() const;
|
||||
const std::string& colorStyle() const;
|
||||
|
||||
short spaceBefore(const ZLTextStyleEntry::Metrics& aMetrics) const;
|
||||
short spaceAfter(const ZLTextStyleEntry::Metrics& aMetrics) const;
|
||||
|
@ -73,6 +73,9 @@ public:
|
|||
|
||||
double lineSpace() const;
|
||||
bool allowHyphenations() const;
|
||||
|
||||
private:
|
||||
std::vector<std::string> iFontFamilies;
|
||||
};
|
||||
|
||||
#endif // BOOKS_TEXT_STYLE_H
|
||||
|
|
|
@ -117,6 +117,13 @@ void BookReader::addLineBreak() {
|
|||
}
|
||||
}
|
||||
|
||||
void BookReader::addEmpty() {
|
||||
if (myTextParagraphExists) {
|
||||
flushTextBufferToParagraph();
|
||||
myCurrentTextModel->addEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
void BookReader::addControl(const ZLTextStyleEntry &entry) {
|
||||
if (myTextParagraphExists) {
|
||||
flushTextBufferToParagraph();
|
||||
|
@ -189,7 +196,7 @@ void BookReader::addContentsData(const std::string &data) {
|
|||
|
||||
void BookReader::flushTextBufferToParagraph() {
|
||||
myCurrentTextModel->addText(myBuffer);
|
||||
myBuffer.clear();
|
||||
myBuffer.resize(0);
|
||||
}
|
||||
|
||||
void BookReader::addImage(const std::string &id, shared_ptr<const ZLImage> image) {
|
||||
|
@ -239,7 +246,7 @@ void BookReader::beginContentsParagraph(int referenceNumber) {
|
|||
ZLTextTreeParagraph *peek = myTOCStack.empty() ? 0 : myTOCStack.top();
|
||||
if (!myContentsBuffer.empty()) {
|
||||
contentsModel.addText(myContentsBuffer);
|
||||
myContentsBuffer.clear();
|
||||
myContentsBuffer.resize(0);
|
||||
myLastTOCParagraphIsEmpty = false;
|
||||
}
|
||||
if (myLastTOCParagraphIsEmpty) {
|
||||
|
@ -259,7 +266,7 @@ void BookReader::endContentsParagraph() {
|
|||
ContentsModel &contentsModel = (ContentsModel&)*myModel.myContentsModel;
|
||||
if (!myContentsBuffer.empty()) {
|
||||
contentsModel.addText(myContentsBuffer);
|
||||
myContentsBuffer.clear();
|
||||
myContentsBuffer.resize(0);
|
||||
myLastTOCParagraphIsEmpty = false;
|
||||
}
|
||||
if (myLastTOCParagraphIsEmpty) {
|
||||
|
|
|
@ -59,6 +59,7 @@ public:
|
|||
void addHyperlinkLabel(const std::string &label, int paragraphNumber);
|
||||
void addFixedHSpace(unsigned char length);
|
||||
void addLineBreak();
|
||||
void addEmpty();
|
||||
|
||||
void addImageReference(const std::string &id, short vOffset = 0);
|
||||
void addImage(const std::string &id, shared_ptr<const ZLImage> image);
|
||||
|
|
|
@ -65,6 +65,7 @@ enum FBTextKind {
|
|||
H6 = 36,
|
||||
EXTERNAL_HYPERLINK = 37,
|
||||
BOOK_HYPERLINK = 38,
|
||||
BLOCKQUOTE = 39
|
||||
};
|
||||
|
||||
#endif /* __FBTEXTKIND_H__ */
|
||||
|
|
|
@ -35,28 +35,39 @@ void StyleSheetTableParser::storeData(const std::string &selector, const StyleSh
|
|||
StyleSheetTable::Style StyleSheetSingleStyleParser::parseString(const char *text) {
|
||||
StyleSheetTable::Style style;
|
||||
if (text) {
|
||||
myReadState = ATTRIBUTE_NAME;
|
||||
reset(ATTRIBUTE_NAME);
|
||||
parse(text, strlen(text), true);
|
||||
if (!myStateStack.empty()) {
|
||||
switch (myStateStack.top()) {
|
||||
case ATTRIBUTE_VALUE_SPACE:
|
||||
case ATTRIBUTE_VALUE_COMMA:
|
||||
finishAttributeValue();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
StyleSheetTable::updateStyle(style, myMap);
|
||||
reset();
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
StyleSheetParser::StyleSheetParser() : myReadState(TAG_NAME), myInsideComment(false), myAtBlockDepth(0) {
|
||||
StyleSheetParser::StyleSheetParser() :
|
||||
myBuffer1(0), myBuffer2(0), myBuffer3(0) {
|
||||
myStateStack.push(SELECTOR);
|
||||
}
|
||||
|
||||
StyleSheetParser::~StyleSheetParser() {
|
||||
}
|
||||
|
||||
void StyleSheetParser::reset() {
|
||||
myWord.erase();
|
||||
myAttributeName.erase();
|
||||
myReadState = TAG_NAME;
|
||||
myInsideComment = false;
|
||||
myAtBlockDepth = 0;
|
||||
mySelectors.clear();
|
||||
void StyleSheetParser::reset(ReadState state) {
|
||||
myWord.resize(0);
|
||||
myAttributeName.resize(0);
|
||||
myStateStack = std::stack<ReadState>();
|
||||
myStateStack.push(state);
|
||||
mySelectors.resize(0);
|
||||
myMap.clear();
|
||||
myBuffer1 = myBuffer2 = myBuffer3 = 0;
|
||||
}
|
||||
|
||||
void StyleSheetParser::parse(ZLInputStream &stream) {
|
||||
|
@ -78,165 +89,279 @@ void StyleSheetParser::parse(const char *text, int len, bool final) {
|
|||
const char *start = text;
|
||||
const char *end = text + len;
|
||||
for (const char *ptr = start; ptr != end; ++ptr) {
|
||||
if ((myReadState != TAG_NAME && isspace(*ptr)) ||
|
||||
(myReadState == TAG_NAME && *ptr == ',')) {
|
||||
if (start != ptr) {
|
||||
myWord.append(start, ptr - start);
|
||||
}
|
||||
processWord(myWord);
|
||||
myWord.erase();
|
||||
start = ptr + 1;
|
||||
} else if (isControlSymbol(*ptr)) {
|
||||
if (start != ptr) {
|
||||
myWord.append(start, ptr - start);
|
||||
}
|
||||
processWord(myWord);
|
||||
myWord.erase();
|
||||
if (!myInsideComment) {
|
||||
processControl(*ptr);
|
||||
}
|
||||
start = ptr + 1;
|
||||
}
|
||||
}
|
||||
if (start < end) {
|
||||
myWord.append(start, end - start);
|
||||
if (final) {
|
||||
processWord(myWord);
|
||||
myWord.erase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StyleSheetParser::isControlSymbol(const char symbol) {
|
||||
switch (symbol) {
|
||||
case '{':
|
||||
case '}':
|
||||
case ';':
|
||||
case ':':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
processChar1(*ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void StyleSheetParser::storeData(const std::string&, const StyleSheetTable::AttributeMap&) {
|
||||
}
|
||||
|
||||
void StyleSheetParser::processControl(const char control) {
|
||||
switch (control) {
|
||||
case '{':
|
||||
switch (myReadState) {
|
||||
case AT_RULE:
|
||||
myReadState = AT_BLOCK;
|
||||
myAtBlockDepth = 1;
|
||||
break;
|
||||
case AT_BLOCK:
|
||||
myAtBlockDepth++;
|
||||
break;
|
||||
case TAG_NAME:
|
||||
myReadState = ATTRIBUTE_NAME;
|
||||
myMap.clear();
|
||||
break;
|
||||
default:
|
||||
myReadState = BROKEN;
|
||||
break;
|
||||
/* Converts \r\n into \n */
|
||||
void StyleSheetParser::processChar1(char c) {
|
||||
if (myBuffer1 == '\r') {
|
||||
myBuffer1 = 0;
|
||||
if (c == '\n') {
|
||||
processChar2('\n');
|
||||
} else {
|
||||
processChar2('\r');
|
||||
}
|
||||
} else if (c == '\r') {
|
||||
myBuffer1 = '\r';
|
||||
} else {
|
||||
processChar2(c);
|
||||
}
|
||||
}
|
||||
|
||||
/* Glues continued lines together */
|
||||
void StyleSheetParser::processChar2(char c) {
|
||||
if (myBuffer2 == '\\') {
|
||||
myBuffer2 = 0;
|
||||
if (c != '\n') {
|
||||
processChar3('\\');
|
||||
processChar3(c);
|
||||
}
|
||||
} else if (c == '\\') {
|
||||
myBuffer2 = '\\';
|
||||
} else {
|
||||
processChar3(c);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handles comments */
|
||||
void StyleSheetParser::processChar3(char c) {
|
||||
switch (myStateStack.top()) {
|
||||
case COMMENT:
|
||||
if (myBuffer3 == '*' && c == '/') {
|
||||
myBuffer3 = 0;
|
||||
myStateStack.pop();
|
||||
} else {
|
||||
myBuffer3 = (c == '*') ? '*' : 0;
|
||||
}
|
||||
break;
|
||||
case STRING_LITERAL_SINGLE:
|
||||
case STRING_LITERAL_DOUBLE:
|
||||
processChar4(c);
|
||||
break;
|
||||
default:
|
||||
if (myBuffer3 == '/' && c == '*') {
|
||||
myStateStack.push(COMMENT);
|
||||
} else {
|
||||
if (myBuffer3) processChar4(myBuffer3);
|
||||
if (c == '/') {
|
||||
myBuffer3 = c;
|
||||
} else {
|
||||
myBuffer3 = 0;
|
||||
processChar4(c);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handles clean input */
|
||||
void StyleSheetParser::processChar4(char c) {
|
||||
switch (myStateStack.top()) {
|
||||
case SELECTOR:
|
||||
switch (c) {
|
||||
case ',':
|
||||
if (ZLStringUtil::stripWhiteSpaces(myWord)) {
|
||||
mySelectors.push_back(myWord);
|
||||
myWord.resize(0);
|
||||
}
|
||||
break;
|
||||
case '{':
|
||||
if (ZLStringUtil::stripWhiteSpaces(myWord)) mySelectors.push_back(myWord);
|
||||
if (!mySelectors.empty()) {
|
||||
if (mySelectors[0][0] == '@') {
|
||||
// Ignore AT-rules
|
||||
mySelectors.resize(0);
|
||||
myStateStack.push(SKIP_BLOCK_CURLY);
|
||||
} else {
|
||||
myMap.clear();
|
||||
myStateStack.push(ATTRIBUTE_NAME);
|
||||
}
|
||||
} else {
|
||||
myStateStack.push(SKIP_BLOCK_CURLY);
|
||||
}
|
||||
myWord.resize(0);
|
||||
break;
|
||||
default:
|
||||
if (!isspace(c) || !myWord.empty()) {
|
||||
myWord.append(1, c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ATTRIBUTE_NAME:
|
||||
switch (c) {
|
||||
case ':':
|
||||
if (ZLStringUtil::stripWhiteSpaces(myWord)) {
|
||||
myAttributeName = myWord;
|
||||
myMap[myAttributeName].resize(0);
|
||||
myWord.resize(0);
|
||||
static const std::string FONT_FAMILY("font-family");
|
||||
if (myAttributeName == FONT_FAMILY) {
|
||||
myStateStack.top() = ATTRIBUTE_VALUE_COMMA;
|
||||
} else {
|
||||
myStateStack.top() = ATTRIBUTE_VALUE_SPACE;
|
||||
}
|
||||
} else {
|
||||
finishAttribute();
|
||||
myStateStack.top() = ATTRIBUTE_IGNORE;
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
case ';':
|
||||
finishAttribute();
|
||||
break;
|
||||
case '}':
|
||||
switch (myReadState) {
|
||||
case AT_BLOCK:
|
||||
if (--myAtBlockDepth > 0) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case AT_RULE:
|
||||
case BROKEN:
|
||||
break;
|
||||
default:
|
||||
for (unsigned int i=0; i<mySelectors.size(); i++) {
|
||||
storeData(mySelectors[i], myMap);
|
||||
}
|
||||
break;
|
||||
finishRule();
|
||||
myStateStack.pop();
|
||||
break;
|
||||
default:
|
||||
if (!isspace(c) || !myWord.empty()) {
|
||||
myWord.append(1, c);
|
||||
}
|
||||
myReadState = TAG_NAME;
|
||||
mySelectors.clear();
|
||||
myMap.clear();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ATTRIBUTE_VALUE_SPACE:
|
||||
case ATTRIBUTE_VALUE_COMMA:
|
||||
switch (c) {
|
||||
case '\'':
|
||||
myStateStack.push(STRING_LITERAL_SINGLE);
|
||||
break;
|
||||
case '"':
|
||||
myStateStack.push(STRING_LITERAL_DOUBLE);
|
||||
break;
|
||||
case '}':
|
||||
finishAttributeValue();
|
||||
finishRule();
|
||||
myStateStack.pop();
|
||||
break;
|
||||
case ';':
|
||||
switch (myReadState) {
|
||||
case AT_RULE:
|
||||
myReadState = TAG_NAME;
|
||||
mySelectors.clear();
|
||||
case '\n':
|
||||
finishAttributeValue();
|
||||
finishAttribute();
|
||||
myStateStack.top() = ATTRIBUTE_NAME;
|
||||
break;
|
||||
case ',':
|
||||
if (myStateStack.top() == ATTRIBUTE_VALUE_COMMA) {
|
||||
finishAttributeValue();
|
||||
} else {
|
||||
myWord.append(1, c);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (isspace(c)) {
|
||||
if (myStateStack.top() == ATTRIBUTE_VALUE_SPACE) {
|
||||
finishAttributeValue();
|
||||
} else if (!myWord.empty()) {
|
||||
myWord.append(1, c);
|
||||
}
|
||||
} else {
|
||||
myWord.append(1, c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ATTRIBUTE_IGNORE:
|
||||
switch (c) {
|
||||
case '\n':
|
||||
case ';':
|
||||
finishAttribute();
|
||||
myStateStack.top() = ATTRIBUTE_NAME;
|
||||
break;
|
||||
case '}':
|
||||
finishRule();
|
||||
myStateStack.pop();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case STRING_LITERAL_SINGLE:
|
||||
case STRING_LITERAL_DOUBLE:
|
||||
if (c == myStateStack.top()) {
|
||||
myStateStack.pop();
|
||||
} else if (c == '\n') {
|
||||
// User agents must close strings upon reaching
|
||||
// the end of a line (i.e., before an unescaped
|
||||
// line feed, carriage return or form feed character),
|
||||
// but then drop the construct (declaration or rule)
|
||||
// in which the string was found.
|
||||
myStateStack.pop();
|
||||
switch (myStateStack.top()) {
|
||||
case ATTRIBUTE_VALUE_SPACE:
|
||||
case ATTRIBUTE_VALUE_COMMA:
|
||||
myStateStack.top() = ATTRIBUTE_IGNORE;
|
||||
myMap[myAttributeName].resize(0);
|
||||
myAttributeName.resize(0);
|
||||
myWord.resize(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
myWord.append(1, c);
|
||||
}
|
||||
break;
|
||||
|
||||
case SKIP_BLOCK_CURLY:
|
||||
case SKIP_BLOCK_SQUARE:
|
||||
switch (c) {
|
||||
case '{':
|
||||
myStateStack.push(SKIP_BLOCK_CURLY);
|
||||
break;
|
||||
case '[':
|
||||
myStateStack.push(SKIP_BLOCK_SQUARE);
|
||||
break;
|
||||
case '\'':
|
||||
myStateStack.push(STRING_LITERAL_SINGLE);
|
||||
break;
|
||||
case '"':
|
||||
myStateStack.push(STRING_LITERAL_DOUBLE);
|
||||
break;
|
||||
default:
|
||||
if (c == myStateStack.top()) {
|
||||
myStateStack.pop();
|
||||
if (myStateStack.top() == SELECTOR) {
|
||||
myWord.resize(0);
|
||||
mySelectors.resize(0);
|
||||
myMap.clear();
|
||||
break;
|
||||
case AT_BLOCK:
|
||||
break;
|
||||
case ATTRIBUTE_VALUE:
|
||||
case ATTRIBUTE_NAME:
|
||||
myReadState = ATTRIBUTE_NAME;
|
||||
break;
|
||||
default:
|
||||
myReadState = BROKEN;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
switch (myReadState) {
|
||||
case AT_BLOCK:
|
||||
break;
|
||||
case ATTRIBUTE_NAME:
|
||||
myReadState = ATTRIBUTE_VALUE;
|
||||
break;
|
||||
default:
|
||||
myReadState = BROKEN;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void StyleSheetParser::processWord(std::string &word) {
|
||||
while (!word.empty()) {
|
||||
int index = word.find(myInsideComment ? "*/" : "/*");
|
||||
if (!myInsideComment) {
|
||||
if (index == -1) {
|
||||
processWordWithoutComments(word);
|
||||
} else if (index > 0) {
|
||||
processWordWithoutComments(word.substr(0, index));
|
||||
}
|
||||
}
|
||||
if (index == -1) {
|
||||
break;
|
||||
}
|
||||
myInsideComment = !myInsideComment;
|
||||
word.erase(0, index + 2);
|
||||
}
|
||||
}
|
||||
|
||||
void StyleSheetParser::processWordWithoutComments(std::string word) {
|
||||
switch (myReadState) {
|
||||
case AT_RULE:
|
||||
case AT_BLOCK:
|
||||
break;
|
||||
case TAG_NAME:
|
||||
ZLStringUtil::stripWhiteSpaces(word);
|
||||
if (!word.empty()) {
|
||||
if (word[0] == '@') {
|
||||
myReadState = AT_RULE;
|
||||
|
||||
} else {
|
||||
mySelectors.push_back(word);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ATTRIBUTE_NAME:
|
||||
myAttributeName = word;
|
||||
myMap[myAttributeName].clear();
|
||||
break;
|
||||
case ATTRIBUTE_VALUE:
|
||||
myMap[myAttributeName].push_back(word);
|
||||
break;
|
||||
case BROKEN:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case COMMENT: // Comments are handled elsewhere
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void StyleSheetParser::finishAttributeValue() {
|
||||
if (ZLStringUtil::stripWhiteSpaces(myWord)) {
|
||||
myMap[myAttributeName].push_back(myWord);
|
||||
myWord.resize(0);
|
||||
}
|
||||
}
|
||||
|
||||
void StyleSheetParser::finishAttribute() {
|
||||
myAttributeName.resize(0);
|
||||
myWord.resize(0);
|
||||
}
|
||||
|
||||
void StyleSheetParser::finishRule() {
|
||||
for (unsigned int i=0; i<mySelectors.size(); i++) {
|
||||
storeData(mySelectors[i], myMap);
|
||||
}
|
||||
myAttributeName.resize(0);
|
||||
myWord.resize(0);
|
||||
mySelectors.resize(0);
|
||||
myMap.clear();
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#include "StyleSheetTable.h"
|
||||
|
||||
#include <stack>
|
||||
|
||||
class ZLInputStream;
|
||||
|
||||
class StyleSheetParser {
|
||||
|
@ -39,26 +41,37 @@ protected:
|
|||
virtual void storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map);
|
||||
|
||||
private:
|
||||
bool isControlSymbol(const char symbol);
|
||||
void processWord(std::string &word);
|
||||
void processWordWithoutComments(std::string word);
|
||||
void processControl(const char control);
|
||||
enum ReadState {
|
||||
COMMENT,
|
||||
SELECTOR,
|
||||
ATTRIBUTE_NAME,
|
||||
ATTRIBUTE_VALUE_SPACE,
|
||||
ATTRIBUTE_VALUE_COMMA,
|
||||
ATTRIBUTE_IGNORE,
|
||||
STRING_LITERAL_SINGLE = '\'',
|
||||
STRING_LITERAL_DOUBLE = '"',
|
||||
SKIP_BLOCK_CURLY = '}',
|
||||
SKIP_BLOCK_SQUARE = ']'
|
||||
};
|
||||
|
||||
void reset(ReadState state);
|
||||
void processChar1(char c);
|
||||
void processChar2(char c);
|
||||
void processChar3(char c);
|
||||
void processChar4(char c);
|
||||
void finishRule();
|
||||
void finishAttribute();
|
||||
void finishAttributeValue();
|
||||
|
||||
private:
|
||||
std::string myWord;
|
||||
std::string myAttributeName;
|
||||
enum {
|
||||
AT_RULE,
|
||||
AT_BLOCK,
|
||||
TAG_NAME,
|
||||
ATTRIBUTE_NAME,
|
||||
ATTRIBUTE_VALUE,
|
||||
BROKEN,
|
||||
} myReadState;
|
||||
bool myInsideComment;
|
||||
int myAtBlockDepth;
|
||||
std::stack<ReadState> myStateStack;
|
||||
std::vector<std::string> mySelectors;
|
||||
StyleSheetTable::AttributeMap myMap;
|
||||
char myBuffer1;
|
||||
char myBuffer2;
|
||||
char myBuffer3;
|
||||
|
||||
friend class StyleSheetSingleStyleParser;
|
||||
};
|
||||
|
@ -81,4 +94,6 @@ public:
|
|||
StyleSheetTable::Style parseString(const char *text);
|
||||
};
|
||||
|
||||
inline void StyleSheetParser::reset() { reset(SELECTOR); }
|
||||
|
||||
#endif /* __STYLESHEETPARSER_H__ */
|
||||
|
|
|
@ -64,6 +64,30 @@ void StyleSheetTable::Style::apply(const Style &other) {
|
|||
if (other.PageBreakAfter != B3_UNDEFINED) {
|
||||
PageBreakAfter = other.PageBreakAfter;
|
||||
}
|
||||
if (other.WhiteSpace != WS_UNDEFINED) {
|
||||
WhiteSpace = other.WhiteSpace;
|
||||
}
|
||||
if (other.DisplayNone) {
|
||||
DisplayNone = other.DisplayNone;
|
||||
}
|
||||
}
|
||||
|
||||
void StyleSheetTable::Style::inherit(const Style &other) {
|
||||
TextStyle.inherit(other.TextStyle);
|
||||
if (other.WhiteSpace != WS_UNDEFINED) {
|
||||
WhiteSpace = other.WhiteSpace;
|
||||
}
|
||||
if (other.DisplayNone) {
|
||||
DisplayNone = other.DisplayNone;
|
||||
}
|
||||
}
|
||||
|
||||
bool StyleSheetTable::Style::equals(const Style &other) const {
|
||||
return PageBreakBefore == other.PageBreakBefore &&
|
||||
PageBreakAfter == other.PageBreakAfter &&
|
||||
WhiteSpace == other.WhiteSpace &&
|
||||
DisplayNone == other.DisplayNone &&
|
||||
TextStyle.equals(other.TextStyle);
|
||||
}
|
||||
|
||||
bool StyleSheetTable::Entry::match(const ElementList &stack) const {
|
||||
|
@ -93,54 +117,28 @@ bool StyleSheetTable::Entry::match(const ElementList &stack) const {
|
|||
|
||||
void StyleSheetTable::addMap(const std::vector<std::string> &selectors, const AttributeMap &map) {
|
||||
if ((!selectors.empty()) && !map.empty()) {
|
||||
// http://www.w3.org/TR/selectors/#specificity
|
||||
int a = 0, b = 0, c = 0;
|
||||
SelectorList stack;
|
||||
for (unsigned int i=0; i<selectors.size(); i++) {
|
||||
const Selector &selector = selectors[i];
|
||||
a += selector.a();
|
||||
b += selector.b();
|
||||
c += selector.c();
|
||||
stack.push_back(selector);
|
||||
}
|
||||
if (a > 255) a = 255;
|
||||
if (b > 255) b = 255;
|
||||
if (c > 255) c = 255;
|
||||
myEntries.push_back(Entry(stack, (a << 16) + (b << 8) + c, map));
|
||||
}
|
||||
}
|
||||
|
||||
bool StyleSheetTable::parseLength(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) {
|
||||
if (!toParse.empty()) {
|
||||
if (ZLStringUtil::stringEndsWith(toParse, "%")) {
|
||||
unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT;
|
||||
size = atoi(toParse.c_str());
|
||||
return true;
|
||||
} else if (ZLStringUtil::stringEndsWith(toParse, "em")) {
|
||||
unit = ZLTextStyleEntry::SIZE_UNIT_EM_100;
|
||||
size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0));
|
||||
return true;
|
||||
} else if (ZLStringUtil::stringEndsWith(toParse, "ex")) {
|
||||
unit = ZLTextStyleEntry::SIZE_UNIT_EX_100;
|
||||
size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0));
|
||||
return true;
|
||||
} else if (ZLStringUtil::stringEndsWith(toParse, "px") ||
|
||||
ZLStringUtil::stringEndsWith(toParse, "pt") ||
|
||||
ZLStringUtil::stringEndsWith(toParse, "pc")) {
|
||||
unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
|
||||
size = atoi(toParse.c_str());
|
||||
return true;
|
||||
} else if (toParse == "0") {
|
||||
unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
|
||||
size = 0;
|
||||
return true;
|
||||
Style style(map);
|
||||
if (!style.empty()) {
|
||||
// http://www.w3.org/TR/selectors/#specificity
|
||||
int a = 0, b = 0, c = 0;
|
||||
SelectorList stack;
|
||||
for (unsigned int i=0; i<selectors.size(); i++) {
|
||||
const Selector &selector = selectors[i];
|
||||
a += selector.a();
|
||||
b += selector.b();
|
||||
c += selector.c();
|
||||
stack.push_back(selector);
|
||||
}
|
||||
if (a > 255) a = 255;
|
||||
if (b > 255) b = 255;
|
||||
if (c > 255) c = 255;
|
||||
myEntries.push_back(Entry(stack, (a << 16) + (b << 8) + c, style));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StyleSheetTable::parseMargin(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) {
|
||||
if (parseLength(toParse, size, unit)) {
|
||||
if (ZLTextStyleEntry::parseLength(toParse, size, unit)) {
|
||||
// Negative margins do make sense but we don't really support them
|
||||
if (size < 0) size = 0;
|
||||
return true;
|
||||
|
@ -166,7 +164,7 @@ void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Lengt
|
|||
void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value) {
|
||||
short size;
|
||||
ZLTextStyleEntry::SizeUnit unit;
|
||||
if (parseLength(value, size, unit)) {
|
||||
if (ZLTextStyleEntry::parseLength(value, size, unit)) {
|
||||
entry.setLength(name, size, unit);
|
||||
}
|
||||
}
|
||||
|
@ -200,9 +198,11 @@ void StyleSheetTable::applyStyles(const ElementList &stack, Style &style) const
|
|||
entries.push_back(&(*i));
|
||||
}
|
||||
}
|
||||
std::sort(entries.begin(), entries.end(), sortBySpecificity);
|
||||
for (std::vector<const Entry*>::const_iterator e = entries.begin(); e != entries.end(); ++e) {
|
||||
style.apply((*e)->Style);
|
||||
if (!entries.empty()) {
|
||||
std::sort(entries.begin(), entries.end(), sortBySpecificity);
|
||||
for (std::vector<const Entry*>::const_iterator e = entries.begin(); e != entries.end(); ++e) {
|
||||
style.apply((*e)->Style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,60 +216,77 @@ const std::vector<std::string> &StyleSheetTable::values(const AttributeMap &map,
|
|||
}
|
||||
|
||||
void StyleSheetTable::updateTextStyle(ZLTextStyleEntry &entry, const AttributeMap &styles) {
|
||||
const std::vector<std::string> &alignment = values(styles, "text-align");
|
||||
static const std::string TEXT_ALIGN("text-align");
|
||||
const std::vector<std::string> &alignment = values(styles, TEXT_ALIGN);
|
||||
if (!alignment.empty()) {
|
||||
if (alignment[0] == "justify") {
|
||||
const std::string &value = alignment.at(0);
|
||||
if (value == "justify") {
|
||||
entry.setAlignmentType(ALIGN_JUSTIFY);
|
||||
} else if (alignment[0] == "left") {
|
||||
} else if (value == "left") {
|
||||
entry.setAlignmentType(ALIGN_LEFT);
|
||||
} else if (alignment[0] == "right") {
|
||||
} else if (value == "right") {
|
||||
entry.setAlignmentType(ALIGN_RIGHT);
|
||||
} else if (alignment[0] == "center") {
|
||||
} else if (value == "center") {
|
||||
entry.setAlignmentType(ALIGN_CENTER);
|
||||
}
|
||||
} else {
|
||||
static const std::string FLOAT("float");
|
||||
const std::vector<std::string> &floatVal = values(styles, FLOAT);
|
||||
if (!floatVal.empty()) {
|
||||
const std::string &value = floatVal.at(0);
|
||||
if (value == "left") {
|
||||
entry.setAlignmentType(ALIGN_LEFT);
|
||||
} else if (value == "right") {
|
||||
entry.setAlignmentType(ALIGN_RIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string> &bold = values(styles, "font-weight");
|
||||
if (!bold.empty()) {
|
||||
static const std::string FONT_WEIGHT("font-weight");
|
||||
const std::vector<std::string> &weight = values(styles, FONT_WEIGHT);
|
||||
if (!weight.empty()) {
|
||||
int num = -1;
|
||||
if (bold[0] == "bold") {
|
||||
const std::string &value = weight.at(0);
|
||||
if (value == "bold") {
|
||||
num = 700;
|
||||
} else if (bold[0] == "normal") {
|
||||
} else if (value == "normal") {
|
||||
num = 400;
|
||||
} else if ((bold[0].length() == 3) &&
|
||||
(bold[0][1] == '0') &&
|
||||
(bold[0][2] == '0') &&
|
||||
(bold[0][0] >= '1') &&
|
||||
(bold[0][0] <= '9')) {
|
||||
num = 100 * (bold[0][0] - '0');
|
||||
} else if (bold[0] == "bolder") {
|
||||
} else if (bold[0] == "lighter") {
|
||||
} else if ((value.length() == 3) &&
|
||||
(value[1] == '0') &&
|
||||
(value[2] == '0') &&
|
||||
(value[0] >= '1') &&
|
||||
(value[0] <= '9')) {
|
||||
num = 100 * (value[0] - '0');
|
||||
} else if (value == "bolder") {
|
||||
} else if (value == "lighter") {
|
||||
}
|
||||
if (num != -1) {
|
||||
entry.setFontModifier(FONT_MODIFIER_BOLD, num >= 600);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string> &italic = values(styles, "font-style");
|
||||
static const std::string FONT_STYLE("font-style");
|
||||
const std::vector<std::string> &italic = values(styles, FONT_STYLE);
|
||||
if (!italic.empty()) {
|
||||
entry.setFontModifier(FONT_MODIFIER_ITALIC, italic[0] == "italic");
|
||||
}
|
||||
|
||||
const std::vector<std::string> &variant = values(styles, "font-variant");
|
||||
static const std::string FONT_VARIANT("font-variant");
|
||||
const std::vector<std::string> &variant = values(styles, FONT_VARIANT);
|
||||
if (!variant.empty()) {
|
||||
entry.setFontModifier(FONT_MODIFIER_SMALLCAPS, variant[0] == "small-caps");
|
||||
}
|
||||
|
||||
const std::vector<std::string> &fontFamily = values(styles, "font-family");
|
||||
if (!fontFamily.empty() && !fontFamily[0].empty()) {
|
||||
entry.setFontFamily(fontFamily[0]);
|
||||
}
|
||||
static const std::string FONT_FAMILY("font-family");
|
||||
const std::vector<std::string> &fontFamilies = values(styles, FONT_FAMILY);
|
||||
if (!fontFamilies.empty()) entry.setFontFamilies(fontFamilies);
|
||||
|
||||
short size;
|
||||
ZLTextStyleEntry::SizeUnit unit;
|
||||
const std::vector<std::string> &fontSize = values(styles, "font-size");
|
||||
static const std::string FONT_SIZE("font-size");
|
||||
const std::vector<std::string> &fontSize = values(styles, FONT_SIZE);
|
||||
if (!fontSize.empty()) {
|
||||
std::string value = fontSize[0];
|
||||
const std::string &value = fontSize.at(0);
|
||||
if (value == "xx-small") {
|
||||
entry.setFontSizeMag(-3);
|
||||
} else if (value == "x-small") {
|
||||
|
@ -285,7 +302,7 @@ void StyleSheetTable::updateTextStyle(ZLTextStyleEntry &entry, const AttributeMa
|
|||
} else if (value == "xx-large") {
|
||||
entry.setFontSizeMag(3);
|
||||
} else {
|
||||
if (parseLength(value, size, unit)) {
|
||||
if (ZLTextStyleEntry::parseLength(value, size, unit)) {
|
||||
switch (unit) {
|
||||
case ZLTextStyleEntry::SIZE_UNIT_PIXEL:
|
||||
// What to do with pixels?
|
||||
|
@ -293,26 +310,68 @@ void StyleSheetTable::updateTextStyle(ZLTextStyleEntry &entry, const AttributeMa
|
|||
case ZLTextStyleEntry::SIZE_UNIT_EM_100:
|
||||
case ZLTextStyleEntry::SIZE_UNIT_EX_100:
|
||||
case ZLTextStyleEntry::SIZE_UNIT_PERCENT:
|
||||
entry.setFontSizeMag((size < 100 && size > 80) ? -1 :
|
||||
(size > 100 && size < 120) ? 1 :
|
||||
(size - 100)/20);
|
||||
// Percent to magnification mapping algorithm
|
||||
// matches ZLTextForcedStyle::fontSize() logic
|
||||
if (size < 100) {
|
||||
if (size >= 80) {
|
||||
entry.setFontSizeMag(-1);
|
||||
} else {
|
||||
int mag;
|
||||
unsigned int x1 = 5*100, x2 = 6*size;
|
||||
// Too many iterations would cause 32-bit
|
||||
// overflow and generally don't make sense.
|
||||
for (mag=1; mag<=6 && x1<x2; ++mag) {
|
||||
x1 *= 5; x2 *= 6;
|
||||
}
|
||||
entry.setFontSizeMag(-mag);
|
||||
}
|
||||
} else if (size > 100) {
|
||||
if (size < 120) {
|
||||
entry.setFontSizeMag(1);
|
||||
} else {
|
||||
int mag;
|
||||
unsigned int x1 = 6*100, x2 = 5*size;
|
||||
for (mag=1; mag<=6 && x1<x2; ++mag) {
|
||||
x1 *= 6; x2 *= 5;
|
||||
}
|
||||
entry.setFontSizeMag(mag);
|
||||
}
|
||||
} else {
|
||||
entry.setFontSizeMag(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string> &opacity = values(styles, "opacity");
|
||||
static const std::string OPACITY("opacity");
|
||||
const std::vector<std::string> &opacity = values(styles, OPACITY);
|
||||
if (!opacity.empty()) {
|
||||
const int value = (int)(255 * ZLStringUtil::stringToDouble(opacity[0], 1));
|
||||
entry.setOpacity((unsigned char)((value < 0) ? 0 : (value > 255) ? 255 : value));
|
||||
}
|
||||
|
||||
std::vector<std::string> margins = values(styles, "margin");
|
||||
if (!margins.empty() && margins.back() == "!important") {
|
||||
// Margins will overwrite padding, sorry
|
||||
static const std::string PADDING_TOP("padding-top");
|
||||
static const std::string PADDING_BOTTOM("padding-bottom");
|
||||
setLength(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, PADDING_TOP);
|
||||
setLength(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, PADDING_BOTTOM);
|
||||
|
||||
static const std::string MARGIN("margin");
|
||||
std::vector<std::string> margins = values(styles, MARGIN);
|
||||
if (!margins.empty()) {
|
||||
// Ignore the "!important" modifier for now
|
||||
margins.pop_back();
|
||||
if (margins.back() == "!important") {
|
||||
margins.pop_back();
|
||||
} else if (margins.back() == "important") {
|
||||
margins.pop_back();
|
||||
if (!margins.empty() && margins.back() == "!") {
|
||||
margins.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (margins.size()) {
|
||||
case 1:
|
||||
if (parseMargin(margins[0], size, unit)) {
|
||||
|
@ -348,49 +407,61 @@ void StyleSheetTable::updateTextStyle(ZLTextStyleEntry &entry, const AttributeMa
|
|||
break;
|
||||
}
|
||||
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, styles, "margin-left");
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, styles, "margin-right");
|
||||
setLength(entry, ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, styles, "text-indent");
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "margin-top");
|
||||
setLength(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "padding-top");
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "margin-bottom");
|
||||
setLength(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "padding-bottom");
|
||||
static const std::string MARGIN_LEFT("margin-left");
|
||||
static const std::string MARGIN_RIGHT("margin-right");
|
||||
static const std::string MARGIN_TOP("margin-top");
|
||||
static const std::string MARGIN_BOTTOM("margin-bottom");
|
||||
static const std::string TEXT_INDENT("text-indent");
|
||||
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, styles, MARGIN_LEFT);
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, styles, MARGIN_RIGHT);
|
||||
setLength(entry, ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, styles, TEXT_INDENT);
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, MARGIN_TOP);
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, MARGIN_BOTTOM);
|
||||
}
|
||||
|
||||
bool StyleSheetTable::getPageBreakBefore(const AttributeMap &map, ZLBoolean3 &value) {
|
||||
const std::vector<std::string> &pbb = values(map, "page-break-before");
|
||||
if (!pbb.empty()) {
|
||||
if ((pbb[0] == "always") ||
|
||||
(pbb[0] == "left") ||
|
||||
(pbb[0] == "right")) {
|
||||
void StyleSheetTable::getPageBreakValue(const std::vector<std::string> &values, ZLBoolean3 &value) {
|
||||
if (!values.empty()) {
|
||||
const std::string &first = values.at(0);
|
||||
if ((first == "always") ||
|
||||
(first == "left") ||
|
||||
(first == "right")) {
|
||||
value = B3_TRUE;
|
||||
return true;
|
||||
} else if (pbb[0] == "avoid") {
|
||||
} else if (first == "avoid") {
|
||||
value = B3_FALSE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StyleSheetTable::getPageBreakAfter(const AttributeMap &map, ZLBoolean3 &value) {
|
||||
const std::vector<std::string> &pba = values(map, "page-break-after");
|
||||
if (!pba.empty()) {
|
||||
if ((pba[0] == "always") ||
|
||||
(pba[0] == "left") ||
|
||||
(pba[0] == "right")) {
|
||||
value = B3_TRUE;
|
||||
return true;
|
||||
} else if (pba[0] == "avoid") {
|
||||
value = B3_FALSE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void StyleSheetTable::updateStyle(Style &style, const AttributeMap &map) {
|
||||
updateTextStyle(style.TextStyle, map);
|
||||
getPageBreakBefore(map, style.PageBreakBefore);
|
||||
getPageBreakAfter(map, style.PageBreakAfter);
|
||||
|
||||
static const std::string PAGE_BREAK_BEFORE("page-break-before");
|
||||
getPageBreakValue(values(map, PAGE_BREAK_BEFORE), style.PageBreakBefore);
|
||||
|
||||
static const std::string PAGE_BREAK_AFTER("page-break-after");
|
||||
getPageBreakValue(values(map, PAGE_BREAK_AFTER), style.PageBreakAfter);
|
||||
|
||||
static const std::string WHITE_SPACE("white-space");
|
||||
const std::vector<std::string> &whiteSpace = values(map, WHITE_SPACE);
|
||||
if (!whiteSpace.empty()) {
|
||||
const std::string &value = whiteSpace.at(0);
|
||||
if (value == "normal") {
|
||||
style.WhiteSpace = WS_NORMAL;
|
||||
} else if (value == "nowrap") {
|
||||
style.WhiteSpace = WS_NOWRAP;
|
||||
} else if (value == "pre") {
|
||||
style.WhiteSpace = WS_PRE;
|
||||
} else if (value == "pre-wrap") {
|
||||
style.WhiteSpace = WS_PRE_WRAP;
|
||||
} else if (value == "pre-line") {
|
||||
style.WhiteSpace = WS_PRE_LINE;
|
||||
}
|
||||
}
|
||||
|
||||
static const std::string DISPLAY("display");
|
||||
const std::vector<std::string> &display = values(map, DISPLAY);
|
||||
if (!display.empty() && display[0] == "none") {
|
||||
style.DisplayNone = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,29 +73,43 @@ public:
|
|||
typedef std::vector<Selector> SelectorList;
|
||||
typedef std::map<std::string,std::vector<std::string> > AttributeMap;
|
||||
|
||||
typedef enum {
|
||||
WS_UNDEFINED,
|
||||
WS_NORMAL,
|
||||
WS_NOWRAP,
|
||||
WS_PRE,
|
||||
WS_PRE_WRAP,
|
||||
WS_PRE_LINE
|
||||
} WhiteSpaceValue;
|
||||
|
||||
struct Style {
|
||||
Style();
|
||||
Style(const Style &other);
|
||||
Style(const AttributeMap &map);
|
||||
|
||||
Style &operator = (const Style &other);
|
||||
void apply(const Style &other);
|
||||
bool operator == (const Style &other) const;
|
||||
bool equals(const Style &other) const;
|
||||
void apply(const Style &other);
|
||||
void inherit(const Style &other);
|
||||
bool empty() const;
|
||||
|
||||
ZLTextStyleEntry TextStyle;
|
||||
ZLBoolean3 PageBreakBefore;
|
||||
ZLBoolean3 PageBreakAfter;
|
||||
WhiteSpaceValue WhiteSpace;
|
||||
bool DisplayNone;
|
||||
};
|
||||
|
||||
typedef std::vector<Style> StyleList;
|
||||
static void updateStyle(Style &style, const AttributeMap &map);
|
||||
static void updateTextStyle(ZLTextStyleEntry &entry, const AttributeMap &map);
|
||||
static bool getPageBreakBefore(const AttributeMap &map, ZLBoolean3 &value);
|
||||
static bool getPageBreakAfter(const AttributeMap &map, ZLBoolean3 &value);
|
||||
static void getPageBreakValue(const std::vector<std::string> &values, ZLBoolean3 &value);
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
Entry();
|
||||
Entry(const SelectorList &selectors, int specificity, const AttributeMap &map);
|
||||
Entry(const SelectorList &selectors, int specificity, const Style &style);
|
||||
Entry &operator = (const Entry &other);
|
||||
bool match(const ElementList &stack) const;
|
||||
|
||||
|
@ -106,13 +120,12 @@ private:
|
|||
|
||||
void addMap(const std::vector<std::string> &selectors, const AttributeMap &map);
|
||||
|
||||
static bool parseLength(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit);
|
||||
static bool parseMargin(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit);
|
||||
static void setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const AttributeMap &map, const std::string &attributeName);
|
||||
static void setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value);
|
||||
static void setMargin(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const AttributeMap &map, const std::string &attributeName);
|
||||
static void setMargin(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value);
|
||||
static const std::vector<std::string> &values(const AttributeMap &map, const std::string &name);
|
||||
static const std::vector<std::string> &values(const AttributeMap &map, const std::string &name);
|
||||
static bool sortBySpecificity(const Entry *e1, const Entry *e2);
|
||||
|
||||
public:
|
||||
|
@ -129,7 +142,7 @@ inline StyleSheetTable::Selector::Selector(const std::string &type, const std::s
|
|||
inline StyleSheetTable::Selector::Selector(const std::string &type, const std::string &klass) : myType(type), myClass(klass) {}
|
||||
inline StyleSheetTable::Selector::Selector(const StyleSheetTable::Selector &other) : myType(other.myType), myClass(other.myClass), myId(other.myId) {}
|
||||
inline StyleSheetTable::Selector::Selector() {}
|
||||
inline StyleSheetTable::Selector &StyleSheetTable::Selector::operator = (const StyleSheetTable::Selector &other) {
|
||||
inline StyleSheetTable::Selector &StyleSheetTable::Selector::operator = (const Selector &other) {
|
||||
myType = other.myType; myClass = other.myClass; myId = other.myId; return *this;
|
||||
}
|
||||
inline bool StyleSheetTable::Selector::operator == (const Selector &other) const {
|
||||
|
@ -138,7 +151,7 @@ inline bool StyleSheetTable::Selector::operator == (const Selector &other) const
|
|||
inline bool StyleSheetTable::Selector::operator != (const Selector &other) const {
|
||||
return (&other != this) && (myType != other.myType || myClass != other.myClass || myId != other.myId);
|
||||
}
|
||||
inline bool StyleSheetTable::Selector::operator < (const StyleSheetTable::Selector &other) const {
|
||||
inline bool StyleSheetTable::Selector::operator < (const Selector &other) const {
|
||||
return myType < other.myType || (myType == other.myType && (myClass < other.myClass || (myClass == other.myClass && myId < other.myId)));
|
||||
}
|
||||
inline bool StyleSheetTable::Selector::match(const Element &element) const {
|
||||
|
@ -151,21 +164,30 @@ inline const std::string &StyleSheetTable::Selector::type() const { return myTyp
|
|||
inline const std::string &StyleSheetTable::Selector::klass() const { return myClass; }
|
||||
inline const std::string &StyleSheetTable::Selector::id() const { return myId; }
|
||||
|
||||
inline StyleSheetTable::Style::Style() : PageBreakBefore(B3_UNDEFINED), PageBreakAfter(B3_UNDEFINED) {}
|
||||
inline StyleSheetTable::Style::Style(const StyleSheetTable::Style &other) : TextStyle(other.TextStyle), PageBreakBefore(other.PageBreakBefore), PageBreakAfter(other.PageBreakAfter) {}
|
||||
inline StyleSheetTable::Style::Style(const StyleSheetTable::AttributeMap &map) : PageBreakBefore(B3_UNDEFINED), PageBreakAfter(B3_UNDEFINED) {
|
||||
inline StyleSheetTable::Style::Style() :
|
||||
PageBreakBefore(B3_UNDEFINED), PageBreakAfter(B3_UNDEFINED), WhiteSpace(WS_UNDEFINED), DisplayNone(false) {}
|
||||
inline StyleSheetTable::Style::Style(const StyleSheetTable::Style &other) :
|
||||
TextStyle(other.TextStyle), PageBreakBefore(other.PageBreakBefore), PageBreakAfter(other.PageBreakAfter), WhiteSpace(other.WhiteSpace), DisplayNone(other.DisplayNone) {}
|
||||
inline StyleSheetTable::Style::Style(const StyleSheetTable::AttributeMap &map) :
|
||||
PageBreakBefore(B3_UNDEFINED), PageBreakAfter(B3_UNDEFINED), WhiteSpace(WS_UNDEFINED), DisplayNone(false) {
|
||||
updateStyle(*this, map);
|
||||
}
|
||||
inline StyleSheetTable::Style &StyleSheetTable::Style::operator = (const StyleSheetTable::Style &other) {
|
||||
inline bool StyleSheetTable::Style::empty() const {
|
||||
return TextStyle.isEmpty() && PageBreakBefore == B3_UNDEFINED && PageBreakAfter == B3_UNDEFINED && WhiteSpace == WS_UNDEFINED && !DisplayNone;
|
||||
}
|
||||
inline bool StyleSheetTable::Style::operator == (const Style &other) const { return equals(other); }
|
||||
inline StyleSheetTable::Style &StyleSheetTable::Style::operator = (const Style &other) {
|
||||
TextStyle = other.TextStyle;
|
||||
PageBreakBefore = other.PageBreakBefore;
|
||||
PageBreakAfter = other.PageBreakAfter;
|
||||
WhiteSpace = other.WhiteSpace;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline StyleSheetTable::Entry::Entry() : Specificity(0) {}
|
||||
inline StyleSheetTable::Entry::Entry(const SelectorList &selectors, int specificity, const AttributeMap &map) : Selectors(selectors), Specificity(specificity), Style(map) {}
|
||||
inline StyleSheetTable::Entry &StyleSheetTable::Entry::operator = (const StyleSheetTable::Entry &other) {
|
||||
inline StyleSheetTable::Entry::Entry(const SelectorList &selectors, int specificity, const StyleSheetTable::Style &style) :
|
||||
Selectors(selectors), Specificity(specificity), Style(style) {}
|
||||
inline StyleSheetTable::Entry &StyleSheetTable::Entry::operator = (const Entry &other) {
|
||||
Selectors = other.Selectors;
|
||||
Style = other.Style;
|
||||
Specificity = other.Specificity;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <ZLUnicodeUtil.h>
|
||||
#include <ZLStringUtil.h>
|
||||
#include <ZLXMLNamespace.h>
|
||||
#include <ZLTextStyleCollection.h>
|
||||
|
||||
#include "XHTMLReader.h"
|
||||
#include "../util/EntityFilesCollector.h"
|
||||
|
@ -90,12 +91,6 @@ public:
|
|||
void doAtEnd(XHTMLReader &reader);
|
||||
};
|
||||
|
||||
class XHTMLTagRestartParagraphAction : public XHTMLTagAction {
|
||||
|
||||
public:
|
||||
void doAtStart(XHTMLReader &reader, const char **xmlattributes);
|
||||
};
|
||||
|
||||
class XHTMLTagLineBreakAction : public XHTMLTagAction {
|
||||
|
||||
public:
|
||||
|
@ -160,7 +155,6 @@ public:
|
|||
XHTMLTagControlAction(FBTextKind control);
|
||||
|
||||
void doAtStart(XHTMLReader &reader, const char **xmlattributes);
|
||||
void doAtEnd(XHTMLReader &reader);
|
||||
|
||||
private:
|
||||
FBTextKind myControl;
|
||||
|
@ -186,10 +180,8 @@ public:
|
|||
};
|
||||
|
||||
void XHTMLTagStyleAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
|
||||
static const std::string TYPE = "text/css";
|
||||
|
||||
const char *type = reader.attributeValue(xmlattributes, "type");
|
||||
if ((type == 0) || (TYPE != type)) {
|
||||
if (!type || strcmp(type, "text/css")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -207,15 +199,13 @@ void XHTMLTagStyleAction::doAtEnd(XHTMLReader &reader) {
|
|||
}
|
||||
|
||||
void XHTMLTagLinkAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
|
||||
static const std::string REL = "stylesheet";
|
||||
const char *rel = reader.attributeValue(xmlattributes, "rel");
|
||||
if ((rel == 0) || (REL != rel)) {
|
||||
if (!rel || strcmp(rel, "stylesheet")) {
|
||||
return;
|
||||
}
|
||||
static const std::string TYPE = "text/css";
|
||||
|
||||
const char *type = reader.attributeValue(xmlattributes, "type");
|
||||
if ((type == 0) || (TYPE != type)) {
|
||||
if (!type || strcmp(type, "text/css")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -234,10 +224,7 @@ void XHTMLTagLinkAction::doAtStart(XHTMLReader &reader, const char **xmlattribut
|
|||
}
|
||||
|
||||
void XHTMLTagParagraphAction::doAtStart(XHTMLReader &reader, const char**) {
|
||||
if (!reader.myNewParagraphInProgress) {
|
||||
beginParagraph(reader);
|
||||
reader.myNewParagraphInProgress = true;
|
||||
}
|
||||
endParagraph(reader);
|
||||
}
|
||||
|
||||
void XHTMLTagParagraphAction::doAtEnd(XHTMLReader &reader) {
|
||||
|
@ -253,16 +240,15 @@ void XHTMLTagBodyAction::doAtEnd(XHTMLReader &reader) {
|
|||
reader.myReadState = XHTMLReader::READ_NOTHING;
|
||||
}
|
||||
|
||||
void XHTMLTagRestartParagraphAction::doAtStart(XHTMLReader &reader, const char**) {
|
||||
if (reader.myCurrentParagraphIsEmpty) {
|
||||
bookReader(reader).addData(" ");
|
||||
}
|
||||
endParagraph(reader);
|
||||
beginParagraph(reader);
|
||||
}
|
||||
|
||||
void XHTMLTagLineBreakAction::doAtEnd(XHTMLReader& reader) {
|
||||
bookReader(reader).addLineBreak();
|
||||
BookReader &br = bookReader(reader);
|
||||
if (br.paragraphIsOpen()) {
|
||||
br.addLineBreak();
|
||||
} else {
|
||||
beginParagraph(reader);
|
||||
br.addLineBreak();
|
||||
endParagraph(reader);
|
||||
}
|
||||
}
|
||||
|
||||
void XHTMLTagItemAction::doAtStart(XHTMLReader &reader, const char**) {
|
||||
|
@ -288,7 +274,7 @@ XHTMLTagImageAction::XHTMLTagImageAction(const std::string &attributeName) {
|
|||
|
||||
void XHTMLTagImageAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
|
||||
// Ignore transparent images
|
||||
if (!reader.myOpacityStack.back()) {
|
||||
if (!reader.myParseStack.back().opacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -302,19 +288,14 @@ void XHTMLTagImageAction::doAtStart(XHTMLReader &reader, const char **xmlattribu
|
|||
return;
|
||||
}
|
||||
|
||||
bool flag = bookReader(reader).paragraphIsOpen();
|
||||
if (flag) {
|
||||
endParagraph(reader);
|
||||
}
|
||||
if ((strlen(fileName) > 2) && strncmp(fileName, "./", 2) == 0) {
|
||||
fileName +=2;
|
||||
}
|
||||
bookReader(reader).addImageReference(fullfileName);
|
||||
bookReader(reader).addImage(fullfileName, new ZLFileImage(ZLFile(fullfileName), 0));
|
||||
if (flag) {
|
||||
beginParagraph(reader);
|
||||
}
|
||||
reader.myElementHasContents.back() = true;
|
||||
|
||||
reader.myParseStack.back().kind = IMAGE;
|
||||
reader.haveContent();
|
||||
reader.myModelReader.addImageReference(fullfileName);
|
||||
reader.myModelReader.addImage(fullfileName, new ZLFileImage(ZLFile(fullfileName), 0));
|
||||
}
|
||||
|
||||
XHTMLTagSvgAction::XHTMLTagSvgAction(XHTMLSvgImageAttributeNamePredicate &predicate) : myPredicate(predicate) {
|
||||
|
@ -339,13 +320,7 @@ XHTMLTagControlAction::XHTMLTagControlAction(FBTextKind control) : myControl(con
|
|||
}
|
||||
|
||||
void XHTMLTagControlAction::doAtStart(XHTMLReader &reader, const char**) {
|
||||
bookReader(reader).pushKind(myControl);
|
||||
bookReader(reader).addControl(myControl, true);
|
||||
}
|
||||
|
||||
void XHTMLTagControlAction::doAtEnd(XHTMLReader &reader) {
|
||||
bookReader(reader).addControl(myControl, false);
|
||||
bookReader(reader).popKind();
|
||||
reader.myParseStack.back().kind = myControl;
|
||||
}
|
||||
|
||||
void XHTMLTagHyperlinkAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
|
||||
|
@ -387,25 +362,25 @@ void XHTMLTagParagraphWithControlAction::doAtStart(XHTMLReader &reader, const ch
|
|||
if ((myControl == TITLE) && (bookReader(reader).model().bookTextModel()->paragraphsNumber() > 1)) {
|
||||
bookReader(reader).insertEndOfSectionParagraph();
|
||||
}
|
||||
bookReader(reader).pushKind(myControl);
|
||||
beginParagraph(reader);
|
||||
reader.myParseStack.back().kind = myControl;
|
||||
}
|
||||
|
||||
void XHTMLTagParagraphWithControlAction::doAtEnd(XHTMLReader &reader) {
|
||||
endParagraph(reader);
|
||||
bookReader(reader).popKind();
|
||||
}
|
||||
|
||||
void XHTMLTagPreAction::doAtStart(XHTMLReader &reader, const char**) {
|
||||
reader.myPreformatted++;
|
||||
beginParagraph(reader);
|
||||
bookReader(reader).addControl(CODE, true);
|
||||
endParagraph(reader);
|
||||
reader.myParseStack.back().kind = PREFORMATTED;
|
||||
if (++reader.myPreformatted == 1) {
|
||||
beginParagraph(reader);
|
||||
}
|
||||
}
|
||||
|
||||
void XHTMLTagPreAction::doAtEnd(XHTMLReader &reader) {
|
||||
bookReader(reader).addControl(CODE, false);
|
||||
endParagraph(reader);
|
||||
reader.myPreformatted--;
|
||||
if (!--reader.myPreformatted) {
|
||||
endParagraph(reader);
|
||||
}
|
||||
}
|
||||
|
||||
XHTMLTagAction *XHTMLReader::addAction(const std::string &tag, XHTMLTagAction *action) {
|
||||
|
@ -454,6 +429,7 @@ void XHTMLReader::fillTagTable() {
|
|||
addAction("dt", new XHTMLTagParagraphWithControlAction(DEFINITION));
|
||||
addAction("dfn", new XHTMLTagParagraphWithControlAction(DEFINITION));
|
||||
addAction("strike", new XHTMLTagControlAction(STRIKETHROUGH));
|
||||
addAction("blockquote", new XHTMLTagParagraphWithControlAction(BLOCKQUOTE));
|
||||
|
||||
addAction("a", new XHTMLTagHyperlinkAction());
|
||||
|
||||
|
@ -467,7 +443,6 @@ void XHTMLReader::fillTagTable() {
|
|||
//addAction("map", new XHTMLTagAction());
|
||||
|
||||
//addAction("base", new XHTMLTagAction());
|
||||
//addAction("blockquote", new XHTMLTagAction());
|
||||
addAction("br", new XHTMLTagLineBreakAction());
|
||||
//addAction("center", new XHTMLTagAction());
|
||||
addAction("div", new XHTMLTagParagraphAction());
|
||||
|
@ -507,29 +482,19 @@ bool XHTMLReader::readFile(const ZLFile &file, const std::string &referenceName)
|
|||
myReferenceDirName = referenceName.substr(0, index + 1);
|
||||
|
||||
myPreformatted = 0;
|
||||
myNewParagraphInProgress = false;
|
||||
myReadState = READ_NOTHING;
|
||||
|
||||
myElementStack.clear();
|
||||
myStyleStack.clear();
|
||||
myElementHasContents.clear();
|
||||
myOpacityStack.clear();
|
||||
myParseStack.resize(1);
|
||||
|
||||
return readDocument(file);
|
||||
}
|
||||
|
||||
shared_ptr<ZLTextStyleEntry> XHTMLReader::addStyleEntry(shared_ptr<ZLTextStyleEntry> entry, shared_ptr<ZLTextStyleEntry> styleEntry) {
|
||||
if (!styleEntry.isNull() && !styleEntry->isEmpty()) {
|
||||
if (entry.isNull()) entry = new ZLTextStyleEntry;
|
||||
entry->apply(*styleEntry);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
void XHTMLReader::startElementHandler(const char *tag, const char **attributes) {
|
||||
static const std::string HASH = "#";
|
||||
const char *id = attributeValue(attributes, "id");
|
||||
const char *style = attributeValue(attributes, "style");
|
||||
const char *inlineStyle = attributeValue(attributes, "style");
|
||||
const char *klass = attributeValue(attributes, "class");
|
||||
if (id != 0) {
|
||||
myModelReader.addHyperlinkLabel(myReferenceName + HASH + id);
|
||||
|
@ -538,69 +503,242 @@ void XHTMLReader::startElementHandler(const char *tag, const char **attributes)
|
|||
const std::string sTag = ZLUnicodeUtil::toLower(tag);
|
||||
myElementStack.push_back(StyleSheetTable::Element(sTag, klass, id));
|
||||
|
||||
StyleSheetTable::Style cssStyle;
|
||||
myStyleSheetTable.applyStyles(myElementStack, cssStyle);
|
||||
if (style != 0) {
|
||||
cssStyle.apply(myStyleParser.parseString(style));
|
||||
}
|
||||
if (cssStyle.PageBreakBefore == B3_TRUE) {
|
||||
myModelReader.insertEndOfSectionParagraph();
|
||||
myStyleStack.resize(myStyleStack.size() + 1);
|
||||
StyleSheetTable::Style *style = &myStyleStack.back();
|
||||
if (myStyleStack.size() > 1) {
|
||||
style->inherit(myStyleStack.at(myStyleStack.size()-2));
|
||||
}
|
||||
|
||||
int opacity = myOpacityStack.empty() ? 255 : myOpacityStack.back();
|
||||
if (cssStyle.TextStyle.opacitySupported()) {
|
||||
opacity *= cssStyle.TextStyle.opacity();
|
||||
myStyleSheetTable.applyStyles(myElementStack, *style);
|
||||
if (inlineStyle) {
|
||||
style->apply(myStyleParser.parseString(inlineStyle));
|
||||
}
|
||||
|
||||
myParseStack.resize(myParseStack.size() + 1);
|
||||
ParseContext &prev(myParseStack.at(myParseStack.size()-2));
|
||||
ParseContext &context(myParseStack.back());
|
||||
if (style->TextStyle.opacitySupported()) {
|
||||
int opacity = prev.opacity;
|
||||
opacity *= style->TextStyle.opacity();
|
||||
opacity /= 255;
|
||||
context.opacity = opacity;
|
||||
} else {
|
||||
context.opacity = prev.opacity;
|
||||
}
|
||||
|
||||
myStyleStack.push_back(cssStyle);
|
||||
myElementHasContents.push_back(false);
|
||||
myOpacityStack.push_back((unsigned char)opacity);
|
||||
// Don't collect empty styles
|
||||
if (style->empty()) {
|
||||
myStyleStack.resize(myStyleStack.size()-1);
|
||||
style = NULL;
|
||||
} else {
|
||||
context.styleIndex = myStyleStack.size() - 1;
|
||||
if (style->PageBreakBefore == B3_TRUE) {
|
||||
addPageBreak();
|
||||
}
|
||||
}
|
||||
|
||||
XHTMLTagAction *action = ourTagActions[sTag];
|
||||
if (action != 0) {
|
||||
action->doAtStart(*this, attributes);
|
||||
}
|
||||
|
||||
myModelReader.addControl(cssStyle.TextStyle);
|
||||
if (context.kind >= 0) {
|
||||
context.decoration = ZLTextStyleCollection::Instance().decoration(context.kind);
|
||||
}
|
||||
|
||||
if (myModelReader.paragraphIsOpen()) {
|
||||
applyStyles(myParseStack.back());
|
||||
}
|
||||
}
|
||||
|
||||
void XHTMLReader::endElementHandler(const char *tag) {
|
||||
myModelReader.addControl(REGULAR, false);
|
||||
bool pageBreak = false;
|
||||
ParseContext &context(myParseStack.back());
|
||||
if (context.styleIndex >= 0) {
|
||||
if (myStyleStack[context.styleIndex].PageBreakAfter == B3_TRUE) {
|
||||
// If we are about to have a page break, we don't want
|
||||
// endParagraph() to apply pending bottom margins.
|
||||
myBottomMargins.resize(0);
|
||||
pageBreak = true;
|
||||
}
|
||||
}
|
||||
|
||||
XHTMLTagAction *action = ourTagActions[ZLUnicodeUtil::toLower(tag)];
|
||||
if (action != 0) {
|
||||
action->doAtEnd(*this);
|
||||
myNewParagraphInProgress = false;
|
||||
}
|
||||
|
||||
const bool haveContents = myElementHasContents.back();
|
||||
if (myStyleStack.back().PageBreakAfter == B3_TRUE && haveContents) {
|
||||
myModelReader.insertEndOfSectionParagraph();
|
||||
if (pageBreak) {
|
||||
addPageBreak();
|
||||
}
|
||||
|
||||
if (myModelReader.paragraphIsOpen()) {
|
||||
if (context.styleIndex >= 0) {
|
||||
myModelReader.addControl(REGULAR, false);
|
||||
}
|
||||
if (context.kind >= 0) {
|
||||
myModelReader.addControl((FBTextKind)context.kind, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!context.bottomMarginApplied && elementHasBottomMargin(context)) {
|
||||
ZLTextStyleEntry::SizeUnit unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
|
||||
short size = 0;
|
||||
if (context.styleIndex >= 0 && myStyleStack[context.styleIndex].TextStyle.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_AFTER)) {
|
||||
size = myStyleStack[context.styleIndex].TextStyle.length(ZLTextStyleEntry::LENGTH_SPACE_AFTER, unit);
|
||||
} else if (context.decoration) {
|
||||
const ZLTextFullStyleDecoration *decoration = context.decoration->fullDecoration();
|
||||
if (decoration) {
|
||||
size = decoration->SpaceAfterOption.value();
|
||||
unit = decoration->SpaceAfterOptionUnit;
|
||||
}
|
||||
}
|
||||
if (size > 0) {
|
||||
addBottomMargin(size, unit);
|
||||
}
|
||||
}
|
||||
|
||||
if (!myModelReader.paragraphIsOpen()) {
|
||||
applyBottomMargins();
|
||||
}
|
||||
|
||||
if (context.styleIndex >= 0) {
|
||||
myStyleStack.pop_back();
|
||||
}
|
||||
myElementStack.pop_back();
|
||||
myStyleStack.pop_back();
|
||||
myElementHasContents.pop_back();
|
||||
myOpacityStack.pop_back();
|
||||
myParseStack.pop_back();
|
||||
}
|
||||
|
||||
if (!myElementHasContents.empty() && haveContents) {
|
||||
myElementHasContents.back() = true;
|
||||
void XHTMLReader::addPageBreak() {
|
||||
myBottomMargins.resize(0);
|
||||
endParagraph();
|
||||
myModelReader.insertEndOfSectionParagraph();
|
||||
}
|
||||
|
||||
void XHTMLReader::addBottomMargin(short size, ZLTextStyleEntry::SizeUnit unit) {
|
||||
for (std::vector<ZLTextStyleEntry>::iterator it = myBottomMargins.begin(); it != myBottomMargins.end(); ++it) {
|
||||
ZLTextStyleEntry::SizeUnit entryUnit;
|
||||
short entrySize = it->length(ZLTextStyleEntry::LENGTH_SPACE_AFTER, entryUnit);
|
||||
if (entryUnit == unit) {
|
||||
it->setLength(ZLTextStyleEntry::LENGTH_SPACE_AFTER, entrySize + size, unit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// No such unit yet
|
||||
myBottomMargins.resize(myBottomMargins.size()+1);
|
||||
myBottomMargins.back().setLength(ZLTextStyleEntry::LENGTH_SPACE_AFTER, size, unit);
|
||||
}
|
||||
|
||||
void XHTMLReader::applyBottomMargins() {
|
||||
if (!myBottomMargins.empty()) {
|
||||
myModelReader.endParagraph();
|
||||
for (std::vector<ZLTextStyleEntry>::const_iterator it = myBottomMargins.begin(); it != myBottomMargins.end(); ++it) {
|
||||
addStyleParagraph(*it);
|
||||
}
|
||||
myBottomMargins.resize(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool XHTMLReader::elementHasTopMargin(const ParseContext &context) const {
|
||||
return
|
||||
(context.styleIndex >= 0 && myStyleStack[context.styleIndex].TextStyle.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE)) ||
|
||||
(context.decoration && context.decoration->fullDecoration());
|
||||
}
|
||||
|
||||
bool XHTMLReader::elementHasBottomMargin(const ParseContext &context) const {
|
||||
return
|
||||
(context.styleIndex >= 0 && myStyleStack[context.styleIndex].TextStyle.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_AFTER)) ||
|
||||
(context.decoration && context.decoration->fullDecoration());
|
||||
}
|
||||
|
||||
void XHTMLReader::addStyleParagraph(const ZLTextStyleEntry &style) {
|
||||
myModelReader.beginParagraph();
|
||||
myModelReader.addControl(style);
|
||||
myModelReader.addEmpty();
|
||||
myModelReader.endParagraph();
|
||||
}
|
||||
|
||||
void XHTMLReader::applyStyles(ParseContext &context) {
|
||||
if (!context.stylesApplied) {
|
||||
context.stylesApplied = true;
|
||||
if (context.kind >= 0) {
|
||||
myModelReader.addControl((FBTextKind)context.kind, true);
|
||||
}
|
||||
if (context.styleIndex >= 0) {
|
||||
myModelReader.addControl(myStyleStack[context.styleIndex].TextStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XHTMLReader::beginParagraph() {
|
||||
myCurrentParagraphIsEmpty = true;
|
||||
myModelReader.beginParagraph();
|
||||
if (!myStyleStack.empty()) {
|
||||
for (StyleSheetTable::StyleList::const_iterator it = myStyleStack.begin(); it != myStyleStack.end(); ++it) {
|
||||
myModelReader.addControl(((*it).TextStyle));
|
||||
if (!myModelReader.paragraphIsOpen()) {
|
||||
myModelReader.beginParagraph();
|
||||
for (std::vector<ParseContext>::iterator it = myParseStack.begin(); it != myParseStack.end(); ++it) {
|
||||
applyStyles(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XHTMLReader::endParagraph() {
|
||||
myModelReader.endParagraph();
|
||||
if (myModelReader.paragraphIsOpen()) {
|
||||
myModelReader.endParagraph();
|
||||
|
||||
// Find which bottom margins have been applied
|
||||
// and at the same time reset stylesApplied flag.
|
||||
std::vector<ParseContext>::reverse_iterator it = myParseStack.rbegin();
|
||||
for (; it != myParseStack.rend(); ++it) {
|
||||
it->stylesApplied = false;
|
||||
if (elementHasBottomMargin(*it)) {
|
||||
it->bottomMarginApplied = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset stylesApplied for the remaining entries
|
||||
for (; it != myParseStack.rend(); ++it) {
|
||||
it->stylesApplied = false;
|
||||
}
|
||||
|
||||
// Apply pending bottom margins
|
||||
applyBottomMargins();
|
||||
}
|
||||
}
|
||||
|
||||
void XHTMLReader::haveContent() {
|
||||
if (!myParseStack.back().haveContent) {
|
||||
// Create empty paragraphs for other parent entries that haven't
|
||||
// had any content yet, in order to apply their top margins. Skip
|
||||
// the last entry as it will be applied for the paragraph we are
|
||||
// about to start.
|
||||
bool skippedLastEntry = false;
|
||||
for (std::vector<ParseContext>::reverse_iterator it = myParseStack.rbegin(); it != myParseStack.rend() && !it->haveContent; ++it) {
|
||||
it->haveContent = true;
|
||||
if (elementHasTopMargin(*it)) {
|
||||
if (!skippedLastEntry) {
|
||||
skippedLastEntry = true;
|
||||
} else {
|
||||
ZLTextStyleEntry::SizeUnit unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
|
||||
short size = 0;
|
||||
if (it->styleIndex >= 0 && myStyleStack[it->styleIndex].TextStyle.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE)) {
|
||||
size = myStyleStack[it->styleIndex].TextStyle.length(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, unit);
|
||||
} else if (it->decoration) {
|
||||
const ZLTextFullStyleDecoration *decoration = it->decoration->fullDecoration();
|
||||
if (decoration) {
|
||||
size = decoration->SpaceBeforeOption.value();
|
||||
unit = decoration->SpaceBeforeOptionUnit;
|
||||
}
|
||||
}
|
||||
if (size > 0) {
|
||||
ZLTextStyleEntry style;
|
||||
style.setLength(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, size, unit);
|
||||
addStyleParagraph(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
myBottomMargins.resize(0);
|
||||
beginParagraph();
|
||||
}
|
||||
|
||||
void XHTMLReader::characterDataHandler(const char *text, size_t len) {
|
||||
|
@ -613,34 +751,49 @@ void XHTMLReader::characterDataHandler(const char *text, size_t len) {
|
|||
}
|
||||
break;
|
||||
case READ_BODY:
|
||||
if (myOpacityStack.back()) {
|
||||
if (myPreformatted) {
|
||||
if ((*text == '\r') || (*text == '\n')) {
|
||||
myModelReader.addLineBreak();
|
||||
}
|
||||
if (myParseStack.back().opacity && !myStyleStack.empty() && !myStyleStack.back().DisplayNone) {
|
||||
const StyleSheetTable::WhiteSpaceValue whiteSpace = myStyleStack.back().WhiteSpace;
|
||||
if (myPreformatted || whiteSpace == StyleSheetTable::WS_PRE || whiteSpace == StyleSheetTable::WS_PRE_WRAP) {
|
||||
size_t spaceCounter = 0;
|
||||
while ((spaceCounter < len) && isspace((unsigned char)*(text + spaceCounter))) {
|
||||
++spaceCounter;
|
||||
}
|
||||
myModelReader.addFixedHSpace(spaceCounter);
|
||||
text += spaceCounter;
|
||||
len -= spaceCounter;
|
||||
} else if ((myNewParagraphInProgress) || !myModelReader.paragraphIsOpen()) {
|
||||
while (isspace((unsigned char)*text)) {
|
||||
while (len > 0 && isspace(*text)) {
|
||||
if (*text == '\n') {
|
||||
haveContent();
|
||||
if (spaceCounter) {
|
||||
myModelReader.addFixedHSpace(spaceCounter);
|
||||
spaceCounter = 0;
|
||||
}
|
||||
myModelReader.addLineBreak();
|
||||
} else if (*text != '\r') {
|
||||
spaceCounter++;
|
||||
}
|
||||
++text;
|
||||
if (--len == 0) {
|
||||
--len;
|
||||
}
|
||||
if (spaceCounter) {
|
||||
haveContent();
|
||||
myModelReader.addFixedHSpace(spaceCounter);
|
||||
}
|
||||
} else if (!myModelReader.paragraphIsOpen()) {
|
||||
while (len > 0 && isspace(*text)) {
|
||||
switch (whiteSpace) {
|
||||
case StyleSheetTable::WS_PRE:
|
||||
case StyleSheetTable::WS_PRE_WRAP:
|
||||
case StyleSheetTable::WS_PRE_LINE:
|
||||
if (*text == '\n') {
|
||||
haveContent();
|
||||
myModelReader.addLineBreak();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
++text;
|
||||
--len;
|
||||
}
|
||||
}
|
||||
if (len > 0) {
|
||||
myCurrentParagraphIsEmpty = false;
|
||||
myElementHasContents.back() = true;
|
||||
if (!myModelReader.paragraphIsOpen()) {
|
||||
myModelReader.beginParagraph();
|
||||
}
|
||||
haveContent();
|
||||
myModelReader.addData(std::string(text, len));
|
||||
myNewParagraphInProgress = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -25,11 +25,13 @@
|
|||
#include <vector>
|
||||
|
||||
#include <ZLXMLReader.h>
|
||||
#include <ZLTextParagraph.h>
|
||||
|
||||
#include "../css/StyleSheetTable.h"
|
||||
#include "../css/StyleSheetParser.h"
|
||||
|
||||
class ZLFile;
|
||||
class ZLTextStyleDecoration;
|
||||
|
||||
class BookReader;
|
||||
class XHTMLReader;
|
||||
|
@ -63,7 +65,18 @@ public:
|
|||
bool readFile(const ZLFile &file, const std::string &referenceName);
|
||||
|
||||
private:
|
||||
void startElementHandler(const char *tag, const char **attributes);
|
||||
struct ParseContext {
|
||||
int kind;
|
||||
int styleIndex;
|
||||
unsigned char opacity;
|
||||
bool haveContent;
|
||||
bool stylesApplied;
|
||||
bool bottomMarginApplied;
|
||||
const ZLTextStyleDecoration *decoration;
|
||||
ParseContext() : kind(-1), styleIndex(-1), opacity(255), haveContent(false), stylesApplied(false), bottomMarginApplied(false), decoration(NULL) {}
|
||||
};
|
||||
|
||||
void startElementHandler(const char *tag, const char **attributes);
|
||||
void endElementHandler(const char *tag);
|
||||
void characterDataHandler(const char *text, size_t len);
|
||||
|
||||
|
@ -71,9 +84,16 @@ private:
|
|||
|
||||
bool processNamespaces() const;
|
||||
|
||||
void beginParagraph();
|
||||
void haveContent();
|
||||
void beginParagraph();
|
||||
void endParagraph();
|
||||
static shared_ptr<ZLTextStyleEntry> addStyleEntry(shared_ptr<ZLTextStyleEntry> entry, shared_ptr<ZLTextStyleEntry> styleEntry);
|
||||
void applyStyles(ParseContext &context);
|
||||
void addStyleParagraph(const ZLTextStyleEntry &style);
|
||||
void addBottomMargin(short size, ZLTextStyleEntry::SizeUnit unit);
|
||||
bool elementHasTopMargin(const ParseContext &context) const;
|
||||
bool elementHasBottomMargin(const ParseContext &context) const;
|
||||
void applyBottomMargins();
|
||||
void addPageBreak();
|
||||
|
||||
private:
|
||||
BookReader &myModelReader;
|
||||
|
@ -81,13 +101,11 @@ private:
|
|||
std::string myReferenceName;
|
||||
std::string myReferenceDirName;
|
||||
int myPreformatted;
|
||||
bool myNewParagraphInProgress;
|
||||
StyleSheetTable myStyleSheetTable;
|
||||
StyleSheetTable::ElementList myElementStack;
|
||||
StyleSheetTable::StyleList myStyleStack;
|
||||
std::vector<unsigned char> myOpacityStack;
|
||||
std::vector<bool> myElementHasContents;
|
||||
bool myCurrentParagraphIsEmpty;
|
||||
std::vector<ParseContext> myParseStack;
|
||||
std::vector<ZLTextStyleEntry> myBottomMargins;
|
||||
StyleSheetSingleStyleParser myStyleParser;
|
||||
shared_ptr<StyleSheetTableParser> myTableParser;
|
||||
enum {
|
||||
|
@ -97,13 +115,13 @@ private:
|
|||
} myReadState;
|
||||
|
||||
friend class XHTMLTagAction;
|
||||
friend class XHTMLTagStyleAction;
|
||||
friend class XHTMLTagLinkAction;
|
||||
friend class XHTMLTagHyperlinkAction;
|
||||
friend class XHTMLTagPreAction;
|
||||
friend class XHTMLTagParagraphAction;
|
||||
friend class XHTMLTagBodyAction;
|
||||
friend class XHTMLTagRestartParagraphAction;
|
||||
friend class XHTMLTagStyleAction;
|
||||
friend class XHTMLTagLinkAction;
|
||||
friend class XHTMLTagHyperlinkAction;
|
||||
friend class XHTMLTagPreAction;
|
||||
friend class XHTMLTagControlAction;
|
||||
friend class XHTMLTagParagraphWithControlAction;
|
||||
friend class XHTMLTagBodyAction;
|
||||
friend class XHTMLTagImageAction;
|
||||
};
|
||||
|
||||
|
|
|
@ -45,6 +45,14 @@ bool ZLStringUtil::stringStartsWith(const std::string &str, const std::string &s
|
|||
#endif
|
||||
}
|
||||
|
||||
bool ZLStringUtil::caseInsensitiveEqual(const std::string &str1, const std::string &str2) {
|
||||
return !strcasecmp(str1.c_str(), str2.c_str());
|
||||
}
|
||||
|
||||
bool ZLStringUtil::caseInsensitiveSort(const std::string &str1, const std::string &str2) {
|
||||
return strcasecmp(str1.c_str(), str2.c_str()) < 0;
|
||||
}
|
||||
|
||||
void ZLStringUtil::appendNumber(std::string &str, unsigned int n) {
|
||||
int len;
|
||||
if (n > 0) {
|
||||
|
@ -75,20 +83,29 @@ void ZLStringUtil::append(std::string &str, const std::vector<std::string> &text
|
|||
}
|
||||
}
|
||||
|
||||
void ZLStringUtil::stripWhiteSpaces(std::string &str) {
|
||||
size_t counter = 0;
|
||||
size_t length = str.length();
|
||||
while ((counter < length) && isspace((unsigned char)str[counter])) {
|
||||
counter++;
|
||||
}
|
||||
str.erase(0, counter);
|
||||
length -= counter;
|
||||
// Returns true if there's anything left
|
||||
bool ZLStringUtil::stripWhiteSpaces(std::string &str) {
|
||||
const size_t old_length = str.length();
|
||||
if (old_length > 0) {
|
||||
size_t end = old_length;
|
||||
while ((end > 0) && isspace((unsigned char)str[end - 1])) {
|
||||
end--;
|
||||
}
|
||||
if (end < old_length) {
|
||||
str.erase(end, old_length - end);
|
||||
}
|
||||
|
||||
size_t r_counter = length;
|
||||
while ((r_counter > 0) && isspace((unsigned char)str[r_counter - 1])) {
|
||||
r_counter--;
|
||||
size_t start = 0;
|
||||
while ((start < end) && isspace((unsigned char)str[start])) {
|
||||
start++;
|
||||
}
|
||||
if (start > 0) {
|
||||
str.erase(0, start);
|
||||
}
|
||||
return !str.empty();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
str.erase(r_counter, length - r_counter);
|
||||
}
|
||||
|
||||
std::vector<std::string> ZLStringUtil::splitString(const char *str, const char* delim) {
|
||||
|
@ -106,6 +123,14 @@ std::vector<std::string> ZLStringUtil::splitString(const char *str, const char*
|
|||
return tokens;
|
||||
}
|
||||
|
||||
void ZLStringUtil::replaceAll(std::string &str, const std::string &find, const std::string &replaceWith) {
|
||||
size_t pos = 0;
|
||||
while ((pos == str.find(find, pos)) != std::string::npos) {
|
||||
str.replace(pos, find.length(), replaceWith);
|
||||
pos += replaceWith.length();
|
||||
}
|
||||
}
|
||||
|
||||
std::string ZLStringUtil::printf(const std::string &format, const std::string &arg0) {
|
||||
int index = format.find("%s");
|
||||
if (index == -1) {
|
||||
|
|
|
@ -31,11 +31,15 @@ private:
|
|||
public:
|
||||
static bool stringStartsWith(const std::string &str, const std::string &start);
|
||||
static bool stringEndsWith(const std::string &str, const std::string &end);
|
||||
static bool endsWith(const std::string &str, char c);
|
||||
static bool caseInsensitiveEqual(const std::string &str1, const std::string &str2);
|
||||
static bool caseInsensitiveSort(const std::string &str1, const std::string &str2);
|
||||
static void appendNumber(std::string &str, unsigned int n);
|
||||
static void append(std::string &str, const std::vector<std::string> &buffer);
|
||||
static void stripWhiteSpaces(std::string &str);
|
||||
static bool stripWhiteSpaces(std::string &str);
|
||||
static std::vector<std::string> splitString(const char *str, const char* delim);
|
||||
static std::vector<std::string> splitString(const std::string &str, const char* delim);
|
||||
static void replaceAll(std::string &str, const std::string &find, const std::string &replaceWith);
|
||||
|
||||
static std::string printf(const std::string &format, const std::string &arg0);
|
||||
|
||||
|
@ -46,5 +50,7 @@ public:
|
|||
inline std::vector<std::string> ZLStringUtil::splitString(const std::string &str, const char* delim) {
|
||||
return ZLStringUtil::splitString(str.c_str(), delim);
|
||||
}
|
||||
|
||||
inline bool ZLStringUtil::endsWith(const std::string &str, char c) {
|
||||
return !str.empty() && str[str.length()-1] == c;
|
||||
}
|
||||
#endif /* __ZLSTRINGUTIL_H__ */
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
*/
|
||||
|
||||
#include "ZLPaintContext.h"
|
||||
#include "ZLStringUtil.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <strings.h>
|
||||
|
||||
ZLPaintContext::ZLPaintContext() {
|
||||
}
|
||||
|
@ -25,9 +29,29 @@ ZLPaintContext::ZLPaintContext() {
|
|||
ZLPaintContext::~ZLPaintContext() {
|
||||
}
|
||||
|
||||
std::string ZLPaintContext::pickFontFamily(const std::vector<std::string> &fonts) const
|
||||
{
|
||||
if (!fonts.empty()) {
|
||||
if (fonts.size() > 1) {
|
||||
const std::vector<std::string> &available = fontFamilies();
|
||||
for (std::vector<std::string>::const_iterator it = fonts.begin(); it != fonts.end(); ++it) {
|
||||
const std::vector<std::string>::const_iterator found =
|
||||
std::lower_bound(available.begin(), available.end(), *it,
|
||||
ZLStringUtil::caseInsensitiveSort);
|
||||
if (found != available.end() && ZLStringUtil::caseInsensitiveEqual(*found, *it)) {
|
||||
return *found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fonts.front();
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
const std::vector<std::string> &ZLPaintContext::fontFamilies() const {
|
||||
if (myFamilies.empty()) {
|
||||
fillFamiliesList(myFamilies);
|
||||
std::sort(myFamilies.begin(), myFamilies.end(), ZLStringUtil::caseInsensitiveSort);
|
||||
}
|
||||
return myFamilies;
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ public:
|
|||
virtual void fillRectangle(int x0, int y0, int x1, int y1) = 0;
|
||||
virtual void drawFilledCircle(int x, int y, int r) = 0;
|
||||
|
||||
std::string pickFontFamily(const std::vector<std::string> & fonts) const;
|
||||
const std::vector<std::string> &fontFamilies() const;
|
||||
virtual const std::string realFontFamilyName(std::string &fontFamily) const = 0;
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
ZLTextArea::Style::Style(const ZLTextArea &area, shared_ptr<ZLTextStyle> style) : myArea(area) {
|
||||
myTextStyle = style;
|
||||
myWordHeight = -1;
|
||||
myArea.context().setFont(myTextStyle->fontFamily(), myTextStyle->fontSize(), myTextStyle->bold(), myTextStyle->italic());
|
||||
myArea.context().setFont(myArea.context().pickFontFamily(myTextStyle->fontFamilies()), myTextStyle->fontSize(), myTextStyle->bold(), myTextStyle->italic());
|
||||
myBidiLevel = myArea.isRtl() ? 1 : 0;
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ void ZLTextArea::Style::setTextStyle(shared_ptr<ZLTextStyle> style, unsigned cha
|
|||
myTextStyle = style;
|
||||
myWordHeight = -1;
|
||||
}
|
||||
myArea.context().setFont(myTextStyle->fontFamily(), myTextStyle->fontSize(), myTextStyle->bold(), myTextStyle->italic());
|
||||
myArea.context().setFont(myArea.context().pickFontFamily(myTextStyle->fontFamilies()), myTextStyle->fontSize(), myTextStyle->bold(), myTextStyle->italic());
|
||||
myBidiLevel = bidiLevel;
|
||||
}
|
||||
|
||||
|
@ -108,6 +108,7 @@ int ZLTextArea::Style::elementWidth(const ZLTextElement &element, unsigned int c
|
|||
case ZLTextElement::CONTROL_ELEMENT:
|
||||
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:
|
||||
case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT:
|
||||
case ZLTextElement::EMPTY_ELEMENT:
|
||||
return 0;
|
||||
case ZLTextElement::FIXED_HSPACE_ELEMENT:
|
||||
return myArea.context().spaceWidth() * ((const ZLTextFixedHSpaceElement&)element).length();
|
||||
|
@ -119,6 +120,8 @@ int ZLTextArea::Style::elementHeight(const ZLTextElement &element, const ZLTextS
|
|||
switch (element.kind()) {
|
||||
case ZLTextElement::NB_HSPACE_ELEMENT:
|
||||
case ZLTextElement::WORD_ELEMENT:
|
||||
case ZLTextElement::EMPTY_LINE_ELEMENT:
|
||||
case ZLTextElement::LINE_BREAK_ELEMENT:
|
||||
if (myWordHeight == -1) {
|
||||
myWordHeight = myArea.context().stringHeight() * textStyle()->lineSpacePercent() / 100 + textStyle()->verticalShift();
|
||||
}
|
||||
|
@ -131,9 +134,6 @@ int ZLTextArea::Style::elementHeight(const ZLTextElement &element, const ZLTextS
|
|||
return - textStyle()->spaceAfter(metrics);
|
||||
case ZLTextElement::AFTER_PARAGRAPH_ELEMENT:
|
||||
return - textStyle()->spaceBefore(metrics);
|
||||
case ZLTextElement::EMPTY_LINE_ELEMENT:
|
||||
case ZLTextElement::LINE_BREAK_ELEMENT:
|
||||
return myArea.context().stringHeight();
|
||||
case ZLTextElement::INDENT_ELEMENT:
|
||||
case ZLTextElement::HSPACE_ELEMENT:
|
||||
case ZLTextElement::FORCED_CONTROL_ELEMENT:
|
||||
|
@ -141,6 +141,7 @@ int ZLTextArea::Style::elementHeight(const ZLTextElement &element, const ZLTextS
|
|||
case ZLTextElement::FIXED_HSPACE_ELEMENT:
|
||||
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:
|
||||
case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT:
|
||||
case ZLTextElement::EMPTY_ELEMENT:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -167,6 +167,7 @@ void ZLTextArea::prepareTextLine(Style &style, const ZLTextLineInfo &info, int y
|
|||
case ZLTextElement::EMPTY_LINE_ELEMENT:
|
||||
case ZLTextElement::LINE_BREAK_ELEMENT:
|
||||
case ZLTextElement::FIXED_HSPACE_ELEMENT:
|
||||
case ZLTextElement::EMPTY_ELEMENT:
|
||||
break;
|
||||
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:
|
||||
//context().setColor(ZLColor(0, 255, 0));
|
||||
|
|
|
@ -170,6 +170,7 @@ ZLTextLineInfoPtr ZLTextArea::processTextLine(Style &style, const ZLTextWordCurs
|
|||
newInfo.Width += lastSpaceWidth;
|
||||
}
|
||||
break;
|
||||
case ZLTextElement::EMPTY_ELEMENT:
|
||||
case ZLTextElement::EMPTY_LINE_ELEMENT:
|
||||
newInfo.IsVisible = true;
|
||||
break;
|
||||
|
@ -178,6 +179,7 @@ ZLTextLineInfoPtr ZLTextArea::processTextLine(Style &style, const ZLTextWordCurs
|
|||
}
|
||||
|
||||
if (elementKind == ZLTextElement::LINE_BREAK_ELEMENT) {
|
||||
newInfo.IsVisible = true;
|
||||
newInfo.End.nextWord();
|
||||
newInfo.setTo(info);
|
||||
break;
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
LINE_BREAK_ELEMENT,
|
||||
START_REVERSED_SEQUENCE_ELEMENT,
|
||||
END_REVERSED_SEQUENCE_ELEMENT,
|
||||
EMPTY_ELEMENT
|
||||
};
|
||||
|
||||
virtual Kind kind() const = 0;
|
||||
|
|
|
@ -111,6 +111,9 @@ void ZLTextParagraphCursor::Builder::fill() {
|
|||
case ZLTextParagraphEntry::LINE_BREAK_ENTRY:
|
||||
myElements.push_back(myTextElementPool.LineBreakElement);
|
||||
break;
|
||||
case ZLTextParagraphEntry::EMPTY_ENTRY:
|
||||
myElements.push_back(myTextElementPool.EmptyElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ ZLTextElementVector::~ZLTextElementVector() {
|
|||
case ZLTextElement::LINE_BREAK_ELEMENT:
|
||||
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:
|
||||
case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT:
|
||||
case ZLTextElement::EMPTY_ELEMENT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +64,7 @@ ZLTextElementPool::ZLTextElementPool() {
|
|||
LineBreakElement = new ZLTextSpecialElement(ZLTextElement::LINE_BREAK_ELEMENT);
|
||||
StartReversedSequenceElement = new ZLTextSpecialElement(ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT);
|
||||
EndReversedSequenceElement = new ZLTextSpecialElement(ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT);
|
||||
EmptyElement = new ZLTextSpecialElement(ZLTextElement::EMPTY_ELEMENT);
|
||||
}
|
||||
|
||||
ZLTextElementPool::~ZLTextElementPool() {
|
||||
|
@ -74,6 +76,7 @@ ZLTextElementPool::~ZLTextElementPool() {
|
|||
delete LineBreakElement;
|
||||
delete StartReversedSequenceElement;
|
||||
delete EndReversedSequenceElement;
|
||||
delete EmptyElement;
|
||||
}
|
||||
|
||||
ZLTextParagraphCursorPtr ZLTextParagraphCursorCache::cursor(const ZLTextModel &model, size_t index) {
|
||||
|
|
|
@ -65,6 +65,7 @@ public:
|
|||
ZLTextElement *LineBreakElement;
|
||||
ZLTextElement *StartReversedSequenceElement;
|
||||
ZLTextElement *EndReversedSequenceElement;
|
||||
ZLTextElement *EmptyElement;
|
||||
|
||||
ZLTextWord *getWord(const char *data, unsigned short length, size_t paragraphOffset, unsigned char bidiLevel);
|
||||
void storeWord(ZLTextWord *word);
|
||||
|
|
|
@ -215,43 +215,53 @@ void ZLTextModel::addControl(ZLTextKind textKind, bool isStart) {
|
|||
|
||||
void ZLTextModel::addControl(const ZLTextStyleEntry &entry) {
|
||||
int len = sizeof(int) + 2;
|
||||
if (entry.myMask & ((1 << ZLTextStyleEntry::NUMBER_OF_LENGTHS) - 1)) {
|
||||
const int mask = entry.myMask;
|
||||
if (mask & ((1 << ZLTextStyleEntry::NUMBER_OF_LENGTHS) - 1)) {
|
||||
for (int i = 0; i < ZLTextStyleEntry::NUMBER_OF_LENGTHS; ++i) {
|
||||
if (entry.myMask & (1 << i)) {
|
||||
if (mask & (1 << i)) {
|
||||
len += (sizeof(short) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entry.opacitySupported()) ++len;
|
||||
if (entry.alignmentTypeSupported()) ++len;
|
||||
if (mask & ZLTextStyleEntry::SUPPORT_OPACITY) ++len;
|
||||
if (mask & ZLTextStyleEntry::SUPPORT_ALIGNMENT_TYPE) ++len;
|
||||
if (entry.supportedFontModifier()) ++len;
|
||||
if (entry.fontSizeSupported()) ++len;
|
||||
if (entry.fontFamilySupported()) {
|
||||
len += entry.fontFamily().length() + 1;
|
||||
if (mask & ZLTextStyleEntry::SUPPORT_FONT_SIZE) ++len;
|
||||
if (mask & ZLTextStyleEntry::SUPPORT_FONT_FAMILIES) {
|
||||
const unsigned char n = entry.myFontFamilies.size();
|
||||
len += 1; // Number of entries
|
||||
for (unsigned int i = 0; i < n; ++i) {
|
||||
len += entry.myFontFamilies.at(i).length() + 1;
|
||||
}
|
||||
}
|
||||
myLastEntryStart = myAllocator.allocate(len);
|
||||
char *address = myLastEntryStart;
|
||||
*address++ = ZLTextParagraphEntry::STYLE_ENTRY;
|
||||
*address++ = entry.mySupportedFontModifier;
|
||||
memcpy(address, &entry.myMask, sizeof(int));
|
||||
memcpy(address, &mask, sizeof(int));
|
||||
address += sizeof(int);
|
||||
if (entry.myMask & ((1 << ZLTextStyleEntry::NUMBER_OF_LENGTHS) - 1)) {
|
||||
if (mask & ((1 << ZLTextStyleEntry::NUMBER_OF_LENGTHS) - 1)) {
|
||||
for (int i = 0; i < ZLTextStyleEntry::NUMBER_OF_LENGTHS; ++i) {
|
||||
if (entry.myMask & (1 << i)) {
|
||||
if (mask & (1 << i)) {
|
||||
*address++ = entry.myLengths[i].Unit;
|
||||
memcpy(address, &entry.myLengths[i].Size, sizeof(short));
|
||||
address += sizeof(short);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entry.opacitySupported()) *address++ = entry.myOpacity;
|
||||
if (entry.alignmentTypeSupported()) *address++ = entry.myAlignmentType;
|
||||
if (mask & ZLTextStyleEntry::SUPPORT_OPACITY) *address++ = entry.myOpacity;
|
||||
if (mask & ZLTextStyleEntry::SUPPORT_ALIGNMENT_TYPE) *address++ = entry.myAlignmentType;
|
||||
if (entry.supportedFontModifier()) *address++ = entry.myFontModifier;
|
||||
if (entry.fontSizeSupported()) *address++ = entry.myFontSizeMag;
|
||||
if (entry.fontFamilySupported()) {
|
||||
memcpy(address, entry.fontFamily().data(), entry.fontFamily().length());
|
||||
address += entry.fontFamily().length();
|
||||
*address++ = '\0';
|
||||
if (mask & ZLTextStyleEntry::SUPPORT_FONT_SIZE) *address++ = entry.myFontSizeMag;
|
||||
if (mask & ZLTextStyleEntry::SUPPORT_FONT_FAMILIES) {
|
||||
const unsigned char n = entry.myFontFamilies.size();
|
||||
*address++ = n;
|
||||
for (unsigned int i = 0; i < n; ++i) {
|
||||
const std::string &font = entry.myFontFamilies.at(i);
|
||||
const unsigned int nbytes = font.length() + 1;
|
||||
memcpy(address, font.c_str(), nbytes);
|
||||
address += nbytes;
|
||||
}
|
||||
}
|
||||
myParagraphs.back()->addEntry(myLastEntryStart);
|
||||
}
|
||||
|
@ -289,3 +299,9 @@ void ZLTextModel::addLineBreak() {
|
|||
*myLastEntryStart = ZLTextParagraphEntry::LINE_BREAK_ENTRY;
|
||||
myParagraphs.back()->addEntry(myLastEntryStart);
|
||||
}
|
||||
|
||||
void ZLTextModel::addEmpty() {
|
||||
myLastEntryStart = myAllocator.allocate(1);
|
||||
*myLastEntryStart = ZLTextParagraphEntry::EMPTY_ENTRY;
|
||||
myParagraphs.back()->addEntry(myLastEntryStart);
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ public:
|
|||
void addFixedHSpace(unsigned char length);
|
||||
void addBidiReset();
|
||||
void addLineBreak();
|
||||
void addEmpty();
|
||||
|
||||
protected:
|
||||
void addParagraphInternal(ZLTextParagraph *paragraph);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include <ZLUnicodeUtil.h>
|
||||
#include <ZLStringUtil.h>
|
||||
#include <ZLImage.h>
|
||||
|
||||
#include "ZLTextParagraph.h"
|
||||
|
@ -34,26 +35,46 @@ size_t ZLTextEntry::dataLength() const {
|
|||
return len;
|
||||
}
|
||||
|
||||
short ZLTextStyleEntry::length(Length name, const Metrics &metrics) const {
|
||||
switch (myLengths[name].Unit) {
|
||||
int ZLTextStyleEntry::hlength(int size, SizeUnit unit, const Metrics &metrics)
|
||||
{
|
||||
switch (unit) {
|
||||
default:
|
||||
case SIZE_UNIT_PIXEL:
|
||||
return myLengths[name].Size;
|
||||
return size;
|
||||
case SIZE_UNIT_EM_100:
|
||||
return (myLengths[name].Size * metrics.FontSize + 50) / 100;
|
||||
return (size * metrics.FontSize + 50) / 100;
|
||||
case SIZE_UNIT_EX_100:
|
||||
return (myLengths[name].Size * metrics.FontXHeight + 50) / 100;
|
||||
return (size * metrics.FontXHeight + 50) / 100;
|
||||
case SIZE_UNIT_PERCENT:
|
||||
switch (name) {
|
||||
default:
|
||||
case LENGTH_LEFT_INDENT:
|
||||
case LENGTH_RIGHT_INDENT:
|
||||
case LENGTH_FIRST_LINE_INDENT_DELTA:
|
||||
return (myLengths[name].Size * metrics.FullWidth + 50) / 100;
|
||||
case LENGTH_SPACE_BEFORE:
|
||||
case LENGTH_SPACE_AFTER:
|
||||
return (myLengths[name].Size * metrics.FullHeight + 50) / 100;
|
||||
}
|
||||
return (size * metrics.FullWidth + 50) / 100;
|
||||
}
|
||||
}
|
||||
|
||||
int ZLTextStyleEntry::vlength(int size, SizeUnit unit, const Metrics &metrics)
|
||||
{
|
||||
switch (unit) {
|
||||
default:
|
||||
case SIZE_UNIT_PIXEL:
|
||||
return size;
|
||||
case SIZE_UNIT_EM_100:
|
||||
return (size * metrics.FontSize + 50) / 100;
|
||||
case SIZE_UNIT_EX_100:
|
||||
return (size * metrics.FontXHeight + 50) / 100;
|
||||
case SIZE_UNIT_PERCENT:
|
||||
return (size * metrics.FullHeight + 50) / 100;
|
||||
}
|
||||
}
|
||||
|
||||
short ZLTextStyleEntry::length(Length name, const Metrics &metrics) const {
|
||||
switch (name) {
|
||||
default:
|
||||
case LENGTH_LEFT_INDENT:
|
||||
case LENGTH_RIGHT_INDENT:
|
||||
case LENGTH_FIRST_LINE_INDENT_DELTA:
|
||||
return hlength(myLengths[name].Size, myLengths[name].Unit, metrics);
|
||||
case LENGTH_SPACE_BEFORE:
|
||||
case LENGTH_SPACE_AFTER:
|
||||
return vlength(myLengths[name].Size, myLengths[name].Unit, metrics);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,43 +101,145 @@ ZLTextStyleEntry::ZLTextStyleEntry(char *address) {
|
|||
if (fontSizeSupported()) {
|
||||
myFontSizeMag = (signed char)*address++;
|
||||
}
|
||||
if (fontFamilySupported()) {
|
||||
myFontFamily = address;
|
||||
if (fontFamiliesSupported()) {
|
||||
unsigned char n = *address++;
|
||||
for (unsigned int i = 0; i < n; ++i) {
|
||||
std::string font(address);
|
||||
address += font.length() + 1;
|
||||
myFontFamilies.push_back(font);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ZLTextStyleEntry::reset() {
|
||||
mySupportedFontModifier = 0;
|
||||
myMask = 0;
|
||||
myFontFamily.clear();
|
||||
myFontFamilies.clear();
|
||||
}
|
||||
|
||||
void ZLTextStyleEntry::apply(const ZLTextStyleEntry &entry) {
|
||||
for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) {
|
||||
if (entry.lengthSupported((Length)i)) {
|
||||
myLengths[i] = entry.myLengths[i];
|
||||
myMask |= 1 << i;
|
||||
void ZLTextStyleEntry::apply(const ZLTextStyleEntry &other) {
|
||||
if (other.myMask & ((1 << NUMBER_OF_LENGTHS) - 1)) {
|
||||
for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) {
|
||||
if (other.myMask & (1 << i)) {
|
||||
myLengths[i] = other.myLengths[i];
|
||||
myMask |= (1 << i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entry.opacitySupported()) {
|
||||
setOpacity(entry.opacity());
|
||||
if (other.myMask & SUPPORT_OPACITY) {
|
||||
setOpacity(other.myOpacity);
|
||||
}
|
||||
if (entry.alignmentTypeSupported()) {
|
||||
setAlignmentType(entry.alignmentType());
|
||||
if (other.myMask & SUPPORT_ALIGNMENT_TYPE) {
|
||||
setAlignmentType(other.myAlignmentType);
|
||||
}
|
||||
if (entry.mySupportedFontModifier) {
|
||||
myFontModifier &= ~entry.mySupportedFontModifier;
|
||||
myFontModifier |= (entry.myFontModifier & entry.mySupportedFontModifier);
|
||||
mySupportedFontModifier |= entry.mySupportedFontModifier;
|
||||
if (other.mySupportedFontModifier) {
|
||||
myFontModifier &= ~other.mySupportedFontModifier;
|
||||
myFontModifier |= (other.myFontModifier & other.mySupportedFontModifier);
|
||||
mySupportedFontModifier |= other.mySupportedFontModifier;
|
||||
}
|
||||
if (entry.fontSizeSupported()) {
|
||||
setFontSizeMag(entry.fontSizeMag());
|
||||
if (other.myMask & SUPPORT_FONT_SIZE) {
|
||||
setFontSizeMag(other.myFontSizeMag);
|
||||
}
|
||||
if (entry.fontFamilySupported()) {
|
||||
setFontFamily(entry.fontFamily());
|
||||
if (other.myMask & SUPPORT_FONT_FAMILIES) {
|
||||
setFontFamilies(other.myFontFamilies);
|
||||
}
|
||||
}
|
||||
|
||||
void ZLTextStyleEntry::inherit(const ZLTextStyleEntry &other) {
|
||||
// text-indent
|
||||
if (other.myMask & (1 << LENGTH_FIRST_LINE_INDENT_DELTA)) {
|
||||
myLengths[LENGTH_FIRST_LINE_INDENT_DELTA] = other.myLengths[LENGTH_FIRST_LINE_INDENT_DELTA];
|
||||
myMask |= (1 << LENGTH_FIRST_LINE_INDENT_DELTA);
|
||||
}
|
||||
// text-align
|
||||
if (other.myMask & SUPPORT_ALIGNMENT_TYPE) {
|
||||
setAlignmentType(other.myAlignmentType);
|
||||
}
|
||||
// font-style, font-variant, font-weight
|
||||
if (other.mySupportedFontModifier) {
|
||||
myFontModifier &= ~other.mySupportedFontModifier;
|
||||
myFontModifier |= (other.myFontModifier & other.mySupportedFontModifier);
|
||||
mySupportedFontModifier |= other.mySupportedFontModifier;
|
||||
}
|
||||
// font-size
|
||||
if (other.myMask & SUPPORT_FONT_SIZE) {
|
||||
setFontSizeMag(other.myFontSizeMag);
|
||||
}
|
||||
// font-family
|
||||
if (other.myMask & SUPPORT_FONT_FAMILIES) {
|
||||
setFontFamilies(other.myFontFamilies);
|
||||
}
|
||||
}
|
||||
|
||||
bool ZLTextStyleEntry::equals(const ZLTextStyleEntry &other) const {
|
||||
if (myMask == other.myMask && mySupportedFontModifier == other.mySupportedFontModifier) {
|
||||
if (myMask & ((1 << NUMBER_OF_LENGTHS) - 1)) {
|
||||
for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) {
|
||||
if (myMask & (1 << i)) {
|
||||
if (myLengths[i].Size != other.myLengths[i].Size ||
|
||||
myLengths[i].Unit != other.myLengths[i].Unit) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((myMask & SUPPORT_OPACITY) && myOpacity != other.myOpacity) {
|
||||
return false;
|
||||
}
|
||||
if ((myMask & SUPPORT_ALIGNMENT_TYPE) && myAlignmentType != other.myAlignmentType) {
|
||||
return false;
|
||||
}
|
||||
if ((myFontModifier & mySupportedFontModifier) != (other.myFontModifier & mySupportedFontModifier)) {
|
||||
return false;
|
||||
}
|
||||
if ((myMask & SUPPORT_FONT_SIZE) && myFontSizeMag != other.myFontSizeMag) {
|
||||
return false;
|
||||
}
|
||||
if ((myMask & SUPPORT_FONT_FAMILIES) && myFontFamilies != other.myFontFamilies) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ZLTextStyleEntry::parseLength(const std::string &toParse, short &size, SizeUnit &unit) {
|
||||
if (!toParse.empty()) {
|
||||
static const std::string PERCENT("%");
|
||||
static const std::string ZERO("0");
|
||||
static const std::string EM("em");
|
||||
static const std::string EX("ex");
|
||||
static const std::string PX("px");
|
||||
static const std::string PT("pt");
|
||||
static const std::string PC("pc");
|
||||
if (ZLStringUtil::stringEndsWith(toParse, PERCENT)) {
|
||||
unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT;
|
||||
size = atoi(toParse.c_str());
|
||||
return true;
|
||||
} else if (ZLStringUtil::stringEndsWith(toParse, EM)) {
|
||||
unit = ZLTextStyleEntry::SIZE_UNIT_EM_100;
|
||||
size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0));
|
||||
return true;
|
||||
} else if (ZLStringUtil::stringEndsWith(toParse, EX)) {
|
||||
unit = ZLTextStyleEntry::SIZE_UNIT_EX_100;
|
||||
size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0));
|
||||
return true;
|
||||
} else if (ZLStringUtil::stringEndsWith(toParse, PX) ||
|
||||
ZLStringUtil::stringEndsWith(toParse, PT) ||
|
||||
ZLStringUtil::stringEndsWith(toParse, PC)) {
|
||||
unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
|
||||
size = atoi(toParse.c_str());
|
||||
return true;
|
||||
} else if (toParse == ZERO) {
|
||||
unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
|
||||
size = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const shared_ptr<ZLTextParagraphEntry> ZLTextParagraph::Iterator::entry() const {
|
||||
if (myEntry.isNull()) {
|
||||
switch (*myPointer) {
|
||||
|
@ -205,11 +328,11 @@ void ZLTextParagraph::Iterator::next() {
|
|||
if (mask & ZLTextStyleEntry::SUPPORT_ALIGNMENT_TYPE) ++myPointer;
|
||||
if (supportedFontModifier) ++myPointer;
|
||||
if (mask & ZLTextStyleEntry::SUPPORT_FONT_SIZE) ++myPointer;
|
||||
if (mask & ZLTextStyleEntry::SUPPORT_FONT_FAMILY) {
|
||||
while (*myPointer != '\0') {
|
||||
++myPointer;
|
||||
if (mask & ZLTextStyleEntry::SUPPORT_FONT_FAMILIES) {
|
||||
unsigned char n = *myPointer++;
|
||||
for (unsigned int i = 0; i < n; ++i) {
|
||||
while (*myPointer++);
|
||||
}
|
||||
++myPointer;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -218,6 +341,7 @@ void ZLTextParagraph::Iterator::next() {
|
|||
break;
|
||||
case ZLTextParagraphEntry::RESET_BIDI_ENTRY:
|
||||
case ZLTextParagraphEntry::LINE_BREAK_ENTRY:
|
||||
case ZLTextParagraphEntry::EMPTY_ENTRY:
|
||||
++myPointer;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,8 @@ public:
|
|||
STYLE_ENTRY = 5,
|
||||
FIXED_HSPACE_ENTRY = 6,
|
||||
RESET_BIDI_ENTRY = 7,
|
||||
LINE_BREAK_ENTRY = 8
|
||||
LINE_BREAK_ENTRY = 8,
|
||||
EMPTY_ENTRY = 9
|
||||
};
|
||||
|
||||
protected:
|
||||
|
@ -99,15 +100,23 @@ public:
|
|||
~ZLTextStyleEntry();
|
||||
|
||||
void reset();
|
||||
void apply(const ZLTextStyleEntry &entry);
|
||||
void apply(const ZLTextStyleEntry &other);
|
||||
void inherit(const ZLTextStyleEntry &other);
|
||||
bool equals(const ZLTextStyleEntry &other) const;
|
||||
bool operator == (const ZLTextStyleEntry &other) const;
|
||||
ZLTextStyleEntry &operator = (const ZLTextStyleEntry &other);
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
bool lengthSupported(Length name) const;
|
||||
short length(Length name, const Metrics &metrics) const;
|
||||
short length(Length name, SizeUnit &unit) const;
|
||||
void setLength(Length name, short length, SizeUnit unit);
|
||||
|
||||
static int hlength(int size, SizeUnit unit, const Metrics &metrics);
|
||||
static int vlength(int size, SizeUnit unit, const Metrics &metrics);
|
||||
static bool parseLength(const std::string &toParse, short &size, SizeUnit &unit);
|
||||
|
||||
bool opacitySupported() const;
|
||||
unsigned char opacity() const;
|
||||
void setOpacity(unsigned char opacity);
|
||||
|
@ -124,14 +133,14 @@ public:
|
|||
signed char fontSizeMag() const;
|
||||
void setFontSizeMag(signed char fontSizeMag);
|
||||
|
||||
bool fontFamilySupported() const;
|
||||
const std::string &fontFamily() const;
|
||||
void setFontFamily(const std::string &fontFamily);
|
||||
bool fontFamiliesSupported() const;
|
||||
const std::vector<std::string> &fontFamilies() const;
|
||||
void setFontFamilies(const std::vector<std::string> &fontFamilies);
|
||||
|
||||
enum {
|
||||
SUPPORT_ALIGNMENT_TYPE = 1 << NUMBER_OF_LENGTHS,
|
||||
SUPPORT_FONT_SIZE = 1 << (NUMBER_OF_LENGTHS + 1),
|
||||
SUPPORT_FONT_FAMILY = 1 << (NUMBER_OF_LENGTHS + 2),
|
||||
SUPPORT_FONT_FAMILIES = 1 << (NUMBER_OF_LENGTHS + 2),
|
||||
SUPPORT_OPACITY = 1 << (NUMBER_OF_LENGTHS + 3)
|
||||
};
|
||||
|
||||
|
@ -145,7 +154,7 @@ private:
|
|||
unsigned char mySupportedFontModifier;
|
||||
unsigned char myFontModifier;
|
||||
signed char myFontSizeMag;
|
||||
std::string myFontFamily;
|
||||
std::vector<std::string> myFontFamilies;
|
||||
|
||||
friend class ZLTextModel;
|
||||
};
|
||||
|
@ -353,9 +362,11 @@ inline ZLTextStyleEntry::~ZLTextStyleEntry() {}
|
|||
inline ZLTextStyleEntry::Metrics::Metrics(int fontSize, int fontXHeight, int fullWidth, int fullHeight) : FontSize(fontSize), FontXHeight(fontXHeight), FullWidth(fullWidth), FullHeight(fullHeight) {}
|
||||
|
||||
inline bool ZLTextStyleEntry::isEmpty() const { return myMask == 0 && mySupportedFontModifier == 0; }
|
||||
inline bool ZLTextStyleEntry::operator == (const ZLTextStyleEntry &other) const { return equals(other); }
|
||||
inline ZLTextStyleEntry &ZLTextStyleEntry::operator = (const ZLTextStyleEntry &other) { reset(); apply(other); return *this; }
|
||||
|
||||
inline bool ZLTextStyleEntry::lengthSupported(Length name) const { return (myMask & (1 << name)) != 0; }
|
||||
inline short ZLTextStyleEntry::length(Length name, SizeUnit &unit) const { unit = myLengths[name].Unit; return myLengths[name].Size; }
|
||||
inline void ZLTextStyleEntry::setLength(Length name, short length, SizeUnit unit) {
|
||||
myLengths[name].Size = length;
|
||||
myLengths[name].Unit = unit;
|
||||
|
@ -385,9 +396,9 @@ inline bool ZLTextStyleEntry::fontSizeSupported() const { return (myMask & SUPPO
|
|||
inline signed char ZLTextStyleEntry::fontSizeMag() const { return myFontSizeMag; }
|
||||
inline void ZLTextStyleEntry::setFontSizeMag(signed char fontSizeMag) { myFontSizeMag = fontSizeMag; myMask |= SUPPORT_FONT_SIZE; }
|
||||
|
||||
inline bool ZLTextStyleEntry::fontFamilySupported() const { return (myMask & SUPPORT_FONT_FAMILY) == SUPPORT_FONT_FAMILY; }
|
||||
inline const std::string &ZLTextStyleEntry::fontFamily() const { return myFontFamily; }
|
||||
inline void ZLTextStyleEntry::setFontFamily(const std::string &fontFamily) { myFontFamily = fontFamily; myMask |= SUPPORT_FONT_FAMILY; }
|
||||
inline bool ZLTextStyleEntry::fontFamiliesSupported() const { return (myMask & SUPPORT_FONT_FAMILIES) == SUPPORT_FONT_FAMILIES; }
|
||||
inline const std::vector<std::string> &ZLTextStyleEntry::fontFamilies() const { return myFontFamilies; }
|
||||
inline void ZLTextStyleEntry::setFontFamilies(const std::vector<std::string> &fontFamilies) { myFontFamilies = fontFamilies; myMask |= SUPPORT_FONT_FAMILIES; }
|
||||
|
||||
inline ZLTextControlEntry::ZLTextControlEntry(ZLTextKind kind, bool isStart) : myKind(kind), myStart(isStart) {}
|
||||
inline ZLTextControlEntry::~ZLTextControlEntry() {}
|
||||
|
|
|
@ -30,7 +30,7 @@ ZLTextStyleDecoration::ZLTextStyleDecoration(const std::string &name, int fontSi
|
|||
ItalicOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":italic", italic),
|
||||
VerticalShiftOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":vShift", verticalShift),
|
||||
AllowHyphenationsOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":allowHyphenations", allowHyphenations),
|
||||
myName(name) {
|
||||
myName(name), myFirstLineIndentDelta(NULL), myFirstLineIndentDeltaUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL) {
|
||||
}
|
||||
|
||||
ZLTextFullStyleDecoration::ZLTextFullStyleDecoration(const std::string &name, int fontSizeDelta, ZLBoolean3 bold, ZLBoolean3 italic, short spaceBefore, short spaceAfter, short lineStartIndent, short lineEndIndent, short firstLineIndentDelta, int verticalShift, ZLTextAlignmentType alignment, double lineSpace, ZLBoolean3 allowHyphenations) : ZLTextStyleDecoration(name, fontSizeDelta, bold, italic, verticalShift, allowHyphenations),
|
||||
|
@ -38,23 +38,74 @@ ZLTextFullStyleDecoration::ZLTextFullStyleDecoration(const std::string &name, in
|
|||
SpaceAfterOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":spaceAfter", -10, 100, spaceAfter),
|
||||
LineStartIndentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":leftIndent", -300, 300, lineStartIndent),
|
||||
LineEndIndentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":rightIndent", -300, 300, lineEndIndent),
|
||||
FirstLineIndentDeltaOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":firstLineIndentDelta", -300, 300, firstLineIndentDelta),
|
||||
AlignmentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":alignment", alignment),
|
||||
LineSpaceOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":lineSpace", lineSpace),
|
||||
LineSpacePercentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":lineSpacePercent", (lineSpace == 0.0) ? -1 : (int)(lineSpace * 100)) {
|
||||
LineSpacePercentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":lineSpacePercent", (lineSpace == 0.0) ? -1 : (int)(lineSpace * 100)),
|
||||
FirstLineIndentDeltaOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":firstLineIndentDelta", -300, 300, firstLineIndentDelta),
|
||||
SpaceBeforeOptionUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL),
|
||||
SpaceAfterOptionUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL),
|
||||
LineStartIndentOptionUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL),
|
||||
LineEndIndentOptionUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL),
|
||||
FirstLineIndentDeltaOptionUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL) {
|
||||
}
|
||||
|
||||
const ZLTextFullStyleDecoration *ZLTextStyleDecoration::fullDecoration() const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
shared_ptr<ZLTextStyle> ZLTextStyleDecoration::createDecoratedStyle(const shared_ptr<ZLTextStyle> base) const {
|
||||
return new ZLTextPartialDecoratedStyle(base, *this);
|
||||
}
|
||||
|
||||
const ZLTextFullStyleDecoration *ZLTextFullStyleDecoration::fullDecoration() const {
|
||||
return this;
|
||||
}
|
||||
|
||||
shared_ptr<ZLTextStyle> ZLTextFullStyleDecoration::createDecoratedStyle(const shared_ptr<ZLTextStyle> base) const {
|
||||
return new ZLTextFullDecoratedStyle(base, *this);
|
||||
}
|
||||
|
||||
const std::string &ZLTextPartialDecoratedStyle::fontFamily() const {
|
||||
short ZLTextFullDecoratedStyle::spaceBefore(const ZLTextStyleEntry::Metrics &metrics) const {
|
||||
return ZLTextStyleEntry::vlength(myDecoration.SpaceBeforeOption.value(), myDecoration.SpaceBeforeOptionUnit, metrics);
|
||||
}
|
||||
|
||||
short ZLTextFullDecoratedStyle::spaceAfter(const ZLTextStyleEntry::Metrics &metrics) const {
|
||||
return ZLTextStyleEntry::vlength(myDecoration.SpaceAfterOption.value(), myDecoration.SpaceAfterOptionUnit, metrics);
|
||||
}
|
||||
|
||||
short ZLTextFullDecoratedStyle::lineStartIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const {
|
||||
return base()->lineEndIndent(metrics, rtl) + (rtl ?
|
||||
ZLTextStyleEntry::hlength(myDecoration.LineEndIndentOption.value(), myDecoration.LineEndIndentOptionUnit, metrics) :
|
||||
ZLTextStyleEntry::hlength(myDecoration.LineStartIndentOption.value(), myDecoration.LineStartIndentOptionUnit, metrics));
|
||||
}
|
||||
|
||||
short ZLTextFullDecoratedStyle::lineEndIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const {
|
||||
return base()->lineStartIndent(metrics, rtl) + (rtl ?
|
||||
ZLTextStyleEntry::hlength(myDecoration.LineStartIndentOption.value(), myDecoration.LineStartIndentOptionUnit, metrics) :
|
||||
ZLTextStyleEntry::hlength(myDecoration.LineEndIndentOption.value(), myDecoration.LineEndIndentOptionUnit, metrics));
|
||||
}
|
||||
|
||||
int ZLTextFullStyleDecoration::firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const {
|
||||
ZLTextStyleEntry::SizeUnit unit;
|
||||
int size;
|
||||
int* indent = ZLTextStyleDecoration::firstLineIndentDelta(unit);
|
||||
if (indent) {
|
||||
size = *indent;
|
||||
} else {
|
||||
size = FirstLineIndentDeltaOption.value();
|
||||
unit = FirstLineIndentDeltaOptionUnit;
|
||||
}
|
||||
return ZLTextStyleEntry::hlength(size, unit, metrics);
|
||||
}
|
||||
|
||||
ZLTextPartialDecoratedStyle::ZLTextPartialDecoratedStyle(const shared_ptr<ZLTextStyle> base, const ZLTextStyleDecoration &decoration) :
|
||||
ZLTextDecoratedStyle(base), myDecoration(decoration) {
|
||||
const std::string &family = myDecoration.FontFamilyOption.value();
|
||||
return (!family.empty()) ? family : base()->fontFamily();
|
||||
if (!family.empty()) myFontFamilies.push_back(family);
|
||||
}
|
||||
|
||||
const std::vector<std::string> &ZLTextPartialDecoratedStyle::fontFamilies() const {
|
||||
return (!myFontFamilies.empty()) ? myFontFamilies : base()->fontFamilies();
|
||||
}
|
||||
|
||||
int ZLTextPartialDecoratedStyle::fontSize() const {
|
||||
|
@ -74,16 +125,26 @@ bool ZLTextPartialDecoratedStyle::italic() const {
|
|||
bool ZLTextPartialDecoratedStyle::allowHyphenations() const {
|
||||
ZLBoolean3 a = myDecoration.AllowHyphenationsOption.value();
|
||||
return (a == B3_UNDEFINED) ? base()->allowHyphenations() : (a == B3_TRUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
short ZLTextPartialDecoratedStyle::firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const {
|
||||
ZLTextStyleEntry::SizeUnit unit;
|
||||
int* indent = myDecoration.firstLineIndentDelta(unit);
|
||||
return indent ? ZLTextStyleEntry::hlength(*indent, unit, metrics) :base()->firstLineIndentDelta(metrics);
|
||||
}
|
||||
|
||||
ZLTextFullDecoratedStyle::ZLTextFullDecoratedStyle(const shared_ptr<ZLTextStyle> base, const ZLTextFullStyleDecoration &decoration) :
|
||||
ZLTextDecoratedStyle(base), myDecoration(decoration) {
|
||||
const std::string &family = myDecoration.FontFamilyOption.value();
|
||||
if (!family.empty()) myFontFamilies.push_back(family);
|
||||
}
|
||||
|
||||
short ZLTextFullDecoratedStyle::firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const {
|
||||
return (alignment() == ALIGN_CENTER) ? 0 : base()->firstLineIndentDelta(metrics) + myDecoration.FirstLineIndentDeltaOption.value();
|
||||
return (alignment() == ALIGN_CENTER) ? 0 : myDecoration.firstLineIndentDelta(metrics);
|
||||
}
|
||||
|
||||
const std::string &ZLTextFullDecoratedStyle::fontFamily() const {
|
||||
const std::string &family = myDecoration.FontFamilyOption.value();
|
||||
return (!family.empty()) ? family : base()->fontFamily();
|
||||
const std::vector<std::string> &ZLTextFullDecoratedStyle::fontFamilies() const {
|
||||
return (!myFontFamilies.empty()) ? myFontFamilies : base()->fontFamilies();
|
||||
}
|
||||
|
||||
int ZLTextFullDecoratedStyle::fontSize() const {
|
||||
|
@ -108,7 +169,6 @@ ZLTextAlignmentType ZLTextFullDecoratedStyle::alignment() const {
|
|||
bool ZLTextFullDecoratedStyle::allowHyphenations() const {
|
||||
ZLBoolean3 a = myDecoration.AllowHyphenationsOption.value();
|
||||
return (a == B3_UNDEFINED) ? base()->allowHyphenations() : (a == B3_TRUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string &ZLTextPartialDecoratedStyle::colorStyle() const {
|
||||
|
@ -150,25 +210,17 @@ short ZLTextForcedStyle::lineEndIndent(const ZLTextStyleEntry::Metrics &metrics,
|
|||
}
|
||||
|
||||
short ZLTextForcedStyle::spaceBefore(const ZLTextStyleEntry::Metrics &metrics) const {
|
||||
if (myEntry.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE)) {
|
||||
const short baseSpace = base()->spaceBefore(metrics);
|
||||
ZLTextStyleEntry::Metrics adjusted(metrics);
|
||||
adjusted.FullHeight -= baseSpace + base()->spaceAfter(metrics);
|
||||
return baseSpace + myEntry.length(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, adjusted);
|
||||
} else {
|
||||
return base()->spaceBefore(metrics);
|
||||
}
|
||||
return
|
||||
myEntry.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE) ?
|
||||
myEntry.length(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, metrics) :
|
||||
base()->spaceBefore(metrics);
|
||||
}
|
||||
|
||||
short ZLTextForcedStyle::spaceAfter(const ZLTextStyleEntry::Metrics &metrics) const {
|
||||
if (myEntry.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_AFTER)) {
|
||||
const short baseSpace = base()->spaceBefore(metrics);
|
||||
ZLTextStyleEntry::Metrics adjusted(metrics);
|
||||
adjusted.FullHeight -= baseSpace + base()->spaceBefore(metrics);
|
||||
return baseSpace + myEntry.length(ZLTextStyleEntry::LENGTH_SPACE_AFTER, adjusted);
|
||||
} else {
|
||||
return base()->spaceAfter(metrics);
|
||||
}
|
||||
return
|
||||
myEntry.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_AFTER) ?
|
||||
myEntry.length(ZLTextStyleEntry::LENGTH_SPACE_AFTER, metrics) :
|
||||
base()->spaceAfter(metrics);
|
||||
}
|
||||
|
||||
short ZLTextForcedStyle::firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const {
|
||||
|
@ -221,10 +273,10 @@ int ZLTextForcedStyle::fontSize() const {
|
|||
}
|
||||
}
|
||||
|
||||
const std::string &ZLTextForcedStyle::fontFamily() const {
|
||||
const std::vector<std::string> &ZLTextForcedStyle::fontFamilies() const {
|
||||
return (!ZLTextStyleCollection::Instance().OverrideSpecifiedFontsOption.value() &&
|
||||
myEntry.fontFamilySupported()) ?
|
||||
myEntry.fontFamily() : base()->fontFamily();
|
||||
myEntry.fontFamiliesSupported()) ?
|
||||
myEntry.fontFamilies() : base()->fontFamilies();
|
||||
}
|
||||
|
||||
const std::string &ZLTextStyleDecoration::colorStyle() const {
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
ZLTextForcedStyle(shared_ptr<ZLTextStyle> base, const ZLTextStyleEntry &entry);
|
||||
~ZLTextForcedStyle();
|
||||
|
||||
const std::string &fontFamily() const;
|
||||
const std::vector<std::string> &fontFamilies() const;
|
||||
int fontSize() const;
|
||||
|
||||
bool bold() const;
|
||||
|
@ -77,7 +77,7 @@ private:
|
|||
|
||||
public:
|
||||
virtual ~ZLTextPartialDecoratedStyle();
|
||||
const std::string &fontFamily() const;
|
||||
const std::vector<std::string> &fontFamilies() const;
|
||||
int fontSize() const;
|
||||
bool bold() const;
|
||||
bool italic() const;
|
||||
|
@ -98,6 +98,7 @@ public:
|
|||
|
||||
private:
|
||||
const ZLTextStyleDecoration &myDecoration;
|
||||
std::vector<std::string> myFontFamilies;
|
||||
};
|
||||
|
||||
class ZLTextFullDecoratedStyle : public ZLTextDecoratedStyle {
|
||||
|
@ -108,7 +109,7 @@ private:
|
|||
|
||||
public:
|
||||
~ZLTextFullDecoratedStyle();
|
||||
const std::string &fontFamily() const;
|
||||
const std::vector<std::string> &fontFamilies() const;
|
||||
int fontSize() const;
|
||||
bool bold() const;
|
||||
bool italic() const;
|
||||
|
@ -129,6 +130,7 @@ public:
|
|||
|
||||
private:
|
||||
const ZLTextFullStyleDecoration &myDecoration;
|
||||
std::vector<std::string> myFontFamilies;
|
||||
};
|
||||
|
||||
inline ZLTextDecoratedStyle::ZLTextDecoratedStyle(const shared_ptr<ZLTextStyle> base) : myBase(base) {}
|
||||
|
@ -143,23 +145,16 @@ inline int ZLTextForcedStyle::verticalShift() const { return base()->verticalShi
|
|||
inline double ZLTextForcedStyle::lineSpace() const { return base()->lineSpace(); }
|
||||
inline bool ZLTextForcedStyle::allowHyphenations() const { return base()->allowHyphenations(); }
|
||||
|
||||
inline ZLTextPartialDecoratedStyle::ZLTextPartialDecoratedStyle(const shared_ptr<ZLTextStyle> base, const ZLTextStyleDecoration &decoration) : ZLTextDecoratedStyle(base), myDecoration(decoration) {}
|
||||
inline ZLTextPartialDecoratedStyle::~ZLTextPartialDecoratedStyle() {}
|
||||
inline short ZLTextPartialDecoratedStyle::spaceBefore(const ZLTextStyleEntry::Metrics &metrics) const { return base()->spaceBefore(metrics); }
|
||||
inline short ZLTextPartialDecoratedStyle::spaceAfter(const ZLTextStyleEntry::Metrics &metrics) const { return base()->spaceAfter(metrics); }
|
||||
inline short ZLTextPartialDecoratedStyle::lineStartIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const { return base()->lineStartIndent(metrics, rtl); }
|
||||
inline short ZLTextPartialDecoratedStyle::lineEndIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const { return base()->lineEndIndent(metrics, rtl); }
|
||||
inline short ZLTextPartialDecoratedStyle::firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const { return base()->firstLineIndentDelta(metrics); }
|
||||
inline int ZLTextPartialDecoratedStyle::verticalShift() const { return base()->verticalShift() + myDecoration.VerticalShiftOption.value(); }
|
||||
inline ZLTextAlignmentType ZLTextPartialDecoratedStyle::alignment() const { return base()->alignment(); }
|
||||
inline double ZLTextPartialDecoratedStyle::lineSpace() const { return base()->lineSpace(); }
|
||||
|
||||
inline ZLTextFullDecoratedStyle::ZLTextFullDecoratedStyle(const shared_ptr<ZLTextStyle> base, const ZLTextFullStyleDecoration &decoration) : ZLTextDecoratedStyle(base), myDecoration(decoration) {}
|
||||
inline ZLTextFullDecoratedStyle::~ZLTextFullDecoratedStyle() {}
|
||||
inline short ZLTextFullDecoratedStyle::spaceBefore(const ZLTextStyleEntry::Metrics&) const { return myDecoration.SpaceBeforeOption.value(); }
|
||||
inline short ZLTextFullDecoratedStyle::spaceAfter(const ZLTextStyleEntry::Metrics&) const { return myDecoration.SpaceAfterOption.value(); }
|
||||
inline short ZLTextFullDecoratedStyle::lineStartIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const { return base()->lineStartIndent(metrics, rtl) + myDecoration.LineStartIndentOption.value(); }
|
||||
inline short ZLTextFullDecoratedStyle::lineEndIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const { return base()->lineEndIndent(metrics, rtl) + myDecoration.LineEndIndentOption.value(); }
|
||||
inline int ZLTextFullDecoratedStyle::verticalShift() const { return base()->verticalShift() + myDecoration.VerticalShiftOption.value(); }
|
||||
inline double ZLTextFullDecoratedStyle::lineSpace() const {
|
||||
const int spacing = myDecoration.LineSpacePercentOption.value();
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define __ZLTEXTSTYLE_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <shared_ptr.h>
|
||||
|
||||
|
@ -43,7 +44,7 @@ public:
|
|||
|
||||
virtual bool isDecorated() const = 0;
|
||||
|
||||
virtual const std::string &fontFamily() const = 0;
|
||||
virtual const std::vector<std::string> &fontFamilies() const = 0;
|
||||
virtual int fontSize() const = 0;
|
||||
|
||||
virtual bool bold() const = 0;
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <ZLibrary.h>
|
||||
#include <ZLFile.h>
|
||||
|
@ -52,6 +54,7 @@ public:
|
|||
void startElementHandler(const char *tag, const char **attributes);
|
||||
|
||||
private:
|
||||
int lengthValue(const char **attributes, const char *name, ZLTextStyleEntry::SizeUnit &unit, int defaultValue = 0);
|
||||
int intValue(const char **attributes, const char *name, int defaultValue = 0);
|
||||
bool booleanValue(const char **attributes, const char *name);
|
||||
ZLBoolean3 b3Value(const char **attributes, const char *name);
|
||||
|
@ -62,6 +65,31 @@ private:
|
|||
|
||||
static const std::string TRUE_STRING = "true";
|
||||
|
||||
int ZLTextStyleReader::lengthValue(const char **attributes, const char *name, ZLTextStyleEntry::SizeUnit &unit, int defaultValue) {
|
||||
const char *stringValue = attributeValue(attributes, name);
|
||||
if (stringValue) {
|
||||
short size;
|
||||
if (ZLTextStyleEntry::parseLength(stringValue, size, unit)) {
|
||||
return size;
|
||||
} else {
|
||||
const char *s = stringValue;
|
||||
while (*s && isspace(*s)) s++;
|
||||
char* endptr = NULL;
|
||||
long number = strtol(s, &endptr, 10);
|
||||
if (endptr && endptr != s) {
|
||||
if ((number != LONG_MAX && number != LONG_MIN) || (errno != ERANGE)) {
|
||||
while (*endptr && isspace(*endptr)) endptr++;
|
||||
if (!*endptr) {
|
||||
unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
|
||||
return number;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
inline int ZLTextStyleReader::intValue(const char **attributes, const char *name, int defaultValue) {
|
||||
const char *stringValue = attributeValue(attributes, name);
|
||||
return (stringValue == 0) ? defaultValue : atoi(stringValue);
|
||||
|
@ -95,12 +123,25 @@ void ZLTextStyleReader::startElementHandler(const char *tag, const char **attrib
|
|||
|
||||
if (booleanValue(attributes, "partial")) {
|
||||
decoration = new ZLTextStyleDecoration(name, fontSizeDelta, bold, italic, verticalShift, allowHyphenations);
|
||||
const char *indentValue = attributeValue(attributes, "firstLineIndentDelta");
|
||||
if (indentValue) {
|
||||
short size;
|
||||
ZLTextStyleEntry::SizeUnit unit;
|
||||
if (ZLTextStyleEntry::parseLength(indentValue, size, unit)) {
|
||||
decoration->setFirstLineIndentDelta(size, unit);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int spaceBefore = intValue(attributes, "spaceBefore");
|
||||
int spaceAfter = intValue(attributes, "spaceAfter");
|
||||
int leftIndent = intValue(attributes, "leftIndent");
|
||||
int rightIndent = intValue(attributes, "rightIndent");
|
||||
int firstLineIndentDelta = intValue(attributes, "firstLineIndentDelta");
|
||||
ZLTextStyleEntry::SizeUnit spaceBeforeUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
|
||||
ZLTextStyleEntry::SizeUnit spaceAfterUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
|
||||
ZLTextStyleEntry::SizeUnit leftIndentUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
|
||||
ZLTextStyleEntry::SizeUnit rightIndentUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
|
||||
ZLTextStyleEntry::SizeUnit firstLineIndentDeltaUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
|
||||
int spaceBefore = lengthValue(attributes, "spaceBefore", spaceBeforeUnit);
|
||||
int spaceAfter = lengthValue(attributes, "spaceAfter", spaceAfterUnit);
|
||||
int leftIndent = lengthValue(attributes, "leftIndent", leftIndentUnit);
|
||||
int rightIndent = lengthValue(attributes, "rightIndent", rightIndentUnit);
|
||||
int firstLineIndentDelta = lengthValue(attributes, "firstLineIndentDelta", firstLineIndentDeltaUnit);
|
||||
|
||||
ZLTextAlignmentType alignment = ALIGN_UNDEFINED;
|
||||
const char *alignmentString = attributeValue(attributes, "alignment");
|
||||
|
@ -120,7 +161,13 @@ void ZLTextStyleReader::startElementHandler(const char *tag, const char **attrib
|
|||
const int lineSpacingPercent = intValue(attributes, "lineSpacingPercent", -1);
|
||||
const double lineSpace = (lineSpacingPercent == -1) ? 0.0 : (lineSpacingPercent / 100.0);
|
||||
|
||||
decoration = new ZLTextFullStyleDecoration(name, fontSizeDelta, bold, italic, spaceBefore, spaceAfter, leftIndent, rightIndent, firstLineIndentDelta, verticalShift, alignment, lineSpace, allowHyphenations);
|
||||
ZLTextFullStyleDecoration *dec = new ZLTextFullStyleDecoration(name, fontSizeDelta, bold, italic, spaceBefore, spaceAfter, leftIndent, rightIndent, firstLineIndentDelta, verticalShift, alignment, lineSpace, allowHyphenations);
|
||||
dec->SpaceBeforeOptionUnit = spaceBeforeUnit;
|
||||
dec->SpaceAfterOptionUnit = spaceAfterUnit;
|
||||
dec->LineStartIndentOptionUnit = leftIndentUnit;
|
||||
dec->LineEndIndentOptionUnit = rightIndentUnit;
|
||||
dec->FirstLineIndentDeltaOptionUnit = firstLineIndentDeltaUnit;
|
||||
decoration = dec;
|
||||
}
|
||||
const char *hyperlink = attributeValue(attributes, "hyperlink");
|
||||
if (hyperlink != 0) {
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
|
||||
#include <ZLOptions.h>
|
||||
#include <ZLTextStyle.h>
|
||||
#include <ZLTextParagraph.h>
|
||||
|
||||
class ZLTextFullStyleDecoration;
|
||||
|
||||
class ZLTextStyleDecoration {
|
||||
|
||||
|
@ -31,7 +34,7 @@ public:
|
|||
ZLTextStyleDecoration(const std::string &name, int fontSizeDelta, ZLBoolean3 bold, ZLBoolean3 italic, int verticalShift, ZLBoolean3 allowHyphenations);
|
||||
virtual ~ZLTextStyleDecoration();
|
||||
|
||||
virtual bool isFullDecoration() const;
|
||||
virtual const ZLTextFullStyleDecoration *fullDecoration() const;
|
||||
|
||||
virtual shared_ptr<ZLTextStyle> createDecoratedStyle(const shared_ptr<ZLTextStyle> base) const;
|
||||
|
||||
|
@ -40,6 +43,9 @@ public:
|
|||
const std::string &colorStyle() const;
|
||||
void setColorStyle(const std::string &colorStyle);
|
||||
|
||||
void setFirstLineIndentDelta(int indent, ZLTextStyleEntry::SizeUnit unit);
|
||||
int* firstLineIndentDelta(ZLTextStyleEntry::SizeUnit &unit) const;
|
||||
|
||||
public:
|
||||
ZLStringOption FontFamilyOption;
|
||||
ZLIntegerRangeOption FontSizeDeltaOption;
|
||||
|
@ -54,6 +60,8 @@ private:
|
|||
std::string myName;
|
||||
|
||||
std::string myColorStyle;
|
||||
int* myFirstLineIndentDelta;
|
||||
ZLTextStyleEntry::SizeUnit myFirstLineIndentDeltaUnit;
|
||||
};
|
||||
|
||||
class ZLTextFullStyleDecoration : public ZLTextStyleDecoration {
|
||||
|
@ -62,8 +70,9 @@ public:
|
|||
ZLTextFullStyleDecoration(const std::string &name, int fontSizeDelta, ZLBoolean3 bold, ZLBoolean3 italic, short spaceBefore, short spaceAfter, short lineStartIndent, short lineEndIndent, short firstLineIndentDelta, int verticalShift, ZLTextAlignmentType alignment, double lineSpace, ZLBoolean3 allowHyphenations);
|
||||
~ZLTextFullStyleDecoration();
|
||||
|
||||
virtual bool isFullDecoration() const;
|
||||
int firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const;
|
||||
|
||||
const ZLTextFullStyleDecoration *fullDecoration() const;
|
||||
shared_ptr<ZLTextStyle> createDecoratedStyle(const shared_ptr<ZLTextStyle> base) const;
|
||||
|
||||
public:
|
||||
|
@ -71,12 +80,21 @@ public:
|
|||
ZLIntegerRangeOption SpaceAfterOption;
|
||||
ZLIntegerRangeOption LineStartIndentOption;
|
||||
ZLIntegerRangeOption LineEndIndentOption;
|
||||
ZLIntegerRangeOption FirstLineIndentDeltaOption;
|
||||
|
||||
ZLIntegerOption AlignmentOption;
|
||||
|
||||
ZLDoubleOption LineSpaceOption;
|
||||
ZLIntegerOption LineSpacePercentOption;
|
||||
|
||||
private:
|
||||
ZLIntegerRangeOption FirstLineIndentDeltaOption;
|
||||
|
||||
public:
|
||||
ZLTextStyleEntry::SizeUnit SpaceBeforeOptionUnit;
|
||||
ZLTextStyleEntry::SizeUnit SpaceAfterOptionUnit;
|
||||
ZLTextStyleEntry::SizeUnit LineStartIndentOptionUnit;
|
||||
ZLTextStyleEntry::SizeUnit LineEndIndentOptionUnit;
|
||||
ZLTextStyleEntry::SizeUnit FirstLineIndentDeltaOptionUnit;
|
||||
};
|
||||
|
||||
class ZLTextStyleCollection {
|
||||
|
@ -103,11 +121,15 @@ private:
|
|||
friend class ZLTextStyleReader;
|
||||
};
|
||||
|
||||
inline ZLTextStyleDecoration::~ZLTextStyleDecoration() {}
|
||||
inline bool ZLTextStyleDecoration::isFullDecoration() const { return false; }
|
||||
inline ZLTextStyleDecoration::~ZLTextStyleDecoration() { delete myFirstLineIndentDelta; }
|
||||
inline const std::string &ZLTextStyleDecoration::name() const { return myName; }
|
||||
inline int* ZLTextStyleDecoration::firstLineIndentDelta(ZLTextStyleEntry::SizeUnit &unit) const {
|
||||
unit = myFirstLineIndentDeltaUnit; return myFirstLineIndentDelta;
|
||||
}
|
||||
inline void ZLTextStyleDecoration::setFirstLineIndentDelta(int indent, ZLTextStyleEntry::SizeUnit unit) {
|
||||
delete myFirstLineIndentDelta; myFirstLineIndentDelta = new int(indent); myFirstLineIndentDeltaUnit = unit;
|
||||
}
|
||||
|
||||
inline ZLTextFullStyleDecoration::~ZLTextFullStyleDecoration() {}
|
||||
inline bool ZLTextFullStyleDecoration::isFullDecoration() const { return true; }
|
||||
|
||||
#endif /* __ZLTEXTSTYLECOLLECTION_H__ */
|
||||
|
|
|
@ -114,7 +114,7 @@ size_t ZLTextView::PositionIndicator::endTextIndex() const {
|
|||
}
|
||||
|
||||
void ZLTextView::PositionIndicator::drawExtraText(const std::string &text) {
|
||||
context().setFont(myTextView.baseStyle()->fontFamily(), myInfo.fontSize(), false, false);
|
||||
context().setFont(context().pickFontFamily(myTextView.baseStyle()->fontFamilies()), myInfo.fontSize(), false, false);
|
||||
context().setColor(myTextView.color());
|
||||
|
||||
int width = context().stringWidth(text.data(), text.length(), false);
|
||||
|
|
Loading…
Reference in a new issue