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.
591 lines
14 KiB
591 lines
14 KiB
/*
|
|
* This file is part of the KDE libraries
|
|
* Copyright (c) 2001-2003 Michael Goffioul <tdeprint@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 <tdelocale.h>
|
|
#include <tdetempfile.h>
|
|
#include <tqfile.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
|
|
void tdeprint_ppdscanner_init( TQIODevice* );
|
|
void tdeprint_ppdscanner_terminate( bool deleteIt = true );
|
|
int tdeprint_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 );
|
|
tdeprint_ppdscanner_init( d );
|
|
if ( tdeprint_ppdparse( this ) != 0 )
|
|
result = false;
|
|
tdeprint_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( tdeprint_ppdscanner_numberoflines() ) + loader.errorMsg();
|
|
return driver;
|
|
}
|
|
|
|
bool PPDLoader::openUi( const TQString& name, const TQString& desc, const TQString& type )
|
|
{
|
|
if ( m_option )
|
|
{
|
|
tqWarning( "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 )
|
|
{
|
|
TQStringVariantMap::ConstIterator it = var.mapFind( "args_byname" );
|
|
if ( it != var.mapEnd() )
|
|
{
|
|
TQVariant opts = it.data();
|
|
for ( it = opts.mapBegin(); it != opts.mapEnd(); ++it )
|
|
{
|
|
TQStringVariantMap 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
|
|
{
|
|
tqWarning( "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 )
|
|
{
|
|
//tqDebug( "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;
|
|
}
|