This commit is contained in:
Kovid Goyal 2015-03-19 10:27:33 +05:30
parent ca26df381a
commit c981e4b50d

View File

@ -55,6 +55,7 @@ if False: # Added by Kovid
# In ODF a style can have a parent, these parents can be chained. # In ODF a style can have a parent, these parents can be chained.
class StyleToCSS: class StyleToCSS:
""" The purpose of the StyleToCSS class is to contain the rules to convert """ The purpose of the StyleToCSS class is to contain the rules to convert
ODF styles to CSS2. Since it needs the generic fonts, it would probably ODF styles to CSS2. Since it needs the generic fonts, it would probably
make sense to also contain the Styles in a dict as well.. make sense to also contain the Styles in a dict as well..
@ -119,12 +120,18 @@ class StyleToCSS:
This method put the font and fallback into a dictionary This method put the font and fallback into a dictionary
""" """
htmlgeneric = "sans-serif" htmlgeneric = "sans-serif"
if generic == "roman": htmlgeneric = "serif" if generic == "roman":
elif generic == "swiss": htmlgeneric = "sans-serif" htmlgeneric = "serif"
elif generic == "modern": htmlgeneric = "monospace" elif generic == "swiss":
elif generic == "decorative": htmlgeneric = "sans-serif" htmlgeneric = "sans-serif"
elif generic == "script": htmlgeneric = "monospace" elif generic == "modern":
elif generic == "system": htmlgeneric = "serif" htmlgeneric = "monospace"
elif generic == "decorative":
htmlgeneric = "sans-serif"
elif generic == "script":
htmlgeneric = "monospace"
elif generic == "system":
htmlgeneric = "serif"
self.fontdict[name] = (family, htmlgeneric) self.fontdict[name] = (family, htmlgeneric)
def c_drawfillimage(self, ruleset, sdict, rule, val): def c_drawfillimage(self, ruleset, sdict, rule, val):
@ -159,8 +166,10 @@ class StyleToCSS:
def c_text_align(self, ruleset, sdict, rule, align): def c_text_align(self, ruleset, sdict, rule, align):
""" Text align """ """ Text align """
if align == "start": align = "left" if align == "start":
if align == "end": align = "right" align = "left"
if align == "end":
align = "right"
sdict['text-align'] = align sdict['text-align'] = align
def c_fn(self, ruleset, sdict, rule, fontstyle): def c_fn(self, ruleset, sdict, rule, fontstyle):
@ -224,7 +233,7 @@ class StyleToCSS:
elif wrap == "run-through": elif wrap == "run-through":
sdict['position'] = "absolute" # Simulate run-through sdict['position'] = "absolute" # Simulate run-through
sdict['top'] = "0" sdict['top'] = "0"
sdict['right'] = "0"; sdict['right'] = "0"
else: # No wrapping else: # No wrapping
sdict['margin-left'] = "auto" sdict['margin-left'] = "auto"
sdict['margin-right'] = "0px" sdict['margin-right'] = "0px"
@ -243,7 +252,7 @@ class StyleToCSS:
sdict['float'] = "left" sdict['float'] = "left"
else: else:
sdict['position'] = "relative" # No wrapping sdict['position'] = "relative" # No wrapping
if ruleset.has_key( (SVGNS,'x') ): if (SVGNS,'x') in ruleset:
sdict['left'] = ruleset[(SVGNS,'x')] sdict['left'] = ruleset[(SVGNS,'x')]
def c_page_width(self, ruleset, sdict, rule, val): def c_page_width(self, ruleset, sdict, rule, val):
@ -286,6 +295,7 @@ class StyleToCSS:
class TagStack: class TagStack:
def __init__(self): def __init__(self):
self.stack = [] self.stack = []
@ -303,13 +313,14 @@ class TagStack:
def rfindattr(self, attr): def rfindattr(self, attr):
""" Find a tag with the given attribute """ """ Find a tag with the given attribute """
for tag, attrs in self.stack: for tag, attrs in self.stack:
if attrs.has_key(attr): if attr in attrs:
return attrs[attr] return attrs[attr]
return None return None
def count_tags(self, tag): def count_tags(self, tag):
c = 0 c = 0
for ttag, tattrs in self.stack: for ttag, tattrs in self.stack:
if ttag == tag: c = c + 1 if ttag == tag:
c = c + 1
return c return c
special_styles = { special_styles = {
@ -341,6 +352,7 @@ special_styles = {
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
class ODF2XHTML(handler.ContentHandler): class ODF2XHTML(handler.ContentHandler):
""" The ODF2XHTML parses an ODF file and produces XHTML""" """ The ODF2XHTML parses an ODF file and produces XHTML"""
def __init__(self, generate_css=True, embedable=False): def __init__(self, generate_css=True, embedable=False):
@ -459,7 +471,6 @@ class ODF2XHTML(handler.ContentHandler):
self.elements[(OFFICENS, u"presentation")] = (None,None) self.elements[(OFFICENS, u"presentation")] = (None,None)
self.elements[(OFFICENS, u"document-content")] = (None,None) self.elements[(OFFICENS, u"document-content")] = (None,None)
def add_style_file(self, stylefilename, media=None): def add_style_file(self, stylefilename, media=None):
""" Add a link to an external style file. """ Add a link to an external style file.
Also turns of the embedding of styles in the HTML Also turns of the embedding of styles in the HTML
@ -506,7 +517,6 @@ class ODF2XHTML(handler.ContentHandler):
# Tags from meta.xml # Tags from meta.xml
self.metatags = [] self.metatags = []
def writeout(self, s): def writeout(self, s):
if s != '': if s != '':
self._wfunc(s) self._wfunc(s)
@ -526,14 +536,14 @@ class ODF2XHTML(handler.ContentHandler):
self.writeout("<%s>" % tag) self.writeout("<%s>" % tag)
else: else:
self.writeout("<%s %s>" % (tag, " ".join(a))) self.writeout("<%s %s>" % (tag, " ".join(a)))
if block == True: if block:
self.writeout("\n") self.writeout("\n")
def closetag(self, tag, block=True): def closetag(self, tag, block=True):
""" Close an open HTML tag """ """ Close an open HTML tag """
self.htmlstack.pop() self.htmlstack.pop()
self.writeout("</%s>" % tag) self.writeout("</%s>" % tag)
if block == True: if block:
self.writeout("\n") self.writeout("\n")
def emptytag(self, tag, attrs={}): def emptytag(self, tag, attrs={}):
@ -604,7 +614,7 @@ class ODF2XHTML(handler.ContentHandler):
def get_anchor(self, name): def get_anchor(self, name):
""" Create a unique anchor id for a href name """ """ Create a unique anchor id for a href name """
if not self.anchors.has_key(name): if name not in self.anchors:
# Changed by Kovid # Changed by Kovid
self.anchors[name] = "anchor%d" % (len(self.anchors) + 1) self.anchors[name] = "anchor%d" % (len(self.anchors) + 1)
return self.anchors.get(name) return self.anchors.get(name)
@ -665,13 +675,13 @@ class ODF2XHTML(handler.ContentHandler):
style = '' style = ''
else: else:
style = "position: absolute;" style = "position: absolute;"
if attrs.has_key( (SVGNS,"width") ): if (SVGNS,"width") in attrs:
style = style + "width:" + attrs[(SVGNS,"width")] + ";" style = style + "width:" + attrs[(SVGNS,"width")] + ";"
if attrs.has_key( (SVGNS,"height") ): if (SVGNS,"height") in attrs:
style = style + "height:" + attrs[(SVGNS,"height")] + ";" style = style + "height:" + attrs[(SVGNS,"height")] + ";"
if attrs.has_key( (SVGNS,"x") ): if (SVGNS,"x") in attrs:
style = style + "left:" + attrs[(SVGNS,"x")] + ";" style = style + "left:" + attrs[(SVGNS,"x")] + ";"
if attrs.has_key( (SVGNS,"y") ): if (SVGNS,"y") in attrs:
style = style + "top:" + attrs[(SVGNS,"y")] + ";" style = style + "top:" + attrs[(SVGNS,"y")] + ";"
if self.generate_css: if self.generate_css:
self.opentag(htmltag, {'class': name, 'style': style}) self.opentag(htmltag, {'class': name, 'style': style})
@ -701,13 +711,13 @@ class ODF2XHTML(handler.ContentHandler):
style = '' style = ''
else: else:
style = "position:absolute;" style = "position:absolute;"
if attrs.has_key( (SVGNS,"width") ): if (SVGNS,"width") in attrs:
style = style + "width:" + attrs[(SVGNS,"width")] + ";" style = style + "width:" + attrs[(SVGNS,"width")] + ";"
if attrs.has_key( (SVGNS,"height") ): if (SVGNS,"height") in attrs:
style = style + "height:" + attrs[(SVGNS,"height")] + ";" style = style + "height:" + attrs[(SVGNS,"height")] + ";"
if attrs.has_key( (SVGNS,"x") ): if (SVGNS,"x") in attrs:
style = style + "left:" + attrs[(SVGNS,"x")] + ";" style = style + "left:" + attrs[(SVGNS,"x")] + ";"
if attrs.has_key( (SVGNS,"y") ): if (SVGNS,"y") in attrs:
style = style + "top:" + attrs[(SVGNS,"y")] + ";" style = style + "top:" + attrs[(SVGNS,"y")] + ";"
if self.generate_css: if self.generate_css:
self.opentag(htmltag, {'class': name, 'style': style}) self.opentag(htmltag, {'class': name, 'style': style})
@ -766,7 +776,7 @@ class ODF2XHTML(handler.ContentHandler):
class_id = attrs[(DRAWNS,"class-id")] class_id = attrs[(DRAWNS,"class-id")]
except KeyError: # Added by Kovid to ignore <draw> without the right except KeyError: # Added by Kovid to ignore <draw> without the right
return # attributes return # attributes
if class_id and class_id.lower() == "00020803-0000-0000-c000-000000000046": ## Microsoft Graph 97 Chart if class_id and class_id.lower() == "00020803-0000-0000-c000-000000000046": # Microsoft Graph 97 Chart
tagattrs = {'name':'object_ole_graph', 'class':'ole-graph'} tagattrs = {'name':'object_ole_graph', 'class':'ole-graph'}
self.opentag('a', tagattrs) self.opentag('a', tagattrs)
self.closetag('a', tagattrs) self.closetag('a', tagattrs)
@ -794,7 +804,7 @@ class ODF2XHTML(handler.ContentHandler):
def s_draw_textbox(self, tag, attrs): def s_draw_textbox(self, tag, attrs):
style = '' style = ''
if attrs.has_key( (FONS,"min-height") ): if (FONS,"min-height") in attrs:
style = style + "min-height:" + attrs[(FONS,"min-height")] + ";" style = style + "min-height:" + attrs[(FONS,"min-height")] + ";"
self.opentag('div') self.opentag('div')
# self.opentag('div', {'style': style}) # self.opentag('div', {'style': style})
@ -837,14 +847,14 @@ dl.notes dd:last-of-type { page-break-after: avoid }
for name in self.stylestack: for name in self.stylestack:
styles = self.styledict.get(name) styles = self.styledict.get(name)
# Preload with the family's default style # Preload with the family's default style
if styles.has_key('__style-family') and self.styledict.has_key(styles['__style-family']): if '__style-family' in styles and styles['__style-family'] in self.styledict:
familystyle = self.styledict[styles['__style-family']].copy() familystyle = self.styledict[styles['__style-family']].copy()
del styles['__style-family'] del styles['__style-family']
for style, val in styles.items(): for style, val in styles.items():
familystyle[style] = val familystyle[style] = val
styles = familystyle styles = familystyle
# Resolve the remaining parent styles # Resolve the remaining parent styles
while styles.has_key('__parent-style-name') and self.styledict.has_key(styles['__parent-style-name']): while '__parent-style-name' in styles and styles['__parent-style-name'] in self.styledict:
parentstyle = self.styledict[styles['__parent-style-name']].copy() parentstyle = self.styledict[styles['__parent-style-name']].copy()
del styles['__parent-style-name'] del styles['__parent-style-name']
for style, val in styles.items(): for style, val in styles.items():
@ -976,7 +986,6 @@ dl.notes dd:last-of-type { page-break-after: avoid }
for key,attr in attrs.items(): for key,attr in attrs.items():
self.styledict[self.currentstyle][key] = attr self.styledict[self.currentstyle][key] = attr
familymap = {'frame':'frame', 'paragraph':'p', 'presentation':'presentation', familymap = {'frame':'frame', 'paragraph':'p', 'presentation':'presentation',
'text':'span','section':'div', 'text':'span','section':'div',
'table':'table','table-cell':'td','table-column':'col', 'table':'table','table-cell':'td','table-column':'col',
@ -1070,7 +1079,7 @@ dl.notes dd:last-of-type { page-break-after: avoid }
pagelayout = attrs.get((STYLENS,'page-layout-name'), None) pagelayout = attrs.get((STYLENS,'page-layout-name'), None)
if pagelayout: if pagelayout:
pagelayout = ".PL-" + pagelayout pagelayout = ".PL-" + pagelayout
if self.styledict.has_key( pagelayout ): if pagelayout in self.styledict:
styles = self.styledict[pagelayout] styles = self.styledict[pagelayout]
for style, val in styles.items(): for style, val in styles.items():
self.styledict[self.currentstyle][style] = val self.styledict[self.currentstyle][style] = val
@ -1100,7 +1109,7 @@ dl.notes dd:last-of-type { page-break-after: avoid }
parent = attrs.get((STYLENS,'parent-style-name')) parent = attrs.get((STYLENS,'parent-style-name'))
self.currentstyle = special_styles.get(name,"."+name) self.currentstyle = special_styles.get(name,"."+name)
self.stylestack.append(self.currentstyle) self.stylestack.append(self.currentstyle)
if not self.styledict.has_key(self.currentstyle): if self.currentstyle not in self.styledict:
self.styledict[self.currentstyle] = {} self.styledict[self.currentstyle] = {}
self.styledict[self.currentstyle]['__style-family'] = htmlfamily self.styledict[self.currentstyle]['__style-family'] = htmlfamily
@ -1109,7 +1118,7 @@ dl.notes dd:last-of-type { page-break-after: avoid }
if parent: if parent:
parent = "%s-%s" % (sfamily, parent) parent = "%s-%s" % (sfamily, parent)
parent = special_styles.get(parent, "."+parent) parent = special_styles.get(parent, "."+parent)
if self.styledict.has_key( parent ): if parent in self.styledict:
styles = self.styledict[parent] styles = self.styledict[parent]
for style, val in styles.items(): for style, val in styles.items():
self.styledict[self.currentstyle][style] = val self.styledict[self.currentstyle][style] = val
@ -1225,8 +1234,10 @@ dl.notes dd:last-of-type { page-break-after: avoid }
def s_text_h(self, tag, attrs): def s_text_h(self, tag, attrs):
""" Headings start """ """ Headings start """
level = int(attrs[(TEXTNS,'outline-level')]) level = int(attrs[(TEXTNS,'outline-level')])
if level > 6: level = 6 # Heading levels go only to 6 in XHTML if level > 6:
if level < 1: level = 1 level = 6 # Heading levels go only to 6 in XHTML
if level < 1:
level = 1
self.headinglevels[level] = self.headinglevels[level] + 1 self.headinglevels[level] = self.headinglevels[level] + 1
name = self.classname(attrs) name = self.classname(attrs)
for x in range(level + 1,10): for x in range(level + 1,10):
@ -1245,12 +1256,15 @@ dl.notes dd:last-of-type { page-break-after: avoid }
""" """
self.writedata() self.writedata()
level = int(attrs[(TEXTNS,'outline-level')]) level = int(attrs[(TEXTNS,'outline-level')])
if level > 6: level = 6 # Heading levels go only to 6 in XHTML if level > 6:
if level < 1: level = 1 level = 6 # Heading levels go only to 6 in XHTML
if level < 1:
level = 1
lev = self.headinglevels[1:level+1] lev = self.headinglevels[1:level+1]
outline = '.'.join(map(str,lev)) outline = '.'.join(map(str,lev))
heading = ''.join(self.data) heading = ''.join(self.data)
if self.title == '': self.title = heading if self.title == '':
self.title = heading
# Changed by Kovid # Changed by Kovid
tail = ''.join(self.data) tail = ''.join(self.data)
anchor = self.get_anchor("%s.%s" % (outline, tail)) anchor = self.get_anchor("%s.%s" % (outline, tail))
@ -1348,12 +1362,18 @@ dl.notes dd:last-of-type { page-break-after: avoid }
self.listtypes[list_class] = 'ol' self.listtypes[list_class] = 'ol'
self.stylestack.append(self.currentstyle) self.stylestack.append(self.currentstyle)
self.styledict[self.currentstyle] = {} self.styledict[self.currentstyle] = {}
if num_format == "1": listtype = "decimal" if num_format == "1":
elif num_format == "I": listtype = "upper-roman" listtype = "decimal"
elif num_format == "i": listtype = "lower-roman" elif num_format == "I":
elif num_format == "A": listtype = "upper-alpha" listtype = "upper-roman"
elif num_format == "a": listtype = "lower-alpha" elif num_format == "i":
else: listtype = "decimal" listtype = "lower-roman"
elif num_format == "A":
listtype = "upper-alpha"
elif num_format == "a":
listtype = "lower-alpha"
else:
listtype = "decimal"
self.styledict[self.currentstyle][('','list-style-type')] = listtype self.styledict[self.currentstyle][('','list-style-type')] = listtype
def e_text_list_level_style_number(self, tag, attrs): def e_text_list_level_style_number(self, tag, attrs):
@ -1535,7 +1555,6 @@ dl.notes dd:last-of-type { page-break-after: avoid }
if node.nodeType == Node.TEXT_NODE or node.nodeType == Node.CDATA_SECTION_NODE: if node.nodeType == Node.TEXT_NODE or node.nodeType == Node.CDATA_SECTION_NODE:
self.characters(unicode(node)) self.characters(unicode(node))
def odf2xhtml(self, odffile): def odf2xhtml(self, odffile):
""" Load a file and return the XHTML """ Load a file and return the XHTML
""" """
@ -1543,7 +1562,8 @@ dl.notes dd:last-of-type { page-break-after: avoid }
return self.xhtml() return self.xhtml()
def _wlines(self,s): def _wlines(self,s):
if s != '': self.lines.append(s) if s != '':
self.lines.append(s)
def xhtml(self): def xhtml(self):
""" Returns the xhtml """ Returns the xhtml
@ -1551,7 +1571,8 @@ dl.notes dd:last-of-type { page-break-after: avoid }
return ''.join(self.lines) return ''.join(self.lines)
def _writecss(self, s): def _writecss(self, s):
if s != '': self._csslines.append(s) if s != '':
self._csslines.append(s)
def _writenothing(self, s): def _writenothing(self, s):
pass pass
@ -1583,6 +1604,7 @@ dl.notes dd:last-of-type { page-break-after: avoid }
class ODF2XHTMLembedded(ODF2XHTML): class ODF2XHTMLembedded(ODF2XHTML):
""" The ODF2XHTML parses an ODF file and produces XHTML""" """ The ODF2XHTML parses an ODF file and produces XHTML"""
def __init__(self, lines, generate_css=True, embedable=False): def __init__(self, lines, generate_css=True, embedable=False):
@ -1625,21 +1647,21 @@ class ODF2XHTMLembedded(ODF2XHTML):
# (OFFICENS, "text"):(self.s_office_text, self.e_office_text), # (OFFICENS, "text"):(self.s_office_text, self.e_office_text),
(OFFICENS, "scripts"):(self.s_ignorexml, None), (OFFICENS, "scripts"):(self.s_ignorexml, None),
(PRESENTATIONNS, "notes"):(self.s_ignorexml, None), (PRESENTATIONNS, "notes"):(self.s_ignorexml, None),
## (STYLENS, "default-page-layout"):(self.s_style_default_page_layout, self.e_style_page_layout), # (STYLENS, "default-page-layout"):(self.s_style_default_page_layout, self.e_style_page_layout),
# (STYLENS, "default-page-layout"):(self.s_ignorexml, None), # (STYLENS, "default-page-layout"):(self.s_ignorexml, None),
# (STYLENS, "default-style"):(self.s_style_default_style, self.e_style_default_style), # (STYLENS, "default-style"):(self.s_style_default_style, self.e_style_default_style),
# (STYLENS, "drawing-page-properties"):(self.s_style_handle_properties, None), # (STYLENS, "drawing-page-properties"):(self.s_style_handle_properties, None),
# (STYLENS, "font-face"):(self.s_style_font_face, None), # (STYLENS, "font-face"):(self.s_style_font_face, None),
## (STYLENS, "footer"):(self.s_style_footer, self.e_style_footer), # (STYLENS, "footer"):(self.s_style_footer, self.e_style_footer),
## (STYLENS, "footer-style"):(self.s_style_footer_style, None), # (STYLENS, "footer-style"):(self.s_style_footer_style, None),
# (STYLENS, "graphic-properties"):(self.s_style_handle_properties, None), # (STYLENS, "graphic-properties"):(self.s_style_handle_properties, None),
# (STYLENS, "handout-master"):(self.s_ignorexml, None), # (STYLENS, "handout-master"):(self.s_ignorexml, None),
## (STYLENS, "header"):(self.s_style_header, self.e_style_header), # (STYLENS, "header"):(self.s_style_header, self.e_style_header),
## (STYLENS, "header-footer-properties"):(self.s_style_handle_properties, None), # (STYLENS, "header-footer-properties"):(self.s_style_handle_properties, None),
## (STYLENS, "header-style"):(self.s_style_header_style, None), # (STYLENS, "header-style"):(self.s_style_header_style, None),
# (STYLENS, "master-page"):(self.s_style_master_page, None), # (STYLENS, "master-page"):(self.s_style_master_page, None),
# (STYLENS, "page-layout-properties"):(self.s_style_handle_properties, None), # (STYLENS, "page-layout-properties"):(self.s_style_handle_properties, None),
## (STYLENS, "page-layout"):(self.s_style_page_layout, self.e_style_page_layout), # (STYLENS, "page-layout"):(self.s_style_page_layout, self.e_style_page_layout),
# (STYLENS, "page-layout"):(self.s_ignorexml, None), # (STYLENS, "page-layout"):(self.s_ignorexml, None),
# (STYLENS, "paragraph-properties"):(self.s_style_handle_properties, None), # (STYLENS, "paragraph-properties"):(self.s_style_handle_properties, None),
# (STYLENS, "style"):(self.s_style_style, self.e_style_style), # (STYLENS, "style"):(self.s_style_style, self.e_style_style),
@ -1680,4 +1702,3 @@ class ODF2XHTMLembedded(ODF2XHTML):
(TEXTNS, "user-index-source"):(self.s_text_x_source, self.e_text_x_source), (TEXTNS, "user-index-source"):(self.s_text_x_source, self.e_text_x_source),
(TEXTNS, "page-number"):(None, None), (TEXTNS, "page-number"):(None, None),
} }