filters

FilterDevice.cpp

00001 /*
00002  * Copyright (c) 2002 Nicolas HADACEK (hadacek@kde.org)
00003  *
00004  * This program is free software; you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License as published by
00006  * the Free Software Foundation; either version 2 of the License, or
00007  * (at your option) any later version.
00008 
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013 
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018  */
00019 
00020 #include "FilterDevice.h"
00021 
00022 #include <math.h>
00023 #include <KoFilterChain.h>
00024 #include <kdebug.h>
00025 
00026 #include "GfxState.h"
00027 #include "Link.h"
00028 
00029 #include "FilterPage.h"
00030 #include "data.h"
00031 #include "dialog.h"
00032 
00033 
00034 namespace PDFImport
00035 {
00036 
00037 Device::Device(Data &data)
00038     : _data(data), _fillColor(Qt::white), _strokeColor(Qt::black)
00039 {
00040     _pages.setAutoDelete(true);
00041 }
00042 
00043 Device::~Device()
00044 {
00045     clear();
00046 }
00047 
00048 void Device::clear()
00049 {
00050     _images.clear();
00051 }
00052 
00053 void Device::init()
00054 {
00055     // get some global infos on frames size
00056     const double maxH = _data.pageRect().bottom();
00057     const double maxR = _data.pageRect().right();
00058 
00059     double maxHeaderBottom = 0;
00060     double minBodyTop = maxH;
00061     double minHeaderBodyDelta = maxH;
00062     double maxBodyBottom = 0;
00063     double minFooterTop = maxH;
00064     double minBodyFooterDelta = maxH;
00065     double minLeft = maxR;
00066     double maxRight = 0;
00067 
00068     for (Page *page = _pages.first(); page; page = _pages.next()) {
00069         const DRect &hr = page->rects()[Header];
00070         const DRect &br = page->rects()[Body];
00071         const DRect &fr = page->rects()[Footer];
00072         if ( hr.isValid() ) {
00073             maxHeaderBottom = kMax(maxHeaderBottom, hr.bottom());
00074             if ( br.isValid() )
00075                 minHeaderBodyDelta =
00076                     kMin(minHeaderBodyDelta, br.top() - hr.bottom());
00077             minLeft = kMin(minLeft, hr.left());
00078             maxRight = kMax(maxRight, hr.right());
00079         }
00080         if ( fr.isValid() ) {
00081             minFooterTop = kMin(minFooterTop, fr.top());
00082             if ( br.isValid() )
00083                 minBodyFooterDelta =
00084                     kMin(minBodyFooterDelta, fr.top() - br.bottom());
00085             minLeft = kMin(minLeft, fr.left());
00086             maxRight = kMax(maxRight, fr.right());
00087         }
00088         if ( br.isValid() ) {
00089             minBodyTop = kMin(minBodyTop, br.top());
00090             maxBodyBottom = kMax(maxBodyBottom, br.bottom());
00091             minLeft = kMin(minLeft, br.left());
00092             maxRight = kMax(maxRight, br.right());
00093         }
00094     }
00095 
00096     // set minimal top and maximal bottom to body frame
00097     double minTop = kMax(maxHeaderBottom+minHeaderBodyDelta, minBodyTop);
00098     double maxBottom = kMin(minFooterTop-minBodyFooterDelta, maxBodyBottom);
00099     for (Page *page = _pages.first(); page; page = _pages.next()) {
00100         DRect &r = page->rects()[Body];
00101         if ( r.top()>minTop ) r.setTop(minTop);
00102         if ( r.bottom()<maxBottom ) r.setBottom(maxBottom);
00103     }
00104 
00105     // set minimal left and maximal right for header and for footer
00106     for (Page *page = _pages.first(); page; page = _pages.next()) {
00107         DRect &hr = page->rects()[Header];
00108         if ( hr.isValid() ) {
00109             if ( hr.left()>minLeft ) hr.setLeft(minLeft);
00110             if ( hr.right()<maxRight ) hr.setRight(maxRight);
00111         }
00112         DRect &fr = page->rects()[Footer];
00113         if ( fr.isValid() ) {
00114             if ( fr.left()>minLeft ) fr.setLeft(minLeft);
00115             if ( fr.right()<maxRight ) fr.setRight(maxRight);
00116         }
00117     }
00118 }
00119 
00120 
00121 void Device::dumpPage(uint i)
00122 {
00123     Page *page = _pages.at(i);
00124     _data.initPage(page->rects(), page->pictures);
00125     page->dump();
00126 }
00127 
00128 void Device::startPage(int, GfxState *)
00129 {
00130     _pages.append( new Page(_data) );
00131 }
00132 
00133 void Device::endPage()
00134 {
00135     if ( !_currentImage.image.isNull() ) addImage();
00136     current()->endPage();
00137     clear();
00138     kdDebug(30516) << "-- end page --------------------------" << endl;
00139 }
00140 
00141 void Device::updateFont(GfxState *state)
00142 {
00143     current()->updateFont(state);
00144 }
00145 
00146 void Device::beginString(GfxState *state, GString *)
00147 {
00148     current()->beginString(state, state->getCurX(), state->getCurY());
00149 }
00150 
00151 void Device::endString(GfxState *)
00152 {
00153     current()->endString();
00154 }
00155 
00156 void Device::drawChar(GfxState *state, double x, double y,
00157                             double dx, double dy, double, double,
00158                             CharCode, Unicode *u, int uLen)
00159 {
00160     current()->addChar(state, x, y, dx, dy, u, uLen);
00161 }
00162 
00163 void Device::drawLink(::Link* link, Catalog *cat)
00164 {
00165     double x1, y1, x2, y2, w;
00166     link->getBorder(&x1, &y1, &x2, &y2, &w);
00167 
00168     int ux1, uy1, ux2, uy2;
00169     cvtUserToDev(x1, y1, &ux1, &uy1);
00170     cvtUserToDev(x2, y2, &ux2, &uy2);
00171 
00172     DRect r(kMin(ux1, ux2), kMax(ux1, ux2), kMin(uy1, uy2), kMax(uy1, uy2));
00173     Link *l = new Link(r, *link->getAction(), *cat);
00174     current()->addLink(l);
00175 }
00176 
00177 void Device::addImage()
00178 {
00179 //    kdDebug(30516) << "-> add image" << endl;
00180     if ( _currentImage.image.width()==0 || _currentImage.image.height()==0 ) {
00181         kdDebug(30516) << "image has null width or height !" << endl;
00182         _currentImage = Image();
00183         return;
00184     }
00185 
00186     // check if same image already put at same place (don't know why it
00187     // appends sometimes : related to KWord printing to pdf ?)
00188     ImageList::iterator it;
00189     for (it=_images.begin(); it!=_images.end(); ++it) {
00190         if ( (*it).rect==_currentImage.rect
00191              && (*it).image==_currentImage.image ) {
00192             kdDebug(30516) << "image already there !\n";
00193             _currentImage = Image();
00194             return;
00195         }
00196     }
00197 
00198     // add image
00199     QString name = QString("pictures/picture%1.png").arg(_data.imageIndex());
00200     QDomElement frameset = _data.pictureFrameset(_currentImage.rect);
00201     current()->pictures.append(frameset);
00202     QDomElement picture = _data.createElement("PICTURE");
00203     picture.setAttribute("keepAspectRatio", "false");
00204     frameset.appendChild(picture);
00205 
00206     QDomElement key = _data.createElement("KEY");
00207     key.setAttribute("msec", 0);
00208     key.setAttribute("second", 0);
00209     key.setAttribute("minute", 0);
00210     key.setAttribute("hour", 0);
00211     key.setAttribute("day", 1);
00212     key.setAttribute("month", 1);
00213     key.setAttribute("year", 1970);
00214     key.setAttribute("filename", name);
00215     picture.appendChild(key);
00216 
00217     key = _data.createElement("KEY");
00218     key.setAttribute("msec", 0);
00219     key.setAttribute("second", 0);
00220     key.setAttribute("minute", 0);
00221     key.setAttribute("hour", 0);
00222     key.setAttribute("day", 1);
00223     key.setAttribute("month", 1);
00224     key.setAttribute("year", 1970);
00225     key.setAttribute("filename", name);
00226     key.setAttribute("name", name);
00227     _data.pictures().appendChild(key);
00228 
00229     KoStoreDevice *sd = _data.chain()->storageFile(name, KoStore::Write);
00230     QImageIO io(sd, "PNG");
00231     io.setImage(_currentImage.image);
00232     bool ok = io.write();
00233     Q_ASSERT(ok);
00234     sd->close();
00235 
00236     _images.append(_currentImage);
00237     _currentImage = Image();
00238 }
00239 
00240 void Device::computeGeometry(GfxState *state, Image &image)
00241 {
00242     double xt, yt, wt, ht;
00243     state->transform(0, 0, &xt, &yt);
00244     state->transformDelta(1, 1, &wt, &ht);
00245     image.rect.setLeft(xt + (wt>0 ? 0 : wt));
00246     image.rect.setRight(image.rect.left() + fabs(wt));
00247     image.rect.setTop(yt + (ht>0 ? 0 : ht));
00248     image.rect.setBottom(image.rect.top() + fabs(ht));
00249 
00250     // #### TODO : take care of image transform (rotation,...)
00251 }
00252 
00253 uint Device::initImage(GfxState *state, int width, int height,
00254                        bool withMask)
00255 {
00256     // get image geometry
00257     Image image;
00258     image.mask = withMask;
00259     computeGeometry(state, image);
00260 
00261     // check if new image
00262 //    kdDebug(30516) << "current image " << _currentImage.image.width()
00263 //                   << " " << _currentImage.rect.left()
00264 //                   << " " << _currentImage.rect.right()
00265 //                   << " " << _currentImage.rect.bottom()
00266 //                   << " " << _currentImage.mask << endl;
00267 //    kdDebug(30516) << "new image " << width
00268 //                   << " " << image.rect.left() << " " << image.rect.right()
00269 //                   << " " << image.rect.top()
00270 //                   << " " << image.mask << endl;
00271     if ( !_currentImage.image.isNull() &&
00272          (_currentImage.image.width()!=width
00273           || !equal(image.rect.left(), _currentImage.rect.left())
00274           || !equal(image.rect.right(), _currentImage.rect.right())
00275           || !equal(image.rect.top(), _currentImage.rect.bottom())
00276           || image.mask!=_currentImage.mask) )
00277         addImage();
00278 
00279     uint offset =
00280         (_currentImage.image.isNull() ? 0 : _currentImage.image.height());
00281     image.image = QImage(width, offset + height, 32);
00282     image.image.setAlphaBuffer(withMask);
00283     if ( !_currentImage.image.isNull() ) { // copy previous
00284 //        kdDebug(30516) << "image continued..." << endl;
00285         for (int j=0; j<_currentImage.image.height(); j++) {
00286             QRgb *pix = (QRgb *)_currentImage.image.scanLine(j);
00287             QRgb *newPix = (QRgb *)image.image.scanLine(j);
00288             for (int i=0; i<width; i++) newPix[i] = pix[i];
00289         }
00290         _currentImage.image = image.image;
00291         _currentImage.rect.setBottom( image.rect.bottom() );
00292     } else _currentImage = image;
00293     return offset;
00294 }
00295 
00296 void Device::drawImage(GfxState *state, Object *, Stream *str,
00297                              int width, int height, GfxImageColorMap *colorMap,
00298                              int *maskColors, GBool inlineImg)
00299 {
00300     kdDebug(30516) << "image kind=" << str->getKind()
00301                    << " inline=" << inlineImg
00302                    << " maskColors=" << (maskColors!=0) << endl;
00303     if ( !_data.options().importImages ) return;
00304 
00305     uint offset = initImage(state, width, height, maskColors!=0);
00306 
00307     // read pixels
00308     int nbComps = colorMap->getNumPixelComps();
00309     int nbBits = colorMap->getBits();
00310     ImageStream *istr = new ImageStream(str, width, nbComps, nbBits);
00311     istr->reset();
00312     for (int j=0; j<height; j++) {
00313         Guchar *p = istr->getLine();
00314         QRgb *pix = (QRgb *)_currentImage.image.scanLine(offset + j);
00315         for (int i=0; i<width; i++) {
00316             GfxRGB rgb;
00317             colorMap->getRGB(p, &rgb);
00318             int alpha = 255;
00319             if (maskColors) {
00320                 for (int k=0; k<nbComps; k++)
00321                     if ( p[k]<maskColors[2*k] || p[k]>maskColors[2*k+1] ) {
00322                         alpha = 0;
00323                         break;
00324                     }
00325             }
00326             pix[i] = qRgba(qRound(rgb.r*255), qRound(rgb.g*255),
00327                            qRound(rgb.b*255), alpha);
00328             p += nbComps;
00329         }
00330     }
00331     delete istr;
00332 }
00333 
00334 void Device::drawImageMask(GfxState *state, Object *, Stream *str,
00335                                  int width, int height, GBool invert,
00336                                  GBool inlineImg)
00337 {
00338     kdDebug(30516) << "image mask ! kind=" << str->getKind()
00339                    << "inline=" << inlineImg << endl;
00340     if ( !_data.options().importImages ) return;
00341 
00342     uint offset = initImage(state, width, height, true);
00343 
00344     // read pixels
00345     GfxRGB rgb;
00346     state->getFillRGB(&rgb);
00347     int red = qRound(rgb.r * 255);
00348     int green = qRound(rgb.g * 255);
00349     int blue = qRound(rgb.b * 255);
00350 
00351     ImageStream *istr = new ImageStream(str, width, 1, 1);
00352     str->reset();
00353     for (int j=0; j<height; j++) {
00354         Guchar *p = istr->getLine();
00355         QRgb *pix = (QRgb *)_currentImage.image.scanLine(offset + j);
00356         for (int i=0; i<width; i++)
00357             pix[i] = qRgba(red, green, blue, 255 * p[i]);
00358     }
00359     delete istr;
00360 
00361     if (invert) _currentImage.image.invertPixels();
00362 }
00363 
00364 void Device::updateAll(GfxState *state)
00365 {
00366     updateFillColor(state);
00367     updateStrokeColor(state);
00368     updateFont(state);
00369 }
00370 
00371 void Device::updateFillColor(GfxState *state)
00372 {
00373     GfxRGB rgb;
00374     state->getFillRGB(&rgb);
00375     _fillColor = toColor(rgb);
00376 }
00377 
00378 void Device::updateStrokeColor(GfxState *state)
00379 {
00380     GfxRGB rgb;
00381     state->getStrokeRGB(&rgb);
00382     _strokeColor = toColor(rgb);
00383 }
00384 
00385 void Device::stroke(GfxState */*state*/)
00386 {
00387 //    kdDebug(30516) << "stroke" << endl;
00388 //    DPathVector path = convertPath(state);
00389 //    for (uint i=0; i<path.size(); i++) {
00390 //        if ( path[i].isHorizontalSegment() ) {
00391 //            kdDebug(30516) << "  horizontal segment" << endl;
00392             // #### FIXME correctly draw the line
00393 //            if ( !_currentImage.image.isNull() ) addImage();
00394 //            _currentImage.rect = path[i].boundingRect();
00395 //            _currentImage.rect.bottom+=1;
00396 //            _currentImage.image =
00397 //                QImage(qRound(_currentImage.rect.width()),
00398 //                       qRound(_currentImage.rect.height()), 32);
00399 //            _currentImage.image.fill(_fillColor.pixel());
00400 //            addImage();
00401 //        } else if ( path[i].isVerticalSegment() ) {
00402 //            kdDebug(30516) << "  vertical segment" << endl;
00403 //        }
00404 //    }
00405 }
00406 
00407 void Device::fill(GfxState */*state*/)
00408 {
00409 //    kdDebug(30516) << "fill" << endl;
00410 //    doFill(state);
00411 }
00412 
00413 void Device::eoFill(GfxState */*state*/)
00414 {
00415 //    kdDebug(30516) << "eoFill" << endl;
00416 //    convertPath(state);
00417 //    doFill(state);
00418 }
00419 
00420 void Device::doFill(const DPathVector &path)
00421 {
00422     for (uint i=0; i<path.size(); i++) {
00423         if ( path[i].isSegment() ) continue;
00424         if ( path[i].isRectangle() ) {
00425             kdDebug(30516) << "fill rectangle" << endl;
00426             if ( !_currentImage.image.isNull() ) addImage();
00427             _currentImage.rect = path[i].boundingRect();
00428             _currentImage.image =
00429                 QImage(qRound(_currentImage.rect.width()),
00430                        qRound(_currentImage.rect.height()), 32);
00431             _currentImage.image.fill(_fillColor.pixel());
00432             addImage();
00433         }
00434     }
00435 }
00436 
00437 DPathVector Device::convertPath(GfxState *state)
00438 {
00439     GfxPath *path = state->getPath();
00440     uint nbPaths = path->getNumSubpaths();
00441     DPathVector vector;
00442     for (uint i=0; i<nbPaths; i++) {
00443         GfxSubpath *spath = path->getSubpath(i);
00444         uint nbPoints = spath->getNumPoints();
00445         DPath dpath;
00446         for (uint k=0; k<nbPoints; k++) {
00447             if ( k>=1 && spath->getCurve(k) ) {
00448                 kdDebug(30516) << "    bezier curve : ignore !" << endl;
00449                 dpath = DPath();
00450                 break;
00451             } else {
00452                 DPoint dpoint;
00453                 state->transform(spath->getX(k), spath->getY(k),
00454                                  &dpoint.x, &dpoint.y);
00455                 dpath.push_back(dpoint);
00456             }
00457         }
00458         if ( dpath.size()!=0 ) vector.push_back(dpath);
00459     }
00460     return vector;
00461 }
00462 
00463 } // namespace
KDE Home | KDE Accessibility Home | Description of Access Keys