00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
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
00123 m_groups.clear();
00124 m_option = NULL;
00125 m_fonts.clear();
00126
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
00217
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
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
00347
00348
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
00511
00512
00513
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 }