640 lines
25 KiB
Python
640 lines
25 KiB
Python
|
from types import StringType, ListType, TupleType
|
||
|
from copy import deepcopy
|
||
|
from Elements import *
|
||
|
|
||
|
DEFAULT_TAB_WIDTH = 720
|
||
|
|
||
|
ParagraphAlignmentMap = { ParagraphPropertySet.LEFT : 'ql',
|
||
|
ParagraphPropertySet.RIGHT : 'qr',
|
||
|
ParagraphPropertySet.CENTER : 'qc',
|
||
|
ParagraphPropertySet.JUSTIFY : 'qj',
|
||
|
ParagraphPropertySet.DISTRIBUTE : 'qd' }
|
||
|
|
||
|
TabAlignmentMap = { TabPropertySet.LEFT : '',
|
||
|
TabPropertySet.RIGHT : 'tqr',
|
||
|
TabPropertySet.CENTER : 'tqc',
|
||
|
TabPropertySet.DECIMAL : 'tqdec' }
|
||
|
|
||
|
TableAlignmentMap = { Table.LEFT : 'trql',
|
||
|
Table.RIGHT : 'trqr',
|
||
|
Table.CENTER : 'trqc' }
|
||
|
|
||
|
CellAlignmentMap = { Cell.ALIGN_TOP : '', # clvertalt
|
||
|
Cell.ALIGN_CENTER : 'clvertalc',
|
||
|
Cell.ALIGN_BOTTOM : 'clvertalb' }
|
||
|
|
||
|
CellFlowMap = { Cell.FLOW_LR_TB : '', # cltxlrtb, Text in a cell flows from left to right and top to bottom (default)
|
||
|
Cell.FLOW_RL_TB : 'cltxtbrl', # Text in a cell flows right to left and top to bottom
|
||
|
Cell.FLOW_LR_BT : 'cltxbtlr', # Text in a cell flows left to right and bottom to top
|
||
|
Cell.FLOW_VERTICAL_LR_TB : 'cltxlrtbv', # Text in a cell flows left to right and top to bottom, vertical
|
||
|
Cell.FLOW_VERTICAL_TB_RL : 'cltxtbrlv' } # Text in a cell flows top to bottom and right to left, vertical
|
||
|
|
||
|
ShadingPatternMap = { ShadingPropertySet.HORIZONTAL : 'bghoriz',
|
||
|
ShadingPropertySet.VERTICAL : 'bgvert',
|
||
|
ShadingPropertySet.FORWARD_DIAGONAL : 'bgfdiag',
|
||
|
ShadingPropertySet.BACKWARD_DIAGONAL : 'bgbdiag',
|
||
|
ShadingPropertySet.VERTICAL_CROSS : 'bgcross',
|
||
|
ShadingPropertySet.DIAGONAL_CROSS : 'bgdcross',
|
||
|
ShadingPropertySet.DARK_HORIZONTAL : 'bgdkhoriz',
|
||
|
ShadingPropertySet.DARK_VERTICAL : 'bgdkvert',
|
||
|
ShadingPropertySet.DARK_FORWARD_DIAGONAL : 'bgdkfdiag',
|
||
|
ShadingPropertySet.DARK_BACKWARD_DIAGONAL : 'bgdkbdiag',
|
||
|
ShadingPropertySet.DARK_VERTICAL_CROSS : 'bgdkcross',
|
||
|
ShadingPropertySet.DARK_DIAGONAL_CROSS : 'bgdkdcross' }
|
||
|
|
||
|
TabLeaderMap = { TabPropertySet.DOTS : 'tldot',
|
||
|
TabPropertySet.HYPHENS : 'tlhyph',
|
||
|
TabPropertySet.UNDERLINE : 'tlul',
|
||
|
TabPropertySet.THICK_LINE : 'tlth',
|
||
|
TabPropertySet.EQUAL_SIGN : 'tleq' }
|
||
|
|
||
|
BorderStyleMap = { BorderPropertySet.SINGLE : 'brdrs',
|
||
|
BorderPropertySet.DOUBLE : 'brdrth',
|
||
|
BorderPropertySet.SHADOWED : 'brdrsh',
|
||
|
BorderPropertySet.DOUBLED : 'brdrdb',
|
||
|
BorderPropertySet.DOTTED : 'brdrdot',
|
||
|
BorderPropertySet.DASHED : 'brdrdash',
|
||
|
BorderPropertySet.HAIRLINE : 'brdrhair' }
|
||
|
|
||
|
SectionBreakTypeMap = { Section.NONE : 'sbknone',
|
||
|
Section.COLUMN : 'sbkcol',
|
||
|
Section.PAGE : 'sbkpage',
|
||
|
Section.EVEN : 'sbkeven',
|
||
|
Section.ODD : 'sbkodd' }
|
||
|
|
||
|
class Settings( list ) :
|
||
|
def __init__( self ) :
|
||
|
super( Settings, self ).__init__()
|
||
|
self._append = super( Settings, self ).append
|
||
|
|
||
|
def append( self, value, mask=None, fallback=None ) :
|
||
|
if (value is not 0) and value in [ False, None, '' ] :
|
||
|
if fallback : self._append( self, fallback )
|
||
|
|
||
|
else :
|
||
|
if mask :
|
||
|
if value is True :
|
||
|
value = mask
|
||
|
else :
|
||
|
value = mask % value
|
||
|
self._append( value )
|
||
|
|
||
|
def Join( self ) :
|
||
|
if self : return r'\%s' % '\\'.join( self )
|
||
|
return ''
|
||
|
|
||
|
def __repr__( self ) :
|
||
|
return self.Join()
|
||
|
|
||
|
class Renderer :
|
||
|
def __init__( self, write_custom_element_callback=None ) :
|
||
|
self.character_style_map = {}
|
||
|
self.paragraph_style_map = {}
|
||
|
self.WriteCustomElement = write_custom_element_callback
|
||
|
|
||
|
#
|
||
|
# All of the Rend* Functions populate a Settings object with values
|
||
|
#
|
||
|
def _RendPageProperties( self, section, settings, in_section ) :
|
||
|
# this one is different from the others as it takes the settings from a
|
||
|
if in_section :
|
||
|
#paper_size_code = 'psz%s'
|
||
|
paper_width_code = 'pgwsxn%s'
|
||
|
paper_height_code = 'pghsxn%s'
|
||
|
landscape = 'lndscpsxn'
|
||
|
margin_suffix = 'sxn'
|
||
|
|
||
|
else :
|
||
|
#paper_size_code = 'psz%s'
|
||
|
paper_width_code = 'paperw%s'
|
||
|
paper_height_code = 'paperh%s'
|
||
|
landscape = 'landscape'
|
||
|
margin_suffix = ''
|
||
|
|
||
|
#settings.append( section.Paper.Code, paper_size_code )
|
||
|
settings.append( section.Paper.Width, paper_width_code )
|
||
|
settings.append( section.Paper.Height, paper_height_code )
|
||
|
|
||
|
if section.Landscape :
|
||
|
settings.append( landscape )
|
||
|
|
||
|
if section.FirstPageNumber :
|
||
|
settings.append( section.FirstPageNumber, 'pgnstarts%s' )
|
||
|
settings.append( 'pgnrestart' )
|
||
|
|
||
|
self._RendMarginsPropertySet( section.Margins, settings, margin_suffix )
|
||
|
|
||
|
def _RendShadingPropertySet( self, shading_props, settings, prefix='' ) :
|
||
|
if not shading_props : return
|
||
|
|
||
|
settings.append( shading_props.Shading, prefix + 'shading%s' )
|
||
|
settings.append( ShadingPatternMap.get( shading_props.Pattern, False ) )
|
||
|
|
||
|
settings.append( self._colour_map.get( shading_props.Foreground, False ), prefix + 'cfpat%s' )
|
||
|
settings.append( self._colour_map.get( shading_props.Background, False ), prefix + 'cbpat%s' )
|
||
|
|
||
|
def _RendBorderPropertySet( self, edge_props, settings ) :
|
||
|
settings.append( BorderStyleMap[ edge_props.Style ] )
|
||
|
settings.append( edge_props.Width , 'brdrw%s' )
|
||
|
settings.append( self._colour_map.get( edge_props.Colour, False ), 'brdrcf%s' )
|
||
|
settings.append( edge_props.Spacing or False , 'brsp%s' )
|
||
|
|
||
|
def _RendFramePropertySet( self, frame_props, settings, tag_prefix='' ) :
|
||
|
if not frame_props : return
|
||
|
|
||
|
if frame_props.Top :
|
||
|
settings.append( tag_prefix + 'brdrt' )
|
||
|
self._RendBorderPropertySet( frame_props.Top, settings )
|
||
|
|
||
|
if frame_props.Left :
|
||
|
settings.append( tag_prefix + 'brdrl' )
|
||
|
self._RendBorderPropertySet( frame_props.Left, settings )
|
||
|
|
||
|
if frame_props.Bottom :
|
||
|
settings.append( tag_prefix + 'brdrb' )
|
||
|
self._RendBorderPropertySet( frame_props.Bottom, settings )
|
||
|
|
||
|
if frame_props.Right :
|
||
|
settings.append( tag_prefix + 'brdrr' )
|
||
|
self._RendBorderPropertySet( frame_props.Right, settings )
|
||
|
|
||
|
def _RendMarginsPropertySet( self, margin_props, settings, suffix='' ) :
|
||
|
if not margin_props : return
|
||
|
|
||
|
settings.append( margin_props.Top, 'margt' + suffix + '%s' )
|
||
|
settings.append( margin_props.Left, 'margl' + suffix + '%s' )
|
||
|
settings.append( margin_props.Bottom, 'margb' + suffix + '%s' )
|
||
|
settings.append( margin_props.Right, 'margr' + suffix + '%s' )
|
||
|
|
||
|
def _RendParagraphPropertySet( self, paragraph_props, settings ) :
|
||
|
if not paragraph_props : return
|
||
|
settings.append( ParagraphAlignmentMap[ paragraph_props.Alignment ] )
|
||
|
|
||
|
settings.append( paragraph_props.SpaceBefore, 'sb%s' )
|
||
|
settings.append( paragraph_props.SpaceAfter, 'sa%s' )
|
||
|
|
||
|
# then we have to find out all of the tabs
|
||
|
width = 0
|
||
|
for tab in paragraph_props.Tabs :
|
||
|
settings.append( TabAlignmentMap[ tab.Alignment ] )
|
||
|
settings.append( TabLeaderMap.get( tab.Leader, '' ) )
|
||
|
|
||
|
width += tab.Width or DEFAULT_TAB_WIDTH
|
||
|
settings.append( 'tx%s' % width )
|
||
|
|
||
|
settings.append( paragraph_props.PageBreakBefore, 'pagebb' )
|
||
|
|
||
|
settings.append( paragraph_props.FirstLineIndent, 'fi%s' )
|
||
|
settings.append( paragraph_props.LeftIndent, 'li%s' )
|
||
|
settings.append( paragraph_props.RightIndent, 'ri%s' )
|
||
|
|
||
|
if paragraph_props.SpaceBetweenLines :
|
||
|
if paragraph_props.SpaceBetweenLines < 0 :
|
||
|
settings.append( paragraph_props.SpaceBetweenLines, r'sl%s\slmult0' )
|
||
|
else :
|
||
|
settings.append( paragraph_props.SpaceBetweenLines, r'sl%s\slmult1' )
|
||
|
|
||
|
def _RendTextPropertySet( self, text_props, settings ) :
|
||
|
if not text_props : return
|
||
|
|
||
|
if text_props.Expansion :
|
||
|
settings.append( text_props.Expansion, 'expndtw%s' )
|
||
|
|
||
|
settings.append( text_props.Bold, 'b' )
|
||
|
settings.append( text_props.Italic, 'i' )
|
||
|
settings.append( text_props.Underline, 'ul' )
|
||
|
settings.append( text_props.DottedUnderline, 'uld' )
|
||
|
settings.append( text_props.DoubleUnderline, 'uldb' )
|
||
|
settings.append( text_props.WordUnderline, 'ulw' )
|
||
|
|
||
|
settings.append( self._font_map.get( text_props.Font, False ), 'f%s' )
|
||
|
settings.append( text_props.Size, 'fs%s' )
|
||
|
settings.append( self._colour_map.get( text_props.Colour, False ), 'cf%s' )
|
||
|
|
||
|
if text_props.Frame :
|
||
|
frame = text_props.Frame
|
||
|
settings.append( 'chbrdr' )
|
||
|
settings.append( BorderStyleMap[ frame.Style ] )
|
||
|
settings.append( frame.Width , 'brdrw%s' )
|
||
|
settings.append( self._colour_map.get( frame.Colour, False ), 'brdrcf%s' )
|
||
|
|
||
|
#
|
||
|
# All of the Write* functions will write to the internal file object
|
||
|
#
|
||
|
# the _ ones probably don't need to be used by anybody outside
|
||
|
# but the other ones like WriteTextElement could be used in the Custom
|
||
|
# callback.
|
||
|
def Write( self, document, fout ) :
|
||
|
# write all of the standard stuff based upon the first document
|
||
|
self._doc = document
|
||
|
self._fout = fout
|
||
|
self._WriteDocument ()
|
||
|
self._WriteColours ()
|
||
|
self._WriteFonts ()
|
||
|
self._WriteStyleSheet()
|
||
|
|
||
|
settings = Settings()
|
||
|
self._RendPageProperties( self._doc.Sections[ 0 ], settings, in_section=False )
|
||
|
self._write( repr( settings ) )
|
||
|
|
||
|
# handle the simplest case first, we don't need to do anymore mucking around
|
||
|
# with section headers, etc we can just rip the document out
|
||
|
if len( document.Sections ) == 1 :
|
||
|
self._WriteSection( document.Sections[ 0 ],
|
||
|
is_first = True,
|
||
|
add_header = False )
|
||
|
|
||
|
else :
|
||
|
for section_idx, section in enumerate( document.Sections ) :
|
||
|
is_first = section_idx == 0
|
||
|
add_header = True
|
||
|
self._WriteSection( section, is_first, add_header )
|
||
|
|
||
|
self._write( '}' )
|
||
|
|
||
|
del self._fout, self._doc, self._CurrentStyle
|
||
|
|
||
|
def _write( self, data, *params ) :
|
||
|
#----------------------------------
|
||
|
# begin modification
|
||
|
# by Herbert Weinhandl
|
||
|
# to convert accented characters
|
||
|
# to their rtf-compatible form
|
||
|
#for c in range( 128, 256 ) :
|
||
|
# data = data.replace( chr(c), "\'%x" % c)
|
||
|
# end modification
|
||
|
#
|
||
|
# This isn't the right place for this as it is going to do
|
||
|
# this loop for all sorts of writes, including settings, control codes, etc.
|
||
|
#
|
||
|
# I will create a def _WriteText (or something) method that is used when the
|
||
|
# actual string that is to be viewed in the document is written, this can then
|
||
|
# do the final accented character check.
|
||
|
#
|
||
|
# I left it here so that I remember to do the right thing when I have time
|
||
|
#----------------------------------
|
||
|
|
||
|
if params : data = data % params
|
||
|
self._fout.write( data )
|
||
|
|
||
|
def _WriteDocument( self ) :
|
||
|
settings = Settings()
|
||
|
|
||
|
assert Languages.IsValid ( self._doc.DefaultLanguage )
|
||
|
assert ViewKind.IsValid ( self._doc.ViewKind )
|
||
|
assert ViewZoomKind.IsValid( self._doc.ViewZoomKind )
|
||
|
assert ViewScale.IsValid ( self._doc.ViewScale )
|
||
|
|
||
|
settings.append( self._doc.DefaultLanguage, 'deflang%s' )
|
||
|
settings.append( self._doc.ViewKind , 'viewkind%s' )
|
||
|
settings.append( self._doc.ViewZoomKind , 'viewzk%s' )
|
||
|
settings.append( self._doc.ViewScale , 'viewscale%s' )
|
||
|
|
||
|
self._write( "{\\rtf1\\ansi\\ansicpg1252\\deff0%s\n" % settings )
|
||
|
|
||
|
def _WriteColours( self ) :
|
||
|
self._write( r"{\colortbl ;" )
|
||
|
|
||
|
self._colour_map = {}
|
||
|
offset = 0
|
||
|
for colour in self._doc.StyleSheet.Colours :
|
||
|
self._write( r'\red%s\green%s\blue%s;', colour.Red, colour.Green, colour.Blue )
|
||
|
self._colour_map[ colour ] = offset + 1
|
||
|
offset += 1
|
||
|
self._write( "}\n" )
|
||
|
|
||
|
def _WriteFonts( self ) :
|
||
|
self._write( r'{\fonttbl' )
|
||
|
|
||
|
self._font_map = {}
|
||
|
offset = 0
|
||
|
for font in self._doc.StyleSheet.Fonts :
|
||
|
pitch = ''
|
||
|
panose = ''
|
||
|
alternate = ''
|
||
|
if font.Pitch : pitch = r'\fprq%s' % font.Pitch
|
||
|
if font.Panose : panose = r'{\*\panose %s}' % font.Panose
|
||
|
if font.Alternate : alternate = r'{\*\falt %s}' % font.Alternate.Name
|
||
|
|
||
|
self._write( r'{\f%s\f%s%s\fcharset%s%s %s%s;}',
|
||
|
offset,
|
||
|
font.Family,
|
||
|
pitch,
|
||
|
font.CharacterSet,
|
||
|
panose,
|
||
|
font.Name,
|
||
|
alternate )
|
||
|
|
||
|
self._font_map[ font ] = offset
|
||
|
offset += 1
|
||
|
|
||
|
self._write( "}\n" )
|
||
|
|
||
|
def _WriteStyleSheet( self ) :
|
||
|
self._write( r"{\stylesheet" )
|
||
|
|
||
|
# TO DO: character styles, does anybody actually use them?
|
||
|
|
||
|
offset_map = {}
|
||
|
for idx, style in enumerate( self._doc.StyleSheet.ParagraphStyles ) :
|
||
|
offset_map[ style ] = idx
|
||
|
|
||
|
# paragraph styles
|
||
|
self.paragraph_style_map = {}
|
||
|
for idx, style in enumerate( self._doc.StyleSheet.ParagraphStyles ) :
|
||
|
|
||
|
if idx == 0 :
|
||
|
default = style
|
||
|
else :
|
||
|
self._write( '\n' )
|
||
|
|
||
|
settings = Settings()
|
||
|
|
||
|
# paragraph properties
|
||
|
self._RendParagraphPropertySet( style.ParagraphPropertySet, settings )
|
||
|
self._RendFramePropertySet ( style.FramePropertySet, settings )
|
||
|
self._RendShadingPropertySet ( style.ShadingPropertySet, settings )
|
||
|
|
||
|
# text properties
|
||
|
self._RendTextPropertySet ( style.TextStyle.TextPropertySet, settings )
|
||
|
self._RendShadingPropertySet( style.TextStyle.ShadingPropertySet, settings )
|
||
|
|
||
|
# have to take
|
||
|
based_on = '\\sbasedon%s' % offset_map.get( style.BasedOn, 0 )
|
||
|
next = '\\snext%s' % offset_map.get( style.Next, 0 )
|
||
|
|
||
|
inln = '\\s%s%s' % ( idx, settings )
|
||
|
self._write( "{%s%s%s %s;}", inln, based_on, next, style.Name )
|
||
|
|
||
|
self.paragraph_style_map[ style ] = inln
|
||
|
|
||
|
# if now style is specified for the first paragraph to be written, this one
|
||
|
# will be used
|
||
|
self._CurrentStyle = self.paragraph_style_map[ default ]
|
||
|
|
||
|
self._write( "}\n" )
|
||
|
|
||
|
def _WriteSection( self, section, is_first, add_header ) :
|
||
|
|
||
|
def WriteHF( hf, rtfword ) :
|
||
|
#if not hf : return
|
||
|
|
||
|
# if we don't have anything in the header/footer then include
|
||
|
# a blank paragraph, this stops it from picking up the header/footer
|
||
|
# from the previous section
|
||
|
# if not hf : hf = [ Paragraph( '' ) ]
|
||
|
if not hf : hf = []
|
||
|
|
||
|
self._write( '{\\%s' % rtfword )
|
||
|
self._WriteElements( hf )
|
||
|
self._write( '}\n' )
|
||
|
|
||
|
settings = Settings()
|
||
|
|
||
|
if not is_first :
|
||
|
# we need to finish off the preceding section
|
||
|
# and reset all of our defaults back to standard
|
||
|
settings.append( 'sect' )
|
||
|
|
||
|
# reset to our defaults
|
||
|
settings.append( 'sectd' )
|
||
|
|
||
|
if add_header :
|
||
|
settings.append( SectionBreakTypeMap[ section.BreakType ] )
|
||
|
self._RendPageProperties( section, settings, in_section=True )
|
||
|
|
||
|
settings.append( section.HeaderY, 'headery%s' )
|
||
|
settings.append( section.FooterY, 'footery%s' )
|
||
|
|
||
|
# write all of these out now as we need to do a write elements in the
|
||
|
# next section
|
||
|
self._write( repr( settings ) )
|
||
|
|
||
|
# finally after all that has settled down we can do the
|
||
|
# headers and footers
|
||
|
if section.FirstHeader or section.FirstFooter :
|
||
|
# include the titlepg flag if the first page has a special format
|
||
|
self._write( r'\titlepg' )
|
||
|
WriteHF( section.FirstHeader, 'headerf' )
|
||
|
WriteHF( section.FirstFooter, 'footerf' )
|
||
|
|
||
|
WriteHF( section.Header, 'header' )
|
||
|
WriteHF( section.Footer, 'footer' )
|
||
|
|
||
|
# and at last the contents of the section that actually appear on the page
|
||
|
self._WriteElements( section )
|
||
|
|
||
|
def _WriteElements( self, elements ) :
|
||
|
new_line = ''
|
||
|
for element in elements :
|
||
|
self._write( new_line )
|
||
|
new_line = '\n'
|
||
|
|
||
|
clss = element.__class__
|
||
|
|
||
|
if clss == Paragraph :
|
||
|
self.WriteParagraphElement( element )
|
||
|
|
||
|
elif clss == Table :
|
||
|
self.WriteTableElement( element )
|
||
|
|
||
|
elif clss == StringType :
|
||
|
self.WriteParagraphElement( Paragraph( element ) )
|
||
|
|
||
|
elif clss in [ RawCode, Image ] :
|
||
|
self.WriteRawCode( element )
|
||
|
|
||
|
#elif clss == List :
|
||
|
# self._HandleListElement( element )
|
||
|
|
||
|
elif self.WriteCustomElement :
|
||
|
self.WriteCustomElement( self, element )
|
||
|
|
||
|
else :
|
||
|
raise Exception( "Don't know how to handle elements of type %s" % clss )
|
||
|
|
||
|
def WriteParagraphElement( self, paragraph_elem, tag_prefix='', tag_suffix=r'\par', opening='{', closing='}' ) :
|
||
|
|
||
|
# the tag_prefix and the tag_suffix take care of paragraphs in tables. A
|
||
|
# paragraph in a table requires and extra tag at the front (intbl) and we
|
||
|
# don't want the ending tag everytime. We want it for all paragraphs but
|
||
|
# the last.
|
||
|
|
||
|
overrides = Settings()
|
||
|
self._RendParagraphPropertySet( paragraph_elem.Properties, overrides )
|
||
|
self._RendFramePropertySet ( paragraph_elem.Frame, overrides )
|
||
|
self._RendShadingPropertySet ( paragraph_elem.Shading, overrides )
|
||
|
|
||
|
# when writing the RTF the style is carried from the previous paragraph to the next,
|
||
|
# so if the currently written paragraph has a style then make it the current one,
|
||
|
# otherwise leave it as it was
|
||
|
self._CurrentStyle = self.paragraph_style_map.get( paragraph_elem.Style, self._CurrentStyle )
|
||
|
|
||
|
self._write( r'%s\pard\plain%s %s%s ' % ( opening, tag_prefix, self._CurrentStyle, overrides ) )
|
||
|
|
||
|
for element in paragraph_elem :
|
||
|
|
||
|
if isinstance( element, StringType ) :
|
||
|
self._write( element )
|
||
|
|
||
|
elif isinstance( element, RawCode ) :
|
||
|
self._write( element.Data )
|
||
|
|
||
|
elif isinstance( element, Text ) :
|
||
|
self.WriteTextElement( element )
|
||
|
|
||
|
elif isinstance( element, Inline ) :
|
||
|
self.WriteInlineElement( element )
|
||
|
|
||
|
elif element == TAB :
|
||
|
self._write( r'\tab ' )
|
||
|
|
||
|
elif element == LINE :
|
||
|
self._write( r'\line ' )
|
||
|
|
||
|
elif self.WriteCustomElement :
|
||
|
self.WriteCustomElement( self, element )
|
||
|
|
||
|
else :
|
||
|
raise Exception( 'Don\'t know how to handle %s' % element )
|
||
|
|
||
|
self._write( tag_suffix + closing )
|
||
|
|
||
|
def WriteRawCode( self, raw_elem ) :
|
||
|
self._write( raw_elem.Data )
|
||
|
|
||
|
def WriteTextElement( self, text_elem ) :
|
||
|
overrides = Settings()
|
||
|
|
||
|
self._RendTextPropertySet ( text_elem.Properties, overrides )
|
||
|
self._RendShadingPropertySet( text_elem.Shading, overrides, 'ch' )
|
||
|
|
||
|
# write the wrapper and then let the custom handler have a go
|
||
|
if overrides : self._write( '{%s ' % repr( overrides ) )
|
||
|
|
||
|
# if the data is just a string then we can now write it
|
||
|
if isinstance( text_elem.Data, StringType ) :
|
||
|
self._write( text_elem.Data or '' )
|
||
|
|
||
|
elif text_elem.Data == TAB :
|
||
|
self._write( r'\tab ' )
|
||
|
|
||
|
else :
|
||
|
self.WriteCustomElement( self, text_elem.Data )
|
||
|
|
||
|
if overrides : self._write( '}' )
|
||
|
|
||
|
def WriteInlineElement( self, inline_elem ) :
|
||
|
overrides = Settings()
|
||
|
|
||
|
self._RendTextPropertySet ( inline_elem.Properties, overrides )
|
||
|
self._RendShadingPropertySet( inline_elem.Shading, overrides, 'ch' )
|
||
|
|
||
|
# write the wrapper and then let the custom handler have a go
|
||
|
if overrides : self._write( '{%s ' % repr( overrides ) )
|
||
|
|
||
|
for element in inline_elem :
|
||
|
# if the data is just a string then we can now write it
|
||
|
if isinstance( element, StringType ) :
|
||
|
self._write( element )
|
||
|
|
||
|
elif isinstance( element, RawCode ) :
|
||
|
self._write( element.Data )
|
||
|
|
||
|
elif element == TAB :
|
||
|
self._write( r'\tab ' )
|
||
|
|
||
|
elif element == LINE :
|
||
|
self._write( r'\line ' )
|
||
|
|
||
|
else :
|
||
|
self.WriteCustomElement( self, element )
|
||
|
|
||
|
if overrides : self._write( '}' )
|
||
|
|
||
|
def WriteText( self, text ) :
|
||
|
self._write( text or '' )
|
||
|
|
||
|
def WriteTableElement( self, table_elem ) :
|
||
|
|
||
|
vmerge = [ False ] * table_elem.ColumnCount
|
||
|
for height, cells in table_elem.Rows :
|
||
|
|
||
|
# calculate the right hand edge of the cells taking into account the spans
|
||
|
offset = table_elem.LeftOffset or 0
|
||
|
cellx = []
|
||
|
cell_idx = 0
|
||
|
for cell in cells :
|
||
|
cellx.append( offset + sum( table_elem.ColumnWidths[ : cell_idx + cell.Span ] ) )
|
||
|
cell_idx += cell.Span
|
||
|
|
||
|
self._write( r'{\trowd' )
|
||
|
|
||
|
settings = Settings()
|
||
|
|
||
|
# the spec says that this value is mandatory and I think that 108 is the default value
|
||
|
# so I'll take care of it here
|
||
|
settings.append( table_elem.GapBetweenCells or 108, 'trgaph%s' )
|
||
|
settings.append( TableAlignmentMap[ table_elem.Alignment ] )
|
||
|
settings.append( height, 'trrh%s' )
|
||
|
settings.append( table_elem.LeftOffset, 'trleft%s' )
|
||
|
|
||
|
width = table_elem.LeftOffset or 0
|
||
|
for idx, cell in enumerate( cells ) :
|
||
|
self._RendFramePropertySet ( cell.Frame, settings, 'cl' )
|
||
|
|
||
|
# cells don't have margins so I don't know why I was doing this
|
||
|
# I think it might have an affect in some versions of some WPs.
|
||
|
#self._RendMarginsPropertySet( cell.Margins, settings, 'cl' )
|
||
|
|
||
|
# if we are starting to merge or if this one is the first in what is
|
||
|
# probably a series of merges then start the vertical merging
|
||
|
if cell.StartVerticalMerge or (cell.VerticalMerge and not vmerge[ idx ]) :
|
||
|
settings.append( 'clvmgf' )
|
||
|
vmerge[ idx ] = True
|
||
|
|
||
|
elif cell.VerticalMerge :
|
||
|
#..continuing a merge
|
||
|
settings.append( 'clvmrg' )
|
||
|
|
||
|
else :
|
||
|
#..no merging going on so make sure that it is off
|
||
|
vmerge[ idx ] = False
|
||
|
|
||
|
# for any cell in the next row that is covered by this span we
|
||
|
# need to run off the vertical merging as we don't want them
|
||
|
# merging up into this spanned cell
|
||
|
for vmerge_idx in range( idx + 1, idx + cell.Span - 1 ) :
|
||
|
vmerge[ vmerge_idx ] = False
|
||
|
|
||
|
settings.append( CellAlignmentMap[ cell.Alignment ] )
|
||
|
settings.append( CellFlowMap[ cell.Flow ] )
|
||
|
|
||
|
# this terminates the definition of a cell and represents the right most edge of the cell from the left margin
|
||
|
settings.append( cellx[ idx ], 'cellx%s' )
|
||
|
|
||
|
self._write( repr( settings ) )
|
||
|
|
||
|
for cell in cells :
|
||
|
if len( cell ) :
|
||
|
last_idx = len( cell ) - 1
|
||
|
for element_idx, element in enumerate( cell ) :
|
||
|
# wrap plain strings in paragraph tags
|
||
|
if isinstance( element, StringType ) :
|
||
|
element = Paragraph( element )
|
||
|
|
||
|
# don't forget the prefix or else word crashes and does all sorts of strange things
|
||
|
if element_idx == last_idx :
|
||
|
self.WriteParagraphElement( element, tag_prefix=r'\intbl', tag_suffix='', opening='', closing='' )
|
||
|
|
||
|
else :
|
||
|
self.WriteParagraphElement( element, tag_prefix=r'\intbl', opening='', closing='' )
|
||
|
|
||
|
self._write( r'\cell' )
|
||
|
|
||
|
else :
|
||
|
self._write( r'\pard\intbl\cell' )
|
||
|
|
||
|
self._write( '\\row}\n' )
|
||
|
|