/* * StateManager.h * * manage reusable CSS classes * * Copyright (C) 2013 Lu Wang */ #ifndef STATEMANAGER_H__ #define STATEMANAGER_H__ #include #include #include "util/math.h" #include "util/CSSClassNames.h" namespace pdf2htmlEX { template class StateManager {}; template class StateManager { public: StateManager() : eps(0) , imp(static_cast(this)) { reset(); } // values no farther than eps are treated as equal void set_eps (double eps) { this->eps = eps; } // usually called at the beginning of a page void reset(void) { _install(imp->default_value()); } /* * install new_value if changed (equal() should be faster than map::lower_bound) * return if the state has been indeed changed */ bool install(double new_value) { if(equal(new_value, value)) return false; _install(new_value); return true; } long long get_id (void) const { return id; } double get_value (void) const { return value; } double get_actual_value (void) const { return actual_value; } void dump_css(std::ostream & out) { for(auto iter = value_map.begin(); iter != value_map.end(); ++iter) { out << "." << imp->get_css_class_name() << iter->second << "{"; imp->dump_value(out, iter->first); out << "}" << std::endl; } } protected: // this version of install does not check if value has been updated // return if a new entry has been created bool _install(double new_value) { value = new_value; auto iter = value_map.lower_bound(new_value - eps); if((iter != value_map.end()) && (abs(iter->first - value) <= eps)) { actual_value = iter->first; id = iter->second; return false; } id = value_map.size(); actual_value = value_map.insert(std::make_pair(new_value, id)).first->first; return true; } double eps; Imp * imp; long long id; double value; // the value we are tracking double actual_value; // the value we actually exported to HTML std::map value_map; }; // Be careful about the mixed usage of Matrix and const double * template class StateManager { public: StateManager() : imp(static_cast(this)) { } void reset(void) { _install(imp->default_value()); } // return if changed bool install(const double * new_value) { // For a transform matrix m // m[4] & m[5] have been taken care of if(tm_equal(new_value, value.m, 4)) return false; _install(new_value); return true; } long long get_id (void) const { return id; } const Matrix & get_value (void) const { return value; } const Matrix & get_actual_value (void) const { return *actual_value; } void dump_css(std::ostream & out) { for(auto iter = value_map.begin(); iter != value_map.end(); ++iter) { out << "." << imp->get_css_class_name() << iter->second << "{"; imp->dump_value(out, iter->first); out << "}" << std::endl; } } protected: // return if a new entry has been created bool _install(const double * new_value) { memcpy(value.m, new_value, sizeof(value.m)); auto iter = value_map.lower_bound(value); if((iter != value_map.end()) && (tm_equal(value.m, iter->first.m, 4))) { actual_value = &(iter->first); id = iter->second; return false; } id = value_map.size(); actual_value = &(value_map.insert(std::make_pair(value, id)).first->first); return true; } Imp * imp; long long id; Matrix value; const Matrix * actual_value; class Matrix_less { public: bool operator () (const Matrix & m1, const Matrix & m2) const { // Note that we only care about the first 4 elements for(int i = 0; i < 4; ++i) { if(m1.m[i] < m2.m[i] - EPS) return true; if(m1.m[i] > m2.m[i] + EPS) return false; } return false; } }; std::map value_map; }; ///////////////////////////////////// // Specific state managers class FontSizeManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::FONT_SIZE_CN; } double default_value(void) { return 0; } void dump_value(std::ostream & out, double value) { out << "font-size:" << round(value) << "px;"; } }; class LetterSpaceManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::LETTER_SPACE_CN; } double default_value(void) { return 0; } void dump_value(std::ostream & out, double value) { out << "letter-spacing:" << round(value) << "px;"; } }; class WordSpaceManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::WORD_SPACE_CN;} double default_value(void) { return 0; } void dump_value(std::ostream & out, double value) { out << "word-spacing:" << round(value) << "px;"; } }; class RiseManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::RISE_CN; } double default_value(void) { return 0; } void dump_value(std::ostream & out, double value) { out << "top:" << round(-value) << "px;"; } }; class WhitespaceManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::WHITESPACE_CN; } double default_value(void) { return 0; } void dump_value(std::ostream & out, double value) { out << ((value > 0) ? "display:inline-block;width:" : "display:inline;margin-left:") << round(value) << "px;"; } }; class HeightManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::HEIGHT_CN; } double default_value(void) { return 0; } void dump_value(std::ostream & out, double value) { out << "height:" << round(value) << "px;"; } }; class LeftManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::LEFT_CN; } double default_value(void) { return 0; } void dump_value(std::ostream & out, double value) { out << "left:" << round(value) << "px;"; } }; class TransformMatrixManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::TRANSFORM_MATRIX_CN; } const double * default_value(void) { return ID_MATRIX; } void dump_value(std::ostream & out, const Matrix & matrix) { // always ignore tm[4] and tm[5] because // we have already shifted the origin // TODO: recognize common matices const auto & m = matrix.m; if(tm_equal(m, ID_MATRIX, 4)) { auto prefixes = {"", "-ms-", "-moz-", "-webkit-", "-o-"}; for(auto iter = prefixes.begin(); iter != prefixes.end(); ++iter) out << *iter << "transform:none;"; } else { auto prefixes = {"", "-ms-", "-moz-", "-webkit-", "-o-"}; for(auto iter = prefixes.begin(); iter != prefixes.end(); ++iter) { // PDF use a different coordinate system from Web out << *iter << "transform:matrix(" << round(m[0]) << ',' << round(-m[1]) << ',' << round(-m[2]) << ',' << round(m[3]) << ','; out << "0,0);"; } } } }; } // namespace pdf2htmlEX #endif //STATEMANAGER_H__