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.
tdelibs/kdeprint/ppdloader.cpp

591 lines
14 KiB

/*
* This file is part of the KDE libraries
* Copyright (c) 2001-2003 Michael Goffioul <kdeprint@swing.be>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
**/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* Needed for getline */
#endif
#include "ppdloader.h"
#include "foomatic2loader.h"
#include "driver.h"
#include <kfilterdev.h>
#include <kdebug.h>
#include <klocale.h>
#include <ktempfile.h>
#include <tqfile.h>
#include <math.h>
#include <stdlib.h>
void kdeprint_ppdscanner_init( TQIODevice* );
void kdeprint_ppdscanner_terminate( bool deleteIt = true );
int kdeprint_ppdscanner_numberoflines();
static TQString processLocaleString( const TQString& s )
{
TQString res;
uint pos = 0;
while ( pos < s.length() )
{
TQChar c = s[ pos++ ];
if ( c == '<' )
{
bool flag = false;
uint hc = 0;
while ( pos < s.length() )
{
TQChar cc = s[ pos++ ];
uint _hc = 0;
if ( cc == '>' )
break;
else if ( cc.isDigit() )
_hc = cc.digitValue();
else
_hc = cc.lower().latin1() - 'a' + 10;
if ( flag )
{
hc |= _hc;
res.append( TQChar( hc ) );
hc = 0;
}
else
hc = ( _hc << 4 );
flag = !flag;
}
}
else
{
res.append( c );
}
}
return res;
}
static TQValueList<float> splitNumberString( const TQString& _s )
{
TQString s = _s.simplifyWhiteSpace();
TQValueList<float> l;
int p1 = 1, p2 = 0;
while ( true )
{
p2 = s.find( ' ', p1 );
if ( p2 != -1 )
{
l.append( s.mid( p1, p2-p1 ).toFloat() );
p1 = p2+1;
}
else
{
// ignore the final quote
l.append( s.mid( p1, s.length() - p1 - 1 ).toFloat() );
break;
}
}
return l;
}
struct PS_private
{
TQString name;
struct
{
float width, height;
} size;
struct
{
float left, bottom, right, top;
} area;
};
PPDLoader::PPDLoader()
{
m_option = 0;
m_ps.setAutoDelete( true );
}
PPDLoader::~PPDLoader()
{
}
DrMain* PPDLoader::readFromFile( const TQString& filename )
{
bool ppdFilenameIsTempFile = false;
TQString ppdFilename = filename;
if (filename.startsWith("compressed-ppd:")) {
KTempFile tempFile(TQString::null, "ppd", 0600);
tempFile.setAutoDelete(false);
ppdFilename = tempFile.name();
TQStringList filenameParts = TQStringList::split(":", filename);
TQString databaseFilename = TQString::null;
TQString compressedFilename = TQString::null;
int i = 0;
for (TQStringList::Iterator it = filenameParts.begin(); it != filenameParts.end(); ++it) {
if (i == 1) {
databaseFilename = *it;
}
else if (i > 1) {
compressedFilename += *it;
}
i++;
}
TQString command = databaseFilename + " cat " + compressedFilename;
FILE* file = popen(command.ascii(), "r");
if (file) {
char * line = NULL;
size_t len = 0;
ssize_t read;
FILE* tmpFileStream = tempFile.fstream();
while ((read = getline(&line, &len, file)) != -1) {
fputs(line, tmpFileStream);
}
if (line) {
free(line);
}
tempFile.close();
pclose(file);
}
else {
fprintf(stderr, "Can't open driver file : %s\n", compressedFilename.ascii());
return 0;
}
ppdFilenameIsTempFile = true;
}
// Initialization
m_groups.clear();
m_option = NULL;
m_fonts.clear();
// Open driver file
TQIODevice *d = KFilterDev::deviceForFile( ppdFilename );
if ( d && d->open( IO_ReadOnly ) )
{
DrMain *driver = new DrMain;
bool result = true;
m_groups.push( driver );
kdeprint_ppdscanner_init( d );
if ( kdeprint_ppdparse( this ) != 0 )
result = false;
kdeprint_ppdscanner_terminate( true );
if ( result )
{
if ( m_groups.size() > 1 )
kdWarning( 500 ) << "PPD syntax error, GROUP specification not correctly closed" << endl;
if ( driver->has( "foodata" ) )
{
Foomatic2Loader loader;
if ( loader.readFromBuffer( driver->get( "foodata" ) ) )
{
driver = loader.modifyDriver( driver );
}
else
kdWarning( 500 ) << "PPD syntax error, Foomatic data read failed" << endl;
}
processPageSizes( driver );
if ( !m_fonts.isEmpty() )
driver->set( "fonts", m_fonts.join( "," ) );
if (ppdFilenameIsTempFile) {
driver->set("temporary-cppd", ppdFilename);
}
return driver;
}
else
kdWarning( 500 ) << "PPD syntax error, PPD parse failed" << endl;
delete driver;
m_ps.clear();
}
else
kdWarning( 500 ) << "PPD read error, unable to open device for file " << ppdFilename << endl;
return 0;
}
DrMain* PPDLoader::loadDriver( const TQString& filename, TQString* msg )
{
PPDLoader loader;
DrMain *driver = loader.readFromFile( filename );
if ( !driver && msg )
*msg = filename + i18n( "(line %1): " ).arg( kdeprint_ppdscanner_numberoflines() ) + loader.errorMsg();
return driver;
}
bool PPDLoader::openUi( const TQString& name, const TQString& desc, const TQString& type )
{
if ( m_option )
{
qWarning( "PPD syntax error, UI specification not correctly closed" );
endUi( m_option->name() );
}
if ( type == "PickOne" || type == "PickMany" )
m_option = new DrListOption;
else if ( type == "Boolean" )
m_option = new DrBooleanOption;
else
return false;
if ( name[ 0 ] == '*' )
m_option->setName( name.mid( 1 ) );
else
m_option->setName( name );
if ( desc.isEmpty() )
m_option->set( "text", m_option->name() );
else
m_option->set( "text", processLocaleString( desc ) );
return true;
}
bool PPDLoader::endUi( const TQString& name )
{
if ( m_option && ( m_option->name() == name || m_option->name() == name.mid( 1 ) ) )
{
if ( m_option->name() == "PageRegion" )
delete m_option;
else
{
TQString defval = m_option->get( "default" );
DrGroup *grp = 0;
if ( !defval.isEmpty() )
m_option->setValueText( defval );
if ( m_groups.size() == 1 )
{
// we don't have any group defined, create the
// most adapted one.
grp = findOrCreateGroupForOption( m_option->name() );
}
else
grp = m_groups.top();
grp->addOption( m_option );
if ( grp->get( "text" ).contains( "install", false ) )
m_option->set( "fixed", "1" );
}
m_option = 0;
return true;
}
return false;
}
bool PPDLoader::openGroup( const TQString& name, const TQString& desc )
{
DrGroup *grp = new DrGroup;
grp->setName( name );
if ( desc.isEmpty() )
grp->set( "text", name );
else
grp->set( "text", processLocaleString( desc ) );
m_groups.top()->addGroup( grp );
m_groups.push( grp );
return true;
}
bool PPDLoader::endGroup( const TQString& name )
{
if ( m_groups.size() > 1 && m_groups.top()->name() == name )
{
m_groups.pop();
return true;
}
return false;
}
bool PPDLoader::putStatement( const TQString& keyword, const TQString& name, const TQString& desc, const TQStringList& values )
{
if ( m_option )
{
if ( !name.isEmpty() && m_option->name() == keyword )
{
if ( m_option->type() >= DrBase::List )
{
DrBase *ch = new DrBase;
ch->setName( name );
if ( desc.isEmpty() )
ch->set( "text", name );
else
ch->set( "text", processLocaleString( desc ) );
static_cast<DrListOption*>( m_option )->addChoice( ch );
}
else
{
TQString fv = m_option->get( "fixedvals" );
if ( fv.isEmpty() )
fv = name;
else
fv.append( "|" + name );
m_option->set( "fixedvals", fv );
}
}
else if ( keyword == "FoomaticRIPOption" && name == m_option->name()
&& values.size() > 1 )
{
TQString type = values[ 0 ];
if ( type == "float" || type == "int" )
{
DrBase *opt = 0;
if ( type == "float" )
opt = new DrFloatOption;
else
opt = new DrIntegerOption;
opt->setName( m_option->name() );
opt->set( "text", m_option->get( "text" ) );
opt->set( "default", m_option->get( "default" ) );
if ( m_option->type() == DrBase::List )
{
TQStringList vals;
TQPtrListIterator<DrBase> it( *( static_cast<DrListOption*>( m_option )->choices() ) );
for ( ; it.current(); ++it )
vals.append( it.current()->name() );
opt->set( "fixedvals", vals.join( "|" ) );
}
delete m_option;
m_option = opt;
}
// FIXME: support other option types
}
else if ( keyword == "FoomaticRIPOptionRange" && name == m_option->name()
&& values.size() >= 2 && ( m_option->type() == DrBase::Float || m_option->type() == DrBase::Integer ) )
{
m_option->set( "minval", values[ 0 ] );
m_option->set( "maxval", values[ 1 ] );
}
}
else if ( keyword == "Font" && m_groups.size() > 0 )
{
m_fonts << name;
}
return true;
}
bool PPDLoader::putStatement2( const TQString& keyword, const TQString& value )
{
if ( !m_option && m_groups.size() == 1 )
{
DrGroup *driver = m_groups.top();
if ( keyword == "NickName" )
{
driver->set( "text", value );
driver->set( "description", value );
}
else if ( keyword == "Manufacturer" )
driver->set( "manufacturer", value );
else if ( keyword == "ShortNickName" )
driver->set( "model", value );
else if ( keyword == "ColorDevice" )
driver->set( "colordevice", value == "True" ? "1" : "0" );
}
return true;
}
bool PPDLoader::putDefault( const TQString& keyword, const TQString& value )
{
if ( keyword == "Resolution" && m_groups.size() > 0 )
{
// Store default resolution as it could be fed back
// to the application. And default resolution can
// occur outside a OpenUI/CloseUI pair.
m_groups[ 0 ]->set( "resolution", value );
}
if ( m_option && m_option->name() == keyword )
{
m_option->set( "default", value );
return true;
}
else
return false;
}
bool PPDLoader::putConstraint( const TQString& opt1, const TQString& opt2, const TQString& ch1, const TQString& ch2 )
{
if ( !m_option && m_groups.size() == 1 )
{
DrMain *driver = static_cast<DrMain*>( m_groups.top() );
driver->addConstraint( new DrConstraint( opt1, opt2, ch1, ch2 ) );
}
return true;
}
bool PPDLoader::putFooData( const TQString& data )
{
if ( !m_option && m_groups.size() == 1 )
{
m_groups.top()->set( "foodata", m_groups.top()->get( "foodata" ) + data + "\n" );
}
return true;
}
bool PPDLoader::putFooProcessedData( const TQVariant& var )
{
TQMap<TQString,TQVariant>::ConstIterator it = var.mapFind( "args_byname" );
if ( it != var.mapEnd() )
{
TQVariant opts = it.data();
for ( it = opts.mapBegin(); it != opts.mapEnd(); ++it )
{
TQMap<TQString,TQVariant> opt = it.data().toMap();
TQString type = opt[ "type" ].toString();
if ( type == "float" || type == "int" )
{
DrBase *o;
if ( type == "float" )
o = new DrFloatOption;
else
o = new DrIntegerOption;
o->setName( opt[ "name" ].toString() );
o->set( "text", opt[ "comment" ].toString() );
o->set( "minval", opt[ "min" ].toString() );
o->set( "maxval", opt[ "max" ].toString() );
o->set( "default", opt[ "default" ].toString() );
o->setValueText( o->get( "default" ) );
DrGroup *grp = 0;
DrBase *old = m_groups.top()->findOption( o->name(), &grp );
if ( old )
{
if ( old->type() == DrBase::List )
{
TQStringList vals;
TQPtrListIterator<DrBase> it( *( static_cast<DrListOption*>( old )->choices() ) );
for ( ; it.current(); ++it )
vals.append( it.current()->name() );
o->set( "fixedvals", vals.join( "|" ) );
}
grp->removeOption( o->name() );
grp->addOption( o );
}
else
{
qWarning( "Option %s not found in original PPD file", o->name().latin1() );
delete o;
}
}
}
}
return true;
}
bool PPDLoader::putPaperDimension( const TQString& name, const TQString& s )
{
TQValueList<float> l = splitNumberString( s );
PS_private *ps = m_ps.find( name );
if ( !ps )
{
ps = new PS_private;
ps->name = name;
m_ps.insert( name, ps );
}
ps->size.width = l[ 0 ];
ps->size.height = l[ 1 ];
return true;
}
bool PPDLoader::putImageableArea( const TQString& name, const TQString& s )
{
TQValueList<float> l = splitNumberString( s );
PS_private *ps = m_ps.find( name );
if ( !ps )
{
ps = new PS_private;
ps->name = name;
m_ps.insert( name, ps );
}
ps->area.left = l[ 0 ];
ps->area.bottom = l[ 1 ];
ps->area.right = l[ 2 ];
ps->area.top = l[ 3 ];
return true;
}
DrGroup* PPDLoader::findOrCreateGroupForOption( const TQString& optname )
{
TQString grpname;
if ( optname == "PageSize" ||
optname == "InputSlot" ||
optname == "ManualFeed" ||
optname == "MediaType" ||
optname == "MediaColor" ||
optname == "MediaWeight" )
grpname = "General";
else if ( optname.startsWith( "stp" ) ||
optname == "Cyan" ||
optname == "Yellow" ||
optname == "Magenta" ||
optname == "Density" ||
optname == "Contrast" )
grpname = "Adjustments";
else if ( optname.startsWith( "JCL" ) )
grpname = "JCL";
else
grpname = "Others";
DrGroup *grp = 0;
for ( TQPtrListIterator<DrGroup> it( m_groups[ 0 ]->groups() ); it.current(); ++it )
if ( it.current()->name() == grpname )
{
grp = it.current();
break;
}
if ( !grp )
{
grp = new DrGroup;
grp->setName( grpname );
grp->set( "text", grpname );
m_groups[ 0 ]->addGroup( grp );
}
return grp;
}
void PPDLoader::processPageSizes( DrMain *driver )
{
TQDictIterator<PS_private> it( m_ps );
for ( ; it.current(); ++it )
{
//qDebug( "ADDING PAGESIZE: %16s, Size = ( %.2f, %.2f ), Area = ( %.2f, %.2f, %.2f, %.2f )", it.current()->name.latin1(),
// it.current()->size.width, it.current()->size.height,
// it.current()->area.left, it.current()->area.bottom,
// it.current()->area.right, it.current()->area.top );
driver->addPageSize( new DrPageSize( it.current()->name,
( int )it.current()->size.width, ( int )it.current()->size.height,
( int )it.current()->area.left, ( int )it.current()->area.bottom,
( int )ceil( it.current()->size.width - it.current()->area.right ),
( int )ceil( it.current()->size.height - it.current()->area.top ) ) );
}
m_ps.clear();
}
void PPDLoader::setErrorMsg( const TQString& msg )
{
m_errormsg = msg;
}
TQString PPDLoader::errorMsg() const
{
return m_errormsg;
}