1
0
mirror of https://github.com/pdf2htmlEX/pdf2htmlEX.git synced 2024-10-06 12:01:39 +00:00
pdf2htmlEX/src/util/StateManager.h

420 lines
13 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-02-05 05:57:11 +00:00
#include "util/math.h"
2013-02-05 10:19:25 +00:00
#include "util/CSSClassNames.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-02-03 16:40:07 +00:00
{
reset();
}
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
// usually called at the beginning of a page
2013-02-05 05:57:11 +00:00
void reset(void) {
2013-02-05 12:27:29 +00:00
_install(imp->default_value());
2013-02-05 05:57:11 +00:00
}
2013-02-03 09:36:28 +00:00
2013-02-02 19:35:56 +00:00
/*
2013-02-05 05:57:11 +00:00
* install new_value if changed (equal() should be faster than map::lower_bound)
2013-02-03 09:36:28 +00:00
* return if the state has been indeed changed
2013-02-02 19:35:56 +00:00
*/
2013-02-05 05:57:11 +00:00
bool install(double new_value) {
2013-02-03 09:36:28 +00:00
if(equal(new_value, value))
return false;
2013-02-05 11:51:04 +00:00
_install(new_value);
return true;
2013-02-03 09:36:28 +00:00
}
2013-02-05 05:57:11 +00:00
2013-02-05 06:36:36 +00:00
long long get_id (void) const { return id; }
double get_value (void) const { return value; }
double get_actual_value (void) const { return actual_value; }
2013-02-05 05:57:11 +00:00
void dump_css(std::ostream & out) {
for(auto iter = value_map.begin(); iter != value_map.end(); ++iter)
{
2013-02-05 10:19:25 +00:00
out << "." << imp->get_css_class_name() << iter->second << "{";
2013-02-05 05:57:11 +00:00
imp->dump_value(out, iter->first);
out << "}" << std::endl;
}
}
protected:
// this version of install does not check if value has been updated
2013-02-05 11:51:04 +00:00
// return if a new entry has been created
2013-02-05 05:57:11 +00:00
bool _install(double new_value) {
2013-02-03 09:36:28 +00:00
value = new_value;
auto iter = value_map.lower_bound(new_value - eps);
2013-02-03 16:40:07 +00:00
if((iter != value_map.end()) && (abs(iter->first - value) <= eps))
{
2013-02-03 09:36:28 +00:00
actual_value = iter->first;
2013-02-05 05:57:11 +00:00
id = iter->second;
return false;
2013-02-03 09:36:28 +00:00
}
2013-02-05 05:57:11 +00:00
id = value_map.size();
2013-02-05 12:27:29 +00:00
actual_value = value_map.insert(std::make_pair(new_value, id)).first->first;
2013-02-05 05:57:11 +00:00
return true;
2013-02-03 16:40:07 +00:00
}
double eps;
2013-02-03 09:36:28 +00:00
Imp * imp;
2013-02-02 19:35:56 +00:00
2013-02-03 09:36:28 +00:00
long long id;
double value; // the value we are tracking
double actual_value; // the value we actually exported to HTML
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 *
template <class Imp>
class StateManager<Matrix, Imp>
{
public:
StateManager()
: imp(static_cast<Imp*>(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; }
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)))
{
id = iter->second;
return false;
}
id = value_map.size();
2013-02-05 13:56:19 +00:00
value_map.insert(std::make_pair(value, id));
2013-02-05 12:27:29 +00:00
return true;
}
Imp * imp;
long long id;
Matrix 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<Matrix, long long, Matrix_less> value_map;
};
2013-02-05 13:56:19 +00:00
template <class Imp>
class StateManager<GfxRGB, Imp>
{
public:
StateManager()
: imp(static_cast<Imp*>(this))
{ }
void reset(void) {
is_transparent = true;
id = -1;
}
bool install(const GfxRGB & new_value) {
if((!is_transparent) && gfxrgb_equal_obj(new_value, value))
return false;
_install(new_value);
return true;
}
bool install_transparent (void) {
if(is_transparent)
return false;
_install_transparent();
return true;
}
long long get_id (void) const { return id; }
const GfxRGB & get_value (void) const { return value; }
bool get_is_transparent (void) const { return is_transparent; }
void dump_css(std::ostream & out) {
out << "." << imp->get_css_class_name() << CSS::INVALID_ID << "{";
imp->dump_transparent(out);
out << "}" << std::endl;
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:
bool _install(const GfxRGB & new_value) {
is_transparent = false;
value = new_value;
auto iter = value_map.find(new_value);
if(iter != value_map.end())
{
id = iter->second;
return false;
}
id = value_map.size();
value_map.insert(std::make_pair(value, id));
return true;
}
bool _install_transparent(void) {
is_transparent = true;
id = -1;
return false;
}
Imp * imp;
long long id;
GfxRGB value;
bool is_transparent;
class GfxRGB_hash
{
public:
size_t operator () (const GfxRGB & rgb) const
{
return ( (((size_t)colToByte(rgb.r)) << 16)
| (((size_t)colToByte(rgb.g)) << 8)
| ((size_t)colToByte(rgb.b))
);
}
};
class GfxRGB_equal
{
public:
bool operator ()(const GfxRGB & rgb1, const GfxRGB & rgb2) const
{
return ((rgb1.r == rgb2.r) && (rgb1.g == rgb2.g) && (rgb1.b == rgb2.b));
}
};
GfxRGB_equal gfxrgb_equal_obj;
std::unordered_map<GfxRGB, long long, GfxRGB_hash, GfxRGB_equal> value_map;
};
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-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-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-05 06:45:40 +00:00
class RiseManager : public StateManager<double, RiseManager>
{
public:
2013-02-05 10:19:25 +00:00
static const char * get_css_class_name (void) { return CSS::RISE_CN; }
2013-02-05 06:45:40 +00:00
double default_value(void) { return 0; }
void dump_value(std::ostream & out, double value) { out << "top:" << round(-value) << "px;"; }
};
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) ? "display:inline-block;width:"
: "display:inline;margin-left:")
<< round(value) << "px;";
}
2013-02-05 06:55:44 +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-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-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
// 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);";
}
}
}
};
2013-02-05 13:56:19 +00:00
class FillColorManager : public StateManager<GfxRGB, FillColorManager>
{
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) {
// normal CSS
out << "." << get_css_class_name() << CSS::INVALID_ID << "{color:white;}" << std::endl;
for(auto iter = value_map.begin(); iter != value_map.end(); ++iter)
{
out << "." << get_css_class_name() << iter->second
<< "{color:" << iter->first << ";}" << std::endl;
}
// webkit
out << CSS::WEBKIT_ONLY << "{" << std::endl;
out << "." << get_css_class_name() << CSS::INVALID_ID << "{color:transparent;}" << std::endl;
out << "}" << std::endl;
}
};
class StrokeColorManager : public StateManager<GfxRGB, StrokeColorManager>
{
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 iter = value_map.begin(); iter != value_map.end(); ++iter)
{
// TODO: take the stroke width from the graphics state,
// currently using 0.015em as a good default
out << "." << get_css_class_name() << iter->second << "{text-shadow:"
<< "-0.015em 0 " << iter->first << ","
<< "0 0.015em " << iter->first << ","
<< "0.015em 0 " << iter->first << ","
<< "0 -0.015em " << iter->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 iter = value_map.begin(); iter != value_map.end(); ++iter)
{
out << "." << get_css_class_name() << iter->second
<< "{-webkit-text-stroke:0.015em " << iter->first << ";text-shadow:none;}" << std::endl;
}
out << "}" << std::endl;
}
};
2013-02-02 19:35:56 +00:00
} // namespace pdf2htmlEX
2013-02-05 06:36:36 +00:00
#endif //STATEMANAGER_H__