<commit_before>#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history www.reportlab.co.uk/rl-cgi/viewcvs.cgi/rlextra/graphics/Csrc/renderPM/renderP.py
__version__=''' $Id$ '''
"""Usage:
    from reportlab.graphics import renderPM
    renderPM.drawToFile(drawing,filename,fmt='GIF',configPIL={....})
Other functions let you create a PM drawing as string or into a PM buffer.
Execute the script to see some test drawings."""

from reportlab.graphics.shapes import *
from reportlab.graphics.renderbase import StateTracker, getStateDelta, renderScaledDrawing
from reportlab.pdfbase.pdfmetrics import getFont, unicode2T1
from math import sin, cos, pi, ceil
from reportlab.lib.utils import getStringIO, open_and_read
from reportlab import rl_config

class RenderPMError(Exception):
    pass

import string, os, sys

try:
    import _renderPM
except ImportError, errMsg:
    raise ImportError, "No module named _renderPM\n" + \
        (str(errMsg)!='No module named _renderPM' and "it may be the wrong version or badly installed!" or
                                    "see http://www.reportlab.org/rl_addons.html")

from types import TupleType, ListType
_SeqTypes = (TupleType,ListType)

def _getImage():
    try:
        from PIL import Image
    except ImportError:
        import Image
    return Image

def Color2Hex(c):
    #assert isinstance(colorobj, colors.Color) #these checks don't work well RGB
    if c: return ((0xFF&int(255*c.red)) << 16) | ((0xFF&int(255*c.green)) << 8) | (0xFF&int(255*c.blue))
    return c

# the main entry point for users...
def draw(drawing, canvas, x, y, showBoundary=rl_config._unset_):
    """As it says"""
    R = _PMRenderer()
    R.draw(renderScaledDrawing(drawing), canvas, x, y, showBoundary=showBoundary)

from reportlab.graphics.renderbase import Renderer
class _PMRenderer(Renderer):
    """This draws onto a pix map image. It needs to be a class
    rather than a function, as some image-specific state tracking is
    needed outside of the state info in the SVG model."""

    def __init__(self):
        self._tracker = StateTracker()

    def pop(self):
        self._tracker.pop()
        self.applyState()

    def push(self,node):
        deltas = getStateDelta(node)
        self._tracker.push(deltas)
        self.applyState()

    def applyState(self):
        s = self._tracker.getState()
        self._canvas.ctm = s['ctm']
        self._canvas.strokeWidth = s['strokeWidth']
        self._canvas.strokeColor = Color2Hex(s['strokeColor'])
        self._canvas.lineCap = s['strokeLineCap']
        self._canvas.lineJoin = s['strokeLineJoin']
        da = s['strokeDashArray']
        da = da and (0,da) or None
        self._canvas.dashArray = da
        self._canvas.fillColor = Color2Hex(s['fillColor'])
        self._canvas.setFont(s['fontName'], s['fontSize'])

    def initState(self,x,y):
        deltas = STATE_DEFAULTS.copy()
        deltas['transform'] = self._canvas._baseCTM[0:4]+(x,y)
        self._tracker.push(deltas)
        self.applyState()

    def drawNode(self, node):
        """This is the recursive method called for each node
        in the tree"""

        #apply state changes
        self.push(node)

        #draw the object, or recurse
        self.drawNodeDispatcher(node)

        # restore the state
        self.pop()

    def drawRect(self, rect):
        c = self._canvas
        if rect.rx == rect.ry == 0:
            #plain old rectangle, draw clockwise (x-axis to y-axis) direction
            c.rect(rect.x,rect.y, rect.width, rect.height)
        else:
            c.roundRect(rect.x,rect.y, rect.width, rect.height, rect.rx, rect.ry)

    def drawLine(self, line):
        self._canvas.line(line.x1,line.y1,line.x2,line.y2)

    def drawImage(self, image):
        if image.path and os.path.exists(image.path):
            if type(image.path) is type(''):
                im = _getImage().open(image.path).convert('RGB')
            else:
                im = image.path.convert('RGB')
            srcW, srcH = im.size
            dstW, dstH = image.width, image.height
            if dstW is None: dstW = srcW
            if dstH is None: dstH = srcH
            self._canvas._aapixbuf(
                    image.x, image.y, dstW, dstH,
                    im.tostring(), srcW, srcH, 3,
                    )

    def drawCircle(self, circle):
        c = self._canvas
        c.circle(circle.cx,circle.cy, circle.r)
        c.fillstrokepath()

    def drawPolyLine(self, polyline, _doClose=0):
        P = polyline.points
        assert len(P) >= 2, 'Polyline must have 1 or more points'
        c = self._canvas
        c.pathBegin()
        c.moveTo(P[0], P[1])
        for i in range(2, len(P), 2):
            c.lineTo(P[i], P[i+1])
        if _doClose:
            c.pathClose()
            c.pathFill()
        c.pathStroke()

    def drawEllipse(self, ellipse):
        c=self._canvas
        c.ellipse(ellipse.cx, ellipse.cy, ellipse.rx,ellipse.ry)
        c.fillstrokepath()

    def drawPolygon(self, polygon):
        self.drawPolyLine(polygon,_doClose=1)

    def drawString(self, stringObj):
        canv = self._canvas
        fill = canv.fillColor
        if fill is not None:
            S = self._tracker.getState()
            text_anchor = S['textAnchor']
            fontName = S['fontName']
            fontSize = S['fontSize']
            text = stringObj.text
            x = stringObj.x
            y = stringObj.y
            if not text_anchor in ['start','inherited']:
                textLen = stringWidth(text, fontName,fontSize)
                if text_anchor=='end':
                    x = x-textLen
                elif text_anchor=='middle':
                    x = x - textLen/2
                else:
                    raise ValueError, 'bad value for textAnchor '+str(text_anchor)
            canv.drawString(x,y,text,_fontInfo=(fontName,fontSize))

    def drawPath(self, path):
        c = self._canvas
        if path is EmptyClipPath:
            del c._clipPaths[-1]
            if c._clipPaths:
                P = c._clipPaths[-1]
                icp = P.isClipPath
                P.isClipPath = 1
                self.drawPath(P)
                P.isClipPath = icp
            else:
                c.clipPathClear()
            return
        c.pathBegin()
        drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.pathClose)
        from reportlab.graphics.shapes import _renderPath
        isClosed = _renderPath(path, drawFuncs)
        if path.isClipPath:
            c.clipPathSet()
            c._clipPaths.append(path)
        else:
            if isClosed: c.pathFill()
            c.pathStroke()

def _setFont(gs,fontName,fontSize):
    try:
        gs.setFont(fontName,fontSize)
    except _renderPM.Error, errMsg:
        if errMsg.args[0]!="Can't find font!": raise
        #here's where we try to add a font to the canvas
        try:
            f = getFont(fontName)
            if _renderPM._version<='0.98':  #added reader arg in 0.99
                _renderPM.makeT1Font(fontName,f.face.findT1File(),f.encoding.vector)
            else:
                _renderPM.makeT1Font(fontName,f.face.findT1File(),f.encoding.vector,open_and_read)
        except:
            s1, s2 = map(str,sys.exc_info()[:2])
            raise RenderPMError, "Can't setFont(%s) missing the T1 files?\nOriginally %s: %s" % (fontName,s1,s2)
        gs.setFont(fontName,fontSize)

def _convert2pilp(im):
    Image = _getImage()
    return im.convert("P", dither=Image.NONE, palette=Image.ADAPTIVE)

def _convert2pilL(im):
    return im.convert("L")

def _convert2pil1(im):
    return im.convert("1")

def _saveAsPICT(im,fn,fmt,transparent=None):
    im = _convert2pilp(im)
    cols, rows = im.size
    #s = _renderPM.pil2pict(cols,rows,im.tostring(),im.im.getpalette(),transparent is not None and Color2Hex(transparent) or -1)
    s = _renderPM.pil2pict(cols,rows,im.tostring(),im.im.getpalette())
    if not hasattr(fn,'write'):
        open(os.path.splitext(fn)[0]+'.'+string.lower(fmt),'wb').write(s)
        if os.name=='mac':
            from reportlab.lib.utils import markfilename
            markfilename(fn,ext='PICT')
    else:
        fn.write(s)

BEZIER_ARC_MAGIC = 0.5522847498     #constant for drawing circular arcs w/ Beziers
class PMCanvas:
    def __init__(self,w,h,dpi=72,bg=0xffffff,configPIL=None):
        '''configPIL dict is passed to image save method'''
        scale = dpi/72.0
        w = int(w*scale+0.5)
        h = int(h*scale+0.5)
        self.__dict__['_gs'] = _renderPM.gstate(w,h,bg=bg)
        self.__dict__['_bg'] = bg
        self.__dict__['_baseCTM'] = (scale,0,0,scale,0,0)
        self.__dict__['_clipPaths'] = []
        self.__dict__['configPIL'] = configPIL
        self.__dict__['_dpi'] = dpi
        self.ctm = self._baseCTM

    def _drawTimeResize(self,w,h,bg=None):
        if bg is None: bg = self._bg
        self._drawing.width, self._drawing.height = w, h
        A = {'ctm':None, 'strokeWidth':None, 'strokeColor':None, 'lineCap':None, 'lineJoin':None, 'dashArray':None, 'fillColor':None}
        gs = self._gs
        fN,fS = gs.fontName, gs.fontSize
        for k in A.keys():
            A[k] = getattr(gs,k)
        del gs, self._gs
        gs = self.__dict__['_gs'] = _renderPM.gstate(w,h,bg=bg)
        for k in A.keys():
            setattr(self,k,A[k])
        gs.setFont(fN,fS)

    def toPIL(self):
        im = _getImage().new('RGB', size=(self._gs.width, self._gs.height))
        im.fromstring(self._gs.pixBuf)
        return im

    def saveToFile(self,fn,fmt=None):
        im = self.toPIL()
        if fmt is None:
            if type(fn) is not StringType:
                raise ValueError, "Invalid type '%s' for fn when fmt is None" % type(fn)
            fmt = os.path.splitext(fn)[1]
            if fmt.startswith('.'): fmt = fmt[1:]
        configPIL = self.configPIL or {}
        fmt = string.upper(fmt)
        if fmt in ('GIF'):
            im = _convert2pilp(im)
        elif fmt in ('TIFF','TIFFP','TIFFL','TIF','TIFF1'):
            if fmt.endswith('P'):
                im = _convert2pilp(im)
            elif fmt.endswith('L'):
                im = _convert2pilL(im)
            elif fmt.endswith('1'):
                im = _convert2pil1(im)
            fmt='TIFF'
        elif fmt in ('PCT','PICT'):
            return _saveAsPICT(im,fn,fmt,transparent=configPIL.get('transparent',None))
        elif fmt in ('PNG','BMP', 'PPM'):
            if fmt=='PNG':
                try:
                    from PIL import PngImagePlugin
                except ImportError:
                    import PngImagePlugin
            elif fmt=='BMP':
                try:
                    from PIL import BmpImagePlugin
                except ImportError:
                    import BmpImagePlugin
        elif fmt in ('JPG','JPEG'):
            fmt = 'JPEG'
        elif fmt in ('GIF',):
            pass
        else:
            raise RenderPMError,"Unknown image kind %s" % fmt
        if fmt=='TIFF':
            tc = configPIL.get('transparent',None)
            if tc:
                from PIL import ImageChops, Image
                T = 768*[0]
                for o, c in zip((0,256,512), tc.bitmap_rgb()):
                    T[o+c] = 255
                #if type(fn) is type(''): ImageChops.invert(im.point(T).convert('L').point(255*[0]+[255])).save(fn+'_mask.gif','GIF')
                im = Image.merge('RGBA', im.split()+(ImageChops.invert(im.point(T).convert('L').point(255*[0]+[255])),))
                #if type(fn) is type(''): im.save(fn+'_masked.gif','GIF')
            for a,d in ('resolution',self._dpi),('resolution unit','inch'):
                configPIL[a] = configPIL.get(a,d)
        im.save(fn,fmt,**configPIL)
        if not hasattr(fn,'write') and os.name=='mac':
            from reportlab.lib.utils import markfilename
            markfilename(fn,ext=fmt)

    def saveToString(self,fmt='GIF'):
        s = getStringIO()
        self.saveToFile(s,fmt=fmt)
        return s.getvalue()

    def _saveToBMP(self,f):
        '''
        <NAME>, <<EMAIL>>, asserts that this is a respectable way to get BMP without PIL
        f is a file like object to which the BMP is written
        '''
        import struct
        gs = self._gs
        pix, width, height = gs.pixBuf, gs.width, gs.height
        f.write(struct.pack('=2sLLLLLLhh24x','BM',len(pix)+54,0,54,40,width,height,1,24))
        rowb = width * 3<commit_msg>renderPM.py: added preConvert/preSaveCB args and also invert option<commit_after>#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history www.reportlab.co.uk/rl-cgi/viewcvs.cgi/rlextra/graphics/Csrc/renderPM/renderP.py
__version__=''' $Id$ '''
"""Usage:
    from reportlab.graphics import renderPM
    renderPM.drawToFile(drawing,filename,fmt='GIF',configPIL={....})
Other functions let you create a PM drawing as string or into a PM buffer.
Execute the script to see some test drawings."""

from reportlab.graphics.shapes import *
from reportlab.graphics.renderbase import StateTracker, getStateDelta, renderScaledDrawing
from reportlab.pdfbase.pdfmetrics import getFont, unicode2T1
from math import sin, cos, pi, ceil
from reportlab.lib.utils import getStringIO, open_and_read
from reportlab import rl_config

class RenderPMError(Exception):
    pass

import string, os, sys

try:
    import _renderPM
except ImportError, errMsg:
    raise ImportError, "No module named _renderPM\n" + \
        (str(errMsg)!='No module named _renderPM' and "it may be the wrong version or badly installed!" or
                                    "see http://www.reportlab.org/rl_addons.html")

from types import TupleType, ListType
_SeqTypes = (TupleType,ListType)

def _getImage():
    try:
        from PIL import Image
    except ImportError:
        import Image
    return Image

def Color2Hex(c):
    #assert isinstance(colorobj, colors.Color) #these checks don't work well RGB
    if c: return ((0xFF&int(255*c.red)) << 16) | ((0xFF&int(255*c.green)) << 8) | (0xFF&int(255*c.blue))
    return c

# the main entry point for users...
def draw(drawing, canvas, x, y, showBoundary=rl_config._unset_):
    """As it says"""
    R = _PMRenderer()
    R.draw(renderScaledDrawing(drawing), canvas, x, y, showBoundary=showBoundary)

from reportlab.graphics.renderbase import Renderer
class _PMRenderer(Renderer):
    """This draws onto a pix map image. It needs to be a class
    rather than a function, as some image-specific state tracking is
    needed outside of the state info in the SVG model."""

    def __init__(self):
        self._tracker = StateTracker()

    def pop(self):
        self._tracker.pop()
        self.applyState()

    def push(self,node):
        deltas = getStateDelta(node)
        self._tracker.push(deltas)
        self.applyState()

    def applyState(self):
        s = self._tracker.getState()
        self._canvas.ctm = s['ctm']
        self._canvas.strokeWidth = s['strokeWidth']
        self._canvas.strokeColor = Color2Hex(s['strokeColor'])
        self._canvas.lineCap = s['strokeLineCap']
        self._canvas.lineJoin = s['strokeLineJoin']
        da = s['strokeDashArray']
        da = da and (0,da) or None
        self._canvas.dashArray = da
        self._canvas.fillColor = Color2Hex(s['fillColor'])
        self._canvas.setFont(s['fontName'], s['fontSize'])

    def initState(self,x,y):
        deltas = STATE_DEFAULTS.copy()
        deltas['transform'] = self._canvas._baseCTM[0:4]+(x,y)
        self._tracker.push(deltas)
        self.applyState()

    def drawNode(self, node):
        """This is the recursive method called for each node
        in the tree"""

        #apply state changes
        self.push(node)

        #draw the object, or recurse
        self.drawNodeDispatcher(node)

        # restore the state
        self.pop()

    def drawRect(self, rect):
        c = self._canvas
        if rect.rx == rect.ry == 0:
            #plain old rectangle, draw clockwise (x-axis to y-axis) direction
            c.rect(rect.x,rect.y, rect.width, rect.height)
        else:
            c.roundRect(rect.x,rect.y, rect.width, rect.height, rect.rx, rect.ry)

    def drawLine(self, line):
        self._canvas.line(line.x1,line.y1,line.x2,line.y2)

    def drawImage(self, image):
        if image.path and os.path.exists(image.path):
            if type(image.path) is type(''):
                im = _getImage().open(image.path).convert('RGB')
            else:
                im = image.path.convert('RGB')
            srcW, srcH = im.size
            dstW, dstH = image.width, image.height
            if dstW is None: dstW = srcW
            if dstH is None: dstH = srcH
            self._canvas._aapixbuf(
                    image.x, image.y, dstW, dstH,
                    im.tostring(), srcW, srcH, 3,
                    )

    def drawCircle(self, circle):
        c = self._canvas
        c.circle(circle.cx,circle.cy, circle.r)
        c.fillstrokepath()

    def drawPolyLine(self, polyline, _doClose=0):
        P = polyline.points
        assert len(P) >= 2, 'Polyline must have 1 or more points'
        c = self._canvas
        c.pathBegin()
        c.moveTo(P[0], P[1])
        for i in range(2, len(P), 2):
            c.lineTo(P[i], P[i+1])
        if _doClose:
            c.pathClose()
            c.pathFill()
        c.pathStroke()

    def drawEllipse(self, ellipse):
        c=self._canvas
        c.ellipse(ellipse.cx, ellipse.cy, ellipse.rx,ellipse.ry)
        c.fillstrokepath()

    def drawPolygon(self, polygon):
        self.drawPolyLine(polygon,_doClose=1)

    def drawString(self, stringObj):
        canv = self._canvas
        fill = canv.fillColor
        if fill is not None:
            S = self._tracker.getState()
            text_anchor = S['textAnchor']
            fontName = S['fontName']
            fontSize = S['fontSize']
            text = stringObj.text
            x = stringObj.x
            y = stringObj.y
            if not text_anchor in ['start','inherited']:
                textLen = stringWidth(text, fontName,fontSize)
                if text_anchor=='end':
                    x = x-textLen
                elif text_anchor=='middle':
                    x = x - textLen/2
                else:
                    raise ValueError, 'bad value for textAnchor '+str(text_anchor)
            canv.drawString(x,y,text,_fontInfo=(fontName,fontSize))

    def drawPath(self, path):
        c = self._canvas
        if path is EmptyClipPath:
            del c._clipPaths[-1]
            if c._clipPaths:
                P = c._clipPaths[-1]
                icp = P.isClipPath
                P.isClipPath = 1
                self.drawPath(P)
                P.isClipPath = icp
            else:
                c.clipPathClear()
            return
        c.pathBegin()
        drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.pathClose)
        from reportlab.graphics.shapes import _renderPath
        isClosed = _renderPath(path, drawFuncs)
        if path.isClipPath:
            c.clipPathSet()
            c._clipPaths.append(path)
        else:
            if isClosed: c.pathFill()
            c.pathStroke()

def _setFont(gs,fontName,fontSize):
    try:
        gs.setFont(fontName,fontSize)
    except _renderPM.Error, errMsg:
        if errMsg.args[0]!="Can't find font!": raise
        #here's where we try to add a font to the canvas
        try:
            f = getFont(fontName)
            if _renderPM._version<='0.98':  #added reader arg in 0.99
                _renderPM.makeT1Font(fontName,f.face.findT1File(),f.encoding.vector)
            else:
                _renderPM.makeT1Font(fontName,f.face.findT1File(),f.encoding.vector,open_and_read)
        except:
            s1, s2 = map(str,sys.exc_info()[:2])
            raise RenderPMError, "Can't setFont(%s) missing the T1 files?\nOriginally %s: %s" % (fontName,s1,s2)
        gs.setFont(fontName,fontSize)

def _convert2pilp(im):
    Image = _getImage()
    return im.convert("P", dither=Image.NONE, palette=Image.ADAPTIVE)

def _convert2pilL(im):
    return im.convert("L")

def _convert2pil1(im):
    return im.convert("1")

def _saveAsPICT(im,fn,fmt,transparent=None):
    im = _convert2pilp(im)
    cols, rows = im.size
    #s = _renderPM.pil2pict(cols,rows,im.tostring(),im.im.getpalette(),transparent is not None and Color2Hex(transparent) or -1)
    s = _renderPM.pil2pict(cols,rows,im.tostring(),im.im.getpalette())
    if not hasattr(fn,'write'):
        open(os.path.splitext(fn)[0]+'.'+string.lower(fmt),'wb').write(s)
        if os.name=='mac':
            from reportlab.lib.utils import markfilename
            markfilename(fn,ext='PICT')
    else:
        fn.write(s)

BEZIER_ARC_MAGIC = 0.5522847498     #constant for drawing circular arcs w/ Beziers
class PMCanvas:
    def __init__(self,w,h,dpi=72,bg=0xffffff,configPIL=None):
        '''configPIL dict is passed to image save method'''
        scale = dpi/72.0
        w = int(w*scale+0.5)
        h = int(h*scale+0.5)
        self.__dict__['_gs'] = _renderPM.gstate(w,h,bg=bg)
        self.__dict__['_bg'] = bg
        self.__dict__['_baseCTM'] = (scale,0,0,scale,0,0)
        self.__dict__['_clipPaths'] = []
        self.__dict__['configPIL'] = configPIL
        self.__dict__['_dpi'] = dpi
        self.ctm = self._baseCTM

    def _drawTimeResize(self,w,h,bg=None):
        if bg is None: bg = self._bg
        self._drawing.width, self._drawing.height = w, h
        A = {'ctm':None, 'strokeWidth':None, 'strokeColor':None, 'lineCap':None, 'lineJoin':None, 'dashArray':None, 'fillColor':None}
        gs = self._gs
        fN,fS = gs.fontName, gs.fontSize
        for k in A.keys():
            A[k] = getattr(gs,k)
        del gs, self._gs
        gs = self.__dict__['_gs'] = _renderPM.gstate(w,h,bg=bg)
        for k in A.keys():
            setattr(self,k,A[k])
        gs.setFont(fN,fS)

    def toPIL(self):
        im = _getImage().new('RGB', size=(self._gs.width, self._gs.height))
        im.fromstring(self._gs.pixBuf)
        return im

    def saveToFile(self,fn,fmt=None):
        im = self.toPIL()
        if fmt is None:
            if type(fn) is not StringType:
                raise ValueError, "Invalid type '%s' for fn when fmt is None" % type(fn)
            fmt = os.path.splitext(fn)[1]
            if fmt.startswith('.'): fmt = fmt[1:]
        configPIL = self.configPIL or {}
        configPIL.setdefault('preConvertCB',None)
        preConvertCB=configPIL.pop('preConvertCB')
        if preConvertCB:
            im = preConvertCB(im)
        fmt = string.upper(fmt)
        if fmt in ('GIF',):
            im = _convert2pilp(im)
        elif fmt in ('TIFF','TIFFP','TIFFL','TIF','TIFF1'):
            if fmt.endswith('P'):
                im = _convert2pilp(im)
            elif fmt.endswith('L'):
                im = _convert2pilL(im)
            elif fmt.endswith('1'):
                im = _convert2pil1(im)
            fmt='TIFF'
        elif fmt in ('PCT','PICT'):
            return _saveAsPICT(im,fn,fmt,transparent=configPIL.get('transparent',None))
        elif fmt in ('PNG','BMP', 'PPM'):
            if fmt=='PNG':
                try:
                    from PIL import PngImagePlugin
                except ImportError:
                    import PngImagePlugin
            elif fmt=='BMP':
                try:
                    from PIL import BmpImagePlugin
                except ImportError:
                    import BmpImagePlugin
        elif fmt in ('JPG','JPEG'):
            fmt = 'JPEG'
        elif fmt in ('GIF',):
            pass
        else:
            raise RenderPMError,"Unknown image kind %s" % fmt
        if fmt=='TIFF':
            tc = configPIL.get('transparent',None)
            if tc:
                from PIL import ImageChops, Image
                T = 768*[0]
                for o, c in zip((0,256,512), tc.bitmap_rgb()):
                    T[o+c] = 255
                #if type(fn) is type(''): ImageChops.invert(im.point(T).convert('L').point(255*[0]+[255])).save(fn+'_mask.gif','GIF')
                im = Image.merge('RGBA', im.split()+(ImageChops.invert(im.point(T).convert('L').point(255*[0]+[255])),))
                #if type(fn) is type(''): im.save(fn+'_masked.gif','GIF')
            for a,d in ('resolution',self._dpi),('resolution unit','inch'):
                configPIL[a] = configPIL.get(a,d)
        configPIL.setdefault('chops_invert',0)
        if configPIL.pop('chops_invert'):
            from PIL import ImageChops
            im = ImageChops.invert(im)
        configPIL.setdefault('preSaveCB',None)
        preSaveCB=configPIL.pop('preSaveCB')
        if preSaveCB:
            im = preSaveCB(im)
        im.save(fn,fmt,**configPIL)
        if not hasattr(fn,'write') and os.name=='mac':
            from reportlab.lib.utils import markfilename
            markfilename(fn,ext=fmt)

    def saveToString(self,fmt='GIF'):
        s = getStringIO()
        self.saveToFile(s,fmt=fmt)
        return s.getvalue()

    def _saveToBMP(self,f):
        '''
        <NAME>, <<EMAIL>>, asserts that this is a respectable way to get BMP without PIL
        f is a file like object to which the BMP is written
        '''
        import struct
        gs = self._gs
        pix, width, height = gs.pixBuf, gs.width, gs.height
        f.write(struct.pack('=2sLLLLLLhh24x','BM',len(pix)+54,0,54,40,width,height,1,24))
        rowb = width * 3
{'fraction_non_alphanumeric': 0.12630836175467483, 'fraction_numerical': 0.012113371751146655, 'mean_word_length': 2.1482167098605456, 'pattern_counts': {'":': 2, '<': 19, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '946570', 'n_tokens_mistral': 8529, 'n_tokens_neox': 8195, 'n_words': 1867}