/* * StateManager.h * * manage reusable CSS classes * * Copyright (C) 2013 Lu Wang */ #ifndef STATEMANAGER_H__ #define STATEMANAGER_H__ #include #include #include #include "Color.h" #include "util/math.h" #include "util/css_const.h" namespace pdf2htmlEX { template class StateManager {}; template class StateManager { public: StateManager() : eps(0) , imp(static_cast(this)) { } // values no farther than eps are treated as equal void set_eps (double eps) { this->eps = eps; } double get_eps (void) const { return eps; } // install new_value into the map // return the corresponding id long long install(double new_value, double * actual_value_ptr = nullptr) { auto iter = value_map.lower_bound(new_value - eps); if((iter != value_map.end()) && (std::abs(iter->first - new_value) <= eps)) { if(actual_value_ptr != nullptr) *actual_value_ptr = iter->first; return iter->second; } long long id = value_map.size(); double v = value_map.insert(iter, std::make_pair(new_value, id))->first; if(actual_value_ptr != nullptr) *actual_value_ptr = v; return id; } void dump_css(std::ostream & out) { for(auto & p : value_map) { out << "." << imp->get_css_class_name() << p.second << "{"; imp->dump_value(out, p.first); out << "}" << std::endl; } } void dump_print_css(std::ostream & out, double scale) { for(auto & p : value_map) { out << "." << imp->get_css_class_name() << p.second << "{"; imp->dump_print_value(out, p.first, scale); out << "}" << std::endl; } } protected: double eps; Imp * imp; std::map value_map; }; // Be careful about the mixed usage of Matrix and const double * // the input is usually double *, which might be changed, so we have to copy the content out // in the map we use Matrix instead of double * such that the array may be automatically release when deconstructing template class StateManager { public: StateManager() : imp(static_cast(this)) { } // return id long long install(const double * new_value) { Matrix m; memcpy(m.m, new_value, sizeof(m.m)); auto iter = value_map.lower_bound(m); if((iter != value_map.end()) && (tm_equal(m.m, iter->first.m, 4))) { return iter->second; } long long id = value_map.size(); value_map.insert(iter, std::make_pair(m, id)); return id; } void dump_css(std::ostream & out) { for(auto & p : value_map) { out << "." << imp->get_css_class_name() << p.second << "{"; imp->dump_value(out, p.first); out << "}" << std::endl; } } void dump_print_css(std::ostream & out, double scale) {} protected: Imp * imp; struct Matrix_less { 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]) return true; if(m1.m[i] > m2.m[i]) return false; } return false; } }; std::map value_map; }; template class StateManager { public: StateManager() : imp(static_cast(this)) { } long long install(const Color & new_value) { auto iter = value_map.find(new_value); if(iter != value_map.end()) { return iter->second; } long long id = value_map.size(); value_map.insert(std::make_pair(new_value, id)); return id; } void dump_css(std::ostream & out) { out << "." << imp->get_css_class_name() << CSS::INVALID_ID << "{"; imp->dump_transparent(out); out << "}" << std::endl; for(auto & p : value_map) { out << "." << imp->get_css_class_name() << p.second << "{"; imp->dump_value(out, p.first); out << "}" << std::endl; } } void dump_print_css(std::ostream & out, double scale) {} protected: Imp * imp; struct Color_hash { size_t operator () (const Color & color) const { if(color.transparent) { return (~((size_t)0)); } else { return ( ((((size_t)colToByte(color.rgb.r)) & 0xff) << 16) | ((((size_t)colToByte(color.rgb.g)) & 0xff) << 8) | (((size_t)colToByte(color.rgb.b)) & 0xff) ); } } }; std::unordered_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;"; } void dump_print_value(std::ostream & out, double value, double scale) { out << "font-size:" << round(value*scale) << "pt;"; } }; 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;"; } void dump_print_value(std::ostream & out, double value, double scale) { out << "letter-spacing:" << round(value*scale) << "pt;"; } }; 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;"; } void dump_print_value(std::ostream & out, double value, double scale) { out << "word-spacing:" << round(value*scale) << "pt;"; } }; class VerticalAlignManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::VERTICAL_ALIGN_CN; } double default_value(void) { return 0; } void dump_value(std::ostream & out, double value) { out << "vertical-align:" << round(value) << "px;"; } void dump_print_value(std::ostream & out, double value, double scale) { out << "vertical-align:" << round(value*scale) << "pt;"; } }; 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) ? "width:" : "margin-left:") << round(value) << "px;"; } void dump_print_value(std::ostream & out, double value, double scale) { value *= scale; out << ((value > 0) ? "width:" : "margin-left:") << round(value) << "pt;"; } }; class WidthManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::WIDTH_CN; } double default_value(void) { return 0; } void dump_value(std::ostream & out, double value) { out << "width:" << round(value) << "px;"; } void dump_print_value(std::ostream & out, double value, double scale) { out << "width:" << round(value*scale) << "pt;"; } }; class BottomManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::BOTTOM_CN; } double default_value(void) { return 0; } void dump_value(std::ostream & out, double value) { out << "bottom:" << round(value) << "px;"; } void dump_print_value(std::ostream & out, double value, double scale) { out << "bottom:" << round(value*scale) << "pt;"; } }; 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;"; } void dump_print_value(std::ostream & out, double value, double scale) { out << "height:" << round(value*scale) << "pt;"; } }; 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;"; } void dump_print_value(std::ostream & out, double value, double scale) { out << "left:" << round(value*scale) << "pt;"; } }; 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 matrices const auto & m = matrix.m; auto prefixes = {"", "-ms-", "-webkit-"}; if(tm_equal(m, ID_MATRIX, 4)) { for(auto & s : prefixes) out << s << "transform:none;"; } else { for(auto & s : prefixes) { // PDF use a different coordinate system from Web out << s << "transform:matrix(" << round(m[0]) << ',' << round(-m[1]) << ',' << round(-m[2]) << ',' << round(m[3]) << ','; out << "0,0);"; } } } }; class FillColorManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::FILL_COLOR_CN; } /* override base's method, as we need some workaround in CSS */ void dump_css(std::ostream & out) { for(auto & p : value_map) { out << "." << get_css_class_name() << p.second << "{color:" << p.first << ";}" << std::endl; } } }; class StrokeColorManager : public StateManager { public: static const char * get_css_class_name (void) { return CSS::STROKE_COLOR_CN; } /* override base's method, as we need some workaround in CSS */ void dump_css(std::ostream & out) { // normal CSS out << "." << get_css_class_name() << CSS::INVALID_ID << "{text-shadow:none;}" << std::endl; for(auto & p : value_map) { // TODO: take the stroke width from the graphics state, // currently using 0.015em as a good default out << "." << get_css_class_name() << p.second << "{text-shadow:" << "-0.015em 0 " << p.first << "," << "0 0.015em " << p.first << "," << "0.015em 0 " << p.first << "," << "0 -0.015em " << p.first << ";" << "}" << std::endl; } // webkit out << CSS::WEBKIT_ONLY << "{" << std::endl; out << "." << get_css_class_name() << CSS::INVALID_ID << "{-webkit-text-stroke:0px transparent;}" << std::endl; for(auto & p : value_map) { out << "." << get_css_class_name() << p.second << "{-webkit-text-stroke:0.015em " << p.first << ";text-shadow:none;}" << std::endl; } out << "}" << std::endl; } }; ///////////////////////////////////// /* * Manage the background image sizes * * We don't merge similar values, since they are bound with PAGE_CONTENT_BOX_number */ class BGImageSizeManager { public: void install(int page_no, double width, double height){ value_map.insert(std::make_pair(page_no, std::make_pair(width, height))); } void dump_css(std::ostream & out) { for(auto & p : value_map) { const auto & s = p.second; out << "." << CSS::PAGE_CONTENT_BOX_CN << p.first << "{"; out << "background-size:" << round(s.first) << "px " << round(s.second) << "px;"; out << "}" << std::endl; } } void dump_print_css(std::ostream & out, double scale) { for(auto & p : value_map) { const auto & s = p.second; out << "." << CSS::PAGE_CONTENT_BOX_CN << p.first << "{"; out << "background-size:" << round(s.first * scale) << "pt " << round(s.second * scale) << "pt;"; out << "}" << std::endl; } } private: std::unordered_map> value_map; }; struct AllStateManager { TransformMatrixManager transform_matrix; VerticalAlignManager vertical_align; StrokeColorManager stroke_color; LetterSpaceManager letter_space; WhitespaceManager whitespace; WordSpaceManager word_space; FillColorManager fill_color; FontSizeManager font_size; BottomManager bottom; HeightManager height; WidthManager width; LeftManager left; BGImageSizeManager bgimage_size; }; } // namespace pdf2htmlEX #endif //STATEMANAGER_H__