def get_backend(self, uri, client): '''Given the filename of an image, returns (fname, backend) where fname is the filename to be used (could be the same as filename, or something different if the image had to be converted or is missing), and backend is an Image class that can handle fname.''' backend = defaultimage # Extract all the information from the URI filename, extension, options = self.split_uri(uri) # If the image doesn't exist, we use a 'missing' image if not os.path.exists(filename): log.error("Missing image file: %s", filename) filename = missing if extension in ['svg', 'svgz']: if SVGImage.available(): log.info('Backend for %s is SVGIMage' % filename) backend = SVGImage else: filename = missing elif extension in [ 'ai', 'ccx', 'cdr', 'cgm', 'cmx', 'fig', 'sk1', 'sk', 'xml', 'wmf' ]: log.info('Backend for %s is VectorImage' % filename) backend = VectorImage elif extension in ['pdf']: if VectorPdf is not None and filename is not missing: backend = VectorPdf filename = uri # PDF images are implemented by converting via PythonMagick # w,h are in pixels. I need to set the density # of the image to the right dpi so this # looks decent elif LazyImports.PMImage or LazyImports.gfx: filename = self.raster(filename, client) else: log.warning("Minimal PDF image support "\ "requires PythonMagick [%s]", filename) filename = missing elif extension != 'jpg' and not LazyImports.PILImage: if LazyImports.PMImage: # Need to convert to JPG via PythonMagick filename = self.raster(filename) else: # No way to make this work log.error('To use a %s image you need PIL installed [%s]', extension, filename) filename = missing return filename, backend
def size_for_node(self, node, client): '''Given a docutils image node, returns the size the image should have in the PDF document, and what 'kind' of size that is. That involves lots of guesswork''' uri = os.path.join(client.basedir, str(node.get("uri"))) srcinfo = client, uri # Extract all the information from the URI imgname, extension, options = self.split_uri(uri) if not os.path.isfile(imgname): imgname = missing scale = float(node.get('scale', 100)) / 100 size_known = False # Figuring out the size to display of an image is ... annoying. # If the user provides a size with a unit, it's simple, adjustUnits # will return it in points and we're done. # However, often the unit wil be "%" (specially if it's meant for # HTML originally. In which case, we will use a percentage of # the containing frame. # Find the image size in pixels: kind = 'direct' xdpi, ydpi = client.styles.def_dpi, client.styles.def_dpi extension = imgname.split('.')[-1].lower() if extension in ['svg', 'svgz'] and SVGImage.available(): iw, ih = SVGImage(imgname, srcinfo=srcinfo).wrap(0, 0) # These are in pt, so convert to px iw = iw * xdpi / 72 ih = ih * ydpi / 72 elif extension in [ "ai", "ccx", "cdr", "cgm", "cmx", "sk1", "sk", "xml", "wmf", "fig" ] and VectorImage.available(): iw, ih = VectorImage(imgname, srcinfo=srcinfo).wrap(0, 0) # These are in pt, so convert to px iw = iw * xdpi / 72 ih = ih * ydpi / 72 elif extension == 'pdf': if VectorPdf is not None: box = VectorPdf.load_xobj(srcinfo).BBox else: pdf = LazyImports.pdfinfo if pdf is None: log.warning( 'PDF images are not supported without pyPdf or pdfrw [%s]', nodeid(node)) return 0, 0, 'direct' reader = pdf.PdfFileReader(open(imgname, 'rb')) box = [float(x) for x in reader.getPage(0)['/MediaBox']] x1, y1, x2, y2 = box # These are in pt, so convert to px iw = float((x2 - x1) * xdpi / 72) ih = float((y2 - y1) * ydpi / 72) size_known = True # Assume size from original PDF is OK else: keeptrying = True if LazyImports.PILImage: try: img = LazyImports.PILImage.open(imgname) img.load() iw, ih = img.size xdpi, ydpi = img.info.get('dpi', (xdpi, ydpi)) keeptrying = False except IOError: # PIL throws this when it's a broken/unknown image pass if keeptrying and LazyImports.PMImage: img = LazyImports.PMImage(imgname) iw = img.size().width() ih = img.size().height() density = img.density() # The density is in pixelspercentimeter (!?) xdpi = density.width() * 2.54 ydpi = density.height() * 2.54 keeptrying = False if keeptrying: log.error( "The image (%s, %s) is broken or in an unknown format", imgname, nodeid(node)) raise ValueError # Try to get the print resolution from the image itself via PIL. # If it fails, assume a DPI of 300, which is pretty much made up, # and then a 100% size would be iw*inch/300, so we pass # that as the second parameter to adjustUnits # # Some say the default DPI should be 72. That would mean # the largest printable image in A4 paper would be something # like 480x640. That would be awful. # w = node.get('width') h = node.get('height') if h is None and w is None: # Nothing specified # Guess from iw, ih log.warning( "Using image %s without specifying size." "Calculating based on image size at %ddpi [%s]", imgname, xdpi, nodeid(node)) w = iw * inch / xdpi h = ih * inch / ydpi elif w is not None: # Node specifies only w # In this particular case, we want the default unit # to be pixels so we work like rst2html if w[-1] == '%': kind = 'percentage_of_container' w = int(w[:-1]) else: # This uses default DPI setting because we # are not using the image's "natural size" # this is what LaTeX does, according to the # docutils mailing list discussion w = client.styles.adjustUnits(w, client.styles.tw, default_unit='px') if h is None: # h is set from w with right aspect ratio h = w * ih / iw else: h = client.styles.adjustUnits(h, ih * inch / ydpi, default_unit='px') elif h is not None and w is None: if h[-1] != '%': h = client.styles.adjustUnits(h, ih * inch / ydpi, default_unit='px') # w is set from h with right aspect ratio w = h * iw / ih else: log.error('Setting height as a percentage does **not** work. '\ 'ignoring height parameter [%s]', nodeid(node)) # Set both from image data w = iw * inch / xdpi h = ih * inch / ydpi # Apply scale factor w = w * scale h = h * scale # And now we have this probably completely bogus size! log.info("Image %s size calculated: %fcm by %fcm [%s]", imgname, w / cm, h / cm, nodeid(node)) return w, h, kind
def get_backend(self, uri, client): '''Given the filename of an image, returns (fname, backend) where fname is the filename to be used (could be the same as filename, or something different if the image had to be converted or is missing), and backend is an Image class that can handle fname. If uri ensd with '.*' then the returned filename will be the best quality supported at the moment. That means: PDF > SVG > anything else ''' backend = defaultimage # Extract all the information from the URI filename, extension, options = self.split_uri(uri) if '*' in filename: preferred = ['gif', 'jpg', 'png'] if SVGImage.available(): preferred.append('svg') preferred.append('pdf') # Find out what images are available available = glob.glob(filename) cfn = available[0] cv = -10 for fn in available: ext = fn.split('.')[-1] if ext in preferred: v = preferred.index(ext) else: v = -1 if v > cv: cv = v cfn = fn # cfn should have our favourite type of # those available filename = cfn extension = cfn.split('.')[-1] uri = filename # If the image doesn't exist, we use a 'missing' image if not os.path.exists(filename): log.error("Missing image file: %s", filename) filename = missing if extension in ['svg', 'svgz']: if SVGImage.available(): log.info('Backend for %s is SVGIMage' % filename) backend = SVGImage else: filename = missing elif extension in ['pdf']: if VectorPdf is not None and filename is not missing: backend = VectorPdf filename = uri # PDF images are implemented by converting via PythonMagick # w,h are in pixels. I need to set the density # of the image to the right dpi so this # looks decent elif LazyImports.PMImage or LazyImports.gfx: filename = self.raster(filename, client) else: log.warning("Minimal PDF image support "\ "requires PythonMagick or the vectorpdf extension [%s]", filename) filename = missing elif extension != 'jpg' and not LazyImports.PILImage: if LazyImports.PMImage: # Need to convert to JPG via PythonMagick filename = self.raster(filename, client) else: # No way to make this work log.error('To use a %s image you need PIL installed [%s]', extension, filename) filename = missing return filename, backend