00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #ifndef _GNU_SOURCE
00021 #define _GNU_SOURCE
00022 #endif
00023
00024 #include "ppdloader.h"
00025 #include "foomatic2loader.h"
00026 #include "driver.h"
00027
00028 #include <kfilterdev.h>
00029 #include <kdebug.h>
00030 #include <tdelocale.h>
00031 #include <tdetempfile.h>
00032 #include <tqfile.h>
00033 #include <math.h>
00034 #include <stdlib.h>
00035
00036 void tdeprint_ppdscanner_init( TQIODevice* );
00037 void tdeprint_ppdscanner_terminate( bool deleteIt = true );
00038 int tdeprint_ppdscanner_numberoflines();
00039
00040 static TQString processLocaleString( const TQString& s )
00041 {
00042 TQString res;
00043 uint pos = 0;
00044 while ( pos < s.length() )
00045 {
00046 TQChar c = s[ pos++ ];
00047 if ( c == '<' )
00048 {
00049 bool flag = false;
00050 uint hc = 0;
00051 while ( pos < s.length() )
00052 {
00053 TQChar cc = s[ pos++ ];
00054 uint _hc = 0;
00055 if ( cc == '>' )
00056 break;
00057 else if ( cc.isDigit() )
00058 _hc = cc.digitValue();
00059 else
00060 _hc = cc.lower().latin1() - 'a' + 10;
00061 if ( flag )
00062 {
00063 hc |= _hc;
00064 res.append( TQChar( hc ) );
00065 hc = 0;
00066 }
00067 else
00068 hc = ( _hc << 4 );
00069 flag = !flag;
00070 }
00071 }
00072 else
00073 {
00074 res.append( c );
00075 }
00076 }
00077 return res;
00078 }
00079
00080 static TQValueList<float> splitNumberString( const TQString& _s )
00081 {
00082 TQString s = _s.simplifyWhiteSpace();
00083 TQValueList<float> l;
00084 int p1 = 1, p2 = 0;
00085 while ( true )
00086 {
00087 p2 = s.find( ' ', p1 );
00088 if ( p2 != -1 )
00089 {
00090 l.append( s.mid( p1, p2-p1 ).toFloat() );
00091 p1 = p2+1;
00092 }
00093 else
00094 {
00095
00096 l.append( s.mid( p1, s.length() - p1 - 1 ).toFloat() );
00097 break;
00098 }
00099 }
00100 return l;
00101 }
00102
00103 struct PS_private
00104 {
00105 TQString name;
00106 struct
00107 {
00108 float width, height;
00109 } size;
00110 struct
00111 {
00112 float left, bottom, right, top;
00113 } area;
00114 };
00115
00116 PPDLoader::PPDLoader()
00117 {
00118 m_option = 0;
00119 m_ps.setAutoDelete( true );
00120 }
00121
00122 PPDLoader::~PPDLoader()
00123 {
00124 }
00125
00126 DrMain* PPDLoader::readFromFile( const TQString& filename )
00127 {
00128 bool ppdFilenameIsTempFile = false;
00129 TQString ppdFilename = filename;
00130
00131 if (filename.startsWith("compressed-ppd:")) {
00132 KTempFile tempFile(TQString::null, "ppd", 0600);
00133 tempFile.setAutoDelete(false);
00134 ppdFilename = tempFile.name();
00135
00136 TQStringList filenameParts = TQStringList::split(":", filename);
00137 TQString databaseFilename = TQString::null;
00138 TQString compressedFilename = TQString::null;
00139 int i = 0;
00140 for (TQStringList::Iterator it = filenameParts.begin(); it != filenameParts.end(); ++it) {
00141 if (i == 1) {
00142 databaseFilename = *it;
00143 }
00144 else if (i > 1) {
00145 compressedFilename += *it;
00146 }
00147 i++;
00148 }
00149
00150 TQString command = databaseFilename + " cat " + compressedFilename;
00151
00152 FILE* file = popen(command.ascii(), "r");
00153 if (file) {
00154 char * line = NULL;
00155 size_t len = 0;
00156 ssize_t read;
00157
00158 FILE* tmpFileStream = tempFile.fstream();
00159
00160 while ((read = getline(&line, &len, file)) != -1) {
00161 fputs(line, tmpFileStream);
00162 }
00163 if (line) {
00164 free(line);
00165 }
00166
00167 tempFile.close();
00168 pclose(file);
00169 }
00170 else {
00171 fprintf(stderr, "Can't open driver file : %s\n", compressedFilename.ascii());
00172 return 0;
00173 }
00174
00175 ppdFilenameIsTempFile = true;
00176 }
00177
00178
00179 m_groups.clear();
00180 m_option = NULL;
00181 m_fonts.clear();
00182
00183 TQIODevice *d = KFilterDev::deviceForFile( ppdFilename );
00184 if ( d && d->open( IO_ReadOnly ) )
00185 {
00186 DrMain *driver = new DrMain;
00187 bool result = true;
00188
00189 m_groups.push( driver );
00190 tdeprint_ppdscanner_init( d );
00191 if ( tdeprint_ppdparse( this ) != 0 )
00192 result = false;
00193 tdeprint_ppdscanner_terminate( true );
00194
00195 if ( result )
00196 {
00197 if ( m_groups.size() > 1 )
00198 kdWarning( 500 ) << "PPD syntax error, GROUP specification not correctly closed" << endl;
00199 if ( driver->has( "foodata" ) )
00200 {
00201 Foomatic2Loader loader;
00202 if ( loader.readFromBuffer( driver->get( "foodata" ) ) )
00203 {
00204 driver = loader.modifyDriver( driver );
00205 }
00206 else
00207 kdWarning( 500 ) << "PPD syntax error, Foomatic data read failed" << endl;
00208 }
00209 processPageSizes( driver );
00210 if ( !m_fonts.isEmpty() )
00211 driver->set( "fonts", m_fonts.join( "," ) );
00212 if (ppdFilenameIsTempFile) {
00213 driver->set("temporary-cppd", ppdFilename);
00214 }
00215 return driver;
00216 }
00217 else
00218 kdWarning( 500 ) << "PPD syntax error, PPD parse failed" << endl;
00219 delete driver;
00220 m_ps.clear();
00221 }
00222 else
00223 kdWarning( 500 ) << "PPD read error, unable to open device for file " << ppdFilename << endl;
00224 return 0;
00225 }
00226
00227 DrMain* PPDLoader::loadDriver( const TQString& filename, TQString* msg )
00228 {
00229 PPDLoader loader;
00230 DrMain *driver = loader.readFromFile( filename );
00231 if ( !driver && msg )
00232 *msg = filename + i18n( "(line %1): " ).arg( tdeprint_ppdscanner_numberoflines() ) + loader.errorMsg();
00233 return driver;
00234 }
00235
00236 bool PPDLoader::openUi( const TQString& name, const TQString& desc, const TQString& type )
00237 {
00238 if ( m_option )
00239 {
00240 tqWarning( "PPD syntax error, UI specification not correctly closed" );
00241 endUi( m_option->name() );
00242 }
00243
00244 if ( type == "PickOne" || type == "PickMany" )
00245 m_option = new DrListOption;
00246 else if ( type == "Boolean" )
00247 m_option = new DrBooleanOption;
00248 else
00249 return false;
00250 if ( name[ 0 ] == '*' )
00251 m_option->setName( name.mid( 1 ) );
00252 else
00253 m_option->setName( name );
00254 if ( desc.isEmpty() )
00255 m_option->set( "text", m_option->name() );
00256 else
00257 m_option->set( "text", processLocaleString( desc ) );
00258 return true;
00259 }
00260
00261 bool PPDLoader::endUi( const TQString& name )
00262 {
00263 if ( m_option && ( m_option->name() == name || m_option->name() == name.mid( 1 ) ) )
00264 {
00265 if ( m_option->name() == "PageRegion" )
00266 delete m_option;
00267 else
00268 {
00269 TQString defval = m_option->get( "default" );
00270 DrGroup *grp = 0;
00271 if ( !defval.isEmpty() )
00272 m_option->setValueText( defval );
00273 if ( m_groups.size() == 1 )
00274 {
00275
00276
00277 grp = findOrCreateGroupForOption( m_option->name() );
00278 }
00279 else
00280 grp = m_groups.top();
00281 grp->addOption( m_option );
00282 if ( grp->get( "text" ).contains( "install", false ) )
00283 m_option->set( "fixed", "1" );
00284 }
00285 m_option = 0;
00286 return true;
00287 }
00288 return false;
00289 }
00290
00291 bool PPDLoader::openGroup( const TQString& name, const TQString& desc )
00292 {
00293 DrGroup *grp = new DrGroup;
00294 grp->setName( name );
00295 if ( desc.isEmpty() )
00296 grp->set( "text", name );
00297 else
00298 grp->set( "text", processLocaleString( desc ) );
00299 m_groups.top()->addGroup( grp );
00300 m_groups.push( grp );
00301 return true;
00302 }
00303
00304 bool PPDLoader::endGroup( const TQString& name )
00305 {
00306 if ( m_groups.size() > 1 && m_groups.top()->name() == name )
00307 {
00308 m_groups.pop();
00309 return true;
00310 }
00311 return false;
00312 }
00313
00314 bool PPDLoader::putStatement( const TQString& keyword, const TQString& name, const TQString& desc, const TQStringList& values )
00315 {
00316 if ( m_option )
00317 {
00318 if ( !name.isEmpty() && m_option->name() == keyword )
00319 {
00320 if ( m_option->type() >= DrBase::List )
00321 {
00322 DrBase *ch = new DrBase;
00323 ch->setName( name );
00324 if ( desc.isEmpty() )
00325 ch->set( "text", name );
00326 else
00327 ch->set( "text", processLocaleString( desc ) );
00328 static_cast<DrListOption*>( m_option )->addChoice( ch );
00329 }
00330 else
00331 {
00332 TQString fv = m_option->get( "fixedvals" );
00333 if ( fv.isEmpty() )
00334 fv = name;
00335 else
00336 fv.append( "|" + name );
00337 m_option->set( "fixedvals", fv );
00338 }
00339 }
00340 else if ( keyword == "FoomaticRIPOption" && name == m_option->name()
00341 && values.size() > 1 )
00342 {
00343 TQString type = values[ 0 ];
00344 if ( type == "float" || type == "int" )
00345 {
00346 DrBase *opt = 0;
00347 if ( type == "float" )
00348 opt = new DrFloatOption;
00349 else
00350 opt = new DrIntegerOption;
00351 opt->setName( m_option->name() );
00352 opt->set( "text", m_option->get( "text" ) );
00353 opt->set( "default", m_option->get( "default" ) );
00354 if ( m_option->type() == DrBase::List )
00355 {
00356 TQStringList vals;
00357 TQPtrListIterator<DrBase> it( *( static_cast<DrListOption*>( m_option )->choices() ) );
00358 for ( ; it.current(); ++it )
00359 vals.append( it.current()->name() );
00360 opt->set( "fixedvals", vals.join( "|" ) );
00361 }
00362 delete m_option;
00363 m_option = opt;
00364 }
00365
00366 }
00367 else if ( keyword == "FoomaticRIPOptionRange" && name == m_option->name()
00368 && values.size() >= 2 && ( m_option->type() == DrBase::Float || m_option->type() == DrBase::Integer ) )
00369 {
00370 m_option->set( "minval", values[ 0 ] );
00371 m_option->set( "maxval", values[ 1 ] );
00372 }
00373 }
00374 else if ( keyword == "Font" && m_groups.size() > 0 )
00375 {
00376 m_fonts << name;
00377 }
00378 return true;
00379 }
00380
00381 bool PPDLoader::putStatement2( const TQString& keyword, const TQString& value )
00382 {
00383 if ( !m_option && m_groups.size() == 1 )
00384 {
00385 DrGroup *driver = m_groups.top();
00386 if ( keyword == "NickName" )
00387 {
00388 driver->set( "text", value );
00389 driver->set( "description", value );
00390 }
00391 else if ( keyword == "Manufacturer" )
00392 driver->set( "manufacturer", value );
00393 else if ( keyword == "ShortNickName" )
00394 driver->set( "model", value );
00395 else if ( keyword == "ColorDevice" )
00396 driver->set( "colordevice", value == "True" ? "1" : "0" );
00397 }
00398 return true;
00399 }
00400
00401 bool PPDLoader::putDefault( const TQString& keyword, const TQString& value )
00402 {
00403 if ( keyword == "Resolution" && m_groups.size() > 0 )
00404 {
00405
00406
00407
00408 m_groups[ 0 ]->set( "resolution", value );
00409 }
00410
00411 if ( m_option && m_option->name() == keyword )
00412 {
00413 m_option->set( "default", value );
00414 return true;
00415 }
00416 else
00417 return false;
00418 }
00419
00420 bool PPDLoader::putConstraint( const TQString& opt1, const TQString& opt2, const TQString& ch1, const TQString& ch2 )
00421 {
00422 if ( !m_option && m_groups.size() == 1 )
00423 {
00424 DrMain *driver = static_cast<DrMain*>( m_groups.top() );
00425 driver->addConstraint( new DrConstraint( opt1, opt2, ch1, ch2 ) );
00426 }
00427 return true;
00428 }
00429
00430 bool PPDLoader::putFooData( const TQString& data )
00431 {
00432 if ( !m_option && m_groups.size() == 1 )
00433 {
00434 m_groups.top()->set( "foodata", m_groups.top()->get( "foodata" ) + data + "\n" );
00435 }
00436 return true;
00437 }
00438
00439 bool PPDLoader::putFooProcessedData( const TQVariant& var )
00440 {
00441 TQMap<TQString,TQVariant>::ConstIterator it = var.mapFind( "args_byname" );
00442 if ( it != var.mapEnd() )
00443 {
00444 TQVariant opts = it.data();
00445 for ( it = opts.mapBegin(); it != opts.mapEnd(); ++it )
00446 {
00447 TQMap<TQString,TQVariant> opt = it.data().toMap();
00448 TQString type = opt[ "type" ].toString();
00449 if ( type == "float" || type == "int" )
00450 {
00451 DrBase *o;
00452 if ( type == "float" )
00453 o = new DrFloatOption;
00454 else
00455 o = new DrIntegerOption;
00456 o->setName( opt[ "name" ].toString() );
00457 o->set( "text", opt[ "comment" ].toString() );
00458 o->set( "minval", opt[ "min" ].toString() );
00459 o->set( "maxval", opt[ "max" ].toString() );
00460 o->set( "default", opt[ "default" ].toString() );
00461 o->setValueText( o->get( "default" ) );
00462
00463 DrGroup *grp = 0;
00464 DrBase *old = m_groups.top()->findOption( o->name(), &grp );
00465 if ( old )
00466 {
00467 if ( old->type() == DrBase::List )
00468 {
00469 TQStringList vals;
00470 TQPtrListIterator<DrBase> it( *( static_cast<DrListOption*>( old )->choices() ) );
00471 for ( ; it.current(); ++it )
00472 vals.append( it.current()->name() );
00473 o->set( "fixedvals", vals.join( "|" ) );
00474 }
00475 grp->removeOption( o->name() );
00476 grp->addOption( o );
00477 }
00478 else
00479 {
00480 tqWarning( "Option %s not found in original PPD file", o->name().latin1() );
00481 delete o;
00482 }
00483 }
00484 }
00485 }
00486 return true;
00487 }
00488
00489 bool PPDLoader::putPaperDimension( const TQString& name, const TQString& s )
00490 {
00491 TQValueList<float> l = splitNumberString( s );
00492
00493 PS_private *ps = m_ps.find( name );
00494 if ( !ps )
00495 {
00496 ps = new PS_private;
00497 ps->name = name;
00498 m_ps.insert( name, ps );
00499 }
00500 ps->size.width = l[ 0 ];
00501 ps->size.height = l[ 1 ];
00502
00503 return true;
00504 }
00505
00506 bool PPDLoader::putImageableArea( const TQString& name, const TQString& s )
00507 {
00508 TQValueList<float> l = splitNumberString( s );
00509
00510 PS_private *ps = m_ps.find( name );
00511 if ( !ps )
00512 {
00513 ps = new PS_private;
00514 ps->name = name;
00515 m_ps.insert( name, ps );
00516 }
00517 ps->area.left = l[ 0 ];
00518 ps->area.bottom = l[ 1 ];
00519 ps->area.right = l[ 2 ];
00520 ps->area.top = l[ 3 ];
00521
00522 return true;
00523 }
00524
00525 DrGroup* PPDLoader::findOrCreateGroupForOption( const TQString& optname )
00526 {
00527 TQString grpname;
00528 if ( optname == "PageSize" ||
00529 optname == "InputSlot" ||
00530 optname == "ManualFeed" ||
00531 optname == "MediaType" ||
00532 optname == "MediaColor" ||
00533 optname == "MediaWeight" )
00534 grpname = "General";
00535 else if ( optname.startsWith( "stp" ) ||
00536 optname == "Cyan" ||
00537 optname == "Yellow" ||
00538 optname == "Magenta" ||
00539 optname == "Density" ||
00540 optname == "Contrast" )
00541 grpname = "Adjustments";
00542 else if ( optname.startsWith( "JCL" ) )
00543 grpname = "JCL";
00544 else
00545 grpname = "Others";
00546
00547 DrGroup *grp = 0;
00548 for ( TQPtrListIterator<DrGroup> it( m_groups[ 0 ]->groups() ); it.current(); ++it )
00549 if ( it.current()->name() == grpname )
00550 {
00551 grp = it.current();
00552 break;
00553 }
00554 if ( !grp )
00555 {
00556 grp = new DrGroup;
00557 grp->setName( grpname );
00558 grp->set( "text", grpname );
00559 m_groups[ 0 ]->addGroup( grp );
00560 }
00561 return grp;
00562 }
00563
00564 void PPDLoader::processPageSizes( DrMain *driver )
00565 {
00566 TQDictIterator<PS_private> it( m_ps );
00567 for ( ; it.current(); ++it )
00568 {
00569
00570
00571
00572
00573 driver->addPageSize( new DrPageSize( it.current()->name,
00574 ( int )it.current()->size.width, ( int )it.current()->size.height,
00575 ( int )it.current()->area.left, ( int )it.current()->area.bottom,
00576 ( int )ceil( it.current()->size.width - it.current()->area.right ),
00577 ( int )ceil( it.current()->size.height - it.current()->area.top ) ) );
00578 }
00579 m_ps.clear();
00580 }
00581
00582 void PPDLoader::setErrorMsg( const TQString& msg )
00583 {
00584 m_errormsg = msg;
00585 }
00586
00587 TQString PPDLoader::errorMsg() const
00588 {
00589 return m_errormsg;
00590 }