Annotation of kupu/plone/html2captioned.py, revision 1.1.1.1

1.1       dwinter     1: # Portal transform for images with captions
                      2: #
                      3: # We want to be able to support captions in images.
                      4: # The easiest way to do this is to define a Portal Transform which is
                      5: # applied to the HTML body text on output.
                      6: #
                      7: # The transform finds all the embedded images, and replaces them with
                      8: # an appropriate chunk of HTML to include the caption.
                      9: #
                     10: from Products.CMFCore.utils import getToolByName
                     11: from Products.PortalTransforms.interfaces import itransform
                     12: from DocumentTemplate.DT_Util import html_quote
                     13: from DocumentTemplate.DT_Var import newline_to_br
                     14: import re
                     15: 
                     16: __revision__ = '$Id$'
                     17: 
                     18: # IMAGE_PATTERN matches an image tag on its own, or an image tag
                     19: # enclosed in a simple <p> or <div>. In the latter case we strip out
                     20: # the enclosing tag since we are going to insert our own.
                     21: PATIMG = '\\<img[^>]+class=[^=>]*captioned[^>]+\\>'
                     22: PATA = '(?:\\<a[^>]*\\>'+PATIMG+'\\</a\\>)' + '|' + PATIMG
                     23: PAT0 = '('+PATA+')'
                     24: PAT1 = '<(?:p|div)[^>]*>'+PAT0 + '</(?:p|div)>' + '|' + PAT0
                     25: IMAGE_PATTERN = re.compile(PAT1, re.IGNORECASE)
                     26: 
                     27: # Regex to match stupid IE attributes. In IE generated HTML an
                     28: # attribute may not be enclosed by quotes if it doesn't contain
                     29: # certain punctuation.
                     30: ATTR_VALUE = '=(?:"?)(?P<%s>(?<=")[^"]*|[^ \/>]*)'
                     31: ATTR_CLASS = ATTR_VALUE % 'class'
                     32: ATTR_WIDTH = ATTR_VALUE % 'width'
                     33: 
                     34: ATTR_PATTERN = re.compile('''
                     35:     (?P<tag>\<
                     36:      ( class%s
                     37:      | src="resolveuid/(?P<src>([^/"#? ]*))
                     38:      | width%s
                     39:      | .
                     40:      )*\>
                     41:     )''' % (ATTR_CLASS, ATTR_WIDTH), re.VERBOSE)
                     42: 
                     43: CLASS_PATTERN = re.compile('\s*class=("[^"]*captioned[^"]*"|[^" \/>]+)')
                     44: IMAGE_TEMPLATE = '''\
                     45: <div class="%(class)s" style="width:%(width)spx;">
                     46:  <div style="width:%(width)spx;">
                     47:   %(tag)s
                     48:  </div>
                     49:  <div class="image-caption">
                     50:   %(caption)s
                     51:  </div>
                     52: </div>
                     53: '''
                     54: 
                     55: UID_PATTERN = re.compile('(?P<tag><(?:a|img) [^>]*(?:src|href)=")(?P<url>[^"]*resolveuid/(?P<uid>[^"#? ]*))')
                     56: 
                     57: class HTMLToCaptioned:
                     58:     """Transform which adds captions to images embedded in HTML"""
                     59:     __implements__ = itransform
                     60:     __name__ = "html_to_captioned"
                     61:     inputs = ('text/html',)
                     62:     output = "text/x-html-captioned"
                     63:     
                     64:     def __init__(self, name=None):
                     65:         self.config_metadata = {
                     66:             'inputs' : ('list', 'Inputs', 'Input(s) MIME type. Change with care.'),
                     67:             }
                     68:         if name:
                     69:             self.__name__ = name
                     70: 
                     71:     def name(self):
                     72:         return self.__name__
                     73: 
                     74:     def __getattr__(self, attr):
                     75:         if attr == 'inputs':
                     76:             return self.config['inputs']
                     77:         if attr == 'output':
                     78:             return self.config['output']
                     79:         raise AttributeError(attr)
                     80: 
                     81:     def convert(self, data, idata, filename=None, **kwargs):
                     82:         """convert the data, store the result in idata and return that
                     83:         optional argument filename may give the original file name of received data
                     84:         additional arguments given to engine's convert, convertTo or __call__ are
                     85:         passed back to the transform
                     86:         
                     87:         The object on which the translation was invoked is available as context
                     88:         (default: None)
                     89:         """
                     90:         context = kwargs.get('context', None)
                     91:         if context:
                     92:             at_tool = context.archetype_tool
                     93: 
                     94:         if context and at_tool:        
                     95:             def replaceImage(match):
                     96:                 tag = match.group(1) or match.group(2)
                     97:                 attrs = ATTR_PATTERN.match(tag)
                     98:                 src = attrs.group('src')
                     99:                 klass = attrs.group('class')
                    100:                 width = attrs.group('width')
                    101:                 if src:
                    102:                     d = attrs.groupdict()
                    103:                     target = at_tool.reference_catalog.lookupObject(src)
                    104:                     if target:
                    105:                         d['caption'] = newline_to_br(target.Description())
                    106:                         d['tag'] = CLASS_PATTERN.sub('', d['tag'])
                    107:                         if not width:
                    108:                             d['width'] = target.getWidth()
                    109: 
                    110:                         return IMAGE_TEMPLATE % d
                    111:                 return match.group(0) # No change
                    112: 
                    113:             html = IMAGE_PATTERN.sub(replaceImage, data)
                    114: 
                    115:             # Replace urls that use UIDs with human friendly urls.
                    116:             def replaceUids(match):
                    117:                 tag = match.group('tag')
                    118:                 uid = match.group('uid')
                    119:                 target = at_tool.reference_catalog.lookupObject(uid)
                    120:                 if target:
                    121:                     return tag + target.absolute_url()
                    122:                 return match.group(0)
                    123: 
                    124:             html = UID_PATTERN.sub(replaceUids, html)
                    125:             
                    126:             idata.setData(html)
                    127:             return idata
                    128: 
                    129:         # No context to use for replacements, so don't bother trying.
                    130:         return data
                    131: 
                    132: def register():
                    133:     return HTMLToCaptioned()
                    134: 
                    135: def initialize():
                    136:     engine = getToolByName(portal, 'portal_transforms')
                    137:     engine.registerTransform(register())

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>