/* * This file is part of the KDE libraries * Copyright (c) 2001-2003 Michael Goffioul * * 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 #include #include #include #include #include #include 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 splitNumberString( const TQString& _s ) { TQString s = _s.simplifyWhiteSpace(); TQValueList 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( 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 it( *( static_cast( 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( 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::ConstIterator it = var.mapFind( "args_byname" ); if ( it != var.mapEnd() ) { TQVariant opts = it.data(); for ( it = opts.mapBegin(); it != opts.mapEnd(); ++it ) { TQMap 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 it( *( static_cast( 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 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 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 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 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; }