Major refactoring of CSS support

Also, improved support for line breaks.
This commit is contained in:
Slava Monich 2015-07-07 18:30:51 +03:00
parent a7e40b8d34
commit 248b307696
21 changed files with 649 additions and 368 deletions

View file

@ -109,6 +109,12 @@ void BookReader::addFixedHSpace(unsigned char length) {
} }
} }
void BookReader::addLineBreak() {
if (myTextParagraphExists) {
myCurrentTextModel->addLineBreak();
}
}
void BookReader::addControl(const ZLTextStyleEntry &entry) { void BookReader::addControl(const ZLTextStyleEntry &entry) {
if (myTextParagraphExists) { if (myTextParagraphExists) {
flushTextBufferToParagraph(); flushTextBufferToParagraph();

View file

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

View file

@ -28,26 +28,22 @@
StyleSheetTableParser::StyleSheetTableParser(StyleSheetTable &table) : myTable(table) { StyleSheetTableParser::StyleSheetTableParser(StyleSheetTable &table) : myTable(table) {
} }
void StyleSheetTableParser::storeData(const std::string &tagName, const std::string &className, const StyleSheetTable::AttributeMap &map) { void StyleSheetTableParser::storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map) {
myTable.addMap(tagName, className, map); myTable.addMap(ZLStringUtil::splitString(selector, " \t+"), map);
} }
shared_ptr<ZLTextStyleEntry> StyleSheetSingleStyleParser::parseString(const char *text, ZLBoolean3 *pageBreakBefore, ZLBoolean3 *pageBreakAfter) { StyleSheetTable::Style StyleSheetSingleStyleParser::parseString(const char *text) {
StyleSheetTable::Style style;
if (text) {
myReadState = ATTRIBUTE_NAME; myReadState = ATTRIBUTE_NAME;
parse(text, strlen(text), true); parse(text, strlen(text), true);
shared_ptr<ZLTextStyleEntry> control = StyleSheetTable::createControl(NULL, myMap); StyleSheetTable::updateStyle(style, myMap);
bool value;
if (pageBreakBefore && StyleSheetTable::getPageBreakBefore(myMap, value)) {
*pageBreakBefore = value ? B3_TRUE : B3_FALSE;
}
if (pageBreakAfter && StyleSheetTable::getPageBreakAfter(myMap, value)) {
*pageBreakAfter = value ? B3_TRUE : B3_FALSE;
}
reset(); reset();
return control; }
return style;
} }
StyleSheetParser::StyleSheetParser() : myReadState(TAG_NAME), myInsideComment(false) { StyleSheetParser::StyleSheetParser() : myReadState(TAG_NAME), myInsideComment(false), myAtBlockDepth(0) {
} }
StyleSheetParser::~StyleSheetParser() { StyleSheetParser::~StyleSheetParser() {
@ -58,6 +54,7 @@ void StyleSheetParser::reset() {
myAttributeName.erase(); myAttributeName.erase();
myReadState = TAG_NAME; myReadState = TAG_NAME;
myInsideComment = false; myInsideComment = false;
myAtBlockDepth = 0;
mySelectors.clear(); mySelectors.clear();
myMap.clear(); myMap.clear();
} }
@ -81,7 +78,8 @@ void StyleSheetParser::parse(const char *text, int len, bool final) {
const char *start = text; const char *start = text;
const char *end = text + len; const char *end = text + len;
for (const char *ptr = start; ptr != end; ++ptr) { for (const char *ptr = start; ptr != end; ++ptr) {
if (isspace(*ptr)) { if ((myReadState != TAG_NAME && isspace(*ptr)) ||
(myReadState == TAG_NAME && *ptr == ',')) {
if (start != ptr) { if (start != ptr) {
myWord.append(start, ptr - start); myWord.append(start, ptr - start);
} }
@ -121,40 +119,78 @@ bool StyleSheetParser::isControlSymbol(const char symbol) {
} }
} }
void StyleSheetParser::storeData(const std::string&, const std::string&, const StyleSheetTable::AttributeMap&) { void StyleSheetParser::storeData(const std::string&, const StyleSheetTable::AttributeMap&) {
} }
void StyleSheetParser::processControl(const char control) { void StyleSheetParser::processControl(const char control) {
switch (control) { switch (control) {
case '{': case '{':
myReadState = (myReadState == TAG_NAME) ? ATTRIBUTE_NAME : BROKEN; 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;
}
break; break;
case '}': case '}':
if (myReadState != BROKEN) { 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++) { for (unsigned int i=0; i<mySelectors.size(); i++) {
std::string selector(mySelectors[i]); storeData(mySelectors[i], myMap);
std::string tag, klass;
const int index = selector.find('.');
if (index == -1) {
tag = selector;
} else {
tag = selector.substr(0, index);
klass = selector.substr(index + 1);
}
storeData(tag, klass, myMap);
} }
break;
} }
myReadState = TAG_NAME; myReadState = TAG_NAME;
mySelectors.clear(); mySelectors.clear();
myMap.clear(); myMap.clear();
break; break;
case ';': case ';':
myReadState = switch (myReadState) {
((myReadState == ATTRIBUTE_VALUE) || case AT_RULE:
(myReadState == ATTRIBUTE_NAME)) ? ATTRIBUTE_NAME : BROKEN; myReadState = TAG_NAME;
mySelectors.clear();
myMap.clear();
break;
case AT_BLOCK:
break;
case ATTRIBUTE_VALUE:
case ATTRIBUTE_NAME:
myReadState = ATTRIBUTE_NAME;
break;
default:
myReadState = BROKEN;
break;
}
break; break;
case ':': case ':':
myReadState = (myReadState == ATTRIBUTE_NAME) ? ATTRIBUTE_VALUE : BROKEN; switch (myReadState) {
case AT_BLOCK:
break;
case ATTRIBUTE_NAME:
myReadState = ATTRIBUTE_VALUE;
break;
default:
myReadState = BROKEN;
break;
}
break; break;
} }
} }
@ -177,18 +213,21 @@ void StyleSheetParser::processWord(std::string &word) {
} }
} }
void StyleSheetParser::processWordWithoutComments(const std::string &word) { void StyleSheetParser::processWordWithoutComments(std::string word) {
switch (myReadState) { switch (myReadState) {
case AT_RULE:
case AT_BLOCK:
break;
case TAG_NAME: case TAG_NAME:
ZLStringUtil::stripWhiteSpaces(word);
if (!word.empty()) { if (!word.empty()) {
const unsigned int len = word.length(); if (word[0] == '@') {
if (word.at(len-1) == ',') { myReadState = AT_RULE;
mySelectors.push_back(word.substr(0, len-1));
} else { } else {
mySelectors.push_back(word); mySelectors.push_back(word);
} }
} }
myMap.clear();
break; break;
case ATTRIBUTE_NAME: case ATTRIBUTE_NAME:
myAttributeName = word; myAttributeName = word;

View file

@ -21,7 +21,6 @@
#define __STYLESHEETPARSER_H__ #define __STYLESHEETPARSER_H__
#include "StyleSheetTable.h" #include "StyleSheetTable.h"
#include "ZLBoolean3.h"
class ZLInputStream; class ZLInputStream;
@ -37,24 +36,27 @@ public:
void parse(const char *text, int len, bool final = false); void parse(const char *text, int len, bool final = false);
protected: protected:
virtual void storeData(const std::string &tagName, const std::string &className, const StyleSheetTable::AttributeMap &map); virtual void storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map);
private: private:
bool isControlSymbol(const char symbol); bool isControlSymbol(const char symbol);
void processWord(std::string &word); void processWord(std::string &word);
void processWordWithoutComments(const std::string &word); void processWordWithoutComments(std::string word);
void processControl(const char control); void processControl(const char control);
private: private:
std::string myWord; std::string myWord;
std::string myAttributeName; std::string myAttributeName;
enum { enum {
AT_RULE,
AT_BLOCK,
TAG_NAME, TAG_NAME,
ATTRIBUTE_NAME, ATTRIBUTE_NAME,
ATTRIBUTE_VALUE, ATTRIBUTE_VALUE,
BROKEN, BROKEN,
} myReadState; } myReadState;
bool myInsideComment; bool myInsideComment;
int myAtBlockDepth;
std::vector<std::string> mySelectors; std::vector<std::string> mySelectors;
StyleSheetTable::AttributeMap myMap; StyleSheetTable::AttributeMap myMap;
@ -67,7 +69,7 @@ public:
StyleSheetTableParser(StyleSheetTable &table); StyleSheetTableParser(StyleSheetTable &table);
private: private:
void storeData(const std::string &tagName, const std::string &className, const StyleSheetTable::AttributeMap &map); void storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map);
private: private:
StyleSheetTable &myTable; StyleSheetTable &myTable;
@ -76,7 +78,7 @@ private:
class StyleSheetSingleStyleParser : public StyleSheetParser { class StyleSheetSingleStyleParser : public StyleSheetParser {
public: public:
shared_ptr<ZLTextStyleEntry> parseString(const char *text, ZLBoolean3* pageBreakBefore, ZLBoolean3* pageBreakAfter); StyleSheetTable::Style parseString(const char *text);
}; };
#endif /* __STYLESHEETPARSER_H__ */ #endif /* __STYLESHEETPARSER_H__ */

View file

@ -18,32 +18,99 @@
*/ */
#include <cstdlib> #include <cstdlib>
#include <algorithm>
#include <ZLStringUtil.h> #include <ZLStringUtil.h>
#include "StyleSheetTable.h" #include "StyleSheetTable.h"
bool StyleSheetTable::isEmpty() const { static const std::string WILDCARD("*");
return myControlMap.empty() && myPageBreakBeforeMap.empty() && myPageBreakAfterMap.empty();
StyleSheetTable::Element::Element(const std::string &tag, const char *klass, const char* id) :
Name(tag), Classes(ZLStringUtil::splitString(klass, ", \n")) {
if (id) Id = id;
} }
void StyleSheetTable::addMap(const std::string &tag, const std::string &aClass, const AttributeMap &map) { StyleSheetTable::Selector::Selector(const std::string &selector) {
if ((!tag.empty() || !aClass.empty()) && !map.empty()) { std::string buf(selector);
Key key(tag, aClass); const int dot = buf.find('.');
// This will update the existing element if it already exists if (dot >= 0) {
// or create a new one if it wasn't there yet myClass = buf.substr(dot + 1);
myControlMap[key] = createControl(myControlMap[key], map); buf = buf.substr(0, dot);
bool value;
if (getPageBreakBefore(map, value)) {
myPageBreakBeforeMap[key] = value;
} }
if (getPageBreakAfter(map, value)) { const int hash = buf.find('#');
myPageBreakAfterMap[key] = value; if (hash < 0) {
myType = buf;
} else {
myType = buf.substr(0, hash);
myId = buf.substr(hash + 1);
} }
if (myType == "*") myType.clear();
}
bool StyleSheetTable::Selector::match(const std::string &pattern, const std::string &str) {
return pattern.empty() || pattern == WILDCARD || pattern == str;
}
bool StyleSheetTable::Selector::match(const std::string &pattern, const std::vector<std::string> &strings) {
return pattern.empty() || pattern == WILDCARD || std::find(strings.begin(), strings.end(), pattern) != strings.end();
}
void StyleSheetTable::Style::apply(const Style &other) {
TextStyle.apply(other.TextStyle);
if (other.PageBreakBefore != B3_UNDEFINED) {
PageBreakBefore = other.PageBreakBefore;
}
if (other.PageBreakAfter != B3_UNDEFINED) {
PageBreakAfter = other.PageBreakAfter;
} }
} }
static bool parseLength(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) { bool StyleSheetTable::Entry::match(const ElementList &stack) const {
if (!stack.empty() && !Selectors.empty()) {
SelectorList::const_reverse_iterator it(Selectors.rbegin());
ElementList::const_reverse_iterator match_it(stack.rbegin());
if ((*it).match(*match_it)) {
++it;
++match_it;
while (it != Selectors.rend()) {
const Selector &selector = (*it);
while (match_it != stack.rend() && !selector.match(*match_it)) {
++match_it;
}
if (match_it == stack.rend()) {
break;
}
++it;
}
if (it == Selectors.rend()) {
return true;
}
}
}
return false;
}
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 (!toParse.empty()) {
if (ZLStringUtil::stringEndsWith(toParse, "%")) { if (ZLStringUtil::stringEndsWith(toParse, "%")) {
unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT; unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT;
@ -72,14 +139,28 @@ static bool parseLength(const std::string &toParse, short &size, ZLTextStyleEntr
return false; return false;
} }
void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const AttributeMap &map, const std::string &attributeName) { bool StyleSheetTable::parseMargin(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) {
StyleSheetTable::AttributeMap::const_iterator it = map.find(attributeName); if (parseLength(toParse, size, unit)) {
if (it == map.end()) { // Negative margins do make sense but we don't really support them
return; if (size < 0) size = 0;
return true;
} else if (toParse == "auto") {
size = 0;
unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
return true;
} else {
return false;
} }
}
void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const AttributeMap &map, const std::string &attributeName) {
AttributeMap::const_iterator it = map.find(attributeName);
if (it != map.end()) {
const std::vector<std::string> &values = it->second; const std::vector<std::string> &values = it->second;
if (!values.empty()) if (!values.empty()) {
setLength(entry, name, values[0]); setLength(entry, name, values[0]);
}
}
} }
void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value) { void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value) {
@ -90,48 +171,39 @@ void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Lengt
} }
} }
bool StyleSheetTable::doBreakBefore(const std::string &tag, const std::string &aClass) const { void StyleSheetTable::setMargin(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const AttributeMap &map, const std::string &attributeName) {
std::map<Key,bool>::const_iterator it = myPageBreakBeforeMap.find(Key(tag, aClass)); AttributeMap::const_iterator it = map.find(attributeName);
if (it != myPageBreakBeforeMap.end()) { if (it != map.end()) {
return it->second; const std::vector<std::string> &values = it->second;
if (!values.empty()) {
setMargin(entry, name, values[0]);
} }
it = myPageBreakBeforeMap.find(Key("", aClass));
if (it != myPageBreakBeforeMap.end()) {
return it->second;
} }
it = myPageBreakBeforeMap.find(Key(tag, ""));
if (it != myPageBreakBeforeMap.end()) {
return it->second;
}
return false;
} }
bool StyleSheetTable::doBreakAfter(const std::string &tag, const std::string &aClass) const { void StyleSheetTable::setMargin(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value) {
std::map<Key,bool>::const_iterator it = myPageBreakAfterMap.find(Key(tag, aClass)); short size;
if (it != myPageBreakAfterMap.end()) { ZLTextStyleEntry::SizeUnit unit;
return it->second; if (parseMargin(value, size, unit)) {
entry.setLength(name, size, unit);
} }
it = myPageBreakAfterMap.find(Key("", aClass));
if (it != myPageBreakAfterMap.end()) {
return it->second;
}
it = myPageBreakAfterMap.find(Key(tag, ""));
if (it != myPageBreakAfterMap.end()) {
return it->second;
}
return false;
} }
shared_ptr<ZLTextStyleEntry> StyleSheetTable::control(const std::string &tag, const std::string &aClass) const { bool StyleSheetTable::sortBySpecificity(const Entry *e1, const Entry *e2) {
std::map<Key,shared_ptr<ZLTextStyleEntry> >::const_iterator it = return e1->Specificity < e2->Specificity;
myControlMap.find(Key(tag, aClass)); }
return (it != myControlMap.end()) ? it->second : 0;
void StyleSheetTable::applyStyles(const ElementList &stack, Style &style) const {
std::vector<const Entry*> entries;
for (std::vector<Entry>::const_iterator i = myEntries.begin(); i != myEntries.end(); ++i) {
if ((*i).match(stack)) {
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);
}
} }
const std::vector<std::string> &StyleSheetTable::values(const AttributeMap &map, const std::string &name) { const std::vector<std::string> &StyleSheetTable::values(const AttributeMap &map, const std::string &name) {
@ -143,21 +215,17 @@ const std::vector<std::string> &StyleSheetTable::values(const AttributeMap &map,
return emptyVector; return emptyVector;
} }
shared_ptr<ZLTextStyleEntry> StyleSheetTable::createControl(shared_ptr<ZLTextStyleEntry> entry, const AttributeMap &styles) { void StyleSheetTable::updateTextStyle(ZLTextStyleEntry &entry, const AttributeMap &styles) {
if (entry.isNull()) {
entry = new ZLTextStyleEntry();
}
const std::vector<std::string> &alignment = values(styles, "text-align"); const std::vector<std::string> &alignment = values(styles, "text-align");
if (!alignment.empty()) { if (!alignment.empty()) {
if (alignment[0] == "justify") { if (alignment[0] == "justify") {
entry->setAlignmentType(ALIGN_JUSTIFY); entry.setAlignmentType(ALIGN_JUSTIFY);
} else if (alignment[0] == "left") { } else if (alignment[0] == "left") {
entry->setAlignmentType(ALIGN_LEFT); entry.setAlignmentType(ALIGN_LEFT);
} else if (alignment[0] == "right") { } else if (alignment[0] == "right") {
entry->setAlignmentType(ALIGN_RIGHT); entry.setAlignmentType(ALIGN_RIGHT);
} else if (alignment[0] == "center") { } else if (alignment[0] == "center") {
entry->setAlignmentType(ALIGN_CENTER); entry.setAlignmentType(ALIGN_CENTER);
} }
} }
@ -178,23 +246,23 @@ shared_ptr<ZLTextStyleEntry> StyleSheetTable::createControl(shared_ptr<ZLTextSty
} else if (bold[0] == "lighter") { } else if (bold[0] == "lighter") {
} }
if (num != -1) { if (num != -1) {
entry->setFontModifier(FONT_MODIFIER_BOLD, num >= 600); entry.setFontModifier(FONT_MODIFIER_BOLD, num >= 600);
} }
} }
const std::vector<std::string> &italic = values(styles, "font-style"); const std::vector<std::string> &italic = values(styles, "font-style");
if (!italic.empty()) { if (!italic.empty()) {
entry->setFontModifier(FONT_MODIFIER_ITALIC, italic[0] == "italic"); entry.setFontModifier(FONT_MODIFIER_ITALIC, italic[0] == "italic");
} }
const std::vector<std::string> &variant = values(styles, "font-variant"); const std::vector<std::string> &variant = values(styles, "font-variant");
if (!variant.empty()) { if (!variant.empty()) {
entry->setFontModifier(FONT_MODIFIER_SMALLCAPS, variant[0] == "small-caps"); entry.setFontModifier(FONT_MODIFIER_SMALLCAPS, variant[0] == "small-caps");
} }
const std::vector<std::string> &fontFamily = values(styles, "font-family"); const std::vector<std::string> &fontFamily = values(styles, "font-family");
if (!fontFamily.empty() && !fontFamily[0].empty()) { if (!fontFamily.empty() && !fontFamily[0].empty()) {
entry->setFontFamily(fontFamily[0]); entry.setFontFamily(fontFamily[0]);
} }
short size; short size;
@ -203,19 +271,19 @@ shared_ptr<ZLTextStyleEntry> StyleSheetTable::createControl(shared_ptr<ZLTextSty
if (!fontSize.empty()) { if (!fontSize.empty()) {
std::string value = fontSize[0]; std::string value = fontSize[0];
if (value == "xx-small") { if (value == "xx-small") {
entry->setFontSizeMag(-3); entry.setFontSizeMag(-3);
} else if (value == "x-small") { } else if (value == "x-small") {
entry->setFontSizeMag(-2); entry.setFontSizeMag(-2);
} else if (value == "small" || value == "smaller") { } else if (value == "small" || value == "smaller") {
entry->setFontSizeMag(-1); entry.setFontSizeMag(-1);
} else if (value == "medium") { } else if (value == "medium") {
entry->setFontSizeMag(0); entry.setFontSizeMag(0);
} else if (value == "large" || value == "larger") { } else if (value == "large" || value == "larger") {
entry->setFontSizeMag(1); entry.setFontSizeMag(1);
} else if (value == "x-large") { } else if (value == "x-large") {
entry->setFontSizeMag(2); entry.setFontSizeMag(2);
} else if (value == "xx-large") { } else if (value == "xx-large") {
entry->setFontSizeMag(3); entry.setFontSizeMag(3);
} else { } else {
if (parseLength(value, size, unit)) { if (parseLength(value, size, unit)) {
switch (unit) { switch (unit) {
@ -225,8 +293,7 @@ shared_ptr<ZLTextStyleEntry> StyleSheetTable::createControl(shared_ptr<ZLTextSty
case ZLTextStyleEntry::SIZE_UNIT_EM_100: case ZLTextStyleEntry::SIZE_UNIT_EM_100:
case ZLTextStyleEntry::SIZE_UNIT_EX_100: case ZLTextStyleEntry::SIZE_UNIT_EX_100:
case ZLTextStyleEntry::SIZE_UNIT_PERCENT: case ZLTextStyleEntry::SIZE_UNIT_PERCENT:
entry->setFontSizeMag( entry.setFontSizeMag((size < 100 && size > 80) ? -1 :
(size < 100 && size > 80) ? -1 :
(size > 100 && size < 120) ? 1 : (size > 100 && size < 120) ? 1 :
(size - 100)/20); (size - 100)/20);
break; break;
@ -235,6 +302,12 @@ shared_ptr<ZLTextStyleEntry> StyleSheetTable::createControl(shared_ptr<ZLTextSty
} }
} }
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"); std::vector<std::string> margins = values(styles, "margin");
if (!margins.empty() && margins.back() == "!important") { if (!margins.empty() && margins.back() == "!important") {
// Ignore the "!important" modifier for now // Ignore the "!important" modifier for now
@ -242,78 +315,82 @@ shared_ptr<ZLTextStyleEntry> StyleSheetTable::createControl(shared_ptr<ZLTextSty
} }
switch (margins.size()) { switch (margins.size()) {
case 1: case 1:
if (parseLength(margins[0], size, unit)) { if (parseMargin(margins[0], size, unit)) {
entry->setLength(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, size, unit); entry.setLength(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, size, unit);
entry->setLength(ZLTextStyleEntry::LENGTH_RIGHT_INDENT, size, unit); entry.setLength(ZLTextStyleEntry::LENGTH_RIGHT_INDENT, size, unit);
entry->setLength(ZLTextStyleEntry::LENGTH_SPACE_AFTER, size, unit); entry.setLength(ZLTextStyleEntry::LENGTH_SPACE_AFTER, size, unit);
entry->setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, size, unit); entry.setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, size, unit);
} }
break; break;
case 2: case 2:
if (parseLength(margins[0], size, unit)) { if (parseMargin(margins[0], size, unit)) {
entry->setLength(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, size, unit); entry.setLength(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, size, unit);
entry->setLength(ZLTextStyleEntry::LENGTH_SPACE_AFTER, size, unit); entry.setLength(ZLTextStyleEntry::LENGTH_SPACE_AFTER, size, unit);
} }
if (parseLength(margins[1], size, unit)) { if (parseMargin(margins[1], size, unit)) {
entry->setLength(ZLTextStyleEntry::LENGTH_RIGHT_INDENT, size, unit); entry.setLength(ZLTextStyleEntry::LENGTH_RIGHT_INDENT, size, unit);
entry->setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, size, unit); entry.setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, size, unit);
} }
break; break;
case 3: case 3:
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, margins[0]); setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, margins[0]);
if (parseLength(margins[1], size, unit)) { if (parseMargin(margins[1], size, unit)) {
entry->setLength(ZLTextStyleEntry::LENGTH_RIGHT_INDENT, size, unit); entry.setLength(ZLTextStyleEntry::LENGTH_RIGHT_INDENT, size, unit);
entry->setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, size, unit); entry.setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, size, unit);
} }
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, margins[2]); setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, margins[2]);
break; break;
case 4: case 4:
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, margins[0]); setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, margins[0]);
setLength(*entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, margins[1]); setMargin(entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, margins[1]);
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, margins[2]); setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, margins[2]);
setLength(*entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, margins[3]); setMargin(entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, margins[3]);
break; break;
} }
setLength(*entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, styles, "margin-left"); setMargin(entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, styles, "margin-left");
setLength(*entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, styles, "margin-right"); setMargin(entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, styles, "margin-right");
setLength(*entry, ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, styles, "text-indent"); setLength(entry, ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, styles, "text-indent");
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "margin-top"); setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "margin-top");
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "padding-top"); setLength(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "padding-top");
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "margin-bottom"); setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "margin-bottom");
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "padding-bottom"); setLength(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "padding-bottom");
return entry;
} }
bool StyleSheetTable::getPageBreakBefore(const AttributeMap &map, bool &value) { bool StyleSheetTable::getPageBreakBefore(const AttributeMap &map, ZLBoolean3 &value) {
const std::vector<std::string> &pbb = values(map, "page-break-before"); const std::vector<std::string> &pbb = values(map, "page-break-before");
if (!pbb.empty()) { if (!pbb.empty()) {
if ((pbb[0] == "always") || if ((pbb[0] == "always") ||
(pbb[0] == "left") || (pbb[0] == "left") ||
(pbb[0] == "right")) { (pbb[0] == "right")) {
value = true; value = B3_TRUE;
return true; return true;
} else if (pbb[0] == "avoid") { } else if (pbb[0] == "avoid") {
value = false; value = B3_FALSE;
return true; return true;
} }
} }
return false; return false;
} }
bool StyleSheetTable::getPageBreakAfter(const AttributeMap &map, bool &value) { bool StyleSheetTable::getPageBreakAfter(const AttributeMap &map, ZLBoolean3 &value) {
const std::vector<std::string> &pba = values(map, "page-break-after"); const std::vector<std::string> &pba = values(map, "page-break-after");
if (!pba.empty()) { if (!pba.empty()) {
if ((pba[0] == "always") || if ((pba[0] == "always") ||
(pba[0] == "left") || (pba[0] == "left") ||
(pba[0] == "right")) { (pba[0] == "right")) {
value = true; value = B3_TRUE;
return true; return true;
} else if (pba[0] == "avoid") { } else if (pba[0] == "avoid") {
value = false; value = B3_FALSE;
return true; return true;
} }
} }
return false; return false;
} }
void StyleSheetTable::updateStyle(Style &style, const AttributeMap &map) {
updateTextStyle(style.TextStyle, map);
getPageBreakBefore(map, style.PageBreakBefore);
getPageBreakAfter(map, style.PageBreakAfter);
}

View file

@ -27,50 +27,151 @@
#include <shared_ptr.h> #include <shared_ptr.h>
#include <ZLTextParagraph.h> #include <ZLTextParagraph.h>
#include <ZLBoolean3.h>
class StyleSheetTable { class StyleSheetTable {
public: public:
struct Element {
Element(const std::string &tag, const char *klass, const char* id);
std::string Name;
std::vector<std::string> Classes;
std::string Id;
};
class Selector {
public:
Selector(const std::string &type, const std::string &klass, const std::string &id);
Selector(const std::string &type, const std::string &klass);
Selector(const std::string &selector);
Selector(const Selector &other);
Selector();
Selector &operator = (const Selector &other);
bool operator == (const Selector &other) const;
bool operator != (const Selector &other) const;
bool operator < (const Selector &other) const;
bool match(const Element &element) const;
static bool match(const std::string &pattern, const std::string &str);
static bool match(const std::string &pattern, const std::vector<std::string> &strings);
const std::string &type() const;
const std::string &klass() const;
const std::string &id() const;
int a() const;
int b() const;
int c() const;
private:
std::string myType;
std::string myClass;
std::string myId;
};
typedef std::vector<Element> ElementList;
typedef std::vector<Selector> SelectorList;
typedef std::map<std::string,std::vector<std::string> > AttributeMap; typedef std::map<std::string,std::vector<std::string> > AttributeMap;
static shared_ptr<ZLTextStyleEntry> createControl(shared_ptr<ZLTextStyleEntry> entry, const AttributeMap &map);
static bool getPageBreakBefore(const AttributeMap &map, bool &value); struct Style {
static bool getPageBreakAfter(const AttributeMap &map, bool &value); Style();
Style(const Style &other);
Style(const AttributeMap &map);
Style &operator = (const Style &other);
void apply(const Style &other);
ZLTextStyleEntry TextStyle;
ZLBoolean3 PageBreakBefore;
ZLBoolean3 PageBreakAfter;
};
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);
private: private:
void addMap(const std::string &tag, const std::string &aClass, const AttributeMap &map); struct Entry {
Entry();
Entry(const SelectorList &selectors, int specificity, const AttributeMap &map);
Entry &operator = (const Entry &other);
bool match(const ElementList &stack) const;
SelectorList Selectors;
int Specificity;
struct Style Style;
};
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 AttributeMap &map, const std::string &attributeName);
static void setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value); static void setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value);
static void setMargin(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const AttributeMap &map, const std::string &attributeName);
static void setMargin(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const 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: public:
bool isEmpty() const; bool isEmpty() const;
bool doBreakBefore(const std::string &tag, const std::string &aClass) const; void applyStyles(const ElementList &stack, Style &style) const;
bool doBreakAfter(const std::string &tag, const std::string &aClass) const;
shared_ptr<ZLTextStyleEntry> control(const std::string &tag, const std::string &aClass) const;
private: private:
struct Key { std::vector<Entry> myEntries;
Key(const std::string &tag, const std::string &aClass);
const std::string TagName;
const std::string ClassName;
bool operator < (const Key &key) const;
};
std::map<Key,shared_ptr<ZLTextStyleEntry> > myControlMap;
std::map<Key,bool> myPageBreakBeforeMap;
std::map<Key,bool> myPageBreakAfterMap;
friend class StyleSheetTableParser; friend class StyleSheetTableParser;
}; };
inline StyleSheetTable::Key::Key(const std::string &tag, const std::string &aClass) : TagName(tag), ClassName(aClass) { inline StyleSheetTable::Selector::Selector(const std::string &type, const std::string &klass, const std::string &id) : myType(type), myClass(klass), myId(id) {}
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) {
myType = other.myType; myClass = other.myClass; myId = other.myId; return *this;
}
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 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 {
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 {
return match(myType, element.Name) && match(myId, element.Id) && match(myClass, element.Classes);
}
inline int StyleSheetTable::Selector::a() const { return myId.empty() ? 0 : 1; }
inline int StyleSheetTable::Selector::b() const { return myClass.empty() ? 0 : 1; }
inline int StyleSheetTable::Selector::c() const { return myType.empty() ? 0 : 1; }
inline const std::string &StyleSheetTable::Selector::type() const { return myType; }
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) {
updateStyle(*this, map);
}
inline StyleSheetTable::Style &StyleSheetTable::Style::operator = (const StyleSheetTable::Style &other) {
TextStyle = other.TextStyle;
PageBreakBefore = other.PageBreakBefore;
PageBreakAfter = other.PageBreakAfter;
return *this;
} }
inline bool StyleSheetTable::Key::operator < (const StyleSheetTable::Key &key) const { inline StyleSheetTable::Entry::Entry() : Specificity(0) {}
return (TagName < key.TagName) || ((TagName == key.TagName) && (ClassName < key.ClassName)); 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) {
Selectors = other.Selectors;
Style = other.Style;
Specificity = other.Specificity;
return *this;
} }
inline bool StyleSheetTable::isEmpty() const { return myEntries.empty(); }
#endif /* __STYLESHEETTABLE_H__ */ #endif /* __STYLESHEETTABLE_H__ */

View file

@ -41,6 +41,12 @@ std::map<std::string,XHTMLTagAction*> XHTMLReader::ourTagActions;
XHTMLTagAction::~XHTMLTagAction() { XHTMLTagAction::~XHTMLTagAction() {
} }
void XHTMLTagAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
}
void XHTMLTagAction::doAtEnd(XHTMLReader &reader) {
}
BookReader &XHTMLTagAction::bookReader(XHTMLReader &reader) { BookReader &XHTMLTagAction::bookReader(XHTMLReader &reader) {
return reader.myModelReader; return reader.myModelReader;
} }
@ -68,7 +74,6 @@ class XHTMLTagLinkAction : public XHTMLTagAction {
public: public:
void doAtStart(XHTMLReader &reader, const char **xmlattributes); void doAtStart(XHTMLReader &reader, const char **xmlattributes);
void doAtEnd(XHTMLReader &reader);
}; };
class XHTMLTagParagraphAction : public XHTMLTagAction { class XHTMLTagParagraphAction : public XHTMLTagAction {
@ -89,6 +94,11 @@ class XHTMLTagRestartParagraphAction : public XHTMLTagAction {
public: public:
void doAtStart(XHTMLReader &reader, const char **xmlattributes); void doAtStart(XHTMLReader &reader, const char **xmlattributes);
};
class XHTMLTagLineBreakAction : public XHTMLTagAction {
public:
void doAtEnd(XHTMLReader &reader); void doAtEnd(XHTMLReader &reader);
}; };
@ -99,7 +109,6 @@ public:
XHTMLTagImageAction(const std::string &attributeName); XHTMLTagImageAction(const std::string &attributeName);
void doAtStart(XHTMLReader &reader, const char **xmlattributes); void doAtStart(XHTMLReader &reader, const char **xmlattributes);
void doAtEnd(XHTMLReader &reader);
private: private:
shared_ptr<ZLXMLReader::AttributeNamePredicate> myPredicate; shared_ptr<ZLXMLReader::AttributeNamePredicate> myPredicate;
@ -224,9 +233,6 @@ void XHTMLTagLinkAction::doAtStart(XHTMLReader &reader, const char **xmlattribut
//reader.myStyleSheetTable.dump(); //reader.myStyleSheetTable.dump();
} }
void XHTMLTagLinkAction::doAtEnd(XHTMLReader&) {
}
void XHTMLTagParagraphAction::doAtStart(XHTMLReader &reader, const char**) { void XHTMLTagParagraphAction::doAtStart(XHTMLReader &reader, const char**) {
if (!reader.myNewParagraphInProgress) { if (!reader.myNewParagraphInProgress) {
beginParagraph(reader); beginParagraph(reader);
@ -255,7 +261,8 @@ void XHTMLTagRestartParagraphAction::doAtStart(XHTMLReader &reader, const char**
beginParagraph(reader); beginParagraph(reader);
} }
void XHTMLTagRestartParagraphAction::doAtEnd(XHTMLReader&) { void XHTMLTagLineBreakAction::doAtEnd(XHTMLReader& reader) {
bookReader(reader).addLineBreak();
} }
void XHTMLTagItemAction::doAtStart(XHTMLReader &reader, const char**) { void XHTMLTagItemAction::doAtStart(XHTMLReader &reader, const char**) {
@ -280,6 +287,11 @@ XHTMLTagImageAction::XHTMLTagImageAction(const std::string &attributeName) {
} }
void XHTMLTagImageAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) { void XHTMLTagImageAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
// Ignore transparent images
if (!reader.myOpacityStack.back()) {
return;
}
const char *fileName = reader.attributeValue(xmlattributes, *myPredicate); const char *fileName = reader.attributeValue(xmlattributes, *myPredicate);
if (fileName == 0) { if (fileName == 0) {
return; return;
@ -302,6 +314,7 @@ void XHTMLTagImageAction::doAtStart(XHTMLReader &reader, const char **xmlattribu
if (flag) { if (flag) {
beginParagraph(reader); beginParagraph(reader);
} }
reader.myElementHasContents.back() = true;
} }
XHTMLTagSvgAction::XHTMLTagSvgAction(XHTMLSvgImageAttributeNamePredicate &predicate) : myPredicate(predicate) { XHTMLTagSvgAction::XHTMLTagSvgAction(XHTMLSvgImageAttributeNamePredicate &predicate) : myPredicate(predicate) {
@ -322,9 +335,6 @@ bool XHTMLSvgImageAttributeNamePredicate::accepts(const ZLXMLReader &reader, con
return myIsEnabled && NamespaceAttributeNamePredicate::accepts(reader, name); return myIsEnabled && NamespaceAttributeNamePredicate::accepts(reader, name);
} }
void XHTMLTagImageAction::doAtEnd(XHTMLReader&) {
}
XHTMLTagControlAction::XHTMLTagControlAction(FBTextKind control) : myControl(control) { XHTMLTagControlAction::XHTMLTagControlAction(FBTextKind control) : myControl(control) {
} }
@ -387,7 +397,7 @@ void XHTMLTagParagraphWithControlAction::doAtEnd(XHTMLReader &reader) {
} }
void XHTMLTagPreAction::doAtStart(XHTMLReader &reader, const char**) { void XHTMLTagPreAction::doAtStart(XHTMLReader &reader, const char**) {
reader.myPreformatted = true; reader.myPreformatted++;
beginParagraph(reader); beginParagraph(reader);
bookReader(reader).addControl(CODE, true); bookReader(reader).addControl(CODE, true);
} }
@ -395,7 +405,7 @@ void XHTMLTagPreAction::doAtStart(XHTMLReader &reader, const char**) {
void XHTMLTagPreAction::doAtEnd(XHTMLReader &reader) { void XHTMLTagPreAction::doAtEnd(XHTMLReader &reader) {
bookReader(reader).addControl(CODE, false); bookReader(reader).addControl(CODE, false);
endParagraph(reader); endParagraph(reader);
reader.myPreformatted = false; reader.myPreformatted--;
} }
XHTMLTagAction *XHTMLReader::addAction(const std::string &tag, XHTMLTagAction *action) { XHTMLTagAction *XHTMLReader::addAction(const std::string &tag, XHTMLTagAction *action) {
@ -440,8 +450,9 @@ void XHTMLReader::fillTagTable() {
addAction("cite", new XHTMLTagControlAction(CITE)); addAction("cite", new XHTMLTagControlAction(CITE));
addAction("sub", new XHTMLTagControlAction(SUB)); addAction("sub", new XHTMLTagControlAction(SUB));
addAction("sup", new XHTMLTagControlAction(SUP)); addAction("sup", new XHTMLTagControlAction(SUP));
addAction("dd", new XHTMLTagControlAction(DEFINITION_DESCRIPTION)); addAction("dd", new XHTMLTagParagraphWithControlAction(DEFINITION_DESCRIPTION));
addAction("dfn", new XHTMLTagControlAction(DEFINITION)); addAction("dt", new XHTMLTagParagraphWithControlAction(DEFINITION));
addAction("dfn", new XHTMLTagParagraphWithControlAction(DEFINITION));
addAction("strike", new XHTMLTagControlAction(STRIKETHROUGH)); addAction("strike", new XHTMLTagControlAction(STRIKETHROUGH));
addAction("a", new XHTMLTagHyperlinkAction()); addAction("a", new XHTMLTagHyperlinkAction());
@ -457,7 +468,7 @@ void XHTMLReader::fillTagTable() {
//addAction("base", new XHTMLTagAction()); //addAction("base", new XHTMLTagAction());
//addAction("blockquote", new XHTMLTagAction()); //addAction("blockquote", new XHTMLTagAction());
addAction("br", new XHTMLTagRestartParagraphAction()); addAction("br", new XHTMLTagLineBreakAction());
//addAction("center", new XHTMLTagAction()); //addAction("center", new XHTMLTagAction());
addAction("div", new XHTMLTagParagraphAction()); addAction("div", new XHTMLTagParagraphAction());
addAction("dt", new XHTMLTagParagraphAction()); addAction("dt", new XHTMLTagParagraphAction());
@ -495,13 +506,14 @@ bool XHTMLReader::readFile(const ZLFile &file, const std::string &referenceName)
const int index = referenceName.rfind('/', referenceName.length() - 1); const int index = referenceName.rfind('/', referenceName.length() - 1);
myReferenceDirName = referenceName.substr(0, index + 1); myReferenceDirName = referenceName.substr(0, index + 1);
myPreformatted = false; myPreformatted = 0;
myNewParagraphInProgress = false; myNewParagraphInProgress = false;
myReadState = READ_NOTHING; myReadState = READ_NOTHING;
myCSSStack.clear(); myElementStack.clear();
myStyleEntryStack.clear(); myStyleStack.clear();
myStylesToRemove = 0; myElementHasContents.clear();
myOpacityStack.clear();
return readDocument(file); return readDocument(file);
} }
@ -516,80 +528,45 @@ shared_ptr<ZLTextStyleEntry> XHTMLReader::addStyleEntry(shared_ptr<ZLTextStyleEn
void XHTMLReader::startElementHandler(const char *tag, const char **attributes) { void XHTMLReader::startElementHandler(const char *tag, const char **attributes) {
static const std::string HASH = "#"; static const std::string HASH = "#";
static const std::string EMPTY;
const char *id = attributeValue(attributes, "id"); const char *id = attributeValue(attributes, "id");
const char *style = attributeValue(attributes, "style");
const char *klass = attributeValue(attributes, "class");
if (id != 0) { if (id != 0) {
myModelReader.addHyperlinkLabel(myReferenceName + HASH + id); myModelReader.addHyperlinkLabel(myReferenceName + HASH + id);
} }
const std::string sTag = ZLUnicodeUtil::toLower(tag); const std::string sTag = ZLUnicodeUtil::toLower(tag);
myElementStack.push_back(StyleSheetTable::Element(sTag, klass, id));
const char *aClass = attributeValue(attributes, "class"); StyleSheetTable::Style cssStyle;
std::vector<std::string> classes; myStyleSheetTable.applyStyles(myElementStack, cssStyle);
if (aClass != 0) {
classes = ZLStringUtil::splitString(aClass, ", \n");
}
if (classes.empty()) {
classes.push_back(EMPTY);
}
shared_ptr<ZLTextStyleEntry> inlineStyle;
const char *style = attributeValue(attributes, "style");
ZLBoolean3 pageBreakBefore(B3_UNDEFINED), pageBreakAfter(B3_UNDEFINED);
if (style != 0) { if (style != 0) {
inlineStyle = myStyleParser.parseString(style, &pageBreakBefore, &pageBreakAfter); cssStyle.apply(myStyleParser.parseString(style));
} }
if (pageBreakBefore == B3_UNDEFINED) { if (cssStyle.PageBreakBefore == B3_TRUE) {
for (unsigned int i=0; i<classes.size(); i++) {
if (myStyleSheetTable.doBreakBefore(sTag, classes[i])) {
pageBreakBefore = B3_TRUE;
break;
}
}
}
if (pageBreakAfter == B3_UNDEFINED) {
for (unsigned int i=0; i<classes.size(); i++) {
if (myStyleSheetTable.doBreakAfter(sTag, classes[i])) {
pageBreakAfter = B3_TRUE;
break;
}
}
}
if (pageBreakBefore == B3_TRUE) {
myModelReader.insertEndOfSectionParagraph(); myModelReader.insertEndOfSectionParagraph();
} }
myDoPageBreakAfterStack.push_back(pageBreakAfter == B3_TRUE);
int opacity = myOpacityStack.empty() ? 255 : myOpacityStack.back();
if (cssStyle.TextStyle.opacitySupported()) {
opacity *= cssStyle.TextStyle.opacity();
opacity /= 255;
}
myStyleStack.push_back(cssStyle);
myElementHasContents.push_back(false);
myOpacityStack.push_back((unsigned char)opacity);
XHTMLTagAction *action = ourTagActions[sTag]; XHTMLTagAction *action = ourTagActions[sTag];
if (action != 0) { if (action != 0) {
action->doAtStart(*this, attributes); action->doAtStart(*this, attributes);
} }
shared_ptr<ZLTextStyleEntry> entry; myModelReader.addControl(cssStyle.TextStyle);
entry = addStyleEntry(entry, myStyleSheetTable.control(sTag, EMPTY));
for (unsigned int i=0; i<classes.size(); i++) {
std::string klass = classes[i];
entry = addStyleEntry(entry, myStyleSheetTable.control(EMPTY, klass));
if (!klass.empty()) {
entry = addStyleEntry(entry, myStyleSheetTable.control(sTag, klass));
}
}
entry = addStyleEntry(entry, inlineStyle);
if (!entry.isNull()) {
myModelReader.addControl(*entry);
myStyleEntryStack.push_back(entry);
myCSSStack.push_back(1);
} else {
myCSSStack.push_back(0);
}
} }
void XHTMLReader::endElementHandler(const char *tag) { void XHTMLReader::endElementHandler(const char *tag) {
for (int i = myCSSStack.back(); i > 0; --i) {
myModelReader.addControl(REGULAR, false); myModelReader.addControl(REGULAR, false);
}
myStylesToRemove = myCSSStack.back();
myCSSStack.pop_back();
XHTMLTagAction *action = ourTagActions[ZLUnicodeUtil::toLower(tag)]; XHTMLTagAction *action = ourTagActions[ZLUnicodeUtil::toLower(tag)];
if (action != 0) { if (action != 0) {
@ -597,58 +574,32 @@ void XHTMLReader::endElementHandler(const char *tag) {
myNewParagraphInProgress = false; myNewParagraphInProgress = false;
} }
for (; myStylesToRemove > 0; --myStylesToRemove) { const bool haveContents = myElementHasContents.back();
myStyleEntryStack.pop_back(); if (myStyleStack.back().PageBreakAfter == B3_TRUE && haveContents) {
}
if (myDoPageBreakAfterStack.back()) {
myModelReader.insertEndOfSectionParagraph(); myModelReader.insertEndOfSectionParagraph();
} }
myDoPageBreakAfterStack.pop_back();
myElementStack.pop_back();
myStyleStack.pop_back();
myElementHasContents.pop_back();
myOpacityStack.pop_back();
if (!myElementHasContents.empty() && haveContents) {
myElementHasContents.back() = true;
}
} }
void XHTMLReader::beginParagraph() { void XHTMLReader::beginParagraph() {
myCurrentParagraphIsEmpty = true; myCurrentParagraphIsEmpty = true;
myModelReader.beginParagraph(); myModelReader.beginParagraph();
bool doBlockSpaceBefore = false; if (!myStyleStack.empty()) {
for (std::vector<shared_ptr<ZLTextStyleEntry> >::const_iterator it = myStyleEntryStack.begin(); it != myStyleEntryStack.end(); ++it) { for (StyleSheetTable::StyleList::const_iterator it = myStyleStack.begin(); it != myStyleStack.end(); ++it) {
myModelReader.addControl(**it); myModelReader.addControl(((*it).TextStyle));
doBlockSpaceBefore =
doBlockSpaceBefore ||
(*it)->lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE);
} }
if (doBlockSpaceBefore) {
ZLTextStyleEntry blockingEntry;
blockingEntry.setLength(
ZLTextStyleEntry::LENGTH_SPACE_BEFORE,
0,
ZLTextStyleEntry::SIZE_UNIT_PIXEL
);
myModelReader.addControl(blockingEntry);
} }
} }
void XHTMLReader::endParagraph() { void XHTMLReader::endParagraph() {
bool doBlockSpaceAfter = false;
for (std::vector<shared_ptr<ZLTextStyleEntry> >::const_iterator it = myStyleEntryStack.begin(); it != myStyleEntryStack.end() - myStylesToRemove; ++it) {
doBlockSpaceAfter =
doBlockSpaceAfter ||
(*it)->lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_AFTER);
}
if (doBlockSpaceAfter) {
ZLTextStyleEntry blockingEntry;
blockingEntry.setLength(
ZLTextStyleEntry::LENGTH_SPACE_AFTER,
0,
ZLTextStyleEntry::SIZE_UNIT_PIXEL
);
myModelReader.addControl(blockingEntry);
}
for (; myStylesToRemove > 0; --myStylesToRemove) {
myModelReader.addControl(*myStyleEntryStack.back());
myStyleEntryStack.pop_back();
}
myModelReader.endParagraph(); myModelReader.endParagraph();
} }
@ -662,12 +613,10 @@ void XHTMLReader::characterDataHandler(const char *text, size_t len) {
} }
break; break;
case READ_BODY: case READ_BODY:
if (myOpacityStack.back()) {
if (myPreformatted) { if (myPreformatted) {
if ((*text == '\r') || (*text == '\n')) { if ((*text == '\r') || (*text == '\n')) {
myModelReader.addControl(CODE, false); myModelReader.addLineBreak();
endParagraph();
beginParagraph();
myModelReader.addControl(CODE, true);
} }
size_t spaceCounter = 0; size_t spaceCounter = 0;
while ((spaceCounter < len) && isspace((unsigned char)*(text + spaceCounter))) { while ((spaceCounter < len) && isspace((unsigned char)*(text + spaceCounter))) {
@ -686,12 +635,14 @@ void XHTMLReader::characterDataHandler(const char *text, size_t len) {
} }
if (len > 0) { if (len > 0) {
myCurrentParagraphIsEmpty = false; myCurrentParagraphIsEmpty = false;
myElementHasContents.back() = true;
if (!myModelReader.paragraphIsOpen()) { if (!myModelReader.paragraphIsOpen()) {
myModelReader.beginParagraph(); myModelReader.beginParagraph();
} }
myModelReader.addData(std::string(text, len)); myModelReader.addData(std::string(text, len));
myNewParagraphInProgress = false; myNewParagraphInProgress = false;
} }
}
break; break;
} }
} }

View file

@ -39,8 +39,8 @@ class XHTMLTagAction {
public: public:
virtual ~XHTMLTagAction(); virtual ~XHTMLTagAction();
virtual void doAtStart(XHTMLReader &reader, const char **xmlattributes) = 0; virtual void doAtStart(XHTMLReader &reader, const char **xmlattributes);
virtual void doAtEnd(XHTMLReader &reader) = 0; virtual void doAtEnd(XHTMLReader &reader);
protected: protected:
static BookReader &bookReader(XHTMLReader &reader); static BookReader &bookReader(XHTMLReader &reader);
@ -80,13 +80,13 @@ private:
std::string myPathPrefix; std::string myPathPrefix;
std::string myReferenceName; std::string myReferenceName;
std::string myReferenceDirName; std::string myReferenceDirName;
bool myPreformatted; int myPreformatted;
bool myNewParagraphInProgress; bool myNewParagraphInProgress;
StyleSheetTable myStyleSheetTable; StyleSheetTable myStyleSheetTable;
std::vector<int> myCSSStack; StyleSheetTable::ElementList myElementStack;
std::vector<shared_ptr<ZLTextStyleEntry> > myStyleEntryStack; StyleSheetTable::StyleList myStyleStack;
int myStylesToRemove; std::vector<unsigned char> myOpacityStack;
std::vector<bool> myDoPageBreakAfterStack; std::vector<bool> myElementHasContents;
bool myCurrentParagraphIsEmpty; bool myCurrentParagraphIsEmpty;
StyleSheetSingleStyleParser myStyleParser; StyleSheetSingleStyleParser myStyleParser;
shared_ptr<StyleSheetTableParser> myTableParser; shared_ptr<StyleSheetTableParser> myTableParser;
@ -104,6 +104,7 @@ private:
friend class XHTMLTagParagraphAction; friend class XHTMLTagParagraphAction;
friend class XHTMLTagBodyAction; friend class XHTMLTagBodyAction;
friend class XHTMLTagRestartParagraphAction; friend class XHTMLTagRestartParagraphAction;
friend class XHTMLTagImageAction;
}; };
#endif /* __XHTMLREADER_H__ */ #endif /* __XHTMLREADER_H__ */

View file

@ -91,10 +91,10 @@ void ZLStringUtil::stripWhiteSpaces(std::string &str) {
str.erase(r_counter, length - r_counter); str.erase(r_counter, length - r_counter);
} }
std::vector<std::string> ZLStringUtil::splitString(const std::string &str, const char* delim) std::vector<std::string> ZLStringUtil::splitString(const char *str, const char* delim) {
{
std::vector<std::string> tokens; std::vector<std::string> tokens;
char *buf = strdup(str.c_str()); if (str != 0) {
char *buf = strdup(str);
char *saveptr; char *saveptr;
char *token = strtok_r(buf, delim, &saveptr); char *token = strtok_r(buf, delim, &saveptr);
while (token) { while (token) {
@ -102,6 +102,7 @@ std::vector<std::string> ZLStringUtil::splitString(const std::string &str, const
token = strtok_r(NULL, delim, &saveptr); token = strtok_r(NULL, delim, &saveptr);
} }
free(buf); free(buf);
}
return tokens; return tokens;
} }

View file

@ -34,6 +34,7 @@ public:
static void appendNumber(std::string &str, unsigned int n); static void appendNumber(std::string &str, unsigned int n);
static void append(std::string &str, const std::vector<std::string> &buffer); static void append(std::string &str, const std::vector<std::string> &buffer);
static void stripWhiteSpaces(std::string &str); static void 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 std::vector<std::string> splitString(const std::string &str, const char* delim);
static std::string printf(const std::string &format, const std::string &arg0); static std::string printf(const std::string &format, const std::string &arg0);
@ -42,4 +43,8 @@ public:
static double stringToDouble(const std::string &value, double defaultValue); static double stringToDouble(const std::string &value, double defaultValue);
}; };
inline std::vector<std::string> ZLStringUtil::splitString(const std::string &str, const char* delim) {
return ZLStringUtil::splitString(str.c_str(), delim);
}
#endif /* __ZLSTRINGUTIL_H__ */ #endif /* __ZLSTRINGUTIL_H__ */

View file

@ -103,6 +103,7 @@ int ZLTextArea::Style::elementWidth(const ZLTextElement &element, unsigned int c
case ZLTextElement::AFTER_PARAGRAPH_ELEMENT: case ZLTextElement::AFTER_PARAGRAPH_ELEMENT:
case ZLTextElement::EMPTY_LINE_ELEMENT: case ZLTextElement::EMPTY_LINE_ELEMENT:
return metrics.FullWidth + abs(textStyle()->lineStartIndent(metrics, false)) + abs(textStyle()->lineEndIndent(metrics, false)) + abs(textStyle()->firstLineIndentDelta(metrics)) + 1; return metrics.FullWidth + abs(textStyle()->lineStartIndent(metrics, false)) + abs(textStyle()->lineEndIndent(metrics, false)) + abs(textStyle()->firstLineIndentDelta(metrics)) + 1;
case ZLTextElement::LINE_BREAK_ELEMENT:
case ZLTextElement::FORCED_CONTROL_ELEMENT: case ZLTextElement::FORCED_CONTROL_ELEMENT:
case ZLTextElement::CONTROL_ELEMENT: case ZLTextElement::CONTROL_ELEMENT:
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT: case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:
@ -131,6 +132,7 @@ int ZLTextArea::Style::elementHeight(const ZLTextElement &element, const ZLTextS
case ZLTextElement::AFTER_PARAGRAPH_ELEMENT: case ZLTextElement::AFTER_PARAGRAPH_ELEMENT:
return - textStyle()->spaceBefore(metrics); return - textStyle()->spaceBefore(metrics);
case ZLTextElement::EMPTY_LINE_ELEMENT: case ZLTextElement::EMPTY_LINE_ELEMENT:
case ZLTextElement::LINE_BREAK_ELEMENT:
return myArea.context().stringHeight(); return myArea.context().stringHeight();
case ZLTextElement::INDENT_ELEMENT: case ZLTextElement::INDENT_ELEMENT:
case ZLTextElement::HSPACE_ELEMENT: case ZLTextElement::HSPACE_ELEMENT:

View file

@ -66,6 +66,15 @@ void ZLTextArea::prepareTextLine(Style &style, const ZLTextLineInfo &info, int y
const bool endOfParagraph = info.End.isEndOfParagraph(); const bool endOfParagraph = info.End.isEndOfParagraph();
bool wordOccured = false; bool wordOccured = false;
bool endsWithLineBreak = false;
if (info.RealStart < info.End) {
ZLTextWordCursor last(info.End);
last.previousWord();
if (last.element().kind() == ZLTextElement::LINE_BREAK_ELEMENT) {
endsWithLineBreak = true;
}
}
int x = info.StartIndent; int x = info.StartIndent;
const int fontSize = style.textStyle()->fontSize(); const int fontSize = style.textStyle()->fontSize();
@ -89,7 +98,7 @@ void ZLTextArea::prepareTextLine(Style &style, const ZLTextLineInfo &info, int y
x += (metrics.FullWidth - style.textStyle()->lineEndIndent(metrics, isRtl()) - info.Width) / 2; x += (metrics.FullWidth - style.textStyle()->lineEndIndent(metrics, isRtl()) - info.Width) / 2;
break; break;
case ALIGN_JUSTIFY: case ALIGN_JUSTIFY:
if (!endOfParagraph && (info.End.element().kind() != ZLTextElement::AFTER_PARAGRAPH_ELEMENT)) { if (!endsWithLineBreak && !endOfParagraph && info.End.element().kind() != ZLTextElement::AFTER_PARAGRAPH_ELEMENT) {
fullCorrection = metrics.FullWidth - style.textStyle()->lineEndIndent(metrics, isRtl()) - info.Width; fullCorrection = metrics.FullWidth - style.textStyle()->lineEndIndent(metrics, isRtl()) - info.Width;
} }
break; break;
@ -140,6 +149,7 @@ void ZLTextArea::prepareTextLine(Style &style, const ZLTextLineInfo &info, int y
case ZLTextElement::BEFORE_PARAGRAPH_ELEMENT: case ZLTextElement::BEFORE_PARAGRAPH_ELEMENT:
case ZLTextElement::AFTER_PARAGRAPH_ELEMENT: case ZLTextElement::AFTER_PARAGRAPH_ELEMENT:
case ZLTextElement::EMPTY_LINE_ELEMENT: case ZLTextElement::EMPTY_LINE_ELEMENT:
case ZLTextElement::LINE_BREAK_ELEMENT:
case ZLTextElement::FIXED_HSPACE_ELEMENT: case ZLTextElement::FIXED_HSPACE_ELEMENT:
break; break;
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT: case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:

View file

@ -177,7 +177,11 @@ ZLTextLineInfoPtr ZLTextArea::processTextLine(Style &style, const ZLTextWordCurs
break; break;
} }
if (newInfo.Width > maxWidth && info.Start != info.End) { if (elementKind == ZLTextElement::LINE_BREAK_ELEMENT) {
newInfo.End.nextWord();
newInfo.setTo(info);
break;
} else if (newInfo.Width > maxWidth && info.Start != info.End) {
if (!info.End.equalElementIndex(start)) { if (!info.End.equalElementIndex(start)) {
break; break;
} }
@ -222,8 +226,7 @@ ZLTextLineInfoPtr ZLTextArea::processTextLine(Style &style, const ZLTextWordCurs
} }
} while (!newInfo.End.equalElementIndex(end)); } while (!newInfo.End.equalElementIndex(end));
if (!newInfo.End.equalElementIndex(end) && useHyphenator && if (elementKind != ZLTextElement::LINE_BREAK_ELEMENT && !newInfo.End.equalElementIndex(end) && useHyphenator && style.textStyle()->allowHyphenations()) {
style.textStyle()->allowHyphenations()) {
const ZLTextElement &element = paragraphCursor[newInfo.End.elementIndex()]; const ZLTextElement &element = paragraphCursor[newInfo.End.elementIndex()];
if (element.kind() == ZLTextElement::WORD_ELEMENT) { if (element.kind() == ZLTextElement::WORD_ELEMENT) {
const int startCharIndex = newInfo.End.charIndex(); const int startCharIndex = newInfo.End.charIndex();

View file

@ -45,6 +45,7 @@ public:
BEFORE_PARAGRAPH_ELEMENT, BEFORE_PARAGRAPH_ELEMENT,
AFTER_PARAGRAPH_ELEMENT, AFTER_PARAGRAPH_ELEMENT,
EMPTY_LINE_ELEMENT, EMPTY_LINE_ELEMENT,
LINE_BREAK_ELEMENT,
START_REVERSED_SEQUENCE_ELEMENT, START_REVERSED_SEQUENCE_ELEMENT,
END_REVERSED_SEQUENCE_ELEMENT, END_REVERSED_SEQUENCE_ELEMENT,
}; };

View file

@ -108,6 +108,9 @@ void ZLTextParagraphCursor::Builder::fill() {
updateBidiLevel(myBaseBidiLevel); updateBidiLevel(myBaseBidiLevel);
myLatestBidiLevel = myBaseBidiLevel; myLatestBidiLevel = myBaseBidiLevel;
break; break;
case ZLTextParagraphEntry::LINE_BREAK_ENTRY:
myElements.push_back(myTextElementPool.LineBreakElement);
break;
} }
} }

View file

@ -46,6 +46,7 @@ ZLTextElementVector::~ZLTextElementVector() {
case ZLTextElement::BEFORE_PARAGRAPH_ELEMENT: case ZLTextElement::BEFORE_PARAGRAPH_ELEMENT:
case ZLTextElement::AFTER_PARAGRAPH_ELEMENT: case ZLTextElement::AFTER_PARAGRAPH_ELEMENT:
case ZLTextElement::EMPTY_LINE_ELEMENT: case ZLTextElement::EMPTY_LINE_ELEMENT:
case ZLTextElement::LINE_BREAK_ELEMENT:
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT: case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:
case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT: case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT:
break; break;
@ -59,6 +60,7 @@ ZLTextElementPool::ZLTextElementPool() {
BeforeParagraphElement = new ZLTextSpecialElement(ZLTextElement::BEFORE_PARAGRAPH_ELEMENT); BeforeParagraphElement = new ZLTextSpecialElement(ZLTextElement::BEFORE_PARAGRAPH_ELEMENT);
AfterParagraphElement = new ZLTextSpecialElement(ZLTextElement::AFTER_PARAGRAPH_ELEMENT); AfterParagraphElement = new ZLTextSpecialElement(ZLTextElement::AFTER_PARAGRAPH_ELEMENT);
EmptyLineElement = new ZLTextSpecialElement(ZLTextElement::EMPTY_LINE_ELEMENT); EmptyLineElement = new ZLTextSpecialElement(ZLTextElement::EMPTY_LINE_ELEMENT);
LineBreakElement = new ZLTextSpecialElement(ZLTextElement::LINE_BREAK_ELEMENT);
StartReversedSequenceElement = new ZLTextSpecialElement(ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT); StartReversedSequenceElement = new ZLTextSpecialElement(ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT);
EndReversedSequenceElement = new ZLTextSpecialElement(ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT); EndReversedSequenceElement = new ZLTextSpecialElement(ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT);
} }
@ -69,6 +71,7 @@ ZLTextElementPool::~ZLTextElementPool() {
delete BeforeParagraphElement; delete BeforeParagraphElement;
delete AfterParagraphElement; delete AfterParagraphElement;
delete EmptyLineElement; delete EmptyLineElement;
delete LineBreakElement;
delete StartReversedSequenceElement; delete StartReversedSequenceElement;
delete EndReversedSequenceElement; delete EndReversedSequenceElement;
} }

View file

@ -62,6 +62,7 @@ public:
ZLTextElement *BeforeParagraphElement; ZLTextElement *BeforeParagraphElement;
ZLTextElement *AfterParagraphElement; ZLTextElement *AfterParagraphElement;
ZLTextElement *EmptyLineElement; ZLTextElement *EmptyLineElement;
ZLTextElement *LineBreakElement;
ZLTextElement *StartReversedSequenceElement; ZLTextElement *StartReversedSequenceElement;
ZLTextElement *EndReversedSequenceElement; ZLTextElement *EndReversedSequenceElement;

View file

@ -214,24 +214,40 @@ void ZLTextModel::addControl(ZLTextKind textKind, bool isStart) {
} }
void ZLTextModel::addControl(const ZLTextStyleEntry &entry) { void ZLTextModel::addControl(const ZLTextStyleEntry &entry) {
int len = sizeof(int) + 5 + ZLTextStyleEntry::NUMBER_OF_LENGTHS * (sizeof(short) + 1); int len = sizeof(int) + 2;
if (entry.myMask & ((1 << ZLTextStyleEntry::NUMBER_OF_LENGTHS) - 1)) {
for (int i = 0; i < ZLTextStyleEntry::NUMBER_OF_LENGTHS; ++i) {
if (entry.myMask & (1 << i)) {
len += (sizeof(short) + 1);
}
}
}
if (entry.opacitySupported()) ++len;
if (entry.alignmentTypeSupported()) ++len;
if (entry.supportedFontModifier()) ++len;
if (entry.fontSizeSupported()) ++len;
if (entry.fontFamilySupported()) { if (entry.fontFamilySupported()) {
len += entry.fontFamily().length() + 1; len += entry.fontFamily().length() + 1;
} }
myLastEntryStart = myAllocator.allocate(len); myLastEntryStart = myAllocator.allocate(len);
char *address = myLastEntryStart; char *address = myLastEntryStart;
*address++ = ZLTextParagraphEntry::STYLE_ENTRY; *address++ = ZLTextParagraphEntry::STYLE_ENTRY;
*address++ = entry.mySupportedFontModifier;
memcpy(address, &entry.myMask, sizeof(int)); memcpy(address, &entry.myMask, sizeof(int));
address += sizeof(int); address += sizeof(int);
if (entry.myMask & ((1 << ZLTextStyleEntry::NUMBER_OF_LENGTHS) - 1)) {
for (int i = 0; i < ZLTextStyleEntry::NUMBER_OF_LENGTHS; ++i) { for (int i = 0; i < ZLTextStyleEntry::NUMBER_OF_LENGTHS; ++i) {
if (entry.myMask & (1 << i)) {
*address++ = entry.myLengths[i].Unit; *address++ = entry.myLengths[i].Unit;
memcpy(address, &entry.myLengths[i].Size, sizeof(short)); memcpy(address, &entry.myLengths[i].Size, sizeof(short));
address += sizeof(short); address += sizeof(short);
} }
*address++ = entry.mySupportedFontModifier; }
*address++ = entry.myFontModifier; }
*address++ = entry.myAlignmentType; if (entry.opacitySupported()) *address++ = entry.myOpacity;
*address++ = entry.myFontSizeMag; if (entry.alignmentTypeSupported()) *address++ = entry.myAlignmentType;
if (entry.supportedFontModifier()) *address++ = entry.myFontModifier;
if (entry.fontSizeSupported()) *address++ = entry.myFontSizeMag;
if (entry.fontFamilySupported()) { if (entry.fontFamilySupported()) {
memcpy(address, entry.fontFamily().data(), entry.fontFamily().length()); memcpy(address, entry.fontFamily().data(), entry.fontFamily().length());
address += entry.fontFamily().length(); address += entry.fontFamily().length();
@ -267,3 +283,9 @@ void ZLTextModel::addBidiReset() {
*myLastEntryStart = ZLTextParagraphEntry::RESET_BIDI_ENTRY; *myLastEntryStart = ZLTextParagraphEntry::RESET_BIDI_ENTRY;
myParagraphs.back()->addEntry(myLastEntryStart); myParagraphs.back()->addEntry(myLastEntryStart);
} }
void ZLTextModel::addLineBreak() {
myLastEntryStart = myAllocator.allocate(1);
*myLastEntryStart = ZLTextParagraphEntry::LINE_BREAK_ENTRY;
myParagraphs.back()->addEntry(myLastEntryStart);
}

View file

@ -72,6 +72,7 @@ public:
void addImage(const std::string &id, const ZLImageMap &imageMap, short vOffset); void addImage(const std::string &id, const ZLImageMap &imageMap, short vOffset);
void addFixedHSpace(unsigned char length); void addFixedHSpace(unsigned char length);
void addBidiReset(); void addBidiReset();
void addLineBreak();
protected: protected:
void addParagraphInternal(ZLTextParagraph *paragraph); void addParagraphInternal(ZLTextParagraph *paragraph);

View file

@ -58,22 +58,39 @@ short ZLTextStyleEntry::length(Length name, const Metrics &metrics) const {
} }
ZLTextStyleEntry::ZLTextStyleEntry(char *address) { ZLTextStyleEntry::ZLTextStyleEntry(char *address) {
mySupportedFontModifier = *address++;
memcpy(&myMask, address, sizeof(int)); memcpy(&myMask, address, sizeof(int));
address += sizeof(int); address += sizeof(int);
for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) { for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) {
if (myMask & (1 << i)) {
myLengths[i].Unit = (SizeUnit)*address++; myLengths[i].Unit = (SizeUnit)*address++;
memcpy(&myLengths[i].Size, address, sizeof(short)); memcpy(&myLengths[i].Size, address, sizeof(short));
address += sizeof(short); address += sizeof(short);
} }
mySupportedFontModifier = *address++; }
myFontModifier = *address++; if (opacitySupported()) {
myOpacity = *address++;
}
if (alignmentTypeSupported()) {
myAlignmentType = (ZLTextAlignmentType)*address++; myAlignmentType = (ZLTextAlignmentType)*address++;
}
if (supportedFontModifier()) {
myFontModifier = *address++;
}
if (fontSizeSupported()) {
myFontSizeMag = (signed char)*address++; myFontSizeMag = (signed char)*address++;
}
if (fontFamilySupported()) { if (fontFamilySupported()) {
myFontFamily = address; myFontFamily = address;
} }
} }
void ZLTextStyleEntry::reset() {
mySupportedFontModifier = 0;
myMask = 0;
myFontFamily.clear();
}
void ZLTextStyleEntry::apply(const ZLTextStyleEntry &entry) { void ZLTextStyleEntry::apply(const ZLTextStyleEntry &entry) {
for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) { for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) {
if (entry.lengthSupported((Length)i)) { if (entry.lengthSupported((Length)i)) {
@ -81,6 +98,9 @@ void ZLTextStyleEntry::apply(const ZLTextStyleEntry &entry) {
myMask |= 1 << i; myMask |= 1 << i;
} }
} }
if (entry.opacitySupported()) {
setOpacity(entry.opacity());
}
if (entry.alignmentTypeSupported()) { if (entry.alignmentTypeSupported()) {
setAlignmentType(entry.alignmentType()); setAlignmentType(entry.alignmentType());
} }
@ -171,10 +191,21 @@ void ZLTextParagraph::Iterator::next() {
case ZLTextParagraphEntry::STYLE_ENTRY: case ZLTextParagraphEntry::STYLE_ENTRY:
{ {
int mask; int mask;
memcpy(&mask, myPointer + 1, sizeof(int)); const unsigned char supportedFontModifier = myPointer[1];
bool withFontFamily = (mask & ZLTextStyleEntry::SUPPORT_FONT_FAMILY) == ZLTextStyleEntry::SUPPORT_FONT_FAMILY; memcpy(&mask, myPointer + 2, sizeof(int));
myPointer += sizeof(int) + ZLTextStyleEntry::NUMBER_OF_LENGTHS * (sizeof(short) + 1) + 5; myPointer += sizeof(int) + 2;
if (withFontFamily) { if (mask & ((1 << ZLTextStyleEntry::NUMBER_OF_LENGTHS) - 1)) {
for (int i = 0; i < ZLTextStyleEntry::NUMBER_OF_LENGTHS; ++i) {
if (mask & (1 << i)) {
myPointer += (sizeof(short) + 1);
}
}
}
if (mask & ZLTextStyleEntry::SUPPORT_OPACITY) ++myPointer;
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') { while (*myPointer != '\0') {
++myPointer; ++myPointer;
} }
@ -186,6 +217,7 @@ void ZLTextParagraph::Iterator::next() {
myPointer += 2; myPointer += 2;
break; break;
case ZLTextParagraphEntry::RESET_BIDI_ENTRY: case ZLTextParagraphEntry::RESET_BIDI_ENTRY:
case ZLTextParagraphEntry::LINE_BREAK_ENTRY:
++myPointer; ++myPointer;
break; break;
} }

View file

@ -44,6 +44,7 @@ public:
STYLE_ENTRY = 5, STYLE_ENTRY = 5,
FIXED_HSPACE_ENTRY = 6, FIXED_HSPACE_ENTRY = 6,
RESET_BIDI_ENTRY = 7, RESET_BIDI_ENTRY = 7,
LINE_BREAK_ENTRY = 8
}; };
protected: protected:
@ -93,10 +94,13 @@ private:
public: public:
ZLTextStyleEntry(); ZLTextStyleEntry();
ZLTextStyleEntry(const ZLTextStyleEntry &other);
ZLTextStyleEntry(char *address); ZLTextStyleEntry(char *address);
~ZLTextStyleEntry(); ~ZLTextStyleEntry();
void reset();
void apply(const ZLTextStyleEntry &entry); void apply(const ZLTextStyleEntry &entry);
ZLTextStyleEntry &operator = (const ZLTextStyleEntry &other);
bool isEmpty() const; bool isEmpty() const;
@ -104,6 +108,10 @@ public:
short length(Length name, const Metrics &metrics) const; short length(Length name, const Metrics &metrics) const;
void setLength(Length name, short length, SizeUnit unit); void setLength(Length name, short length, SizeUnit unit);
bool opacitySupported() const;
unsigned char opacity() const;
void setOpacity(unsigned char opacity);
bool alignmentTypeSupported() const; bool alignmentTypeSupported() const;
ZLTextAlignmentType alignmentType() const; ZLTextAlignmentType alignmentType() const;
void setAlignmentType(ZLTextAlignmentType alignmentType); void setAlignmentType(ZLTextAlignmentType alignmentType);
@ -120,9 +128,12 @@ public:
const std::string &fontFamily() const; const std::string &fontFamily() const;
void setFontFamily(const std::string &fontFamily); void setFontFamily(const std::string &fontFamily);
static const int SUPPORT_ALIGNMENT_TYPE = 1 << NUMBER_OF_LENGTHS; enum {
static const int SUPPORT_FONT_SIZE = 1 << (NUMBER_OF_LENGTHS + 1); SUPPORT_ALIGNMENT_TYPE = 1 << NUMBER_OF_LENGTHS,
static const int SUPPORT_FONT_FAMILY = 1 << (NUMBER_OF_LENGTHS + 2); SUPPORT_FONT_SIZE = 1 << (NUMBER_OF_LENGTHS + 1),
SUPPORT_FONT_FAMILY = 1 << (NUMBER_OF_LENGTHS + 2),
SUPPORT_OPACITY = 1 << (NUMBER_OF_LENGTHS + 3)
};
private: private:
int myMask; int myMask;
@ -130,6 +141,7 @@ private:
LengthType myLengths[NUMBER_OF_LENGTHS]; LengthType myLengths[NUMBER_OF_LENGTHS];
ZLTextAlignmentType myAlignmentType; ZLTextAlignmentType myAlignmentType;
unsigned char myOpacity;
unsigned char mySupportedFontModifier; unsigned char mySupportedFontModifier;
unsigned char myFontModifier; unsigned char myFontModifier;
signed char myFontSizeMag; signed char myFontSizeMag;
@ -333,12 +345,15 @@ private:
inline ZLTextParagraphEntry::ZLTextParagraphEntry() {} inline ZLTextParagraphEntry::ZLTextParagraphEntry() {}
inline ZLTextParagraphEntry::~ZLTextParagraphEntry() {} inline ZLTextParagraphEntry::~ZLTextParagraphEntry() {}
inline ZLTextStyleEntry::ZLTextStyleEntry() : myMask(0), mySupportedFontModifier(0), myFontModifier(0) {} inline ZLTextStyleEntry::ZLTextStyleEntry() : myMask(0), mySupportedFontModifier(0) {}
inline ZLTextStyleEntry::ZLTextStyleEntry(const ZLTextStyleEntry &other) : ZLTextParagraphEntry(), myMask(0), mySupportedFontModifier(0) { apply(other); }
inline ZLTextStyleEntry::~ZLTextStyleEntry() {} inline ZLTextStyleEntry::~ZLTextStyleEntry() {}
inline ZLTextStyleEntry::Metrics::Metrics(int fontSize, int fontXHeight, int fullWidth, int fullHeight) : FontSize(fontSize), FontXHeight(fontXHeight), FullWidth(fullWidth), FullHeight(fullHeight) {} inline ZLTextStyleEntry::Metrics::Metrics(int fontSize, int fontXHeight, int fullWidth, int fullHeight) : FontSize(fontSize), FontXHeight(fontXHeight), FullWidth(fullWidth), FullHeight(fullHeight) {}
inline bool ZLTextStyleEntry::isEmpty() const { return myMask == 0 && mySupportedFontModifier == 0; } inline bool ZLTextStyleEntry::isEmpty() const { return myMask == 0 && mySupportedFontModifier == 0; }
inline ZLTextStyleEntry &ZLTextStyleEntry::operator = (const ZLTextStyleEntry &other) { reset(); apply(other); return *this; }
inline bool ZLTextStyleEntry::lengthSupported(Length name) const { return (myMask & (1 << name)) != 0; } inline bool ZLTextStyleEntry::lengthSupported(Length name) const { return (myMask & (1 << name)) != 0; }
inline void ZLTextStyleEntry::setLength(Length name, short length, SizeUnit unit) { inline void ZLTextStyleEntry::setLength(Length name, short length, SizeUnit unit) {
@ -347,7 +362,11 @@ inline void ZLTextStyleEntry::setLength(Length name, short length, SizeUnit unit
myMask |= 1 << name; myMask |= 1 << name;
} }
inline bool ZLTextStyleEntry::alignmentTypeSupported() const { return (myMask & SUPPORT_ALIGNMENT_TYPE) == SUPPORT_ALIGNMENT_TYPE; } inline bool ZLTextStyleEntry::opacitySupported() const { return (myMask & SUPPORT_OPACITY) != 0; }
inline unsigned char ZLTextStyleEntry::opacity() const { return myOpacity; }
inline void ZLTextStyleEntry::setOpacity(unsigned char opacity) { myOpacity = opacity; myMask |= SUPPORT_OPACITY; }
inline bool ZLTextStyleEntry::alignmentTypeSupported() const { return (myMask & SUPPORT_ALIGNMENT_TYPE) != 0; }
inline ZLTextAlignmentType ZLTextStyleEntry::alignmentType() const { return myAlignmentType; } inline ZLTextAlignmentType ZLTextStyleEntry::alignmentType() const { return myAlignmentType; }
inline void ZLTextStyleEntry::setAlignmentType(ZLTextAlignmentType alignmentType) { myAlignmentType = alignmentType; myMask |= SUPPORT_ALIGNMENT_TYPE; } inline void ZLTextStyleEntry::setAlignmentType(ZLTextAlignmentType alignmentType) { myAlignmentType = alignmentType; myMask |= SUPPORT_ALIGNMENT_TYPE; }