filters

misc.cpp

00001 /*
00002  * Copyright (c) 2002-2003 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 "misc.h"
00021 
00022 #include <math.h>
00023 #include <qfontmetrics.h>
00024 #include <qfontdatabase.h>
00025 #include <kglobal.h>
00026 #include <kdebug.h>
00027 
00028 #include "Link.h"
00029 #include "Catalog.h"
00030 #include "GfxState.h"
00031 #include "GfxFont.h"
00032 
00033 
00034 namespace PDFImport
00035 {
00036 
00037 
00038 QColor toColor(GfxRGB &rgb)
00039 {
00040     return QColor(qRound(rgb.r*255), qRound(rgb.g*255), qRound(rgb.b*255));
00041 }
00042 
00043 //-----------------------------------------------------------------------------
00044 bool DRect::operator ==(const DRect &r) const
00045 {
00046     return ( equal(_top, r._top) && equal(_bottom, r._bottom)
00047              && equal(_left, r._left) && equal(_right, r._right) );
00048 }
00049 
00050 bool DRect::isInside(const DRect &r, double percent) const
00051 {
00052     return ( more(r._top, _top, percent) && less(r._bottom, _bottom, percent)
00053              && more(r._left, _left, percent)
00054              && less(r._right, _right, percent) );
00055 }
00056 
00057 void DRect::unite(const DRect &r)
00058 {
00059     if ( !r.isValid() ) return;
00060     if ( !isValid() ) {
00061         *this = r;
00062         return;
00063     }
00064     _left = kMin(_left, r._left);
00065     _right = kMax(_right, r._right);
00066     _top = kMin(_top, r._top);
00067     _bottom = kMax(_bottom, r._bottom);
00068 }
00069 
00070 QString DRect::toString() const
00071 {
00072     if ( !isValid() ) return "invalid rect";
00073     return QString("left=%1 right=%2 top=%3 bottom=%4").arg(_left).arg(_right)
00074         .arg(_top).arg(_bottom);
00075 }
00076 
00077 bool DPath::isRectangle() const
00078 {
00079     if ( size()!=5 ) return false;
00080     if ( !equal(at(0).x, at(3).x) || !equal(at(0).x, at(4).x) ) return false;
00081     if ( !equal(at(0).y, at(1).y) || !equal(at(0).y, at(4).y) ) return false;
00082     if ( !equal(at(1).x, at(2).x) || !equal(at(2).y, at(3).y) ) return false;
00083     return true;
00084 }
00085 
00086 DRect DPath::boundingRect() const
00087 {
00088     if ( size()==0 ) return DRect();
00089     DRect r(at(0).x, at(0).x, at(0).y, at(0).y);
00090     for (uint i=1; i<size(); i++) {
00091         r.setTop( kMin(r.top(), at(i).y) );
00092         r.setBottom( kMax(r.bottom(), at(i).y) );
00093         r.setLeft( kMin(r.left(), at(i).x) );
00094         r.setRight( kMax(r.right(), at(i).x) );
00095     }
00096     return r;
00097 }
00098 
00099 
00100 //-----------------------------------------------------------------------------
00101 QDict<Font::Data> *Font::_dict = 0;
00102 const char *Font::FAMILY_DATA[Nb_Family] = {
00103     "Times", "Helvetica", "Courier", "Symbol"
00104 };
00105 
00106 void Font::init()
00107 {
00108     Q_ASSERT( _dict==0 );
00109     _dict = new QDict<Data>(100, false); // case insensitive
00110     _dict->setAutoDelete(true);
00111 }
00112 
00113 void Font::cleanup()
00114 {
00115     delete _dict;
00116     _dict = 0;
00117 }
00118 
00119 Font::Font()
00120     : _pointSize(12), _color(Qt::black)
00121 {
00122     init("times-roman");
00123 }
00124 
00125 Font::Font(const GfxState *state, double size)
00126 {
00127     if ( size<1 ) kdDebug(30516) << "very small font size=" << size << endl;
00128     _pointSize = qRound(size);
00129 
00130     GfxRGB rgb;
00131     state->getFillRGB(&rgb);
00132     _color = toColor(rgb);
00133 
00134     GfxFont *font = state->getFont();
00135     GString *gname = (font ? font->getName() : 0);
00136     QString name = (gname ? gname->getCString() : 0);
00137 //    kdDebug(30516) << "font: " << name << endl;
00138     name = name.section('+', 1, 1).lower();
00139     if ( name.isEmpty() ) name = "##dummy"; // dummy name
00140     init(name);
00141 }
00142 
00143 struct KnownData {
00144     const char *name;
00145     FontFamily family;
00146     FontStyle  style;
00147     bool   latex;
00148 };
00149 static const KnownData KNOWN_DATA[] = {
00150     // standard XPDF fonts (the order is important for finding !)
00151     { "times-roman",           Times,       Regular,    false },
00152     { "times-bolditalic",      Times,       BoldItalic, false },
00153     { "times-bold",            Times,       Bold,       false },
00154     { "times-italic",          Times,       Italic,     false },
00155     { "helvetica-bolditalic",  Helvetica,   BoldItalic, false },
00156     { "helvetica-bold",        Helvetica,   Bold,       false },
00157     { "helvetica-italic",      Times,       Italic,     false },
00158     { "helvetica",             Helvetica,   Regular,    false },
00159     { "courier-bolditalic",    Courier,     BoldItalic, false },
00160     { "courier-bold",          Courier,     Bold,       false },
00161     { "courier-italic",        Courier,     Italic,     false },
00162     { "courier",               Courier,     Regular,    false },
00163     { "symbol",                Symbol,      Regular,    false },
00164 
00165     // some latex fonts
00166     { "cmr",                   Times,       Regular,    true  },
00167     { "cmbx",                  Times,       Bold,       true  },
00168     { "cmcsc",                 Times,       Regular,    true  }, // small caps
00169     { "cmmi",                  Times,       Italic,     true  },
00170     { "cmtt",                  Courier,     Regular,    true  },
00171     { "cmsy",                  Symbol,      Regular,    true  },
00172     { "msbm",                  Times,       Regular,    true  }, // math caps
00173 
00174     { 0,                       Nb_Family,   Regular,    false }
00175 };
00176 
00177 void Font::init(const QString &n)
00178 {
00179     // check if font already parsed
00180     _data = _dict->find(n);
00181     if ( _data==0 ) {
00182 //        kdDebug(30516) << "font " << n << endl;
00183         QString name = n;
00184         name.replace("oblique", "italic");
00185 
00186         // check if known font
00187         _data = new Data;
00188         uint i = 0;
00189         while ( KNOWN_DATA[i].name!=0 ) {
00190             if ( name.find(KNOWN_DATA[i].name)!=-1 ) {
00191 //                kdDebug(30516) << "found " << KNOWN_DATA[i].name
00192 //                               << " " << isBold(KNOWN_DATA[i].style) << endl;
00193                 _data->family = FAMILY_DATA[KNOWN_DATA[i].family];
00194                 _data->style = KNOWN_DATA[i].style;
00195                 _data->latex = KNOWN_DATA[i].latex;
00196                 break;
00197             }
00198             i++;
00199         }
00200 
00201         if ( _data->family.isEmpty() ) { // let's try harder
00202             // simple heuristic
00203             kdDebug(30516) << "unknown font : " << n << endl;
00204             if ( name.find("times")!=-1 )
00205             _data->family = FAMILY_DATA[Times];
00206             else if ( name.find("helvetica")!=-1 )
00207                 _data->family = FAMILY_DATA[Helvetica];
00208             else if ( name.find("courier")!=-1 )
00209                 _data->family = FAMILY_DATA[Courier];
00210             else if ( name.find("symbol")!=-1 )
00211                 _data->family = FAMILY_DATA[Symbol];
00212             else { // with Qt
00213                 QFontDatabase fdb;
00214                 QStringList list = fdb.families();
00215                 list = list.grep(name, false);
00216                 if ( !list.isEmpty() ) {
00217                     _data->family = list[0];
00218                     kdDebug(30516) << "in Qt database as " << list[0] << endl;
00219                 }
00220                 else {
00221                     kdDebug(30516) << "really unknown font !" << endl;
00222                     _data->family = name;
00223                 }
00224             }
00225 
00226             bool italic = ( name.find("italic")!=-1 );
00227             bool bold = ( name.find("bold")!=-1 );
00228             _data->style = toStyle(bold, italic);
00229             _data->latex = false;
00230         }
00231 
00232         _dict->insert(name, _data);
00233     }
00234 
00235     // check if QFont already created
00236     if ( !_data->height.contains(_pointSize) ) {
00237         QFont font(_data->family, _pointSize,
00238                    (isBold(_data->style) ? QFont::Bold : QFont::Normal),
00239                    isItalic(_data->style));
00240         QFontMetrics fm(font);
00241         _data->height.insert(_pointSize, fm.height());
00242     }
00243 }
00244 
00245 bool Font::operator ==(const Font &font) const
00246 {
00247     if ( _pointSize!=font._pointSize ) return false;
00248     if ( _data->family!=font._data->family ) return false;
00249     if ( _data->style!=font._data->style ) return false;
00250 //    if ( _underline!=font._underline ) return false;
00251 //    if ( _strikeOut!=font._strikeOut ) return false;
00252     if ( _color!=font._color ) return false;
00253     return true;
00254 }
00255 
00256 bool Font::format(QDomDocument &doc, QDomElement &f,
00257                   uint pos, uint len, bool all) const
00258 {
00259     f.setAttribute("id", 1);
00260     if (!all) f.setAttribute("pos", pos);
00261     if (!all) f.setAttribute("len", len);
00262 
00263     QDomElement element;
00264     Font def;
00265 
00266     if ( all || _data->family!=def._data->family ) {
00267         element = doc.createElement("FONT");
00268         element.setAttribute("name", _data->family);
00269         f.appendChild(element);
00270     }
00271     if ( all || _pointSize!=def._pointSize ) {
00272         element = doc.createElement("SIZE");
00273         element.setAttribute("value", _pointSize);
00274         f.appendChild(element);
00275     }
00276     if ( all || isItalic(_data->style)!=isItalic(def._data->style) ) {
00277         element = doc.createElement("ITALIC");
00278         element.setAttribute("value", (isItalic(_data->style) ? 1 : 0));
00279         f.appendChild(element);
00280     }
00281     if ( all || isBold(_data->style)!=isBold(def._data->style) ) {
00282         element = doc.createElement("WEIGHT");
00283         element.setAttribute("value",
00284                          (isBold(_data->style) ? QFont::Bold : QFont::Normal));
00285         f.appendChild(element);
00286     }
00287 //    if ( all || _underline!=def._underline ) {
00288 //        element = doc.createElement("UNDERLINE");
00289 //        element.setAttribute("value", (_underline ? 1 : 0));
00290 //        f.appendChild(element);
00291 //    }
00292 //    if ( all || _strikeOut!=def._strikeOut ) {
00293 //        element = doc.createElement("STRIKEOUT");
00294 //        element.setAttribute("value", (_strikeOut ? 1 : 0));
00295 //        f.appendChild(element);
00296 //    }
00297 
00298     if (all) {
00299         element = doc.createElement("VERTALIGN");
00300         element.setAttribute("value", 0);
00301         f.appendChild(element);
00302     }
00303 
00304     if ( all || _color!=def._color ) {
00305         element = doc.createElement("COLOR");
00306         element.setAttribute("red", _color.red());
00307         element.setAttribute("green", _color.green());
00308         element.setAttribute("blue", _color.blue());
00309         f.appendChild(element);
00310     }
00311 
00312     if (all) { // #### FIXME
00313         element = doc.createElement("TEXTBACKGROUNDCOLOR");
00314         element.setAttribute("red",  255);
00315         element.setAttribute("green",255);
00316         element.setAttribute("blue", 255);
00317         f.appendChild(element);
00318     }
00319 
00320     return f.hasChildNodes();
00321 }
00322 
00323 void Font::setFamily(FontFamily f)
00324 {
00325     int k = -1;
00326     uint i=0;
00327     while ( KNOWN_DATA[i].name!=0 ) {
00328         if ( KNOWN_DATA[i].family==f ) {
00329             if ( KNOWN_DATA[i].style==_data->style ) {
00330                 k = i;
00331                 break;
00332             }
00333             if ( k==-1 ) k = i;
00334         }
00335         i++;
00336     }
00337     if ( k==-1 ) k = 0;
00338 
00339     init(KNOWN_DATA[k].name);
00340 }
00341 
00342 //-----------------------------------------------------------------------------
00343 Link::Link(const DRect &rect, LinkAction &action, Catalog &catalog)
00344     : _rect(rect)
00345 {
00346     switch ( action.getKind() ) {
00347     case actionGoTo: {
00348         LinkGoTo &lgoto = static_cast<LinkGoTo &>(action);
00349         LinkDest *dest = (lgoto.getDest() ? lgoto.getDest()->copy()
00350                           : catalog.findDest( lgoto.getNamedDest() ));
00351         int page = 1;
00352         if (dest) {
00353             if ( dest->isPageRef() ) {
00354                 Ref pageref = dest->getPageRef();
00355                 page = catalog.findPage(pageref.num, pageref.gen);
00356             } else page = dest->getPageNum();
00357             delete dest;
00358         }
00359 
00360         _href = QString("bkm://") + pageLinkName(page);
00361 //        kdDebug(30516) << "link to page " << page << endl;
00362         break;
00363     }
00364 
00365     case actionGoToR: {
00366         LinkGoToR &lgotor = static_cast<LinkGoToR &>(action);
00367         _href = "file://";
00368         if ( lgotor.getFileName() )
00369             _href += lgotor.getFileName()->getCString();
00370         int page = 1;
00371         if ( lgotor.getDest() ) {
00372             LinkDest *dest = lgotor.getDest()->copy();
00373             if ( !dest->isPageRef() ) page = dest->getPageNum();
00374             delete dest;
00375         }
00376 
00377         kdDebug(30516) << "link to filename \"" << _href << "\" (page "
00378                        << page << ")" <<endl;
00379         break;
00380     }
00381 
00382     case actionLaunch: {
00383         LinkLaunch &llaunch = static_cast<LinkLaunch &>(action);
00384         _href = "file://";
00385         if ( llaunch.getFileName() )
00386             _href += llaunch.getFileName()->getCString();
00387 
00388         kdDebug(30516) << "link to launch/open \"" << _href << "\"" << endl;
00389         break;
00390     }
00391 
00392     case actionURI: {
00393         LinkURI &luri = static_cast<LinkURI &>(action);
00394         if ( luri.getURI() ) _href = luri.getURI()->getCString();
00395 
00396         kdDebug(30516) << "link to URI \"" << _href << "\"" << endl;
00397         break;
00398     }
00399 
00400     case actionMovie:
00401     case actionNamed:
00402     case actionUnknown:
00403         kdDebug(30516) << "unsupported link=" << action.getKind() << endl;
00404         break;
00405     }
00406 }
00407 
00408 void Link::format(QDomDocument &doc, QDomElement &f, uint pos,
00409                   const QString &text) const
00410 {
00411     f.setAttribute("id", 4);
00412     f.setAttribute("pos", pos);
00413     f.setAttribute("len", 1);
00414 
00415     QDomElement v = doc.createElement("VARIABLE");
00416     QDomElement element = doc.createElement("TYPE");
00417     element.setAttribute("type", 9);
00418     element.setAttribute("key", "STRING");
00419     element.setAttribute("text", text);
00420     v.appendChild(element);
00421     element = doc.createElement("LINK");
00422     element.setAttribute("linkName", text);
00423     element.setAttribute("hrefName", _href);
00424     v.appendChild(element);
00425 
00426     f.appendChild(v);
00427 }
00428 
00429 QString Link::pageLinkName(uint i)
00430 {
00431     return QString("page") + QString::number(i);
00432 }
00433 
00434 } // namespace
KDE Home | KDE Accessibility Home | Description of Access Keys