1 | //======================================================================== |
2 | // |
3 | // BBoxOutputDev.cc |
4 | // |
5 | // This file is licensed under the GPLv2 or later |
6 | // |
7 | // Copyright 2020 sgerwk <sgerwk@aol.com> |
8 | // Copyright 2022 Oliver Sander <oliver.sander@tu-dresden.de> |
9 | // |
10 | //======================================================================== |
11 | |
12 | #include <cmath> |
13 | #include <BBoxOutputDev.h> |
14 | #include <GfxFont.h> |
15 | |
16 | #define writingModeHorizontal 0 |
17 | #define writingModeVertical 1 |
18 | |
19 | BBoxOutputDev::BBoxOutputDev() : BBoxOutputDev(true, true, true) { } |
20 | |
21 | BBoxOutputDev::BBoxOutputDev(bool textA, bool vectorA, bool rasterA) : BBoxOutputDev(textA, vectorA, rasterA, true) { } |
22 | |
23 | BBoxOutputDev::BBoxOutputDev(bool textA, bool vectorA, bool rasterA, bool lwidthA) |
24 | { |
25 | hasGraphics = false; |
26 | text = textA; |
27 | vector = vectorA; |
28 | raster = rasterA; |
29 | lwidth = lwidthA; |
30 | } |
31 | |
32 | double BBoxOutputDev::getX1() const |
33 | { |
34 | return bb.x1; |
35 | } |
36 | |
37 | double BBoxOutputDev::getY1() const |
38 | { |
39 | return bb.y1; |
40 | } |
41 | |
42 | double BBoxOutputDev::getX2() const |
43 | { |
44 | return bb.x2; |
45 | } |
46 | |
47 | double BBoxOutputDev::getY2() const |
48 | { |
49 | return bb.y2; |
50 | } |
51 | |
52 | double BBoxOutputDev::getHasGraphics() const |
53 | { |
54 | return hasGraphics; |
55 | } |
56 | |
57 | void BBoxOutputDev::endPage() { } |
58 | |
59 | void BBoxOutputDev::stroke(GfxState *state) |
60 | { |
61 | updatePath(bbA: &bb, path: state->getPath(), state); |
62 | } |
63 | |
64 | void BBoxOutputDev::fill(GfxState *state) |
65 | { |
66 | updatePath(bbA: &bb, path: state->getPath(), state); |
67 | } |
68 | |
69 | void BBoxOutputDev::eoFill(GfxState *state) |
70 | { |
71 | updatePath(bbA: &bb, path: state->getPath(), state); |
72 | } |
73 | |
74 | void BBoxOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) |
75 | { |
76 | updateImage(bbA: &bb, state); |
77 | } |
78 | |
79 | void BBoxOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) |
80 | { |
81 | updateImage(bbA: &bb, state); |
82 | } |
83 | |
84 | 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) |
85 | { |
86 | updateImage(bbA: &bb, state); |
87 | } |
88 | |
89 | 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, |
90 | bool maskInterpolate) |
91 | { |
92 | updateImage(bbA: &bb, state); |
93 | } |
94 | |
95 | 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) |
96 | { |
97 | double leftent, rightent, ascent, descent; |
98 | const double *fm, *fb; |
99 | double fontSize, w, adjust; |
100 | double fx, fy; |
101 | |
102 | if (!text) { |
103 | return; |
104 | } |
105 | |
106 | const GfxFont *const font = state->getFont().get(); |
107 | if (!font) { |
108 | return; |
109 | } |
110 | |
111 | if (code == (CharCode)0x20) { |
112 | return; |
113 | } |
114 | |
115 | fontSize = state->getFontSize(); |
116 | |
117 | fb = font->getFontBBox(); |
118 | if (font->getWMode() == writingModeHorizontal) { |
119 | leftent = 0; |
120 | rightent = 0; |
121 | ascent = font->getAscent(); |
122 | descent = font->getDescent(); |
123 | } else { |
124 | if (fb[0] == 0 && fb[1] == 0 && fb[2] == 0 && fb[3] == 0) { |
125 | leftent = -0.5; |
126 | rightent = 0.5; |
127 | } else { |
128 | leftent = fb[1]; |
129 | rightent = fb[3]; |
130 | } |
131 | ascent = 0; |
132 | descent = 0; |
133 | } |
134 | |
135 | if (font->getType() != fontType3) { |
136 | adjust = 1; |
137 | } else { |
138 | // adjust font size for type3 fonts, |
139 | // similar to TextPage::updateFont() |
140 | w = ((Gfx8BitFont *)font)->getWidth(c: code); |
141 | adjust = w / 0.5; |
142 | fm = font->getFontMatrix(); |
143 | if (fm[0] != 0) { |
144 | adjust *= fabs(x: fm[3] / fm[0]); |
145 | } |
146 | } |
147 | |
148 | ascent *= adjust * fontSize; |
149 | descent *= adjust * fontSize; |
150 | leftent *= adjust * fontSize; |
151 | rightent *= adjust * fontSize; |
152 | |
153 | state->textTransformDelta(x1: leftent, y1: descent, x2: &fx, y2: &fy); |
154 | updatePoint(bbA: &bb, x: fx + x, y: fy + y, state); |
155 | |
156 | state->textTransformDelta(x1: rightent, y1: ascent, x2: &fx, y2: &fy); |
157 | updatePoint(bbA: &bb, x: fx + x, y: fy + y, state); |
158 | |
159 | state->textTransformDelta(x1: leftent, y1: descent, x2: &fx, y2: &fy); |
160 | updatePoint(bbA: &bb, x: fx + x + dx, y: fy + y + dy, state); |
161 | |
162 | state->textTransformDelta(x1: rightent, y1: ascent, x2: &fx, y2: &fy); |
163 | updatePoint(bbA: &bb, x: fx + x + dx, y: fy + y + dy, state); |
164 | } |
165 | |
166 | /* update the bounding box with a new point */ |
167 | void BBoxOutputDev::updatePoint(PDFRectangle *bbA, double x, double y, const GfxState *state) |
168 | { |
169 | Matrix o = { 1, 0, 0, 1, 0, 0 }; |
170 | double tx, ty; |
171 | double xMin, yMin, xMax, yMax; |
172 | |
173 | state->getClipBBox(xMin: &xMin, yMin: &yMin, xMax: &xMax, yMax: &yMax); |
174 | |
175 | o.scale(sx: 1, sy: -1); |
176 | o.translate(tx: 0, ty: -state->getPageHeight()); |
177 | |
178 | state->transform(x1: x, y1: y, x2: &tx, y2: &ty); |
179 | tx = tx < xMin ? xMin : tx > xMax ? xMax : tx; |
180 | ty = ty < yMin ? yMin : ty > yMax ? yMax : ty; |
181 | o.transform(x: tx, y: ty, tx: &x, ty: &y); |
182 | |
183 | if (!hasGraphics || bbA->x1 > x) { |
184 | bbA->x1 = x; |
185 | } |
186 | if (!hasGraphics || bbA->y1 > y) { |
187 | bbA->y1 = y; |
188 | } |
189 | if (!hasGraphics || bbA->x2 < x) { |
190 | bbA->x2 = x; |
191 | } |
192 | if (!hasGraphics || bbA->y2 < y) { |
193 | bbA->y2 = y; |
194 | } |
195 | hasGraphics = true; |
196 | } |
197 | |
198 | /* update the bounding box with a new path */ |
199 | void BBoxOutputDev::updatePath(PDFRectangle *bbA, const GfxPath *path, const GfxState *state) |
200 | { |
201 | int i, j; |
202 | const GfxSubpath *subpath; |
203 | double x, y; |
204 | double w; |
205 | if (!vector) { |
206 | return; |
207 | } |
208 | w = lwidth ? state->getLineWidth() : 0; |
209 | for (i = 0; i < path->getNumSubpaths(); i++) { |
210 | subpath = path->getSubpath(i); |
211 | for (j = 0; j < subpath->getNumPoints(); j++) { |
212 | x = subpath->getX(i: j); |
213 | y = subpath->getY(i: j); |
214 | updatePoint(bbA, x: x - w / 2, y: y - w / 2, state); |
215 | updatePoint(bbA, x: x + w / 2, y: y + w / 2, state); |
216 | } |
217 | } |
218 | } |
219 | |
220 | /* update the bounding box with a new image */ |
221 | void BBoxOutputDev::updateImage(PDFRectangle *bbA, const GfxState *state) |
222 | { |
223 | if (!raster) { |
224 | return; |
225 | } |
226 | updatePoint(bbA, x: 0, y: 1, state); |
227 | updatePoint(bbA, x: 1, y: 0, state); |
228 | } |
229 | |