def _convert_table_rows(container, node, context, cell_style=None): for row in node: if row.tagname != "row": printwarn('node "%s" not supported in thead/tbody' % row.tagname) continue odf_row = odf_create_row() container.append(odf_row) for entry in row: if entry.tagname != "entry": printwarn('node "%s" not supported in row' % entry.tagname) continue # Create a new odf_cell odf_cell = odf_create_cell(cell_type="string", style=cell_style) odf_row.append(odf_cell) # XXX We don't add table:covered-table-cell ! # It's bad but OO can nevertheless load the file morecols = entry.get("morecols") if morecols is not None: morecols = int(morecols) + 1 odf_cell.set_attribute('table:number-columns-spanned', str(morecols)) morerows = entry.get("morerows") if morerows is not None: morerows = int(morerows) + 1 odf_cell.set_attribute('table:number-rows-spanned', str(morerows)) push_convert_pop(entry, odf_cell, context)
def convert_list(node, context, list_type): # Reuse template styles if list_type == "enumerated": style_name = "Numbering_20_1" else: style_name = "List_20_1" odf_list = odf_create_list(style=style_name) context['top'].append(odf_list) # Save the current top old_top = context['top'] for item in node: if item.tagname != "list_item": printwarn("node not supported: %s" % item.tagname) continue # Create a new item odf_item = odf_create_list_item() odf_list.append(odf_item) # A new top context['top'] = odf_item for child in item: convert_node(child, context) # And restore the top context['top'] = old_top
def convert_definition_list(node, context): """Convert a list of term/definition pairs to styled paragraphs. The "Definition List Term" style is looked for term paragraphs, and the "Definition List Definition" style is looked for definition paragraphs. """ styles = context['styles'] term_style = _get_term_style(context).get_name() definition_style = _get_definition_style(context).get_name() for item in node: if item.tagname != "definition_list_item": printwarn('node "%s" not supported in definition_list' % (item.tagname)) continue for child in item: tagname = child.tagname if tagname == "term": paragraph = odf_create_paragraph(style=term_style) context['top'].append(paragraph) push_convert_pop(child, paragraph, context) elif tagname == "definition": # Push a style on the stack for next paragraphs to use styles['paragraph'] = definition_style for subchildren in child: convert_node(subchildren, context) # Pop the paragraph style (may already be popped) styles.pop('paragraph', None) else: printwarn('node "%s" not supported in definition_list_item' % tagname)
def convert_definition_list(node, context): """Convert a list of term/definition pairs to styled paragraphs. The "Definition List Term" style is looked for term paragraphs, and the "Definition List Definition" style is looked for definition paragraphs. """ styles = context['styles'] term_style = _get_term_style(context).get_name() definition_style = _get_definition_style(context).get_name() for item in node: if item.tagname != "definition_list_item": printwarn('node "%s" not supported in definition_list' % ( item.tagname)) continue for child in item: tagname = child.tagname if tagname == "term": paragraph = odf_create_paragraph(style=term_style) context['top'].append(paragraph) push_convert_pop(child, paragraph, context) elif tagname == "definition": # Push a style on the stack for next paragraphs to use styles['paragraph'] = definition_style for subchildren in child: convert_node(subchildren, context) # Pop the paragraph style (may already be popped) styles.pop('paragraph', None) else: printwarn('node "%s" not supported in definition_list_item' % tagname)
def convert_node(node, context): tagname = node.tagname convert_method = convert_methods.get(tagname) if convert_method is not None: convert_method(node, context) else: printwarn("node not supported: %s" % tagname)
def _add_image(image, caption, context, width=None, height=None): # Load the image to find its size encoding = stdout.encoding if stdout.encoding is not None else "utf-8" try: image_file = open(image.encode(encoding), 'rb') image_object = Image.open(image_file) except (UnicodeEncodeError, IOError, OverflowError), e: printwarn('unable to insert the image "%s": %s' % (image, e)) return
def convert_node(node, context): tagname = node.tagname # From the context convert_method = context.get('convert_' + tagname) if convert_method is None: # Default method convert_method = __convert_methods.get(tagname) if convert_method is not None: return convert_method(node, context) message = "node not supported: %s" % tagname if context['strict']: raise ValueError, message printwarn(message)
def convert_topic(node, context): # Reset the top to body context['top'] = context['body'] # Yet an other TOC ? if context['skip_toc']: return if context['toc'] is not None: printwarn("a TOC is already inserted") return title = node.next_node(condition=nodes.title).astext() toc = odf_create_toc(title=title) context['body'].append(toc) context['toc'] = toc
def __init__(self, path_or_file): want_folder = False if isinstance(path_or_file, basestring): # Path self.path = path = path_or_file if os.path.isdir(path): # opening a folder want_folder = True else: file = open(path, 'rb') else: # File-like assumed self.path = None file = path_or_file if want_folder: self.__data = data = path self.__packaging = 'folder' try: mimetype, timestamp = self.__get_folder_part('mimetype') except: printwarn( "corrupted or not an OpenDocument folder (missing mimetype)" ) mimetype = '' timestamp = None if mimetype not in ODF_MIMETYPES: message = 'Document of unknown type "%s", trying with ODF_TEXT.' % mimetype printwarn(message) mimetype = ODF_EXTENSIONS['odt'] self.__parts = {'mimetype': mimetype} self.__parts_ts = {'mimetype': timestamp} else: self.__data = data = file.read() zip_expected = data[:4] == 'PK\x03\x04' # Most probably zipped document try: mimetype = self.__get_zip_part('mimetype') self.__packaging = 'zip' except BadZipfile: if zip_expected: raise ValueError, "corrupted or not an OpenDocument archive" # Maybe XML document try: mimetype = self.__get_xml_part('mimetype') except ValueError: raise ValueError, "bad OpenDocument format" self.__packaging = 'flat' if mimetype not in ODF_MIMETYPES: message = 'Document of unknown type "%s"' % mimetype raise ValueError, message self.__parts = {'mimetype': mimetype}
def __init__(self, path_or_file): want_folder = False if isinstance(path_or_file, basestring): # Path self.path = path = path_or_file if os.path.isdir(path): # opening a folder want_folder = True else: file = open(path, 'rb') else: # File-like assumed self.path = None file = path_or_file if want_folder: self.__data = data = path self.__packaging = 'folder' try: mimetype, timestamp = self.__get_folder_part('mimetype') except: printwarn ("corrupted or not an OpenDocument folder (missing mimetype)") mimetype = '' timestamp = None if mimetype not in ODF_MIMETYPES: message = 'Document of unknown type "%s", trying with ODF_TEXT.' % mimetype printwarn(message) mimetype = ODF_EXTENSIONS['odt'] self.__parts = {'mimetype': mimetype} self.__parts_ts = {'mimetype': timestamp} else: self.__data = data = file.read() zip_expected = data[:4] == 'PK\x03\x04' # Most probably zipped document try: mimetype = self.__get_zip_part('mimetype') self.__packaging = 'zip' except BadZipfile: if zip_expected: raise ValueError("corrupted or not an OpenDocument archive") # Maybe XML document try: mimetype = self.__get_xml_part('mimetype') except ValueError: raise ValueError("bad OpenDocument format") self.__packaging = 'flat' if mimetype not in ODF_MIMETYPES: message = 'Document of unknown type "%s"' % mimetype raise ValueError(message) self.__parts = {'mimetype': mimetype}
def _do_backup(self, target): parts = target.split('.', 1) if len(parts) == 1: back_file = target + '.backup' else: back_file = parts[0] + '.backup.' + parts[1] if os.path.exists(target): if os.path.isdir(back_file): try: shutil.rmtree(back_file) except Exception as e: printwarn(str(e)) try: shutil.move(target, back_file) except Exception as e: printwarn(str(e))
def convert_literal_block(node, context): paragraph = odf_create_paragraph(style="Preformatted_20_Text") context['top'].append(paragraph) # Convert for child in node: # Only text if child.tagname != "#text": printwarn('node "%s" not supported in literal block' % ( child.tagname)) continue text = child.astext() tmp = [] spaces = 0 for c in text: if c == '\n': if tmp: tmp = u"".join(tmp) paragraph.append(tmp) tmp = [] spaces = 0 paragraph.append(odf_create_line_break()) elif c == '\r': continue elif c == ' ': spaces += 1 elif c == '\t': # Tab = 4 spaces spaces += 4 else: if spaces >= 2: if tmp: tmp = u"".join(tmp) paragraph.append(tmp) tmp = [] paragraph.append(' ') paragraph.append( odf_create_spaces(spaces - 1)) spaces = 0 elif spaces == 1: tmp.append(' ') spaces = 0 tmp.append(c) if tmp: tmp = u"".join(tmp) paragraph.append(tmp)
def convert_table(node, context): cell_style = _get_cell_style(context).get_name() for tgroup in node: if tgroup.tagname != "tgroup": printwarn('node "%s" not supported in table' % tgroup.tagname) continue columns_number = 0 odf_table = None for child in tgroup: tagname = child.tagname if tagname == "thead" or tagname == "tbody": # Create a new table with the info columns_number if odf_table is None: context['tables_number'] += 1 # TODO Make it possible directly with odf_create_table odf_table = odf_create_table(name="table%d" % context['tables_number']) columns = odf_create_column(repeated=columns_number) odf_table.append(columns) # Convert! if tagname == "thead": header = odf_create_header_rows() odf_table.append(header) _convert_table_rows(header, child, context, cell_style=cell_style) else: _convert_table_rows(odf_table, child, context, cell_style=cell_style) elif tagname == "colspec": columns_number += 1 else: printwarn('node "%s" not supported in tgroup' % (child.tagname)) continue context['top'].append(odf_table)
def convert_figure(node, context): image = None caption = None width = None height = None for child in node: tagname = child.tagname if tagname == "image": if image is not None: printwarn("unexpected duplicate image in a figure") continue image = child.get("uri") width = child.get('width') height = child.get('height') elif tagname == "caption": if caption is not None: printwarn("unexpected duplicate caption in a figure") continue caption = child.astext() _add_image(image, caption, context, width=width, height=height)
def convert_table(node, context): cell_style = _get_cell_style(context).get_name() for tgroup in node: if tgroup.tagname != "tgroup": printwarn('node "%s" not supported in table' % tgroup.tagname) continue columns_number = 0 odf_table = None for child in tgroup: tagname = child.tagname if tagname == "thead" or tagname == "tbody": # Create a new table with the info columns_number if odf_table is None: context['tables_number'] += 1 # TODO Make it possible directly with odf_create_table odf_table = odf_create_table(name="table%d" % context['tables_number']) columns = odf_create_column(repeated=columns_number) odf_table.append(columns) # Convert! if tagname == "thead": header = odf_create_header_rows() odf_table.append(header) _convert_table_rows(header, child, context, cell_style=cell_style) else: _convert_table_rows(odf_table, child, context, cell_style=cell_style) elif tagname == "colspec": columns_number += 1 else: printwarn('node "%s" not supported in tgroup' % ( child.tagname)) continue context['top'].append(odf_table)
def convert_footnote(node, context): # XXX ids is a list ?? refid = node.get("ids")[0] # Find the footnote footnotes = context['footnotes'] if refid not in footnotes: printwarn('unknown footnote "%s"' % refid) return footnote_body = footnotes[refid].get_element("text:note-body") # Save the current top old_top = context['top'] # Fill the note context['top'] = footnote_body for child in node: # We skip the label (already added) if child.tagname == "label": continue convert_node(child, context) # And restore the top context['top'] = old_top
def __save_folder(self, folder): """Save a folder ODF from the available parts. """ encoding = sys.getfilesystemencoding() def dump(path, content): try: path = path.decode(encoding) except: pass file_name = os.path.join(folder, path) dir_name = os.path.dirname(file_name) if not os.path.exists(dir_name): os.makedirs(dir_name, mode=0755) if path.endswith(u'/') : # folder if not os.path.isdir(file_name): os.makedirs(file_name.encode(encoding), mode=0777) else: open(file_name.encode(encoding), 'wb', 0666).write(content) if isinstance(folder, basestring) and not isinstance(folder, unicode): folder = folder.decode(encoding) # Parts were loaded by "save" parts = self.__parts # Parts to save, except manifest at the end part_names = parts.keys() try: part_names.remove(ODF_MANIFEST) except KeyError: printwarn(u"missing '%s'" % ODF_MANIFEST) # "Pretty-save" parts in some order # mimetype requires to be first and uncompressed try: dump('mimetype', parts['mimetype']) part_names.remove('mimetype') except: printwarn("missing 'mimetype'") # XML parts for path in ODF_CONTENT, ODF_META, ODF_SETTINGS, ODF_STYLES: if path not in parts: printwarn("missing '%s'" % path) continue dump(path, parts[path]) part_names.remove(path) # Everything else for path in part_names: data = parts[path] if data is None: # Deleted continue dump(path, data) # Manifest dump(ODF_MANIFEST, parts[ODF_MANIFEST])
def __save_zip(self, file): """Save a Zip ODF from the available parts. """ # Parts were loaded by "save" parts = self.__parts compression = ZIP_DEFLATED try: filezip = ZipFile(file, 'w', compression=compression) except RuntimeError: # No zlib module compression = ZIP_STORED filezip = ZipFile(file, 'w', compression=compression) # Parts to save, except manifest at the end part_names = parts.keys() try: part_names.remove(ODF_MANIFEST) except KeyError: printwarn("missing '%s'" % ODF_MANIFEST) # "Pretty-save" parts in some order # mimetype requires to be first and uncompressed filezip.compression = ZIP_STORED try: filezip.writestr('mimetype', parts['mimetype']) filezip.compression = compression part_names.remove('mimetype') except: printwarn("missing 'mimetype'") # XML parts for path in ODF_CONTENT, ODF_META, ODF_SETTINGS, ODF_STYLES: if path not in parts: printwarn("missing '%s'" % path) continue filezip.writestr(path, parts[path]) part_names.remove(path) # Everything else for path in part_names: data = parts[path] if data is None: # Deleted continue filezip.writestr(path, data) # Manifest filezip.writestr(ODF_MANIFEST, parts[ODF_MANIFEST]) filezip.close()
def save(self, target=None, packaging=None, backup=False): """Save the container to the given target, a path or a file-like object. Package the output document in the same format than this document, unless "packaging" is different. Arguments: target -- str or file-like packaging -- 'zip' or 'flat', or for debugging purpose 'folder' backup -- boolean """ if isinstance(target, basestring) and not isinstance(target, unicode): encoding = sys.getfilesystemencoding() target = target.decode(encoding) parts = self.__parts # Packaging if packaging is None: if self.__packaging: packaging = self.__packaging else: packaging = 'zip' # default packaging = packaging.strip().lower() if packaging not in ('zip', 'flat', 'folder'): raise ValueError('packaging type "%s" not supported' % packaging) # Load parts else they will be considered deleted for path in self.get_parts(): if path not in parts: self.get_part(path) # Open output file close_after = False if target is None: target = self.path if isinstance(target, basestring): while target.endswith(os.sep): target = target[:-1] while target.endswith('.folder'): target = target.split('.folder', 1)[0] if packaging in ('zip', 'flat'): if isinstance(target, basestring): if backup: self._do_backup(target) dest_file = open(target, 'wb') close_after = True else: dest_file = target if packaging == 'folder': if not isinstance(target, basestring): raise ValueError("Saving in folder format requires a folder " "name, not %s." % target) if not target.endswith('.folder'): target = target + '.folder' if backup: self._do_backup(target) else: if os.path.exists(target): try: shutil.rmtree(target) except Exception as e: printwarn(str(e)) os.mkdir(target, 0777) dest_file = target # Serialize if packaging == 'zip': self.__save_zip(dest_file) elif packaging == 'flat': self.__save_xml(dest_file) else: # folder self.__save_folder(dest_file) # Close files we opened ourselves if close_after: dest_file.close()