1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-04 10:19:33 +02:00
FBReaderJ/jni/NativeFormats/fbreader/src/formats/css/StyleSheetTable.cpp
2014-11-08 05:00:16 +00:00

340 lines
12 KiB
C++

/*
* Copyright (C) 2004-2014 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 "StyleSheetTable.h"
#include "StyleSheetUtil.h"
#include "CSSSelector.h"
bool StyleSheetTable::isEmpty() const {
return myControlMap.empty() && myPageBreakBeforeMap.empty() && myPageBreakAfterMap.empty();
}
void StyleSheetTable::addMap(shared_ptr<CSSSelector> selectorPtr, const AttributeMap &map) {
if (!selectorPtr.isNull() && !map.empty()) {
const CSSSelector &selector = *selectorPtr;
myControlMap[selector] = createOrUpdateControl(map, myControlMap[selector]);
const std::string &pbb = value(map, "page-break-before");
if (pbb == "always" || pbb == "left" || pbb == "right") {
myPageBreakBeforeMap[selector] = true;
} else if (pbb == "avoid") {
myPageBreakBeforeMap[selector] = false;
}
const std::string &pba = value(map, "page-break-after");
if (pba == "always" || pba == "left" || pba == "right") {
myPageBreakAfterMap[selector] = true;
} else if (pba == "avoid") {
myPageBreakAfterMap[selector] = false;
}
}
}
static bool parseLength(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) {
if (toParse == "0") {
unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
size = 0;
return true;
} else if (ZLStringUtil::stringEndsWith(toParse, "%")) {
unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT;
size = std::atoi(toParse.c_str());
return true;
} else if (ZLStringUtil::stringEndsWith(toParse, "rem")) {
unit = ZLTextStyleEntry::SIZE_UNIT_REM_100;
size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0));
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;
}
static bool trySetLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Feature featureId, const std::string &value) {
short size;
ZLTextStyleEntry::SizeUnit unit;
if (::parseLength(value, size, unit)) {
entry.setLength(featureId, size, unit);
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()) {
::trySetLength(entry, featureId, it->second);
return;
}
}
bool StyleSheetTable::doBreakBefore(const std::string &tag, const std::string &aClass) const {
std::map<CSSSelector,bool>::const_iterator it = myPageBreakBeforeMap.find(CSSSelector(tag, aClass));
if (it != myPageBreakBeforeMap.end()) {
return it->second;
}
it = myPageBreakBeforeMap.find(CSSSelector("", aClass));
if (it != myPageBreakBeforeMap.end()) {
return it->second;
}
it = myPageBreakBeforeMap.find(CSSSelector(tag, ""));
if (it != myPageBreakBeforeMap.end()) {
return it->second;
}
return false;
}
bool StyleSheetTable::doBreakAfter(const std::string &tag, const std::string &aClass) const {
std::map<CSSSelector,bool>::const_iterator it = myPageBreakAfterMap.find(CSSSelector(tag, aClass));
if (it != myPageBreakAfterMap.end()) {
return it->second;
}
it = myPageBreakAfterMap.find(CSSSelector("", aClass));
if (it != myPageBreakAfterMap.end()) {
return it->second;
}
it = myPageBreakAfterMap.find(CSSSelector(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<CSSSelector,shared_ptr<ZLTextStyleEntry> >::const_iterator it =
myControlMap.find(CSSSelector(tag, aClass));
return it != myControlMap.end() ? it->second : 0;
}
std::vector<std::pair<CSSSelector,shared_ptr<ZLTextStyleEntry> > > StyleSheetTable::allControls(const std::string &tag, const std::string &aClass) const {
const CSSSelector key(tag, aClass);
std::vector<std::pair<CSSSelector,shared_ptr<ZLTextStyleEntry> > > pairs;
std::map<CSSSelector,shared_ptr<ZLTextStyleEntry> >::const_iterator it =
myControlMap.lower_bound(key);
for (std::map<CSSSelector,shared_ptr<ZLTextStyleEntry> >::const_iterator jt = it; jt != myControlMap.end() && key.weakEquals(jt->first); ++jt) {
pairs.push_back(*jt);
}
return pairs;
}
const std::string &StyleSheetTable::value(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::string emptyString;
return emptyString;
}
shared_ptr<ZLTextStyleEntry> StyleSheetTable::createOrUpdateControl(const AttributeMap &styles, shared_ptr<ZLTextStyleEntry> entry) {
if (entry.isNull()) {
entry = new ZLTextStyleEntry(ZLTextStyleEntry::STYLE_CSS_ENTRY);
}
const std::string &alignment = value(styles, "text-align");
if (alignment == "justify") {
entry->setAlignmentType(ALIGN_JUSTIFY);
} else if (alignment == "left") {
entry->setAlignmentType(ALIGN_LEFT);
} else if (alignment == "right") {
entry->setAlignmentType(ALIGN_RIGHT);
} else if (alignment == "center") {
entry->setAlignmentType(ALIGN_CENTER);
}
const std::string &deco = value(styles, "text-decoration");
if (deco == "underline") {
entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_UNDERLINED, true);
} else if (deco == "line-through") {
entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_STRIKEDTHROUGH, true);
} else if (deco == "none") {
entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_UNDERLINED, false);
entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_STRIKEDTHROUGH, false);
}
const std::string bold = value(styles, "font-weight");
if (!bold.empty()) {
int num = -1;
if (bold == "bold") {
num = 700;
} else if (bold == "normal") {
num = 400;
} else if (bold == "bolder") {
// TODO: implement
} else if (bold == "lighter") {
// TODO: implement
} else {
num = ZLStringUtil::parseDecimal(bold, -1);
}
if (num != -1) {
entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_BOLD, num >= 600);
}
}
const std::string &italic = value(styles, "font-style");
if (!italic.empty()) {
entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_ITALIC, italic == "italic" || italic == "oblique");
}
const std::string &variant = value(styles, "font-variant");
if (!variant.empty()) {
entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_SMALLCAPS, variant == "small-caps");
}
const std::string &fontFamily = value(styles, "font-family");
if (!fontFamily.empty()) {
entry->setFontFamilies(StyleSheetUtil::splitCommaSeparatedList(fontFamily));
}
const std::string &fontSize = value(styles, "font-size");
if (!fontSize.empty()) {
bool doSetFontSize = true;
short size = 100;
ZLTextStyleEntry::SizeUnit unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT;
if (fontSize == "xx-small") {
size = 58;
} else if (fontSize == "x-small") {
size = 69;
} else if (fontSize == "small") {
size = 83;
} else if (fontSize == "medium") {
size = 100;
} else if (fontSize == "large") {
size = 120;
} else if (fontSize == "x-large") {
size = 144;
} else if (fontSize == "xx-large") {
size = 173;
} else if (fontSize == "inherit") {
entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_INHERIT, true);
doSetFontSize = false;
} else if (fontSize == "smaller") {
entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_SMALLER, true);
doSetFontSize = false;
} else if (fontSize == "larger") {
entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_LARGER, true);
doSetFontSize = false;
} else if (!::parseLength(fontSize, size, unit)) {
doSetFontSize = false;
}
if (doSetFontSize) {
entry->setLength(ZLTextStyleEntry::LENGTH_FONT_SIZE, size, unit);
}
}
const std::string margin = value(styles, "margin");
if (!margin.empty()) {
std::vector<std::string> split = ZLStringUtil::split(margin, " ", true);
if (split.size() > 0) {
switch (split.size()) {
case 1:
split.push_back(split[0]);
// go through
case 2:
split.push_back(split[0]);
// go through
case 3:
split.push_back(split[1]);
break;
}
}
::trySetLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, split[0]);
::trySetLength(*entry, ZLTextStyleEntry::LENGTH_MARGIN_RIGHT, split[1]);
::trySetLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, split[2]);
::trySetLength(*entry, ZLTextStyleEntry::LENGTH_MARGIN_LEFT, split[3]);
}
const std::string padding = value(styles, "padding");
if (!padding.empty()) {
std::vector<std::string> split = ZLStringUtil::split(padding, " ", true);
if (split.size() > 0) {
switch (split.size()) {
case 1:
split.push_back(split[0]);
// go through
case 2:
split.push_back(split[0]);
// go through
case 3:
split.push_back(split[1]);
break;
}
}
::trySetLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, split[0]);
::trySetLength(*entry, ZLTextStyleEntry::LENGTH_PADDING_RIGHT, split[1]);
::trySetLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, split[2]);
::trySetLength(*entry, ZLTextStyleEntry::LENGTH_PADDING_LEFT, split[3]);
}
setLength(*entry, ZLTextStyleEntry::LENGTH_MARGIN_LEFT, styles, "margin-left");
setLength(*entry, ZLTextStyleEntry::LENGTH_MARGIN_RIGHT, styles, "margin-right");
setLength(*entry, ZLTextStyleEntry::LENGTH_PADDING_LEFT, styles, "padding-left");
setLength(*entry, ZLTextStyleEntry::LENGTH_PADDING_RIGHT, styles, "padding-right");
setLength(*entry, ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT, 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");
const std::string verticalAlign = value(styles, "vertical-align");
if (!verticalAlign.empty()) {
static const char* values[] = { "sub", "super", "top", "text-top", "middle", "bottom", "text-bottom", "initial", "inherit" };
int index = sizeof(values) / sizeof(const char*) - 1;
for (; index >= 0; --index) {
if (verticalAlign == values[index]) {
break;
}
}
if (index >= 0) {
entry->setVerticalAlignCode((unsigned char)index);
} else {
::trySetLength(*entry, ZLTextStyleEntry::LENGTH_VERTICAL_ALIGN, verticalAlign);
}
}
return entry;
}
void StyleSheetTable::clear() {
myControlMap.clear();
myPageBreakBeforeMap.clear();
myPageBreakAfterMap.clear();
}