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