You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
435 lines
14 KiB
435 lines
14 KiB
/*
|
|
* Copyright (c) 2002-2003 Nicolas HADACEK (hadacek@kde.org)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "misc.h"
|
|
|
|
#include <math.h>
|
|
#include <tqfontmetrics.h>
|
|
#include <tqfontdatabase.h>
|
|
#include <tdeglobal.h>
|
|
#include <kdebug.h>
|
|
|
|
#include "Link.h"
|
|
#include "Catalog.h"
|
|
#include "GfxState.h"
|
|
#include "GfxFont.h"
|
|
|
|
|
|
namespace PDFImport
|
|
{
|
|
|
|
|
|
TQColor toColor(GfxRGB &rgb)
|
|
{
|
|
return TQColor(tqRound(rgb.r*255), tqRound(rgb.g*255), tqRound(rgb.b*255));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool DRect::operator ==(const DRect &r) const
|
|
{
|
|
return ( equal(_top, r._top) && equal(_bottom, r._bottom)
|
|
&& equal(_left, r._left) && equal(_right, r._right) );
|
|
}
|
|
|
|
bool DRect::isInside(const DRect &r, double percent) const
|
|
{
|
|
return ( more(r._top, _top, percent) && less(r._bottom, _bottom, percent)
|
|
&& more(r._left, _left, percent)
|
|
&& less(r._right, _right, percent) );
|
|
}
|
|
|
|
void DRect::unite(const DRect &r)
|
|
{
|
|
if ( !r.isValid() ) return;
|
|
if ( !isValid() ) {
|
|
*this = r;
|
|
return;
|
|
}
|
|
_left = kMin(_left, r._left);
|
|
_right = kMax(_right, r._right);
|
|
_top = kMin(_top, r._top);
|
|
_bottom = kMax(_bottom, r._bottom);
|
|
}
|
|
|
|
TQString DRect::toString() const
|
|
{
|
|
if ( !isValid() ) return "invalid rect";
|
|
return TQString("left=%1 right=%2 top=%3 bottom=%4").arg(_left).arg(_right)
|
|
.arg(_top).arg(_bottom);
|
|
}
|
|
|
|
bool DPath::isRectangle() const
|
|
{
|
|
if ( size()!=5 ) return false;
|
|
if ( !equal(at(0).x, at(3).x) || !equal(at(0).x, at(4).x) ) return false;
|
|
if ( !equal(at(0).y, at(1).y) || !equal(at(0).y, at(4).y) ) return false;
|
|
if ( !equal(at(1).x, at(2).x) || !equal(at(2).y, at(3).y) ) return false;
|
|
return true;
|
|
}
|
|
|
|
DRect DPath::boundingRect() const
|
|
{
|
|
if ( size()==0 ) return DRect();
|
|
DRect r(at(0).x, at(0).x, at(0).y, at(0).y);
|
|
for (uint i=1; i<size(); i++) {
|
|
r.setTop( kMin(r.top(), at(i).y) );
|
|
r.setBottom( kMax(r.bottom(), at(i).y) );
|
|
r.setLeft( kMin(r.left(), at(i).x) );
|
|
r.setRight( kMax(r.right(), at(i).x) );
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
TQDict<Font::Data> *Font::_dict = 0;
|
|
const char *Font::FAMILY_DATA[Nb_Family] = {
|
|
"Times", "Helvetica", "Courier", "Symbol"
|
|
};
|
|
|
|
void Font::init()
|
|
{
|
|
Q_ASSERT( _dict==0 );
|
|
_dict = new TQDict<Data>(100, false); // case insensitive
|
|
_dict->setAutoDelete(true);
|
|
}
|
|
|
|
void Font::cleanup()
|
|
{
|
|
delete _dict;
|
|
_dict = 0;
|
|
}
|
|
|
|
Font::Font()
|
|
: _pointSize(12), _color(TQt::black)
|
|
{
|
|
init("times-roman");
|
|
}
|
|
|
|
Font::Font(const GfxState *state, double size)
|
|
{
|
|
if ( size<1 ) kdDebug(30516) << "very small font size=" << size << endl;
|
|
_pointSize = tqRound(size);
|
|
|
|
GfxRGB rgb;
|
|
state->getFillRGB(&rgb);
|
|
_color = toColor(rgb);
|
|
|
|
GfxFont *font = state->getFont();
|
|
GString *gname = (font ? font->getName() : 0);
|
|
TQString name = (gname ? gname->getCString() : 0);
|
|
// kdDebug(30516) << "font: " << name << endl;
|
|
name = name.section('+', 1, 1).lower();
|
|
if ( name.isEmpty() ) name = "##dummy"; // dummy name
|
|
init(name);
|
|
}
|
|
|
|
struct KnownData {
|
|
const char *name;
|
|
FontFamily family;
|
|
FontStyle style;
|
|
bool latex;
|
|
};
|
|
static const KnownData KNOWN_DATA[] = {
|
|
// standard XPDF fonts (the order is important for finding !)
|
|
{ "times-roman", Times, Regular, false },
|
|
{ "times-bolditalic", Times, BoldItalic, false },
|
|
{ "times-bold", Times, Bold, false },
|
|
{ "times-italic", Times, Italic, false },
|
|
{ "helvetica-bolditalic", Helvetica, BoldItalic, false },
|
|
{ "helvetica-bold", Helvetica, Bold, false },
|
|
{ "helvetica-italic", Times, Italic, false },
|
|
{ "helvetica", Helvetica, Regular, false },
|
|
{ "courier-bolditalic", Courier, BoldItalic, false },
|
|
{ "courier-bold", Courier, Bold, false },
|
|
{ "courier-italic", Courier, Italic, false },
|
|
{ "courier", Courier, Regular, false },
|
|
{ "symbol", Symbol, Regular, false },
|
|
|
|
// some latex fonts
|
|
{ "cmr", Times, Regular, true },
|
|
{ "cmbx", Times, Bold, true },
|
|
{ "cmcsc", Times, Regular, true }, // small caps
|
|
{ "cmmi", Times, Italic, true },
|
|
{ "cmtt", Courier, Regular, true },
|
|
{ "cmsy", Symbol, Regular, true },
|
|
{ "msbm", Times, Regular, true }, // math caps
|
|
|
|
{ 0, Nb_Family, Regular, false }
|
|
};
|
|
|
|
void Font::init(const TQString &n)
|
|
{
|
|
// check if font already parsed
|
|
_data = _dict->find(n);
|
|
if ( _data==0 ) {
|
|
// kdDebug(30516) << "font " << n << endl;
|
|
TQString name = n;
|
|
name.replace("oblique", "italic");
|
|
|
|
// check if known font
|
|
_data = new Data;
|
|
uint i = 0;
|
|
while ( KNOWN_DATA[i].name!=0 ) {
|
|
if ( name.find(KNOWN_DATA[i].name)!=-1 ) {
|
|
// kdDebug(30516) << "found " << KNOWN_DATA[i].name
|
|
// << " " << isBold(KNOWN_DATA[i].style) << endl;
|
|
_data->family = FAMILY_DATA[KNOWN_DATA[i].family];
|
|
_data->style = KNOWN_DATA[i].style;
|
|
_data->latex = KNOWN_DATA[i].latex;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if ( _data->family.isEmpty() ) { // let's try harder
|
|
// simple heuristic
|
|
kdDebug(30516) << "unknown font : " << n << endl;
|
|
if ( name.find("times")!=-1 )
|
|
_data->family = FAMILY_DATA[Times];
|
|
else if ( name.find("helvetica")!=-1 )
|
|
_data->family = FAMILY_DATA[Helvetica];
|
|
else if ( name.find("courier")!=-1 )
|
|
_data->family = FAMILY_DATA[Courier];
|
|
else if ( name.find("symbol")!=-1 )
|
|
_data->family = FAMILY_DATA[Symbol];
|
|
else { // with TQt
|
|
TQFontDatabase fdb;
|
|
TQStringList list = fdb.families();
|
|
list = list.grep(name, false);
|
|
if ( !list.isEmpty() ) {
|
|
_data->family = list[0];
|
|
kdDebug(30516) << "in TQt database as " << list[0] << endl;
|
|
}
|
|
else {
|
|
kdDebug(30516) << "really unknown font !" << endl;
|
|
_data->family = name;
|
|
}
|
|
}
|
|
|
|
bool italic = ( name.find("italic")!=-1 );
|
|
bool bold = ( name.find("bold")!=-1 );
|
|
_data->style = toStyle(bold, italic);
|
|
_data->latex = false;
|
|
}
|
|
|
|
_dict->insert(name, _data);
|
|
}
|
|
|
|
// check if TQFont already created
|
|
if ( !_data->height.contains(_pointSize) ) {
|
|
TQFont font(_data->family, _pointSize,
|
|
(isBold(_data->style) ? TQFont::Bold : TQFont::Normal),
|
|
isItalic(_data->style));
|
|
TQFontMetrics fm(font);
|
|
_data->height.insert(_pointSize, fm.height());
|
|
}
|
|
}
|
|
|
|
bool Font::operator ==(const Font &font) const
|
|
{
|
|
if ( _pointSize!=font._pointSize ) return false;
|
|
if ( _data->family!=font._data->family ) return false;
|
|
if ( _data->style!=font._data->style ) return false;
|
|
// if ( _underline!=font._underline ) return false;
|
|
// if ( _strikeOut!=font._strikeOut ) return false;
|
|
if ( _color!=font._color ) return false;
|
|
return true;
|
|
}
|
|
|
|
bool Font::format(TQDomDocument &doc, TQDomElement &f,
|
|
uint pos, uint len, bool all) const
|
|
{
|
|
f.setAttribute("id", 1);
|
|
if (!all) f.setAttribute("pos", pos);
|
|
if (!all) f.setAttribute("len", len);
|
|
|
|
TQDomElement element;
|
|
Font def;
|
|
|
|
if ( all || _data->family!=def._data->family ) {
|
|
element = doc.createElement("FONT");
|
|
element.setAttribute("name", _data->family);
|
|
f.appendChild(element);
|
|
}
|
|
if ( all || _pointSize!=def._pointSize ) {
|
|
element = doc.createElement("SIZE");
|
|
element.setAttribute("value", _pointSize);
|
|
f.appendChild(element);
|
|
}
|
|
if ( all || isItalic(_data->style)!=isItalic(def._data->style) ) {
|
|
element = doc.createElement("ITALIC");
|
|
element.setAttribute("value", (isItalic(_data->style) ? 1 : 0));
|
|
f.appendChild(element);
|
|
}
|
|
if ( all || isBold(_data->style)!=isBold(def._data->style) ) {
|
|
element = doc.createElement("WEIGHT");
|
|
element.setAttribute("value",
|
|
(isBold(_data->style) ? TQFont::Bold : TQFont::Normal));
|
|
f.appendChild(element);
|
|
}
|
|
// if ( all || _underline!=def._underline ) {
|
|
// element = doc.createElement("UNDERLINE");
|
|
// element.setAttribute("value", (_underline ? 1 : 0));
|
|
// f.appendChild(element);
|
|
// }
|
|
// if ( all || _strikeOut!=def._strikeOut ) {
|
|
// element = doc.createElement("STRIKEOUT");
|
|
// element.setAttribute("value", (_strikeOut ? 1 : 0));
|
|
// f.appendChild(element);
|
|
// }
|
|
|
|
if (all) {
|
|
element = doc.createElement("VERTALIGN");
|
|
element.setAttribute("value", 0);
|
|
f.appendChild(element);
|
|
}
|
|
|
|
if ( all || _color!=def._color ) {
|
|
element = doc.createElement("COLOR");
|
|
element.setAttribute("red", _color.red());
|
|
element.setAttribute("green", _color.green());
|
|
element.setAttribute("blue", _color.blue());
|
|
f.appendChild(element);
|
|
}
|
|
|
|
if (all) { // #### FIXME
|
|
element = doc.createElement("TEXTBACKGROUNDCOLOR");
|
|
element.setAttribute("red", 255);
|
|
element.setAttribute("green",255);
|
|
element.setAttribute("blue", 255);
|
|
f.appendChild(element);
|
|
}
|
|
|
|
return f.hasChildNodes();
|
|
}
|
|
|
|
void Font::setFamily(FontFamily f)
|
|
{
|
|
int k = -1;
|
|
uint i=0;
|
|
while ( KNOWN_DATA[i].name!=0 ) {
|
|
if ( KNOWN_DATA[i].family==f ) {
|
|
if ( KNOWN_DATA[i].style==_data->style ) {
|
|
k = i;
|
|
break;
|
|
}
|
|
if ( k==-1 ) k = i;
|
|
}
|
|
i++;
|
|
}
|
|
if ( k==-1 ) k = 0;
|
|
|
|
init(KNOWN_DATA[k].name);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
Link::Link(const DRect &rect, LinkAction &action, Catalog &catalog)
|
|
: _rect(rect)
|
|
{
|
|
switch ( action.getKind() ) {
|
|
case actionGoTo: {
|
|
LinkGoTo &lgoto = static_cast<LinkGoTo &>(action);
|
|
LinkDest *dest = (lgoto.getDest() ? lgoto.getDest()->copy()
|
|
: catalog.findDest( lgoto.getNamedDest() ));
|
|
int page = 1;
|
|
if (dest) {
|
|
if ( dest->isPageRef() ) {
|
|
Ref pageref = dest->getPageRef();
|
|
page = catalog.findPage(pageref.num, pageref.gen);
|
|
} else page = dest->getPageNum();
|
|
delete dest;
|
|
}
|
|
|
|
_href = TQString("bkm://") + pageLinkName(page);
|
|
// kdDebug(30516) << "link to page " << page << endl;
|
|
break;
|
|
}
|
|
|
|
case actionGoToR: {
|
|
LinkGoToR &lgotor = static_cast<LinkGoToR &>(action);
|
|
_href = "file://";
|
|
if ( lgotor.getFileName() )
|
|
_href += lgotor.getFileName()->getCString();
|
|
int page = 1;
|
|
if ( lgotor.getDest() ) {
|
|
LinkDest *dest = lgotor.getDest()->copy();
|
|
if ( !dest->isPageRef() ) page = dest->getPageNum();
|
|
delete dest;
|
|
}
|
|
|
|
kdDebug(30516) << "link to filename \"" << _href << "\" (page "
|
|
<< page << ")" <<endl;
|
|
break;
|
|
}
|
|
|
|
case actionLaunch: {
|
|
LinkLaunch &llaunch = static_cast<LinkLaunch &>(action);
|
|
_href = "file://";
|
|
if ( llaunch.getFileName() )
|
|
_href += llaunch.getFileName()->getCString();
|
|
|
|
kdDebug(30516) << "link to launch/open \"" << _href << "\"" << endl;
|
|
break;
|
|
}
|
|
|
|
case actionURI: {
|
|
LinkURI &luri = static_cast<LinkURI &>(action);
|
|
if ( luri.getURI() ) _href = luri.getURI()->getCString();
|
|
|
|
kdDebug(30516) << "link to URI \"" << _href << "\"" << endl;
|
|
break;
|
|
}
|
|
|
|
case actionMovie:
|
|
case actionNamed:
|
|
case actionUnknown:
|
|
kdDebug(30516) << "unsupported link=" << action.getKind() << endl;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Link::format(TQDomDocument &doc, TQDomElement &f, uint pos,
|
|
const TQString &text) const
|
|
{
|
|
f.setAttribute("id", 4);
|
|
f.setAttribute("pos", pos);
|
|
f.setAttribute("len", 1);
|
|
|
|
TQDomElement v = doc.createElement("VARIABLE");
|
|
TQDomElement element = doc.createElement("TYPE");
|
|
element.setAttribute("type", 9);
|
|
element.setAttribute("key", "STRING");
|
|
element.setAttribute("text", text);
|
|
v.appendChild(element);
|
|
element = doc.createElement("LINK");
|
|
element.setAttribute("linkName", text);
|
|
element.setAttribute("hrefName", _href);
|
|
v.appendChild(element);
|
|
|
|
f.appendChild(v);
|
|
}
|
|
|
|
TQString Link::pageLinkName(uint i)
|
|
{
|
|
return TQString("page") + TQString::number(i);
|
|
}
|
|
|
|
} // namespace
|