filters

FilterDevice.cpp
1/*
2 * Copyright (c) 2002 Nicolas HADACEK (hadacek@kde.org)
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "FilterDevice.h"
21
22#include <math.h>
23#include <KoFilterChain.h>
24#include <kdebug.h>
25
26#include "GfxState.h"
27#include "Link.h"
28
29#include "FilterPage.h"
30#include "data.h"
31#include "dialog.h"
32
33
34namespace PDFImport
35{
36
37Device::Device(Data &data)
38 : _data(data), _fillColor(TQt::white), _strokeColor(TQt::black)
39{
40 _pages.setAutoDelete(true);
41}
42
43Device::~Device()
44{
45 clear();
46}
47
48void Device::clear()
49{
50 _images.clear();
51}
52
53void Device::init()
54{
55 // get some global infos on frames size
56 const double maxH = _data.pageRect().bottom();
57 const double maxR = _data.pageRect().right();
58
59 double maxHeaderBottom = 0;
60 double minBodyTop = maxH;
61 double minHeaderBodyDelta = maxH;
62 double maxBodyBottom = 0;
63 double minFooterTop = maxH;
64 double minBodyFooterDelta = maxH;
65 double minLeft = maxR;
66 double maxRight = 0;
67
68 for (Page *page = _pages.first(); page; page = _pages.next()) {
69 const DRect &hr = page->rects()[Header];
70 const DRect &br = page->rects()[Body];
71 const DRect &fr = page->rects()[Footer];
72 if ( hr.isValid() ) {
73 maxHeaderBottom = kMax(maxHeaderBottom, hr.bottom());
74 if ( br.isValid() )
75 minHeaderBodyDelta =
76 kMin(minHeaderBodyDelta, br.top() - hr.bottom());
77 minLeft = kMin(minLeft, hr.left());
78 maxRight = kMax(maxRight, hr.right());
79 }
80 if ( fr.isValid() ) {
81 minFooterTop = kMin(minFooterTop, fr.top());
82 if ( br.isValid() )
83 minBodyFooterDelta =
84 kMin(minBodyFooterDelta, fr.top() - br.bottom());
85 minLeft = kMin(minLeft, fr.left());
86 maxRight = kMax(maxRight, fr.right());
87 }
88 if ( br.isValid() ) {
89 minBodyTop = kMin(minBodyTop, br.top());
90 maxBodyBottom = kMax(maxBodyBottom, br.bottom());
91 minLeft = kMin(minLeft, br.left());
92 maxRight = kMax(maxRight, br.right());
93 }
94 }
95
96 // set minimal top and maximal bottom to body frame
97 double minTop = kMax(maxHeaderBottom+minHeaderBodyDelta, minBodyTop);
98 double maxBottom = kMin(minFooterTop-minBodyFooterDelta, maxBodyBottom);
99 for (Page *page = _pages.first(); page; page = _pages.next()) {
100 DRect &r = page->rects()[Body];
101 if ( r.top()>minTop ) r.setTop(minTop);
102 if ( r.bottom()<maxBottom ) r.setBottom(maxBottom);
103 }
104
105 // set minimal left and maximal right for header and for footer
106 for (Page *page = _pages.first(); page; page = _pages.next()) {
107 DRect &hr = page->rects()[Header];
108 if ( hr.isValid() ) {
109 if ( hr.left()>minLeft ) hr.setLeft(minLeft);
110 if ( hr.right()<maxRight ) hr.setRight(maxRight);
111 }
112 DRect &fr = page->rects()[Footer];
113 if ( fr.isValid() ) {
114 if ( fr.left()>minLeft ) fr.setLeft(minLeft);
115 if ( fr.right()<maxRight ) fr.setRight(maxRight);
116 }
117 }
118}
119
120
121void Device::dumpPage(uint i)
122{
123 Page *page = _pages.at(i);
124 _data.initPage(page->rects(), page->pictures);
125 page->dump();
126}
127
128void Device::startPage(int, GfxState *)
129{
130 _pages.append( new Page(_data) );
131}
132
133void Device::endPage()
134{
135 if ( !_currentImage.image.isNull() ) addImage();
136 current()->endPage();
137 clear();
138 kdDebug(30516) << "-- end page --------------------------" << endl;
139}
140
141void Device::updateFont(GfxState *state)
142{
143 current()->updateFont(state);
144}
145
146void Device::beginString(GfxState *state, GString *)
147{
148 current()->beginString(state, state->getCurX(), state->getCurY());
149}
150
151void Device::endString(GfxState *)
152{
153 current()->endString();
154}
155
156void Device::drawChar(GfxState *state, double x, double y,
157 double dx, double dy, double, double,
158 CharCode, Unicode *u, int uLen)
159{
160 current()->addChar(state, x, y, dx, dy, u, uLen);
161}
162
163void Device::drawLink(::Link* link, Catalog *cat)
164{
165 double x1, y1, x2, y2, w;
166 link->getBorder(&x1, &y1, &x2, &y2, &w);
167
168 int ux1, uy1, ux2, uy2;
169 cvtUserToDev(x1, y1, &ux1, &uy1);
170 cvtUserToDev(x2, y2, &ux2, &uy2);
171
172 DRect r(kMin(ux1, ux2), kMax(ux1, ux2), kMin(uy1, uy2), kMax(uy1, uy2));
173 Link *l = new Link(r, *link->getAction(), *cat);
174 current()->addLink(l);
175}
176
177void Device::addImage()
178{
179// kdDebug(30516) << "-> add image" << endl;
180 if ( _currentImage.image.width()==0 || _currentImage.image.height()==0 ) {
181 kdDebug(30516) << "image has null width or height !" << endl;
182 _currentImage = Image();
183 return;
184 }
185
186 // check if same image already put at same place (don't know why it
187 // appends sometimes : related to KWord printing to pdf ?)
188 ImageList::iterator it;
189 for (it=_images.begin(); it!=_images.end(); ++it) {
190 if ( (*it).rect==_currentImage.rect
191 && (*it).image==_currentImage.image ) {
192 kdDebug(30516) << "image already there !\n";
193 _currentImage = Image();
194 return;
195 }
196 }
197
198 // add image
199 TQString name = TQString("pictures/picture%1.png").arg(_data.imageIndex());
200 TQDomElement frameset = _data.pictureFrameset(_currentImage.rect);
201 current()->pictures.append(frameset);
202 TQDomElement picture = _data.createElement("PICTURE");
203 picture.setAttribute("keepAspectRatio", "false");
204 frameset.appendChild(picture);
205
206 TQDomElement key = _data.createElement("KEY");
207 key.setAttribute("msec", 0);
208 key.setAttribute("second", 0);
209 key.setAttribute("minute", 0);
210 key.setAttribute("hour", 0);
211 key.setAttribute("day", 1);
212 key.setAttribute("month", 1);
213 key.setAttribute("year", 1970);
214 key.setAttribute("filename", name);
215 picture.appendChild(key);
216
217 key = _data.createElement("KEY");
218 key.setAttribute("msec", 0);
219 key.setAttribute("second", 0);
220 key.setAttribute("minute", 0);
221 key.setAttribute("hour", 0);
222 key.setAttribute("day", 1);
223 key.setAttribute("month", 1);
224 key.setAttribute("year", 1970);
225 key.setAttribute("filename", name);
226 key.setAttribute("name", name);
227 _data.pictures().appendChild(key);
228
229 KoStoreDevice *sd = _data.chain()->storageFile(name, KoStore::Write);
230 TQImageIO io(sd, "PNG");
231 io.setImage(_currentImage.image);
232 bool ok = io.write();
233 Q_ASSERT(ok);
234 sd->close();
235
236 _images.append(_currentImage);
237 _currentImage = Image();
238}
239
240void Device::computeGeometry(GfxState *state, Image &image)
241{
242 double xt, yt, wt, ht;
243 state->transform(0, 0, &xt, &yt);
244 state->transformDelta(1, 1, &wt, &ht);
245 image.rect.setLeft(xt + (wt>0 ? 0 : wt));
246 image.rect.setRight(image.rect.left() + fabs(wt));
247 image.rect.setTop(yt + (ht>0 ? 0 : ht));
248 image.rect.setBottom(image.rect.top() + fabs(ht));
249
250 // #### TODO : take care of image transform (rotation,...)
251}
252
253uint Device::initImage(GfxState *state, int width, int height,
254 bool withMask)
255{
256 // get image geometry
257 Image image;
258 image.mask = withMask;
259 computeGeometry(state, image);
260
261 // check if new image
262// kdDebug(30516) << "current image " << _currentImage.image.width()
263// << " " << _currentImage.rect.left()
264// << " " << _currentImage.rect.right()
265// << " " << _currentImage.rect.bottom()
266// << " " << _currentImage.mask << endl;
267// kdDebug(30516) << "new image " << width
268// << " " << image.rect.left() << " " << image.rect.right()
269// << " " << image.rect.top()
270// << " " << image.mask << endl;
271 if ( !_currentImage.image.isNull() &&
272 (_currentImage.image.width()!=width
273 || !equal(image.rect.left(), _currentImage.rect.left())
274 || !equal(image.rect.right(), _currentImage.rect.right())
275 || !equal(image.rect.top(), _currentImage.rect.bottom())
276 || image.mask!=_currentImage.mask) )
277 addImage();
278
279 uint offset =
280 (_currentImage.image.isNull() ? 0 : _currentImage.image.height());
281 image.image = TQImage(width, offset + height, 32);
282 image.image.setAlphaBuffer(withMask);
283 if ( !_currentImage.image.isNull() ) { // copy previous
284// kdDebug(30516) << "image continued..." << endl;
285 for (int j=0; j<_currentImage.image.height(); j++) {
286 TQRgb *pix = (TQRgb *)_currentImage.image.scanLine(j);
287 TQRgb *newPix = (TQRgb *)image.image.scanLine(j);
288 for (int i=0; i<width; i++) newPix[i] = pix[i];
289 }
290 _currentImage.image = image.image;
291 _currentImage.rect.setBottom( image.rect.bottom() );
292 } else _currentImage = image;
293 return offset;
294}
295
296void Device::drawImage(GfxState *state, Object *, Stream *str,
297 int width, int height, GfxImageColorMap *colorMap,
298 int *maskColors, GBool inlineImg)
299{
300 kdDebug(30516) << "image kind=" << str->getKind()
301 << " inline=" << inlineImg
302 << " maskColors=" << (maskColors!=0) << endl;
303 if ( !_data.options().importImages ) return;
304
305 uint offset = initImage(state, width, height, maskColors!=0);
306
307 // read pixels
308 int nbComps = colorMap->getNumPixelComps();
309 int nbBits = colorMap->getBits();
310 ImageStream *istr = new ImageStream(str, width, nbComps, nbBits);
311 istr->reset();
312 for (int j=0; j<height; j++) {
313 Guchar *p = istr->getLine();
314 TQRgb *pix = (TQRgb *)_currentImage.image.scanLine(offset + j);
315 for (int i=0; i<width; i++) {
316 GfxRGB rgb;
317 colorMap->getRGB(p, &rgb);
318 int alpha = 255;
319 if (maskColors) {
320 for (int k=0; k<nbComps; k++)
321 if ( p[k]<maskColors[2*k] || p[k]>maskColors[2*k+1] ) {
322 alpha = 0;
323 break;
324 }
325 }
326 pix[i] = tqRgba(tqRound(rgb.r*255), tqRound(rgb.g*255),
327 tqRound(rgb.b*255), alpha);
328 p += nbComps;
329 }
330 }
331 delete istr;
332}
333
334void Device::drawImageMask(GfxState *state, Object *, Stream *str,
335 int width, int height, GBool invert,
336 GBool inlineImg)
337{
338 kdDebug(30516) << "image mask ! kind=" << str->getKind()
339 << "inline=" << inlineImg << endl;
340 if ( !_data.options().importImages ) return;
341
342 uint offset = initImage(state, width, height, true);
343
344 // read pixels
345 GfxRGB rgb;
346 state->getFillRGB(&rgb);
347 int red = tqRound(rgb.r * 255);
348 int green = tqRound(rgb.g * 255);
349 int blue = tqRound(rgb.b * 255);
350
351 ImageStream *istr = new ImageStream(str, width, 1, 1);
352 str->reset();
353 for (int j=0; j<height; j++) {
354 Guchar *p = istr->getLine();
355 TQRgb *pix = (TQRgb *)_currentImage.image.scanLine(offset + j);
356 for (int i=0; i<width; i++)
357 pix[i] = tqRgba(red, green, blue, 255 * p[i]);
358 }
359 delete istr;
360
361 if (invert) _currentImage.image.invertPixels();
362}
363
364void Device::updateAll(GfxState *state)
365{
366 updateFillColor(state);
367 updateStrokeColor(state);
368 updateFont(state);
369}
370
371void Device::updateFillColor(GfxState *state)
372{
373 GfxRGB rgb;
374 state->getFillRGB(&rgb);
375 _fillColor = toColor(rgb);
376}
377
378void Device::updateStrokeColor(GfxState *state)
379{
380 GfxRGB rgb;
381 state->getStrokeRGB(&rgb);
382 _strokeColor = toColor(rgb);
383}
384
385void Device::stroke(GfxState */*state*/)
386{
387// kdDebug(30516) << "stroke" << endl;
388// DPathVector path = convertPath(state);
389// for (uint i=0; i<path.size(); i++) {
390// if ( path[i].isHorizontalSegment() ) {
391// kdDebug(30516) << " horizontal segment" << endl;
392 // #### FIXME correctly draw the line
393// if ( !_currentImage.image.isNull() ) addImage();
394// _currentImage.rect = path[i].boundingRect();
395// _currentImage.rect.bottom+=1;
396// _currentImage.image =
397// TQImage(tqRound(_currentImage.rect.width()),
398// tqRound(_currentImage.rect.height()), 32);
399// _currentImage.image.fill(_fillColor.pixel());
400// addImage();
401// } else if ( path[i].isVerticalSegment() ) {
402// kdDebug(30516) << " vertical segment" << endl;
403// }
404// }
405}
406
407void Device::fill(GfxState */*state*/)
408{
409// kdDebug(30516) << "fill" << endl;
410// doFill(state);
411}
412
413void Device::eoFill(GfxState */*state*/)
414{
415// kdDebug(30516) << "eoFill" << endl;
416// convertPath(state);
417// doFill(state);
418}
419
420void Device::doFill(const DPathVector &path)
421{
422 for (uint i=0; i<path.size(); i++) {
423 if ( path[i].isSegment() ) continue;
424 if ( path[i].isRectangle() ) {
425 kdDebug(30516) << "fill rectangle" << endl;
426 if ( !_currentImage.image.isNull() ) addImage();
427 _currentImage.rect = path[i].boundingRect();
428 _currentImage.image =
429 TQImage(tqRound(_currentImage.rect.width()),
430 tqRound(_currentImage.rect.height()), 32);
431 _currentImage.image.fill(_fillColor.pixel());
432 addImage();
433 }
434 }
435}
436
437DPathVector Device::convertPath(GfxState *state)
438{
439 GfxPath *path = state->getPath();
440 uint nbPaths = path->getNumSubpaths();
441 DPathVector vector;
442 for (uint i=0; i<nbPaths; i++) {
443 GfxSubpath *spath = path->getSubpath(i);
444 uint nbPoints = spath->getNumPoints();
445 DPath dpath;
446 for (uint k=0; k<nbPoints; k++) {
447 if ( k>=1 && spath->getCurve(k) ) {
448 kdDebug(30516) << " bezier curve : ignore !" << endl;
449 dpath = DPath();
450 break;
451 } else {
452 DPoint dpoint;
453 state->transform(spath->getX(k), spath->getY(k),
454 &dpoint.x, &dpoint.y);
455 dpath.push_back(dpoint);
456 }
457 }
458 if ( dpath.size()!=0 ) vector.push_back(dpath);
459 }
460 return vector;
461}
462
463} // namespace
The body is a frameset.
Definition body.h:38