blob: 88b044e26b2fd30ada57fee841f1c962bf286e97 [file] [log] [blame]
//========================================================================
//
// BBoxOutputDev.cc
//
// This file is licensed under the GPLv2 or later
//
// Copyright 2020 sgerwk <sgerwk@aol.com>
// Copyright 2022 Oliver Sander <oliver.sander@tu-dresden.de>
//
//========================================================================
#include <cmath>
#include <BBoxOutputDev.h>
#include <GfxFont.h>
#define writingModeHorizontal 0
#define writingModeVertical 1
BBoxOutputDev::BBoxOutputDev() : BBoxOutputDev(true, true, true) { }
BBoxOutputDev::BBoxOutputDev(bool textA, bool vectorA, bool rasterA) : BBoxOutputDev(textA, vectorA, rasterA, true) { }
BBoxOutputDev::BBoxOutputDev(bool textA, bool vectorA, bool rasterA, bool lwidthA)
{
hasGraphics = false;
text = textA;
vector = vectorA;
raster = rasterA;
lwidth = lwidthA;
}
double BBoxOutputDev::getX1() const
{
return bb.x1;
}
double BBoxOutputDev::getY1() const
{
return bb.y1;
}
double BBoxOutputDev::getX2() const
{
return bb.x2;
}
double BBoxOutputDev::getY2() const
{
return bb.y2;
}
double BBoxOutputDev::getHasGraphics() const
{
return hasGraphics;
}
void BBoxOutputDev::endPage() { }
void BBoxOutputDev::stroke(GfxState *state)
{
updatePath(&bb, state->getPath(), state);
}
void BBoxOutputDev::fill(GfxState *state)
{
updatePath(&bb, state->getPath(), state);
}
void BBoxOutputDev::eoFill(GfxState *state)
{
updatePath(&bb, state->getPath(), state);
}
void BBoxOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg)
{
updateImage(&bb, state);
}
void BBoxOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg)
{
updateImage(&bb, state);
}
void BBoxOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate)
{
updateImage(&bb, state);
}
void BBoxOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap,
bool maskInterpolate)
{
updateImage(&bb, state);
}
void BBoxOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen)
{
double leftent, rightent, ascent, descent;
const double *fm, *fb;
double fontSize, w, adjust;
double fx, fy;
if (!text) {
return;
}
const GfxFont *const font = state->getFont().get();
if (!font) {
return;
}
if (code == (CharCode)0x20) {
return;
}
fontSize = state->getFontSize();
fb = font->getFontBBox();
if (font->getWMode() == writingModeHorizontal) {
leftent = 0;
rightent = 0;
ascent = font->getAscent();
descent = font->getDescent();
} else {
if (fb[0] == 0 && fb[1] == 0 && fb[2] == 0 && fb[3] == 0) {
leftent = -0.5;
rightent = 0.5;
} else {
leftent = fb[1];
rightent = fb[3];
}
ascent = 0;
descent = 0;
}
if (font->getType() != fontType3) {
adjust = 1;
} else {
// adjust font size for type3 fonts,
// similar to TextPage::updateFont()
w = ((Gfx8BitFont *)font)->getWidth(code);
adjust = w / 0.5;
fm = font->getFontMatrix();
if (fm[0] != 0) {
adjust *= fabs(fm[3] / fm[0]);
}
}
ascent *= adjust * fontSize;
descent *= adjust * fontSize;
leftent *= adjust * fontSize;
rightent *= adjust * fontSize;
state->textTransformDelta(leftent, descent, &fx, &fy);
updatePoint(&bb, fx + x, fy + y, state);
state->textTransformDelta(rightent, ascent, &fx, &fy);
updatePoint(&bb, fx + x, fy + y, state);
state->textTransformDelta(leftent, descent, &fx, &fy);
updatePoint(&bb, fx + x + dx, fy + y + dy, state);
state->textTransformDelta(rightent, ascent, &fx, &fy);
updatePoint(&bb, fx + x + dx, fy + y + dy, state);
}
/* update the bounding box with a new point */
void BBoxOutputDev::updatePoint(PDFRectangle *bbA, double x, double y, const GfxState *state)
{
Matrix o = { 1, 0, 0, 1, 0, 0 };
double tx, ty;
double xMin, yMin, xMax, yMax;
state->getClipBBox(&xMin, &yMin, &xMax, &yMax);
o.scale(1, -1);
o.translate(0, -state->getPageHeight());
state->transform(x, y, &tx, &ty);
tx = tx < xMin ? xMin : tx > xMax ? xMax : tx;
ty = ty < yMin ? yMin : ty > yMax ? yMax : ty;
o.transform(tx, ty, &x, &y);
if (!hasGraphics || bbA->x1 > x) {
bbA->x1 = x;
}
if (!hasGraphics || bbA->y1 > y) {
bbA->y1 = y;
}
if (!hasGraphics || bbA->x2 < x) {
bbA->x2 = x;
}
if (!hasGraphics || bbA->y2 < y) {
bbA->y2 = y;
}
hasGraphics = true;
}
/* update the bounding box with a new path */
void BBoxOutputDev::updatePath(PDFRectangle *bbA, const GfxPath *path, const GfxState *state)
{
int i, j;
const GfxSubpath *subpath;
double x, y;
double w;
if (!vector) {
return;
}
w = lwidth ? state->getLineWidth() : 0;
for (i = 0; i < path->getNumSubpaths(); i++) {
subpath = path->getSubpath(i);
for (j = 0; j < subpath->getNumPoints(); j++) {
x = subpath->getX(j);
y = subpath->getY(j);
updatePoint(bbA, x - w / 2, y - w / 2, state);
updatePoint(bbA, x + w / 2, y + w / 2, state);
}
}
}
/* update the bounding box with a new image */
void BBoxOutputDev::updateImage(PDFRectangle *bbA, const GfxState *state)
{
if (!raster) {
return;
}
updatePoint(bbA, 0, 1, state);
updatePoint(bbA, 1, 0, state);
}