[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"?>
<DefaultStyles>
<style id="0" name="Regular Paragraph" firstLineIndentDelta="40" allowHyphenations="true"/>
<style id="0" name="Regular Paragraph" firstLineIndentDelta="1em" allowHyphenations="true"/>
<style id="1" name="Title" fontSizeDelta="10" bold="true" spaceBefore="2" spaceAfter="7" alignment="center" allowHyphenations="false"/>
<style id="3" name="Poem Title" fontSizeDelta="2" bold="true" spaceBefore="6" spaceAfter="6" leftIndent="40" allowHyphenations="false"/>
<style id="2" name="Section Title" fontSizeDelta="6" bold="true" spaceAfter="5" alignment="center" allowHyphenations="false"/>
<style id="31" name="Header 1" fontSizeDelta="6" bold="true" spaceAfter="5" alignment="center" allowHyphenations="false"/>
<style id="32" name="Header 2" fontSizeDelta="6" bold="true" spaceAfter="5" alignment="center" allowHyphenations="false"/>
<style id="5" name="Annotation" fontSizeDelta="-2" firstLineIndentDelta="20" allowHyphenations="true"/>
<style id="6" name="Epigraph" fontSizeDelta="-2" italic="true" leftIndent="80" allowHyphenations="true"/>
<style id="4" name="Subtitle" bold="true" allowHyphenations="true"/>
<style id="33" name="Header 3" bold="true" allowHyphenations="true"/>
<style id="34" name="Header 4" bold="true" allowHyphenations="true"/>
<style id="13" name="Author" leftIndent="20" allowHyphenations="false"/>
<style id="14" name="Date" leftIndent="40" allowHyphenations="false"/>
<style id="7" name="Stanza" spaceBefore="6" spaceAfter="6" alignment="linestart" allowHyphenations="false"/>
<style id="8" name="Verse" leftIndent="20" alignment="linestart" allowHyphenations="false"/>
<style id="10" name="Image" spaceBefore="8" alignment="center"/>
<style id="10" name="Image" spaceBefore="8" spaceAfter="8" alignment="center"/>
<style id="23" name="Contents Table" spaceAfter="7" leftIndent="20" firstLineIndentDelta="-20" alignment="linestart"/>
<style id="25" name="Library Entry" alignment="linestart" allowHyphenations="false"/>
<style id="9" name="Preformatted text" italic="true" alignment="linestart" allowHyphenations="false"/>
<style id="9" name="Preformatted text" firstLineIndentDelta="0" alignment="linestart" allowHyphenations="false" family="monospace"/>
<style id="31" name="Header 1" firstLineIndentDelta="0" fontSizeDelta="6" spaceAfter="1em" bold="true" allowHyphenations="false"/>
<style id="32" name="Header 2" firstLineIndentDelta="0" fontSizeDelta="4" spaceAfter="1em" bold="true" allowHyphenations="false"/>
<style id="33" name="Header 3" firstLineIndentDelta="0" spaceBefore="1em" spaceAfter="1em" bold="true" allowHyphenations="true"/>
<style id="34" name="Header 4" firstLineIndentDelta="0" spaceBefore="1em" spaceAfter="1em" bold="true" allowHyphenations="true"/>
<style id="35" name="Header 5" firstLineIndentDelta="0" spaceBefore="1em" spaceAfter="1em" bold="true"/>
<style id="36" name="Header 6" firstLineIndentDelta="0" spaceBefore="1em" spaceAfter="1em" bold="true"/>
<style id="39" name="Quote" spaceBefore="1em" spaceAfter="1em" leftIndent="2em" rightIndent="1em"/>
<style id="12" partial="true" name="Cite" italic="true"/>
<style id="15" partial="true" name="Internal Hyperlink" allowHyphenations="false" hyperlink="internal"/>
@ -27,14 +30,12 @@
<style id="16" partial="true" name="Footnote" fontSizeDelta="-6" vShift="10" allowHyphenations="false" hyperlink="internal"/>
<style id="17" partial="true" name="Emphasis" italic="true"/>
<style id="18" partial="true" name="Strong" bold="true"/>
<style id="35" name="Header 5" bold="true"/>
<style id="36" name="Header 6" bold="true"/>
<style id="19" partial="true" name="Subscript" fontSizeDelta="-4" vShift="-4" allowHyphenations="false"/>
<style id="20" partial="true" name="Superscript" fontSizeDelta="-4" vShift="10" allowHyphenations="false"/>
<style id="21" partial="true" name="Code" italic="false" allowHyphenations="false"/>
<style id="21" partial="true" name="Code" firstLineIndentDelta="0" allowHyphenations="false" family="monospace"/>
<style id="22" partial="true" name="StrikeThrough"/>
<style id="27" partial="true" name="Italic" italic="true"/>
<style id="28" partial="true" name="Bold" bold="true"/>
<style id="29" partial="true" name="Definition" italic="true"/>
<style id="30" partial="true" name="Definition Description" bold="true" italic="true"/>
<style id="30" partial="true" name="Definition Description" bold="true" italic="true"/>
</DefaultStyles>

View file

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

View file

@ -54,6 +54,11 @@ shared_ptr<ZLTextStyle> BooksTextStyle::defaults()
return style;
}
BooksTextStyle::BooksTextStyle()
{
iFontFamilies.push_back(Default::FONT_FAMILY);
}
bool
BooksTextStyle::equalLayout(
shared_ptr<ZLTextStyle> aStyle1,
@ -74,9 +79,9 @@ bool BooksTextStyle::isDecorated() const
return false;
}
const std::string& BooksTextStyle::fontFamily() const
const std::vector<std::string>& BooksTextStyle::fontFamilies() const
{
return Default::FONT_FAMILY;
return iFontFamilies;
}
int BooksTextStyle::fontSize() const

View file

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

View file

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

View file

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

View file

@ -65,6 +65,7 @@ enum FBTextKind {
H6 = 36,
EXTERNAL_HYPERLINK = 37,
BOOK_HYPERLINK = 38,
BLOCKQUOTE = 39
};
#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 style;
if (text) {
myReadState = ATTRIBUTE_NAME;
reset(ATTRIBUTE_NAME);
parse(text, strlen(text), true);
if (!myStateStack.empty()) {
switch (myStateStack.top()) {
case ATTRIBUTE_VALUE_SPACE:
case ATTRIBUTE_VALUE_COMMA:
finishAttributeValue();
break;
default:
break;
}
}
StyleSheetTable::updateStyle(style, myMap);
reset();
}
return style;
}
StyleSheetParser::StyleSheetParser() : myReadState(TAG_NAME), myInsideComment(false), myAtBlockDepth(0) {
StyleSheetParser::StyleSheetParser() :
myBuffer1(0), myBuffer2(0), myBuffer3(0) {
myStateStack.push(SELECTOR);
}
StyleSheetParser::~StyleSheetParser() {
}
void StyleSheetParser::reset() {
myWord.erase();
myAttributeName.erase();
myReadState = TAG_NAME;
myInsideComment = false;
myAtBlockDepth = 0;
mySelectors.clear();
void StyleSheetParser::reset(ReadState state) {
myWord.resize(0);
myAttributeName.resize(0);
myStateStack = std::stack<ReadState>();
myStateStack.push(state);
mySelectors.resize(0);
myMap.clear();
myBuffer1 = myBuffer2 = myBuffer3 = 0;
}
void StyleSheetParser::parse(ZLInputStream &stream) {
@ -78,165 +89,279 @@ void StyleSheetParser::parse(const char *text, int len, bool final) {
const char *start = text;
const char *end = text + len;
for (const char *ptr = start; ptr != end; ++ptr) {
if ((myReadState != TAG_NAME && isspace(*ptr)) ||
(myReadState == TAG_NAME && *ptr == ',')) {
if (start != ptr) {
myWord.append(start, ptr - start);
}
processWord(myWord);
myWord.erase();
start = ptr + 1;
} else if (isControlSymbol(*ptr)) {
if (start != ptr) {
myWord.append(start, ptr - start);
}
processWord(myWord);
myWord.erase();
if (!myInsideComment) {
processControl(*ptr);
}
start = ptr + 1;
}
}
if (start < end) {
myWord.append(start, end - start);
if (final) {
processWord(myWord);
myWord.erase();
}
}
}
bool StyleSheetParser::isControlSymbol(const char symbol) {
switch (symbol) {
case '{':
case '}':
case ';':
case ':':
return true;
default:
return false;
processChar1(*ptr);
}
}
void StyleSheetParser::storeData(const std::string&, const StyleSheetTable::AttributeMap&) {
}
void StyleSheetParser::processControl(const char control) {
switch (control) {
case '{':
switch (myReadState) {
case AT_RULE:
myReadState = AT_BLOCK;
myAtBlockDepth = 1;
break;
case AT_BLOCK:
myAtBlockDepth++;
break;
case TAG_NAME:
myReadState = ATTRIBUTE_NAME;
myMap.clear();
break;
default:
myReadState = BROKEN;
break;
/* Converts \r\n into \n */
void StyleSheetParser::processChar1(char c) {
if (myBuffer1 == '\r') {
myBuffer1 = 0;
if (c == '\n') {
processChar2('\n');
} else {
processChar2('\r');
}
} else if (c == '\r') {
myBuffer1 = '\r';
} else {
processChar2(c);
}
}
/* Glues continued lines together */
void StyleSheetParser::processChar2(char c) {
if (myBuffer2 == '\\') {
myBuffer2 = 0;
if (c != '\n') {
processChar3('\\');
processChar3(c);
}
} else if (c == '\\') {
myBuffer2 = '\\';
} else {
processChar3(c);
}
}
/* Handles comments */
void StyleSheetParser::processChar3(char c) {
switch (myStateStack.top()) {
case COMMENT:
if (myBuffer3 == '*' && c == '/') {
myBuffer3 = 0;
myStateStack.pop();
} else {
myBuffer3 = (c == '*') ? '*' : 0;
}
break;
case STRING_LITERAL_SINGLE:
case STRING_LITERAL_DOUBLE:
processChar4(c);
break;
default:
if (myBuffer3 == '/' && c == '*') {
myStateStack.push(COMMENT);
} else {
if (myBuffer3) processChar4(myBuffer3);
if (c == '/') {
myBuffer3 = c;
} else {
myBuffer3 = 0;
processChar4(c);
}
}
break;
}
}
/* Handles clean input */
void StyleSheetParser::processChar4(char c) {
switch (myStateStack.top()) {
case SELECTOR:
switch (c) {
case ',':
if (ZLStringUtil::stripWhiteSpaces(myWord)) {
mySelectors.push_back(myWord);
myWord.resize(0);
}
break;
case '{':
if (ZLStringUtil::stripWhiteSpaces(myWord)) mySelectors.push_back(myWord);
if (!mySelectors.empty()) {
if (mySelectors[0][0] == '@') {
// Ignore AT-rules
mySelectors.resize(0);
myStateStack.push(SKIP_BLOCK_CURLY);
} else {
myMap.clear();
myStateStack.push(ATTRIBUTE_NAME);
}
} else {
myStateStack.push(SKIP_BLOCK_CURLY);
}
myWord.resize(0);
break;
default:
if (!isspace(c) || !myWord.empty()) {
myWord.append(1, c);
}
break;
}
break;
case ATTRIBUTE_NAME:
switch (c) {
case ':':
if (ZLStringUtil::stripWhiteSpaces(myWord)) {
myAttributeName = myWord;
myMap[myAttributeName].resize(0);
myWord.resize(0);
static const std::string FONT_FAMILY("font-family");
if (myAttributeName == FONT_FAMILY) {
myStateStack.top() = ATTRIBUTE_VALUE_COMMA;
} else {
myStateStack.top() = ATTRIBUTE_VALUE_SPACE;
}
} else {
finishAttribute();
myStateStack.top() = ATTRIBUTE_IGNORE;
}
break;
case '\n':
case ';':
finishAttribute();
break;
case '}':
switch (myReadState) {
case AT_BLOCK:
if (--myAtBlockDepth > 0) {
return;
}
break;
case AT_RULE:
case BROKEN:
break;
default:
for (unsigned int i=0; i<mySelectors.size(); i++) {
storeData(mySelectors[i], myMap);
}
break;
finishRule();
myStateStack.pop();
break;
default:
if (!isspace(c) || !myWord.empty()) {
myWord.append(1, c);
}
myReadState = TAG_NAME;
mySelectors.clear();
myMap.clear();
break;
}
break;
case ATTRIBUTE_VALUE_SPACE:
case ATTRIBUTE_VALUE_COMMA:
switch (c) {
case '\'':
myStateStack.push(STRING_LITERAL_SINGLE);
break;
case '"':
myStateStack.push(STRING_LITERAL_DOUBLE);
break;
case '}':
finishAttributeValue();
finishRule();
myStateStack.pop();
break;
case ';':
switch (myReadState) {
case AT_RULE:
myReadState = TAG_NAME;
mySelectors.clear();
case '\n':
finishAttributeValue();
finishAttribute();
myStateStack.top() = ATTRIBUTE_NAME;
break;
case ',':
if (myStateStack.top() == ATTRIBUTE_VALUE_COMMA) {
finishAttributeValue();
} else {
myWord.append(1, c);
}
break;
default:
if (isspace(c)) {
if (myStateStack.top() == ATTRIBUTE_VALUE_SPACE) {
finishAttributeValue();
} else if (!myWord.empty()) {
myWord.append(1, c);
}
} else {
myWord.append(1, c);
}
break;
}
break;
case ATTRIBUTE_IGNORE:
switch (c) {
case '\n':
case ';':
finishAttribute();
myStateStack.top() = ATTRIBUTE_NAME;
break;
case '}':
finishRule();
myStateStack.pop();
break;
default:
break;
}
break;
case STRING_LITERAL_SINGLE:
case STRING_LITERAL_DOUBLE:
if (c == myStateStack.top()) {
myStateStack.pop();
} else if (c == '\n') {
// User agents must close strings upon reaching
// the end of a line (i.e., before an unescaped
// line feed, carriage return or form feed character),
// but then drop the construct (declaration or rule)
// in which the string was found.
myStateStack.pop();
switch (myStateStack.top()) {
case ATTRIBUTE_VALUE_SPACE:
case ATTRIBUTE_VALUE_COMMA:
myStateStack.top() = ATTRIBUTE_IGNORE;
myMap[myAttributeName].resize(0);
myAttributeName.resize(0);
myWord.resize(0);
break;
default:
break;
}
} else {
myWord.append(1, c);
}
break;
case SKIP_BLOCK_CURLY:
case SKIP_BLOCK_SQUARE:
switch (c) {
case '{':
myStateStack.push(SKIP_BLOCK_CURLY);
break;
case '[':
myStateStack.push(SKIP_BLOCK_SQUARE);
break;
case '\'':
myStateStack.push(STRING_LITERAL_SINGLE);
break;
case '"':
myStateStack.push(STRING_LITERAL_DOUBLE);
break;
default:
if (c == myStateStack.top()) {
myStateStack.pop();
if (myStateStack.top() == SELECTOR) {
myWord.resize(0);
mySelectors.resize(0);
myMap.clear();
break;
case AT_BLOCK:
break;
case ATTRIBUTE_VALUE:
case ATTRIBUTE_NAME:
myReadState = ATTRIBUTE_NAME;
break;
default:
myReadState = BROKEN;
break;
}
break;
case ':':
switch (myReadState) {
case AT_BLOCK:
break;
case ATTRIBUTE_NAME:
myReadState = ATTRIBUTE_VALUE;
break;
default:
myReadState = BROKEN;
break;
}
break;
}
}
void StyleSheetParser::processWord(std::string &word) {
while (!word.empty()) {
int index = word.find(myInsideComment ? "*/" : "/*");
if (!myInsideComment) {
if (index == -1) {
processWordWithoutComments(word);
} else if (index > 0) {
processWordWithoutComments(word.substr(0, index));
}
}
if (index == -1) {
break;
}
myInsideComment = !myInsideComment;
word.erase(0, index + 2);
}
}
void StyleSheetParser::processWordWithoutComments(std::string word) {
switch (myReadState) {
case AT_RULE:
case AT_BLOCK:
break;
case TAG_NAME:
ZLStringUtil::stripWhiteSpaces(word);
if (!word.empty()) {
if (word[0] == '@') {
myReadState = AT_RULE;
} else {
mySelectors.push_back(word);
}
}
break;
case ATTRIBUTE_NAME:
myAttributeName = word;
myMap[myAttributeName].clear();
break;
case ATTRIBUTE_VALUE:
myMap[myAttributeName].push_back(word);
break;
case BROKEN:
break;
}
break;
case COMMENT: // Comments are handled elsewhere
break;
}
}
void StyleSheetParser::finishAttributeValue() {
if (ZLStringUtil::stripWhiteSpaces(myWord)) {
myMap[myAttributeName].push_back(myWord);
myWord.resize(0);
}
}
void StyleSheetParser::finishAttribute() {
myAttributeName.resize(0);
myWord.resize(0);
}
void StyleSheetParser::finishRule() {
for (unsigned int i=0; i<mySelectors.size(); i++) {
storeData(mySelectors[i], myMap);
}
myAttributeName.resize(0);
myWord.resize(0);
mySelectors.resize(0);
myMap.clear();
}

View file

@ -22,6 +22,8 @@
#include "StyleSheetTable.h"
#include <stack>
class ZLInputStream;
class StyleSheetParser {
@ -39,26 +41,37 @@ protected:
virtual void storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map);
private:
bool isControlSymbol(const char symbol);
void processWord(std::string &word);
void processWordWithoutComments(std::string word);
void processControl(const char control);
enum ReadState {
COMMENT,
SELECTOR,
ATTRIBUTE_NAME,
ATTRIBUTE_VALUE_SPACE,
ATTRIBUTE_VALUE_COMMA,
ATTRIBUTE_IGNORE,
STRING_LITERAL_SINGLE = '\'',
STRING_LITERAL_DOUBLE = '"',
SKIP_BLOCK_CURLY = '}',
SKIP_BLOCK_SQUARE = ']'
};
void reset(ReadState state);
void processChar1(char c);
void processChar2(char c);
void processChar3(char c);
void processChar4(char c);
void finishRule();
void finishAttribute();
void finishAttributeValue();
private:
std::string myWord;
std::string myAttributeName;
enum {
AT_RULE,
AT_BLOCK,
TAG_NAME,
ATTRIBUTE_NAME,
ATTRIBUTE_VALUE,
BROKEN,
} myReadState;
bool myInsideComment;
int myAtBlockDepth;
std::stack<ReadState> myStateStack;
std::vector<std::string> mySelectors;
StyleSheetTable::AttributeMap myMap;
char myBuffer1;
char myBuffer2;
char myBuffer3;
friend class StyleSheetSingleStyleParser;
};
@ -81,4 +94,6 @@ public:
StyleSheetTable::Style parseString(const char *text);
};
inline void StyleSheetParser::reset() { reset(SELECTOR); }
#endif /* __STYLESHEETPARSER_H__ */

View file

@ -64,6 +64,30 @@ void StyleSheetTable::Style::apply(const Style &other) {
if (other.PageBreakAfter != B3_UNDEFINED) {
PageBreakAfter = other.PageBreakAfter;
}
if (other.WhiteSpace != WS_UNDEFINED) {
WhiteSpace = other.WhiteSpace;
}
if (other.DisplayNone) {
DisplayNone = other.DisplayNone;
}
}
void StyleSheetTable::Style::inherit(const Style &other) {
TextStyle.inherit(other.TextStyle);
if (other.WhiteSpace != WS_UNDEFINED) {
WhiteSpace = other.WhiteSpace;
}
if (other.DisplayNone) {
DisplayNone = other.DisplayNone;
}
}
bool StyleSheetTable::Style::equals(const Style &other) const {
return PageBreakBefore == other.PageBreakBefore &&
PageBreakAfter == other.PageBreakAfter &&
WhiteSpace == other.WhiteSpace &&
DisplayNone == other.DisplayNone &&
TextStyle.equals(other.TextStyle);
}
bool StyleSheetTable::Entry::match(const ElementList &stack) const {
@ -93,54 +117,28 @@ bool StyleSheetTable::Entry::match(const ElementList &stack) const {
void StyleSheetTable::addMap(const std::vector<std::string> &selectors, const AttributeMap &map) {
if ((!selectors.empty()) && !map.empty()) {
// http://www.w3.org/TR/selectors/#specificity
int a = 0, b = 0, c = 0;
SelectorList stack;
for (unsigned int i=0; i<selectors.size(); i++) {
const Selector &selector = selectors[i];
a += selector.a();
b += selector.b();
c += selector.c();
stack.push_back(selector);
}
if (a > 255) a = 255;
if (b > 255) b = 255;
if (c > 255) c = 255;
myEntries.push_back(Entry(stack, (a << 16) + (b << 8) + c, map));
}
}
bool StyleSheetTable::parseLength(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) {
if (!toParse.empty()) {
if (ZLStringUtil::stringEndsWith(toParse, "%")) {
unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT;
size = atoi(toParse.c_str());
return true;
} else if (ZLStringUtil::stringEndsWith(toParse, "em")) {
unit = ZLTextStyleEntry::SIZE_UNIT_EM_100;
size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0));
return true;
} else if (ZLStringUtil::stringEndsWith(toParse, "ex")) {
unit = ZLTextStyleEntry::SIZE_UNIT_EX_100;
size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0));
return true;
} else if (ZLStringUtil::stringEndsWith(toParse, "px") ||
ZLStringUtil::stringEndsWith(toParse, "pt") ||
ZLStringUtil::stringEndsWith(toParse, "pc")) {
unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
size = atoi(toParse.c_str());
return true;
} else if (toParse == "0") {
unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
size = 0;
return true;
Style style(map);
if (!style.empty()) {
// http://www.w3.org/TR/selectors/#specificity
int a = 0, b = 0, c = 0;
SelectorList stack;
for (unsigned int i=0; i<selectors.size(); i++) {
const Selector &selector = selectors[i];
a += selector.a();
b += selector.b();
c += selector.c();
stack.push_back(selector);
}
if (a > 255) a = 255;
if (b > 255) b = 255;
if (c > 255) c = 255;
myEntries.push_back(Entry(stack, (a << 16) + (b << 8) + c, style));
}
}
return false;
}
bool StyleSheetTable::parseMargin(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) {
if (parseLength(toParse, size, unit)) {
if (ZLTextStyleEntry::parseLength(toParse, size, unit)) {
// Negative margins do make sense but we don't really support them
if (size < 0) size = 0;
return true;
@ -166,7 +164,7 @@ void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Lengt
void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value) {
short size;
ZLTextStyleEntry::SizeUnit unit;
if (parseLength(value, size, unit)) {
if (ZLTextStyleEntry::parseLength(value, size, unit)) {
entry.setLength(name, size, unit);
}
}
@ -200,9 +198,11 @@ void StyleSheetTable::applyStyles(const ElementList &stack, Style &style) const
entries.push_back(&(*i));
}
}
std::sort(entries.begin(), entries.end(), sortBySpecificity);
for (std::vector<const Entry*>::const_iterator e = entries.begin(); e != entries.end(); ++e) {
style.apply((*e)->Style);
if (!entries.empty()) {
std::sort(entries.begin(), entries.end(), sortBySpecificity);
for (std::vector<const Entry*>::const_iterator e = entries.begin(); e != entries.end(); ++e) {
style.apply((*e)->Style);
}
}
}
@ -216,60 +216,77 @@ const std::vector<std::string> &StyleSheetTable::values(const AttributeMap &map,
}
void StyleSheetTable::updateTextStyle(ZLTextStyleEntry &entry, const AttributeMap &styles) {
const std::vector<std::string> &alignment = values(styles, "text-align");
static const std::string TEXT_ALIGN("text-align");
const std::vector<std::string> &alignment = values(styles, TEXT_ALIGN);
if (!alignment.empty()) {
if (alignment[0] == "justify") {
const std::string &value = alignment.at(0);
if (value == "justify") {
entry.setAlignmentType(ALIGN_JUSTIFY);
} else if (alignment[0] == "left") {
} else if (value == "left") {
entry.setAlignmentType(ALIGN_LEFT);
} else if (alignment[0] == "right") {
} else if (value == "right") {
entry.setAlignmentType(ALIGN_RIGHT);
} else if (alignment[0] == "center") {
} else if (value == "center") {
entry.setAlignmentType(ALIGN_CENTER);
}
} else {
static const std::string FLOAT("float");
const std::vector<std::string> &floatVal = values(styles, FLOAT);
if (!floatVal.empty()) {
const std::string &value = floatVal.at(0);
if (value == "left") {
entry.setAlignmentType(ALIGN_LEFT);
} else if (value == "right") {
entry.setAlignmentType(ALIGN_RIGHT);
}
}
}
const std::vector<std::string> &bold = values(styles, "font-weight");
if (!bold.empty()) {
static const std::string FONT_WEIGHT("font-weight");
const std::vector<std::string> &weight = values(styles, FONT_WEIGHT);
if (!weight.empty()) {
int num = -1;
if (bold[0] == "bold") {
const std::string &value = weight.at(0);
if (value == "bold") {
num = 700;
} else if (bold[0] == "normal") {
} else if (value == "normal") {
num = 400;
} else if ((bold[0].length() == 3) &&
(bold[0][1] == '0') &&
(bold[0][2] == '0') &&
(bold[0][0] >= '1') &&
(bold[0][0] <= '9')) {
num = 100 * (bold[0][0] - '0');
} else if (bold[0] == "bolder") {
} else if (bold[0] == "lighter") {
} else if ((value.length() == 3) &&
(value[1] == '0') &&
(value[2] == '0') &&
(value[0] >= '1') &&
(value[0] <= '9')) {
num = 100 * (value[0] - '0');
} else if (value == "bolder") {
} else if (value == "lighter") {
}
if (num != -1) {
entry.setFontModifier(FONT_MODIFIER_BOLD, num >= 600);
}
}
const std::vector<std::string> &italic = values(styles, "font-style");
static const std::string FONT_STYLE("font-style");
const std::vector<std::string> &italic = values(styles, FONT_STYLE);
if (!italic.empty()) {
entry.setFontModifier(FONT_MODIFIER_ITALIC, italic[0] == "italic");
}
const std::vector<std::string> &variant = values(styles, "font-variant");
static const std::string FONT_VARIANT("font-variant");
const std::vector<std::string> &variant = values(styles, FONT_VARIANT);
if (!variant.empty()) {
entry.setFontModifier(FONT_MODIFIER_SMALLCAPS, variant[0] == "small-caps");
}
const std::vector<std::string> &fontFamily = values(styles, "font-family");
if (!fontFamily.empty() && !fontFamily[0].empty()) {
entry.setFontFamily(fontFamily[0]);
}
static const std::string FONT_FAMILY("font-family");
const std::vector<std::string> &fontFamilies = values(styles, FONT_FAMILY);
if (!fontFamilies.empty()) entry.setFontFamilies(fontFamilies);
short size;
ZLTextStyleEntry::SizeUnit unit;
const std::vector<std::string> &fontSize = values(styles, "font-size");
static const std::string FONT_SIZE("font-size");
const std::vector<std::string> &fontSize = values(styles, FONT_SIZE);
if (!fontSize.empty()) {
std::string value = fontSize[0];
const std::string &value = fontSize.at(0);
if (value == "xx-small") {
entry.setFontSizeMag(-3);
} else if (value == "x-small") {
@ -285,7 +302,7 @@ void StyleSheetTable::updateTextStyle(ZLTextStyleEntry &entry, const AttributeMa
} else if (value == "xx-large") {
entry.setFontSizeMag(3);
} else {
if (parseLength(value, size, unit)) {
if (ZLTextStyleEntry::parseLength(value, size, unit)) {
switch (unit) {
case ZLTextStyleEntry::SIZE_UNIT_PIXEL:
// What to do with pixels?
@ -293,26 +310,68 @@ void StyleSheetTable::updateTextStyle(ZLTextStyleEntry &entry, const AttributeMa
case ZLTextStyleEntry::SIZE_UNIT_EM_100:
case ZLTextStyleEntry::SIZE_UNIT_EX_100:
case ZLTextStyleEntry::SIZE_UNIT_PERCENT:
entry.setFontSizeMag((size < 100 && size > 80) ? -1 :
(size > 100 && size < 120) ? 1 :
(size - 100)/20);
// Percent to magnification mapping algorithm
// matches ZLTextForcedStyle::fontSize() logic
if (size < 100) {
if (size >= 80) {
entry.setFontSizeMag(-1);
} else {
int mag;
unsigned int x1 = 5*100, x2 = 6*size;
// Too many iterations would cause 32-bit
// overflow and generally don't make sense.
for (mag=1; mag<=6 && x1<x2; ++mag) {
x1 *= 5; x2 *= 6;
}
entry.setFontSizeMag(-mag);
}
} else if (size > 100) {
if (size < 120) {
entry.setFontSizeMag(1);
} else {
int mag;
unsigned int x1 = 6*100, x2 = 5*size;
for (mag=1; mag<=6 && x1<x2; ++mag) {
x1 *= 6; x2 *= 5;
}
entry.setFontSizeMag(mag);
}
} else {
entry.setFontSizeMag(0);
}
break;
}
}
}
}
const std::vector<std::string> &opacity = values(styles, "opacity");
static const std::string OPACITY("opacity");
const std::vector<std::string> &opacity = values(styles, OPACITY);
if (!opacity.empty()) {
const int value = (int)(255 * ZLStringUtil::stringToDouble(opacity[0], 1));
entry.setOpacity((unsigned char)((value < 0) ? 0 : (value > 255) ? 255 : value));
}
std::vector<std::string> margins = values(styles, "margin");
if (!margins.empty() && margins.back() == "!important") {
// Margins will overwrite padding, sorry
static const std::string PADDING_TOP("padding-top");
static const std::string PADDING_BOTTOM("padding-bottom");
setLength(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, PADDING_TOP);
setLength(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, PADDING_BOTTOM);
static const std::string MARGIN("margin");
std::vector<std::string> margins = values(styles, MARGIN);
if (!margins.empty()) {
// Ignore the "!important" modifier for now
margins.pop_back();
if (margins.back() == "!important") {
margins.pop_back();
} else if (margins.back() == "important") {
margins.pop_back();
if (!margins.empty() && margins.back() == "!") {
margins.pop_back();
}
}
}
switch (margins.size()) {
case 1:
if (parseMargin(margins[0], size, unit)) {
@ -348,49 +407,61 @@ void StyleSheetTable::updateTextStyle(ZLTextStyleEntry &entry, const AttributeMa
break;
}
setMargin(entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, styles, "margin-left");
setMargin(entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, styles, "margin-right");
setLength(entry, ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, styles, "text-indent");
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "margin-top");
setLength(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "padding-top");
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "margin-bottom");
setLength(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "padding-bottom");
static const std::string MARGIN_LEFT("margin-left");
static const std::string MARGIN_RIGHT("margin-right");
static const std::string MARGIN_TOP("margin-top");
static const std::string MARGIN_BOTTOM("margin-bottom");
static const std::string TEXT_INDENT("text-indent");
setMargin(entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, styles, MARGIN_LEFT);
setMargin(entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, styles, MARGIN_RIGHT);
setLength(entry, ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, styles, TEXT_INDENT);
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, MARGIN_TOP);
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, MARGIN_BOTTOM);
}
bool StyleSheetTable::getPageBreakBefore(const AttributeMap &map, ZLBoolean3 &value) {
const std::vector<std::string> &pbb = values(map, "page-break-before");
if (!pbb.empty()) {
if ((pbb[0] == "always") ||
(pbb[0] == "left") ||
(pbb[0] == "right")) {
void StyleSheetTable::getPageBreakValue(const std::vector<std::string> &values, ZLBoolean3 &value) {
if (!values.empty()) {
const std::string &first = values.at(0);
if ((first == "always") ||
(first == "left") ||
(first == "right")) {
value = B3_TRUE;
return true;
} else if (pbb[0] == "avoid") {
} else if (first == "avoid") {
value = B3_FALSE;
return true;
}
}
return false;
}
bool StyleSheetTable::getPageBreakAfter(const AttributeMap &map, ZLBoolean3 &value) {
const std::vector<std::string> &pba = values(map, "page-break-after");
if (!pba.empty()) {
if ((pba[0] == "always") ||
(pba[0] == "left") ||
(pba[0] == "right")) {
value = B3_TRUE;
return true;
} else if (pba[0] == "avoid") {
value = B3_FALSE;
return true;
}
}
return false;
}
void StyleSheetTable::updateStyle(Style &style, const AttributeMap &map) {
updateTextStyle(style.TextStyle, map);
getPageBreakBefore(map, style.PageBreakBefore);
getPageBreakAfter(map, style.PageBreakAfter);
static const std::string PAGE_BREAK_BEFORE("page-break-before");
getPageBreakValue(values(map, PAGE_BREAK_BEFORE), style.PageBreakBefore);
static const std::string PAGE_BREAK_AFTER("page-break-after");
getPageBreakValue(values(map, PAGE_BREAK_AFTER), style.PageBreakAfter);
static const std::string WHITE_SPACE("white-space");
const std::vector<std::string> &whiteSpace = values(map, WHITE_SPACE);
if (!whiteSpace.empty()) {
const std::string &value = whiteSpace.at(0);
if (value == "normal") {
style.WhiteSpace = WS_NORMAL;
} else if (value == "nowrap") {
style.WhiteSpace = WS_NOWRAP;
} else if (value == "pre") {
style.WhiteSpace = WS_PRE;
} else if (value == "pre-wrap") {
style.WhiteSpace = WS_PRE_WRAP;
} else if (value == "pre-line") {
style.WhiteSpace = WS_PRE_LINE;
}
}
static const std::string DISPLAY("display");
const std::vector<std::string> &display = values(map, DISPLAY);
if (!display.empty() && display[0] == "none") {
style.DisplayNone = true;
}
}

View file

@ -73,29 +73,43 @@ public:
typedef std::vector<Selector> SelectorList;
typedef std::map<std::string,std::vector<std::string> > AttributeMap;
typedef enum {
WS_UNDEFINED,
WS_NORMAL,
WS_NOWRAP,
WS_PRE,
WS_PRE_WRAP,
WS_PRE_LINE
} WhiteSpaceValue;
struct Style {
Style();
Style(const Style &other);
Style(const AttributeMap &map);
Style &operator = (const Style &other);
void apply(const Style &other);
bool operator == (const Style &other) const;
bool equals(const Style &other) const;
void apply(const Style &other);
void inherit(const Style &other);
bool empty() const;
ZLTextStyleEntry TextStyle;
ZLBoolean3 PageBreakBefore;
ZLBoolean3 PageBreakAfter;
WhiteSpaceValue WhiteSpace;
bool DisplayNone;
};
typedef std::vector<Style> StyleList;
static void updateStyle(Style &style, const AttributeMap &map);
static void updateTextStyle(ZLTextStyleEntry &entry, const AttributeMap &map);
static bool getPageBreakBefore(const AttributeMap &map, ZLBoolean3 &value);
static bool getPageBreakAfter(const AttributeMap &map, ZLBoolean3 &value);
static void getPageBreakValue(const std::vector<std::string> &values, ZLBoolean3 &value);
private:
struct Entry {
Entry();
Entry(const SelectorList &selectors, int specificity, const AttributeMap &map);
Entry(const SelectorList &selectors, int specificity, const Style &style);
Entry &operator = (const Entry &other);
bool match(const ElementList &stack) const;
@ -106,13 +120,12 @@ private:
void addMap(const std::vector<std::string> &selectors, const AttributeMap &map);
static bool parseLength(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit);
static bool parseMargin(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit);
static void setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const AttributeMap &map, const std::string &attributeName);
static void setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value);
static void setMargin(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const AttributeMap &map, const std::string &attributeName);
static void setMargin(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value);
static const std::vector<std::string> &values(const AttributeMap &map, const std::string &name);
static const std::vector<std::string> &values(const AttributeMap &map, const std::string &name);
static bool sortBySpecificity(const Entry *e1, const Entry *e2);
public:
@ -129,7 +142,7 @@ inline StyleSheetTable::Selector::Selector(const std::string &type, const std::s
inline StyleSheetTable::Selector::Selector(const std::string &type, const std::string &klass) : myType(type), myClass(klass) {}
inline StyleSheetTable::Selector::Selector(const StyleSheetTable::Selector &other) : myType(other.myType), myClass(other.myClass), myId(other.myId) {}
inline StyleSheetTable::Selector::Selector() {}
inline StyleSheetTable::Selector &StyleSheetTable::Selector::operator = (const StyleSheetTable::Selector &other) {
inline StyleSheetTable::Selector &StyleSheetTable::Selector::operator = (const Selector &other) {
myType = other.myType; myClass = other.myClass; myId = other.myId; return *this;
}
inline bool StyleSheetTable::Selector::operator == (const Selector &other) const {
@ -138,7 +151,7 @@ inline bool StyleSheetTable::Selector::operator == (const Selector &other) const
inline bool StyleSheetTable::Selector::operator != (const Selector &other) const {
return (&other != this) && (myType != other.myType || myClass != other.myClass || myId != other.myId);
}
inline bool StyleSheetTable::Selector::operator < (const StyleSheetTable::Selector &other) const {
inline bool StyleSheetTable::Selector::operator < (const Selector &other) const {
return myType < other.myType || (myType == other.myType && (myClass < other.myClass || (myClass == other.myClass && myId < other.myId)));
}
inline bool StyleSheetTable::Selector::match(const Element &element) const {
@ -151,21 +164,30 @@ inline const std::string &StyleSheetTable::Selector::type() const { return myTyp
inline const std::string &StyleSheetTable::Selector::klass() const { return myClass; }
inline const std::string &StyleSheetTable::Selector::id() const { return myId; }
inline StyleSheetTable::Style::Style() : PageBreakBefore(B3_UNDEFINED), PageBreakAfter(B3_UNDEFINED) {}
inline StyleSheetTable::Style::Style(const StyleSheetTable::Style &other) : TextStyle(other.TextStyle), PageBreakBefore(other.PageBreakBefore), PageBreakAfter(other.PageBreakAfter) {}
inline StyleSheetTable::Style::Style(const StyleSheetTable::AttributeMap &map) : PageBreakBefore(B3_UNDEFINED), PageBreakAfter(B3_UNDEFINED) {
inline StyleSheetTable::Style::Style() :
PageBreakBefore(B3_UNDEFINED), PageBreakAfter(B3_UNDEFINED), WhiteSpace(WS_UNDEFINED), DisplayNone(false) {}
inline StyleSheetTable::Style::Style(const StyleSheetTable::Style &other) :
TextStyle(other.TextStyle), PageBreakBefore(other.PageBreakBefore), PageBreakAfter(other.PageBreakAfter), WhiteSpace(other.WhiteSpace), DisplayNone(other.DisplayNone) {}
inline StyleSheetTable::Style::Style(const StyleSheetTable::AttributeMap &map) :
PageBreakBefore(B3_UNDEFINED), PageBreakAfter(B3_UNDEFINED), WhiteSpace(WS_UNDEFINED), DisplayNone(false) {
updateStyle(*this, map);
}
inline StyleSheetTable::Style &StyleSheetTable::Style::operator = (const StyleSheetTable::Style &other) {
inline bool StyleSheetTable::Style::empty() const {
return TextStyle.isEmpty() && PageBreakBefore == B3_UNDEFINED && PageBreakAfter == B3_UNDEFINED && WhiteSpace == WS_UNDEFINED && !DisplayNone;
}
inline bool StyleSheetTable::Style::operator == (const Style &other) const { return equals(other); }
inline StyleSheetTable::Style &StyleSheetTable::Style::operator = (const Style &other) {
TextStyle = other.TextStyle;
PageBreakBefore = other.PageBreakBefore;
PageBreakAfter = other.PageBreakAfter;
WhiteSpace = other.WhiteSpace;
return *this;
}
inline StyleSheetTable::Entry::Entry() : Specificity(0) {}
inline StyleSheetTable::Entry::Entry(const SelectorList &selectors, int specificity, const AttributeMap &map) : Selectors(selectors), Specificity(specificity), Style(map) {}
inline StyleSheetTable::Entry &StyleSheetTable::Entry::operator = (const StyleSheetTable::Entry &other) {
inline StyleSheetTable::Entry::Entry(const SelectorList &selectors, int specificity, const StyleSheetTable::Style &style) :
Selectors(selectors), Specificity(specificity), Style(style) {}
inline StyleSheetTable::Entry &StyleSheetTable::Entry::operator = (const Entry &other) {
Selectors = other.Selectors;
Style = other.Style;
Specificity = other.Specificity;

View file

@ -25,6 +25,7 @@
#include <ZLUnicodeUtil.h>
#include <ZLStringUtil.h>
#include <ZLXMLNamespace.h>
#include <ZLTextStyleCollection.h>
#include "XHTMLReader.h"
#include "../util/EntityFilesCollector.h"
@ -90,12 +91,6 @@ public:
void doAtEnd(XHTMLReader &reader);
};
class XHTMLTagRestartParagraphAction : public XHTMLTagAction {
public:
void doAtStart(XHTMLReader &reader, const char **xmlattributes);
};
class XHTMLTagLineBreakAction : public XHTMLTagAction {
public:
@ -160,7 +155,6 @@ public:
XHTMLTagControlAction(FBTextKind control);
void doAtStart(XHTMLReader &reader, const char **xmlattributes);
void doAtEnd(XHTMLReader &reader);
private:
FBTextKind myControl;
@ -186,10 +180,8 @@ public:
};
void XHTMLTagStyleAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
static const std::string TYPE = "text/css";
const char *type = reader.attributeValue(xmlattributes, "type");
if ((type == 0) || (TYPE != type)) {
if (!type || strcmp(type, "text/css")) {
return;
}
@ -207,15 +199,13 @@ void XHTMLTagStyleAction::doAtEnd(XHTMLReader &reader) {
}
void XHTMLTagLinkAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
static const std::string REL = "stylesheet";
const char *rel = reader.attributeValue(xmlattributes, "rel");
if ((rel == 0) || (REL != rel)) {
if (!rel || strcmp(rel, "stylesheet")) {
return;
}
static const std::string TYPE = "text/css";
const char *type = reader.attributeValue(xmlattributes, "type");
if ((type == 0) || (TYPE != type)) {
if (!type || strcmp(type, "text/css")) {
return;
}
@ -234,10 +224,7 @@ void XHTMLTagLinkAction::doAtStart(XHTMLReader &reader, const char **xmlattribut
}
void XHTMLTagParagraphAction::doAtStart(XHTMLReader &reader, const char**) {
if (!reader.myNewParagraphInProgress) {
beginParagraph(reader);
reader.myNewParagraphInProgress = true;
}
endParagraph(reader);
}
void XHTMLTagParagraphAction::doAtEnd(XHTMLReader &reader) {
@ -253,16 +240,15 @@ void XHTMLTagBodyAction::doAtEnd(XHTMLReader &reader) {
reader.myReadState = XHTMLReader::READ_NOTHING;
}
void XHTMLTagRestartParagraphAction::doAtStart(XHTMLReader &reader, const char**) {
if (reader.myCurrentParagraphIsEmpty) {
bookReader(reader).addData(" ");
}
endParagraph(reader);
beginParagraph(reader);
}
void XHTMLTagLineBreakAction::doAtEnd(XHTMLReader& reader) {
bookReader(reader).addLineBreak();
BookReader &br = bookReader(reader);
if (br.paragraphIsOpen()) {
br.addLineBreak();
} else {
beginParagraph(reader);
br.addLineBreak();
endParagraph(reader);
}
}
void XHTMLTagItemAction::doAtStart(XHTMLReader &reader, const char**) {
@ -288,7 +274,7 @@ XHTMLTagImageAction::XHTMLTagImageAction(const std::string &attributeName) {
void XHTMLTagImageAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
// Ignore transparent images
if (!reader.myOpacityStack.back()) {
if (!reader.myParseStack.back().opacity) {
return;
}
@ -302,19 +288,14 @@ void XHTMLTagImageAction::doAtStart(XHTMLReader &reader, const char **xmlattribu
return;
}
bool flag = bookReader(reader).paragraphIsOpen();
if (flag) {
endParagraph(reader);
}
if ((strlen(fileName) > 2) && strncmp(fileName, "./", 2) == 0) {
fileName +=2;
}
bookReader(reader).addImageReference(fullfileName);
bookReader(reader).addImage(fullfileName, new ZLFileImage(ZLFile(fullfileName), 0));
if (flag) {
beginParagraph(reader);
}
reader.myElementHasContents.back() = true;
reader.myParseStack.back().kind = IMAGE;
reader.haveContent();
reader.myModelReader.addImageReference(fullfileName);
reader.myModelReader.addImage(fullfileName, new ZLFileImage(ZLFile(fullfileName), 0));
}
XHTMLTagSvgAction::XHTMLTagSvgAction(XHTMLSvgImageAttributeNamePredicate &predicate) : myPredicate(predicate) {
@ -339,13 +320,7 @@ XHTMLTagControlAction::XHTMLTagControlAction(FBTextKind control) : myControl(con
}
void XHTMLTagControlAction::doAtStart(XHTMLReader &reader, const char**) {
bookReader(reader).pushKind(myControl);
bookReader(reader).addControl(myControl, true);
}
void XHTMLTagControlAction::doAtEnd(XHTMLReader &reader) {
bookReader(reader).addControl(myControl, false);
bookReader(reader).popKind();
reader.myParseStack.back().kind = myControl;
}
void XHTMLTagHyperlinkAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
@ -387,25 +362,25 @@ void XHTMLTagParagraphWithControlAction::doAtStart(XHTMLReader &reader, const ch
if ((myControl == TITLE) && (bookReader(reader).model().bookTextModel()->paragraphsNumber() > 1)) {
bookReader(reader).insertEndOfSectionParagraph();
}
bookReader(reader).pushKind(myControl);
beginParagraph(reader);
reader.myParseStack.back().kind = myControl;
}
void XHTMLTagParagraphWithControlAction::doAtEnd(XHTMLReader &reader) {
endParagraph(reader);
bookReader(reader).popKind();
}
void XHTMLTagPreAction::doAtStart(XHTMLReader &reader, const char**) {
reader.myPreformatted++;
beginParagraph(reader);
bookReader(reader).addControl(CODE, true);
endParagraph(reader);
reader.myParseStack.back().kind = PREFORMATTED;
if (++reader.myPreformatted == 1) {
beginParagraph(reader);
}
}
void XHTMLTagPreAction::doAtEnd(XHTMLReader &reader) {
bookReader(reader).addControl(CODE, false);
endParagraph(reader);
reader.myPreformatted--;
if (!--reader.myPreformatted) {
endParagraph(reader);
}
}
XHTMLTagAction *XHTMLReader::addAction(const std::string &tag, XHTMLTagAction *action) {
@ -454,6 +429,7 @@ void XHTMLReader::fillTagTable() {
addAction("dt", new XHTMLTagParagraphWithControlAction(DEFINITION));
addAction("dfn", new XHTMLTagParagraphWithControlAction(DEFINITION));
addAction("strike", new XHTMLTagControlAction(STRIKETHROUGH));
addAction("blockquote", new XHTMLTagParagraphWithControlAction(BLOCKQUOTE));
addAction("a", new XHTMLTagHyperlinkAction());
@ -467,7 +443,6 @@ void XHTMLReader::fillTagTable() {
//addAction("map", new XHTMLTagAction());
//addAction("base", new XHTMLTagAction());
//addAction("blockquote", new XHTMLTagAction());
addAction("br", new XHTMLTagLineBreakAction());
//addAction("center", new XHTMLTagAction());
addAction("div", new XHTMLTagParagraphAction());
@ -507,29 +482,19 @@ bool XHTMLReader::readFile(const ZLFile &file, const std::string &referenceName)
myReferenceDirName = referenceName.substr(0, index + 1);
myPreformatted = 0;
myNewParagraphInProgress = false;
myReadState = READ_NOTHING;
myElementStack.clear();
myStyleStack.clear();
myElementHasContents.clear();
myOpacityStack.clear();
myParseStack.resize(1);
return readDocument(file);
}
shared_ptr<ZLTextStyleEntry> XHTMLReader::addStyleEntry(shared_ptr<ZLTextStyleEntry> entry, shared_ptr<ZLTextStyleEntry> styleEntry) {
if (!styleEntry.isNull() && !styleEntry->isEmpty()) {
if (entry.isNull()) entry = new ZLTextStyleEntry;
entry->apply(*styleEntry);
}
return entry;
}
void XHTMLReader::startElementHandler(const char *tag, const char **attributes) {
static const std::string HASH = "#";
const char *id = attributeValue(attributes, "id");
const char *style = attributeValue(attributes, "style");
const char *inlineStyle = attributeValue(attributes, "style");
const char *klass = attributeValue(attributes, "class");
if (id != 0) {
myModelReader.addHyperlinkLabel(myReferenceName + HASH + id);
@ -538,69 +503,242 @@ void XHTMLReader::startElementHandler(const char *tag, const char **attributes)
const std::string sTag = ZLUnicodeUtil::toLower(tag);
myElementStack.push_back(StyleSheetTable::Element(sTag, klass, id));
StyleSheetTable::Style cssStyle;
myStyleSheetTable.applyStyles(myElementStack, cssStyle);
if (style != 0) {
cssStyle.apply(myStyleParser.parseString(style));
}
if (cssStyle.PageBreakBefore == B3_TRUE) {
myModelReader.insertEndOfSectionParagraph();
myStyleStack.resize(myStyleStack.size() + 1);
StyleSheetTable::Style *style = &myStyleStack.back();
if (myStyleStack.size() > 1) {
style->inherit(myStyleStack.at(myStyleStack.size()-2));
}
int opacity = myOpacityStack.empty() ? 255 : myOpacityStack.back();
if (cssStyle.TextStyle.opacitySupported()) {
opacity *= cssStyle.TextStyle.opacity();
myStyleSheetTable.applyStyles(myElementStack, *style);
if (inlineStyle) {
style->apply(myStyleParser.parseString(inlineStyle));
}
myParseStack.resize(myParseStack.size() + 1);
ParseContext &prev(myParseStack.at(myParseStack.size()-2));
ParseContext &context(myParseStack.back());
if (style->TextStyle.opacitySupported()) {
int opacity = prev.opacity;
opacity *= style->TextStyle.opacity();
opacity /= 255;
context.opacity = opacity;
} else {
context.opacity = prev.opacity;
}
myStyleStack.push_back(cssStyle);
myElementHasContents.push_back(false);
myOpacityStack.push_back((unsigned char)opacity);
// Don't collect empty styles
if (style->empty()) {
myStyleStack.resize(myStyleStack.size()-1);
style = NULL;
} else {
context.styleIndex = myStyleStack.size() - 1;
if (style->PageBreakBefore == B3_TRUE) {
addPageBreak();
}
}
XHTMLTagAction *action = ourTagActions[sTag];
if (action != 0) {
action->doAtStart(*this, attributes);
}
myModelReader.addControl(cssStyle.TextStyle);
if (context.kind >= 0) {
context.decoration = ZLTextStyleCollection::Instance().decoration(context.kind);
}
if (myModelReader.paragraphIsOpen()) {
applyStyles(myParseStack.back());
}
}
void XHTMLReader::endElementHandler(const char *tag) {
myModelReader.addControl(REGULAR, false);
bool pageBreak = false;
ParseContext &context(myParseStack.back());
if (context.styleIndex >= 0) {
if (myStyleStack[context.styleIndex].PageBreakAfter == B3_TRUE) {
// If we are about to have a page break, we don't want
// endParagraph() to apply pending bottom margins.
myBottomMargins.resize(0);
pageBreak = true;
}
}
XHTMLTagAction *action = ourTagActions[ZLUnicodeUtil::toLower(tag)];
if (action != 0) {
action->doAtEnd(*this);
myNewParagraphInProgress = false;
}
const bool haveContents = myElementHasContents.back();
if (myStyleStack.back().PageBreakAfter == B3_TRUE && haveContents) {
myModelReader.insertEndOfSectionParagraph();
if (pageBreak) {
addPageBreak();
}
if (myModelReader.paragraphIsOpen()) {
if (context.styleIndex >= 0) {
myModelReader.addControl(REGULAR, false);
}
if (context.kind >= 0) {
myModelReader.addControl((FBTextKind)context.kind, false);
}
}
if (!context.bottomMarginApplied && elementHasBottomMargin(context)) {
ZLTextStyleEntry::SizeUnit unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
short size = 0;
if (context.styleIndex >= 0 && myStyleStack[context.styleIndex].TextStyle.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_AFTER)) {
size = myStyleStack[context.styleIndex].TextStyle.length(ZLTextStyleEntry::LENGTH_SPACE_AFTER, unit);
} else if (context.decoration) {
const ZLTextFullStyleDecoration *decoration = context.decoration->fullDecoration();
if (decoration) {
size = decoration->SpaceAfterOption.value();
unit = decoration->SpaceAfterOptionUnit;
}
}
if (size > 0) {
addBottomMargin(size, unit);
}
}
if (!myModelReader.paragraphIsOpen()) {
applyBottomMargins();
}
if (context.styleIndex >= 0) {
myStyleStack.pop_back();
}
myElementStack.pop_back();
myStyleStack.pop_back();
myElementHasContents.pop_back();
myOpacityStack.pop_back();
myParseStack.pop_back();
}
if (!myElementHasContents.empty() && haveContents) {
myElementHasContents.back() = true;
void XHTMLReader::addPageBreak() {
myBottomMargins.resize(0);
endParagraph();
myModelReader.insertEndOfSectionParagraph();
}
void XHTMLReader::addBottomMargin(short size, ZLTextStyleEntry::SizeUnit unit) {
for (std::vector<ZLTextStyleEntry>::iterator it = myBottomMargins.begin(); it != myBottomMargins.end(); ++it) {
ZLTextStyleEntry::SizeUnit entryUnit;
short entrySize = it->length(ZLTextStyleEntry::LENGTH_SPACE_AFTER, entryUnit);
if (entryUnit == unit) {
it->setLength(ZLTextStyleEntry::LENGTH_SPACE_AFTER, entrySize + size, unit);
return;
}
}
// No such unit yet
myBottomMargins.resize(myBottomMargins.size()+1);
myBottomMargins.back().setLength(ZLTextStyleEntry::LENGTH_SPACE_AFTER, size, unit);
}
void XHTMLReader::applyBottomMargins() {
if (!myBottomMargins.empty()) {
myModelReader.endParagraph();
for (std::vector<ZLTextStyleEntry>::const_iterator it = myBottomMargins.begin(); it != myBottomMargins.end(); ++it) {
addStyleParagraph(*it);
}
myBottomMargins.resize(0);
}
}
bool XHTMLReader::elementHasTopMargin(const ParseContext &context) const {
return
(context.styleIndex >= 0 && myStyleStack[context.styleIndex].TextStyle.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE)) ||
(context.decoration && context.decoration->fullDecoration());
}
bool XHTMLReader::elementHasBottomMargin(const ParseContext &context) const {
return
(context.styleIndex >= 0 && myStyleStack[context.styleIndex].TextStyle.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_AFTER)) ||
(context.decoration && context.decoration->fullDecoration());
}
void XHTMLReader::addStyleParagraph(const ZLTextStyleEntry &style) {
myModelReader.beginParagraph();
myModelReader.addControl(style);
myModelReader.addEmpty();
myModelReader.endParagraph();
}
void XHTMLReader::applyStyles(ParseContext &context) {
if (!context.stylesApplied) {
context.stylesApplied = true;
if (context.kind >= 0) {
myModelReader.addControl((FBTextKind)context.kind, true);
}
if (context.styleIndex >= 0) {
myModelReader.addControl(myStyleStack[context.styleIndex].TextStyle);
}
}
}
void XHTMLReader::beginParagraph() {
myCurrentParagraphIsEmpty = true;
myModelReader.beginParagraph();
if (!myStyleStack.empty()) {
for (StyleSheetTable::StyleList::const_iterator it = myStyleStack.begin(); it != myStyleStack.end(); ++it) {
myModelReader.addControl(((*it).TextStyle));
if (!myModelReader.paragraphIsOpen()) {
myModelReader.beginParagraph();
for (std::vector<ParseContext>::iterator it = myParseStack.begin(); it != myParseStack.end(); ++it) {
applyStyles(*it);
}
}
}
void XHTMLReader::endParagraph() {
myModelReader.endParagraph();
if (myModelReader.paragraphIsOpen()) {
myModelReader.endParagraph();
// Find which bottom margins have been applied
// and at the same time reset stylesApplied flag.
std::vector<ParseContext>::reverse_iterator it = myParseStack.rbegin();
for (; it != myParseStack.rend(); ++it) {
it->stylesApplied = false;
if (elementHasBottomMargin(*it)) {
it->bottomMarginApplied = true;
break;
}
}
// Reset stylesApplied for the remaining entries
for (; it != myParseStack.rend(); ++it) {
it->stylesApplied = false;
}
// Apply pending bottom margins
applyBottomMargins();
}
}
void XHTMLReader::haveContent() {
if (!myParseStack.back().haveContent) {
// Create empty paragraphs for other parent entries that haven't
// had any content yet, in order to apply their top margins. Skip
// the last entry as it will be applied for the paragraph we are
// about to start.
bool skippedLastEntry = false;
for (std::vector<ParseContext>::reverse_iterator it = myParseStack.rbegin(); it != myParseStack.rend() && !it->haveContent; ++it) {
it->haveContent = true;
if (elementHasTopMargin(*it)) {
if (!skippedLastEntry) {
skippedLastEntry = true;
} else {
ZLTextStyleEntry::SizeUnit unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
short size = 0;
if (it->styleIndex >= 0 && myStyleStack[it->styleIndex].TextStyle.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE)) {
size = myStyleStack[it->styleIndex].TextStyle.length(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, unit);
} else if (it->decoration) {
const ZLTextFullStyleDecoration *decoration = it->decoration->fullDecoration();
if (decoration) {
size = decoration->SpaceBeforeOption.value();
unit = decoration->SpaceBeforeOptionUnit;
}
}
if (size > 0) {
ZLTextStyleEntry style;
style.setLength(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, size, unit);
addStyleParagraph(style);
}
}
}
}
}
myBottomMargins.resize(0);
beginParagraph();
}
void XHTMLReader::characterDataHandler(const char *text, size_t len) {
@ -613,34 +751,49 @@ void XHTMLReader::characterDataHandler(const char *text, size_t len) {
}
break;
case READ_BODY:
if (myOpacityStack.back()) {
if (myPreformatted) {
if ((*text == '\r') || (*text == '\n')) {
myModelReader.addLineBreak();
}
if (myParseStack.back().opacity && !myStyleStack.empty() && !myStyleStack.back().DisplayNone) {
const StyleSheetTable::WhiteSpaceValue whiteSpace = myStyleStack.back().WhiteSpace;
if (myPreformatted || whiteSpace == StyleSheetTable::WS_PRE || whiteSpace == StyleSheetTable::WS_PRE_WRAP) {
size_t spaceCounter = 0;
while ((spaceCounter < len) && isspace((unsigned char)*(text + spaceCounter))) {
++spaceCounter;
}
myModelReader.addFixedHSpace(spaceCounter);
text += spaceCounter;
len -= spaceCounter;
} else if ((myNewParagraphInProgress) || !myModelReader.paragraphIsOpen()) {
while (isspace((unsigned char)*text)) {
while (len > 0 && isspace(*text)) {
if (*text == '\n') {
haveContent();
if (spaceCounter) {
myModelReader.addFixedHSpace(spaceCounter);
spaceCounter = 0;
}
myModelReader.addLineBreak();
} else if (*text != '\r') {
spaceCounter++;
}
++text;
if (--len == 0) {
--len;
}
if (spaceCounter) {
haveContent();
myModelReader.addFixedHSpace(spaceCounter);
}
} else if (!myModelReader.paragraphIsOpen()) {
while (len > 0 && isspace(*text)) {
switch (whiteSpace) {
case StyleSheetTable::WS_PRE:
case StyleSheetTable::WS_PRE_WRAP:
case StyleSheetTable::WS_PRE_LINE:
if (*text == '\n') {
haveContent();
myModelReader.addLineBreak();
}
break;
default:
break;
}
++text;
--len;
}
}
if (len > 0) {
myCurrentParagraphIsEmpty = false;
myElementHasContents.back() = true;
if (!myModelReader.paragraphIsOpen()) {
myModelReader.beginParagraph();
}
haveContent();
myModelReader.addData(std::string(text, len));
myNewParagraphInProgress = false;
}
}
break;

View file

@ -25,11 +25,13 @@
#include <vector>
#include <ZLXMLReader.h>
#include <ZLTextParagraph.h>
#include "../css/StyleSheetTable.h"
#include "../css/StyleSheetParser.h"
class ZLFile;
class ZLTextStyleDecoration;
class BookReader;
class XHTMLReader;
@ -63,7 +65,18 @@ public:
bool readFile(const ZLFile &file, const std::string &referenceName);
private:
void startElementHandler(const char *tag, const char **attributes);
struct ParseContext {
int kind;
int styleIndex;
unsigned char opacity;
bool haveContent;
bool stylesApplied;
bool bottomMarginApplied;
const ZLTextStyleDecoration *decoration;
ParseContext() : kind(-1), styleIndex(-1), opacity(255), haveContent(false), stylesApplied(false), bottomMarginApplied(false), decoration(NULL) {}
};
void startElementHandler(const char *tag, const char **attributes);
void endElementHandler(const char *tag);
void characterDataHandler(const char *text, size_t len);
@ -71,9 +84,16 @@ private:
bool processNamespaces() const;
void beginParagraph();
void haveContent();
void beginParagraph();
void endParagraph();
static shared_ptr<ZLTextStyleEntry> addStyleEntry(shared_ptr<ZLTextStyleEntry> entry, shared_ptr<ZLTextStyleEntry> styleEntry);
void applyStyles(ParseContext &context);
void addStyleParagraph(const ZLTextStyleEntry &style);
void addBottomMargin(short size, ZLTextStyleEntry::SizeUnit unit);
bool elementHasTopMargin(const ParseContext &context) const;
bool elementHasBottomMargin(const ParseContext &context) const;
void applyBottomMargins();
void addPageBreak();
private:
BookReader &myModelReader;
@ -81,13 +101,11 @@ private:
std::string myReferenceName;
std::string myReferenceDirName;
int myPreformatted;
bool myNewParagraphInProgress;
StyleSheetTable myStyleSheetTable;
StyleSheetTable::ElementList myElementStack;
StyleSheetTable::StyleList myStyleStack;
std::vector<unsigned char> myOpacityStack;
std::vector<bool> myElementHasContents;
bool myCurrentParagraphIsEmpty;
std::vector<ParseContext> myParseStack;
std::vector<ZLTextStyleEntry> myBottomMargins;
StyleSheetSingleStyleParser myStyleParser;
shared_ptr<StyleSheetTableParser> myTableParser;
enum {
@ -97,13 +115,13 @@ private:
} myReadState;
friend class XHTMLTagAction;
friend class XHTMLTagStyleAction;
friend class XHTMLTagLinkAction;
friend class XHTMLTagHyperlinkAction;
friend class XHTMLTagPreAction;
friend class XHTMLTagParagraphAction;
friend class XHTMLTagBodyAction;
friend class XHTMLTagRestartParagraphAction;
friend class XHTMLTagStyleAction;
friend class XHTMLTagLinkAction;
friend class XHTMLTagHyperlinkAction;
friend class XHTMLTagPreAction;
friend class XHTMLTagControlAction;
friend class XHTMLTagParagraphWithControlAction;
friend class XHTMLTagBodyAction;
friend class XHTMLTagImageAction;
};

View file

@ -45,6 +45,14 @@ bool ZLStringUtil::stringStartsWith(const std::string &str, const std::string &s
#endif
}
bool ZLStringUtil::caseInsensitiveEqual(const std::string &str1, const std::string &str2) {
return !strcasecmp(str1.c_str(), str2.c_str());
}
bool ZLStringUtil::caseInsensitiveSort(const std::string &str1, const std::string &str2) {
return strcasecmp(str1.c_str(), str2.c_str()) < 0;
}
void ZLStringUtil::appendNumber(std::string &str, unsigned int n) {
int len;
if (n > 0) {
@ -75,20 +83,29 @@ void ZLStringUtil::append(std::string &str, const std::vector<std::string> &text
}
}
void ZLStringUtil::stripWhiteSpaces(std::string &str) {
size_t counter = 0;
size_t length = str.length();
while ((counter < length) && isspace((unsigned char)str[counter])) {
counter++;
}
str.erase(0, counter);
length -= counter;
// Returns true if there's anything left
bool ZLStringUtil::stripWhiteSpaces(std::string &str) {
const size_t old_length = str.length();
if (old_length > 0) {
size_t end = old_length;
while ((end > 0) && isspace((unsigned char)str[end - 1])) {
end--;
}
if (end < old_length) {
str.erase(end, old_length - end);
}
size_t r_counter = length;
while ((r_counter > 0) && isspace((unsigned char)str[r_counter - 1])) {
r_counter--;
size_t start = 0;
while ((start < end) && isspace((unsigned char)str[start])) {
start++;
}
if (start > 0) {
str.erase(0, start);
}
return !str.empty();
} else {
return false;
}
str.erase(r_counter, length - r_counter);
}
std::vector<std::string> ZLStringUtil::splitString(const char *str, const char* delim) {
@ -106,6 +123,14 @@ std::vector<std::string> ZLStringUtil::splitString(const char *str, const char*
return tokens;
}
void ZLStringUtil::replaceAll(std::string &str, const std::string &find, const std::string &replaceWith) {
size_t pos = 0;
while ((pos == str.find(find, pos)) != std::string::npos) {
str.replace(pos, find.length(), replaceWith);
pos += replaceWith.length();
}
}
std::string ZLStringUtil::printf(const std::string &format, const std::string &arg0) {
int index = format.find("%s");
if (index == -1) {

View file

@ -31,11 +31,15 @@ private:
public:
static bool stringStartsWith(const std::string &str, const std::string &start);
static bool stringEndsWith(const std::string &str, const std::string &end);
static bool endsWith(const std::string &str, char c);
static bool caseInsensitiveEqual(const std::string &str1, const std::string &str2);
static bool caseInsensitiveSort(const std::string &str1, const std::string &str2);
static void appendNumber(std::string &str, unsigned int n);
static void append(std::string &str, const std::vector<std::string> &buffer);
static void stripWhiteSpaces(std::string &str);
static bool stripWhiteSpaces(std::string &str);
static std::vector<std::string> splitString(const char *str, const char* delim);
static std::vector<std::string> splitString(const std::string &str, const char* delim);
static void replaceAll(std::string &str, const std::string &find, const std::string &replaceWith);
static std::string printf(const std::string &format, const std::string &arg0);
@ -46,5 +50,7 @@ public:
inline std::vector<std::string> ZLStringUtil::splitString(const std::string &str, const char* delim) {
return ZLStringUtil::splitString(str.c_str(), delim);
}
inline bool ZLStringUtil::endsWith(const std::string &str, char c) {
return !str.empty() && str[str.length()-1] == c;
}
#endif /* __ZLSTRINGUTIL_H__ */

View file

@ -18,6 +18,10 @@
*/
#include "ZLPaintContext.h"
#include "ZLStringUtil.h"
#include <algorithm>
#include <strings.h>
ZLPaintContext::ZLPaintContext() {
}
@ -25,9 +29,29 @@ ZLPaintContext::ZLPaintContext() {
ZLPaintContext::~ZLPaintContext() {
}
std::string ZLPaintContext::pickFontFamily(const std::vector<std::string> &fonts) const
{
if (!fonts.empty()) {
if (fonts.size() > 1) {
const std::vector<std::string> &available = fontFamilies();
for (std::vector<std::string>::const_iterator it = fonts.begin(); it != fonts.end(); ++it) {
const std::vector<std::string>::const_iterator found =
std::lower_bound(available.begin(), available.end(), *it,
ZLStringUtil::caseInsensitiveSort);
if (found != available.end() && ZLStringUtil::caseInsensitiveEqual(*found, *it)) {
return *found;
}
}
}
return fonts.front();
}
return std::string();
}
const std::vector<std::string> &ZLPaintContext::fontFamilies() const {
if (myFamilies.empty()) {
fillFamiliesList(myFamilies);
std::sort(myFamilies.begin(), myFamilies.end(), ZLStringUtil::caseInsensitiveSort);
}
return myFamilies;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -22,6 +22,7 @@
#include <algorithm>
#include <ZLUnicodeUtil.h>
#include <ZLStringUtil.h>
#include <ZLImage.h>
#include "ZLTextParagraph.h"
@ -34,26 +35,46 @@ size_t ZLTextEntry::dataLength() const {
return len;
}
short ZLTextStyleEntry::length(Length name, const Metrics &metrics) const {
switch (myLengths[name].Unit) {
int ZLTextStyleEntry::hlength(int size, SizeUnit unit, const Metrics &metrics)
{
switch (unit) {
default:
case SIZE_UNIT_PIXEL:
return myLengths[name].Size;
return size;
case SIZE_UNIT_EM_100:
return (myLengths[name].Size * metrics.FontSize + 50) / 100;
return (size * metrics.FontSize + 50) / 100;
case SIZE_UNIT_EX_100:
return (myLengths[name].Size * metrics.FontXHeight + 50) / 100;
return (size * metrics.FontXHeight + 50) / 100;
case SIZE_UNIT_PERCENT:
switch (name) {
default:
case LENGTH_LEFT_INDENT:
case LENGTH_RIGHT_INDENT:
case LENGTH_FIRST_LINE_INDENT_DELTA:
return (myLengths[name].Size * metrics.FullWidth + 50) / 100;
case LENGTH_SPACE_BEFORE:
case LENGTH_SPACE_AFTER:
return (myLengths[name].Size * metrics.FullHeight + 50) / 100;
}
return (size * metrics.FullWidth + 50) / 100;
}
}
int ZLTextStyleEntry::vlength(int size, SizeUnit unit, const Metrics &metrics)
{
switch (unit) {
default:
case SIZE_UNIT_PIXEL:
return size;
case SIZE_UNIT_EM_100:
return (size * metrics.FontSize + 50) / 100;
case SIZE_UNIT_EX_100:
return (size * metrics.FontXHeight + 50) / 100;
case SIZE_UNIT_PERCENT:
return (size * metrics.FullHeight + 50) / 100;
}
}
short ZLTextStyleEntry::length(Length name, const Metrics &metrics) const {
switch (name) {
default:
case LENGTH_LEFT_INDENT:
case LENGTH_RIGHT_INDENT:
case LENGTH_FIRST_LINE_INDENT_DELTA:
return hlength(myLengths[name].Size, myLengths[name].Unit, metrics);
case LENGTH_SPACE_BEFORE:
case LENGTH_SPACE_AFTER:
return vlength(myLengths[name].Size, myLengths[name].Unit, metrics);
}
}
@ -80,43 +101,145 @@ ZLTextStyleEntry::ZLTextStyleEntry(char *address) {
if (fontSizeSupported()) {
myFontSizeMag = (signed char)*address++;
}
if (fontFamilySupported()) {
myFontFamily = address;
if (fontFamiliesSupported()) {
unsigned char n = *address++;
for (unsigned int i = 0; i < n; ++i) {
std::string font(address);
address += font.length() + 1;
myFontFamilies.push_back(font);
}
}
}
void ZLTextStyleEntry::reset() {
mySupportedFontModifier = 0;
myMask = 0;
myFontFamily.clear();
myFontFamilies.clear();
}
void ZLTextStyleEntry::apply(const ZLTextStyleEntry &entry) {
for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) {
if (entry.lengthSupported((Length)i)) {
myLengths[i] = entry.myLengths[i];
myMask |= 1 << i;
void ZLTextStyleEntry::apply(const ZLTextStyleEntry &other) {
if (other.myMask & ((1 << NUMBER_OF_LENGTHS) - 1)) {
for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) {
if (other.myMask & (1 << i)) {
myLengths[i] = other.myLengths[i];
myMask |= (1 << i);
}
}
}
if (entry.opacitySupported()) {
setOpacity(entry.opacity());
if (other.myMask & SUPPORT_OPACITY) {
setOpacity(other.myOpacity);
}
if (entry.alignmentTypeSupported()) {
setAlignmentType(entry.alignmentType());
if (other.myMask & SUPPORT_ALIGNMENT_TYPE) {
setAlignmentType(other.myAlignmentType);
}
if (entry.mySupportedFontModifier) {
myFontModifier &= ~entry.mySupportedFontModifier;
myFontModifier |= (entry.myFontModifier & entry.mySupportedFontModifier);
mySupportedFontModifier |= entry.mySupportedFontModifier;
if (other.mySupportedFontModifier) {
myFontModifier &= ~other.mySupportedFontModifier;
myFontModifier |= (other.myFontModifier & other.mySupportedFontModifier);
mySupportedFontModifier |= other.mySupportedFontModifier;
}
if (entry.fontSizeSupported()) {
setFontSizeMag(entry.fontSizeMag());
if (other.myMask & SUPPORT_FONT_SIZE) {
setFontSizeMag(other.myFontSizeMag);
}
if (entry.fontFamilySupported()) {
setFontFamily(entry.fontFamily());
if (other.myMask & SUPPORT_FONT_FAMILIES) {
setFontFamilies(other.myFontFamilies);
}
}
void ZLTextStyleEntry::inherit(const ZLTextStyleEntry &other) {
// text-indent
if (other.myMask & (1 << LENGTH_FIRST_LINE_INDENT_DELTA)) {
myLengths[LENGTH_FIRST_LINE_INDENT_DELTA] = other.myLengths[LENGTH_FIRST_LINE_INDENT_DELTA];
myMask |= (1 << LENGTH_FIRST_LINE_INDENT_DELTA);
}
// text-align
if (other.myMask & SUPPORT_ALIGNMENT_TYPE) {
setAlignmentType(other.myAlignmentType);
}
// font-style, font-variant, font-weight
if (other.mySupportedFontModifier) {
myFontModifier &= ~other.mySupportedFontModifier;
myFontModifier |= (other.myFontModifier & other.mySupportedFontModifier);
mySupportedFontModifier |= other.mySupportedFontModifier;
}
// font-size
if (other.myMask & SUPPORT_FONT_SIZE) {
setFontSizeMag(other.myFontSizeMag);
}
// font-family
if (other.myMask & SUPPORT_FONT_FAMILIES) {
setFontFamilies(other.myFontFamilies);
}
}
bool ZLTextStyleEntry::equals(const ZLTextStyleEntry &other) const {
if (myMask == other.myMask && mySupportedFontModifier == other.mySupportedFontModifier) {
if (myMask & ((1 << NUMBER_OF_LENGTHS) - 1)) {
for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) {
if (myMask & (1 << i)) {
if (myLengths[i].Size != other.myLengths[i].Size ||
myLengths[i].Unit != other.myLengths[i].Unit) {
return false;
}
}
}
}
if ((myMask & SUPPORT_OPACITY) && myOpacity != other.myOpacity) {
return false;
}
if ((myMask & SUPPORT_ALIGNMENT_TYPE) && myAlignmentType != other.myAlignmentType) {
return false;
}
if ((myFontModifier & mySupportedFontModifier) != (other.myFontModifier & mySupportedFontModifier)) {
return false;
}
if ((myMask & SUPPORT_FONT_SIZE) && myFontSizeMag != other.myFontSizeMag) {
return false;
}
if ((myMask & SUPPORT_FONT_FAMILIES) && myFontFamilies != other.myFontFamilies) {
return false;
}
return true;
} else {
return false;
}
}
bool ZLTextStyleEntry::parseLength(const std::string &toParse, short &size, SizeUnit &unit) {
if (!toParse.empty()) {
static const std::string PERCENT("%");
static const std::string ZERO("0");
static const std::string EM("em");
static const std::string EX("ex");
static const std::string PX("px");
static const std::string PT("pt");
static const std::string PC("pc");
if (ZLStringUtil::stringEndsWith(toParse, PERCENT)) {
unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT;
size = atoi(toParse.c_str());
return true;
} else if (ZLStringUtil::stringEndsWith(toParse, EM)) {
unit = ZLTextStyleEntry::SIZE_UNIT_EM_100;
size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0));
return true;
} else if (ZLStringUtil::stringEndsWith(toParse, EX)) {
unit = ZLTextStyleEntry::SIZE_UNIT_EX_100;
size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0));
return true;
} else if (ZLStringUtil::stringEndsWith(toParse, PX) ||
ZLStringUtil::stringEndsWith(toParse, PT) ||
ZLStringUtil::stringEndsWith(toParse, PC)) {
unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
size = atoi(toParse.c_str());
return true;
} else if (toParse == ZERO) {
unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
size = 0;
return true;
}
}
return false;
}
const shared_ptr<ZLTextParagraphEntry> ZLTextParagraph::Iterator::entry() const {
if (myEntry.isNull()) {
switch (*myPointer) {
@ -205,11 +328,11 @@ void ZLTextParagraph::Iterator::next() {
if (mask & ZLTextStyleEntry::SUPPORT_ALIGNMENT_TYPE) ++myPointer;
if (supportedFontModifier) ++myPointer;
if (mask & ZLTextStyleEntry::SUPPORT_FONT_SIZE) ++myPointer;
if (mask & ZLTextStyleEntry::SUPPORT_FONT_FAMILY) {
while (*myPointer != '\0') {
++myPointer;
if (mask & ZLTextStyleEntry::SUPPORT_FONT_FAMILIES) {
unsigned char n = *myPointer++;
for (unsigned int i = 0; i < n; ++i) {
while (*myPointer++);
}
++myPointer;
}
break;
}
@ -218,6 +341,7 @@ void ZLTextParagraph::Iterator::next() {
break;
case ZLTextParagraphEntry::RESET_BIDI_ENTRY:
case ZLTextParagraphEntry::LINE_BREAK_ENTRY:
case ZLTextParagraphEntry::EMPTY_ENTRY:
++myPointer;
break;
}

View file

@ -44,7 +44,8 @@ public:
STYLE_ENTRY = 5,
FIXED_HSPACE_ENTRY = 6,
RESET_BIDI_ENTRY = 7,
LINE_BREAK_ENTRY = 8
LINE_BREAK_ENTRY = 8,
EMPTY_ENTRY = 9
};
protected:
@ -99,15 +100,23 @@ public:
~ZLTextStyleEntry();
void reset();
void apply(const ZLTextStyleEntry &entry);
void apply(const ZLTextStyleEntry &other);
void inherit(const ZLTextStyleEntry &other);
bool equals(const ZLTextStyleEntry &other) const;
bool operator == (const ZLTextStyleEntry &other) const;
ZLTextStyleEntry &operator = (const ZLTextStyleEntry &other);
bool isEmpty() const;
bool lengthSupported(Length name) const;
short length(Length name, const Metrics &metrics) const;
short length(Length name, SizeUnit &unit) const;
void setLength(Length name, short length, SizeUnit unit);
static int hlength(int size, SizeUnit unit, const Metrics &metrics);
static int vlength(int size, SizeUnit unit, const Metrics &metrics);
static bool parseLength(const std::string &toParse, short &size, SizeUnit &unit);
bool opacitySupported() const;
unsigned char opacity() const;
void setOpacity(unsigned char opacity);
@ -124,14 +133,14 @@ public:
signed char fontSizeMag() const;
void setFontSizeMag(signed char fontSizeMag);
bool fontFamilySupported() const;
const std::string &fontFamily() const;
void setFontFamily(const std::string &fontFamily);
bool fontFamiliesSupported() const;
const std::vector<std::string> &fontFamilies() const;
void setFontFamilies(const std::vector<std::string> &fontFamilies);
enum {
SUPPORT_ALIGNMENT_TYPE = 1 << NUMBER_OF_LENGTHS,
SUPPORT_FONT_SIZE = 1 << (NUMBER_OF_LENGTHS + 1),
SUPPORT_FONT_FAMILY = 1 << (NUMBER_OF_LENGTHS + 2),
SUPPORT_FONT_FAMILIES = 1 << (NUMBER_OF_LENGTHS + 2),
SUPPORT_OPACITY = 1 << (NUMBER_OF_LENGTHS + 3)
};
@ -145,7 +154,7 @@ private:
unsigned char mySupportedFontModifier;
unsigned char myFontModifier;
signed char myFontSizeMag;
std::string myFontFamily;
std::vector<std::string> myFontFamilies;
friend class ZLTextModel;
};
@ -353,9 +362,11 @@ inline ZLTextStyleEntry::~ZLTextStyleEntry() {}
inline ZLTextStyleEntry::Metrics::Metrics(int fontSize, int fontXHeight, int fullWidth, int fullHeight) : FontSize(fontSize), FontXHeight(fontXHeight), FullWidth(fullWidth), FullHeight(fullHeight) {}
inline bool ZLTextStyleEntry::isEmpty() const { return myMask == 0 && mySupportedFontModifier == 0; }
inline bool ZLTextStyleEntry::operator == (const ZLTextStyleEntry &other) const { return equals(other); }
inline ZLTextStyleEntry &ZLTextStyleEntry::operator = (const ZLTextStyleEntry &other) { reset(); apply(other); return *this; }
inline bool ZLTextStyleEntry::lengthSupported(Length name) const { return (myMask & (1 << name)) != 0; }
inline short ZLTextStyleEntry::length(Length name, SizeUnit &unit) const { unit = myLengths[name].Unit; return myLengths[name].Size; }
inline void ZLTextStyleEntry::setLength(Length name, short length, SizeUnit unit) {
myLengths[name].Size = length;
myLengths[name].Unit = unit;
@ -385,9 +396,9 @@ inline bool ZLTextStyleEntry::fontSizeSupported() const { return (myMask & SUPPO
inline signed char ZLTextStyleEntry::fontSizeMag() const { return myFontSizeMag; }
inline void ZLTextStyleEntry::setFontSizeMag(signed char fontSizeMag) { myFontSizeMag = fontSizeMag; myMask |= SUPPORT_FONT_SIZE; }
inline bool ZLTextStyleEntry::fontFamilySupported() const { return (myMask & SUPPORT_FONT_FAMILY) == SUPPORT_FONT_FAMILY; }
inline const std::string &ZLTextStyleEntry::fontFamily() const { return myFontFamily; }
inline void ZLTextStyleEntry::setFontFamily(const std::string &fontFamily) { myFontFamily = fontFamily; myMask |= SUPPORT_FONT_FAMILY; }
inline bool ZLTextStyleEntry::fontFamiliesSupported() const { return (myMask & SUPPORT_FONT_FAMILIES) == SUPPORT_FONT_FAMILIES; }
inline const std::vector<std::string> &ZLTextStyleEntry::fontFamilies() const { return myFontFamilies; }
inline void ZLTextStyleEntry::setFontFamilies(const std::vector<std::string> &fontFamilies) { myFontFamilies = fontFamilies; myMask |= SUPPORT_FONT_FAMILIES; }
inline ZLTextControlEntry::ZLTextControlEntry(ZLTextKind kind, bool isStart) : myKind(kind), myStart(isStart) {}
inline ZLTextControlEntry::~ZLTextControlEntry() {}

View file

@ -30,7 +30,7 @@ ZLTextStyleDecoration::ZLTextStyleDecoration(const std::string &name, int fontSi
ItalicOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":italic", italic),
VerticalShiftOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":vShift", verticalShift),
AllowHyphenationsOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":allowHyphenations", allowHyphenations),
myName(name) {
myName(name), myFirstLineIndentDelta(NULL), myFirstLineIndentDeltaUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL) {
}
ZLTextFullStyleDecoration::ZLTextFullStyleDecoration(const std::string &name, int fontSizeDelta, ZLBoolean3 bold, ZLBoolean3 italic, short spaceBefore, short spaceAfter, short lineStartIndent, short lineEndIndent, short firstLineIndentDelta, int verticalShift, ZLTextAlignmentType alignment, double lineSpace, ZLBoolean3 allowHyphenations) : ZLTextStyleDecoration(name, fontSizeDelta, bold, italic, verticalShift, allowHyphenations),
@ -38,23 +38,74 @@ ZLTextFullStyleDecoration::ZLTextFullStyleDecoration(const std::string &name, in
SpaceAfterOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":spaceAfter", -10, 100, spaceAfter),
LineStartIndentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":leftIndent", -300, 300, lineStartIndent),
LineEndIndentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":rightIndent", -300, 300, lineEndIndent),
FirstLineIndentDeltaOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":firstLineIndentDelta", -300, 300, firstLineIndentDelta),
AlignmentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":alignment", alignment),
LineSpaceOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":lineSpace", lineSpace),
LineSpacePercentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":lineSpacePercent", (lineSpace == 0.0) ? -1 : (int)(lineSpace * 100)) {
LineSpacePercentOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":lineSpacePercent", (lineSpace == 0.0) ? -1 : (int)(lineSpace * 100)),
FirstLineIndentDeltaOption(ZLCategoryKey::LOOK_AND_FEEL, STYLE, name + ":firstLineIndentDelta", -300, 300, firstLineIndentDelta),
SpaceBeforeOptionUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL),
SpaceAfterOptionUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL),
LineStartIndentOptionUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL),
LineEndIndentOptionUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL),
FirstLineIndentDeltaOptionUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL) {
}
const ZLTextFullStyleDecoration *ZLTextStyleDecoration::fullDecoration() const {
return NULL;
}
shared_ptr<ZLTextStyle> ZLTextStyleDecoration::createDecoratedStyle(const shared_ptr<ZLTextStyle> base) const {
return new ZLTextPartialDecoratedStyle(base, *this);
}
const ZLTextFullStyleDecoration *ZLTextFullStyleDecoration::fullDecoration() const {
return this;
}
shared_ptr<ZLTextStyle> ZLTextFullStyleDecoration::createDecoratedStyle(const shared_ptr<ZLTextStyle> base) const {
return new ZLTextFullDecoratedStyle(base, *this);
}
const std::string &ZLTextPartialDecoratedStyle::fontFamily() const {
short ZLTextFullDecoratedStyle::spaceBefore(const ZLTextStyleEntry::Metrics &metrics) const {
return ZLTextStyleEntry::vlength(myDecoration.SpaceBeforeOption.value(), myDecoration.SpaceBeforeOptionUnit, metrics);
}
short ZLTextFullDecoratedStyle::spaceAfter(const ZLTextStyleEntry::Metrics &metrics) const {
return ZLTextStyleEntry::vlength(myDecoration.SpaceAfterOption.value(), myDecoration.SpaceAfterOptionUnit, metrics);
}
short ZLTextFullDecoratedStyle::lineStartIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const {
return base()->lineEndIndent(metrics, rtl) + (rtl ?
ZLTextStyleEntry::hlength(myDecoration.LineEndIndentOption.value(), myDecoration.LineEndIndentOptionUnit, metrics) :
ZLTextStyleEntry::hlength(myDecoration.LineStartIndentOption.value(), myDecoration.LineStartIndentOptionUnit, metrics));
}
short ZLTextFullDecoratedStyle::lineEndIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const {
return base()->lineStartIndent(metrics, rtl) + (rtl ?
ZLTextStyleEntry::hlength(myDecoration.LineStartIndentOption.value(), myDecoration.LineStartIndentOptionUnit, metrics) :
ZLTextStyleEntry::hlength(myDecoration.LineEndIndentOption.value(), myDecoration.LineEndIndentOptionUnit, metrics));
}
int ZLTextFullStyleDecoration::firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const {
ZLTextStyleEntry::SizeUnit unit;
int size;
int* indent = ZLTextStyleDecoration::firstLineIndentDelta(unit);
if (indent) {
size = *indent;
} else {
size = FirstLineIndentDeltaOption.value();
unit = FirstLineIndentDeltaOptionUnit;
}
return ZLTextStyleEntry::hlength(size, unit, metrics);
}
ZLTextPartialDecoratedStyle::ZLTextPartialDecoratedStyle(const shared_ptr<ZLTextStyle> base, const ZLTextStyleDecoration &decoration) :
ZLTextDecoratedStyle(base), myDecoration(decoration) {
const std::string &family = myDecoration.FontFamilyOption.value();
return (!family.empty()) ? family : base()->fontFamily();
if (!family.empty()) myFontFamilies.push_back(family);
}
const std::vector<std::string> &ZLTextPartialDecoratedStyle::fontFamilies() const {
return (!myFontFamilies.empty()) ? myFontFamilies : base()->fontFamilies();
}
int ZLTextPartialDecoratedStyle::fontSize() const {
@ -74,16 +125,26 @@ bool ZLTextPartialDecoratedStyle::italic() const {
bool ZLTextPartialDecoratedStyle::allowHyphenations() const {
ZLBoolean3 a = myDecoration.AllowHyphenationsOption.value();
return (a == B3_UNDEFINED) ? base()->allowHyphenations() : (a == B3_TRUE);
return true;
}
short ZLTextPartialDecoratedStyle::firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const {
ZLTextStyleEntry::SizeUnit unit;
int* indent = myDecoration.firstLineIndentDelta(unit);
return indent ? ZLTextStyleEntry::hlength(*indent, unit, metrics) :base()->firstLineIndentDelta(metrics);
}
ZLTextFullDecoratedStyle::ZLTextFullDecoratedStyle(const shared_ptr<ZLTextStyle> base, const ZLTextFullStyleDecoration &decoration) :
ZLTextDecoratedStyle(base), myDecoration(decoration) {
const std::string &family = myDecoration.FontFamilyOption.value();
if (!family.empty()) myFontFamilies.push_back(family);
}
short ZLTextFullDecoratedStyle::firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const {
return (alignment() == ALIGN_CENTER) ? 0 : base()->firstLineIndentDelta(metrics) + myDecoration.FirstLineIndentDeltaOption.value();
return (alignment() == ALIGN_CENTER) ? 0 : myDecoration.firstLineIndentDelta(metrics);
}
const std::string &ZLTextFullDecoratedStyle::fontFamily() const {
const std::string &family = myDecoration.FontFamilyOption.value();
return (!family.empty()) ? family : base()->fontFamily();
const std::vector<std::string> &ZLTextFullDecoratedStyle::fontFamilies() const {
return (!myFontFamilies.empty()) ? myFontFamilies : base()->fontFamilies();
}
int ZLTextFullDecoratedStyle::fontSize() const {
@ -108,7 +169,6 @@ ZLTextAlignmentType ZLTextFullDecoratedStyle::alignment() const {
bool ZLTextFullDecoratedStyle::allowHyphenations() const {
ZLBoolean3 a = myDecoration.AllowHyphenationsOption.value();
return (a == B3_UNDEFINED) ? base()->allowHyphenations() : (a == B3_TRUE);
return true;
}
const std::string &ZLTextPartialDecoratedStyle::colorStyle() const {
@ -150,25 +210,17 @@ short ZLTextForcedStyle::lineEndIndent(const ZLTextStyleEntry::Metrics &metrics,
}
short ZLTextForcedStyle::spaceBefore(const ZLTextStyleEntry::Metrics &metrics) const {
if (myEntry.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE)) {
const short baseSpace = base()->spaceBefore(metrics);
ZLTextStyleEntry::Metrics adjusted(metrics);
adjusted.FullHeight -= baseSpace + base()->spaceAfter(metrics);
return baseSpace + myEntry.length(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, adjusted);
} else {
return base()->spaceBefore(metrics);
}
return
myEntry.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE) ?
myEntry.length(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, metrics) :
base()->spaceBefore(metrics);
}
short ZLTextForcedStyle::spaceAfter(const ZLTextStyleEntry::Metrics &metrics) const {
if (myEntry.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_AFTER)) {
const short baseSpace = base()->spaceBefore(metrics);
ZLTextStyleEntry::Metrics adjusted(metrics);
adjusted.FullHeight -= baseSpace + base()->spaceBefore(metrics);
return baseSpace + myEntry.length(ZLTextStyleEntry::LENGTH_SPACE_AFTER, adjusted);
} else {
return base()->spaceAfter(metrics);
}
return
myEntry.lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_AFTER) ?
myEntry.length(ZLTextStyleEntry::LENGTH_SPACE_AFTER, metrics) :
base()->spaceAfter(metrics);
}
short ZLTextForcedStyle::firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const {
@ -221,10 +273,10 @@ int ZLTextForcedStyle::fontSize() const {
}
}
const std::string &ZLTextForcedStyle::fontFamily() const {
const std::vector<std::string> &ZLTextForcedStyle::fontFamilies() const {
return (!ZLTextStyleCollection::Instance().OverrideSpecifiedFontsOption.value() &&
myEntry.fontFamilySupported()) ?
myEntry.fontFamily() : base()->fontFamily();
myEntry.fontFamiliesSupported()) ?
myEntry.fontFamilies() : base()->fontFamilies();
}
const std::string &ZLTextStyleDecoration::colorStyle() const {

View file

@ -46,7 +46,7 @@ public:
ZLTextForcedStyle(shared_ptr<ZLTextStyle> base, const ZLTextStyleEntry &entry);
~ZLTextForcedStyle();
const std::string &fontFamily() const;
const std::vector<std::string> &fontFamilies() const;
int fontSize() const;
bool bold() const;
@ -77,7 +77,7 @@ private:
public:
virtual ~ZLTextPartialDecoratedStyle();
const std::string &fontFamily() const;
const std::vector<std::string> &fontFamilies() const;
int fontSize() const;
bool bold() const;
bool italic() const;
@ -98,6 +98,7 @@ public:
private:
const ZLTextStyleDecoration &myDecoration;
std::vector<std::string> myFontFamilies;
};
class ZLTextFullDecoratedStyle : public ZLTextDecoratedStyle {
@ -108,7 +109,7 @@ private:
public:
~ZLTextFullDecoratedStyle();
const std::string &fontFamily() const;
const std::vector<std::string> &fontFamilies() const;
int fontSize() const;
bool bold() const;
bool italic() const;
@ -129,6 +130,7 @@ public:
private:
const ZLTextFullStyleDecoration &myDecoration;
std::vector<std::string> myFontFamilies;
};
inline ZLTextDecoratedStyle::ZLTextDecoratedStyle(const shared_ptr<ZLTextStyle> base) : myBase(base) {}
@ -143,23 +145,16 @@ inline int ZLTextForcedStyle::verticalShift() const { return base()->verticalShi
inline double ZLTextForcedStyle::lineSpace() const { return base()->lineSpace(); }
inline bool ZLTextForcedStyle::allowHyphenations() const { return base()->allowHyphenations(); }
inline ZLTextPartialDecoratedStyle::ZLTextPartialDecoratedStyle(const shared_ptr<ZLTextStyle> base, const ZLTextStyleDecoration &decoration) : ZLTextDecoratedStyle(base), myDecoration(decoration) {}
inline ZLTextPartialDecoratedStyle::~ZLTextPartialDecoratedStyle() {}
inline short ZLTextPartialDecoratedStyle::spaceBefore(const ZLTextStyleEntry::Metrics &metrics) const { return base()->spaceBefore(metrics); }
inline short ZLTextPartialDecoratedStyle::spaceAfter(const ZLTextStyleEntry::Metrics &metrics) const { return base()->spaceAfter(metrics); }
inline short ZLTextPartialDecoratedStyle::lineStartIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const { return base()->lineStartIndent(metrics, rtl); }
inline short ZLTextPartialDecoratedStyle::lineEndIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const { return base()->lineEndIndent(metrics, rtl); }
inline short ZLTextPartialDecoratedStyle::firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const { return base()->firstLineIndentDelta(metrics); }
inline int ZLTextPartialDecoratedStyle::verticalShift() const { return base()->verticalShift() + myDecoration.VerticalShiftOption.value(); }
inline ZLTextAlignmentType ZLTextPartialDecoratedStyle::alignment() const { return base()->alignment(); }
inline double ZLTextPartialDecoratedStyle::lineSpace() const { return base()->lineSpace(); }
inline ZLTextFullDecoratedStyle::ZLTextFullDecoratedStyle(const shared_ptr<ZLTextStyle> base, const ZLTextFullStyleDecoration &decoration) : ZLTextDecoratedStyle(base), myDecoration(decoration) {}
inline ZLTextFullDecoratedStyle::~ZLTextFullDecoratedStyle() {}
inline short ZLTextFullDecoratedStyle::spaceBefore(const ZLTextStyleEntry::Metrics&) const { return myDecoration.SpaceBeforeOption.value(); }
inline short ZLTextFullDecoratedStyle::spaceAfter(const ZLTextStyleEntry::Metrics&) const { return myDecoration.SpaceAfterOption.value(); }
inline short ZLTextFullDecoratedStyle::lineStartIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const { return base()->lineStartIndent(metrics, rtl) + myDecoration.LineStartIndentOption.value(); }
inline short ZLTextFullDecoratedStyle::lineEndIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const { return base()->lineEndIndent(metrics, rtl) + myDecoration.LineEndIndentOption.value(); }
inline int ZLTextFullDecoratedStyle::verticalShift() const { return base()->verticalShift() + myDecoration.VerticalShiftOption.value(); }
inline double ZLTextFullDecoratedStyle::lineSpace() const {
const int spacing = myDecoration.LineSpacePercentOption.value();

View file

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

View file

@ -19,6 +19,8 @@
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <limits.h>
#include <ZLibrary.h>
#include <ZLFile.h>
@ -52,6 +54,7 @@ public:
void startElementHandler(const char *tag, const char **attributes);
private:
int lengthValue(const char **attributes, const char *name, ZLTextStyleEntry::SizeUnit &unit, int defaultValue = 0);
int intValue(const char **attributes, const char *name, int defaultValue = 0);
bool booleanValue(const char **attributes, const char *name);
ZLBoolean3 b3Value(const char **attributes, const char *name);
@ -62,6 +65,31 @@ private:
static const std::string TRUE_STRING = "true";
int ZLTextStyleReader::lengthValue(const char **attributes, const char *name, ZLTextStyleEntry::SizeUnit &unit, int defaultValue) {
const char *stringValue = attributeValue(attributes, name);
if (stringValue) {
short size;
if (ZLTextStyleEntry::parseLength(stringValue, size, unit)) {
return size;
} else {
const char *s = stringValue;
while (*s && isspace(*s)) s++;
char* endptr = NULL;
long number = strtol(s, &endptr, 10);
if (endptr && endptr != s) {
if ((number != LONG_MAX && number != LONG_MIN) || (errno != ERANGE)) {
while (*endptr && isspace(*endptr)) endptr++;
if (!*endptr) {
unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
return number;
}
}
}
}
}
return defaultValue;
}
inline int ZLTextStyleReader::intValue(const char **attributes, const char *name, int defaultValue) {
const char *stringValue = attributeValue(attributes, name);
return (stringValue == 0) ? defaultValue : atoi(stringValue);
@ -95,12 +123,25 @@ void ZLTextStyleReader::startElementHandler(const char *tag, const char **attrib
if (booleanValue(attributes, "partial")) {
decoration = new ZLTextStyleDecoration(name, fontSizeDelta, bold, italic, verticalShift, allowHyphenations);
const char *indentValue = attributeValue(attributes, "firstLineIndentDelta");
if (indentValue) {
short size;
ZLTextStyleEntry::SizeUnit unit;
if (ZLTextStyleEntry::parseLength(indentValue, size, unit)) {
decoration->setFirstLineIndentDelta(size, unit);
}
}
} else {
int spaceBefore = intValue(attributes, "spaceBefore");
int spaceAfter = intValue(attributes, "spaceAfter");
int leftIndent = intValue(attributes, "leftIndent");
int rightIndent = intValue(attributes, "rightIndent");
int firstLineIndentDelta = intValue(attributes, "firstLineIndentDelta");
ZLTextStyleEntry::SizeUnit spaceBeforeUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
ZLTextStyleEntry::SizeUnit spaceAfterUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
ZLTextStyleEntry::SizeUnit leftIndentUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
ZLTextStyleEntry::SizeUnit rightIndentUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
ZLTextStyleEntry::SizeUnit firstLineIndentDeltaUnit(ZLTextStyleEntry::SIZE_UNIT_PIXEL);
int spaceBefore = lengthValue(attributes, "spaceBefore", spaceBeforeUnit);
int spaceAfter = lengthValue(attributes, "spaceAfter", spaceAfterUnit);
int leftIndent = lengthValue(attributes, "leftIndent", leftIndentUnit);
int rightIndent = lengthValue(attributes, "rightIndent", rightIndentUnit);
int firstLineIndentDelta = lengthValue(attributes, "firstLineIndentDelta", firstLineIndentDeltaUnit);
ZLTextAlignmentType alignment = ALIGN_UNDEFINED;
const char *alignmentString = attributeValue(attributes, "alignment");
@ -120,7 +161,13 @@ void ZLTextStyleReader::startElementHandler(const char *tag, const char **attrib
const int lineSpacingPercent = intValue(attributes, "lineSpacingPercent", -1);
const double lineSpace = (lineSpacingPercent == -1) ? 0.0 : (lineSpacingPercent / 100.0);
decoration = new ZLTextFullStyleDecoration(name, fontSizeDelta, bold, italic, spaceBefore, spaceAfter, leftIndent, rightIndent, firstLineIndentDelta, verticalShift, alignment, lineSpace, allowHyphenations);
ZLTextFullStyleDecoration *dec = new ZLTextFullStyleDecoration(name, fontSizeDelta, bold, italic, spaceBefore, spaceAfter, leftIndent, rightIndent, firstLineIndentDelta, verticalShift, alignment, lineSpace, allowHyphenations);
dec->SpaceBeforeOptionUnit = spaceBeforeUnit;
dec->SpaceAfterOptionUnit = spaceAfterUnit;
dec->LineStartIndentOptionUnit = leftIndentUnit;
dec->LineEndIndentOptionUnit = rightIndentUnit;
dec->FirstLineIndentDeltaOptionUnit = firstLineIndentDeltaUnit;
decoration = dec;
}
const char *hyperlink = attributeValue(attributes, "hyperlink");
if (hyperlink != 0) {

View file

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

View file

@ -114,7 +114,7 @@ size_t ZLTextView::PositionIndicator::endTextIndex() const {
}
void ZLTextView::PositionIndicator::drawExtraText(const std::string &text) {
context().setFont(myTextView.baseStyle()->fontFamily(), myInfo.fontSize(), false, false);
context().setFont(context().pickFontFamily(myTextView.baseStyle()->fontFamilies()), myInfo.fontSize(), false, false);
context().setColor(myTextView.color());
int width = context().stringWidth(text.data(), text.length(), false);