1 /*
2  * Copyright (C) 2004-2010 Geometer Plus <contact@geometerplus.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19 
20 #include <cstdlib>
21 
22 #include <ZLStringUtil.h>
23 
24 #include "StyleSheetTable.h"
25 
isEmpty() const26 bool StyleSheetTable::isEmpty() const {
27 	return myControlMap.empty() && myPageBreakBeforeMap.empty() && myPageBreakAfterMap.empty();
28 }
29 
addMap(const std::string & tag,const std::string & aClass,const AttributeMap & map)30 void StyleSheetTable::addMap(const std::string &tag, const std::string &aClass, const AttributeMap &map) {
31 	if ((!tag.empty() || !aClass.empty()) && !map.empty()) {
32 		Key key(tag, aClass);
33 		myControlMap[key] = createControl(map);
34 		const std::vector<std::string> &pbb = values(map, "page-break-before");
35 		if (!pbb.empty()) {
36 			if ((pbb[0] == "always") ||
37 					(pbb[0] == "left") ||
38 					(pbb[0] == "right")) {
39 				myPageBreakBeforeMap[key] = true;
40 			} else if (pbb[0] == "avoid") {
41 				myPageBreakBeforeMap[key] = false;
42 			}
43 		}
44 		const std::vector<std::string> &pba = values(map, "page-break-after");
45 		if (!pba.empty()) {
46 			if ((pba[0] == "always") ||
47 					(pba[0] == "left") ||
48 					(pba[0] == "right")) {
49 				myPageBreakAfterMap[key] = true;
50 			} else if (pba[0] == "avoid") {
51 				myPageBreakAfterMap[key] = false;
52 			}
53 		}
54 	}
55 }
56 
parseLength(const std::string & toParse,short & size,ZLTextStyleEntry::SizeUnit & unit)57 static void parseLength(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) {
58 	if (ZLStringUtil::stringEndsWith(toParse, "%")) {
59 		unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT;
60 		size = atoi(toParse.c_str());
61 	} else if (ZLStringUtil::stringEndsWith(toParse, "em")) {
62 		unit = ZLTextStyleEntry::SIZE_UNIT_EM_100;
63 		size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0));
64 	} else if (ZLStringUtil::stringEndsWith(toParse, "ex")) {
65 		unit = ZLTextStyleEntry::SIZE_UNIT_EX_100;
66 		size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0));
67 	} else {
68 		unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL;
69 		size = atoi(toParse.c_str());
70 	}
71 }
72 
setLength(ZLTextStyleEntry & entry,ZLTextStyleEntry::Length name,const AttributeMap & map,const std::string & attributeName)73 void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Length name, const AttributeMap &map, const std::string &attributeName) {
74 	StyleSheetTable::AttributeMap::const_iterator it = map.find(attributeName);
75 	if (it == map.end()) {
76 		return;
77 	}
78 	const std::vector<std::string> &values = it->second;
79 	if (!values.empty() && !values[0].empty()) {
80 		short size;
81 		ZLTextStyleEntry::SizeUnit unit;
82 		parseLength(values[0], size, unit);
83 		entry.setLength(name, size, unit);
84 	}
85 }
86 
doBreakBefore(const std::string & tag,const std::string & aClass) const87 bool StyleSheetTable::doBreakBefore(const std::string &tag, const std::string &aClass) const {
88 	std::map<Key,bool>::const_iterator it = myPageBreakBeforeMap.find(Key(tag, aClass));
89 	if (it != myPageBreakBeforeMap.end()) {
90 		return it->second;
91 	}
92 
93 	it = myPageBreakBeforeMap.find(Key("", aClass));
94 	if (it != myPageBreakBeforeMap.end()) {
95 		return it->second;
96 	}
97 
98 	it = myPageBreakBeforeMap.find(Key(tag, ""));
99 	if (it != myPageBreakBeforeMap.end()) {
100 		return it->second;
101 	}
102 
103 	return false;
104 }
105 
doBreakAfter(const std::string & tag,const std::string & aClass) const106 bool StyleSheetTable::doBreakAfter(const std::string &tag, const std::string &aClass) const {
107 	std::map<Key,bool>::const_iterator it = myPageBreakAfterMap.find(Key(tag, aClass));
108 	if (it != myPageBreakAfterMap.end()) {
109 		return it->second;
110 	}
111 
112 	it = myPageBreakAfterMap.find(Key("", aClass));
113 	if (it != myPageBreakAfterMap.end()) {
114 		return it->second;
115 	}
116 
117 	it = myPageBreakAfterMap.find(Key(tag, ""));
118 	if (it != myPageBreakAfterMap.end()) {
119 		return it->second;
120 	}
121 
122 	return false;
123 }
124 
control(const std::string & tag,const std::string & aClass) const125 shared_ptr<ZLTextStyleEntry> StyleSheetTable::control(const std::string &tag, const std::string &aClass) const {
126 	std::map<Key,shared_ptr<ZLTextStyleEntry> >::const_iterator it =
127 		myControlMap.find(Key(tag, aClass));
128 	return (it != myControlMap.end()) ? it->second : 0;
129 }
130 
values(const AttributeMap & map,const std::string & name)131 const std::vector<std::string> &StyleSheetTable::values(const AttributeMap &map, const std::string &name) {
132 	const AttributeMap::const_iterator it = map.find(name);
133 	if (it != map.end()) {
134 		return it->second;
135 	}
136 	static const std::vector<std::string> emptyVector;
137 	return emptyVector;
138 }
139 
createControl(const AttributeMap & styles)140 shared_ptr<ZLTextStyleEntry> StyleSheetTable::createControl(const AttributeMap &styles) {
141 	shared_ptr<ZLTextStyleEntry> entry = new ZLTextStyleEntry();
142 
143 	const std::vector<std::string> &alignment = values(styles, "text-align");
144 	if (!alignment.empty()) {
145 		if (alignment[0] == "justify") {
146 			entry->setAlignmentType(ALIGN_JUSTIFY);
147 		} else if (alignment[0] == "left") {
148 			entry->setAlignmentType(ALIGN_LEFT);
149 		} else if (alignment[0] == "right") {
150 			entry->setAlignmentType(ALIGN_RIGHT);
151 		} else if (alignment[0] == "center") {
152 			entry->setAlignmentType(ALIGN_CENTER);
153 		}
154 	}
155 
156 	const std::vector<std::string> &bold = values(styles, "font-weight");
157 	if (!bold.empty()) {
158 		int num = -1;
159 		if (bold[0] == "bold") {
160 			num = 700;
161 		} else if (bold[0] == "normal") {
162 			num = 400;
163 		} else if ((bold[0].length() == 3) &&
164 							 (bold[0][1] == '0') &&
165 							 (bold[0][2] == '0') &&
166 							 (bold[0][0] >= '1') &&
167 							 (bold[0][0] <= '9')) {
168 			num = 100 * (bold[0][0] - '0');
169 		} else if (bold[0] == "bolder") {
170 		} else if (bold[0] == "lighter") {
171 		}
172 		if (num != -1) {
173 			entry->setFontModifier(FONT_MODIFIER_BOLD, num >= 600);
174 		}
175 	}
176 
177 	const std::vector<std::string> &italic = values(styles, "font-style");
178 	if (!italic.empty()) {
179 		entry->setFontModifier(FONT_MODIFIER_ITALIC, italic[0] == "italic");
180 	}
181 
182 	const std::vector<std::string> &variant = values(styles, "font-variant");
183 	if (!variant.empty()) {
184 		entry->setFontModifier(FONT_MODIFIER_SMALLCAPS, variant[0] == "small-caps");
185 	}
186 
187 	const std::vector<std::string> &fontFamily = values(styles, "font-family");
188 	if (!fontFamily.empty() && !fontFamily[0].empty()) {
189 		entry->setFontFamily(fontFamily[0]);
190 	}
191 
192 	const std::vector<std::string> &fontSize = values(styles, "font-size");
193 	if (!fontSize.empty()) {
194 		if (fontSize[0] == "xx-small") {
195 			entry->setFontSizeMag(-3);
196 		} else if (fontSize[0] == "x-small") {
197 			entry->setFontSizeMag(-2);
198 		} else if (fontSize[0] == "small") {
199 			entry->setFontSizeMag(-1);
200 		} else if (fontSize[0] == "medium") {
201 			entry->setFontSizeMag(0);
202 		} else if (fontSize[0] == "large") {
203 			entry->setFontSizeMag(1);
204 		} else if (fontSize[0] == "x-large") {
205 			entry->setFontSizeMag(2);
206 		} else if (fontSize[0] == "xx-large") {
207 			entry->setFontSizeMag(3);
208 		}
209 	}
210 
211 	setLength(*entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, styles, "margin-left");
212 	setLength(*entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, styles, "margin-right");
213 	setLength(*entry, ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, styles, "text-indent");
214 	setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "margin-top");
215 	setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "padding-top");
216 	setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "margin-bottom");
217 	setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "padding-bottom");
218 
219 	return entry;
220 }
221