diff options
Diffstat (limited to 'reader/src/formats/css')
-rw-r--r-- | reader/src/formats/css/StyleSheetParser.cpp | 244 | ||||
-rw-r--r-- | reader/src/formats/css/StyleSheetParser.h | 84 | ||||
-rw-r--r-- | reader/src/formats/css/StyleSheetTable.cpp | 267 | ||||
-rw-r--r-- | reader/src/formats/css/StyleSheetTable.h | 76 |
4 files changed, 671 insertions, 0 deletions
diff --git a/reader/src/formats/css/StyleSheetParser.cpp b/reader/src/formats/css/StyleSheetParser.cpp new file mode 100644 index 0000000..33dc900 --- /dev/null +++ b/reader/src/formats/css/StyleSheetParser.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <contact@geometerplus.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <cctype> +#include <cstring> + +#include <ZLStringUtil.h> +#include <ZLInputStream.h> +#include <ZLLogger.h> + +#include "StyleSheetParser.h" + +StyleSheetTableParser::StyleSheetTableParser(StyleSheetTable &table) : myTable(table) { + //ZLLogger::Instance().registerClass("CSS"); +} + +void StyleSheetTableParser::storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map) { + std::string s = selector; + ZLStringUtil::stripWhiteSpaces(s); + + if (s.empty()) { + return; + } + + if (s[0] == '@') { + processAtRule(s, map); + return; + } + + const std::vector<std::string> ids = ZLStringUtil::split(s, ","); + for (std::vector<std::string>::const_iterator it = ids.begin(); it != ids.end(); ++it) { + std::string id = *it; + ZLStringUtil::stripWhiteSpaces(id); + if (!id.empty()) { + const std::size_t index = id.find('.'); + if (index == std::string::npos) { + myTable.addMap(id, std::string(), map); + } else { + myTable.addMap(id.substr(0, index), id.substr(index + 1), map); + } + } + } +} + +void StyleSheetTableParser::processAtRule(const std::string &name, const StyleSheetTable::AttributeMap &map) { + (void)map; + if (name == "@font-face") { + } +} + +shared_ptr<ZLTextStyleEntry> StyleSheetSingleStyleParser::parseString(const char *text) { + myReadState = WAITING_FOR_ATTRIBUTE; + parse(text, std::strlen(text), true); + shared_ptr<ZLTextStyleEntry> control = StyleSheetTable::createControl(myMap); + reset(); + return control; +} + +StyleSheetParser::StyleSheetParser() { + reset(); +} + +StyleSheetParser::~StyleSheetParser() { +} + +void StyleSheetParser::reset() { + myWord.erase(); + myAttributeName.erase(); + myReadState = WAITING_FOR_SELECTOR; + myInsideComment = false; + mySelectorString.erase(); + myMap.clear(); +} + +void StyleSheetParser::parse(ZLInputStream &stream) { + if (stream.open()) { + char *buffer = new char[1024]; + while (true) { + int len = stream.read(buffer, 1024); + if (len == 0) { + break; + } + parse(buffer, len); + } + delete[] buffer; + stream.close(); + } +} + +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 (std::isspace(*ptr)) { + if (start != ptr) { + myWord.append(start, ptr - start); + } + processWord(myWord); + myWord.erase(); + start = ptr + 1; + } else if (isControlSymbol(*ptr)) { + if (start != ptr) { + myWord.append(start, ptr - start); + } + processWord(myWord); + myWord.erase(); + processControl(*ptr); + start = ptr + 1; + } + } + if (start < end) { + myWord.append(start, end - start); + if (final) { + processWord(myWord); + myWord.erase(); + } + } +} + +bool StyleSheetParser::isControlSymbol(const char symbol) { + switch (myReadState) { + default: + case WAITING_FOR_SELECTOR: + return false; + case SELECTOR: + return symbol == '{' || symbol == ';'; + case WAITING_FOR_ATTRIBUTE: + return symbol == '}' || symbol == ':'; + case ATTRIBUTE_NAME: + return symbol == ':'; + case ATTRIBUTE_VALUE: + return symbol == '}' || symbol == ';'; + } +} + +void StyleSheetParser::storeData(const std::string&, const StyleSheetTable::AttributeMap&) { +} + +void StyleSheetParser::processAtRule(const std::string&, const StyleSheetTable::AttributeMap&) { +} + +void StyleSheetParser::processControl(const char control) { + switch (myReadState) { + case WAITING_FOR_SELECTOR: + break; + case SELECTOR: + switch (control) { + case '{': + myReadState = WAITING_FOR_ATTRIBUTE; + break; + case ';': + myReadState = WAITING_FOR_SELECTOR; + mySelectorString.erase(); + break; + } + break; + case WAITING_FOR_ATTRIBUTE: + if (control == '}') { + myReadState = WAITING_FOR_SELECTOR; + storeData(mySelectorString, myMap); + mySelectorString.erase(); + myMap.clear(); + } + break; + case ATTRIBUTE_NAME: + if (control == ':') { + myReadState = ATTRIBUTE_VALUE; + } + break; + case ATTRIBUTE_VALUE: + if (control == ';') { + myReadState = WAITING_FOR_ATTRIBUTE; + } else if (control == '}') { + myReadState = WAITING_FOR_SELECTOR; + storeData(mySelectorString, myMap); + mySelectorString.erase(); + myMap.clear(); + } + break; + } +} + +void StyleSheetParser::processWord(std::string &word) { + while (!word.empty()) { + int index = word.find(myInsideComment ? "*/" : "/*"); + if (!myInsideComment) { + if (index == -1) { + processWordWithoutComments(word); + } else if (index > 0) { + processWordWithoutComments(word.substr(0, index)); + } + } + if (index == -1) { + break; + } + myInsideComment = !myInsideComment; + word.erase(0, index + 2); + } +} + +void StyleSheetParser::processWordWithoutComments(const std::string &word) { + switch (myReadState) { + case WAITING_FOR_SELECTOR: + myReadState = SELECTOR; + mySelectorString = word; + break; + case SELECTOR: + mySelectorString += ' ' + word; + break; + case WAITING_FOR_ATTRIBUTE: + myReadState = ATTRIBUTE_NAME; + // go through + case ATTRIBUTE_NAME: + myAttributeName = word; + myMap[myAttributeName].clear(); + break; + case ATTRIBUTE_VALUE: + { + const std::size_t l = word.length(); + if (l >= 2 && (word[0] == '"' || word[0] == '\'') && word[0] == word[l - 1]) { + myMap[myAttributeName].push_back(word.substr(1, l - 2)); + } else { + myMap[myAttributeName].push_back(word); + } + break; + } + } +} diff --git a/reader/src/formats/css/StyleSheetParser.h b/reader/src/formats/css/StyleSheetParser.h new file mode 100644 index 0000000..8949823 --- /dev/null +++ b/reader/src/formats/css/StyleSheetParser.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <contact@geometerplus.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __STYLESHEETPARSER_H__ +#define __STYLESHEETPARSER_H__ + +#include "StyleSheetTable.h" + +class ZLInputStream; + +class StyleSheetParser { + +protected: + StyleSheetParser(); + +public: + virtual ~StyleSheetParser(); + void reset(); + void parse(ZLInputStream &stream); + void parse(const char *text, int len, bool final = false); + +protected: + virtual void storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map); + virtual void processAtRule(const std::string &name, const StyleSheetTable::AttributeMap &map); + +private: + bool isControlSymbol(const char symbol); + void processWord(std::string &word); + void processWordWithoutComments(const std::string &word); + void processControl(const char control); + +private: + std::string myWord; + std::string myAttributeName; + enum { + WAITING_FOR_SELECTOR, + SELECTOR, + WAITING_FOR_ATTRIBUTE, + ATTRIBUTE_NAME, + ATTRIBUTE_VALUE, + } myReadState; + bool myInsideComment; + std::string mySelectorString; + StyleSheetTable::AttributeMap myMap; + +friend class StyleSheetSingleStyleParser; +}; + +class StyleSheetTableParser : public StyleSheetParser { + +public: + StyleSheetTableParser(StyleSheetTable &table); + +private: + void storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map); + void processAtRule(const std::string &name, const StyleSheetTable::AttributeMap &map); + +private: + StyleSheetTable &myTable; +}; + +class StyleSheetSingleStyleParser : public StyleSheetParser { + +public: + shared_ptr<ZLTextStyleEntry> parseString(const char *text); +}; + +#endif /* __STYLESHEETPARSER_H__ */ diff --git a/reader/src/formats/css/StyleSheetTable.cpp b/reader/src/formats/css/StyleSheetTable.cpp new file mode 100644 index 0000000..fe45a85 --- /dev/null +++ b/reader/src/formats/css/StyleSheetTable.cpp @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <contact@geometerplus.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <cstdlib> + +#include <ZLStringUtil.h> +#include <ZLLogger.h> + +#include "StyleSheetTable.h" + +bool StyleSheetTable::isEmpty() const { + return myControlMap.empty() && myPageBreakBeforeMap.empty() && myPageBreakAfterMap.empty(); +} + +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); + myControlMap[key] = createControl(map); + const std::vector<std::string> &pbb = values(map, "page-break-before"); + if (!pbb.empty()) { + if ((pbb[0] == "always") || + (pbb[0] == "left") || + (pbb[0] == "right")) { + myPageBreakBeforeMap[key] = true; + } else if (pbb[0] == "avoid") { + myPageBreakBeforeMap[key] = false; + } + } + const std::vector<std::string> &pba = values(map, "page-break-after"); + if (!pba.empty()) { + if ((pba[0] == "always") || + (pba[0] == "left") || + (pba[0] == "right")) { + myPageBreakAfterMap[key] = true; + } else if (pba[0] == "avoid") { + myPageBreakAfterMap[key] = false; + } + } + } +} + +static bool parseLength(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) { + if (ZLStringUtil::stringEndsWith(toParse, "%")) { + unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT; + size = std::atoi(toParse.c_str()); + return true; + } else if (ZLStringUtil::stringEndsWith(toParse, "em")) { + unit = ZLTextStyleEntry::SIZE_UNIT_EM_100; + size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0)); + return true; + } else if (ZLStringUtil::stringEndsWith(toParse, "ex")) { + unit = ZLTextStyleEntry::SIZE_UNIT_EX_100; + size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0)); + return true; + } else if (ZLStringUtil::stringEndsWith(toParse, "px")) { + unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL; + size = std::atoi(toParse.c_str()); + return true; + } else if (ZLStringUtil::stringEndsWith(toParse, "pt")) { + unit = ZLTextStyleEntry::SIZE_UNIT_POINT; + size = std::atoi(toParse.c_str()); + return true; + } + return false; +} + +void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Feature featureId, const AttributeMap &map, const std::string &attributeName) { + StyleSheetTable::AttributeMap::const_iterator it = map.find(attributeName); + if (it == map.end()) { + return; + } + const std::vector<std::string> &values = it->second; + if (!values.empty() && !values[0].empty()) { + short size; + ZLTextStyleEntry::SizeUnit unit; + if (parseLength(values[0], size, unit)) { + entry.setLength(featureId, size, unit); + } + } +} + +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; + } + + 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 { + 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; + } + + 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) { + const AttributeMap::const_iterator it = map.find(name); + if (it != map.end()) { + return it->second; + } + static const std::vector<std::string> emptyVector; + return emptyVector; +} + +shared_ptr<ZLTextStyleEntry> StyleSheetTable::createControl(const AttributeMap &styles) { + shared_ptr<ZLTextStyleEntry> entry = new ZLTextStyleEntry(ZLTextStyleEntry::STYLE_CSS_ENTRY); + + const std::vector<std::string> &alignment = values(styles, "text-align"); + if (!alignment.empty()) { + if (alignment[0] == "justify") { + entry->setAlignmentType(ALIGN_JUSTIFY); + } else if (alignment[0] == "left") { + entry->setAlignmentType(ALIGN_LEFT); + } else if (alignment[0] == "right") { + entry->setAlignmentType(ALIGN_RIGHT); + } else if (alignment[0] == "center") { + entry->setAlignmentType(ALIGN_CENTER); + } + } + + const std::vector<std::string> &deco = values(styles, "text-decoration"); + for (std::vector<std::string>::const_iterator it = deco.begin(); it != deco.end(); ++it) { + if (*it == "underline") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_UNDERLINED, true); + } else if (*it == "line-through") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_STRIKEDTHROUGH, true); + } else if (*it == "none") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_UNDERLINED, false); + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_STRIKEDTHROUGH, false); + } + } + + const std::vector<std::string> &bold = values(styles, "font-weight"); + if (!bold.empty()) { + //ZLLogger::Instance().println(ZLLogger::DEFAULT_CLASS, "bold: " + bold[0]); + int num = -1; + if (bold[0] == "bold") { + num = 700; + } else if (bold[0] == "normal") { + num = 400; + } else if (bold[0] == "bolder") { + // TODO: implement + } else if (bold[0] == "lighter") { + // TODO: implement + } else { + num = ZLStringUtil::stringToInteger(bold[0], -1); + } + if (num != -1) { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_BOLD, num >= 600); + } + } + + const std::vector<std::string> &italic = values(styles, "font-style"); + if (!italic.empty()) { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_ITALIC, italic[0] == "italic"); + } + + const std::vector<std::string> &variant = values(styles, "font-variant"); + if (!variant.empty()) { + entry->setFontModifier(ZLTextStyleEntry::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]); + //ZLLogger::Instance().println(ZLLogger::DEFAULT_CLASS, "font family: " + fontFamily[0]); + } + + const std::vector<std::string> &fontSize = values(styles, "font-size"); + if (!fontSize.empty()) { + //TODO implement FONT_MODIFIER_INHERIT, SMALLER and LARGER support + bool doSetFontSize = true; + short size = 100; + ZLTextStyleEntry::SizeUnit unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT; + if (fontSize[0] == "xx-small") { + size = 58; + } else if (fontSize[0] == "x-small") { + size = 69; + } else if (fontSize[0] == "small") { + size = 83; + } else if (fontSize[0] == "medium") { + size = 100; + } else if (fontSize[0] == "large") { + size = 120; + } else if (fontSize[0] == "x-large") { + size = 144; + } else if (fontSize[0] == "xx-large") { + size = 173; + } else if (fontSize[0] == "inherit") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_INHERIT, true); + doSetFontSize = false; + } else if (fontSize[0] == "smaller") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_SMALLER, true); + doSetFontSize = false; + } else if (fontSize[0] == "larger") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_LARGER, true); + doSetFontSize = false; + } else if (!parseLength(fontSize[0], size, unit)) { + doSetFontSize = false; + } + if (doSetFontSize) { + entry->setLength(ZLTextStyleEntry::LENGTH_FONT_SIZE, size, unit); + } + } + + 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; +} + +void StyleSheetTable::clear() { + myControlMap.clear(); + myPageBreakBeforeMap.clear(); + myPageBreakAfterMap.clear(); +} diff --git a/reader/src/formats/css/StyleSheetTable.h b/reader/src/formats/css/StyleSheetTable.h new file mode 100644 index 0000000..54236fb --- /dev/null +++ b/reader/src/formats/css/StyleSheetTable.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <contact@geometerplus.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __STYLESHEETTABLE_H__ +#define __STYLESHEETTABLE_H__ + +#include <string> +#include <map> +#include <vector> + +#include <shared_ptr.h> + +#include <ZLTextParagraph.h> +#include <ZLTextStyleEntry.h> + +class StyleSheetTable { + +public: + typedef std::map<std::string,std::vector<std::string> > AttributeMap; + static shared_ptr<ZLTextStyleEntry> createControl(const AttributeMap &map); + +private: + void addMap(const std::string &tag, const std::string &aClass, const AttributeMap &map); + + static void setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Feature featureId, const AttributeMap &map, const std::string &attributeName); + static const std::vector<std::string> &values(const AttributeMap &map, const std::string &name); + +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 clear(); + +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; + +friend class StyleSheetTableParser; +}; + +inline StyleSheetTable::Key::Key(const std::string &tag, const std::string &aClass) : TagName(tag), ClassName(aClass) { +} + +inline bool StyleSheetTable::Key::operator < (const StyleSheetTable::Key &key) const { + return (TagName < key.TagName) || ((TagName == key.TagName) && (ClassName < key.ClassName)); +} + +#endif /* __STYLESHEETTABLE_H__ */ |