ppdloader.cpp

00001 /*
00002  *  This file is part of the KDE libraries
00003  *  Copyright (c) 2001-2003 Michael Goffioul <kdeprint@swing.be>
00004  *
00005  *  This library is free software; you can redistribute it and/or
00006  *  modify it under the terms of the GNU Library General Public
00007  *  License version 2 as published by the Free Software Foundation.
00008  *
00009  *  This library 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 GNU
00012  *  Library General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU Library General Public License
00015  *  along with this library; see the file COPYING.LIB.  If not, write to
00016  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  *  Boston, MA 02110-1301, USA.
00018  **/
00019 
00020 #include "ppdloader.h"
00021 #include "foomatic2loader.h"
00022 #include "driver.h"
00023 
00024 #include <kfilterdev.h>
00025 #include <kdebug.h>
00026 #include <klocale.h>
00027 #include <qfile.h>
00028 #include <math.h>
00029 
00030 void kdeprint_ppdscanner_init( QIODevice* );
00031 void kdeprint_ppdscanner_terminate( bool deleteIt = true );
00032 int kdeprint_ppdscanner_numberoflines();
00033 
00034 static QString processLocaleString( const QString& s )
00035 {
00036     QString res;
00037     uint pos = 0;
00038     while ( pos < s.length() )
00039     {
00040         QChar c = s[ pos++ ];
00041         if ( c == '<' )
00042         {
00043             bool flag = false;
00044             uint hc = 0;
00045             while ( pos < s.length() )
00046             {
00047                 QChar cc = s[ pos++ ];
00048                 uint _hc = 0;
00049                 if ( cc == '>' )
00050                     break;
00051                 else if ( cc.isDigit() )
00052                     _hc = cc.digitValue();
00053                 else
00054                     _hc = cc.lower().latin1() - 'a' + 10;
00055                 if ( flag )
00056                 {
00057                     hc |= _hc;
00058                     res.append( QChar( hc ) );
00059                     hc = 0;
00060                 }
00061                 else
00062                     hc = ( _hc << 4 );
00063                 flag = !flag;
00064             }
00065         }
00066         else
00067         {
00068             res.append( c );
00069         }
00070     }
00071     return res;
00072 }
00073 
00074 static QValueList<float> splitNumberString( const QString& _s )
00075 {
00076         QString s = _s.simplifyWhiteSpace();
00077     QValueList<float> l;
00078     int p1 = 1, p2 = 0;
00079     while ( true )
00080     {
00081         p2 = s.find( ' ', p1 );
00082         if ( p2 != -1 )
00083         {
00084             l.append( s.mid( p1, p2-p1 ).toFloat() );
00085             p1 = p2+1;
00086         }
00087         else
00088         {
00089             // ignore the final quote
00090             l.append( s.mid( p1, s.length() - p1 - 1 ).toFloat() );
00091             break;
00092         }
00093     }
00094     return l;
00095 }
00096 
00097 struct PS_private
00098 {
00099     QString name;
00100     struct
00101     {
00102         float width, height;
00103     } size;
00104     struct
00105     {
00106         float left, bottom, right, top;
00107     } area;
00108 };
00109 
00110 PPDLoader::PPDLoader()
00111 {
00112     m_option = 0;
00113     m_ps.setAutoDelete( true );
00114 }
00115 
00116 PPDLoader::~PPDLoader()
00117 {
00118 }
00119 
00120 DrMain* PPDLoader::readFromFile( const QString& filename )
00121 {
00122     // Initialization
00123     m_groups.clear();
00124     m_option = NULL;
00125     m_fonts.clear();
00126     // Open driver file
00127     QIODevice *d = KFilterDev::deviceForFile( filename );
00128     if ( d && d->open( IO_ReadOnly ) )
00129     {
00130         DrMain *driver = new DrMain;
00131         bool result = true;
00132 
00133         m_groups.push( driver );
00134         kdeprint_ppdscanner_init( d );
00135         if ( kdeprint_ppdparse( this ) != 0 )
00136             result = false;
00137         kdeprint_ppdscanner_terminate( true );
00138 
00139         if ( result )
00140         {
00141             if ( m_groups.size() > 1 )
00142                 kdWarning( 500 ) << "PPD syntax error, GROUP specification not correctly closed" << endl;
00143             if ( driver->has( "foodata" ) )
00144             {
00145                 Foomatic2Loader loader;
00146                 if ( loader.readFromBuffer( driver->get( "foodata" ) ) )
00147                 {
00148                     driver = loader.modifyDriver( driver );
00149                 }
00150                 else
00151                     kdWarning( 500 ) << "PPD syntax error, Foomatic data read failed" << endl;
00152             }
00153             processPageSizes( driver );
00154             if ( !m_fonts.isEmpty() )
00155                 driver->set( "fonts", m_fonts.join( "," ) );
00156             return driver;
00157         }
00158         else
00159             kdWarning( 500 ) << "PPD syntax error, PPD parse failed" << endl;
00160         delete driver;
00161         m_ps.clear();
00162     }
00163     else
00164         kdWarning( 500 ) << "PPD read error, unable to open device for file " << filename << endl;
00165     return 0;
00166 }
00167 
00168 DrMain* PPDLoader::loadDriver( const QString& filename, QString* msg )
00169 {
00170     PPDLoader loader;
00171     DrMain *driver = loader.readFromFile( filename );
00172     if ( !driver && msg )
00173         *msg = filename + i18n( "(line %1): " ).arg( kdeprint_ppdscanner_numberoflines() ) + loader.errorMsg();
00174     return driver;
00175 }
00176 
00177 bool PPDLoader::openUi( const QString& name, const QString& desc, const QString& type )
00178 {
00179     if ( m_option )
00180     {
00181         qWarning( "PPD syntax error, UI specification not correctly closed" );
00182         endUi( m_option->name() );
00183     }
00184 
00185     if ( type == "PickOne" || type == "PickMany" )
00186         m_option = new DrListOption;
00187     else if ( type == "Boolean" )
00188         m_option = new DrBooleanOption;
00189     else
00190         return false;
00191     if ( name[ 0 ] == '*' )
00192         m_option->setName( name.mid( 1 ) );
00193     else
00194         m_option->setName( name );
00195     if ( desc.isEmpty() )
00196         m_option->set( "text", m_option->name() );
00197     else
00198         m_option->set( "text", processLocaleString( desc ) );
00199     return true;
00200 }
00201 
00202 bool PPDLoader::endUi( const QString& name )
00203 {
00204     if ( m_option && ( m_option->name() == name || m_option->name() == name.mid( 1 ) ) )
00205     {
00206         if ( m_option->name() == "PageRegion" )
00207             delete m_option;
00208         else
00209         {
00210             QString defval = m_option->get( "default" );
00211             DrGroup *grp = 0;
00212             if ( !defval.isEmpty() )
00213                 m_option->setValueText( defval );
00214             if ( m_groups.size() == 1 )
00215             {
00216                 // we don't have any group defined, create the
00217                 // most adapted one.
00218                 grp = findOrCreateGroupForOption( m_option->name() );
00219             }
00220             else
00221                 grp = m_groups.top();
00222             grp->addOption( m_option );
00223             if ( grp->get( "text" ).contains( "install", false ) )
00224                 m_option->set( "fixed", "1" );
00225         }
00226         m_option = 0;
00227         return true;
00228     }
00229     return false;
00230 }
00231 
00232 bool PPDLoader::openGroup( const QString& name, const QString& desc )
00233 {
00234     DrGroup *grp = new DrGroup;
00235     grp->setName( name );
00236     if ( desc.isEmpty() )
00237         grp->set( "text", name );
00238     else
00239         grp->set( "text", processLocaleString( desc ) );
00240     m_groups.top()->addGroup( grp );
00241     m_groups.push( grp );
00242     return true;
00243 }
00244 
00245 bool PPDLoader::endGroup( const QString& name )
00246 {
00247     if ( m_groups.size() > 1 && m_groups.top()->name() == name )
00248     {
00249         m_groups.pop();
00250         return true;
00251     }
00252     return false;
00253 }
00254 
00255 bool PPDLoader::putStatement( const QString& keyword, const QString& name, const QString& desc, const QStringList& values )
00256 {
00257     if ( m_option )
00258     {
00259         if ( !name.isEmpty() && m_option->name() == keyword )
00260         {
00261             if ( m_option->type() >= DrBase::List )
00262             {
00263                 DrBase *ch = new DrBase;
00264                 ch->setName( name );
00265                 if ( desc.isEmpty() )
00266                     ch->set( "text", name );
00267                 else
00268                     ch->set( "text", processLocaleString( desc ) );
00269                 static_cast<DrListOption*>( m_option )->addChoice( ch );
00270             }
00271             else
00272             {
00273                 QString fv = m_option->get( "fixedvals" );
00274                 if ( fv.isEmpty() )
00275                     fv = name;
00276                 else
00277                     fv.append( "|" + name );
00278                 m_option->set( "fixedvals", fv );
00279             }
00280         }
00281         else if ( keyword == "FoomaticRIPOption" && name == m_option->name()
00282                 && values.size() > 1 )
00283         {
00284             QString type = values[ 0 ];
00285             if ( type == "float" || type == "int" )
00286             {
00287                 DrBase *opt = 0;
00288                 if ( type == "float" )
00289                     opt = new DrFloatOption;
00290                 else
00291                     opt = new DrIntegerOption;
00292                 opt->setName( m_option->name() );
00293                 opt->set( "text", m_option->get( "text" ) );
00294                 opt->set( "default", m_option->get( "default" ) );
00295                 if ( m_option->type() == DrBase::List )
00296                 {
00297                     QStringList vals;
00298                     QPtrListIterator<DrBase> it( *( static_cast<DrListOption*>( m_option )->choices() ) );
00299                     for ( ; it.current(); ++it )
00300                         vals.append( it.current()->name() );
00301                     opt->set( "fixedvals", vals.join( "|" ) );
00302                 }
00303                 delete m_option;
00304                 m_option = opt;
00305             }
00306             // FIXME: support other option types
00307         }
00308         else if ( keyword == "FoomaticRIPOptionRange" && name == m_option->name()
00309                 && values.size() >= 2 && ( m_option->type() == DrBase::Float || m_option->type() == DrBase::Integer ) )
00310         {
00311             m_option->set( "minval", values[ 0 ] );
00312             m_option->set( "maxval", values[ 1 ] );
00313         }
00314     }
00315     else if ( keyword == "Font" && m_groups.size() > 0 )
00316     {
00317         m_fonts << name;
00318     }
00319     return true;
00320 }
00321 
00322 bool PPDLoader::putStatement2( const QString& keyword, const QString& value )
00323 {
00324     if ( !m_option && m_groups.size() == 1 )
00325     {
00326         DrGroup *driver = m_groups.top();
00327         if ( keyword == "NickName" )
00328         {
00329             driver->set( "text", value );
00330             driver->set( "description", value );
00331         }
00332         else if ( keyword == "Manufacturer" )
00333             driver->set( "manufacturer", value );
00334         else if ( keyword == "ShortNickName" )
00335             driver->set( "model", value );
00336         else if ( keyword == "ColorDevice" )
00337             driver->set( "colordevice", value == "True" ? "1" : "0" );
00338     }
00339     return true;
00340 }
00341 
00342 bool PPDLoader::putDefault( const QString& keyword, const QString& value )
00343 {
00344     if ( keyword == "Resolution" && m_groups.size() > 0 )
00345     {
00346         // Store default resolution as it could be fed back
00347         // to the application. And default resolution can
00348         // occur outside a OpenUI/CloseUI pair.
00349         m_groups[ 0 ]->set( "resolution", value );
00350     }
00351 
00352     if ( m_option && m_option->name() == keyword )
00353     {
00354         m_option->set( "default", value );
00355         return true;
00356     }
00357     else
00358         return false;
00359 }
00360 
00361 bool PPDLoader::putConstraint( const QString& opt1, const QString& opt2, const QString& ch1, const QString& ch2 )
00362 {
00363     if ( !m_option && m_groups.size() == 1 )
00364     {
00365         DrMain *driver = static_cast<DrMain*>( m_groups.top() );
00366         driver->addConstraint( new DrConstraint( opt1, opt2, ch1, ch2 ) );
00367     }
00368     return true;
00369 }
00370 
00371 bool PPDLoader::putFooData( const QString& data )
00372 {
00373     if ( !m_option && m_groups.size() == 1 )
00374     {
00375         m_groups.top()->set( "foodata", m_groups.top()->get( "foodata" ) + data + "\n" );
00376     }
00377     return true;
00378 }
00379 
00380 bool PPDLoader::putFooProcessedData( const QVariant& var )
00381 {
00382     QMap<QString,QVariant>::ConstIterator it = var.mapFind( "args_byname" );
00383     if ( it != var.mapEnd() )
00384     {
00385         QVariant opts = it.data();
00386         for ( it = opts.mapBegin(); it != opts.mapEnd(); ++it )
00387         {
00388             QMap<QString,QVariant> opt = it.data().toMap();
00389             QString type = opt[ "type" ].toString();
00390             if ( type == "float" || type == "int" )
00391             {
00392                 DrBase *o;
00393                 if ( type == "float" )
00394                     o = new DrFloatOption;
00395                 else
00396                     o = new DrIntegerOption;
00397                 o->setName( opt[ "name" ].toString() );
00398                 o->set( "text", opt[ "comment" ].toString() );
00399                 o->set( "minval", opt[ "min" ].toString() );
00400                 o->set( "maxval", opt[ "max" ].toString() );
00401                 o->set( "default", opt[ "default" ].toString() );
00402                 o->setValueText( o->get( "default" ) );
00403 
00404                 DrGroup *grp = 0;
00405                 DrBase *old = m_groups.top()->findOption( o->name(), &grp );
00406                 if ( old )
00407                 {
00408                     if ( old->type() == DrBase::List )
00409                     {
00410                         QStringList vals;
00411                         QPtrListIterator<DrBase> it( *( static_cast<DrListOption*>( old )->choices() ) );
00412                         for ( ; it.current(); ++it )
00413                             vals.append( it.current()->name() );
00414                         o->set( "fixedvals", vals.join( "|" ) );
00415                     }
00416                     grp->removeOption( o->name() );
00417                     grp->addOption( o );
00418                 }
00419                 else
00420                 {
00421                     qWarning( "Option %s not found in original PPD file", o->name().latin1() );
00422                     delete o;
00423                 }
00424             }
00425         }
00426     }
00427     return true;
00428 }
00429 
00430 bool PPDLoader::putPaperDimension( const QString& name, const QString& s )
00431 {
00432     QValueList<float> l = splitNumberString( s );
00433 
00434     PS_private *ps = m_ps.find( name );
00435     if ( !ps )
00436     {
00437         ps = new PS_private;
00438         ps->name = name;
00439         m_ps.insert( name, ps );
00440     }
00441     ps->size.width = l[ 0 ];
00442     ps->size.height = l[ 1 ];
00443 
00444     return true;
00445 }
00446 
00447 bool PPDLoader::putImageableArea( const QString& name, const QString& s )
00448 {
00449     QValueList<float> l = splitNumberString( s );
00450 
00451     PS_private *ps = m_ps.find( name );
00452     if ( !ps )
00453     {
00454         ps = new PS_private;
00455         ps->name = name;
00456         m_ps.insert( name, ps );
00457     }
00458     ps->area.left = l[ 0 ];
00459     ps->area.bottom = l[ 1 ];
00460     ps->area.right = l[ 2 ];
00461     ps->area.top = l[ 3 ];
00462 
00463     return true;
00464 }
00465 
00466 DrGroup* PPDLoader::findOrCreateGroupForOption( const QString& optname )
00467 {
00468     QString grpname;
00469     if ( optname == "PageSize" ||
00470             optname == "InputSlot" ||
00471             optname == "ManualFeed" ||
00472             optname == "MediaType" ||
00473             optname == "MediaColor" ||
00474             optname == "MediaWeight" )
00475         grpname = "General";
00476     else if ( optname.startsWith( "stp" ) ||
00477             optname == "Cyan" ||
00478             optname == "Yellow" ||
00479             optname == "Magenta" ||
00480             optname == "Density" ||
00481             optname == "Contrast" )
00482         grpname = "Adjustments";
00483     else if ( optname.startsWith( "JCL" ) )
00484         grpname = "JCL";
00485     else
00486         grpname = "Others";
00487 
00488     DrGroup *grp = 0;
00489     for ( QPtrListIterator<DrGroup> it( m_groups[ 0 ]->groups() ); it.current(); ++it )
00490         if ( it.current()->name() == grpname )
00491         {
00492             grp = it.current();
00493             break;
00494         }
00495     if ( !grp )
00496     {
00497         grp = new DrGroup;
00498         grp->setName( grpname );
00499         grp->set( "text", grpname );
00500         m_groups[ 0 ]->addGroup( grp );
00501     }
00502     return grp;
00503 }
00504 
00505 void PPDLoader::processPageSizes( DrMain *driver )
00506 {
00507     QDictIterator<PS_private> it( m_ps );
00508     for ( ; it.current(); ++it )
00509     {
00510         //qDebug( "ADDING PAGESIZE: %16s, Size = ( %.2f, %.2f ),  Area = ( %.2f, %.2f, %.2f, %.2f )", it.current()->name.latin1(),
00511         //      it.current()->size.width, it.current()->size.height,
00512         //      it.current()->area.left, it.current()->area.bottom,
00513         //      it.current()->area.right, it.current()->area.top );
00514         driver->addPageSize( new DrPageSize( it.current()->name,
00515                     ( int )it.current()->size.width, ( int )it.current()->size.height,
00516                     ( int )it.current()->area.left, ( int )it.current()->area.bottom,
00517                     ( int )ceil( it.current()->size.width - it.current()->area.right ),
00518                     ( int )ceil( it.current()->size.height - it.current()->area.top ) ) );
00519     }
00520     m_ps.clear();
00521 }
00522 
00523 void PPDLoader::setErrorMsg( const QString& msg )
00524 {
00525     m_errormsg = msg;
00526 }
00527 
00528 QString PPDLoader::errorMsg() const
00529 {
00530     return m_errormsg;
00531 }
KDE Home | KDE Accessibility Home | Description of Access Keys