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 #ifndef _GNU_SOURCE 00021 #define _GNU_SOURCE /* Needed for getline */ 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 <klocale.h> 00031 #include <ktempfile.h> 00032 #include <tqfile.h> 00033 #include <math.h> 00034 #include <stdlib.h> 00035 00036 void kdeprint_ppdscanner_init( TQIODevice* ); 00037 void kdeprint_ppdscanner_terminate( bool deleteIt = true ); 00038 int kdeprint_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 // ignore the final quote 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 // Initialization 00179 m_groups.clear(); 00180 m_option = NULL; 00181 m_fonts.clear(); 00182 // Open driver file 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 kdeprint_ppdscanner_init( d ); 00191 if ( kdeprint_ppdparse( this ) != 0 ) 00192 result = false; 00193 kdeprint_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( kdeprint_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 qWarning( "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 // we don't have any group defined, create the 00276 // most adapted one. 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 // FIXME: support other option types 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 // Store default resolution as it could be fed back 00406 // to the application. And default resolution can 00407 // occur outside a OpenUI/CloseUI pair. 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 qWarning( "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 //qDebug( "ADDING PAGESIZE: %16s, Size = ( %.2f, %.2f ), Area = ( %.2f, %.2f, %.2f, %.2f )", it.current()->name.latin1(), 00570 // it.current()->size.width, it.current()->size.height, 00571 // it.current()->area.left, it.current()->area.bottom, 00572 // it.current()->area.right, it.current()->area.top ); 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 }