Changeset 89:757b097c61a5 for relatorio/templates/opendocument.py
- Timestamp:
- 01/15/09 13:38:25 (4 years ago)
- Branch:
- default
- Files:
-
- 1 modified
-
relatorio/templates/opendocument.py (modified) (13 diffs)
Legend:
- Unmodified
- Added
- Removed
-
relatorio/templates/opendocument.py
r84 r89 11 11 # This program is distributed in the hope that it will be useful, but WITHOUT 12 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 13 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 14 14 # details. 15 15 # … … 42 42 ChartTemplate = type(None) 43 43 44 GENSHI_EXPR = re.compile(r'''((/)?(for|choose|otherwise|when|if|with)\s*(\s(\w+)=["'](.*)["']|$)|.*)''') 44 GENSHI_EXPR = re.compile(r''' 45 (/)? # is this a closing tag? 46 (for|choose|otherwise|when|if|with) # tag directive 47 \s* 48 (?:\s(\w+)=["'](.*)["']|$) # match a single attr & its value 49 | 50 .* # or anything else 51 ''', re.VERBOSE) 52 45 53 EXTENSIONS = {'image/png': 'png', 46 54 'image/jpeg': 'jpg', … … 65 73 66 74 class ImageHref: 67 "A class used to add image in the odf zipfile"68 75 "A class used to add images in the odf zipfile" 76 69 77 def __init__(self, zfile, context): 70 78 self.zip = zfile … … 72 80 73 81 def __call__(self, expr, name): 82 #FIXME: name argument is unused 74 83 bitstream, mimetype = expr 75 84 if isinstance(bitstream, Report): … … 97 106 def _parse(self, source, encoding): 98 107 """parses the odf file. 99 108 100 109 It adds genshi directives and finds the inner docs. 101 110 """ … … 115 124 content = zf.read(c_path) 116 125 styles = zf.read(s_path) 117 126 118 127 c_parsed = template._parse(self.insert_directives(zf.read(c_path)), 119 128 encoding) … … 147 156 def _invert_style(self, tree): 148 157 "inverts the text:a and text:span" 149 xpath_expr = "//text:a[starts-with(@xlink:href, 'relatorio://')]" \158 xpath_expr = "//text:a[starts-with(@xlink:href, 'relatorio://')]" \ 150 159 "/text:span" 151 160 for span in tree.xpath(xpath_expr, namespaces=self.namespaces): … … 163 172 # If this node href matches a genshi directive it is kept for further 164 173 # processing. 165 r_statements, genshi_dir = [], []166 174 xlink_href_attrib = '{%s}href' % self.namespaces['xlink'] 167 175 text_a = '{%s}a' % self.namespaces['text'] 168 176 placeholder = '{%s}placeholder' % self.namespaces['text'] 169 170 177 s_xpath = "//text:a[starts-with(@xlink:href, 'relatorio://')]" \ 171 178 "| //text:placeholder" 179 180 r_statements = [] 181 opened_tags = [] 182 # We map each opening tag with its closing tag 183 closing_tags = {} 172 184 for statement in tree.xpath(s_xpath, namespaces=self.namespaces): 173 185 if statement.tag == placeholder: … … 182 194 warnings.warn('No statement text in %s' % self.filepath) 183 195 elif expr != statement.text and statement.tag == text_a: 184 warnings.warn('url and text do not match in %s: %s != %s' 196 warnings.warn('url and text do not match in %s: %s != %s' 185 197 % (self.filepath, expr, 186 198 statement.text.encode('utf-8'))) 187 199 188 expr, closing, directive, _, attr, attr_val = \200 closing, directive, attr, attr_val = \ 189 201 GENSHI_EXPR.match(expr).groups() 202 is_opening = closing != '/' 190 203 if directive is not None: 191 genshi_dir.append((statement, closing)) 192 r_statements.append((statement, 193 (expr, closing, directive, attr, attr_val))) 194 195 return r_statements, genshi_dir 204 # map closing tags with their opening tag 205 if is_opening: 206 opened_tags.append(statement) 207 else: 208 closing_tags[id(opened_tags.pop())] = statement 209 # - we operate only on opening statements 210 if is_opening: 211 r_statements.append((statement, 212 (expr, directive, attr, attr_val)) 213 ) 214 assert not opened_tags 215 216 return r_statements, closing_tags 196 217 197 218 def _handle_relatorio_tags(self, tree): … … 207 228 genshi_replace = '{%s}replace' % self.namespaces['py'] 208 229 209 r_statements, genshi_directives = self._relatorio_statements(tree) 210 # We match the opening and closing directives together 211 idx = 0 212 genshi_pairs, inserted = [], [] 213 for statement, closing in genshi_directives: 214 if closing is None: 215 genshi_pairs.append([statement, None]) 216 inserted.append(idx) 217 idx += 1 218 else: 219 genshi_pairs[inserted.pop()][1] = statement 230 r_statements, closing_tags = self._relatorio_statements(tree) 220 231 221 232 for r_node, parsed in r_statements: 222 expr, c_dir, directive, attr, a_val = parsed 223 233 expr, directive, attr, a_val = parsed 234 235 # If the node is a genshi directive statement: 224 236 if directive is not None: 225 # If the node is a genshi directive statement: 226 # - we operate only on opening statement 227 # - we find the nearest ancestor of the closing and opening 228 # statement 229 # - we create a <py:xxx> node 230 # - we add all the node between the opening and closing 231 # statements to this new node 232 # - we replace the opening statement by the <py:for> node 233 # - we delete the closing statement 234 235 if c_dir is not None: 236 # pass the closing statements 237 continue 238 for pair in genshi_pairs: 239 if pair[0] == r_node: 240 opening, closing = pair 241 break 242 243 o_ancestors = list(opening.iterancestors()) 237 238 opening = r_node 239 closing = closing_tags[id(r_node)] 240 241 # - we find the nearest common ancestor of the closing and 242 # opening statements 243 o_ancestors = [] 244 244 c_ancestors = list(closing.iterancestors()) 245 for node in o_ancestors: 246 if node in c_ancestors: 245 ancestor = None 246 for node in opening.iterancestors(): 247 try: 248 idx = c_ancestors.index(node) 249 assert c_ancestors[idx] == node 250 del c_ancestors[idx:] 247 251 ancestor = node 248 252 break 249 253 except ValueError: 254 pass 255 o_ancestors.append(node) 256 assert ancestor is not None, \ 257 "No common ancestor found for opening and closing tag" 258 259 # - we create a <py:xxx> node 250 260 genshi_node = EtreeElement('{%s}%s' % (self.namespaces['py'], 251 directive), 261 directive), 252 262 attrib={attr: a_val}, 253 263 nsmap=self.namespaces) 264 265 # - we add all the nodes between the opening and closing 266 # statements to this new node 254 267 can_append = False 255 268 for node in ancestor.iterchildren(): 256 269 if node in o_ancestors: 257 270 outermost_o_ancestor = node 271 assert outermost_o_ancestor == o_ancestors[-1] 258 272 can_append = True 259 273 continue 260 274 if node in c_ancestors: 261 275 outermost_c_ancestor = node 276 assert outermost_c_ancestor == c_ancestors[-1] 262 277 break 263 278 if can_append: 279 # we are between the opening and closing node 264 280 genshi_node.append(node) 281 282 # - we replace the opening statement by the <py:xxx> node 265 283 ancestor.replace(outermost_o_ancestor, genshi_node) 284 285 # - we delete the closing statement (and its ancestors) 266 286 ancestor.remove(outermost_c_ancestor) 267 287 else: … … 281 301 282 302 def _handle_images(self, tree): 283 "replaces all draw:frame named 'image: ...' by a draw:image node" 303 "replaces all draw:frame named 'image: ...' by a draw:image node" 284 304 draw_name = '{%s}name' % self.namespaces['draw'] 285 305 draw_image = '{%s}image' % self.namespaces['draw'] … … 289 309 d_name = draw.attrib[draw_name] 290 310 attr_expr = "make_href(%s, %r)" % (d_name[7:], d_name[7:]) 291 image_node = EtreeElement(draw_image, 311 image_node = EtreeElement(draw_image, 292 312 attrib={python_attrs: attr_expr}, 293 313 nsmap=self.namespaces) … … 333 353 elif f_info.filename in files: 334 354 stream = files[f_info.filename] 335 self.outzip.writestr(f_info.filename, 355 self.outzip.writestr(f_info.filename, 336 356 output_encode(self.xml_serializer(stream))) 337 357 else:
