1
0
mirror of https://github.com/pdf2htmlEX/pdf2htmlEX.git synced 2024-09-19 13:50:06 +00:00
pdf2htmlEX/src/HTMLRenderer/draw.cc

421 lines
11 KiB
C++
Raw Normal View History

2012-10-01 17:59:04 +00:00
/*
* Draw.cc
*
* Handling path drawing
*
* by WangLu
* 2012.10.01
*/
2012-10-02 18:19:40 +00:00
#include <algorithm>
#include <cmath>
#include <sstream>
#include <vector>
#include <iostream>
2012-10-01 17:59:04 +00:00
#include "HTMLRenderer.h"
2012-11-29 10:28:07 +00:00
#include "util/misc.h"
2012-11-29 10:16:05 +00:00
#include "util/math.h"
2012-11-29 09:28:05 +00:00
#include "util/namespace.h"
2012-10-01 17:59:04 +00:00
namespace pdf2htmlEX {
using std::swap;
2012-10-02 18:19:40 +00:00
using std::min;
using std::max;
using std::acos;
using std::asin;
using std::ostringstream;
using std::sqrt;
using std::vector;
using std::ostream;
2012-10-01 17:59:04 +00:00
static bool is_horizontal_line(GfxSubpath * path)
{
return ((path->getNumPoints() == 2)
&& (!path->getCurve(1))
2012-11-29 10:16:05 +00:00
&& (equal(path->getY(0), path->getY(1))));
2012-10-01 17:59:04 +00:00
}
2012-10-03 04:42:00 +00:00
static bool is_vertical_line(GfxSubpath * path)
{
return ((path->getNumPoints() == 2)
&& (!path->getCurve(1))
2012-11-29 10:16:05 +00:00
&& (equal(path->getX(0), path->getX(1))));
2012-10-03 04:42:00 +00:00
}
2012-10-01 17:59:04 +00:00
static bool is_rectangle(GfxSubpath * path)
{
if (!(((path->getNumPoints() != 4) && (path->isClosed()))
|| ((path->getNumPoints() == 5)
2012-11-29 10:16:05 +00:00
&& equal(path->getX(0), path->getX(4))
&& equal(path->getY(0), path->getY(4)))))
2012-10-01 17:59:04 +00:00
return false;
2012-10-01 20:06:38 +00:00
for(int i = 1; i < path->getNumPoints(); ++i)
if(path->getCurve(i))
return false;
2012-11-29 10:16:05 +00:00
return (equal(path->getY(0), path->getY(1))
&& equal(path->getX(1), path->getX(2))
&& equal(path->getY(2), path->getY(3))
&& equal(path->getX(3), path->getX(0)))
|| (equal(path->getX(0), path->getX(1))
&& equal(path->getY(1), path->getY(2))
&& equal(path->getX(2), path->getX(3))
&& equal(path->getY(3), path->getY(0)));
2012-10-01 17:59:04 +00:00
}
2012-10-02 18:19:40 +00:00
static void get_shading_bbox(GfxState * state, GfxShading * shading,
double & x1, double & y1, double & x2, double & y2)
{
// from SplashOutputDev.cc in poppler
if(shading->getHasBBox())
{
shading->getBBox(&x1, &y1, &x2, &y2);
}
else
{
state->getClipBBox(&x1, &y1, &x2, &y2);
Matrix ctm, ictm;
state->getCTM(&ctm);
ctm.invertTo(&ictm);
double x[4], y[4];
ictm.transform(x1, y1, &x[0], &y[0]);
ictm.transform(x2, y1, &x[1], &y[1]);
ictm.transform(x1, y2, &x[2], &y[2]);
ictm.transform(x2, y2, &x[3], &y[3]);
x1 = x2 = x[0];
y1 = y2 = y[0];
for(int i = 1; i < 4; ++i)
{
x1 = min<double>(x1, x[i]);
y1 = min<double>(y1, y[i]);
x2 = max<double>(x2, x[i]);
y2 = max<double>(y2, y[i]);
}
}
}
/*
* Note that the coordinate system in HTML and PDF are different
2012-10-08 08:36:47 +00:00
* This functions returns the angle of vector (dx,dy) in the PDF coordinate system, in rad
*/
static double get_angle(double dx, double dy)
2012-10-02 18:19:40 +00:00
{
2012-11-29 10:16:05 +00:00
double r = hypot(dx, dy);
2012-10-02 18:19:40 +00:00
2012-10-03 04:42:00 +00:00
/*
* acos always returns [0, pi]
*/
2012-10-02 18:19:40 +00:00
double ang = acos(dx / r);
2012-10-03 04:42:00 +00:00
/*
* for angle below x-axis
*/
if(dy < 0)
ang = -ang;
2012-10-02 18:19:40 +00:00
return ang;
2012-10-02 18:19:40 +00:00
}
class LinearGradient
{
public:
2012-10-03 04:42:00 +00:00
LinearGradient(GfxAxialShading * shading,
2012-10-02 18:19:40 +00:00
double x1, double y1, double x2, double y2);
void dumpto (ostream & out);
static void style_function (void * p, ostream & out)
{
static_cast<LinearGradient*>(p)->dumpto(out);
}
// TODO, add alpha
2012-10-02 18:19:40 +00:00
class ColorStop
{
public:
GfxRGB rgb;
double pos; // [0,1]
};
vector<ColorStop> stops;
double angle;
2012-10-02 18:19:40 +00:00
};
LinearGradient::LinearGradient (GfxAxialShading * shading,
double x1, double y1, double x2, double y2)
{
// coordinate for t = 0 and t = 1
double t0x, t0y, t1x, t1y;
shading->getCoords(&t0x, &t0y, &t1x, &t1y);
angle = get_angle(t1x - t0x, t1y - t0y);
2012-10-02 18:19:40 +00:00
// get the range of t in the box
// from GfxState.cc in poppler
double box_tmin, box_tmax;
{
double idx = t1x - t0x;
double idy = t1y - t0y;
double inv_len = 1.0 / (idx * idx + idy * idy);
idx *= inv_len;
idy *= inv_len;
// t of (x1,y1)
box_tmin = box_tmax = (x1 - t0x) * idx + (y1 - t0y) * idy;
double tdx = (x2 - x1) * idx;
if(tdx < 0)
box_tmin += tdx;
else
box_tmax += tdx;
double tdy = (y2 - y1) * idy;
if(tdy < 0)
box_tmin += tdy;
else
box_tmax += tdy;
}
// get the domain of t in the box
double domain_tmin = max<double>(box_tmin, shading->getDomain0());
double domain_tmax = min<double>(box_tmax, shading->getDomain1());
// TODO: better sampling
// TODO: check background color
{
stops.clear();
double tstep = (domain_tmax - domain_tmin) / 13.0;
for(double t = domain_tmin; t <= domain_tmax; t += tstep)
{
GfxColor color;
shading->getColor(t, &color);
ColorStop stop;
shading->getColorSpace()->getRGB(&color, &stop.rgb);
stop.pos = (t - box_tmin) / (box_tmax - box_tmin);
stops.push_back(stop);
}
}
}
void LinearGradient::dumpto (ostream & out)
{
2012-10-02 19:00:51 +00:00
auto prefixes = {"", "-ms-", "-moz-", "-webkit-", "-o-"};
for(auto iter = prefixes.begin(); iter != prefixes.end(); ++iter)
{
2012-11-29 10:16:05 +00:00
out << "background-image:" << (*iter) << "linear-gradient(" << round(angle) << "rad";
2012-10-02 19:00:51 +00:00
for(auto iter2 = stops.begin(); iter2 != stops.end(); ++iter2)
{
2012-11-29 10:16:05 +00:00
out << "," << (iter2->rgb) << " " << round((iter2->pos) * 100) << "%";
2012-10-02 19:00:51 +00:00
}
out << ");";
}
2012-10-02 18:19:40 +00:00
}
GBool HTMLRenderer::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax)
{
if(!(param->css_draw)) return gFalse;
double x1, y1, x2, y2;
get_shading_bbox(state, shading, x1, y1, x2, y2);
LinearGradient lg(shading, x1, y1, x2, y2);
// TODO: check background color
css_draw_rectangle(x1, y1, x2-x1, y2-y1, state->getCTM(),
nullptr, 0,
nullptr, nullptr,
LinearGradient::style_function, &lg);
return gTrue;
}
2012-10-01 17:59:04 +00:00
//TODO track state
//TODO connection style
2012-10-03 14:46:27 +00:00
bool HTMLRenderer::css_do_path(GfxState *state, bool fill, bool test_only)
2012-10-01 17:59:04 +00:00
{
2012-10-03 14:46:27 +00:00
if(!(param->css_draw)) return false;
2012-10-02 08:06:08 +00:00
2012-10-01 17:59:04 +00:00
GfxPath * path = state->getPath();
2012-10-03 14:46:27 +00:00
/*
* capacity check
*/
for(int i = 0; i < path->getNumSubpaths(); ++i)
{
GfxSubpath * subpath = path->getSubpath(i);
if(!(is_horizontal_line(subpath)
|| is_vertical_line(subpath)
|| is_rectangle(subpath)))
return false;
}
if(test_only)
return true;
2012-10-01 17:59:04 +00:00
for(int i = 0; i < path->getNumSubpaths(); ++i)
{
GfxSubpath * subpath = path->getSubpath(i);
if(is_horizontal_line(subpath))
{
double x1 = subpath->getX(0);
double x2 = subpath->getX(1);
double y = subpath->getY(0);
if(x1 > x2) swap(x1, x2);
2012-10-01 20:06:38 +00:00
GfxRGB stroke_color;
state->getStrokeRGB(&stroke_color);
2012-10-01 17:59:04 +00:00
2012-10-01 20:06:38 +00:00
double lw = state->getLineWidth();
2012-10-02 18:19:40 +00:00
css_draw_rectangle(x1, y - lw/2, x2-x1, lw, state->getCTM(),
2012-10-01 20:06:38 +00:00
nullptr, 0,
2012-10-02 18:19:40 +00:00
nullptr, &stroke_color);
2012-10-01 17:59:04 +00:00
}
2012-10-03 04:42:00 +00:00
else if(is_vertical_line(subpath))
{
double x = subpath->getX(0);
double y1 = subpath->getY(0);
double y2 = subpath->getY(1);
if(y1 > y2) swap(y1, y2);
GfxRGB stroke_color;
state->getStrokeRGB(&stroke_color);
double lw = state->getLineWidth();
css_draw_rectangle(x-lw/2, y1, lw, y2-y1, state->getCTM(),
nullptr, 0,
nullptr, &stroke_color);
}
else if(is_rectangle(subpath))
2012-10-01 17:59:04 +00:00
{
double x1 = subpath->getX(0);
double x2 = subpath->getX(2);
double y1 = subpath->getY(0);
double y2 = subpath->getY(2);
2012-10-03 14:46:27 +00:00
2012-10-01 17:59:04 +00:00
if(x1 > x2) swap(x1, x2);
if(y1 > y2) swap(y1, y2);
2012-10-01 20:06:38 +00:00
double x,y,w,h,lw[2];
2012-10-01 20:31:55 +00:00
css_fix_rectangle_border_width(x1, y1, x2, y2, (fill ? 0.0 : state->getLineWidth()),
2012-10-01 20:06:38 +00:00
x,y,w,h,lw[0],lw[1]);
GfxRGB stroke_color;
2012-10-01 20:31:55 +00:00
if(!fill) state->getStrokeRGB(&stroke_color);
2012-10-01 20:06:38 +00:00
GfxRGB fill_color;
if(fill) state->getFillRGB(&fill_color);
int lw_count = 2;
2012-10-01 20:31:55 +00:00
GfxRGB * ps = fill ? nullptr : (&stroke_color);
2012-10-01 20:06:38 +00:00
GfxRGB * pf = fill ? (&fill_color) : nullptr;
2012-11-29 10:16:05 +00:00
if(equal(h, 0) || equal(w, 0))
2012-10-01 20:06:38 +00:00
{
// orthogonal line
// TODO: check length
2012-10-01 20:31:55 +00:00
pf = ps;
2012-10-01 20:06:38 +00:00
ps = nullptr;
h += lw[0];
w += lw[1];
}
2012-10-02 18:19:40 +00:00
css_draw_rectangle(x, y, w, h, state->getCTM(),
2012-10-01 20:06:38 +00:00
lw, lw_count,
2012-10-02 18:19:40 +00:00
ps, pf);
2012-10-01 17:59:04 +00:00
}
2012-10-03 14:46:27 +00:00
else
{
assert(false);
}
2012-10-01 17:59:04 +00:00
}
2012-10-03 14:46:27 +00:00
return true;
2012-10-01 17:59:04 +00:00
}
2012-10-02 18:19:40 +00:00
void HTMLRenderer::css_draw_rectangle(double x, double y, double w, double h, const double * tm,
2012-10-01 20:06:38 +00:00
double * line_width_array, int line_width_count,
2012-10-02 18:19:40 +00:00
const GfxRGB * line_color, const GfxRGB * fill_color,
void (*style_function)(void *, ostream &), void * style_function_data)
2012-10-01 20:06:38 +00:00
{
close_text_line();
2012-10-02 18:19:40 +00:00
double new_tm[6];
memcpy(new_tm, tm, sizeof(new_tm));
2012-10-02 08:06:08 +00:00
2012-11-29 10:16:05 +00:00
tm_transform(new_tm, x, y);
2012-10-02 08:06:08 +00:00
double scale = 1.0;
{
2012-10-02 18:19:40 +00:00
static const double sqrt2 = sqrt(2.0);
double i1 = (new_tm[0] + new_tm[2]) / sqrt2;
double i2 = (new_tm[1] + new_tm[3]) / sqrt2;
2012-11-29 10:16:05 +00:00
scale = hypot(i1, i2);
if(is_positive(scale))
2012-10-02 08:06:08 +00:00
{
for(int i = 0; i < 4; ++i)
2012-10-02 18:19:40 +00:00
new_tm[i] /= scale;
2012-10-02 08:06:08 +00:00
}
else
{
scale = 1.0;
}
}
2012-10-02 18:19:40 +00:00
html_fout << "<div class=\"Cd t" << install_transform_matrix(new_tm) << "\" style=\"";
2012-10-01 20:06:38 +00:00
if(line_color)
{
html_fout << "border-color:" << *line_color << ";";
html_fout << "border-width:";
for(int i = 0; i < line_width_count; ++i)
{
if(i > 0) html_fout << ' ';
2012-10-02 08:06:08 +00:00
double lw = line_width_array[i] * scale;
2012-11-29 10:16:05 +00:00
html_fout << round(lw);
if(is_positive(lw)) html_fout << "px";
2012-10-01 20:06:38 +00:00
}
html_fout << ";";
}
else
{
html_fout << "border:none;";
}
if(fill_color)
{
html_fout << "background-color:" << (*fill_color) << ";";
}
else
{
html_fout << "background-color:transparent;";
}
2012-10-02 18:19:40 +00:00
if(style_function)
{
style_function(style_function_data, html_fout);
}
2012-11-29 10:16:05 +00:00
html_fout << "bottom:" << round(y) << "px;"
<< "left:" << round(x) << "px;"
<< "width:" << round(w * scale) << "px;"
<< "height:" << round(h * scale) << "px;";
2012-10-01 20:26:13 +00:00
html_fout << "\"></div>";
2012-10-01 20:06:38 +00:00
}
2012-10-01 17:59:04 +00:00
} // namespace pdf2htmlEX