Major refactoring of CSS support
Also, improved support for line breaks.
This commit is contained in:
parent
a7e40b8d34
commit
248b307696
21 changed files with 649 additions and 368 deletions
|
@ -109,6 +109,12 @@ void BookReader::addFixedHSpace(unsigned char length) {
|
|||
}
|
||||
}
|
||||
|
||||
void BookReader::addLineBreak() {
|
||||
if (myTextParagraphExists) {
|
||||
myCurrentTextModel->addLineBreak();
|
||||
}
|
||||
}
|
||||
|
||||
void BookReader::addControl(const ZLTextStyleEntry &entry) {
|
||||
if (myTextParagraphExists) {
|
||||
flushTextBufferToParagraph();
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
void addHyperlinkLabel(const std::string &label);
|
||||
void addHyperlinkLabel(const std::string &label, int paragraphNumber);
|
||||
void addFixedHSpace(unsigned char length);
|
||||
void addLineBreak();
|
||||
|
||||
void addImageReference(const std::string &id, short vOffset = 0);
|
||||
void addImage(const std::string &id, shared_ptr<const ZLImage> image);
|
||||
|
|
|
@ -28,26 +28,22 @@
|
|||
StyleSheetTableParser::StyleSheetTableParser(StyleSheetTable &table) : myTable(table) {
|
||||
}
|
||||
|
||||
void StyleSheetTableParser::storeData(const std::string &tagName, const std::string &className, const StyleSheetTable::AttributeMap &map) {
|
||||
myTable.addMap(tagName, className, map);
|
||||
void StyleSheetTableParser::storeData(const std::string &selector, const StyleSheetTable::AttributeMap &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;
|
||||
parse(text, strlen(text), true);
|
||||
shared_ptr<ZLTextStyleEntry> control = StyleSheetTable::createControl(NULL, 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;
|
||||
}
|
||||
StyleSheetTable::updateStyle(style, myMap);
|
||||
reset();
|
||||
return control;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
StyleSheetParser::StyleSheetParser() : myReadState(TAG_NAME), myInsideComment(false) {
|
||||
StyleSheetParser::StyleSheetParser() : myReadState(TAG_NAME), myInsideComment(false), myAtBlockDepth(0) {
|
||||
}
|
||||
|
||||
StyleSheetParser::~StyleSheetParser() {
|
||||
|
@ -58,6 +54,7 @@ void StyleSheetParser::reset() {
|
|||
myAttributeName.erase();
|
||||
myReadState = TAG_NAME;
|
||||
myInsideComment = false;
|
||||
myAtBlockDepth = 0;
|
||||
mySelectors.clear();
|
||||
myMap.clear();
|
||||
}
|
||||
|
@ -81,7 +78,8 @@ 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 (isspace(*ptr)) {
|
||||
if ((myReadState != TAG_NAME && isspace(*ptr)) ||
|
||||
(myReadState == TAG_NAME && *ptr == ',')) {
|
||||
if (start != ptr) {
|
||||
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) {
|
||||
switch (control) {
|
||||
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;
|
||||
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++) {
|
||||
std::string selector(mySelectors[i]);
|
||||
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);
|
||||
storeData(mySelectors[i], myMap);
|
||||
}
|
||||
break;
|
||||
}
|
||||
myReadState = TAG_NAME;
|
||||
mySelectors.clear();
|
||||
myMap.clear();
|
||||
break;
|
||||
case ';':
|
||||
myReadState =
|
||||
((myReadState == ATTRIBUTE_VALUE) ||
|
||||
(myReadState == ATTRIBUTE_NAME)) ? ATTRIBUTE_NAME : BROKEN;
|
||||
switch (myReadState) {
|
||||
case AT_RULE:
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
case AT_RULE:
|
||||
case AT_BLOCK:
|
||||
break;
|
||||
case TAG_NAME:
|
||||
ZLStringUtil::stripWhiteSpaces(word);
|
||||
if (!word.empty()) {
|
||||
const unsigned int len = word.length();
|
||||
if (word.at(len-1) == ',') {
|
||||
mySelectors.push_back(word.substr(0, len-1));
|
||||
if (word[0] == '@') {
|
||||
myReadState = AT_RULE;
|
||||
|
||||
} else {
|
||||
mySelectors.push_back(word);
|
||||
}
|
||||
}
|
||||
myMap.clear();
|
||||
break;
|
||||
case ATTRIBUTE_NAME:
|
||||
myAttributeName = word;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#define __STYLESHEETPARSER_H__
|
||||
|
||||
#include "StyleSheetTable.h"
|
||||
#include "ZLBoolean3.h"
|
||||
|
||||
class ZLInputStream;
|
||||
|
||||
|
@ -37,24 +36,27 @@ public:
|
|||
void parse(const char *text, int len, bool final = false);
|
||||
|
||||
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:
|
||||
bool isControlSymbol(const char symbol);
|
||||
void processWord(std::string &word);
|
||||
void processWordWithoutComments(const std::string &word);
|
||||
void processWordWithoutComments(std::string word);
|
||||
void processControl(const char control);
|
||||
|
||||
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::vector<std::string> mySelectors;
|
||||
StyleSheetTable::AttributeMap myMap;
|
||||
|
||||
|
@ -67,7 +69,7 @@ public:
|
|||
StyleSheetTableParser(StyleSheetTable &table);
|
||||
|
||||
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:
|
||||
StyleSheetTable &myTable;
|
||||
|
@ -76,7 +78,7 @@ private:
|
|||
class StyleSheetSingleStyleParser : public StyleSheetParser {
|
||||
|
||||
public:
|
||||
shared_ptr<ZLTextStyleEntry> parseString(const char *text, ZLBoolean3* pageBreakBefore, ZLBoolean3* pageBreakAfter);
|
||||
StyleSheetTable::Style parseString(const char *text);
|
||||
};
|
||||
|
||||
#endif /* __STYLESHEETPARSER_H__ */
|
||||
|
|
|
@ -18,32 +18,99 @@
|
|||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
|
||||
#include <ZLStringUtil.h>
|
||||
|
||||
#include "StyleSheetTable.h"
|
||||
|
||||
bool StyleSheetTable::isEmpty() const {
|
||||
return myControlMap.empty() && myPageBreakBeforeMap.empty() && myPageBreakAfterMap.empty();
|
||||
static const std::string WILDCARD("*");
|
||||
|
||||
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) {
|
||||
if ((!tag.empty() || !aClass.empty()) && !map.empty()) {
|
||||
Key key(tag, aClass);
|
||||
// This will update the existing element if it already exists
|
||||
// or create a new one if it wasn't there yet
|
||||
myControlMap[key] = createControl(myControlMap[key], map);
|
||||
bool value;
|
||||
if (getPageBreakBefore(map, value)) {
|
||||
myPageBreakBeforeMap[key] = value;
|
||||
StyleSheetTable::Selector::Selector(const std::string &selector) {
|
||||
std::string buf(selector);
|
||||
const int dot = buf.find('.');
|
||||
if (dot >= 0) {
|
||||
myClass = buf.substr(dot + 1);
|
||||
buf = buf.substr(0, dot);
|
||||
}
|
||||
if (getPageBreakAfter(map, value)) {
|
||||
myPageBreakAfterMap[key] = value;
|
||||
const int hash = buf.find('#');
|
||||
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 (ZLStringUtil::stringEndsWith(toParse, "%")) {
|
||||
unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT;
|
||||
|
@ -72,15 +139,29 @@ static bool parseLength(const std::string &toParse, short &size, ZLTextStyleEntr
|
|||
return false;
|
||||
}
|
||||
|
||||
void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const AttributeMap &map, const std::string &attributeName) {
|
||||
StyleSheetTable::AttributeMap::const_iterator it = map.find(attributeName);
|
||||
if (it == map.end()) {
|
||||
return;
|
||||
bool StyleSheetTable::parseMargin(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) {
|
||||
if (parseLength(toParse, size, unit)) {
|
||||
// Negative margins do make sense but we don't really support them
|
||||
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;
|
||||
if (!values.empty())
|
||||
if (!values.empty()) {
|
||||
setLength(entry, name, values[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value) {
|
||||
short size;
|
||||
|
@ -90,48 +171,39 @@ void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Lengt
|
|||
}
|
||||
}
|
||||
|
||||
bool StyleSheetTable::doBreakBefore(const std::string &tag, const std::string &aClass) const {
|
||||
std::map<Key,bool>::const_iterator it = myPageBreakBeforeMap.find(Key(tag, aClass));
|
||||
if (it != myPageBreakBeforeMap.end()) {
|
||||
return it->second;
|
||||
void StyleSheetTable::setMargin(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;
|
||||
if (!values.empty()) {
|
||||
setMargin(entry, name, values[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it = myPageBreakBeforeMap.find(Key("", aClass));
|
||||
if (it != myPageBreakBeforeMap.end()) {
|
||||
return it->second;
|
||||
void StyleSheetTable::setMargin(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const std::string &value) {
|
||||
short size;
|
||||
ZLTextStyleEntry::SizeUnit unit;
|
||||
if (parseMargin(value, size, unit)) {
|
||||
entry.setLength(name, size, unit);
|
||||
}
|
||||
}
|
||||
|
||||
it = myPageBreakBeforeMap.find(Key(tag, ""));
|
||||
if (it != myPageBreakBeforeMap.end()) {
|
||||
return it->second;
|
||||
bool StyleSheetTable::sortBySpecificity(const Entry *e1, const Entry *e2) {
|
||||
return e1->Specificity < e2->Specificity;
|
||||
}
|
||||
|
||||
return false;
|
||||
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));
|
||||
}
|
||||
|
||||
bool StyleSheetTable::doBreakAfter(const std::string &tag, const std::string &aClass) const {
|
||||
std::map<Key,bool>::const_iterator it = myPageBreakAfterMap.find(Key(tag, aClass));
|
||||
if (it != myPageBreakAfterMap.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
it = myPageBreakAfterMap.find(Key("", aClass));
|
||||
if (it != myPageBreakAfterMap.end()) {
|
||||
return it->second;
|
||||
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);
|
||||
}
|
||||
|
||||
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 {
|
||||
std::map<Key,shared_ptr<ZLTextStyleEntry> >::const_iterator it =
|
||||
myControlMap.find(Key(tag, aClass));
|
||||
return (it != myControlMap.end()) ? it->second : 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
shared_ptr<ZLTextStyleEntry> StyleSheetTable::createControl(shared_ptr<ZLTextStyleEntry> entry, const AttributeMap &styles) {
|
||||
if (entry.isNull()) {
|
||||
entry = new ZLTextStyleEntry();
|
||||
}
|
||||
|
||||
void StyleSheetTable::updateTextStyle(ZLTextStyleEntry &entry, const AttributeMap &styles) {
|
||||
const std::vector<std::string> &alignment = values(styles, "text-align");
|
||||
if (!alignment.empty()) {
|
||||
if (alignment[0] == "justify") {
|
||||
entry->setAlignmentType(ALIGN_JUSTIFY);
|
||||
entry.setAlignmentType(ALIGN_JUSTIFY);
|
||||
} else if (alignment[0] == "left") {
|
||||
entry->setAlignmentType(ALIGN_LEFT);
|
||||
entry.setAlignmentType(ALIGN_LEFT);
|
||||
} else if (alignment[0] == "right") {
|
||||
entry->setAlignmentType(ALIGN_RIGHT);
|
||||
entry.setAlignmentType(ALIGN_RIGHT);
|
||||
} 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") {
|
||||
}
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
if (!fontFamily.empty() && !fontFamily[0].empty()) {
|
||||
entry->setFontFamily(fontFamily[0]);
|
||||
entry.setFontFamily(fontFamily[0]);
|
||||
}
|
||||
|
||||
short size;
|
||||
|
@ -203,19 +271,19 @@ shared_ptr<ZLTextStyleEntry> StyleSheetTable::createControl(shared_ptr<ZLTextSty
|
|||
if (!fontSize.empty()) {
|
||||
std::string value = fontSize[0];
|
||||
if (value == "xx-small") {
|
||||
entry->setFontSizeMag(-3);
|
||||
entry.setFontSizeMag(-3);
|
||||
} else if (value == "x-small") {
|
||||
entry->setFontSizeMag(-2);
|
||||
entry.setFontSizeMag(-2);
|
||||
} else if (value == "small" || value == "smaller") {
|
||||
entry->setFontSizeMag(-1);
|
||||
entry.setFontSizeMag(-1);
|
||||
} else if (value == "medium") {
|
||||
entry->setFontSizeMag(0);
|
||||
entry.setFontSizeMag(0);
|
||||
} else if (value == "large" || value == "larger") {
|
||||
entry->setFontSizeMag(1);
|
||||
entry.setFontSizeMag(1);
|
||||
} else if (value == "x-large") {
|
||||
entry->setFontSizeMag(2);
|
||||
entry.setFontSizeMag(2);
|
||||
} else if (value == "xx-large") {
|
||||
entry->setFontSizeMag(3);
|
||||
entry.setFontSizeMag(3);
|
||||
} else {
|
||||
if (parseLength(value, size, 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_EX_100:
|
||||
case ZLTextStyleEntry::SIZE_UNIT_PERCENT:
|
||||
entry->setFontSizeMag(
|
||||
(size < 100 && size > 80) ? -1 :
|
||||
entry.setFontSizeMag((size < 100 && size > 80) ? -1 :
|
||||
(size > 100 && size < 120) ? 1 :
|
||||
(size - 100)/20);
|
||||
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");
|
||||
if (!margins.empty() && margins.back() == "!important") {
|
||||
// Ignore the "!important" modifier for now
|
||||
|
@ -242,78 +315,82 @@ shared_ptr<ZLTextStyleEntry> StyleSheetTable::createControl(shared_ptr<ZLTextSty
|
|||
}
|
||||
switch (margins.size()) {
|
||||
case 1:
|
||||
if (parseLength(margins[0], size, unit)) {
|
||||
entry->setLength(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, size, unit);
|
||||
entry->setLength(ZLTextStyleEntry::LENGTH_RIGHT_INDENT, size, unit);
|
||||
entry->setLength(ZLTextStyleEntry::LENGTH_SPACE_AFTER, size, unit);
|
||||
entry->setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, size, unit);
|
||||
if (parseMargin(margins[0], size, unit)) {
|
||||
entry.setLength(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, size, unit);
|
||||
entry.setLength(ZLTextStyleEntry::LENGTH_RIGHT_INDENT, size, unit);
|
||||
entry.setLength(ZLTextStyleEntry::LENGTH_SPACE_AFTER, size, unit);
|
||||
entry.setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, size, unit);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (parseLength(margins[0], size, unit)) {
|
||||
entry->setLength(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, size, unit);
|
||||
entry->setLength(ZLTextStyleEntry::LENGTH_SPACE_AFTER, size, unit);
|
||||
if (parseMargin(margins[0], size, unit)) {
|
||||
entry.setLength(ZLTextStyleEntry::LENGTH_SPACE_BEFORE, size, unit);
|
||||
entry.setLength(ZLTextStyleEntry::LENGTH_SPACE_AFTER, size, unit);
|
||||
}
|
||||
if (parseLength(margins[1], size, unit)) {
|
||||
entry->setLength(ZLTextStyleEntry::LENGTH_RIGHT_INDENT, size, unit);
|
||||
entry->setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, size, unit);
|
||||
if (parseMargin(margins[1], size, unit)) {
|
||||
entry.setLength(ZLTextStyleEntry::LENGTH_RIGHT_INDENT, size, unit);
|
||||
entry.setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, size, unit);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, margins[0]);
|
||||
if (parseLength(margins[1], size, unit)) {
|
||||
entry->setLength(ZLTextStyleEntry::LENGTH_RIGHT_INDENT, size, unit);
|
||||
entry->setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, size, unit);
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, margins[0]);
|
||||
if (parseMargin(margins[1], size, unit)) {
|
||||
entry.setLength(ZLTextStyleEntry::LENGTH_RIGHT_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;
|
||||
case 4:
|
||||
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, margins[0]);
|
||||
setLength(*entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, margins[1]);
|
||||
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, margins[2]);
|
||||
setLength(*entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, margins[3]);
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, margins[0]);
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, margins[1]);
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, margins[2]);
|
||||
setMargin(entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, margins[3]);
|
||||
break;
|
||||
}
|
||||
|
||||
setLength(*entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, styles, "margin-left");
|
||||
setLength(*entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, styles, "margin-right");
|
||||
setLength(*entry, ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, styles, "text-indent");
|
||||
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "margin-top");
|
||||
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "padding-top");
|
||||
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "margin-bottom");
|
||||
setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "padding-bottom");
|
||||
|
||||
return entry;
|
||||
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");
|
||||
}
|
||||
|
||||
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");
|
||||
if (!pbb.empty()) {
|
||||
if ((pbb[0] == "always") ||
|
||||
(pbb[0] == "left") ||
|
||||
(pbb[0] == "right")) {
|
||||
value = true;
|
||||
value = B3_TRUE;
|
||||
return true;
|
||||
} else if (pbb[0] == "avoid") {
|
||||
value = false;
|
||||
value = B3_FALSE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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");
|
||||
if (!pba.empty()) {
|
||||
if ((pba[0] == "always") ||
|
||||
(pba[0] == "left") ||
|
||||
(pba[0] == "right")) {
|
||||
value = true;
|
||||
value = B3_TRUE;
|
||||
return true;
|
||||
} else if (pba[0] == "avoid") {
|
||||
value = false;
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -27,50 +27,151 @@
|
|||
#include <shared_ptr.h>
|
||||
|
||||
#include <ZLTextParagraph.h>
|
||||
#include <ZLBoolean3.h>
|
||||
|
||||
class StyleSheetTable {
|
||||
|
||||
public:
|
||||
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);
|
||||
static bool getPageBreakAfter(const AttributeMap &map, bool &value);
|
||||
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:
|
||||
void addMap(const std::string &tag, const std::string &aClass, const AttributeMap &map);
|
||||
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;
|
||||
|
||||
struct Style {
|
||||
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:
|
||||
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 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 bool sortBySpecificity(const Entry *e1, const Entry *e2);
|
||||
|
||||
public:
|
||||
bool isEmpty() const;
|
||||
bool doBreakBefore(const std::string &tag, const std::string &aClass) 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;
|
||||
void applyStyles(const ElementList &stack, Style &style) const;
|
||||
|
||||
private:
|
||||
struct Key {
|
||||
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;
|
||||
std::vector<Entry> myEntries;
|
||||
|
||||
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 {
|
||||
return (TagName < key.TagName) || ((TagName == key.TagName) && (ClassName < key.ClassName));
|
||||
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) {
|
||||
Selectors = other.Selectors;
|
||||
Style = other.Style;
|
||||
Specificity = other.Specificity;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool StyleSheetTable::isEmpty() const { return myEntries.empty(); }
|
||||
|
||||
#endif /* __STYLESHEETTABLE_H__ */
|
||||
|
|
|
@ -41,6 +41,12 @@ std::map<std::string,XHTMLTagAction*> XHTMLReader::ourTagActions;
|
|||
XHTMLTagAction::~XHTMLTagAction() {
|
||||
}
|
||||
|
||||
void XHTMLTagAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
|
||||
}
|
||||
|
||||
void XHTMLTagAction::doAtEnd(XHTMLReader &reader) {
|
||||
}
|
||||
|
||||
BookReader &XHTMLTagAction::bookReader(XHTMLReader &reader) {
|
||||
return reader.myModelReader;
|
||||
}
|
||||
|
@ -68,7 +74,6 @@ class XHTMLTagLinkAction : public XHTMLTagAction {
|
|||
|
||||
public:
|
||||
void doAtStart(XHTMLReader &reader, const char **xmlattributes);
|
||||
void doAtEnd(XHTMLReader &reader);
|
||||
};
|
||||
|
||||
class XHTMLTagParagraphAction : public XHTMLTagAction {
|
||||
|
@ -89,6 +94,11 @@ class XHTMLTagRestartParagraphAction : public XHTMLTagAction {
|
|||
|
||||
public:
|
||||
void doAtStart(XHTMLReader &reader, const char **xmlattributes);
|
||||
};
|
||||
|
||||
class XHTMLTagLineBreakAction : public XHTMLTagAction {
|
||||
|
||||
public:
|
||||
void doAtEnd(XHTMLReader &reader);
|
||||
};
|
||||
|
||||
|
@ -99,7 +109,6 @@ public:
|
|||
XHTMLTagImageAction(const std::string &attributeName);
|
||||
|
||||
void doAtStart(XHTMLReader &reader, const char **xmlattributes);
|
||||
void doAtEnd(XHTMLReader &reader);
|
||||
|
||||
private:
|
||||
shared_ptr<ZLXMLReader::AttributeNamePredicate> myPredicate;
|
||||
|
@ -224,9 +233,6 @@ void XHTMLTagLinkAction::doAtStart(XHTMLReader &reader, const char **xmlattribut
|
|||
//reader.myStyleSheetTable.dump();
|
||||
}
|
||||
|
||||
void XHTMLTagLinkAction::doAtEnd(XHTMLReader&) {
|
||||
}
|
||||
|
||||
void XHTMLTagParagraphAction::doAtStart(XHTMLReader &reader, const char**) {
|
||||
if (!reader.myNewParagraphInProgress) {
|
||||
beginParagraph(reader);
|
||||
|
@ -255,7 +261,8 @@ void XHTMLTagRestartParagraphAction::doAtStart(XHTMLReader &reader, const char**
|
|||
beginParagraph(reader);
|
||||
}
|
||||
|
||||
void XHTMLTagRestartParagraphAction::doAtEnd(XHTMLReader&) {
|
||||
void XHTMLTagLineBreakAction::doAtEnd(XHTMLReader& reader) {
|
||||
bookReader(reader).addLineBreak();
|
||||
}
|
||||
|
||||
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) {
|
||||
// Ignore transparent images
|
||||
if (!reader.myOpacityStack.back()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *fileName = reader.attributeValue(xmlattributes, *myPredicate);
|
||||
if (fileName == 0) {
|
||||
return;
|
||||
|
@ -302,6 +314,7 @@ void XHTMLTagImageAction::doAtStart(XHTMLReader &reader, const char **xmlattribu
|
|||
if (flag) {
|
||||
beginParagraph(reader);
|
||||
}
|
||||
reader.myElementHasContents.back() = true;
|
||||
}
|
||||
|
||||
XHTMLTagSvgAction::XHTMLTagSvgAction(XHTMLSvgImageAttributeNamePredicate &predicate) : myPredicate(predicate) {
|
||||
|
@ -322,9 +335,6 @@ bool XHTMLSvgImageAttributeNamePredicate::accepts(const ZLXMLReader &reader, con
|
|||
return myIsEnabled && NamespaceAttributeNamePredicate::accepts(reader, name);
|
||||
}
|
||||
|
||||
void XHTMLTagImageAction::doAtEnd(XHTMLReader&) {
|
||||
}
|
||||
|
||||
XHTMLTagControlAction::XHTMLTagControlAction(FBTextKind control) : myControl(control) {
|
||||
}
|
||||
|
||||
|
@ -387,7 +397,7 @@ void XHTMLTagParagraphWithControlAction::doAtEnd(XHTMLReader &reader) {
|
|||
}
|
||||
|
||||
void XHTMLTagPreAction::doAtStart(XHTMLReader &reader, const char**) {
|
||||
reader.myPreformatted = true;
|
||||
reader.myPreformatted++;
|
||||
beginParagraph(reader);
|
||||
bookReader(reader).addControl(CODE, true);
|
||||
}
|
||||
|
@ -395,7 +405,7 @@ void XHTMLTagPreAction::doAtStart(XHTMLReader &reader, const char**) {
|
|||
void XHTMLTagPreAction::doAtEnd(XHTMLReader &reader) {
|
||||
bookReader(reader).addControl(CODE, false);
|
||||
endParagraph(reader);
|
||||
reader.myPreformatted = false;
|
||||
reader.myPreformatted--;
|
||||
}
|
||||
|
||||
XHTMLTagAction *XHTMLReader::addAction(const std::string &tag, XHTMLTagAction *action) {
|
||||
|
@ -440,8 +450,9 @@ void XHTMLReader::fillTagTable() {
|
|||
addAction("cite", new XHTMLTagControlAction(CITE));
|
||||
addAction("sub", new XHTMLTagControlAction(SUB));
|
||||
addAction("sup", new XHTMLTagControlAction(SUP));
|
||||
addAction("dd", new XHTMLTagControlAction(DEFINITION_DESCRIPTION));
|
||||
addAction("dfn", new XHTMLTagControlAction(DEFINITION));
|
||||
addAction("dd", new XHTMLTagParagraphWithControlAction(DEFINITION_DESCRIPTION));
|
||||
addAction("dt", new XHTMLTagParagraphWithControlAction(DEFINITION));
|
||||
addAction("dfn", new XHTMLTagParagraphWithControlAction(DEFINITION));
|
||||
addAction("strike", new XHTMLTagControlAction(STRIKETHROUGH));
|
||||
|
||||
addAction("a", new XHTMLTagHyperlinkAction());
|
||||
|
@ -457,7 +468,7 @@ void XHTMLReader::fillTagTable() {
|
|||
|
||||
//addAction("base", new XHTMLTagAction());
|
||||
//addAction("blockquote", new XHTMLTagAction());
|
||||
addAction("br", new XHTMLTagRestartParagraphAction());
|
||||
addAction("br", new XHTMLTagLineBreakAction());
|
||||
//addAction("center", new XHTMLTagAction());
|
||||
addAction("div", 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);
|
||||
myReferenceDirName = referenceName.substr(0, index + 1);
|
||||
|
||||
myPreformatted = false;
|
||||
myPreformatted = 0;
|
||||
myNewParagraphInProgress = false;
|
||||
myReadState = READ_NOTHING;
|
||||
|
||||
myCSSStack.clear();
|
||||
myStyleEntryStack.clear();
|
||||
myStylesToRemove = 0;
|
||||
myElementStack.clear();
|
||||
myStyleStack.clear();
|
||||
myElementHasContents.clear();
|
||||
myOpacityStack.clear();
|
||||
|
||||
return readDocument(file);
|
||||
}
|
||||
|
@ -516,80 +528,45 @@ shared_ptr<ZLTextStyleEntry> XHTMLReader::addStyleEntry(shared_ptr<ZLTextStyleEn
|
|||
|
||||
void XHTMLReader::startElementHandler(const char *tag, const char **attributes) {
|
||||
static const std::string HASH = "#";
|
||||
static const std::string EMPTY;
|
||||
const char *id = attributeValue(attributes, "id");
|
||||
const char *style = attributeValue(attributes, "style");
|
||||
const char *klass = attributeValue(attributes, "class");
|
||||
if (id != 0) {
|
||||
myModelReader.addHyperlinkLabel(myReferenceName + HASH + id);
|
||||
}
|
||||
|
||||
const std::string sTag = ZLUnicodeUtil::toLower(tag);
|
||||
myElementStack.push_back(StyleSheetTable::Element(sTag, klass, id));
|
||||
|
||||
const char *aClass = attributeValue(attributes, "class");
|
||||
std::vector<std::string> classes;
|
||||
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);
|
||||
StyleSheetTable::Style cssStyle;
|
||||
myStyleSheetTable.applyStyles(myElementStack, cssStyle);
|
||||
if (style != 0) {
|
||||
inlineStyle = myStyleParser.parseString(style, &pageBreakBefore, &pageBreakAfter);
|
||||
cssStyle.apply(myStyleParser.parseString(style));
|
||||
}
|
||||
if (pageBreakBefore == B3_UNDEFINED) {
|
||||
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) {
|
||||
if (cssStyle.PageBreakBefore == B3_TRUE) {
|
||||
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];
|
||||
if (action != 0) {
|
||||
action->doAtStart(*this, attributes);
|
||||
}
|
||||
|
||||
shared_ptr<ZLTextStyleEntry> entry;
|
||||
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);
|
||||
}
|
||||
myModelReader.addControl(cssStyle.TextStyle);
|
||||
}
|
||||
|
||||
void XHTMLReader::endElementHandler(const char *tag) {
|
||||
for (int i = myCSSStack.back(); i > 0; --i) {
|
||||
myModelReader.addControl(REGULAR, false);
|
||||
}
|
||||
myStylesToRemove = myCSSStack.back();
|
||||
myCSSStack.pop_back();
|
||||
|
||||
XHTMLTagAction *action = ourTagActions[ZLUnicodeUtil::toLower(tag)];
|
||||
if (action != 0) {
|
||||
|
@ -597,58 +574,32 @@ void XHTMLReader::endElementHandler(const char *tag) {
|
|||
myNewParagraphInProgress = false;
|
||||
}
|
||||
|
||||
for (; myStylesToRemove > 0; --myStylesToRemove) {
|
||||
myStyleEntryStack.pop_back();
|
||||
}
|
||||
|
||||
if (myDoPageBreakAfterStack.back()) {
|
||||
const bool haveContents = myElementHasContents.back();
|
||||
if (myStyleStack.back().PageBreakAfter == B3_TRUE && haveContents) {
|
||||
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() {
|
||||
myCurrentParagraphIsEmpty = true;
|
||||
myModelReader.beginParagraph();
|
||||
bool doBlockSpaceBefore = false;
|
||||
for (std::vector<shared_ptr<ZLTextStyleEntry> >::const_iterator it = myStyleEntryStack.begin(); it != myStyleEntryStack.end(); ++it) {
|
||||
myModelReader.addControl(**it);
|
||||
doBlockSpaceBefore =
|
||||
doBlockSpaceBefore ||
|
||||
(*it)->lengthSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE);
|
||||
if (!myStyleStack.empty()) {
|
||||
for (StyleSheetTable::StyleList::const_iterator it = myStyleStack.begin(); it != myStyleStack.end(); ++it) {
|
||||
myModelReader.addControl(((*it).TextStyle));
|
||||
}
|
||||
|
||||
if (doBlockSpaceBefore) {
|
||||
ZLTextStyleEntry blockingEntry;
|
||||
blockingEntry.setLength(
|
||||
ZLTextStyleEntry::LENGTH_SPACE_BEFORE,
|
||||
0,
|
||||
ZLTextStyleEntry::SIZE_UNIT_PIXEL
|
||||
);
|
||||
myModelReader.addControl(blockingEntry);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -662,12 +613,10 @@ void XHTMLReader::characterDataHandler(const char *text, size_t len) {
|
|||
}
|
||||
break;
|
||||
case READ_BODY:
|
||||
if (myOpacityStack.back()) {
|
||||
if (myPreformatted) {
|
||||
if ((*text == '\r') || (*text == '\n')) {
|
||||
myModelReader.addControl(CODE, false);
|
||||
endParagraph();
|
||||
beginParagraph();
|
||||
myModelReader.addControl(CODE, true);
|
||||
myModelReader.addLineBreak();
|
||||
}
|
||||
size_t spaceCounter = 0;
|
||||
while ((spaceCounter < len) && isspace((unsigned char)*(text + spaceCounter))) {
|
||||
|
@ -686,12 +635,14 @@ void XHTMLReader::characterDataHandler(const char *text, size_t len) {
|
|||
}
|
||||
if (len > 0) {
|
||||
myCurrentParagraphIsEmpty = false;
|
||||
myElementHasContents.back() = true;
|
||||
if (!myModelReader.paragraphIsOpen()) {
|
||||
myModelReader.beginParagraph();
|
||||
}
|
||||
myModelReader.addData(std::string(text, len));
|
||||
myNewParagraphInProgress = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,8 +39,8 @@ class XHTMLTagAction {
|
|||
public:
|
||||
virtual ~XHTMLTagAction();
|
||||
|
||||
virtual void doAtStart(XHTMLReader &reader, const char **xmlattributes) = 0;
|
||||
virtual void doAtEnd(XHTMLReader &reader) = 0;
|
||||
virtual void doAtStart(XHTMLReader &reader, const char **xmlattributes);
|
||||
virtual void doAtEnd(XHTMLReader &reader);
|
||||
|
||||
protected:
|
||||
static BookReader &bookReader(XHTMLReader &reader);
|
||||
|
@ -80,13 +80,13 @@ private:
|
|||
std::string myPathPrefix;
|
||||
std::string myReferenceName;
|
||||
std::string myReferenceDirName;
|
||||
bool myPreformatted;
|
||||
int myPreformatted;
|
||||
bool myNewParagraphInProgress;
|
||||
StyleSheetTable myStyleSheetTable;
|
||||
std::vector<int> myCSSStack;
|
||||
std::vector<shared_ptr<ZLTextStyleEntry> > myStyleEntryStack;
|
||||
int myStylesToRemove;
|
||||
std::vector<bool> myDoPageBreakAfterStack;
|
||||
StyleSheetTable::ElementList myElementStack;
|
||||
StyleSheetTable::StyleList myStyleStack;
|
||||
std::vector<unsigned char> myOpacityStack;
|
||||
std::vector<bool> myElementHasContents;
|
||||
bool myCurrentParagraphIsEmpty;
|
||||
StyleSheetSingleStyleParser myStyleParser;
|
||||
shared_ptr<StyleSheetTableParser> myTableParser;
|
||||
|
@ -104,6 +104,7 @@ private:
|
|||
friend class XHTMLTagParagraphAction;
|
||||
friend class XHTMLTagBodyAction;
|
||||
friend class XHTMLTagRestartParagraphAction;
|
||||
friend class XHTMLTagImageAction;
|
||||
};
|
||||
|
||||
#endif /* __XHTMLREADER_H__ */
|
||||
|
|
|
@ -91,10 +91,10 @@ void ZLStringUtil::stripWhiteSpaces(std::string &str) {
|
|||
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;
|
||||
char *buf = strdup(str.c_str());
|
||||
if (str != 0) {
|
||||
char *buf = strdup(str);
|
||||
char *saveptr;
|
||||
char *token = strtok_r(buf, delim, &saveptr);
|
||||
while (token) {
|
||||
|
@ -102,6 +102,7 @@ std::vector<std::string> ZLStringUtil::splitString(const std::string &str, const
|
|||
token = strtok_r(NULL, delim, &saveptr);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
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 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::string printf(const std::string &format, const std::string &arg0);
|
||||
|
@ -42,4 +43,8 @@ public:
|
|||
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__ */
|
||||
|
|
|
@ -103,6 +103,7 @@ int ZLTextArea::Style::elementWidth(const ZLTextElement &element, unsigned int c
|
|||
case ZLTextElement::AFTER_PARAGRAPH_ELEMENT:
|
||||
case ZLTextElement::EMPTY_LINE_ELEMENT:
|
||||
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::CONTROL_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:
|
||||
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:
|
||||
|
|
|
@ -66,6 +66,15 @@ void ZLTextArea::prepareTextLine(Style &style, const ZLTextLineInfo &info, int y
|
|||
const bool endOfParagraph = info.End.isEndOfParagraph();
|
||||
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;
|
||||
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
}
|
||||
break;
|
||||
|
@ -140,6 +149,7 @@ void ZLTextArea::prepareTextLine(Style &style, const ZLTextLineInfo &info, int y
|
|||
case ZLTextElement::BEFORE_PARAGRAPH_ELEMENT:
|
||||
case ZLTextElement::AFTER_PARAGRAPH_ELEMENT:
|
||||
case ZLTextElement::EMPTY_LINE_ELEMENT:
|
||||
case ZLTextElement::LINE_BREAK_ELEMENT:
|
||||
case ZLTextElement::FIXED_HSPACE_ELEMENT:
|
||||
break;
|
||||
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:
|
||||
|
|
|
@ -177,7 +177,11 @@ ZLTextLineInfoPtr ZLTextArea::processTextLine(Style &style, const ZLTextWordCurs
|
|||
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)) {
|
||||
break;
|
||||
}
|
||||
|
@ -222,8 +226,7 @@ ZLTextLineInfoPtr ZLTextArea::processTextLine(Style &style, const ZLTextWordCurs
|
|||
}
|
||||
} while (!newInfo.End.equalElementIndex(end));
|
||||
|
||||
if (!newInfo.End.equalElementIndex(end) && useHyphenator &&
|
||||
style.textStyle()->allowHyphenations()) {
|
||||
if (elementKind != ZLTextElement::LINE_BREAK_ELEMENT && !newInfo.End.equalElementIndex(end) && useHyphenator && style.textStyle()->allowHyphenations()) {
|
||||
const ZLTextElement &element = paragraphCursor[newInfo.End.elementIndex()];
|
||||
if (element.kind() == ZLTextElement::WORD_ELEMENT) {
|
||||
const int startCharIndex = newInfo.End.charIndex();
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
BEFORE_PARAGRAPH_ELEMENT,
|
||||
AFTER_PARAGRAPH_ELEMENT,
|
||||
EMPTY_LINE_ELEMENT,
|
||||
LINE_BREAK_ELEMENT,
|
||||
START_REVERSED_SEQUENCE_ELEMENT,
|
||||
END_REVERSED_SEQUENCE_ELEMENT,
|
||||
};
|
||||
|
|
|
@ -108,6 +108,9 @@ void ZLTextParagraphCursor::Builder::fill() {
|
|||
updateBidiLevel(myBaseBidiLevel);
|
||||
myLatestBidiLevel = myBaseBidiLevel;
|
||||
break;
|
||||
case ZLTextParagraphEntry::LINE_BREAK_ENTRY:
|
||||
myElements.push_back(myTextElementPool.LineBreakElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ ZLTextElementVector::~ZLTextElementVector() {
|
|||
case ZLTextElement::BEFORE_PARAGRAPH_ELEMENT:
|
||||
case ZLTextElement::AFTER_PARAGRAPH_ELEMENT:
|
||||
case ZLTextElement::EMPTY_LINE_ELEMENT:
|
||||
case ZLTextElement::LINE_BREAK_ELEMENT:
|
||||
case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT:
|
||||
case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT:
|
||||
break;
|
||||
|
@ -59,6 +60,7 @@ ZLTextElementPool::ZLTextElementPool() {
|
|||
BeforeParagraphElement = new ZLTextSpecialElement(ZLTextElement::BEFORE_PARAGRAPH_ELEMENT);
|
||||
AfterParagraphElement = new ZLTextSpecialElement(ZLTextElement::AFTER_PARAGRAPH_ELEMENT);
|
||||
EmptyLineElement = new ZLTextSpecialElement(ZLTextElement::EMPTY_LINE_ELEMENT);
|
||||
LineBreakElement = new ZLTextSpecialElement(ZLTextElement::LINE_BREAK_ELEMENT);
|
||||
StartReversedSequenceElement = new ZLTextSpecialElement(ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT);
|
||||
EndReversedSequenceElement = new ZLTextSpecialElement(ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT);
|
||||
}
|
||||
|
@ -69,6 +71,7 @@ ZLTextElementPool::~ZLTextElementPool() {
|
|||
delete BeforeParagraphElement;
|
||||
delete AfterParagraphElement;
|
||||
delete EmptyLineElement;
|
||||
delete LineBreakElement;
|
||||
delete StartReversedSequenceElement;
|
||||
delete EndReversedSequenceElement;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ public:
|
|||
ZLTextElement *BeforeParagraphElement;
|
||||
ZLTextElement *AfterParagraphElement;
|
||||
ZLTextElement *EmptyLineElement;
|
||||
ZLTextElement *LineBreakElement;
|
||||
ZLTextElement *StartReversedSequenceElement;
|
||||
ZLTextElement *EndReversedSequenceElement;
|
||||
|
||||
|
|
|
@ -214,24 +214,40 @@ void ZLTextModel::addControl(ZLTextKind textKind, bool isStart) {
|
|||
}
|
||||
|
||||
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()) {
|
||||
len += entry.fontFamily().length() + 1;
|
||||
}
|
||||
myLastEntryStart = myAllocator.allocate(len);
|
||||
char *address = myLastEntryStart;
|
||||
*address++ = ZLTextParagraphEntry::STYLE_ENTRY;
|
||||
*address++ = entry.mySupportedFontModifier;
|
||||
memcpy(address, &entry.myMask, sizeof(int));
|
||||
address += sizeof(int);
|
||||
if (entry.myMask & ((1 << ZLTextStyleEntry::NUMBER_OF_LENGTHS) - 1)) {
|
||||
for (int i = 0; i < ZLTextStyleEntry::NUMBER_OF_LENGTHS; ++i) {
|
||||
if (entry.myMask & (1 << i)) {
|
||||
*address++ = entry.myLengths[i].Unit;
|
||||
memcpy(address, &entry.myLengths[i].Size, sizeof(short));
|
||||
address += sizeof(short);
|
||||
}
|
||||
*address++ = entry.mySupportedFontModifier;
|
||||
*address++ = entry.myFontModifier;
|
||||
*address++ = entry.myAlignmentType;
|
||||
*address++ = entry.myFontSizeMag;
|
||||
}
|
||||
}
|
||||
if (entry.opacitySupported()) *address++ = entry.myOpacity;
|
||||
if (entry.alignmentTypeSupported()) *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();
|
||||
|
@ -267,3 +283,9 @@ void ZLTextModel::addBidiReset() {
|
|||
*myLastEntryStart = ZLTextParagraphEntry::RESET_BIDI_ENTRY;
|
||||
myParagraphs.back()->addEntry(myLastEntryStart);
|
||||
}
|
||||
|
||||
void ZLTextModel::addLineBreak() {
|
||||
myLastEntryStart = myAllocator.allocate(1);
|
||||
*myLastEntryStart = ZLTextParagraphEntry::LINE_BREAK_ENTRY;
|
||||
myParagraphs.back()->addEntry(myLastEntryStart);
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@ public:
|
|||
void addImage(const std::string &id, const ZLImageMap &imageMap, short vOffset);
|
||||
void addFixedHSpace(unsigned char length);
|
||||
void addBidiReset();
|
||||
void addLineBreak();
|
||||
|
||||
protected:
|
||||
void addParagraphInternal(ZLTextParagraph *paragraph);
|
||||
|
|
|
@ -58,22 +58,39 @@ short ZLTextStyleEntry::length(Length name, const Metrics &metrics) const {
|
|||
}
|
||||
|
||||
ZLTextStyleEntry::ZLTextStyleEntry(char *address) {
|
||||
mySupportedFontModifier = *address++;
|
||||
memcpy(&myMask, address, sizeof(int));
|
||||
address += sizeof(int);
|
||||
for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) {
|
||||
if (myMask & (1 << i)) {
|
||||
myLengths[i].Unit = (SizeUnit)*address++;
|
||||
memcpy(&myLengths[i].Size, address, sizeof(short));
|
||||
address += sizeof(short);
|
||||
}
|
||||
mySupportedFontModifier = *address++;
|
||||
myFontModifier = *address++;
|
||||
}
|
||||
if (opacitySupported()) {
|
||||
myOpacity = *address++;
|
||||
}
|
||||
if (alignmentTypeSupported()) {
|
||||
myAlignmentType = (ZLTextAlignmentType)*address++;
|
||||
}
|
||||
if (supportedFontModifier()) {
|
||||
myFontModifier = *address++;
|
||||
}
|
||||
if (fontSizeSupported()) {
|
||||
myFontSizeMag = (signed char)*address++;
|
||||
}
|
||||
if (fontFamilySupported()) {
|
||||
myFontFamily = address;
|
||||
}
|
||||
}
|
||||
|
||||
void ZLTextStyleEntry::reset() {
|
||||
mySupportedFontModifier = 0;
|
||||
myMask = 0;
|
||||
myFontFamily.clear();
|
||||
}
|
||||
|
||||
void ZLTextStyleEntry::apply(const ZLTextStyleEntry &entry) {
|
||||
for (int i = 0; i < NUMBER_OF_LENGTHS; ++i) {
|
||||
if (entry.lengthSupported((Length)i)) {
|
||||
|
@ -81,6 +98,9 @@ void ZLTextStyleEntry::apply(const ZLTextStyleEntry &entry) {
|
|||
myMask |= 1 << i;
|
||||
}
|
||||
}
|
||||
if (entry.opacitySupported()) {
|
||||
setOpacity(entry.opacity());
|
||||
}
|
||||
if (entry.alignmentTypeSupported()) {
|
||||
setAlignmentType(entry.alignmentType());
|
||||
}
|
||||
|
@ -171,10 +191,21 @@ void ZLTextParagraph::Iterator::next() {
|
|||
case ZLTextParagraphEntry::STYLE_ENTRY:
|
||||
{
|
||||
int mask;
|
||||
memcpy(&mask, myPointer + 1, sizeof(int));
|
||||
bool withFontFamily = (mask & ZLTextStyleEntry::SUPPORT_FONT_FAMILY) == ZLTextStyleEntry::SUPPORT_FONT_FAMILY;
|
||||
myPointer += sizeof(int) + ZLTextStyleEntry::NUMBER_OF_LENGTHS * (sizeof(short) + 1) + 5;
|
||||
if (withFontFamily) {
|
||||
const unsigned char supportedFontModifier = myPointer[1];
|
||||
memcpy(&mask, myPointer + 2, sizeof(int));
|
||||
myPointer += sizeof(int) + 2;
|
||||
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') {
|
||||
++myPointer;
|
||||
}
|
||||
|
@ -186,6 +217,7 @@ void ZLTextParagraph::Iterator::next() {
|
|||
myPointer += 2;
|
||||
break;
|
||||
case ZLTextParagraphEntry::RESET_BIDI_ENTRY:
|
||||
case ZLTextParagraphEntry::LINE_BREAK_ENTRY:
|
||||
++myPointer;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ public:
|
|||
STYLE_ENTRY = 5,
|
||||
FIXED_HSPACE_ENTRY = 6,
|
||||
RESET_BIDI_ENTRY = 7,
|
||||
LINE_BREAK_ENTRY = 8
|
||||
};
|
||||
|
||||
protected:
|
||||
|
@ -93,10 +94,13 @@ private:
|
|||
|
||||
public:
|
||||
ZLTextStyleEntry();
|
||||
ZLTextStyleEntry(const ZLTextStyleEntry &other);
|
||||
ZLTextStyleEntry(char *address);
|
||||
~ZLTextStyleEntry();
|
||||
|
||||
void reset();
|
||||
void apply(const ZLTextStyleEntry &entry);
|
||||
ZLTextStyleEntry &operator = (const ZLTextStyleEntry &other);
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
|
@ -104,6 +108,10 @@ public:
|
|||
short length(Length name, const Metrics &metrics) const;
|
||||
void setLength(Length name, short length, SizeUnit unit);
|
||||
|
||||
bool opacitySupported() const;
|
||||
unsigned char opacity() const;
|
||||
void setOpacity(unsigned char opacity);
|
||||
|
||||
bool alignmentTypeSupported() const;
|
||||
ZLTextAlignmentType alignmentType() const;
|
||||
void setAlignmentType(ZLTextAlignmentType alignmentType);
|
||||
|
@ -120,9 +128,12 @@ public:
|
|||
const std::string &fontFamily() const;
|
||||
void setFontFamily(const std::string &fontFamily);
|
||||
|
||||
static const int SUPPORT_ALIGNMENT_TYPE = 1 << NUMBER_OF_LENGTHS;
|
||||
static const int SUPPORT_FONT_SIZE = 1 << (NUMBER_OF_LENGTHS + 1);
|
||||
static const int SUPPORT_FONT_FAMILY = 1 << (NUMBER_OF_LENGTHS + 2);
|
||||
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_OPACITY = 1 << (NUMBER_OF_LENGTHS + 3)
|
||||
};
|
||||
|
||||
private:
|
||||
int myMask;
|
||||
|
@ -130,6 +141,7 @@ private:
|
|||
LengthType myLengths[NUMBER_OF_LENGTHS];
|
||||
|
||||
ZLTextAlignmentType myAlignmentType;
|
||||
unsigned char myOpacity;
|
||||
unsigned char mySupportedFontModifier;
|
||||
unsigned char myFontModifier;
|
||||
signed char myFontSizeMag;
|
||||
|
@ -333,12 +345,15 @@ private:
|
|||
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::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 ZLTextStyleEntry &ZLTextStyleEntry::operator = (const ZLTextStyleEntry &other) { reset(); apply(other); return *this; }
|
||||
|
||||
inline bool ZLTextStyleEntry::lengthSupported(Length name) const { return (myMask & (1 << name)) != 0; }
|
||||
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;
|
||||
}
|
||||
|
||||
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 void ZLTextStyleEntry::setAlignmentType(ZLTextAlignmentType alignmentType) { myAlignmentType = alignmentType; myMask |= SUPPORT_ALIGNMENT_TYPE; }
|
||||
|
||||
|
|
Loading…
Reference in a new issue