Back to index

system-config-printer  1.3.9+20120706
Public Member Functions | Public Attributes | Static Public Attributes | Private Member Functions | Static Private Attributes
cupshelpers.ppds.PPDs Class Reference

List of all members.

Public Member Functions

def __init__
def getMakes
def getModels
def getInfoFromModel
def getInfoFromPPDName
def getStatusFromFit
def orderPPDNamesByPreference
def getPPDNamesFromDeviceID
def getPPDNameFromDeviceID

Public Attributes

 ppds
 makes
 ids
 drivertypes
 preforder
 lmakes
 lmodels

Static Public Attributes

int STATUS_SUCCESS = 0
int STATUS_MODEL_MISMATCH = 1
int STATUS_GENERIC_DRIVER = 2
int STATUS_NO_DRIVER = 3
 FIT_EXACT_CMD = xmldriverprefs.DriverType.FIT_EXACT_CMD
 FIT_EXACT = xmldriverprefs.DriverType.FIT_EXACT
 FIT_CLOSE = xmldriverprefs.DriverType.FIT_CLOSE
 FIT_GENERIC = xmldriverprefs.DriverType.FIT_GENERIC
 FIT_NONE = xmldriverprefs.DriverType.FIT_NONE

Private Member Functions

def _findBestMatchPPDs
def _getPPDNameFromCommandSet
def _init_makes
def _init_ids

Static Private Attributes

dictionary _fit_to_status

Detailed Description

This class is for handling the list of PPDs returned by CUPS.  It
indexes by PPD name and device ID, filters by natural language so
that foreign-language PPDs are not included, and sorts by driver
type.  If an exactly-matching PPD is not available, it can
substitute with a PPD for a similar model or for a generic driver.

Definition at line 297 of file ppds.py.


Constructor & Destructor Documentation

def cupshelpers.ppds.PPDs.__init__ (   self,
  ppds,
  language = None,
  xml_dir = None 
)
@type ppds: dict
@param ppds: dict of PPDs as returned by cups.Connection.getPPDs()
or cups.Connection.getPPDs2()

@type language: string
       @param language: language name, as given by the first element
of the pair returned by locale.getlocale()

Definition at line 324 of file ppds.py.

00324 
00325     def __init__ (self, ppds, language=None, xml_dir=None):
00326         """
00327         @type ppds: dict
00328         @param ppds: dict of PPDs as returned by cups.Connection.getPPDs()
00329         or cups.Connection.getPPDs2()
00330 
00331         @type language: string
00332        @param language: language name, as given by the first element
00333         of the pair returned by locale.getlocale()
00334         """
00335         self.ppds = ppds.copy ()
00336         self.makes = None
00337         self.ids = None
00338 
00339         self.drivertypes = xmldriverprefs.DriverTypes ()
00340         self.preforder = xmldriverprefs.PreferenceOrder ()
00341         if xml_dir == None:
00342             xml_dir = os.environ.get ("CUPSHELPERS_XMLDIR")
00343             if xml_dir == None:
00344                 import config
00345                 xml_dir = os.path.join (config.sysconfdir, "cupshelpers")
00346 
00347         try:
00348             xmlfile = os.path.join (xml_dir, "preferreddrivers.xml")
00349             (drivertypes, preferenceorder) = \
00350                 xmldriverprefs.PreferredDrivers (xmlfile)
00351             self.drivertypes.load (drivertypes)
00352             self.preforder.load (preferenceorder)
00353         except Exception, e:
00354             print "Error loading %s: %s" % (xmlfile, e)
00355             self.drivertypes = None
00356             self.preforder = None
00357 
00358         if (language == None or
00359             language == "C" or
00360             language == "POSIX"):
00361             language = "en_US"
00362 
00363         u = language.find ("_")
00364         if u != -1:
00365             short_language = language[:u]
00366         else:
00367             short_language = language
00368 
00369         to_remove = []
00370         for ppdname, ppddict in self.ppds.iteritems ():
00371             try:
00372                 natural_language = _singleton (ppddict['ppd-natural-language'])
00373             except KeyError:
00374                 continue
00375 
00376             if natural_language == "en":
00377                 # Some manufacturer's PPDs are only available in this
00378                 # language, so always let them though.
00379                 continue
00380 
00381             if natural_language == language:
00382                 continue
00383 
00384             if natural_language == short_language:
00385                 continue
00386 
00387             to_remove.append (ppdname)
00388 
00389         for ppdname in to_remove:
00390             del self.ppds[ppdname]
00391 
00392         # CUPS sets the 'raw' model's ppd-make-and-model to 'Raw Queue'
00393         # which unfortunately then appears as manufacturer Raw and
00394         # model Queue.  Use 'Generic' for this model.
00395         if self.ppds.has_key ('raw'):
00396             makemodel = _singleton (self.ppds['raw']['ppd-make-and-model'])
00397             if not makemodel.startswith ("Generic "):
00398                 self.ppds['raw']['ppd-make-and-model'] = "Generic " + makemodel


Member Function Documentation

def cupshelpers.ppds.PPDs._findBestMatchPPDs (   self,
  mdls,
  mdl 
) [private]
Find the best-matching PPDs based on the MDL Device ID.
This function could be made a lot smarter.

Definition at line 814 of file ppds.py.

00814 
00815     def _findBestMatchPPDs (self, mdls, mdl):
00816         """
00817         Find the best-matching PPDs based on the MDL Device ID.
00818         This function could be made a lot smarter.
00819         """
00820 
00821         _debugprint ("Trying best match")
00822         mdll = mdl.lower ()
00823         if mdll.endswith (" series"):
00824             # Strip " series" from the end of the MDL field.
00825             mdll = mdll[:-7]
00826             mdl = mdl[:-7]
00827         best_mdl = None
00828         best_matchlen = 0
00829         mdlnames = mdls.keys ()
00830 
00831         # Perform a case-insensitive model sort on the names.
00832         mdlnamesl = map (lambda x: (x, x.lower()), mdlnames)
00833         mdlnamesl.append ((mdl, mdll))
00834         mdlnamesl.sort (lambda x, y: cups.modelSort(x[1], y[1]))
00835         i = mdlnamesl.index ((mdl, mdll))
00836         candidates = [mdlnamesl[i - 1]]
00837         if i + 1 < len (mdlnamesl):
00838             candidates.append (mdlnamesl[i + 1])
00839             _debugprint (candidates[0][0] + " <= " + mdl + " <= " +
00840                         candidates[1][0])
00841         else:
00842             _debugprint (candidates[0][0] + " <= " + mdl)
00843 
00844         # Look at the models immediately before and after ours in the
00845         # sorted list, and pick the one with the longest initial match.
00846         for (candidate, candidatel) in candidates:
00847             prefix = os.path.commonprefix ([candidatel, mdll])
00848             if len (prefix) > best_matchlen:
00849                 best_mdl = mdls[candidate].keys ()
00850                 best_matchlen = len (prefix)
00851                 _debugprint ("%s: match length %d" % (candidate, best_matchlen))
00852 
00853         # Did we match more than half of the model name?
00854         if best_mdl and best_matchlen > (len (mdll) / 2):
00855             ppdnamelist = best_mdl
00856             if best_matchlen == len (mdll):
00857                 fit = self.FIT_EXACT
00858             else:
00859                 fit = self.FIT_CLOSE
00860         else:
00861             fit = self.FIT_NONE
00862             ppdnamelist = None
00863 
00864             # Last resort.  Find the "most important" word in the MDL
00865             # field and look for a match based solely on that.  If
00866             # there are digits, try lowering the number of
00867             # significant figures.
00868             mdlnames.sort (cups.modelSort)
00869             mdlitems = map (lambda x: (x.lower (), mdls[x]), mdlnames)
00870             modelid = None
00871             for word in mdll.split (' '):
00872                 if modelid == None:
00873                     modelid = word
00874 
00875                 have_digits = False
00876                 for i in range (len (word)):
00877                     if word[i].isdigit ():
00878                         have_digits = True
00879                         break
00880 
00881                 if have_digits:
00882                     modelid = word
00883                     break
00884 
00885             digits = 0
00886             digits_start = -1
00887             digits_end = -1
00888             for i in range (len (modelid)):
00889                 if modelid[i].isdigit ():
00890                     if digits_start == -1:
00891                         digits_start = i
00892                     digits_end = i
00893                     digits += 1
00894                 elif digits_start != -1:
00895                     break
00896             digits_end += 1
00897             modelnumber = 0
00898             if digits > 0:
00899                 modelnumber = int (modelid[digits_start:digits_end])
00900                 modelpattern = (modelid[:digits_start] + "%d" +
00901                                 modelid[digits_end:])
00902                 _debugprint ("Searching for model ID '%s', '%s' %% %d" %
00903                              (modelid, modelpattern, modelnumber))
00904                 ignore_digits = 0
00905                 best_mdl = None
00906                 found = False
00907                 while ignore_digits < digits:
00908                     div = pow (10, ignore_digits)
00909                     modelid = modelpattern % ((modelnumber / div) * div)
00910                     _debugprint ("Ignoring %d of %d digits, trying %s" %
00911                                  (ignore_digits, digits, modelid))
00912 
00913                     for (name, ppds) in mdlitems:
00914                         for word in name.split (' '):
00915                             if word.lower () == modelid:
00916                                 found = True
00917                                 break
00918 
00919                         if found:
00920                             best_mdl = ppds.keys ()
00921                             break
00922 
00923                     if found:
00924                         break
00925 
00926                     ignore_digits += 1
00927                     if digits < 2:
00928                         break
00929 
00930                 if found:
00931                     ppdnamelist = best_mdl
00932                     fit = self.FIT_CLOSE
00933 
00934         return (fit, ppdnamelist)

Here is the caller graph for this function:

def cupshelpers.ppds.PPDs._getPPDNameFromCommandSet (   self,
  commandsets = [] 
) [private]
Return ppd-name list or None, given a list of strings representing
the command sets supported.

Definition at line 935 of file ppds.py.

00935 
00936     def _getPPDNameFromCommandSet (self, commandsets=[]):
00937         """Return ppd-name list or None, given a list of strings representing
00938         the command sets supported."""
00939         try:
00940             self._init_makes ()
00941             models = self.makes["Generic"]
00942         except KeyError:
00943             return None
00944 
00945         def get (*candidates):
00946             for model in candidates:
00947                 (s, ppds) = self._findBestMatchPPDs (models, model)
00948                 if s == self.FIT_EXACT:
00949                     return ppds
00950             return None
00951 
00952         cmdsets = map (lambda x: x.lower (), commandsets)
00953         if (("postscript" in cmdsets) or ("postscript2" in cmdsets) or
00954             ("postscript level 2 emulation" in cmdsets)):
00955             return get ("PostScript")
00956         elif (("pclxl" in cmdsets) or ("pcl-xl" in cmdsets) or
00957               ("pcl6" in cmdsets) or ("pcl 6 emulation" in cmdsets)):
00958             return get ("PCL 6/PCL XL", "PCL Laser")
00959         elif "pcl5e" in cmdsets:
00960             return get ("PCL 5e", "PCL Laser")
00961         elif "pcl5c" in cmdsets:
00962             return get ("PCL 5c", "PCL Laser")
00963         elif ("pcl5" in cmdsets) or ("pcl 5 emulation" in cmdsets):
00964             return get ("PCL 5", "PCL Laser")
00965         elif "pcl" in cmdsets:
00966             return get ("PCL 3", "PCL Laser")
00967         elif (("escpl2" in cmdsets) or ("esc/p2" in cmdsets) or
00968               ("escp2e" in cmdsets)):
00969             return get ("ESC/P Dot Matrix")
00970         return None

Here is the call graph for this function:

Here is the caller graph for this function:

def cupshelpers.ppds.PPDs._init_ids (   self) [private]

Definition at line 1082 of file ppds.py.

01082 
01083     def _init_ids (self):
01084         if self.ids:
01085             return
01086 
01087         ids = {}
01088         for ppdname, ppddict in self.ppds.iteritems ():
01089             id = _singleton (ppddict.get ('ppd-device-id'))
01090             if not id:
01091                 continue
01092 
01093             id_dict = parseDeviceID (id)
01094             lmfg = id_dict['MFG'].lower ()
01095             lmdl = id_dict['MDL'].lower ()
01096 
01097             bad = False
01098             if len (lmfg) == 0:
01099                 bad = True
01100             if len (lmdl) == 0:
01101                 bad = True
01102             if bad:
01103                 continue
01104 
01105             if not ids.has_key (lmfg):
01106                 ids[lmfg] = {}
01107 
01108             if not ids[lmfg].has_key (lmdl):
01109                 ids[lmfg][lmdl] = []
01110 
01111             ids[lmfg][lmdl].append (ppdname)
01112 
01113         self.ids = ids

Here is the caller graph for this function:

def cupshelpers.ppds.PPDs._init_makes (   self) [private]

Definition at line 971 of file ppds.py.

00971 
00972     def _init_makes (self):
00973         if self.makes:
00974             return
00975 
00976         tstart = time.time ()
00977         makes = {}
00978         lmakes = {}
00979         lmodels = {}
00980         aliases = {} # Generic model name: set(specific model names)
00981         for ppdname, ppddict in self.ppds.iteritems ():
00982             # One entry for ppd-make-and-model
00983             ppd_make_and_model = _singleton (ppddict['ppd-make-and-model'])
00984             ppd_mm_split = ppdMakeModelSplit (ppd_make_and_model)
00985             ppd_makes_and_models = set([ppd_mm_split])
00986 
00987             # The ppd-product IPP attribute contains values from each
00988             # Product PPD attribute as well as the value from the
00989             # ModelName attribute if present.  The Product attribute
00990             # values are surrounded by parentheses; the ModelName
00991             # attribute value is not.
00992 
00993             # Add another entry for each ppd-product that came from a
00994             # Product attribute in the PPD file.
00995             ppd_products = ppddict.get ('ppd-product', [])
00996             if not isinstance (ppd_products, list):
00997                 ppd_products = [ppd_products]
00998             ppd_products = set (filter (lambda x: x.startswith ("("),
00999                                         ppd_products))
01000             if ppd_products:
01001                 # If there is only one ppd-product value it is
01002                 # unlikely to be useful.
01003                 if len (ppd_products) == 1:
01004                     ppd_products = set()
01005 
01006                 make = _singleton (ppddict.get ('ppd-make', '')).rstrip ()
01007                 if make:
01008                     make += ' '
01009                 lmake = normalize (make)
01010                 for ppd_product in ppd_products:
01011                     # *Product: attribute is "(text)"
01012                     if (ppd_product.startswith ("(") and
01013                         ppd_product.endswith (")")):
01014                         ppd_product = ppd_product[1:len (ppd_product) - 1]
01015 
01016                     if not ppd_product:
01017                         continue
01018 
01019                     # If manufacturer name missing, take it from ppd-make
01020                     lprod = normalize (ppd_product)
01021                     if not lprod.startswith (lmake):
01022                         ppd_product = make + ppd_product
01023 
01024                     ppd_makes_and_models.add (ppdMakeModelSplit (ppd_product))
01025 
01026             # Add the entries to our dictionary
01027             for make, model in ppd_makes_and_models:
01028                 lmake = normalize (make)
01029                 lmodel = normalize (model)
01030                 if not lmakes.has_key (lmake):
01031                     lmakes[lmake] = make
01032                     lmodels[lmake] = {}
01033                     makes[make] = {}
01034                 else:
01035                     make = lmakes[lmake]
01036 
01037                 if not lmodels[lmake].has_key (lmodel):
01038                     lmodels[lmake][lmodel] = model
01039                     makes[make][model] = {}
01040                 else:
01041                     model = lmodels[lmake][lmodel]
01042 
01043                 makes[make][model][ppdname] = ppddict
01044 
01045             # Build list of model aliases
01046             if ppd_mm_split in ppd_makes_and_models:
01047                 ppd_makes_and_models.remove (ppd_mm_split)
01048 
01049             if ppd_makes_and_models:
01050                 (make, model) = ppd_mm_split
01051                 if aliases.has_key (make):
01052                     models = aliases[make].get (model, set())
01053                 else:
01054                     aliases[make] = {}
01055                     models = set()
01056 
01057                 models = models.union (map (lambda x: x[1],
01058                                             ppd_makes_and_models))
01059                 aliases[make][model] = models
01060 
01061         # Now, for each set of model aliases, add all drivers from the
01062         # "main" (generic) model name to each of the specific models.
01063         for make, models in aliases.iteritems ():
01064             lmake = normalize (make)
01065             main_make = lmakes[lmake]
01066             for model, modelnames in models.iteritems ():
01067                 main_model = lmodels[lmake].get (normalize (model))
01068                 if not main_model:
01069                     continue
01070 
01071                 main_ppds = makes[main_make][main_model]
01072 
01073                 for eachmodel in modelnames:
01074                     this_model = lmodels[lmake].get (normalize (eachmodel))
01075                     ppds = makes[main_make][this_model]
01076                     ppds.update (main_ppds)
01077 
01078         self.makes = makes
01079         self.lmakes = lmakes
01080         self.lmodels = lmodels
01081         _debugprint ("init_makes: %.3fs" % (time.time () - tstart))

Here is the caller graph for this function:

def cupshelpers.ppds.PPDs.getInfoFromModel (   self,
  make,
  model 
)
       Obtain a list of PPDs that are suitable for use with a
particular printer model, given its make and model name.

       @returns: a dict, indexed by ppd-name, of dicts representing
PPDs (as given by cups.Connection.getPPDs)

Definition at line 428 of file ppds.py.

00428 
00429     def getInfoFromModel (self, make, model):
00430         """
00431        Obtain a list of PPDs that are suitable for use with a
00432         particular printer model, given its make and model name.
00433 
00434        @returns: a dict, indexed by ppd-name, of dicts representing
00435         PPDs (as given by cups.Connection.getPPDs)
00436        """
00437         self._init_makes ()
00438         try:
00439             return self.makes[make][model]
00440         except KeyError:
00441             return {}

Here is the call graph for this function:

def cupshelpers.ppds.PPDs.getInfoFromPPDName (   self,
  ppdname 
)
       @returns: a dict representing a PPD, as given by
       cups.Connection.getPPDs

Definition at line 442 of file ppds.py.

00442 
00443     def getInfoFromPPDName (self, ppdname):
00444         """
00445        @returns: a dict representing a PPD, as given by
00446        cups.Connection.getPPDs
00447        """
00448         return self.ppds[ppdname]

       @returns: a list of strings representing makes, sorted according
to the current locale

Definition at line 399 of file ppds.py.

00399 
00400     def getMakes (self):
00401         """
00402        @returns: a list of strings representing makes, sorted according
00403         to the current locale
00404        """
00405         self._init_makes ()
00406         makes_list = self.makes.keys ()
00407         makes_list.sort (locale.strcoll)
00408         try:
00409             # "Generic" should be listed first.
00410             makes_list.remove ("Generic")
00411             makes_list.insert (0, "Generic")
00412         except ValueError:
00413             pass
00414         return makes_list

Here is the call graph for this function:

def cupshelpers.ppds.PPDs.getModels (   self,
  make 
)
       @returns: a list of strings representing models, sorted using
       cups.modelSort()

Definition at line 415 of file ppds.py.

00415 
00416     def getModels (self, make):
00417         """
00418        @returns: a list of strings representing models, sorted using
00419        cups.modelSort()
00420        """
00421         self._init_makes ()
00422         try:
00423             models_list = self.makes[make].keys ()
00424         except KeyError:
00425             return []
00426         models_list.sort (key=lambda x: x.lower(), cmp=cups.modelSort)
00427         return models_list

Here is the call graph for this function:

def cupshelpers.ppds.PPDs.getPPDNameFromDeviceID (   self,
  mfg,
  mdl,
  description = "",
  commandsets = [],
  uri = None,
  downloadedfiles = [],
  make_and_model = None 
)
       Obtain a best-effort PPD match for an IEEE 1284 Device ID.
       The status is one of:

 - L{STATUS_SUCCESS}: the match was successful, and an exact
    match was found

 - L{STATUS_MODEL_MISMATCH}: a similar match was found, but
    the model name does not exactly match

 - L{STATUS_GENERIC_DRIVER}: no match was found, but a
    generic driver is available that can drive this device
    according to its command set list

 - L{STATUS_NO_DRIVER}: no match was found at all, and the
    returned PPD name is a last resort

       @param mfg: MFG or MANUFACTURER field
       @type mfg: string
       @param mdl: MDL or MODEL field
       @type mdl: string
       @param description: DES or DESCRIPTION field, optional
       @type description: string
       @param commandsets: CMD or COMMANDSET field, optional
       @type commandsets: string
       @param uri: device URI, optional (only needed for debugging)
       @type uri: string
@param downloadedfiles: filenames from downloaded packages
@type downloadedfiles: string list
@param make_and_model: device-make-and-model string
@type make_and_model: string
       @returns: an integer,string pair of (status,ppd-name)

Definition at line 758 of file ppds.py.

00758 
00759                                 make_and_model=None):
00760         """
00761        Obtain a best-effort PPD match for an IEEE 1284 Device ID.
00762        The status is one of:
00763 
00764          - L{STATUS_SUCCESS}: the match was successful, and an exact
00765             match was found
00766 
00767          - L{STATUS_MODEL_MISMATCH}: a similar match was found, but
00768             the model name does not exactly match
00769 
00770          - L{STATUS_GENERIC_DRIVER}: no match was found, but a
00771             generic driver is available that can drive this device
00772             according to its command set list
00773 
00774          - L{STATUS_NO_DRIVER}: no match was found at all, and the
00775             returned PPD name is a last resort
00776 
00777        @param mfg: MFG or MANUFACTURER field
00778        @type mfg: string
00779        @param mdl: MDL or MODEL field
00780        @type mdl: string
00781        @param description: DES or DESCRIPTION field, optional
00782        @type description: string
00783        @param commandsets: CMD or COMMANDSET field, optional
00784        @type commandsets: string
00785        @param uri: device URI, optional (only needed for debugging)
00786        @type uri: string
00787         @param downloadedfiles: filenames from downloaded packages
00788         @type downloadedfiles: string list
00789         @param make_and_model: device-make-and-model string
00790         @type make_and_model: string
00791        @returns: an integer,string pair of (status,ppd-name)
00792        """
00793 
00794         fit = self.getPPDNamesFromDeviceID (mfg, mdl, description,
00795                                             commandsets, uri,
00796                                             make_and_model)
00797 
00798         # We've got a set of PPDs, any of which will drive the device.
00799         # Now we have to choose the "best" one.  This is quite tricky
00800         # to decide, so let's sort them in order of preference and
00801         # take the first.
00802         devid = { "MFG": mfg, "MDL": mdl,
00803                   "DES": description,
00804                   "CMD": commandsets }
00805         ppdnamelist = self.orderPPDNamesByPreference (fit.keys (),
00806                                                       downloadedfiles,
00807                                                       make_and_model,
00808                                                       devid, fit)
00809         _debugprint ("Found PPDs: %s" % str (ppdnamelist))
00810 
00811         status = self.getStatusFromFit (fit[ppdnamelist[0]])
00812         print "Using %s (status: %d)" % (ppdnamelist[0], status)
00813         return (status, ppdnamelist[0])

Here is the call graph for this function:

Here is the caller graph for this function:

def cupshelpers.ppds.PPDs.getPPDNamesFromDeviceID (   self,
  mfg,
  mdl,
  description = "",
  commandsets = [],
  uri = None,
  make_and_model = None 
)
       Obtain a best-effort PPD match for an IEEE 1284 Device ID.

       @param mfg: MFG or MANUFACTURER field
       @type mfg: string
       @param mdl: MDL or MODEL field
       @type mdl: string
       @param description: DES or DESCRIPTION field, optional
       @type description: string
       @param commandsets: CMD or COMMANDSET field, optional
       @type commandsets: string
       @param uri: device URI, optional (only needed for debugging)
       @type uri: string
@param make_and_model: device-make-and-model string
@type make_and_model: string
       @returns: a dict of fit (string) indexed by PPD name

Definition at line 515 of file ppds.py.

00515 
00516                                  make_and_model=None):
00517         """
00518        Obtain a best-effort PPD match for an IEEE 1284 Device ID.
00519 
00520        @param mfg: MFG or MANUFACTURER field
00521        @type mfg: string
00522        @param mdl: MDL or MODEL field
00523        @type mdl: string
00524        @param description: DES or DESCRIPTION field, optional
00525        @type description: string
00526        @param commandsets: CMD or COMMANDSET field, optional
00527        @type commandsets: string
00528        @param uri: device URI, optional (only needed for debugging)
00529        @type uri: string
00530         @param make_and_model: device-make-and-model string
00531         @type make_and_model: string
00532        @returns: a dict of fit (string) indexed by PPD name
00533        """
00534         _debugprint ("\n%s %s" % (mfg, mdl))
00535         orig_mfg = mfg
00536         orig_mdl = mdl
00537         self._init_ids ()
00538 
00539         # Start with an empty result list and build it up using
00540         # several search methods, in increasing order of fuzziness.
00541         fit = {}
00542 
00543         # First, try looking up the device using the manufacturer and
00544         # model fields from the Device ID exactly as they appear (but
00545         # case-insensitively).
00546         mfgl = mfg.lower ()
00547         mdll = mdl.lower ()
00548 
00549         id_matched = False
00550         try:
00551             for each in self.ids[mfgl][mdll]:
00552                 fit[each] = self.FIT_EXACT
00553             id_matched = True
00554         except KeyError:
00555             pass
00556 
00557         # The HP PPDs say "HP" not "Hewlett-Packard", so try that.
00558         if mfgl == "hewlett-packard":
00559             try:
00560                 for each in self.ids["hp"][mdll]:
00561                     fit[each] = self.FIT_EXACT
00562                 print ("**** Incorrect IEEE 1284 Device ID: %s" %
00563                        self.ids["hp"][mdll])
00564                 print "**** Actual ID is MFG:%s;MDL:%s;" % (mfg, mdl)
00565                 print "**** Please report a bug against the HPLIP component"
00566                 id_matched = True
00567             except KeyError:
00568                 pass
00569 
00570         # Now try looking up the device by ppd-make-and-model.
00571         _debugprint ("Trying make/model names")
00572         mdls = None
00573         self._init_makes ()
00574         make = None
00575         if mfgl == "":
00576             (mfg, mdl) = ppdMakeModelSplit (mdl)
00577             mfgl = normalize (mfg)
00578             mdll = normalize (mdl)
00579 
00580         _debugprint ("mfgl: %s" % mfgl)
00581         _debugprint ("mdll: %s" % mdll)
00582         mfgrepl = {"hewlett-packard": "hp",
00583                    "lexmark international": "lexmark",
00584                    "kyocera": "kyocera mita"}
00585         if self.lmakes.has_key (mfgl):
00586             # Found manufacturer.
00587             make = self.lmakes[mfgl]
00588         elif mfgrepl.has_key (mfgl):
00589             rmfg = mfgrepl[mfgl]
00590             if self.lmakes.has_key (rmfg):
00591                 mfg = rmfg
00592                 mfgl = mfg
00593                 # Found manufacturer (after mapping to canonical name)
00594                 _debugprint ("remapped mfgl: %s" % mfgl)
00595                 make = self.lmakes[mfgl]
00596 
00597         _debugprint ("make: %s" % make)
00598         if make != None:
00599             mdls = self.makes[make]
00600             mdlsl = self.lmodels[normalize(make)]
00601 
00602             # Remove manufacturer name from model field
00603             for prefix in [mfgl, 'hewlett-packard', 'hp']:
00604                 if mdll.startswith (prefix + ' '):
00605                     mdl = mdl[len (prefix) + 1:]
00606                     mdll = normalize (mdl)
00607                     _debugprint ("unprefixed mdll: %s" % mdll)
00608 
00609             if self.lmodels[mfgl].has_key (mdll):
00610                 model = mdlsl[mdll]
00611                 for each in mdls[model].keys ():
00612                     fit[each] = self.FIT_EXACT
00613                     _debugprint ("%s: %s" % (fit[each], each))
00614             else:
00615                 # Make use of the model name clean-up in the
00616                 # ppdMakeModelSplit () function
00617                 (mfg2, mdl2) = ppdMakeModelSplit (mfg + " " + mdl)
00618                 mdl2l = normalize (mdl2)
00619                 _debugprint ("re-split mdll: %s" % mdl2l)
00620                 if self.lmodels[mfgl].has_key (mdl2l):
00621                     model = mdlsl[mdl2l]
00622                     for each in mdls[model].keys ():
00623                         fit[each] = self.FIT_EXACT
00624                         _debugprint ("%s: %s" % (fit[each], each))
00625       
00626         if not fit and mdls:
00627             (s, ppds) = self._findBestMatchPPDs (mdls, mdl)
00628             if s != self.FIT_NONE:
00629                 for each in ppds:
00630                     fit[each] = s
00631                     _debugprint ("%s: %s" % (fit[each], each))
00632 
00633         if commandsets:
00634             if type (commandsets) != list:
00635                 commandsets = commandsets.split (',')
00636 
00637             _debugprint ("Checking CMD field")
00638             generic = self._getPPDNameFromCommandSet (commandsets)
00639             if generic:
00640                 for driver in generic:
00641                     fit[driver] = self.FIT_GENERIC
00642                     _debugprint ("%s: %s" % (fit[driver], driver))
00643 
00644         # What about the CMD field of the Device ID?  Some devices
00645         # have optional units for page description languages, such as
00646         # PostScript, and they will report different CMD strings
00647         # accordingly.
00648         #
00649         # By convention, if a PPD contains a Device ID with a CMD
00650         # field, that PPD can only be used whenever any of the
00651         # comma-separated words in the CMD field appear in the
00652         # device's ID.
00653         # (See Red Hat bug #630058).
00654         #
00655         # We'll do that check now, and any PPDs that fail
00656         # (e.g. PostScript PPD for non-PostScript printer) can be
00657         # eliminated from the list.
00658         #
00659         # The reason we don't do this check any earlier is that we
00660         # don't want to eliminate PPDs only to have the fuzzy matcher
00661         # add them back in afterwards.
00662         #
00663         # While doing this, any drivers that we can positively confirm
00664         # as using a command set understood by the printer will be
00665         # converted from FIT_EXACT to FIT_EXACT_CMD.
00666         if id_matched and len (commandsets) > 0:
00667             failed = set()
00668             exact_cmd = set()
00669             for ppdname in fit.keys ():
00670                 ppd_cmd_field = None
00671                 ppd = self.ppds[ppdname]
00672                 ppd_device_id = _singleton (ppd.get ('ppd-device-id'))
00673                 if ppd_device_id:
00674                     ppd_device_id_dict = parseDeviceID (ppd_device_id)
00675                     ppd_cmd_field = ppd_device_id_dict["CMD"]
00676 
00677                 if (not ppd_cmd_field and
00678                     # ppd-type is not reliable for driver-generated
00679                     # PPDs (see CUPS STR #3720).  Neither gutenprint
00680                     # nor foomatic specify ppd-type in their CUPS
00681                     # drivers.
00682                     ppdname.find (":") == -1):
00683                     # If this is a PostScript PPD we know which
00684                     # command set it will use.
00685                     ppd_type = _singleton (ppd.get ('ppd-type'))
00686                     if ppd_type == "postscript":
00687                         ppd_cmd_field = ["POSTSCRIPT"]
00688 
00689                 if not ppd_cmd_field:
00690                     # We can't be sure which command set this driver
00691                     # uses.
00692                     continue
00693 
00694                 usable = False
00695                 for pdl in ppd_cmd_field:
00696                     if pdl in commandsets:
00697                         usable = True
00698                         break
00699 
00700                 if usable:
00701                     exact_cmd.add (ppdname)
00702                 else:
00703                     failed.add (ppdname)
00704 
00705             # Assign the more specific fit "exact-cmd" to those that
00706             # positively matched the CMD field.
00707             for each in exact_cmd:
00708                 if fit[each] == self.FIT_EXACT:
00709                     fit[each] = self.FIT_EXACT_CMD
00710                     _debugprint (self.FIT_EXACT_CMD + ": %s" % each)
00711 
00712             _debugprint ("Removed %s due to CMD mis-match" % failed)
00713             for each in failed:
00714                 del fit[each]
00715 
00716         if not fit:
00717             fallbacks = ["textonly.ppd", "postscript.ppd"]
00718             found = False
00719             for fallback in fallbacks:
00720                 _debugprint ("'%s' fallback" % fallback)
00721                 fallbackgz = fallback + ".gz"
00722                 for ppdpath in self.ppds.keys ():
00723                     if (ppdpath.endswith (fallback) or
00724                         ppdpath.endswith (fallbackgz)):
00725                         fit[ppdpath] = self.FIT_NONE
00726                         found = True
00727                         break
00728 
00729                 if found:
00730                     break
00731 
00732                 _debugprint ("Fallback '%s' not available" % fallback)
00733 
00734             if not found:
00735                 _debugprint ("No fallback available; choosing any")
00736                 fit[self.ppds.keys ()[0]] = self.FIT_NONE
00737 
00738         if not id_matched:
00739             sanitised_uri = re.sub (pattern="//[^@]*@/?", repl="//",
00740                                     string=str (uri))
00741             try:
00742                 cmd = reduce (lambda x, y: x + ","+ y, commandsets)
00743             except TypeError:
00744                 cmd = ""
00745             id = "MFG:%s;MDL:%s;" % (orig_mfg, orig_mdl)
00746             if cmd:
00747                 id += "CMD:%s;" % cmd
00748             if description:
00749                 id += "DES:%s;" % description
00750 
00751             print "No ID match for device %s:" % sanitised_uri
00752             print id
00753 
00754         return fit

Here is the call graph for this function:

Here is the caller graph for this function:

def cupshelpers.ppds.PPDs.getStatusFromFit (   self,
  fit 
)

Definition at line 449 of file ppds.py.

00449 
00450     def getStatusFromFit (self, fit):
00451         return self._fit_to_status.get (fit, xmldriverprefs.DriverType.FIT_NONE)

Here is the call graph for this function:

Here is the caller graph for this function:

def cupshelpers.ppds.PPDs.orderPPDNamesByPreference (   self,
  ppdnamelist = [],
  downloadedfiles = [],
  make_and_model = None,
  devid = None,
  fit = None 
)
       Sort a list of PPD names by preferred driver type.

       @param ppdnamelist: PPD names
       @type ppdnamelist: string list
@param downloadedfiles: Filenames from packages downloaded
@type downloadedfiles: string list
@param make_and_model: device-make-and-model name
@type make_and_model: string
@param devid: Device ID dict
@type devid: dict indexed by Device ID field name, of strings;
except for CMD field which must be a string list
@param fit: Driver fit string for each PPD name
@type fit: dict of PPD name:fit
       @returns: string list

Definition at line 455 of file ppds.py.

00455 
00456                                    devid=None, fit=None):
00457         """
00458 
00459        Sort a list of PPD names by preferred driver type.
00460 
00461        @param ppdnamelist: PPD names
00462        @type ppdnamelist: string list
00463         @param downloadedfiles: Filenames from packages downloaded
00464         @type downloadedfiles: string list
00465         @param make_and_model: device-make-and-model name
00466         @type make_and_model: string
00467         @param devid: Device ID dict
00468         @type devid: dict indexed by Device ID field name, of strings;
00469         except for CMD field which must be a string list
00470         @param fit: Driver fit string for each PPD name
00471         @type fit: dict of PPD name:fit
00472        @returns: string list
00473        """
00474         if fit == None:
00475             fit = {}
00476 
00477         if self.drivertypes and self.preforder:
00478             ppds = {}
00479             for ppdname in ppdnamelist:
00480                 ppds[ppdname] = self.ppds[ppdname]
00481 
00482             orderedtypes = self.preforder.get_ordered_types (self.drivertypes,
00483                                                              make_and_model,
00484                                                              devid)
00485             orderedppds = self.drivertypes.get_ordered_ppdnames (orderedtypes,
00486                                                                  ppds, fit)
00487             ppdnamelist = map (lambda (typ, name): name, orderedppds)
00488 
00489         # Special handling for files we've downloaded.  First collect
00490         # their basenames.
00491         downloadedfnames = set()
00492         for downloadedfile in downloadedfiles:
00493             (path, slash, fname) = downloadedfile.rpartition ("/")
00494             downloadedfnames.add (fname)
00495 
00496         if downloadedfnames:
00497             # Next compare the basenames of each ppdname
00498             downloadedppdnames = []
00499             for ppdname in ppdnamelist:
00500                 (path, slash, ppdfname) = ppdname.rpartition ("/")
00501                 if ppdfname in downloadedfnames:
00502                     downloadedppdnames.add (ppdname)
00503 
00504             # Finally, promote the matching ones to the head of the list.
00505             if downloadedppdnames:
00506                 for ppdname in ppdnamelist:
00507                     if ppdname not in downloadedppdnames:
00508                         downloadedppdnames.append (ppdname)
00509 
00510                 ppdnamelist = downloadedppdnames
00511 
00512         return ppdnamelist

Here is the call graph for this function:

Here is the caller graph for this function:


Member Data Documentation

dictionary cupshelpers.ppds.PPDs._fit_to_status [static, private]
Initial value:
{ FIT_EXACT_CMD: STATUS_SUCCESS,
                       FIT_EXACT: STATUS_SUCCESS,
                       FIT_CLOSE: STATUS_MODEL_MISMATCH,
                       FIT_GENERIC: STATUS_GENERIC_DRIVER,
                       FIT_NONE: STATUS_NO_DRIVER }

Definition at line 318 of file ppds.py.

Definition at line 338 of file ppds.py.

Definition at line 314 of file ppds.py.

Definition at line 313 of file ppds.py.

Definition at line 312 of file ppds.py.

Definition at line 315 of file ppds.py.

Definition at line 316 of file ppds.py.

Definition at line 336 of file ppds.py.

Definition at line 1078 of file ppds.py.

Definition at line 1079 of file ppds.py.

Definition at line 335 of file ppds.py.

Definition at line 334 of file ppds.py.

Definition at line 339 of file ppds.py.

Definition at line 309 of file ppds.py.

Definition at line 308 of file ppds.py.

Definition at line 310 of file ppds.py.

Definition at line 307 of file ppds.py.


The documentation for this class was generated from the following file: