diff options
author | dkuhlman <dkuhlman@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2008-10-23 23:22:13 +0000 |
---|---|---|
committer | dkuhlman <dkuhlman@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2008-10-23 23:22:13 +0000 |
commit | cd36c04f9940b12f4ae0a68d56e7c71406b55ab9 (patch) | |
tree | e7c331a14432baec2987553d9d55e4da9358d0c8 | |
parent | ad4f61a485765e7f09a226613caf1e92ffbf6c5a (diff) | |
download | docutils-cd36c04f9940b12f4ae0a68d56e7c71406b55ab9.tar.gz |
Work on footnotes and citations and various fixes
git-svn-id: http://svn.code.sf.net/p/docutils/code/trunk@5683 929543f6-e4f2-0310-98a6-ba3bd3dd1d04
-rw-r--r-- | sandbox/OpenDocument/README.txt | 22 | ||||
-rw-r--r-- | sandbox/OpenDocument/docs/odtwriter.txt | 112 | ||||
-rw-r--r-- | sandbox/OpenDocument/odtwriter/__init__.py | 641 | ||||
-rw-r--r-- | sandbox/OpenDocument/odtwriter/styles.odt | bin | 9560 -> 9749 bytes | |||
-rw-r--r-- | sandbox/OpenDocument/setup.py | 2 |
5 files changed, 400 insertions, 377 deletions
diff --git a/sandbox/OpenDocument/README.txt b/sandbox/OpenDocument/README.txt index 184ecb910..ca56ddd7f 100644 --- a/sandbox/OpenDocument/README.txt +++ b/sandbox/OpenDocument/README.txt @@ -38,6 +38,28 @@ For more information on Docutils, see: http://docutils.sourceforge.net/ History ======= +2008/10/23 -- Version 1.3c +-------------------------- + +Fixes for line blocks. These are now indented correctly, I +believe, even when inside a block quote. There are new styles for +line blocks (rststyle-lineblock1, ...). See the doc. + +Fixes to footnotes. Auto-numbered and auto-symbol footnotes seem +to work correctly now. There is a constant (footnote_chars) +containing the symbols (*, **, ***, ++, etc). + +Added support for citations. + +Fixed sections -- Now start at correct level. + +Added styles rststyle-title and rststyle-subtitle. + +Various source code clean-ups. + +Added style rststyle-footnote and rststyle-citation. + + 2008/07/08 -- Version 1.3b -------------------------- diff --git a/sandbox/OpenDocument/docs/odtwriter.txt b/sandbox/OpenDocument/docs/odtwriter.txt index 7531944a3..13e92d0be 100644 --- a/sandbox/OpenDocument/docs/odtwriter.txt +++ b/sandbox/OpenDocument/docs/odtwriter.txt @@ -8,7 +8,7 @@ Odtwriter for Docutils :address: dkuhlman@rexx.com http://www.rexx.com/~dkuhlman -:revision: 1.3b +:revision: 1.3c :date: |date| .. |date| date:: %B %d, %Y @@ -54,8 +54,8 @@ Where to get it The source distribution of ODF/ODT writer for Docutils is here: -`http://www.rexx.com/~dkuhlman/odtwriter-1.3b.tar.gz -<http://www.rexx.com/~dkuhlman/odtwriter-1.3b.tar.gz>`_. +`http://www.rexx.com/~dkuhlman/odtwriter-1.3c.tar.gz +<http://www.rexx.com/~dkuhlman/odtwriter-1.3c.tar.gz>`_. ``odtwriter`` is also available via Subversion from the Docutils repository under @@ -145,19 +145,35 @@ The following command line flags are specific to ``odtwriter``: Specify a stylesheet file, relative to the current working directory. The path is adjusted relative to the output ODF file. Overrides --stylesheet. - Default: "/usr/local/lib/python2.5/site- + Default: "/usr/local/lib/python2.6/site- packages/docutils/writers/odtwriter/styles.odt" +--odf-config-file=<file> + Specify a configuration/mapping file relative to the + current working directory for additional ODF options. + In particular, this file may contain a section named + "Formats" that maps default style names to names to be + used in the resulting output file allowing for + adhering to external standards. For more info and the + format of the configuration/mapping file, see the + odtwriter doc. --cloak-email-addresses Obfuscate email addresses to confuse harvesters while still keeping email links usable with standards- compliant browsers. +--no-cloak-email-addresses + Do not obfuscate email addresses. --table-border-thickness=TABLE_BORDER_THICKNESS Specify the thickness of table borders in thousands of a cm. Default is 35. --add-syntax-highlighting - Add syntax highlighting in literal code - blocks. Default is no. Requires installation of - Pygments. + Add syntax highlighting in literal code blocks. +--no-add-syntax-highlighting + Do not add syntax highlighting in literal code blocks. + (default) +--create-sections Create sections for headers. (default) +--no-create-sections Do not create sections for headers. +--create-links Create links. +--no-create-links Do not create links. (default) Styles @@ -286,18 +302,10 @@ rststyle-footer (``--date`` and ``--time``), and view source link (``--source-link`` and ``--source-url=URL``). -rststyle-footnote - The style for footnotes. This style affects the footnote - content, not the footnote reference in the body of the document. - rststyle-header The style for headers. The header content originates from the ``..header::`` directive. -rststyle-heading{1|2|3|4|5} - Headings (sub-titles). Five levels of sub-headings are - provided: rststyle-heading1 through rststyle-heading5. - rststyle-highlights The style for highlightss, for example, the body of an ``.. highlights::`` directive. Derived from @@ -320,11 +328,6 @@ rststyle-textbody Normal text. The style for paragraphs. Derived from the ``Text body`` style in ``oowriter``. -rststyle-lineblock - The style for line blocks created either with the ``.. line-block::`` - directive or with vertical bars. Derived from the - ``rststyle-textbody`` style. - Character styles ~~~~~~~~~~~~~~~~ @@ -492,8 +495,6 @@ the ``rststyle-heading1`` style to the generated rubric:: :class: rststyle-heading1 - - Table styles ~~~~~~~~~~~~ @@ -512,6 +513,73 @@ in the generated ``.odt``. These styles have names prefixed with http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=office +Line block styles +~~~~~~~~~~~~~~~~~~ + +The line block styles wrap the various nested levels of line +blocks. There is one line block style for each indent level. + +rststyle-lineblock1 + Line block style for line block with no indent. + +rststyle-lineblock2 + Line block style for line block indented 1 level. + +rststyle-lineblock3 + Line block style for line block indented 2 levels. + +rststyle-lineblock4 + Line block style for line block indented 3 levels. + +rststyle-lineblock5 + Line block style for line block indented 4 levels. + +rststyle-lineblock6 + Line block style for line block indented 5 levels. + +Notes: + +- ``odtwriter`` does not check for a maximum level of indents + within line blocks. Therefore, you can define additional line + block styles for additional levels if you need them. Define + these styles with the names ``rststyle-lineblock7``, + ``rststyle-lineblock8``, ... + +- Since the line block style is used to create indentation, a line + block that is inside a block quote will use + ``rststyle-lineblock2`` as its first level of indentation. + + +Footnote and citation styles +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +rststyle-footnote + The style for footnotes. This style affects the footnote + content, *not* the footnote reference in the body of the document. + +rststyle-citation + The style for citations. This style affects the citation + content, *not* the citation reference in the body of the document. + You might need to adjust the indentation in this style + depending on the length of the label used in your citations. + +Heading and title styles +~~~~~~~~~~~~~~~~~~~~~~~~~ + +rststyle-heading{1|2|3|4|5} + The styles for headings (section titles and sub-titles). Five + levels of sub-headings are provided: rststyle-heading1 through + rststyle-heading5. + +rststyle-title + The style for the document title. + +rststyle-subtitle + The style for the document sub-title. + + + + Defining and using a custom stylesheet --------------------------------------- diff --git a/sandbox/OpenDocument/odtwriter/__init__.py b/sandbox/OpenDocument/odtwriter/__init__.py index 4a6d13f93..0c1e8fce3 100644 --- a/sandbox/OpenDocument/odtwriter/__init__.py +++ b/sandbox/OpenDocument/odtwriter/__init__.py @@ -20,6 +20,7 @@ import re import StringIO import inspect import imp +import copy import docutils from docutils import frontend, nodes, utils, writers, languages from docutils.parsers import rst @@ -31,13 +32,11 @@ WhichElementTree = '' try: # 1. Try to use lxml. from lxml import etree - #print '*** using lxml' WhichElementTree = 'lxml' except ImportError, e: try: # 2. Try to use ElementTree from the Python standard library. from xml.etree import ElementTree as etree - #print '*** using ElementTree' WhichElementTree = 'elementtree' except ImportError, e: try: @@ -157,12 +156,19 @@ try: except ImportError, exp: Image = None +## from IPython.Shell import IPShellEmbed +## args = ['-pdb', '-pi1', 'In <\\#>: ', '-pi2', ' .\\D.: ', +## '-po', 'Out<\\#>: ', '-nosep'] +## ipshell = IPShellEmbed(args, +## banner = 'Entering IPython. Press Ctrl-D to exit.', +## exit_msg = 'Leaving Interpreter, back to program.') + from IPython.Shell import IPShellEmbed args = ['-pdb', '-pi1', 'In <\\#>: ', '-pi2', ' .\\D.: ', - '-po', 'Out<\\#>: ', '-nosep'] + '-po', 'Out<\\#>: ', '-nosep'] ipshell = IPShellEmbed(args, - banner = 'Entering IPython. Press Ctrl-D to exit.', - exit_msg = 'Leaving Interpreter, back to program.') + banner = 'Entering IPython. Press Ctrl-D to exit.', + exit_msg = 'Leaving Interpreter, back to program.') # @@ -192,9 +198,6 @@ SPACES_PATTERN = re.compile(r'( +)') TABS_PATTERN = re.compile(r'(\t+)') FILL_PAT1 = re.compile(r'^ +') FILL_PAT2 = re.compile(r' {2,}') -# Match a section number followed by 3 \xa0 bytes. -# Note that we actually check for the class "auto-toc" instead. -#SECTNUM_PAT = re.compile(r'^\d*(\.\d*)*\240\240\240') TableStylePrefix = 'Table' @@ -389,7 +392,6 @@ def add_ns(tag, nsdict=CNSD): if ns is None: raise RuntimeError, 'Invalid namespace prefix: %s' % nstag tag = '{%s}%s' % (ns, name,) - #print '*** tag: "%s"' % tag return tag def ToString(et): @@ -416,56 +418,34 @@ def escape_cdata(text): # Classes # -# Does this version of Docutils has Directive support? -if hasattr(rst, 'Directive'): +# +# Class to control syntax highlighting. +class SyntaxHighlightCodeBlock(rst.Directive): + required_arguments = 1 + optional_arguments = 0 + has_content = True # - # Class to control syntax highlighting. -## class SyntaxHighlight(rst.Directive): -## required_arguments = 1 -## optional_arguments = 0 -## # -## # See visit_field for code that processes the node created here. -## def run(self): -## arguments = ' '.join(self.arguments) -## paragraph = nodes.paragraph(arguments, arguments) -## field_body = nodes.field_body() -## field_body += paragraph -## paragraph = nodes.paragraph('syntaxhighlight', 'syntaxhighlight') -## field_name = nodes.field_name() -## field_name += paragraph -## field = nodes.field() -## field += field_name -## field += field_body -## return [field] -## -## rst.directives.register_directive('sourcecode', SyntaxHighlight) - - class SyntaxHighlightCodeBlock(rst.Directive): - required_arguments = 1 - optional_arguments = 0 - has_content = True - # - # See visit_literal_block for code that processes the node - # created here. - def run(self): - language = self.arguments[0] - code_block = nodes.literal_block(classes=["code-block", language], - language=language) - lines = self.content - content = '\n'.join(lines) - text_node = nodes.Text(content) - code_block.append(text_node) - # Mark this node for high-lighting so that visit_literal_block - # will be able to hight-light those produced here and - # *not* high-light regular literal blocks (:: in reST). - code_block['hilight'] = True - #import pdb; pdb.set_trace() - return [code_block] - - rst.directives.register_directive('sourcecode', SyntaxHighlightCodeBlock) - rst.directives.register_directive('code', SyntaxHighlightCodeBlock) - rst.directives.register_directive('code-block', SyntaxHighlightCodeBlock) + # See visit_literal_block for code that processes the node + # created here. + def run(self): + language = self.arguments[0] + code_block = nodes.literal_block(classes=["code-block", language], + language=language) + lines = self.content + content = '\n'.join(lines) + text_node = nodes.Text(content) + code_block.append(text_node) + # Mark this node for high-lighting so that visit_literal_block + # will be able to hight-light those produced here and + # *not* high-light regular literal blocks (:: in reST). + code_block['hilight'] = True + #import pdb; pdb.set_trace() + return [code_block] +rst.directives.register_directive('sourcecode', SyntaxHighlightCodeBlock) +rst.directives.register_directive('code', SyntaxHighlightCodeBlock) + + rst.directives.register_directive('code-block', SyntaxHighlightCodeBlock) # # Register directives defined in a module named "odtwriter_plugins". @@ -537,7 +517,6 @@ class Writer(writers.Writer): """Formats this writer supports.""" default_stylesheet = 'styles' + EXTENSION -## default_plugins_name = 'docutils_plugins' default_stylesheet_path = utils.relative_path( os.path.join(os.getcwd(), 'dummy'), @@ -549,80 +528,6 @@ class Writer(writers.Writer): os.path.join(os.getcwd(), 'dummy'), os.path.join(os.path.dirname(__file__), default_template)) -## settings_spec = ( -## 'ODF-Specific Options', -## None, -## (('Specify the template file (UTF-8 encoded). Default is "%s".' -## % default_template_path, -## ['--template'], -## {'default': default_template_path, 'metavar': '<file>'}), -## ('Specify a stylesheet URL, used verbatim. Overrides ' -## '--stylesheet-path.', -## ['--stylesheet'], -## {'metavar': '<URL>', 'overrides': 'stylesheet_path'}), -## ('Specify a stylesheet file, relative to the current working ' -## 'directory. The path is adjusted relative to the output ODF ' -## 'file. Overrides --stylesheet. Default: "%s"' -## % default_stylesheet_path, -## ['--stylesheet-path'], -## {'metavar': '<file>', 'overrides': 'stylesheet', -## 'default': default_stylesheet_path}), -## ('Specify the initial header level. Default is 1 for "<h1>". ' -## 'Does not affect document title & subtitle (see --no-doc-title).', -## ['--initial-header-level'], -## {'choices': '1 2 3 4 5 6'.split(), 'default': '1', -## 'metavar': '<level>'}), -## ('Specify the maximum width (in characters) for one-column field ' -## 'names. Longer field names will span an entire row of the table ' -## 'used to render the field list. Default is 14 characters. ' -## 'Use 0 for "no limit".', -## ['--field-name-limit'], -## {'default': 14, 'metavar': '<level>', -## 'validator': frontend.validate_nonnegative_int}), -## ('Specify the maximum width (in characters) for options in option ' -## 'lists. Longer options will span an entire row of the table used ' -## 'to render the option list. Default is 14 characters. ' -## 'Use 0 for "no limit".', -## ['--option-limit'], -## {'default': 14, 'metavar': '<level>', -## 'validator': frontend.validate_nonnegative_int}), -## ('Format for footnote references: one of "superscript" or ' -## '"brackets". Default is "brackets".', -## ['--footnote-references'], -## {'choices': ['superscript', 'brackets'], 'default': 'brackets', -## 'metavar': '<format>', -## 'overrides': 'trim_footnote_reference_space'}), -## ('Format for block quote attributions: one of "dash" (em-dash ' -## 'prefix), "parentheses"/"parens", or "none". Default is "dash".', -## ['--attribution'], -## {'choices': ['dash', 'parentheses', 'parens', 'none'], -## 'default': 'dash', 'metavar': '<format>'}), -## ('Remove extra vertical whitespace between items of "simple" bullet ' -## 'lists and enumerated lists. Default: enabled.', -## ['--compact-lists'], -## {'default': 1, 'action': 'store_true', -## 'validator': frontend.validate_boolean}), -## ('Disable compact simple bullet and enumerated lists.', -## ['--no-compact-lists'], -## {'dest': 'compact_lists', 'action': 'store_false'}), -## ('Remove extra vertical whitespace between items of simple field ' -## 'lists. Default: enabled.', -## ['--compact-field-lists'], -## {'default': 1, 'action': 'store_true', -## 'validator': frontend.validate_boolean}), -## ('Disable compact simple field lists.', -## ['--no-compact-field-lists'], -## {'dest': 'compact_field_lists', 'action': 'store_false'}), -## ('Omit the XML declaration. Use with caution.', -## ['--no-xml-declaration'], -## {'dest': 'xml_declaration', 'default': 1, 'action': 'store_false', -## 'validator': frontend.validate_boolean}), -## ('Obfuscate email addresses to confuse harvesters while still ' -## 'keeping email links usable with standards-compliant browsers.', -## ['--cloak-email-addresses'], -## {'action': 'store_true', 'validator': frontend.validate_boolean}), -## )) - settings_spec = ( 'ODF-Specific Options', None, @@ -652,42 +557,57 @@ class Writer(writers.Writer): ('Obfuscate email addresses to confuse harvesters while still ' 'keeping email links usable with standards-compliant browsers.', ['--cloak-email-addresses'], - {'default': False, 'action': 'store_true', + {'default': False, + 'action': 'store_true', + 'dest': 'cloak_email_addresses', + 'validator': frontend.validate_boolean}), + ('Do not obfuscate email addresses.', + ['--no-cloak-email-addresses'], + {'default': False, + 'action': 'store_false', + 'dest': 'cloak_email_addresses', 'validator': frontend.validate_boolean}), ('Specify the thickness of table borders in thousands of a cm. ' 'Default is 35.', ['--table-border-thickness'], {'default': 35, 'validator': frontend.validate_nonnegative_int}), - ('Add syntax highlighting in literal code blocks.' - 'Default is No. Requires installation of Pygments.', + ('Add syntax highlighting in literal code blocks.', ['--add-syntax-highlighting'], - {'default': False, 'action': 'store_true', + {'default': False, + 'action': 'store_true', + 'dest': 'add_syntax_highlighting', + 'validator': frontend.validate_boolean}), + ('Do not add syntax highlighting in literal code blocks. (default)', + ['--no-add-syntax-highlighting'], + {'default': False, + 'action': 'store_false', + 'dest': 'add_syntax_highlighting', 'validator': frontend.validate_boolean}), - ('Create sections for headers. ' - 'Default is Yes.', + ('Create sections for headers. (default)', ['--create-sections'], - {'default': True, 'action': 'store_true', + {'default': True, + 'action': 'store_true', + 'dest': 'create_sections', 'validator': frontend.validate_boolean}), - ('Create no sections for headers.', + ('Do not create sections for headers.', ['--no-create-sections'], - {'action': 'store_false', + {'default': True, + 'action': 'store_false', 'dest': 'create_sections', 'validator': frontend.validate_boolean}), - ('Create links. ' - 'Default is No.', + ('Create links.', ['--create-links'], - {'default': False, 'action': 'store_true', + {'default': False, + 'action': 'store_true', + 'dest': 'create_links', 'validator': frontend.validate_boolean}), - ('Create no links.', + ('Do not create links. (default)', ['--no-create-links'], - {'action': 'store_false', + {'default': False, + 'action': 'store_false', 'dest': 'create_links', 'validator': frontend.validate_boolean}), -## ('Specify a plugins/directives module (without .py). ' -## 'Default: "%s"' % default_plugins_name, -## ['--plugins-module-name'], -## {'default': default_plugins_name}), )) settings_defaults = { @@ -886,24 +806,6 @@ class Writer(writers.Writer): class ODFTranslator(nodes.GenericNodeVisitor): -## used_styles = ( -## 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem', -## 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist', -## 'bulletitem', 'bulletlist', 'caption', 'centeredtextbody', 'codeblock', -## 'codeblock-classname', 'codeblock-comment', 'codeblock-functionname', -## 'codeblock-keyword', 'codeblock-name', 'codeblock-number', -## 'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem', -## 'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist', -## 'epigraph-enumitem', 'epigraph-enumlist', 'footer', 'footnote', -## 'header', 'highlights', 'highlights-bulletitem', -## 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist', -## 'horizontalline', 'inlineliteral', 'lineblock', 'quotation', 'rubric', -## 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist', -## 'heading%d', 'admon-%s-hdr', 'admon-%s-body', 'tableoption', -## 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c', -## 'Table%d.%c%d', -## ) - used_styles = ( 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem', 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist', @@ -916,7 +818,7 @@ class ODFTranslator(nodes.GenericNodeVisitor): 'footnote', 'citation', 'header', 'highlights', 'highlights-bulletitem', 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist', - 'horizontalline', 'inlineliteral', 'lineblock', 'quotation', 'rubric', + 'horizontalline', 'inlineliteral', 'quotation', 'rubric', 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist', 'title', 'subtitle', @@ -950,6 +852,12 @@ class ODFTranslator(nodes.GenericNodeVisitor): 'tableoption', 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c', 'Table%d.%c%d', + 'lineblock1', + 'lineblock2', + 'lineblock3', + 'lineblock4', + 'lineblock5', + 'lineblock6', ) def __init__(self, document): @@ -962,7 +870,7 @@ class ODFTranslator(nodes.GenericNodeVisitor): parser = ConfigParser() parser.read(self.settings.odf_config_file) - for ( rststyle, format, ) in parser.items("Formats"): + for rststyle, format in parser.items("Formats"): if rststyle not in self.used_styles: print '***' print ('*** Warning: Style "%s" ' @@ -994,19 +902,6 @@ class ODFTranslator(nodes.GenericNodeVisitor): el = self.generate_content_element(el) self.current_element = el self.body_text_element = el -# test styles -## if WhichElementTree == 'lxml': -## root = Element( -## 'office:document-styles', -## nsmap=STYLES_NAMESPACE_DICT, -## nsdict=STYLES_NAMESPACE_DICT, -## ) -## else: -## root = Element('office:document-styles', -## attrib=STYLES_NAMESPACE_ATTRIB, -## nsdict=STYLES_NAMESPACE_DICT, -## ) -## self.styles_tree = etree.ElementTree(element=root) self.paragraph_style_stack = [self.rststyle('textbody'), ] self.list_style_stack = [] self.table_count = 0 @@ -1028,15 +923,18 @@ class ODFTranslator(nodes.GenericNodeVisitor): self.in_footer = False self.blockstyle = '' self.in_table_of_contents = False - self.footnote_dict = {} - self.footnote_found = False + self.footnote_ref_dict = {} + self.footnote_list = [] + self.footnote_chars_idx = 0 + self.footnote_level = 0 self.pending_ids = [ ] self.in_paragraph = False self.found_doc_title = False self.bumped_list_level_stack = [] self.meta_dict = {} - self.in_footnote = False - self.in_citation = None + self.line_block_level = 0 + self.line_indent_level = 0 + self.citation_id = None def add_doc_title(self): text = self.settings.title @@ -1056,8 +954,6 @@ class ODFTranslator(nodes.GenericNodeVisitor): If `parameters` is given `name` must contain a matching number of ``%`` and is used as a format expression with `parameters` as the value. """ -## template = self.format_map.get(name, 'rststyle-%s' % name) -## return template % parameters name1 = name % parameters stylename = self.format_map.get(name1, 'rststyle-%s' % name1) return stylename @@ -1082,7 +978,7 @@ class ODFTranslator(nodes.GenericNodeVisitor): w, h = 612, 792 # default to Letter def walk(el): if el.tag == "{%s}page-layout-properties" % SNSD["style"] and \ - not el.attrib.has_key("{%s}page-width" % SNSD["fo"]): + not el.attrib.has_key("{%s}page-width" % SNSD["fo"]): el.attrib["{%s}page-width" % SNSD["fo"]] = "%.3fpt" % w el.attrib["{%s}page-height" % SNSD["fo"]] = "%.3fpt" % h el.attrib["{%s}margin-left" % SNSD["fo"]] = \ @@ -1141,13 +1037,6 @@ class ODFTranslator(nodes.GenericNodeVisitor): def content_astext(self): return self.astext() -# test styles -## def styles_astext(self): -## root = self.styles_tree.getroot() -## et = etree.ElementTree(root) -## s1 = ToString(et) -## return s1 - def set_title(self, title): self.title = title def get_title(self): return self.title def set_embedded_file_list(self, embedded_file_list): @@ -1155,6 +1044,37 @@ class ODFTranslator(nodes.GenericNodeVisitor): def get_embedded_file_list(self): return self.embedded_file_list def get_meta_dict(self): return self.meta_dict + def process_footnotes(self): + for node, el1 in self.footnote_list: + backrefs = node.attributes.get('backrefs', []) + first = True + for ref in backrefs: + el2 = self.footnote_ref_dict.get(ref) + if el2 is not None: + if first: + first = False + el3 = copy.deepcopy(el1) + el2.append(el3) + else: + children = el2.getchildren() + if len(children) > 0: # and 'id' in el2.attrib: + child = children[0] + ref1 = child.text + attribkey = add_ns('text:id', nsdict=SNSD) + id1 = el2.get(attribkey, 'footnote-error') + if id1 is None: + id1 = '' + tag = add_ns('text:note-ref', nsdict=SNSD) + el2.tag = tag + el2.attrib.clear() + attribkey = add_ns('text:note-class', nsdict=SNSD) + el2.attrib[attribkey] = 'footnote' + attribkey = add_ns('text:ref-name', nsdict=SNSD) + el2.attrib[attribkey] = id1 + attribkey = add_ns('text:reference-format', nsdict=SNSD) + el2.attrib[attribkey] = 'page' + el2.text = ref1 + # # Utility methods @@ -1262,7 +1182,6 @@ class ODFTranslator(nodes.GenericNodeVisitor): # Are we in mixed content? If so, add the text to the # etree tail of the previous sibling element. if len(self.current_element.getchildren()) > 0: - #print '*** (visit_Text) 1. text: %s' % text if self.current_element.getchildren()[-1].tail: self.current_element.getchildren()[-1].tail += text else: @@ -1378,10 +1297,12 @@ class ODFTranslator(nodes.GenericNodeVisitor): else: self.paragraph_style_stack.append(self.rststyle('blockquote')) self.blockstyle = self.rststyle('blockquote') + self.line_indent_level += 1 def depart_block_quote(self, node): self.paragraph_style_stack.pop() self.blockstyle = '' + self.line_indent_level -= 1 def visit_bullet_list(self, node): #ipshell('At visit_bullet_list') @@ -1518,18 +1439,16 @@ class ODFTranslator(nodes.GenericNodeVisitor): def visit_document(self, node): #ipshell('At visit_document') - #self.set_current_element(self.content_tree.getroot()) pass def depart_document(self, node): - pass + self.process_footnotes() def visit_docinfo(self, node): - #self.document.reporter.debug_flag = 1 - self.trace_visit_node(node) + #self.trace_visit_node(node) self.section_level += 1 self.section_count += 1 - if self.settings.create_sections: + if self.settings.create_sections: el = self.append_child('text:section', attrib={ 'text:name': 'Section%d' % self.section_count, 'text:style-name': 'Sect%d' % self.section_level, @@ -1537,10 +1456,9 @@ class ODFTranslator(nodes.GenericNodeVisitor): self.set_current_element(el) def depart_docinfo(self, node): - #self.document.reporter.debug_flag = 0 - self.trace_depart_node(node) + #self.trace_depart_node(node) self.section_level -= 1 - if self.settings.create_sections: + if self.settings.create_sections: self.set_to_parent() def visit_emphasis(self, node): @@ -1627,22 +1545,6 @@ class ODFTranslator(nodes.GenericNodeVisitor): def visit_field(self, node): pass -## # Note that the syntaxhighlight directive produces this field node. -## # See class SyntaxHighlight, above. -## #ipshell('At visit_field') -## children = node.children -## if len(children) == 2: -## name = children[0].astext() -## if name == 'syntaxhighlight': -## body = children[1].astext() -## args = body.split() -## if args[0] == 'on': -## self.syntaxhighlighting = 1 -## elif args[0] == 'off': -## self.syntaxhighlighting = 0 -## else: -## self.syntaxhighlight_lexer = args[0] -## raise nodes.SkipChildren() def depart_field(self, node): pass @@ -1687,102 +1589,126 @@ class ODFTranslator(nodes.GenericNodeVisitor): def visit_footnote(self, node): #ipshell('At visit_footnote') - self.in_footnote = True - ids = node.attributes['ids'] - el1 = None - for id in ids: - if id in self.footnote_dict: - el1 = self.footnote_dict[id] - break - if el1 is not None: - el2 = SubElement(el1, 'text:note-body') - self.paragraph_style_stack.append(self.rststyle('footnote')) - self.set_current_element(el2) - self.footnote_found = True + self.footnote_level += 1 + self.save_footnote_current = self.current_element + el1 = Element('text:note-body') + self.current_element = el1 + self.footnote_list.append((node, el1)) + if isinstance(node, docutils.nodes.citation): + self.paragraph_style_stack.append(self.rststyle('citation')) else: - self.footnote_found = False + self.paragraph_style_stack.append(self.rststyle('footnote')) def depart_footnote(self, node): #ipshell('At depart_footnote') - self.in_footnote = False - if self.footnote_found: - self.current_element.text = '' - self.set_to_parent() - self.set_to_parent() - self.set_to_parent() - self.paragraph_style_stack.pop() + self.paragraph_style_stack.pop() + self.current_element = self.save_footnote_current + self.footnote_level -= 1 + + footnote_chars = [ + '*', '**', '***', + '++', '+++', + '##', '###', + '@@', '@@@', + ] def visit_footnote_reference(self, node): #ipshell('At visit_footnote_reference') - if not self.in_footnote: - id = node.attributes['refid'] - children = self.current_element.getchildren() - if len(children) > 0: - if children[-1].tail and children[-1].tail[-1] == ' ': - children[-1].tail = children[-1].tail[:-1] - elif (self.current_element.text and - self.current_element.text[-1] == ' '): - self.current_element.text = self.current_element.text[:-1] + if self.footnote_level <= 0: + id = node.attributes['ids'][0] + refid = node.attributes.get('refid') + if refid is None: + refid = '' el1 = self.append_child('text:note', attrib={ - 'text:id': '%s' % (id, ), + 'text:id': '%s' % (refid, ), 'text:note-class': 'footnote', }) - self.footnote_dict[id] = el1 + note_auto = str(node.attributes.get('auto', 1)) + if isinstance(node, docutils.nodes.citation_reference): + citation = '[%s]' % node.astext() + el2 = SubElement(el1, 'text:note-citation', attrib={ + 'text:label': citation, + }) + el2.text = citation + elif note_auto == '1': + el2 = SubElement(el1, 'text:note-citation') + el2.text = node.astext() + elif note_auto == '*': + if self.footnote_chars_idx >= len( + ODFTranslator.footnote_chars): + self.footnote_chars_idx = 0 + footnote_char = ODFTranslator.footnote_chars[ + self.footnote_chars_idx] + self.footnote_chars_idx += 1 + el2 = SubElement(el1, 'text:note-citation', attrib={ + 'text:label': footnote_char, + }) + el2.text = footnote_char + self.footnote_ref_dict[id] = el1 raise nodes.SkipChildren() def depart_footnote_reference(self, node): #ipshell('At depart_footnote_reference') pass - def visit_citation(self, node): - #ipshell('At visit_citation') - for id in node.attributes['ids']: - self.in_citation = id - break - self.paragraph_style_stack.append(self.rststyle('blockindent')) - self.bumped_list_level_stack.append(ListLevel(1)) - - def depart_citation(self, node): - #ipshell('At depart_citation') - self.in_citation = None - self.paragraph_style_stack.pop() - self.bumped_list_level_stack.pop() - - def visit_citation_reference(self, node): - #ipshell('At visit_citation_reference') - if self.settings.create_links: - id = node.attributes['refid'] - el = self.append_child('text:reference-ref', attrib={ - 'text:ref-name': '%s' % (id, ), - 'text:reference-format': 'text', - }) - el.text = '[' - self.set_current_element(el) - else: - self.current_element.text += '[' - - def depart_citation_reference(self, node): - #ipshell('At depart_citation_reference') - self.current_element.text += ']' - if self.settings.create_links: - self.set_to_parent() +## def visit_citation(self, node): +## #ipshell('At visit_citation') +## for id in node.attributes['ids']: +## self.citation_id = id +## break +## self.paragraph_style_stack.append(self.rststyle('blockindent')) +## self.bumped_list_level_stack.append(ListLevel(1)) +## +## def depart_citation(self, node): +## #ipshell('At depart_citation') +## self.citation_id = None +## self.paragraph_style_stack.pop() +## self.bumped_list_level_stack.pop() +## +## def visit_citation_reference(self, node): +## ipshell('At visit_citation_reference') +## if self.settings.create_links: +## id = node.attributes['refid'] +## el = self.append_child('text:reference-ref', attrib={ +## 'text:ref-name': '%s' % (id, ), +## 'text:reference-format': 'text', +## }) +## el.text = '[' +## self.set_current_element(el) +## else: +## self.current_element.text += '[' +## +## def depart_citation_reference(self, node): +## #ipshell('At depart_citation_reference') +## self.current_element.text += ']' +## if self.settings.create_links: +## self.set_to_parent() + + visit_citation = visit_footnote + depart_citation = depart_footnote + visit_citation_reference = visit_footnote_reference + depart_citation_reference = depart_footnote_reference def visit_label(self, node): #ipshell('At visit_label') - if self.in_citation is not None: + if isinstance(node.parent, docutils.nodes.footnote): + raise nodes.SkipChildren() + elif self.citation_id is not None: el = self.append_p('textbody') self.set_current_element(el) if self.settings.create_links: el1 = self.append_child('text:reference-mark-start', attrib={ - 'text:name': '%s' % (self.in_citation, ), + 'text:name': '%s' % (self.citation_id, ), }) def depart_label(self, node): #ipshell('At depart_label') - if self.in_citation is not None: + if isinstance(node.parent, docutils.nodes.footnote): + pass + elif self.citation_id is not None: if self.settings.create_links: el = self.append_child('text:reference-mark-end', attrib={ - 'text:name': '%s' % (self.in_citation, ), + 'text:name': '%s' % (self.citation_id, ), }) self.set_to_parent() @@ -1865,16 +1791,21 @@ class ODFTranslator(nodes.GenericNodeVisitor): def depart_image(self, node): pass - def get_image_width_height(self, node, attr, scale): + def get_image_width_height(self, node, attr): size = None if attr in node.attributes: + size = node.attributes[attr] + unit = size[-2:] + if unit.isalpha(): + size = size[:-2] + else: + unit = 'px' try: - size = int(node.attributes[attr]) - size *= 35.278 / 1000.0 - size *= scale + size = float(size) except ValueError, e: print 'Error: Invalid %s for image: "%s"' % ( - attr, node.attributes[attr], ) + attr, node.attributes[attr]) + size = [size, unit] return size def get_image_scale(self, node): @@ -1891,30 +1822,41 @@ class ODFTranslator(nodes.GenericNodeVisitor): scale = 1.0 return scale - def get_image_scale_width_height(self, node, source): + def get_image_scaled_width_height(self, node, source): scale = self.get_image_scale(node) - width = self.get_image_width_height(node, 'width', scale) - height = self.get_image_width_height(node, 'height', scale) - if ('scale' in node.attributes and - ('width' not in node.attributes or - 'height' not in node.attributes)): - if Image is not None: - if source in self.image_dict: - filename, destination = self.image_dict[source] - imageobj = Image.open(filename, 'r') - width, height = imageobj.size - width = width * (35.278 / 1000.0) - width *= scale - height = height * (35.278 / 1000.0) - height *= scale - else: - raise RuntimeError, 'image has scale and no height/width and PIL not installed' - return scale, width, height + width = self.get_image_width_height(node, 'width') + height = self.get_image_width_height(node, 'height') + + dpi = (72, 72) + if Image is not None and source in self.image_dict: + filename, destination = self.image_dict[source] + imageobj = Image.open(filename, 'r') + dpi = imageobj.info.get('dpi', dpi) + # dpi information can be (xdpi, ydpi) or xydpi + try: iter(dpi) + except: dpi = (dpi, dpi) + else: + imageobj = None + + if width is None or height is None: + if imageobj is None: + raise RuntimeError, 'image size not fully specified and PIL not installed' + if width is None: width = [imageobj.size[0], 'px'] + if height is None: height = [imageobj.size[1], 'px'] + + width[0] *= scale + height[0] *= scale + if width[1] == 'px': width = [width[0] / dpi[0], 'in'] + if height[1] == 'px': height = [height[0] / dpi[1], 'in'] + + width[0] = str(width[0]) + height[0] = str(height[0]) + return ''.join(width), ''.join(height) def generate_figure(self, node, source, destination, current_element): #ipshell('At generate_figure') caption = None - scale, width, height = self.get_image_scale_width_height(node, source) + width, height = self.get_image_scaled_width_height(node, source) for node1 in node.parent.children: if node1.tagname == 'caption': caption = node1.astext() @@ -1984,17 +1926,15 @@ class ODFTranslator(nodes.GenericNodeVisitor): } el2 = SubElement(el1, 'style:graphic-properties', attrib=attrib, nsdict=SNSD) - # Add the content wrapper. -## attrib = {'text:style-name': self.rststyle('textbody')} -## el1 = SubElement(self.current_element, 'text:p', attrib=attrib) attrib = { 'draw:style-name': style_name, 'draw:name': 'Frame1', 'text:anchor-type': 'paragraph', 'draw:z-index': '1', } - if width is not None: - attrib['svg:width'] = '%.2fcm' % (width, ) + attrib['svg:width'] = width + # dbg + #attrib['svg:height'] = height el3 = SubElement(current_element, 'draw:frame', attrib=attrib) attrib = {} el4 = SubElement(el3, 'draw:text-box', attrib=attrib) @@ -2002,14 +1942,12 @@ class ODFTranslator(nodes.GenericNodeVisitor): 'text:style-name': self.rststyle('caption'), } el5 = SubElement(el4, 'text:p', attrib=attrib) -## if caption is not None: -## el3.tail = caption return el3, el5, caption def generate_image(self, node, source, destination, current_element, #ipshell('At generate_image') frame_attrs=None): - scale, width, height = self.get_image_scale_width_height(node, source) + width, height = self.get_image_scaled_width_height(node, source) self.image_style_count += 1 style_name = 'rstframestyle%d' % self.image_style_count # Add the style. @@ -2075,10 +2013,8 @@ class ODFTranslator(nodes.GenericNodeVisitor): attrib['text:anchor-type'] = 'char' else: attrib['text:anchor-type'] = 'paragraph' - if width is not None: - attrib['svg:width'] = '%.2fcm' % (width, ) - if height is not None: - attrib['svg:height'] = '%.2fcm' % (height, ) + attrib['svg:width'] = width + attrib['svg:height'] = height el1 = SubElement(current_element, 'draw:frame', attrib=attrib) el2 = SubElement(el1, 'draw:image', attrib={ 'xlink:href': '%s' % (destination, ), @@ -2104,33 +2040,31 @@ class ODFTranslator(nodes.GenericNodeVisitor): def depart_legend(self, node): pass - def visit_line(self, node): - #ipshell('At visit_line') - pass - - def depart_line(self, node): - #ipshell('At depart_line') - pass - def visit_line_block(self, node): #ipshell('At visit_line_block') - s1 = node.astext() - lines = s1.split('\n') - el = self.append_p('lineblock', lines[0]) - first = True - if len(lines) > 1: - for line in lines[1:]: - if line == '': - if first: - first = False - continue - first = True - el1 = SubElement(el, 'text:line-break') - el1.tail = line + self.line_indent_level += 1 + self.line_block_level += 1 def depart_line_block(self, node): #ipshell('At depart_line_block') - pass + if self.line_block_level <= 1: + el1 = SubElement(self.current_element, 'text:p', attrib={ + 'text:style-name': self.rststyle('lineblock1'), + }) + self.line_indent_level -= 1 + self.line_block_level -= 1 + + def visit_line(self, node): + #ipshell('At visit_line') + style = 'lineblock%d' % self.line_indent_level + el1 = SubElement(self.current_element, 'text:p', attrib={ + 'text:style-name': self.rststyle(style), + }) + self.current_element = el1 + + def depart_line(self, node): + #ipshell('At depart_line') + self.set_to_parent() def visit_literal(self, node): #ipshell('At visit_literal') @@ -2155,7 +2089,6 @@ class ODFTranslator(nodes.GenericNodeVisitor): return count def _add_syntax_highlighting(self, insource, language): - #print '(_add_syntax_highlighting) using lexer: %s' % (language, ) lexer = pygments.lexers.get_lexer_by_name(language, stripall=True) if language in ('latex', 'tex'): fmtr = OdtPygmentsLaTeXFormatter(lambda name, parameters=(): @@ -2314,7 +2247,6 @@ class ODFTranslator(nodes.GenericNodeVisitor): self.set_current_element(el) def depart_option_list(self, node): - #self.document.reporter.debug_flag = 0 self.set_to_parent() def visit_option_list_item(self, node): @@ -2413,7 +2345,7 @@ class ODFTranslator(nodes.GenericNodeVisitor): for k,v in CONTENT_NAMESPACE_ATTRIB.items()]) contentstr = '<stuff %s>%s</stuff>' % (attrstr, rawstr, ) if WhichElementTree != "lxml": - content = content.encode("utf-8") + contentstr = contentstr.encode("utf-8") content = etree.fromstring(contentstr) elements = content.getchildren() if len(elements) > 0: @@ -2457,7 +2389,6 @@ class ODFTranslator(nodes.GenericNodeVisitor): }) else: raise RuntimeError, 'References must have "refuri" or "refid" attribute.' - #print '(visit_reference) href: "%s" text: "%s"' % (href, text, ) if (self.in_table_of_contents and len(node.children) >= 1 and isinstance(node.children[0], docutils.nodes.generated)): @@ -2491,7 +2422,7 @@ class ODFTranslator(nodes.GenericNodeVisitor): #ipshell('At visit_section') self.section_level += 1 self.section_count += 1 - if self.settings.create_sections: + if self.settings.create_sections: el = self.append_child('text:section', attrib={ 'text:name': 'Section%d' % self.section_count, 'text:style-name': 'Sect%d' % self.section_level, @@ -2500,8 +2431,8 @@ class ODFTranslator(nodes.GenericNodeVisitor): def depart_section(self, node): self.section_level -= 1 - if self.settings.create_sections: - self.set_to_parent() +## if self.settings.create_sections: +## self.set_to_parent() def visit_strong(self, node): #ipshell('At visit_strong') @@ -2539,6 +2470,8 @@ class ODFTranslator(nodes.GenericNodeVisitor): el1_1 = SubElement(el1, 'style:table-properties', attrib={ #'style:width': '17.59cm', 'table:align': 'margins', + 'fo:margin-top': '0in', + 'fo:margin-bottom': '0.10in', }, nsdict=SNSD) # We use a single cell style for all cells in this table. # That's probably not correct, but seems to work. diff --git a/sandbox/OpenDocument/odtwriter/styles.odt b/sandbox/OpenDocument/odtwriter/styles.odt Binary files differindex e61b12a0d..88769defa 100644 --- a/sandbox/OpenDocument/odtwriter/styles.odt +++ b/sandbox/OpenDocument/odtwriter/styles.odt diff --git a/sandbox/OpenDocument/setup.py b/sandbox/OpenDocument/setup.py index 6da364a47..699c7c42a 100644 --- a/sandbox/OpenDocument/setup.py +++ b/sandbox/OpenDocument/setup.py @@ -5,7 +5,7 @@ from setuptools import setup setup(name="odtwriter", - version="1.3b", + version="1.3c", description="convert rst to ODF/odt/odp.", author="Dave Kuhlman", author_email="dkuhlman@rexx.com", |