[fbreader] Refactored CSS and XHTML support

This significantly improves EPUB rendering.
This commit is contained in:
Slava Monich 2015-08-09 00:54:38 +03:00
parent ab68bef4e6
commit c6e46b94a2
34 changed files with 1349 additions and 590 deletions

View file

@ -1,24 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<DefaultStyles> <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="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="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="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="5" name="Annotation" fontSizeDelta="-2" firstLineIndentDelta="20" allowHyphenations="true"/>
<style id="6" name="Epigraph" fontSizeDelta="-2" italic="true" leftIndent="80" 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="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="13" name="Author" leftIndent="20" allowHyphenations="false"/>
<style id="14" name="Date" leftIndent="40" 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="7" name="Stanza" spaceBefore="6" spaceAfter="6" alignment="linestart" allowHyphenations="false"/>
<style id="8" name="Verse" leftIndent="20" 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="23" name="Contents Table" spaceAfter="7" leftIndent="20" firstLineIndentDelta="-20" alignment="linestart"/>
<style id="25" name="Library Entry" alignment="linestart" allowHyphenations="false"/> <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="12" partial="true" name="Cite" italic="true"/>
<style id="15" partial="true" name="Internal Hyperlink" allowHyphenations="false" hyperlink="internal"/> <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="16" partial="true" name="Footnote" fontSizeDelta="-6" vShift="10" allowHyphenations="false" hyperlink="internal"/>
<style id="17" partial="true" name="Emphasis" italic="true"/> <style id="17" partial="true" name="Emphasis" italic="true"/>
<style id="18" partial="true" name="Strong" bold="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="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="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="22" partial="true" name="StrikeThrough"/>
<style id="27" partial="true" name="Italic" italic="true"/> <style id="27" partial="true" name="Italic" italic="true"/>
<style id="28" partial="true" name="Bold" bold="true"/> <style id="28" partial="true" name="Bold" bold="true"/>
<style id="29" partial="true" name="Definition" italic="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> </DefaultStyles>

View file

@ -65,7 +65,7 @@ public:
bool isDecorated() const; bool isDecorated() const;
const std::string &fontFamily() const; const std::vector<std::string> &fontFamilies() const;
int fontSize() const; int fontSize() const;
bool bold() const; bool bold() const;
@ -102,10 +102,10 @@ BooksSettings::TextStyle::isDecorated() const
return iDefaultStyle->isDecorated(); return iDefaultStyle->isDecorated();
} }
const std::string& const std::vector<std::string>&
BooksSettings::TextStyle::fontFamily() const BooksSettings::TextStyle::fontFamilies() const
{ {
return iDefaultStyle->fontFamily(); return iDefaultStyle->fontFamilies();
} }
int int

View file

@ -54,6 +54,11 @@ shared_ptr<ZLTextStyle> BooksTextStyle::defaults()
return style; return style;
} }
BooksTextStyle::BooksTextStyle()
{
iFontFamilies.push_back(Default::FONT_FAMILY);
}
bool bool
BooksTextStyle::equalLayout( BooksTextStyle::equalLayout(
shared_ptr<ZLTextStyle> aStyle1, shared_ptr<ZLTextStyle> aStyle1,
@ -74,9 +79,9 @@ bool BooksTextStyle::isDecorated() const
return false; 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 int BooksTextStyle::fontSize() const

View file

@ -49,18 +49,18 @@ private:
static weak_ptr<ZLTextStyle> gInstance; static weak_ptr<ZLTextStyle> gInstance;
private: private:
BooksTextStyle() {} BooksTextStyle();
public: public:
bool isDecorated() const; bool isDecorated() const;
const std::string &fontFamily() const; const std::vector<std::string>& fontFamilies() const;
int fontSize() const; int fontSize() const;
bool bold() const; bool bold() const;
bool italic() const; bool italic() const;
const std::string &colorStyle() const; const std::string& colorStyle() const;
short spaceBefore(const ZLTextStyleEntry::Metrics& aMetrics) const; short spaceBefore(const ZLTextStyleEntry::Metrics& aMetrics) const;
short spaceAfter(const ZLTextStyleEntry::Metrics& aMetrics) const; short spaceAfter(const ZLTextStyleEntry::Metrics& aMetrics) const;
@ -73,6 +73,9 @@ public:
double lineSpace() const; double lineSpace() const;
bool allowHyphenations() const; bool allowHyphenations() const;
private:
std::vector<std::string> iFontFamilies;
}; };
#endif // BOOKS_TEXT_STYLE_H #endif // BOOKS_TEXT_STYLE_H

View file

@ -117,6 +117,13 @@ void BookReader::addLineBreak() {
} }
} }
void BookReader::addEmpty() {
if (myTextParagraphExists) {
flushTextBufferToParagraph();
myCurrentTextModel->addEmpty();
}
}
void BookReader::addControl(const ZLTextStyleEntry &entry) { void BookReader::addControl(const ZLTextStyleEntry &entry) {
if (myTextParagraphExists) { if (myTextParagraphExists) {
flushTextBufferToParagraph(); flushTextBufferToParagraph();
@ -189,7 +196,7 @@ void BookReader::addContentsData(const std::string &data) {
void BookReader::flushTextBufferToParagraph() { void BookReader::flushTextBufferToParagraph() {
myCurrentTextModel->addText(myBuffer); myCurrentTextModel->addText(myBuffer);
myBuffer.clear(); myBuffer.resize(0);
} }
void BookReader::addImage(const std::string &id, shared_ptr<const ZLImage> image) { 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(); ZLTextTreeParagraph *peek = myTOCStack.empty() ? 0 : myTOCStack.top();
if (!myContentsBuffer.empty()) { if (!myContentsBuffer.empty()) {
contentsModel.addText(myContentsBuffer); contentsModel.addText(myContentsBuffer);
myContentsBuffer.clear(); myContentsBuffer.resize(0);
myLastTOCParagraphIsEmpty = false; myLastTOCParagraphIsEmpty = false;
} }
if (myLastTOCParagraphIsEmpty) { if (myLastTOCParagraphIsEmpty) {
@ -259,7 +266,7 @@ void BookReader::endContentsParagraph() {
ContentsModel &contentsModel = (ContentsModel&)*myModel.myContentsModel; ContentsModel &contentsModel = (ContentsModel&)*myModel.myContentsModel;
if (!myContentsBuffer.empty()) { if (!myContentsBuffer.empty()) {
contentsModel.addText(myContentsBuffer); contentsModel.addText(myContentsBuffer);
myContentsBuffer.clear(); myContentsBuffer.resize(0);
myLastTOCParagraphIsEmpty = false; myLastTOCParagraphIsEmpty = false;
} }
if (myLastTOCParagraphIsEmpty) { if (myLastTOCParagraphIsEmpty) {

View file

@ -59,6 +59,7 @@ public:
void addHyperlinkLabel(const std::string &label, int paragraphNumber); void addHyperlinkLabel(const std::string &label, int paragraphNumber);
void addFixedHSpace(unsigned char length); void addFixedHSpace(unsigned char length);
void addLineBreak(); void addLineBreak();
void addEmpty();
void addImageReference(const std::string &id, short vOffset = 0); void addImageReference(const std::string &id, short vOffset = 0);
void addImage(const std::string &id, shared_ptr<const ZLImage> image); void addImage(const std::string &id, shared_ptr<const ZLImage> image);

View file

@ -65,6 +65,7 @@ enum FBTextKind {
H6 = 36, H6 = 36,
EXTERNAL_HYPERLINK = 37, EXTERNAL_HYPERLINK = 37,
BOOK_HYPERLINK = 38, BOOK_HYPERLINK = 38,
BLOCKQUOTE = 39
}; };
#endif /* __FBTEXTKIND_H__ */ #endif /* __FBTEXTKIND_H__ */

View file

@ -35,28 +35,39 @@ void StyleSheetTableParser::storeData(const std::string &selector, const StyleSh
StyleSheetTable::Style StyleSheetSingleStyleParser::parseString(const char *text) { StyleSheetTable::Style StyleSheetSingleStyleParser::parseString(const char *text) {
StyleSheetTable::Style style; StyleSheetTable::Style style;
if (text) { if (text) {
myReadState = ATTRIBUTE_NAME; reset(ATTRIBUTE_NAME);
parse(text, strlen(text), true); 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); StyleSheetTable::updateStyle(style, myMap);
reset();
} }
return style; return style;
} }
StyleSheetParser::StyleSheetParser() : myReadState(TAG_NAME), myInsideComment(false), myAtBlockDepth(0) { StyleSheetParser::StyleSheetParser() :
myBuffer1(0), myBuffer2(0), myBuffer3(0) {
myStateStack.push(SELECTOR);
} }
StyleSheetParser::~StyleSheetParser() { StyleSheetParser::~StyleSheetParser() {
} }
void StyleSheetParser::reset() { void StyleSheetParser::reset(ReadState state) {
myWord.erase(); myWord.resize(0);
myAttributeName.erase(); myAttributeName.resize(0);
myReadState = TAG_NAME; myStateStack = std::stack<ReadState>();
myInsideComment = false; myStateStack.push(state);
myAtBlockDepth = 0; mySelectors.resize(0);
mySelectors.clear();
myMap.clear(); myMap.clear();
myBuffer1 = myBuffer2 = myBuffer3 = 0;
} }
void StyleSheetParser::parse(ZLInputStream &stream) { 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 *start = text;
const char *end = text + len; const char *end = text + len;
for (const char *ptr = start; ptr != end; ++ptr) { for (const char *ptr = start; ptr != end; ++ptr) {
if ((myReadState != TAG_NAME && isspace(*ptr)) || processChar1(*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;
} }
} }
void StyleSheetParser::storeData(const std::string&, const StyleSheetTable::AttributeMap&) { void StyleSheetParser::storeData(const std::string&, const StyleSheetTable::AttributeMap&) {
} }
void StyleSheetParser::processControl(const char control) { /* Converts \r\n into \n */
switch (control) { void StyleSheetParser::processChar1(char c) {
case '{': if (myBuffer1 == '\r') {
switch (myReadState) { myBuffer1 = 0;
case AT_RULE: if (c == '\n') {
myReadState = AT_BLOCK; processChar2('\n');
myAtBlockDepth = 1; } else {
break; processChar2('\r');
case AT_BLOCK: }
myAtBlockDepth++; } else if (c == '\r') {
break; myBuffer1 = '\r';
case TAG_NAME: } else {
myReadState = ATTRIBUTE_NAME; processChar2(c);
myMap.clear(); }
break; }
default:
myReadState = BROKEN; /* Glues continued lines together */
break; 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; break;
case '}': case '}':
switch (myReadState) { finishRule();
case AT_BLOCK: myStateStack.pop();
if (--myAtBlockDepth > 0) { break;
return; default:
} if (!isspace(c) || !myWord.empty()) {
break; myWord.append(1, c);
case AT_RULE:
case BROKEN:
break;
default:
for (unsigned int i=0; i<mySelectors.size(); i++) {
storeData(mySelectors[i], myMap);
}
break;
} }
myReadState = TAG_NAME; break;
mySelectors.clear(); }
myMap.clear(); 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; break;
case ';': case ';':
switch (myReadState) { case '\n':
case AT_RULE: finishAttributeValue();
myReadState = TAG_NAME; finishAttribute();
mySelectors.clear(); 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(); 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; break;
case ATTRIBUTE_NAME: }
myAttributeName = word; break;
myMap[myAttributeName].clear();
break; case COMMENT: // Comments are handled elsewhere
case ATTRIBUTE_VALUE: break;
myMap[myAttributeName].push_back(word);
break;
case BROKEN:
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();
}

View file

@ -22,6 +22,8 @@
#include "StyleSheetTable.h" #include "StyleSheetTable.h"
#include <stack>
class ZLInputStream; class ZLInputStream;
class StyleSheetParser { class StyleSheetParser {
@ -39,26 +41,37 @@ protected:
virtual void storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map); virtual void storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map);
private: private:
bool isControlSymbol(const char symbol); enum ReadState {
void processWord(std::string &word); COMMENT,
void processWordWithoutComments(std::string word); SELECTOR,
void processControl(const char control); 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: private:
std::string myWord; std::string myWord;
std::string myAttributeName; std::string myAttributeName;
enum { std::stack<ReadState> myStateStack;
AT_RULE,
AT_BLOCK,
TAG_NAME,
ATTRIBUTE_NAME,
ATTRIBUTE_VALUE,
BROKEN,
} myReadState;
bool myInsideComment;
int myAtBlockDepth;
std::vector<std::string> mySelectors; std::vector<std::string> mySelectors;
StyleSheetTable::AttributeMap myMap; StyleSheetTable::AttributeMap myMap;
char myBuffer1;
char myBuffer2;
char myBuffer3;
friend class StyleSheetSingleStyleParser; friend class StyleSheetSingleStyleParser;
}; };
@ -81,4 +94,6 @@ public:
StyleSheetTable::Style parseString(const char *text); StyleSheetTable::Style parseString(const char *text);
}; };
inline void StyleSheetParser::reset() { reset(SELECTOR); }
#endif /* __STYLESHEETPARSER_H__ */ #endif /* __STYLESHEETPARSER_H__ */

View file

@ -64,6 +64,30 @@ void StyleSheetTable::Style::apply(const Style &other) {
if (other.PageBreakAfter != B3_UNDEFINED) { if (other.PageBreakAfter != B3_UNDEFINED) {
PageBreakAfter = other.PageBreakAfter; 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 { 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) { void StyleSheetTable::addMap(const std::vector<std::string> &selectors, const AttributeMap &map) {
if ((!selectors.empty()) && !map.empty()) { if ((!selectors.empty()) && !map.empty()) {
// http://www.w3.org/TR/selectors/#specificity Style style(map);
int a = 0, b = 0, c = 0; if (!style.empty()) {
SelectorList stack; // http://www.w3.org/TR/selectors/#specificity
for (unsigned int i=0; i<selectors.size(); i++) { int a = 0, b = 0, c = 0;
const Selector &selector = selectors[i]; SelectorList stack;
a += selector.a(); for (unsigned int i=0; i<selectors.size(); i++) {
b += selector.b(); const Selector &selector = selectors[i];
c += selector.c(); a += selector.a();
stack.push_back(selector); b += selector.b();
} c += selector.c();
if (a > 255) a = 255; stack.push_back(selector);
if (b > 255) b = 255; }
if (c > 255) c = 255; if (a > 255) a = 255;
myEntries.push_back(Entry(stack, (a << 16) + (b << 8) + c, map)); if (b > 255) b = 255;
} if (c > 255) c = 255;
} myEntries.push_back(Entry(stack, (a << 16) + (b << 8) + c, style));
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;
} }
} }
return false;
} }
bool StyleSheetTable::parseMargin(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) { 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 // Negative margins do make sense but we don't really support them
if (size < 0) size = 0; if (size < 0) size = 0;
return true; 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) { void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value) {
short size; short size;
ZLTextStyleEntry::SizeUnit unit; ZLTextStyleEntry::SizeUnit unit;
if (parseLength(value, size, unit)) { if (ZLTextStyleEntry::parseLength(value, size, unit)) {
entry.setLength(name, size, unit); entry.setLength(name, size, unit);
} }
} }
@ -200,9 +198,11 @@ void StyleSheetTable::applyStyles(const ElementList &stack, Style &style) const
entries.push_back(&(*i)); entries.push_back(&(*i));
} }
} }
std::sort(entries.begin(), entries.end(), sortBySpecificity); if (!entries.empty()) {
for (std::vector<const Entry*>::const_iterator e = entries.begin(); e != entries.end(); ++e) { std::sort(entries.begin(), entries.end(), sortBySpecificity);
style.apply((*e)->Style); 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) { 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.empty()) {
if (alignment[0] == "justify") { const std::string &value = alignment.at(0);
if (value == "justify") {
entry.setAlignmentType(ALIGN_JUSTIFY); entry.setAlignmentType(ALIGN_JUSTIFY);
} else if (alignment[0] == "left") { } else if (value == "left") {
entry.setAlignmentType(ALIGN_LEFT); entry.setAlignmentType(ALIGN_LEFT);
} else if (alignment[0] == "right") { } else if (value == "right") {
entry.setAlignmentType(ALIGN_RIGHT); entry.setAlignmentType(ALIGN_RIGHT);
} else if (alignment[0] == "center") { } else if (value == "center") {
entry.setAlignmentType(ALIGN_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"); static const std::string FONT_WEIGHT("font-weight");
if (!bold.empty()) { const std::vector<std::string> &weight = values(styles, FONT_WEIGHT);
if (!weight.empty()) {
int num = -1; int num = -1;
if (bold[0] == "bold") { const std::string &value = weight.at(0);
if (value == "bold") {
num = 700; num = 700;
} else if (bold[0] == "normal") { } else if (value == "normal") {
num = 400; num = 400;
} else if ((bold[0].length() == 3) && } else if ((value.length() == 3) &&
(bold[0][1] == '0') && (value[1] == '0') &&
(bold[0][2] == '0') && (value[2] == '0') &&
(bold[0][0] >= '1') && (value[0] >= '1') &&
(bold[0][0] <= '9')) { (value[0] <= '9')) {
num = 100 * (bold[0][0] - '0'); num = 100 * (value[0] - '0');
} else if (bold[0] == "bolder") { } else if (value == "bolder") {
} else if (bold[0] == "lighter") { } else if (value == "lighter") {
} }
if (num != -1) { if (num != -1) {
entry.setFontModifier(FONT_MODIFIER_BOLD, num >= 600); 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()) { if (!italic.empty()) {
entry.setFontModifier(FONT_MODIFIER_ITALIC, italic[0] == "italic"); 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()) { if (!variant.empty()) {
entry.setFontModifier(FONT_MODIFIER_SMALLCAPS, variant[0] == "small-caps"); entry.setFontModifier(FONT_MODIFIER_SMALLCAPS, variant[0] == "small-caps");
} }
const std::vector<std::string> &fontFamily = values(styles, "font-family"); static const std::string FONT_FAMILY("font-family");
if (!fontFamily.empty() && !fontFamily[0].empty()) { const std::vector<std::string> &fontFamilies = values(styles, FONT_FAMILY);
entry.setFontFamily(fontFamily[0]); if (!fontFamilies.empty()) entry.setFontFamilies(fontFamilies);
}
short size; short size;
ZLTextStyleEntry::SizeUnit unit; 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()) { if (!fontSize.empty()) {
std::string value = fontSize[0]; const std::string &value = fontSize.at(0);
if (value == "xx-small") { if (value == "xx-small") {
entry.setFontSizeMag(-3); entry.setFontSizeMag(-3);
} else if (value == "x-small") { } else if (value == "x-small") {
@ -285,7 +302,7 @@ void StyleSheetTable::updateTextStyle(ZLTextStyleEntry &entry, const AttributeMa
} else if (value == "xx-large") { } else if (value == "xx-large") {
entry.setFontSizeMag(3); entry.setFontSizeMag(3);
} else { } else {
if (parseLength(value, size, unit)) { if (ZLTextStyleEntry::parseLength(value, size, unit)) {
switch (unit) { switch (unit) {
case ZLTextStyleEntry::SIZE_UNIT_PIXEL: case ZLTextStyleEntry::SIZE_UNIT_PIXEL:
// What to do with pixels? // 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_EM_100:
case ZLTextStyleEntry::SIZE_UNIT_EX_100: case ZLTextStyleEntry::SIZE_UNIT_EX_100:
case ZLTextStyleEntry::SIZE_UNIT_PERCENT: case ZLTextStyleEntry::SIZE_UNIT_PERCENT:
entry.setFontSizeMag((size < 100 && size > 80) ? -1 : // Percent to magnification mapping algorithm
(size > 100 && size < 120) ? 1 : // matches ZLTextForcedStyle::fontSize() logic
(size - 100)/20); 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; 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()) { if (!opacity.empty()) {
const int value = (int)(255 * ZLStringUtil::stringToDouble(opacity[0], 1)); const int value = (int)(255 * ZLStringUtil::stringToDouble(opacity[0], 1));
entry.setOpacity((unsigned char)((value < 0) ? 0 : (value > 255) ? 255 : value)); entry.setOpacity((unsigned char)((value < 0) ? 0 : (value > 255) ? 255 : value));
} }
std::vector<std::string> margins = values(styles, "margin"); // Margins will overwrite padding, sorry
if (!margins.empty() && margins.back() == "!important") { 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 // 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()) { switch (margins.size()) {
case 1: case 1:
if (parseMargin(margins[0], size, unit)) { if (parseMargin(margins[0], size, unit)) {
@ -348,49 +407,61 @@ void StyleSheetTable::updateTextStyle(ZLTextStyleEntry &entry, const AttributeMa
break; break;
} }
setMargin(entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, styles, "margin-left"); static const std::string MARGIN_LEFT("margin-left");
setMargin(entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, styles, "margin-right"); static const std::string MARGIN_RIGHT("margin-right");
setLength(entry, ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, styles, "text-indent"); static const std::string MARGIN_TOP("margin-top");
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "margin-top"); static const std::string MARGIN_BOTTOM("margin-bottom");
setLength(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "padding-top"); static const std::string TEXT_INDENT("text-indent");
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "margin-bottom");
setLength(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "padding-bottom"); 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) { void StyleSheetTable::getPageBreakValue(const std::vector<std::string> &values, ZLBoolean3 &value) {
const std::vector<std::string> &pbb = values(map, "page-break-before"); if (!values.empty()) {
if (!pbb.empty()) { const std::string &first = values.at(0);
if ((pbb[0] == "always") || if ((first == "always") ||
(pbb[0] == "left") || (first == "left") ||
(pbb[0] == "right")) { (first == "right")) {
value = B3_TRUE; value = B3_TRUE;
return true; } else if (first == "avoid") {
} else if (pbb[0] == "avoid") {
value = B3_FALSE; 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) { void StyleSheetTable::updateStyle(Style &style, const AttributeMap &map) {
updateTextStyle(style.TextStyle, 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;
}
} }

View file

@ -73,29 +73,43 @@ public:
typedef std::vector<Selector> SelectorList; typedef std::vector<Selector> SelectorList;
typedef std::map<std::string,std::vector<std::string> > AttributeMap; 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 { struct Style {
Style(); Style();
Style(const Style &other); Style(const Style &other);
Style(const AttributeMap &map); Style(const AttributeMap &map);
Style &operator = (const Style &other); 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; ZLTextStyleEntry TextStyle;
ZLBoolean3 PageBreakBefore; ZLBoolean3 PageBreakBefore;
ZLBoolean3 PageBreakAfter; ZLBoolean3 PageBreakAfter;
WhiteSpaceValue WhiteSpace;
bool DisplayNone;
}; };
typedef std::vector<Style> StyleList; typedef std::vector<Style> StyleList;
static void updateStyle(Style &style, const AttributeMap &map); static void updateStyle(Style &style, const AttributeMap &map);
static void updateTextStyle(ZLTextStyleEntry &entry, const AttributeMap &map); static void updateTextStyle(ZLTextStyleEntry &entry, const AttributeMap &map);
static bool getPageBreakBefore(const AttributeMap &map, ZLBoolean3 &value); static void getPageBreakValue(const std::vector<std::string> &values, ZLBoolean3 &value);
static bool getPageBreakAfter(const AttributeMap &map, ZLBoolean3 &value);
private: private:
struct Entry { struct Entry {
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); Entry &operator = (const Entry &other);
bool match(const ElementList &stack) const; bool match(const ElementList &stack) const;
@ -106,13 +120,12 @@ private:
void addMap(const std::vector<std::string> &selectors, const AttributeMap &map); 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 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 AttributeMap &map, const std::string &attributeName);
static void setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value); 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 AttributeMap &map, const std::string &attributeName);
static void setMargin(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value); 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); static bool sortBySpecificity(const Entry *e1, const Entry *e2);
public: 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 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(const StyleSheetTable::Selector &other) : myType(other.myType), myClass(other.myClass), myId(other.myId) {}
inline StyleSheetTable::Selector::Selector() {} 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; myType = other.myType; myClass = other.myClass; myId = other.myId; return *this;
} }
inline bool StyleSheetTable::Selector::operator == (const Selector &other) const { 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 { inline bool StyleSheetTable::Selector::operator != (const Selector &other) const {
return (&other != this) && (myType != other.myType || myClass != other.myClass || myId != other.myId); 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))); 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 { 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::klass() const { return myClass; }
inline const std::string &StyleSheetTable::Selector::id() const { return myId; } inline const std::string &StyleSheetTable::Selector::id() const { return myId; }
inline StyleSheetTable::Style::Style() : PageBreakBefore(B3_UNDEFINED), PageBreakAfter(B3_UNDEFINED) {} inline StyleSheetTable::Style::Style() :
inline StyleSheetTable::Style::Style(const StyleSheetTable::Style &other) : TextStyle(other.TextStyle), PageBreakBefore(other.PageBreakBefore), PageBreakAfter(other.PageBreakAfter) {} PageBreakBefore(B3_UNDEFINED), PageBreakAfter(B3_UNDEFINED), WhiteSpace(WS_UNDEFINED), DisplayNone(false) {}
inline StyleSheetTable::Style::Style(const StyleSheetTable::AttributeMap &map) : PageBreakBefore(B3_UNDEFINED), PageBreakAfter(B3_UNDEFINED) { 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); 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; TextStyle = other.TextStyle;
PageBreakBefore = other.PageBreakBefore; PageBreakBefore = other.PageBreakBefore;
PageBreakAfter = other.PageBreakAfter; PageBreakAfter = other.PageBreakAfter;
WhiteSpace = other.WhiteSpace;
return *this; return *this;
} }
inline StyleSheetTable::Entry::Entry() : Specificity(0) {} 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::Entry(const SelectorList &selectors, int specificity, const StyleSheetTable::Style &style) :
inline StyleSheetTable::Entry &StyleSheetTable::Entry::operator = (const StyleSheetTable::Entry &other) { Selectors(selectors), Specificity(specificity), Style(style) {}
inline StyleSheetTable::Entry &StyleSheetTable::Entry::operator = (const Entry &other) {
Selectors = other.Selectors; Selectors = other.Selectors;
Style = other.Style; Style = other.Style;
Specificity = other.Specificity; Specificity = other.Specificity;

View file

@ -25,6 +25,7 @@
#include <ZLUnicodeUtil.h> #include <ZLUnicodeUtil.h>
#include <ZLStringUtil.h> #include <ZLStringUtil.h>
#include <ZLXMLNamespace.h> #include <ZLXMLNamespace.h>
#include <ZLTextStyleCollection.h>
#include "XHTMLReader.h" #include "XHTMLReader.h"
#include "../util/EntityFilesCollector.h" #include "../util/EntityFilesCollector.h"
@ -90,12 +91,6 @@ public:
void doAtEnd(XHTMLReader &reader); void doAtEnd(XHTMLReader &reader);
}; };
class XHTMLTagRestartParagraphAction : public XHTMLTagAction {
public:
void doAtStart(XHTMLReader &reader, const char **xmlattributes);
};
class XHTMLTagLineBreakAction : public XHTMLTagAction { class XHTMLTagLineBreakAction : public XHTMLTagAction {
public: public:
@ -160,7 +155,6 @@ public:
XHTMLTagControlAction(FBTextKind control); XHTMLTagControlAction(FBTextKind control);
void doAtStart(XHTMLReader &reader, const char **xmlattributes); void doAtStart(XHTMLReader &reader, const char **xmlattributes);
void doAtEnd(XHTMLReader &reader);
private: private:
FBTextKind myControl; FBTextKind myControl;
@ -186,10 +180,8 @@ public:
}; };
void XHTMLTagStyleAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) { void XHTMLTagStyleAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
static const std::string TYPE = "text/css";
const char *type = reader.attributeValue(xmlattributes, "type"); const char *type = reader.attributeValue(xmlattributes, "type");
if ((type == 0) || (TYPE != type)) { if (!type || strcmp(type, "text/css")) {
return; return;
} }
@ -207,15 +199,13 @@ void XHTMLTagStyleAction::doAtEnd(XHTMLReader &reader) {
} }
void XHTMLTagLinkAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) { void XHTMLTagLinkAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
static const std::string REL = "stylesheet";
const char *rel = reader.attributeValue(xmlattributes, "rel"); const char *rel = reader.attributeValue(xmlattributes, "rel");
if ((rel == 0) || (REL != rel)) { if (!rel || strcmp(rel, "stylesheet")) {
return; return;
} }
static const std::string TYPE = "text/css";
const char *type = reader.attributeValue(xmlattributes, "type"); const char *type = reader.attributeValue(xmlattributes, "type");
if ((type == 0) || (TYPE != type)) { if (!type || strcmp(type, "text/css")) {
return; return;
} }
@ -234,10 +224,7 @@ void XHTMLTagLinkAction::doAtStart(XHTMLReader &reader, const char **xmlattribut
} }
void XHTMLTagParagraphAction::doAtStart(XHTMLReader &reader, const char**) { void XHTMLTagParagraphAction::doAtStart(XHTMLReader &reader, const char**) {
if (!reader.myNewParagraphInProgress) { endParagraph(reader);
beginParagraph(reader);
reader.myNewParagraphInProgress = true;
}
} }
void XHTMLTagParagraphAction::doAtEnd(XHTMLReader &reader) { void XHTMLTagParagraphAction::doAtEnd(XHTMLReader &reader) {
@ -253,16 +240,15 @@ void XHTMLTagBodyAction::doAtEnd(XHTMLReader &reader) {
reader.myReadState = XHTMLReader::READ_NOTHING; 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) { 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**) { 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) { void XHTMLTagImageAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
// Ignore transparent images // Ignore transparent images
if (!reader.myOpacityStack.back()) { if (!reader.myParseStack.back().opacity) {
return; return;
} }
@ -302,19 +288,14 @@ void XHTMLTagImageAction::doAtStart(XHTMLReader &reader, const char **xmlattribu
return; return;
} }
bool flag = bookReader(reader).paragraphIsOpen();
if (flag) {
endParagraph(reader);
}
if ((strlen(fileName) > 2) && strncmp(fileName, "./", 2) == 0) { if ((strlen(fileName) > 2) && strncmp(fileName, "./", 2) == 0) {
fileName +=2; fileName +=2;
} }
bookReader(reader).addImageReference(fullfileName);
bookReader(reader).addImage(fullfileName, new ZLFileImage(ZLFile(fullfileName), 0)); reader.myParseStack.back().kind = IMAGE;
if (flag) { reader.haveContent();
beginParagraph(reader); reader.myModelReader.addImageReference(fullfileName);
} reader.myModelReader.addImage(fullfileName, new ZLFileImage(ZLFile(fullfileName), 0));
reader.myElementHasContents.back() = true;
} }
XHTMLTagSvgAction::XHTMLTagSvgAction(XHTMLSvgImageAttributeNamePredicate &predicate) : myPredicate(predicate) { XHTMLTagSvgAction::XHTMLTagSvgAction(XHTMLSvgImageAttributeNamePredicate &predicate) : myPredicate(predicate) {
@ -339,13 +320,7 @@ XHTMLTagControlAction::XHTMLTagControlAction(FBTextKind control) : myControl(con
} }
void XHTMLTagControlAction::doAtStart(XHTMLReader &reader, const char**) { void XHTMLTagControlAction::doAtStart(XHTMLReader &reader, const char**) {
bookReader(reader).pushKind(myControl); reader.myParseStack.back().kind = myControl;
bookReader(reader).addControl(myControl, true);
}
void XHTMLTagControlAction::doAtEnd(XHTMLReader &reader) {
bookReader(reader).addControl(myControl, false);
bookReader(reader).popKind();
} }
void XHTMLTagHyperlinkAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) { 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)) { if ((myControl == TITLE) && (bookReader(reader).model().bookTextModel()->paragraphsNumber() > 1)) {
bookReader(reader).insertEndOfSectionParagraph(); bookReader(reader).insertEndOfSectionParagraph();
} }
bookReader(reader).pushKind(myControl); reader.myParseStack.back().kind = myControl;
beginParagraph(reader);
} }
void XHTMLTagParagraphWithControlAction::doAtEnd(XHTMLReader &reader) { void XHTMLTagParagraphWithControlAction::doAtEnd(XHTMLReader &reader) {
endParagraph(reader); endParagraph(reader);
bookReader(reader).popKind();
} }
void XHTMLTagPreAction::doAtStart(XHTMLReader &reader, const char**) { void XHTMLTagPreAction::doAtStart(XHTMLReader &reader, const char**) {
reader.myPreformatted++; endParagraph(reader);
beginParagraph(reader); reader.myParseStack.back().kind = PREFORMATTED;
bookReader(reader).addControl(CODE, true); if (++reader.myPreformatted == 1) {
beginParagraph(reader);
}
} }
void XHTMLTagPreAction::doAtEnd(XHTMLReader &reader) { void XHTMLTagPreAction::doAtEnd(XHTMLReader &reader) {
bookReader(reader).addControl(CODE, false); if (!--reader.myPreformatted) {
endParagraph(reader); endParagraph(reader);
reader.myPreformatted--; }
} }
XHTMLTagAction *XHTMLReader::addAction(const std::string &tag, XHTMLTagAction *action) { XHTMLTagAction *XHTMLReader::addAction(const std::string &tag, XHTMLTagAction *action) {
@ -454,6 +429,7 @@ void XHTMLReader::fillTagTable() {
addAction("dt", new XHTMLTagParagraphWithControlAction(DEFINITION)); addAction("dt", new XHTMLTagParagraphWithControlAction(DEFINITION));
addAction("dfn", new XHTMLTagParagraphWithControlAction(DEFINITION)); addAction("dfn", new XHTMLTagParagraphWithControlAction(DEFINITION));
addAction("strike", new XHTMLTagControlAction(STRIKETHROUGH)); addAction("strike", new XHTMLTagControlAction(STRIKETHROUGH));
addAction("blockquote", new XHTMLTagParagraphWithControlAction(BLOCKQUOTE));
addAction("a", new XHTMLTagHyperlinkAction()); addAction("a", new XHTMLTagHyperlinkAction());
@ -467,7 +443,6 @@ void XHTMLReader::fillTagTable() {
//addAction("map", new XHTMLTagAction()); //addAction("map", new XHTMLTagAction());
//addAction("base", new XHTMLTagAction()); //addAction("base", new XHTMLTagAction());
//addAction("blockquote", new XHTMLTagAction());
addAction("br", new XHTMLTagLineBreakAction()); addAction("br", new XHTMLTagLineBreakAction());
//addAction("center", new XHTMLTagAction()); //addAction("center", new XHTMLTagAction());
addAction("div", new XHTMLTagParagraphAction()); addAction("div", new XHTMLTagParagraphAction());
@ -507,29 +482,19 @@ bool XHTMLReader::readFile(const ZLFile &file, const std::string &referenceName)
myReferenceDirName = referenceName.substr(0, index + 1); myReferenceDirName = referenceName.substr(0, index + 1);
myPreformatted = 0; myPreformatted = 0;
myNewParagraphInProgress = false;
myReadState = READ_NOTHING; myReadState = READ_NOTHING;
myElementStack.clear(); myElementStack.clear();
myStyleStack.clear(); myStyleStack.clear();
myElementHasContents.clear(); myParseStack.resize(1);
myOpacityStack.clear();
return readDocument(file); 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) { void XHTMLReader::startElementHandler(const char *tag, const char **attributes) {
static const std::string HASH = "#"; static const std::string HASH = "#";
const char *id = attributeValue(attributes, "id"); const char *id = attributeValue(attributes, "id");
const char *style = attributeValue(attributes, "style"); const char *inlineStyle = attributeValue(attributes, "style");
const char *klass = attributeValue(attributes, "class"); const char *klass = attributeValue(attributes, "class");
if (id != 0) { if (id != 0) {
myModelReader.addHyperlinkLabel(myReferenceName + HASH + id); 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); const std::string sTag = ZLUnicodeUtil::toLower(tag);
myElementStack.push_back(StyleSheetTable::Element(sTag, klass, id)); myElementStack.push_back(StyleSheetTable::Element(sTag, klass, id));
StyleSheetTable::Style cssStyle; myStyleStack.resize(myStyleStack.size() + 1);
myStyleSheetTable.applyStyles(myElementStack, cssStyle); StyleSheetTable::Style *style = &myStyleStack.back();
if (style != 0) { if (myStyleStack.size() > 1) {
cssStyle.apply(myStyleParser.parseString(style)); style->inherit(myStyleStack.at(myStyleStack.size()-2));
}
if (cssStyle.PageBreakBefore == B3_TRUE) {
myModelReader.insertEndOfSectionParagraph();
} }
int opacity = myOpacityStack.empty() ? 255 : myOpacityStack.back(); myStyleSheetTable.applyStyles(myElementStack, *style);
if (cssStyle.TextStyle.opacitySupported()) { if (inlineStyle) {
opacity *= cssStyle.TextStyle.opacity(); 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; opacity /= 255;
context.opacity = opacity;
} else {
context.opacity = prev.opacity;
} }
myStyleStack.push_back(cssStyle); // Don't collect empty styles
myElementHasContents.push_back(false); if (style->empty()) {
myOpacityStack.push_back((unsigned char)opacity); myStyleStack.resize(myStyleStack.size()-1);
style = NULL;
} else {
context.styleIndex = myStyleStack.size() - 1;
if (style->PageBreakBefore == B3_TRUE) {
addPageBreak();
}
}
XHTMLTagAction *action = ourTagActions[sTag]; XHTMLTagAction *action = ourTagActions[sTag];
if (action != 0) { if (action != 0) {
action->doAtStart(*this, attributes); 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) { 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)]; XHTMLTagAction *action = ourTagActions[ZLUnicodeUtil::toLower(tag)];
if (action != 0) { if (action != 0) {
action->doAtEnd(*this); action->doAtEnd(*this);
myNewParagraphInProgress = false;
} }
const bool haveContents = myElementHasContents.back(); if (pageBreak) {
if (myStyleStack.back().PageBreakAfter == B3_TRUE && haveContents) { addPageBreak();
myModelReader.insertEndOfSectionParagraph();
} }
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(); myElementStack.pop_back();
myStyleStack.pop_back(); myParseStack.pop_back();
myElementHasContents.pop_back(); }
myOpacityStack.pop_back();
if (!myElementHasContents.empty() && haveContents) { void XHTMLReader::addPageBreak() {
myElementHasContents.back() = true; 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() { void XHTMLReader::beginParagraph() {
myCurrentParagraphIsEmpty = true; if (!myModelReader.paragraphIsOpen()) {
myModelReader.beginParagraph(); myModelReader.beginParagraph();
if (!myStyleStack.empty()) { for (std::vector<ParseContext>::iterator it = myParseStack.begin(); it != myParseStack.end(); ++it) {
for (StyleSheetTable::StyleList::const_iterator it = myStyleStack.begin(); it != myStyleStack.end(); ++it) { applyStyles(*it);
myModelReader.addControl(((*it).TextStyle));
} }
} }
} }
void XHTMLReader::endParagraph() { 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) { void XHTMLReader::characterDataHandler(const char *text, size_t len) {
@ -613,34 +751,49 @@ void XHTMLReader::characterDataHandler(const char *text, size_t len) {
} }
break; break;
case READ_BODY: case READ_BODY:
if (myOpacityStack.back()) { if (myParseStack.back().opacity && !myStyleStack.empty() && !myStyleStack.back().DisplayNone) {
if (myPreformatted) { const StyleSheetTable::WhiteSpaceValue whiteSpace = myStyleStack.back().WhiteSpace;
if ((*text == '\r') || (*text == '\n')) { if (myPreformatted || whiteSpace == StyleSheetTable::WS_PRE || whiteSpace == StyleSheetTable::WS_PRE_WRAP) {
myModelReader.addLineBreak();
}
size_t spaceCounter = 0; size_t spaceCounter = 0;
while ((spaceCounter < len) && isspace((unsigned char)*(text + spaceCounter))) { while (len > 0 && isspace(*text)) {
++spaceCounter; if (*text == '\n') {
} haveContent();
myModelReader.addFixedHSpace(spaceCounter); if (spaceCounter) {
text += spaceCounter; myModelReader.addFixedHSpace(spaceCounter);
len -= spaceCounter; spaceCounter = 0;
} else if ((myNewParagraphInProgress) || !myModelReader.paragraphIsOpen()) { }
while (isspace((unsigned char)*text)) { myModelReader.addLineBreak();
} else if (*text != '\r') {
spaceCounter++;
}
++text; ++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; break;
} }
++text;
--len;
} }
} }
if (len > 0) { if (len > 0) {
myCurrentParagraphIsEmpty = false; haveContent();
myElementHasContents.back() = true;
if (!myModelReader.paragraphIsOpen()) {
myModelReader.beginParagraph();
}
myModelReader.addData(std::string(text, len)); myModelReader.addData(std::string(text, len));
myNewParagraphInProgress = false;
} }
} }
break; break;

View file

@ -25,11 +25,13 @@
#include <vector> #include <vector>
#include <ZLXMLReader.h> #include <ZLXMLReader.h>
#include <ZLTextParagraph.h>
#include "../css/StyleSheetTable.h" #include "../css/StyleSheetTable.h"
#include "../css/StyleSheetParser.h" #include "../css/StyleSheetParser.h"
class ZLFile; class ZLFile;
class ZLTextStyleDecoration;
class BookReader; class BookReader;
class XHTMLReader; class XHTMLReader;
@ -63,7 +65,18 @@ public:
bool readFile(const ZLFile &file, const std::string &referenceName); bool readFile(const ZLFile &file, const std::string &referenceName);
private: 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 endElementHandler(const char *tag);
void characterDataHandler(const char *text, size_t len); void characterDataHandler(const char *text, size_t len);
@ -71,9 +84,16 @@ private:
bool processNamespaces() const; bool processNamespaces() const;
void beginParagraph(); void haveContent();
void beginParagraph();
void endParagraph(); 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: private:
BookReader &myModelReader; BookReader &myModelReader;
@ -81,13 +101,11 @@ private:
std::string myReferenceName; std::string myReferenceName;
std::string myReferenceDirName; std::string myReferenceDirName;
int myPreformatted; int myPreformatted;
bool myNewParagraphInProgress;
StyleSheetTable myStyleSheetTable; StyleSheetTable myStyleSheetTable;
StyleSheetTable::ElementList myElementStack; StyleSheetTable::ElementList myElementStack;
StyleSheetTable::StyleList myStyleStack; StyleSheetTable::StyleList myStyleStack;
std::vector<unsigned char> myOpacityStack; std::vector<ParseContext> myParseStack;
std::vector<bool> myElementHasContents; std::vector<ZLTextStyleEntry> myBottomMargins;
bool myCurrentParagraphIsEmpty;
StyleSheetSingleStyleParser myStyleParser; StyleSheetSingleStyleParser myStyleParser;
shared_ptr<StyleSheetTableParser> myTableParser; shared_ptr<StyleSheetTableParser> myTableParser;
enum { enum {
@ -97,13 +115,13 @@ private:
} myReadState; } myReadState;
friend class XHTMLTagAction; friend class XHTMLTagAction;
friend class XHTMLTagStyleAction; friend class XHTMLTagStyleAction;
friend class XHTMLTagLinkAction; friend class XHTMLTagLinkAction;
friend class XHTMLTagHyperlinkAction; friend class XHTMLTagHyperlinkAction;
friend class XHTMLTagPreAction; friend class XHTMLTagPreAction;
friend class XHTMLTagParagraphAction; friend class XHTMLTagControlAction;
friend class XHTMLTagBodyAction; friend class XHTMLTagParagraphWithControlAction;
friend class XHTMLTagRestartParagraphAction; friend class XHTMLTagBodyAction;
friend class XHTMLTagImageAction; friend class XHTMLTagImageAction;
}; };

View file

@ -45,6 +45,14 @@ bool ZLStringUtil::stringStartsWith(const std::string &str, const std::string &s
#endif #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) { void ZLStringUtil::appendNumber(std::string &str, unsigned int n) {
int len; int len;
if (n > 0) { 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) { // Returns true if there's anything left
size_t counter = 0; bool ZLStringUtil::stripWhiteSpaces(std::string &str) {
size_t length = str.length(); const size_t old_length = str.length();
while ((counter < length) && isspace((unsigned char)str[counter])) { if (old_length > 0) {
counter++; size_t end = old_length;
} while ((end > 0) && isspace((unsigned char)str[end - 1])) {
str.erase(0, counter); end--;
length -= counter; }
if (end < old_length) {
str.erase(end, old_length - end);
}
size_t r_counter = length; size_t start = 0;
while ((r_counter > 0) && isspace((unsigned char)str[r_counter - 1])) { while ((start < end) && isspace((unsigned char)str[start])) {
r_counter--; 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) { 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; 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) { std::string ZLStringUtil::printf(const std::string &format, const std::string &arg0) {
int index = format.find("%s"); int index = format.find("%s");
if (index == -1) { if (index == -1) {

View file

@ -31,11 +31,15 @@ private:
public: public:
static bool stringStartsWith(const std::string &str, const std::string &start); static bool stringStartsWith(const std::string &str, const std::string &start);
static bool stringEndsWith(const std::string &str, const std::string &end); 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 appendNumber(std::string &str, unsigned int n);
static void append(std::string &str, const std::vector<std::string> &buffer); 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 char *str, const char* delim);
static std::vector<std::string> splitString(const std::string &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); 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) { inline std::vector<std::string> ZLStringUtil::splitString(const std::string &str, const char* delim) {
return ZLStringUtil::splitString(str.c_str(), 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__ */ #endif /* __ZLSTRINGUTIL_H__ */

View file

@ -18,6 +18,10 @@
*/ */
#include "ZLPaintContext.h" #include "ZLPaintContext.h"
#include "ZLStringUtil.h"
#include <algorithm>
#include <strings.h>
ZLPaintContext::ZLPaintContext() { ZLPaintContext::ZLPaintContext() {
} }
@ -25,9 +29,29 @@ ZLPaintContext::ZLPaintContext() {
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 { const std::vector<std::string> &ZLPaintContext::fontFamilies() const {
if (myFamilies.empty()) { if (myFamilies.empty()) {
fillFamiliesList(myFamilies); fillFamiliesList(myFamilies);
std::sort(myFamilies.begin(), myFamilies.end(), ZLStringUtil::caseInsensitiveSort);
} }
return myFamilies; return myFamilies;
} }

View file

@ -76,6 +76,7 @@ public:
virtual void fillRectangle(int x0, int y0, int x1, int y1) = 0; virtual void fillRectangle(int x0, int y0, int x1, int y1) = 0;
virtual void drawFilledCircle(int x, int y, int r) = 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; const std::vector<std::string> &fontFamilies() const;
virtual const std::string realFontFamilyName(std::string &fontFamily) const = 0; virtual const std::string realFontFamilyName(std::string &fontFamily) const = 0;

View file

@ -33,7 +33,7 @@
ZLTextArea::Style::Style(const ZLTextArea &area, shared_ptr<ZLTextStyle> style) : myArea(area) { ZLTextArea::Style::Style(const ZLTextArea &area, shared_ptr<ZLTextStyle> style) : myArea(area) {
myTextStyle = style; myTextStyle = style;
myWordHeight = -1; 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; myBidiLevel = myArea.isRtl() ? 1 : 0;
} }
@ -42,7 +42,7 @@ void ZLTextArea::Style::setTextStyle(shared_ptr<ZLTextStyle> style, unsigned cha
myTextStyle = style; myTextStyle = style;
myWordHeight = -1; 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; myBidiLevel = bidiLevel;
} }
@ -108,6 +108,7 @@ int ZLTextArea::Style::elementWidth(const ZLTextElement &element, unsigned int c
case ZLTextElement::CONTROL_ELEMENT: case ZLTextElement::CONTROL_ELEMENT:
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT: case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:
case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT: case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT:
case ZLTextElement::EMPTY_ELEMENT:
return 0; return 0;
case ZLTextElement::FIXED_HSPACE_ELEMENT: case ZLTextElement::FIXED_HSPACE_ELEMENT:
return myArea.context().spaceWidth() * ((const ZLTextFixedHSpaceElement&)element).length(); return myArea.context().spaceWidth() * ((const ZLTextFixedHSpaceElement&)element).length();
@ -119,6 +120,8 @@ int ZLTextArea::Style::elementHeight(const ZLTextElement &element, const ZLTextS
switch (element.kind()) { switch (element.kind()) {
case ZLTextElement::NB_HSPACE_ELEMENT: case ZLTextElement::NB_HSPACE_ELEMENT:
case ZLTextElement::WORD_ELEMENT: case ZLTextElement::WORD_ELEMENT:
case ZLTextElement::EMPTY_LINE_ELEMENT:
case ZLTextElement::LINE_BREAK_ELEMENT:
if (myWordHeight == -1) { if (myWordHeight == -1) {
myWordHeight = myArea.context().stringHeight() * textStyle()->lineSpacePercent() / 100 + textStyle()->verticalShift(); 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); return - textStyle()->spaceAfter(metrics);
case ZLTextElement::AFTER_PARAGRAPH_ELEMENT: case ZLTextElement::AFTER_PARAGRAPH_ELEMENT:
return - textStyle()->spaceBefore(metrics); return - textStyle()->spaceBefore(metrics);
case ZLTextElement::EMPTY_LINE_ELEMENT:
case ZLTextElement::LINE_BREAK_ELEMENT:
return myArea.context().stringHeight();
case ZLTextElement::INDENT_ELEMENT: case ZLTextElement::INDENT_ELEMENT:
case ZLTextElement::HSPACE_ELEMENT: case ZLTextElement::HSPACE_ELEMENT:
case ZLTextElement::FORCED_CONTROL_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::FIXED_HSPACE_ELEMENT:
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT: case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:
case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT: case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT:
case ZLTextElement::EMPTY_ELEMENT:
return 0; return 0;
} }
return 0; return 0;

View file

@ -167,6 +167,7 @@ void ZLTextArea::prepareTextLine(Style &style, const ZLTextLineInfo &info, int y
case ZLTextElement::EMPTY_LINE_ELEMENT: case ZLTextElement::EMPTY_LINE_ELEMENT:
case ZLTextElement::LINE_BREAK_ELEMENT: case ZLTextElement::LINE_BREAK_ELEMENT:
case ZLTextElement::FIXED_HSPACE_ELEMENT: case ZLTextElement::FIXED_HSPACE_ELEMENT:
case ZLTextElement::EMPTY_ELEMENT:
break; break;
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT: case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:
//context().setColor(ZLColor(0, 255, 0)); //context().setColor(ZLColor(0, 255, 0));

View file

@ -170,6 +170,7 @@ ZLTextLineInfoPtr ZLTextArea::processTextLine(Style &style, const ZLTextWordCurs
newInfo.Width += lastSpaceWidth; newInfo.Width += lastSpaceWidth;
} }
break; break;
case ZLTextElement::EMPTY_ELEMENT:
case ZLTextElement::EMPTY_LINE_ELEMENT: case ZLTextElement::EMPTY_LINE_ELEMENT:
newInfo.IsVisible = true; newInfo.IsVisible = true;
break; break;
@ -178,6 +179,7 @@ ZLTextLineInfoPtr ZLTextArea::processTextLine(Style &style, const ZLTextWordCurs
} }
if (elementKind == ZLTextElement::LINE_BREAK_ELEMENT) { if (elementKind == ZLTextElement::LINE_BREAK_ELEMENT) {
newInfo.IsVisible = true;
newInfo.End.nextWord(); newInfo.End.nextWord();
newInfo.setTo(info); newInfo.setTo(info);
break; break;

View file

@ -48,6 +48,7 @@ public:
LINE_BREAK_ELEMENT, LINE_BREAK_ELEMENT,
START_REVERSED_SEQUENCE_ELEMENT, START_REVERSED_SEQUENCE_ELEMENT,
END_REVERSED_SEQUENCE_ELEMENT, END_REVERSED_SEQUENCE_ELEMENT,
EMPTY_ELEMENT
}; };
virtual Kind kind() const = 0; virtual Kind kind() const = 0;

View file

@ -111,6 +111,9 @@ void ZLTextParagraphCursor::Builder::fill() {
case ZLTextParagraphEntry::LINE_BREAK_ENTRY: case ZLTextParagraphEntry::LINE_BREAK_ENTRY:
myElements.push_back(myTextElementPool.LineBreakElement); myElements.push_back(myTextElementPool.LineBreakElement);
break; break;
case ZLTextParagraphEntry::EMPTY_ENTRY:
myElements.push_back(myTextElementPool.EmptyElement);
break;
} }
} }

View file

@ -49,6 +49,7 @@ ZLTextElementVector::~ZLTextElementVector() {
case ZLTextElement::LINE_BREAK_ELEMENT: case ZLTextElement::LINE_BREAK_ELEMENT:
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT: case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:
case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT: case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT:
case ZLTextElement::EMPTY_ELEMENT:
break; break;
} }
} }
@ -63,6 +64,7 @@ ZLTextElementPool::ZLTextElementPool() {
LineBreakElement = new ZLTextSpecialElement(ZLTextElement::LINE_BREAK_ELEMENT); LineBreakElement = new ZLTextSpecialElement(ZLTextElement::LINE_BREAK_ELEMENT);
StartReversedSequenceElement = new ZLTextSpecialElement(ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT); StartReversedSequenceElement = new ZLTextSpecialElement(ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT);
EndReversedSequenceElement = new ZLTextSpecialElement(ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT); EndReversedSequenceElement = new ZLTextSpecialElement(ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT);
EmptyElement = new ZLTextSpecialElement(ZLTextElement::EMPTY_ELEMENT);
} }
ZLTextElementPool::~ZLTextElementPool() { ZLTextElementPool::~ZLTextElementPool() {
@ -74,6 +76,7 @@ ZLTextElementPool::~ZLTextElementPool() {
delete LineBreakElement; delete LineBreakElement;
delete StartReversedSequenceElement; delete StartReversedSequenceElement;
delete EndReversedSequenceElement; delete EndReversedSequenceElement;
delete EmptyElement;
} }
ZLTextParagraphCursorPtr ZLTextParagraphCursorCache::cursor(const ZLTextModel &model, size_t index) { ZLTextParagraphCursorPtr ZLTextParagraphCursorCache::cursor(const ZLTextModel &model, size_t index) {

View file

@ -65,6 +65,7 @@ public:
ZLTextElement *LineBreakElement; ZLTextElement *LineBreakElement;
ZLTextElement *StartReversedSequenceElement; ZLTextElement *StartReversedSequenceElement;
ZLTextElement *EndReversedSequenceElement; ZLTextElement *EndReversedSequenceElement;
ZLTextElement *EmptyElement;
ZLTextWord *getWord(const char *data, unsigned short length, size_t paragraphOffset, unsigned char bidiLevel); ZLTextWord *getWord(const char *data, unsigned short length, size_t paragraphOffset, unsigned char bidiLevel);
void storeWord(ZLTextWord *word); void storeWord(ZLTextWord *word);

View file

@ -215,43 +215,53 @@ void ZLTextModel::addControl(ZLTextKind textKind, bool isStart) {
void ZLTextModel::addControl(const ZLTextStyleEntry &entry) { void ZLTextModel::addControl(const ZLTextStyleEntry &entry) {
int len = sizeof(int) + 2; 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) { for (int i = 0; i < ZLTextStyleEntry::NUMBER_OF_LENGTHS; ++i) {
if (entry.myMask & (1 << i)) { if (mask & (1 << i)) {
len += (sizeof(short) + 1); len += (sizeof(short) + 1);
} }
} }
} }
if (entry.opacitySupported()) ++len; if (mask & ZLTextStyleEntry::SUPPORT_OPACITY) ++len;
if (entry.alignmentTypeSupported()) ++len; if (mask & ZLTextStyleEntry::SUPPORT_ALIGNMENT_TYPE) ++len;
if (entry.supportedFontModifier()) ++len; if (entry.supportedFontModifier()) ++len;
if (entry.fontSizeSupported()) ++len; if (mask & ZLTextStyleEntry::SUPPORT_FONT_SIZE) ++len;
if (entry.fontFamilySupported()) { if (mask & ZLTextStyleEntry::SUPPORT_FONT_FAMILIES) {
len += entry.fontFamily().length() + 1; 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); myLastEntryStart = myAllocator.allocate(len);
char *address = myLastEntryStart; char *address = myLastEntryStart;
*address++ = ZLTextParagraphEntry::STYLE_ENTRY; *address++ = ZLTextParagraphEntry::STYLE_ENTRY;
*address++ = entry.mySupportedFontModifier; *address++ = entry.mySupportedFontModifier;
memcpy(address, &entry.myMask, sizeof(int)); memcpy(address, &mask, sizeof(int));
address += 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) { for (int i = 0; i < ZLTextStyleEntry::NUMBER_OF_LENGTHS; ++i) {
if (entry.myMask & (1 << i)) { if (mask & (1 << i)) {
*address++ = entry.myLengths[i].Unit; *address++ = entry.myLengths[i].Unit;
memcpy(address, &entry.myLengths[i].Size, sizeof(short)); memcpy(address, &entry.myLengths[i].Size, sizeof(short));
address += sizeof(short); address += sizeof(short);
} }
} }
} }
if (entry.opacitySupported()) *address++ = entry.myOpacity; if (mask & ZLTextStyleEntry::SUPPORT_OPACITY) *address++ = entry.myOpacity;
if (entry.alignmentTypeSupported()) *address++ = entry.myAlignmentType; if (mask & ZLTextStyleEntry::SUPPORT_ALIGNMENT_TYPE) *address++ = entry.myAlignmentType;
if (entry.supportedFontModifier()) *address++ = entry.myFontModifier; if (entry.supportedFontModifier()) *address++ = entry.myFontModifier;
if (entry.fontSizeSupported()) *address++ = entry.myFontSizeMag; if (mask & ZLTextStyleEntry::SUPPORT_FONT_SIZE) *address++ = entry.myFontSizeMag;
if (entry.fontFamilySupported()) { if (mask & ZLTextStyleEntry::SUPPORT_FONT_FAMILIES) {
memcpy(address, entry.fontFamily().data(), entry.fontFamily().length()); const unsigned char n = entry.myFontFamilies.size();
address += entry.fontFamily().length(); *address++ = n;
*address++ = '\0'; 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); myParagraphs.back()->addEntry(myLastEntryStart);
} }
@ -289,3 +299,9 @@ void ZLTextModel::addLineBreak() {
*myLastEntryStart = ZLTextParagraphEntry::LINE_BREAK_ENTRY; *myLastEntryStart = ZLTextParagraphEntry::LINE_BREAK_ENTRY;
myParagraphs.back()->addEntry(myLastEntryStart); myParagraphs.back()->addEntry(myLastEntryStart);
} }
void ZLTextModel::addEmpty() {
myLastEntryStart = myAllocator.allocate(1);
*myLastEntryStart = ZLTextParagraphEntry::EMPTY_ENTRY;
myParagraphs.back()->addEntry(myLastEntryStart);
}

View file

@ -73,6 +73,7 @@ public:
void addFixedHSpace(unsigned char length); void addFixedHSpace(unsigned char length);
void addBidiReset(); void addBidiReset();
void addLineBreak(); void addLineBreak();
void addEmpty();
protected: protected:
void addParagraphInternal(ZLTextParagraph *paragraph); void addParagraphInternal(ZLTextParagraph *paragraph);

View file

@ -22,6 +22,7 @@
#include <algorithm> #include <algorithm>
#include <ZLUnicodeUtil.h> #include <ZLUnicodeUtil.h>
#include <ZLStringUtil.h>
#include <ZLImage.h> #include <ZLImage.h>
#include "ZLTextParagraph.h" #include "ZLTextParagraph.h"
@ -34,26 +35,46 @@ size_t ZLTextEntry::dataLength() const {
return len; return len;
} }
short ZLTextStyleEntry::length(Length name, const Metrics &metrics) const { int ZLTextStyleEntry::hlength(int size, SizeUnit unit, const Metrics &metrics)
switch (myLengths[name].Unit) { {
switch (unit) {
default: default:
case SIZE_UNIT_PIXEL: case SIZE_UNIT_PIXEL:
return myLengths[name].Size; return size;
case SIZE_UNIT_EM_100: case SIZE_UNIT_EM_100:
return (myLengths[name].Size * metrics.FontSize + 50) / 100; return (size * metrics.FontSize + 50) / 100;
case SIZE_UNIT_EX_100: case SIZE_UNIT_EX_100:
return (myLengths[name].Size * metrics.FontXHeight + 50) / 100; return (size * metrics.FontXHeight + 50) / 100;
case SIZE_UNIT_PERCENT: case SIZE_UNIT_PERCENT:
switch (name) { return (size * metrics.FullWidth + 50) / 100;
default: }
case LENGTH_LEFT_INDENT: }
case LENGTH_RIGHT_INDENT:
case LENGTH_FIRST_LINE_INDENT_DELTA: int ZLTextStyleEntry::vlength(int size, SizeUnit unit, const Metrics &metrics)
return (myLengths[name].Size * metrics.FullWidth + 50) / 100; {
case LENGTH_SPACE_BEFORE: switch (unit) {
case LENGTH_SPACE_AFTER: default:
return (myLengths[name].Size * metrics.FullHeight + 50) / 100; 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()) { if (fontSizeSupported()) {
myFontSizeMag = (signed char)*address++; myFontSizeMag = (signed char)*address++;
} }
if (fontFamilySupported()) { if (fontFamiliesSupported()) {
myFontFamily = address; 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() { void ZLTextStyleEntry::reset() {
mySupportedFontModifier = 0; mySupportedFontModifier = 0;
myMask = 0; myMask = 0;
myFontFamily.clear(); myFontFamilies.clear();
} }
void ZLTextStyleEntry::apply(const ZLTextStyleEntry &entry) { void ZLTextStyleEntry::apply(const ZLTextStyleEntry &other) {
for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) { if (other.myMask & ((1 << NUMBER_OF_LENGTHS) - 1)) {
if (entry.lengthSupported((Length)i)) { for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) {
myLengths[i] = entry.myLengths[i]; if (other.myMask & (1 << i)) {
myMask |= 1 << i; myLengths[i] = other.myLengths[i];
myMask |= (1 << i);
}
} }
} }
if (entry.opacitySupported()) { if (other.myMask & SUPPORT_OPACITY) {
setOpacity(entry.opacity()); setOpacity(other.myOpacity);
} }
if (entry.alignmentTypeSupported()) { if (other.myMask & SUPPORT_ALIGNMENT_TYPE) {
setAlignmentType(entry.alignmentType()); setAlignmentType(other.myAlignmentType);
} }
if (entry.mySupportedFontModifier) { if (other.mySupportedFontModifier) {
myFontModifier &= ~entry.mySupportedFontModifier; myFontModifier &= ~other.mySupportedFontModifier;
myFontModifier |= (entry.myFontModifier & entry.mySupportedFontModifier); myFontModifier |= (other.myFontModifier & other.mySupportedFontModifier);
mySupportedFontModifier |= entry.mySupportedFontModifier; mySupportedFontModifier |= other.mySupportedFontModifier;
} }
if (entry.fontSizeSupported()) { if (other.myMask & SUPPORT_FONT_SIZE) {
setFontSizeMag(entry.fontSizeMag()); setFontSizeMag(other.myFontSizeMag);
} }
if (entry.fontFamilySupported()) { if (other.myMask & SUPPORT_FONT_FAMILIES) {
setFontFamily(entry.fontFamily()); 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 { const shared_ptr<ZLTextParagraphEntry> ZLTextParagraph::Iterator::entry() const {
if (myEntry.isNull()) { if (myEntry.isNull()) {
switch (*myPointer) { switch (*myPointer) {
@ -205,11 +328,11 @@ void ZLTextParagraph::Iterator::next() {
if (mask & ZLTextStyleEntry::SUPPORT_ALIGNMENT_TYPE) ++myPointer; if (mask & ZLTextStyleEntry::SUPPORT_ALIGNMENT_TYPE) ++myPointer;
if (supportedFontModifier) ++myPointer; if (supportedFontModifier) ++myPointer;
if (mask & ZLTextStyleEntry::SUPPORT_FONT_SIZE) ++myPointer; if (mask & ZLTextStyleEntry::SUPPORT_FONT_SIZE) ++myPointer;
if (mask & ZLTextStyleEntry::SUPPORT_FONT_FAMILY) { if (mask & ZLTextStyleEntry::SUPPORT_FONT_FAMILIES) {
while (*myPointer != '\0') { unsigned char n = *myPointer++;
++myPointer; for (unsigned int i = 0; i < n; ++i) {
while (*myPointer++);
} }
++myPointer;
} }
break; break;
} }
@ -218,6 +341,7 @@ void ZLTextParagraph::Iterator::next() {
break; break;
case ZLTextParagraphEntry::RESET_BIDI_ENTRY: case ZLTextParagraphEntry::RESET_BIDI_ENTRY:
case ZLTextParagraphEntry::LINE_BREAK_ENTRY: case ZLTextParagraphEntry::LINE_BREAK_ENTRY:
case ZLTextParagraphEntry::EMPTY_ENTRY:
++myPointer; ++myPointer;
break; break;
} }

View file

@ -44,7 +44,8 @@ public:
STYLE_ENTRY = 5, STYLE_ENTRY = 5,
FIXED_HSPACE_ENTRY = 6, FIXED_HSPACE_ENTRY = 6,
RESET_BIDI_ENTRY = 7, RESET_BIDI_ENTRY = 7,
LINE_BREAK_ENTRY = 8 LINE_BREAK_ENTRY = 8,
EMPTY_ENTRY = 9
}; };
protected: protected:
@ -99,15 +100,23 @@ public:
~ZLTextStyleEntry(); ~ZLTextStyleEntry();
void reset(); 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); ZLTextStyleEntry &operator = (const ZLTextStyleEntry &other);
bool isEmpty() const; bool isEmpty() const;
bool lengthSupported(Length name) const; bool lengthSupported(Length name) const;
short length(Length name, const Metrics &metrics) const; short length(Length name, const Metrics &metrics) const;
short length(Length name, SizeUnit &unit) const;
void setLength(Length name, short length, SizeUnit unit); 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; bool opacitySupported() const;
unsigned char opacity() const; unsigned char opacity() const;
void setOpacity(unsigned char opacity); void setOpacity(unsigned char opacity);
@ -124,14 +133,14 @@ public:
signed char fontSizeMag() const; signed char fontSizeMag() const;
void setFontSizeMag(signed char fontSizeMag); void setFontSizeMag(signed char fontSizeMag);
bool fontFamilySupported() const; bool fontFamiliesSupported() const;
const std::string &fontFamily() const; const std::vector<std::string> &fontFamilies() const;
void setFontFamily(const std::string &fontFamily); void setFontFamilies(const std::vector<std::string> &fontFamilies);
enum { enum {
SUPPORT_ALIGNMENT_TYPE = 1 << NUMBER_OF_LENGTHS, SUPPORT_ALIGNMENT_TYPE = 1 << NUMBER_OF_LENGTHS,
SUPPORT_FONT_SIZE = 1 << (NUMBER_OF_LENGTHS + 1), 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) SUPPORT_OPACITY = 1 << (NUMBER_OF_LENGTHS + 3)
}; };
@ -145,7 +154,7 @@ private:
unsigned char mySupportedFontModifier; unsigned char mySupportedFontModifier;
unsigned char myFontModifier; unsigned char myFontModifier;
signed char myFontSizeMag; signed char myFontSizeMag;
std::string myFontFamily; std::vector<std::string> myFontFamilies;
friend class ZLTextModel; 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 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::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 ZLTextStyleEntry &ZLTextStyleEntry::operator = (const ZLTextStyleEntry &other) { reset(); apply(other); return *this; }
inline bool ZLTextStyleEntry::lengthSupported(Length name) const { return (myMask & (1 << name)) != 0; } 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) { inline void ZLTextStyleEntry::setLength(Length name, short length, SizeUnit unit) {
myLengths[name].Size = length; myLengths[name].Size = length;
myLengths[name].Unit = unit; 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 signed char ZLTextStyleEntry::fontSizeMag() const { return myFontSizeMag; }
inline void ZLTextStyleEntry::setFontSizeMag(signed char fontSizeMag) { myFontSizeMag = fontSizeMag; myMask |= SUPPORT_FONT_SIZE; } 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 bool ZLTextStyleEntry::fontFamiliesSupported() const { return (myMask & SUPPORT_FONT_FAMILIES) == SUPPORT_FONT_FAMILIES; }
inline const std::string &ZLTextStyleEntry::fontFamily() const { return myFontFamily; } inline const std::vector<std::string> &ZLTextStyleEntry::fontFamilies() const { return myFontFamilies; }
inline void ZLTextStyleEntry::setFontFamily(const std::string &fontFamily) { myFontFamily = fontFamily; myMask |= SUPPORT_FONT_FAMILY; } 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(ZLTextKind kind, bool isStart) : myKind(kind), myStart(isStart) {}
inline ZLTextControlEntry::~ZLTextControlEntry() {} inline ZLTextControlEntry::~ZLTextControlEntry() {}

View file

@ -30,7 +30,7 @@ ZLTextStyleDecoration::ZLTextStyleDecoration(const std::string &name, int fontSi
ItalicOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":italic", italic), ItalicOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":italic", italic),
VerticalShiftOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":vShift", verticalShift), VerticalShiftOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":vShift", verticalShift),
AllowHyphenationsOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":allowHyphenations", allowHyphenations), 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), 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), SpaceAfterOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":spaceAfter", -10, 100, spaceAfter),
LineStartIndentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":leftIndent", -300, 300, lineStartIndent), LineStartIndentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":leftIndent", -300, 300, lineStartIndent),
LineEndIndentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":rightIndent", -300, 300, lineEndIndent), 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), AlignmentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":alignment", alignment),
LineSpaceOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":lineSpace", lineSpace), 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 { shared_ptr<ZLTextStyle> ZLTextStyleDecoration::createDecoratedStyle(const shared_ptr<ZLTextStyle> base) const {
return new ZLTextPartialDecoratedStyle(base, *this); return new ZLTextPartialDecoratedStyle(base, *this);
} }
const ZLTextFullStyleDecoration *ZLTextFullStyleDecoration::fullDecoration() const {
return this;
}
shared_ptr<ZLTextStyle> ZLTextFullStyleDecoration::createDecoratedStyle(const shared_ptr<ZLTextStyle> base) const { shared_ptr<ZLTextStyle> ZLTextFullStyleDecoration::createDecoratedStyle(const shared_ptr<ZLTextStyle> base) const {
return new ZLTextFullDecoratedStyle(base, *this); 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(); 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 { int ZLTextPartialDecoratedStyle::fontSize() const {
@ -74,16 +125,26 @@ bool ZLTextPartialDecoratedStyle::italic() const {
bool ZLTextPartialDecoratedStyle::allowHyphenations() const { bool ZLTextPartialDecoratedStyle::allowHyphenations() const {
ZLBoolean3 a = myDecoration.AllowHyphenationsOption.value(); ZLBoolean3 a = myDecoration.AllowHyphenationsOption.value();
return (a == B3_UNDEFINED) ? base()->allowHyphenations() : (a == B3_TRUE); 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 { 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::vector<std::string> &ZLTextFullDecoratedStyle::fontFamilies() const {
const std::string &family = myDecoration.FontFamilyOption.value(); return (!myFontFamilies.empty()) ? myFontFamilies : base()->fontFamilies();
return (!family.empty()) ? family : base()->fontFamily();
} }
int ZLTextFullDecoratedStyle::fontSize() const { int ZLTextFullDecoratedStyle::fontSize() const {
@ -108,7 +169,6 @@ ZLTextAlignmentType ZLTextFullDecoratedStyle::alignment() const {
bool ZLTextFullDecoratedStyle::allowHyphenations() const { bool ZLTextFullDecoratedStyle::allowHyphenations() const {
ZLBoolean3 a = myDecoration.AllowHyphenationsOption.value(); ZLBoolean3 a = myDecoration.AllowHyphenationsOption.value();
return (a == B3_UNDEFINED) ? base()->allowHyphenations() : (a == B3_TRUE); return (a == B3_UNDEFINED) ? base()->allowHyphenations() : (a == B3_TRUE);
return true;
} }
const std::string &ZLTextPartialDecoratedStyle::colorStyle() const { 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 { short ZLTextForcedStyle::spaceBefore(const ZLTextStyleEntry::Metrics &metrics) const {
if (myEntry.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE)) { return
const short baseSpace = base()->spaceBefore(metrics); myEntry.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE) ?
ZLTextStyleEntry::Metrics adjusted(metrics); myEntry.length(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, metrics) :
adjusted.FullHeight -= baseSpace + base()->spaceAfter(metrics); base()->spaceBefore(metrics);
return baseSpace + myEntry.length(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, adjusted);
} else {
return base()->spaceBefore(metrics);
}
} }
short ZLTextForcedStyle::spaceAfter(const ZLTextStyleEntry::Metrics &metrics) const { short ZLTextForcedStyle::spaceAfter(const ZLTextStyleEntry::Metrics &metrics) const {
if (myEntry.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_AFTER)) { return
const short baseSpace = base()->spaceBefore(metrics); myEntry.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_AFTER) ?
ZLTextStyleEntry::Metrics adjusted(metrics); myEntry.length(ZLTextStyleEntry::LENGTH_SPACE_AFTER, metrics) :
adjusted.FullHeight -= baseSpace + base()->spaceBefore(metrics); base()->spaceAfter(metrics);
return baseSpace + myEntry.length(ZLTextStyleEntry::LENGTH_SPACE_AFTER, adjusted);
} else {
return base()->spaceAfter(metrics);
}
} }
short ZLTextForcedStyle::firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const { 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() && return (!ZLTextStyleCollection::Instance().OverrideSpecifiedFontsOption.value() &&
myEntry.fontFamilySupported()) ? myEntry.fontFamiliesSupported()) ?
myEntry.fontFamily() : base()->fontFamily(); myEntry.fontFamilies() : base()->fontFamilies();
} }
const std::string &ZLTextStyleDecoration::colorStyle() const { const std::string &ZLTextStyleDecoration::colorStyle() const {

View file

@ -46,7 +46,7 @@ public:
ZLTextForcedStyle(shared_ptr<ZLTextStyle> base, const ZLTextStyleEntry &entry); ZLTextForcedStyle(shared_ptr<ZLTextStyle> base, const ZLTextStyleEntry &entry);
~ZLTextForcedStyle(); ~ZLTextForcedStyle();
const std::string &fontFamily() const; const std::vector<std::string> &fontFamilies() const;
int fontSize() const; int fontSize() const;
bool bold() const; bool bold() const;
@ -77,7 +77,7 @@ private:
public: public:
virtual ~ZLTextPartialDecoratedStyle(); virtual ~ZLTextPartialDecoratedStyle();
const std::string &fontFamily() const; const std::vector<std::string> &fontFamilies() const;
int fontSize() const; int fontSize() const;
bool bold() const; bool bold() const;
bool italic() const; bool italic() const;
@ -98,6 +98,7 @@ public:
private: private:
const ZLTextStyleDecoration &myDecoration; const ZLTextStyleDecoration &myDecoration;
std::vector<std::string> myFontFamilies;
}; };
class ZLTextFullDecoratedStyle : public ZLTextDecoratedStyle { class ZLTextFullDecoratedStyle : public ZLTextDecoratedStyle {
@ -108,7 +109,7 @@ private:
public: public:
~ZLTextFullDecoratedStyle(); ~ZLTextFullDecoratedStyle();
const std::string &fontFamily() const; const std::vector<std::string> &fontFamilies() const;
int fontSize() const; int fontSize() const;
bool bold() const; bool bold() const;
bool italic() const; bool italic() const;
@ -129,6 +130,7 @@ public:
private: private:
const ZLTextFullStyleDecoration &myDecoration; const ZLTextFullStyleDecoration &myDecoration;
std::vector<std::string> myFontFamilies;
}; };
inline ZLTextDecoratedStyle::ZLTextDecoratedStyle(const shared_ptr<ZLTextStyle> base) : myBase(base) {} 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 double ZLTextForcedStyle::lineSpace() const { return base()->lineSpace(); }
inline bool ZLTextForcedStyle::allowHyphenations() const { return base()->allowHyphenations(); } 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 ZLTextPartialDecoratedStyle::~ZLTextPartialDecoratedStyle() {}
inline short ZLTextPartialDecoratedStyle::spaceBefore(const ZLTextStyleEntry::Metrics &metrics) const { return base()->spaceBefore(metrics); } 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::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::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::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 int ZLTextPartialDecoratedStyle::verticalShift() const { return base()->verticalShift() + myDecoration.VerticalShiftOption.value(); }
inline ZLTextAlignmentType ZLTextPartialDecoratedStyle::alignment() const { return base()->alignment(); } inline ZLTextAlignmentType ZLTextPartialDecoratedStyle::alignment() const { return base()->alignment(); }
inline double ZLTextPartialDecoratedStyle::lineSpace() const { return base()->lineSpace(); } 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 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 int ZLTextFullDecoratedStyle::verticalShift() const { return base()->verticalShift() + myDecoration.VerticalShiftOption.value(); }
inline double ZLTextFullDecoratedStyle::lineSpace() const { inline double ZLTextFullDecoratedStyle::lineSpace() const {
const int spacing = myDecoration.LineSpacePercentOption.value(); const int spacing = myDecoration.LineSpacePercentOption.value();

View file

@ -21,6 +21,7 @@
#define __ZLTEXTSTYLE_H__ #define __ZLTEXTSTYLE_H__
#include <string> #include <string>
#include <vector>
#include <shared_ptr.h> #include <shared_ptr.h>
@ -43,7 +44,7 @@ public:
virtual bool isDecorated() const = 0; 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 int fontSize() const = 0;
virtual bool bold() const = 0; virtual bool bold() const = 0;

View file

@ -19,6 +19,8 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <errno.h>
#include <limits.h>
#include <ZLibrary.h> #include <ZLibrary.h>
#include <ZLFile.h> #include <ZLFile.h>
@ -52,6 +54,7 @@ public:
void startElementHandler(const char *tag, const char **attributes); void startElementHandler(const char *tag, const char **attributes);
private: 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); int intValue(const char **attributes, const char *name, int defaultValue = 0);
bool booleanValue(const char **attributes, const char *name); bool booleanValue(const char **attributes, const char *name);
ZLBoolean3 b3Value(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"; 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) { inline int ZLTextStyleReader::intValue(const char **attributes, const char *name, int defaultValue) {
const char *stringValue = attributeValue(attributes, name); const char *stringValue = attributeValue(attributes, name);
return (stringValue == 0) ? defaultValue : atoi(stringValue); return (stringValue == 0) ? defaultValue : atoi(stringValue);
@ -95,12 +123,25 @@ void ZLTextStyleReader::startElementHandler(const char *tag, const char **attrib
if (booleanValue(attributes, "partial")) { if (booleanValue(attributes, "partial")) {
decoration = new ZLTextStyleDecoration(name, fontSizeDelta, bold, italic, verticalShift, allowHyphenations); 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 { } else {
int spaceBefore = intValue(attributes, "spaceBefore"); ZLTextStyleEntry::SizeUnit spaceBeforeUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
int spaceAfter = intValue(attributes, "spaceAfter"); ZLTextStyleEntry::SizeUnit spaceAfterUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
int leftIndent = intValue(attributes, "leftIndent"); ZLTextStyleEntry::SizeUnit leftIndentUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
int rightIndent = intValue(attributes, "rightIndent"); ZLTextStyleEntry::SizeUnit rightIndentUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
int firstLineIndentDelta = intValue(attributes, "firstLineIndentDelta"); 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; ZLTextAlignmentType alignment = ALIGN_UNDEFINED;
const char *alignmentString = attributeValue(attributes, "alignment"); 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 int lineSpacingPercent = intValue(attributes, "lineSpacingPercent", -1);
const double lineSpace = (lineSpacingPercent == -1) ? 0.0 : (lineSpacingPercent / 100.0); 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"); const char *hyperlink = attributeValue(attributes, "hyperlink");
if (hyperlink != 0) { if (hyperlink != 0) {

View file

@ -24,6 +24,9 @@
#include <ZLOptions.h> #include <ZLOptions.h>
#include <ZLTextStyle.h> #include <ZLTextStyle.h>
#include <ZLTextParagraph.h>
class ZLTextFullStyleDecoration;
class ZLTextStyleDecoration { class ZLTextStyleDecoration {
@ -31,7 +34,7 @@ public:
ZLTextStyleDecoration(const std::string &name, int fontSizeDelta, ZLBoolean3 bold, ZLBoolean3 italic, int verticalShift, ZLBoolean3 allowHyphenations); ZLTextStyleDecoration(const std::string &name, int fontSizeDelta, ZLBoolean3 bold, ZLBoolean3 italic, int verticalShift, ZLBoolean3 allowHyphenations);
virtual ~ZLTextStyleDecoration(); virtual ~ZLTextStyleDecoration();
virtual bool isFullDecoration() const; virtual const ZLTextFullStyleDecoration *fullDecoration() const;
virtual shared_ptr<ZLTextStyle> createDecoratedStyle(const shared_ptr<ZLTextStyle> base) const; virtual shared_ptr<ZLTextStyle> createDecoratedStyle(const shared_ptr<ZLTextStyle> base) const;
@ -40,6 +43,9 @@ public:
const std::string &colorStyle() const; const std::string &colorStyle() const;
void setColorStyle(const std::string &colorStyle); void setColorStyle(const std::string &colorStyle);
void setFirstLineIndentDelta(int indent, ZLTextStyleEntry::SizeUnit unit);
int* firstLineIndentDelta(ZLTextStyleEntry::SizeUnit &unit) const;
public: public:
ZLStringOption FontFamilyOption; ZLStringOption FontFamilyOption;
ZLIntegerRangeOption FontSizeDeltaOption; ZLIntegerRangeOption FontSizeDeltaOption;
@ -54,6 +60,8 @@ private:
std::string myName; std::string myName;
std::string myColorStyle; std::string myColorStyle;
int* myFirstLineIndentDelta;
ZLTextStyleEntry::SizeUnit myFirstLineIndentDeltaUnit;
}; };
class ZLTextFullStyleDecoration : public ZLTextStyleDecoration { 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(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(); ~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; shared_ptr<ZLTextStyle> createDecoratedStyle(const shared_ptr<ZLTextStyle> base) const;
public: public:
@ -71,12 +80,21 @@ public:
ZLIntegerRangeOption SpaceAfterOption; ZLIntegerRangeOption SpaceAfterOption;
ZLIntegerRangeOption LineStartIndentOption; ZLIntegerRangeOption LineStartIndentOption;
ZLIntegerRangeOption LineEndIndentOption; ZLIntegerRangeOption LineEndIndentOption;
ZLIntegerRangeOption FirstLineIndentDeltaOption;
ZLIntegerOption AlignmentOption; ZLIntegerOption AlignmentOption;
ZLDoubleOption LineSpaceOption; ZLDoubleOption LineSpaceOption;
ZLIntegerOption LineSpacePercentOption; ZLIntegerOption LineSpacePercentOption;
private:
ZLIntegerRangeOption FirstLineIndentDeltaOption;
public:
ZLTextStyleEntry::SizeUnit SpaceBeforeOptionUnit;
ZLTextStyleEntry::SizeUnit SpaceAfterOptionUnit;
ZLTextStyleEntry::SizeUnit LineStartIndentOptionUnit;
ZLTextStyleEntry::SizeUnit LineEndIndentOptionUnit;
ZLTextStyleEntry::SizeUnit FirstLineIndentDeltaOptionUnit;
}; };
class ZLTextStyleCollection { class ZLTextStyleCollection {
@ -103,11 +121,15 @@ private:
friend class ZLTextStyleReader; friend class ZLTextStyleReader;
}; };
inline ZLTextStyleDecoration::~ZLTextStyleDecoration() {} inline ZLTextStyleDecoration::~ZLTextStyleDecoration() { delete myFirstLineIndentDelta; }
inline bool ZLTextStyleDecoration::isFullDecoration() const { return false; }
inline const std::string &ZLTextStyleDecoration::name() const { return myName; } 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 ZLTextFullStyleDecoration::~ZLTextFullStyleDecoration() {}
inline bool ZLTextFullStyleDecoration::isFullDecoration() const { return true; }
#endif /* __ZLTEXTSTYLECOLLECTION_H__ */ #endif /* __ZLTEXTSTYLECOLLECTION_H__ */

View file

@ -114,7 +114,7 @@ size_t ZLTextView::PositionIndicator::endTextIndex() const {
} }
void ZLTextView::PositionIndicator::drawExtraText(const std::string &text) { 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()); context().setColor(myTextView.color());
int width = context().stringWidth(text.data(), text.length(), false); int width = context().stringWidth(text.data(), text.length(), false);