pdf2htmlEX/src/StateManager.h

431 lines
14 KiB
C
Raw Normal View History

2013-02-02 19:35:56 +00:00
/*
2013-02-05 06:36:36 +00:00
* StateManager.h
2013-02-02 19:35:56 +00:00
*
* manage reusable CSS classes
*
* Copyright (C) 2013 Lu Wang <coolwanglu@gmail.com>
*/
2013-02-05 06:36:36 +00:00
#ifndef STATEMANAGER_H__
#define STATEMANAGER_H__
2013-02-02 19:35:56 +00:00
2013-02-03 16:40:07 +00:00
#include <iostream>
2013-02-03 09:36:28 +00:00
#include <map>
2013-02-05 13:56:19 +00:00
#include <unordered_map>
2013-02-02 19:35:56 +00:00
2013-04-06 08:45:01 +00:00
#include "Color.h"
2013-02-05 05:57:11 +00:00
#include "util/math.h"
2013-02-27 18:11:34 +00:00
#include "util/css_const.h"
2013-02-02 19:35:56 +00:00
namespace pdf2htmlEX {
2013-02-05 06:36:36 +00:00
template<class ValueType, class Imp> class StateManager {};
2013-02-03 09:36:28 +00:00
template<class Imp>
2013-02-05 06:36:36 +00:00
class StateManager<double, Imp>
2013-02-02 19:35:56 +00:00
{
public:
2013-02-05 06:36:36 +00:00
StateManager()
2013-02-03 16:40:07 +00:00
: eps(0)
2013-02-05 05:57:11 +00:00
, imp(static_cast<Imp*>(this))
2013-04-04 13:19:28 +00:00
{ }
2013-02-03 16:40:07 +00:00
2013-02-05 11:53:24 +00:00
// values no farther than eps are treated as equal
void set_eps (double eps) {
2013-02-03 16:40:07 +00:00
this->eps = eps;
}
2013-02-02 19:35:56 +00:00
2013-04-03 06:04:39 +00:00
double get_eps (void) const {
return eps;
}
2013-04-04 13:19:28 +00:00
// install new_value into the map
// return the corresponding id
2013-03-30 14:37:20 +00:00
long long install(double new_value, double * actual_value_ptr = nullptr) {
auto iter = value_map.lower_bound(new_value - eps);
2014-06-24 08:31:33 +00:00
if((iter != value_map.end()) && (std::abs(iter->first - new_value) <= eps))
2013-03-30 14:37:20 +00:00
{
if(actual_value_ptr != nullptr)
*actual_value_ptr = iter->first;
return iter->second;
}
long long id = value_map.size();
2013-03-30 17:00:04 +00:00
double v = value_map.insert(iter, std::make_pair(new_value, id))->first;
2013-03-30 14:37:20 +00:00
if(actual_value_ptr != nullptr)
*actual_value_ptr = v;
return id;
}
2013-02-05 05:57:11 +00:00
void dump_css(std::ostream & out) {
2014-11-16 14:04:02 +00:00
for(auto & p : value_map)
2013-02-05 05:57:11 +00:00
{
2014-11-16 14:04:02 +00:00
out << "." << imp->get_css_class_name() << p.second << "{";
imp->dump_value(out, p.first);
2013-02-05 05:57:11 +00:00
out << "}" << std::endl;
}
}
2013-02-06 12:29:48 +00:00
void dump_print_css(std::ostream & out, double scale) {
2014-11-16 14:04:02 +00:00
for(auto & p : value_map)
2013-02-06 12:29:48 +00:00
{
2014-11-16 14:04:02 +00:00
out << "." << imp->get_css_class_name() << p.second << "{";
imp->dump_print_value(out, p.first, scale);
2013-02-06 12:29:48 +00:00
out << "}" << std::endl;
}
}
2013-02-05 05:57:11 +00:00
protected:
2013-02-03 16:40:07 +00:00
double eps;
2013-02-03 09:36:28 +00:00
Imp * imp;
std::map<double, long long> value_map;
2013-02-02 19:35:56 +00:00
};
2013-02-05 12:27:29 +00:00
// Be careful about the mixed usage of Matrix and const double *
2013-03-30 14:37:20 +00:00
// the input is usually double *, which might be changed, so we have to copy the content out
2014-07-13 23:59:30 +00:00
// in the map we use Matrix instead of double * such that the array may be automatically release when deconstructing
2013-02-05 12:27:29 +00:00
template <class Imp>
class StateManager<Matrix, Imp>
{
public:
StateManager()
: imp(static_cast<Imp*>(this))
{ }
2013-04-04 13:19:28 +00:00
// 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;
}
2013-03-30 14:37:20 +00:00
2013-04-04 13:19:28 +00:00
long long id = value_map.size();
value_map.insert(iter, std::make_pair(m, id));
return id;
2013-02-05 12:27:29 +00:00
}
void dump_css(std::ostream & out) {
2014-11-16 14:04:02 +00:00
for(auto & p : value_map)
2013-02-05 12:27:29 +00:00
{
2014-11-16 14:04:02 +00:00
out << "." << imp->get_css_class_name() << p.second << "{";
imp->dump_value(out, p.first);
2013-02-05 12:27:29 +00:00
out << "}" << std::endl;
}
}
2013-02-06 12:29:48 +00:00
void dump_print_css(std::ostream & out, double scale) {}
2013-02-05 12:27:29 +00:00
protected:
Imp * imp;
2013-06-13 10:00:25 +00:00
struct Matrix_less
2013-02-05 12:27:29 +00:00
{
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])
2013-02-05 12:27:29 +00:00
return true;
if(m1.m[i] > m2.m[i])
2013-02-05 12:27:29 +00:00
return false;
}
return false;
}
};
2013-03-30 14:37:20 +00:00
2013-02-05 12:27:29 +00:00
std::map<Matrix, long long, Matrix_less> value_map;
};
2013-02-05 13:56:19 +00:00
template <class Imp>
2013-04-04 13:19:28 +00:00
class StateManager<Color, Imp>
2013-02-05 13:56:19 +00:00
{
public:
StateManager()
: imp(static_cast<Imp*>(this))
{ }
2013-04-04 13:19:28 +00:00
long long install(const Color & new_value) {
auto iter = value_map.find(new_value);
if(iter != value_map.end())
{
return iter->second;
}
2013-02-05 13:56:19 +00:00
2013-04-04 13:19:28 +00:00
long long id = value_map.size();
value_map.insert(std::make_pair(new_value, id));
return id;
2013-02-05 13:56:19 +00:00
}
void dump_css(std::ostream & out) {
out << "." << imp->get_css_class_name() << CSS::INVALID_ID << "{";
imp->dump_transparent(out);
out << "}" << std::endl;
2014-11-16 14:04:02 +00:00
for(auto & p : value_map)
2013-02-05 13:56:19 +00:00
{
2014-11-16 14:04:02 +00:00
out << "." << imp->get_css_class_name() << p.second << "{";
imp->dump_value(out, p.first);
2013-02-05 13:56:19 +00:00
out << "}" << std::endl;
}
}
2013-02-06 12:29:48 +00:00
void dump_print_css(std::ostream & out, double scale) {}
2013-02-05 13:56:19 +00:00
protected:
Imp * imp;
2013-06-13 10:00:25 +00:00
struct Color_hash
2013-02-05 13:56:19 +00:00
{
2013-04-04 13:19:28 +00:00
size_t operator () (const Color & color) const
2013-02-05 13:56:19 +00:00
{
2013-04-04 13:19:28 +00:00
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)
);
}
2013-02-05 13:56:19 +00:00
}
};
2013-04-04 13:19:28 +00:00
std::unordered_map<Color, long long, Color_hash> value_map;
2013-02-05 13:56:19 +00:00
};
2013-02-05 12:27:29 +00:00
/////////////////////////////////////
// Specific state managers
2013-02-05 06:36:36 +00:00
class FontSizeManager : public StateManager<double, FontSizeManager>
2013-02-03 16:40:07 +00:00
{
public:
2013-02-05 10:19:25 +00:00
static const char * get_css_class_name (void) { return CSS::FONT_SIZE_CN; }
2013-02-03 16:40:07 +00:00
double default_value(void) { return 0; }
2013-02-05 05:57:11 +00:00
void dump_value(std::ostream & out, double value) { out << "font-size:" << round(value) << "px;"; }
2013-02-06 12:29:48 +00:00
void dump_print_value(std::ostream & out, double value, double scale) { out << "font-size:" << round(value*scale) << "pt;"; }
2013-02-03 16:40:07 +00:00
};
2013-02-05 10:19:25 +00:00
class LetterSpaceManager : public StateManager<double, LetterSpaceManager>
2013-02-03 16:40:07 +00:00
{
public:
2013-02-05 10:19:25 +00:00
static const char * get_css_class_name (void) { return CSS::LETTER_SPACE_CN; }
2013-02-03 16:40:07 +00:00
double default_value(void) { return 0; }
2013-02-05 05:57:11 +00:00
void dump_value(std::ostream & out, double value) { out << "letter-spacing:" << round(value) << "px;"; }
2013-02-06 12:29:48 +00:00
void dump_print_value(std::ostream & out, double value, double scale) { out << "letter-spacing:" << round(value*scale) << "pt;"; }
2013-02-03 16:40:07 +00:00
};
2013-02-05 06:36:36 +00:00
class WordSpaceManager : public StateManager<double, WordSpaceManager>
2013-02-05 06:21:07 +00:00
{
public:
2013-02-05 10:19:25 +00:00
static const char * get_css_class_name (void) { return CSS::WORD_SPACE_CN;}
2013-02-05 06:21:07 +00:00
double default_value(void) { return 0; }
void dump_value(std::ostream & out, double value) { out << "word-spacing:" << round(value) << "px;"; }
2013-02-06 12:29:48 +00:00
void dump_print_value(std::ostream & out, double value, double scale) { out << "word-spacing:" << round(value*scale) << "pt;"; }
2013-02-05 06:21:07 +00:00
};
2013-04-05 13:53:34 +00:00
class VerticalAlignManager : public StateManager<double, VerticalAlignManager>
2013-02-05 06:45:40 +00:00
{
public:
2013-04-05 13:53:34 +00:00
static const char * get_css_class_name (void) { return CSS::VERTICAL_ALIGN_CN; }
2013-02-05 06:45:40 +00:00
double default_value(void) { return 0; }
2013-04-04 08:28:59 +00:00
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;"; }
2013-02-05 06:45:40 +00:00
};
2013-02-05 06:55:44 +00:00
class WhitespaceManager : public StateManager<double, WhitespaceManager>
{
public:
2013-02-05 10:19:25 +00:00
static const char * get_css_class_name (void) { return CSS::WHITESPACE_CN; }
2013-02-05 06:55:44 +00:00
double default_value(void) { return 0; }
2013-02-05 07:05:36 +00:00
void dump_value(std::ostream & out, double value) {
out << ((value > 0) ? "width:"
: "margin-left:")
2013-02-05 07:05:36 +00:00
<< round(value) << "px;";
}
2013-02-06 12:29:48 +00:00
void dump_print_value(std::ostream & out, double value, double scale)
{
value *= scale;
out << ((value > 0) ? "width:"
: "margin-left:")
2013-02-06 12:29:48 +00:00
<< round(value) << "pt;";
}
2013-02-05 06:55:44 +00:00
};
2013-02-06 12:09:40 +00:00
class WidthManager : public StateManager<double, WidthManager>
{
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;"; }
2013-02-06 12:29:48 +00:00
void dump_print_value(std::ostream & out, double value, double scale) { out << "width:" << round(value*scale) << "pt;"; }
2013-02-06 12:09:40 +00:00
};
class BottomManager : public StateManager<double, BottomManager>
{
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;"; }
2013-02-06 12:29:48 +00:00
void dump_print_value(std::ostream & out, double value, double scale) { out << "bottom:" << round(value*scale) << "pt;"; }
2013-02-06 12:09:40 +00:00
};
2013-02-05 06:51:00 +00:00
class HeightManager : public StateManager<double, HeightManager>
{
public:
2013-02-05 10:19:25 +00:00
static const char * get_css_class_name (void) { return CSS::HEIGHT_CN; }
2013-02-05 06:51:00 +00:00
double default_value(void) { return 0; }
void dump_value(std::ostream & out, double value) { out << "height:" << round(value) << "px;"; }
2013-02-06 12:29:48 +00:00
void dump_print_value(std::ostream & out, double value, double scale) { out << "height:" << round(value*scale) << "pt;"; }
2013-02-05 06:51:00 +00:00
};
2013-02-05 07:05:36 +00:00
class LeftManager : public StateManager<double, LeftManager>
{
public:
2013-02-05 10:19:25 +00:00
static const char * get_css_class_name (void) { return CSS::LEFT_CN; }
2013-02-05 07:05:36 +00:00
double default_value(void) { return 0; }
void dump_value(std::ostream & out, double value) { out << "left:" << round(value) << "px;"; }
2013-02-06 12:29:48 +00:00
void dump_print_value(std::ostream & out, double value, double scale) { out << "left:" << round(value*scale) << "pt;"; }
2013-02-05 07:05:36 +00:00
};
2013-02-05 12:27:29 +00:00
class TransformMatrixManager : public StateManager<Matrix, TransformMatrixManager>
{
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
2014-07-13 23:59:30 +00:00
// TODO: recognize common matrices
2013-02-05 12:27:29 +00:00
const auto & m = matrix.m;
2013-09-28 03:35:54 +00:00
auto prefixes = {"", "-ms-", "-webkit-"};
2013-02-05 12:27:29 +00:00
if(tm_equal(m, ID_MATRIX, 4))
{
2014-11-16 14:04:02 +00:00
for(auto & s : prefixes)
out << s << "transform:none;";
2013-02-05 12:27:29 +00:00
}
else
{
2014-11-16 14:04:02 +00:00
for(auto & s : prefixes)
2013-02-05 12:27:29 +00:00
{
// PDF use a different coordinate system from Web
2014-11-16 14:04:02 +00:00
out << s << "transform:matrix("
2013-02-05 12:27:29 +00:00
<< round(m[0]) << ','
<< round(-m[1]) << ','
<< round(-m[2]) << ','
<< round(m[3]) << ',';
out << "0,0);";
}
}
}
};
2013-04-04 13:19:28 +00:00
class FillColorManager : public StateManager<Color, FillColorManager>
2013-02-05 13:56:19 +00:00
{
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) {
2014-11-16 14:04:02 +00:00
for(auto & p : value_map)
2013-02-05 13:56:19 +00:00
{
2014-11-16 14:04:02 +00:00
out << "." << get_css_class_name() << p.second
<< "{color:" << p.first << ";}" << std::endl;
2013-02-05 13:56:19 +00:00
}
}
};
2013-04-04 13:19:28 +00:00
class StrokeColorManager : public StateManager<Color, StrokeColorManager>
2013-02-05 13:56:19 +00:00
{
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;
2014-11-16 14:04:02 +00:00
for(auto & p : value_map)
2013-02-05 13:56:19 +00:00
{
// TODO: take the stroke width from the graphics state,
// currently using 0.015em as a good default
2014-11-16 14:04:02 +00:00
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 << ";"
2013-02-05 13:56:19 +00:00
<< "}" << std::endl;
}
// webkit
out << CSS::WEBKIT_ONLY << "{" << std::endl;
out << "." << get_css_class_name() << CSS::INVALID_ID << "{-webkit-text-stroke:0px transparent;}" << std::endl;
2014-11-16 14:04:02 +00:00
for(auto & p : value_map)
2013-02-05 13:56:19 +00:00
{
2014-11-16 14:04:02 +00:00
out << "." << get_css_class_name() << p.second
<< "{-webkit-text-stroke:0.015em " << p.first << ";text-shadow:none;}" << std::endl;
2013-02-05 13:56:19 +00:00
}
out << "}" << std::endl;
}
};
2013-02-28 14:37:15 +00:00
/////////////////////////////////////
/*
* Manage the background image sizes
2013-04-07 14:39:02 +00:00
*
* We don't merge similar values, since they are bound with PAGE_CONTENT_BOX_number
2013-02-28 14:37:15 +00:00
*/
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) {
2014-11-16 14:04:02 +00:00
for(auto & p : value_map)
2013-02-28 14:37:15 +00:00
{
2014-11-16 14:04:02 +00:00
const auto & s = p.second;
out << "." << CSS::PAGE_CONTENT_BOX_CN << p.first << "{";
2013-02-28 14:37:15 +00:00
out << "background-size:" << round(s.first) << "px " << round(s.second) << "px;";
out << "}" << std::endl;
}
}
void dump_print_css(std::ostream & out, double scale) {
2014-11-16 14:04:02 +00:00
for(auto & p : value_map)
2013-02-28 14:37:15 +00:00
{
2014-11-16 14:04:02 +00:00
const auto & s = p.second;
out << "." << CSS::PAGE_CONTENT_BOX_CN << p.first << "{";
2013-02-28 14:37:15 +00:00
out << "background-size:" << round(s.first * scale) << "pt " << round(s.second * scale) << "pt;";
out << "}" << std::endl;
}
}
private:
std::unordered_map<int, std::pair<double,double>> value_map;
};
2013-04-07 08:10:52 +00:00
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;
};
2013-02-02 19:35:56 +00:00
} // namespace pdf2htmlEX
2013-02-05 06:36:36 +00:00
#endif //STATEMANAGER_H__