1 | from copy import deepcopy |
---|
2 | |
---|
3 | from .Elements import * |
---|
4 | from .Constants import PY2 |
---|
5 | if PY2: |
---|
6 | StringType = basestring |
---|
7 | else: |
---|
8 | StringType = str |
---|
9 | |
---|
10 | DEFAULT_TAB_WIDTH = 720 |
---|
11 | |
---|
12 | ParagraphAlignmentMap = { ParagraphPropertySet.LEFT : 'ql', |
---|
13 | ParagraphPropertySet.RIGHT : 'qr', |
---|
14 | ParagraphPropertySet.CENTER : 'qc', |
---|
15 | ParagraphPropertySet.JUSTIFY : 'qj', |
---|
16 | ParagraphPropertySet.DISTRIBUTE : 'qd' } |
---|
17 | |
---|
18 | TabAlignmentMap = { TabPropertySet.LEFT : '', |
---|
19 | TabPropertySet.RIGHT : 'tqr', |
---|
20 | TabPropertySet.CENTER : 'tqc', |
---|
21 | TabPropertySet.DECIMAL : 'tqdec' } |
---|
22 | |
---|
23 | TableAlignmentMap = { Table.LEFT : 'trql', |
---|
24 | Table.RIGHT : 'trqr', |
---|
25 | Table.CENTER : 'trqc' } |
---|
26 | |
---|
27 | CellAlignmentMap = { Cell.ALIGN_TOP : '', # clvertalt |
---|
28 | Cell.ALIGN_CENTER : 'clvertalc', |
---|
29 | Cell.ALIGN_BOTTOM : 'clvertalb' } |
---|
30 | |
---|
31 | CellFlowMap = { Cell.FLOW_LR_TB : '', # cltxlrtb, Text in a cell flows from left to right and top to bottom (default) |
---|
32 | Cell.FLOW_RL_TB : 'cltxtbrl', # Text in a cell flows right to left and top to bottom |
---|
33 | Cell.FLOW_LR_BT : 'cltxbtlr', # Text in a cell flows left to right and bottom to top |
---|
34 | Cell.FLOW_VERTICAL_LR_TB : 'cltxlrtbv', # Text in a cell flows left to right and top to bottom, vertical |
---|
35 | Cell.FLOW_VERTICAL_TB_RL : 'cltxtbrlv' } # Text in a cell flows top to bottom and right to left, vertical |
---|
36 | |
---|
37 | ShadingPatternMap = { ShadingPropertySet.HORIZONTAL : 'bghoriz', |
---|
38 | ShadingPropertySet.VERTICAL : 'bgvert', |
---|
39 | ShadingPropertySet.FORWARD_DIAGONAL : 'bgfdiag', |
---|
40 | ShadingPropertySet.BACKWARD_DIAGONAL : 'bgbdiag', |
---|
41 | ShadingPropertySet.VERTICAL_CROSS : 'bgcross', |
---|
42 | ShadingPropertySet.DIAGONAL_CROSS : 'bgdcross', |
---|
43 | ShadingPropertySet.DARK_HORIZONTAL : 'bgdkhoriz', |
---|
44 | ShadingPropertySet.DARK_VERTICAL : 'bgdkvert', |
---|
45 | ShadingPropertySet.DARK_FORWARD_DIAGONAL : 'bgdkfdiag', |
---|
46 | ShadingPropertySet.DARK_BACKWARD_DIAGONAL : 'bgdkbdiag', |
---|
47 | ShadingPropertySet.DARK_VERTICAL_CROSS : 'bgdkcross', |
---|
48 | ShadingPropertySet.DARK_DIAGONAL_CROSS : 'bgdkdcross' } |
---|
49 | |
---|
50 | TabLeaderMap = { TabPropertySet.DOTS : 'tldot', |
---|
51 | TabPropertySet.HYPHENS : 'tlhyph', |
---|
52 | TabPropertySet.UNDERLINE : 'tlul', |
---|
53 | TabPropertySet.THICK_LINE : 'tlth', |
---|
54 | TabPropertySet.EQUAL_SIGN : 'tleq' } |
---|
55 | |
---|
56 | BorderStyleMap = { BorderPropertySet.SINGLE : 'brdrs', |
---|
57 | BorderPropertySet.DOUBLE : 'brdrth', |
---|
58 | BorderPropertySet.SHADOWED : 'brdrsh', |
---|
59 | BorderPropertySet.DOUBLED : 'brdrdb', |
---|
60 | BorderPropertySet.DOTTED : 'brdrdot', |
---|
61 | BorderPropertySet.DASHED : 'brdrdash', |
---|
62 | BorderPropertySet.HAIRLINE : 'brdrhair' } |
---|
63 | |
---|
64 | SectionBreakTypeMap = { Section.NONE : 'sbknone', |
---|
65 | Section.COLUMN : 'sbkcol', |
---|
66 | Section.PAGE : 'sbkpage', |
---|
67 | Section.EVEN : 'sbkeven', |
---|
68 | Section.ODD : 'sbkodd' } |
---|
69 | |
---|
70 | class Settings( list ) : |
---|
71 | def __init__( self ) : |
---|
72 | super( Settings, self ).__init__() |
---|
73 | self._append = super( Settings, self ).append |
---|
74 | |
---|
75 | def append( self, value, mask=None, fallback=None ) : |
---|
76 | if (value is not 0) and value in [ False, None, '' ] : |
---|
77 | if fallback : self._append( self, fallback ) |
---|
78 | |
---|
79 | else : |
---|
80 | if mask : |
---|
81 | if value is True : |
---|
82 | value = mask |
---|
83 | else : |
---|
84 | value = mask % value |
---|
85 | self._append( value ) |
---|
86 | |
---|
87 | def Join( self ) : |
---|
88 | if self : return r'\%s' % '\\'.join( self ) |
---|
89 | return '' |
---|
90 | |
---|
91 | def __repr__( self ) : |
---|
92 | return self.Join() |
---|
93 | |
---|
94 | class Renderer : |
---|
95 | def __init__( self, write_custom_element_callback=None ) : |
---|
96 | self.character_style_map = {} |
---|
97 | self.paragraph_style_map = {} |
---|
98 | self.WriteCustomElement = write_custom_element_callback |
---|
99 | |
---|
100 | # |
---|
101 | # All of the Rend* Functions populate a Settings object with values |
---|
102 | # |
---|
103 | def _RendPageProperties( self, section, settings, in_section ) : |
---|
104 | # this one is different from the others as it takes the settings from a |
---|
105 | if in_section : |
---|
106 | #paper_size_code = 'psz%s' |
---|
107 | paper_width_code = 'pgwsxn%s' |
---|
108 | paper_height_code = 'pghsxn%s' |
---|
109 | landscape = 'lndscpsxn' |
---|
110 | margin_suffix = 'sxn' |
---|
111 | |
---|
112 | else : |
---|
113 | #paper_size_code = 'psz%s' |
---|
114 | paper_width_code = 'paperw%s' |
---|
115 | paper_height_code = 'paperh%s' |
---|
116 | landscape = 'landscape' |
---|
117 | margin_suffix = '' |
---|
118 | |
---|
119 | #settings.append( section.Paper.Code, paper_size_code ) |
---|
120 | settings.append( section.Paper.Width, paper_width_code ) |
---|
121 | settings.append( section.Paper.Height, paper_height_code ) |
---|
122 | |
---|
123 | if section.Landscape : |
---|
124 | settings.append( landscape ) |
---|
125 | |
---|
126 | if section.FirstPageNumber : |
---|
127 | settings.append( section.FirstPageNumber, 'pgnstarts%s' ) |
---|
128 | settings.append( 'pgnrestart' ) |
---|
129 | |
---|
130 | self._RendMarginsPropertySet( section.Margins, settings, margin_suffix ) |
---|
131 | |
---|
132 | def _RendShadingPropertySet( self, shading_props, settings, prefix='' ) : |
---|
133 | if not shading_props : return |
---|
134 | |
---|
135 | settings.append( shading_props.Shading, prefix + 'shading%s' ) |
---|
136 | settings.append( ShadingPatternMap.get( shading_props.Pattern, False ) ) |
---|
137 | |
---|
138 | settings.append( self._colour_map.get( shading_props.Foreground, False ), prefix + 'cfpat%s' ) |
---|
139 | settings.append( self._colour_map.get( shading_props.Background, False ), prefix + 'cbpat%s' ) |
---|
140 | |
---|
141 | def _RendBorderPropertySet( self, edge_props, settings ) : |
---|
142 | settings.append( BorderStyleMap[ edge_props.Style ] ) |
---|
143 | settings.append( edge_props.Width , 'brdrw%s' ) |
---|
144 | settings.append( self._colour_map.get( edge_props.Colour, False ), 'brdrcf%s' ) |
---|
145 | settings.append( edge_props.Spacing or False , 'brsp%s' ) |
---|
146 | |
---|
147 | def _RendFramePropertySet( self, frame_props, settings, tag_prefix='' ) : |
---|
148 | if not frame_props : return |
---|
149 | |
---|
150 | if frame_props.Top : |
---|
151 | settings.append( tag_prefix + 'brdrt' ) |
---|
152 | self._RendBorderPropertySet( frame_props.Top, settings ) |
---|
153 | |
---|
154 | if frame_props.Left : |
---|
155 | settings.append( tag_prefix + 'brdrl' ) |
---|
156 | self._RendBorderPropertySet( frame_props.Left, settings ) |
---|
157 | |
---|
158 | if frame_props.Bottom : |
---|
159 | settings.append( tag_prefix + 'brdrb' ) |
---|
160 | self._RendBorderPropertySet( frame_props.Bottom, settings ) |
---|
161 | |
---|
162 | if frame_props.Right : |
---|
163 | settings.append( tag_prefix + 'brdrr' ) |
---|
164 | self._RendBorderPropertySet( frame_props.Right, settings ) |
---|
165 | |
---|
166 | def _RendMarginsPropertySet( self, margin_props, settings, suffix='' ) : |
---|
167 | if not margin_props : return |
---|
168 | |
---|
169 | settings.append( margin_props.Top, 'margt' + suffix + '%s' ) |
---|
170 | settings.append( margin_props.Left, 'margl' + suffix + '%s' ) |
---|
171 | settings.append( margin_props.Bottom, 'margb' + suffix + '%s' ) |
---|
172 | settings.append( margin_props.Right, 'margr' + suffix + '%s' ) |
---|
173 | |
---|
174 | def _RendParagraphPropertySet( self, paragraph_props, settings ) : |
---|
175 | if not paragraph_props : return |
---|
176 | settings.append( ParagraphAlignmentMap[ paragraph_props.Alignment ] ) |
---|
177 | |
---|
178 | settings.append( paragraph_props.SpaceBefore, 'sb%s' ) |
---|
179 | settings.append( paragraph_props.SpaceAfter, 'sa%s' ) |
---|
180 | |
---|
181 | # then we have to find out all of the tabs |
---|
182 | width = 0 |
---|
183 | for tab in paragraph_props.Tabs : |
---|
184 | settings.append( TabAlignmentMap[ tab.Alignment ] ) |
---|
185 | settings.append( TabLeaderMap.get( tab.Leader, '' ) ) |
---|
186 | |
---|
187 | width += tab.Width or DEFAULT_TAB_WIDTH |
---|
188 | settings.append( 'tx%s' % width ) |
---|
189 | |
---|
190 | settings.append( paragraph_props.PageBreakBefore, 'pagebb' ) |
---|
191 | |
---|
192 | settings.append( paragraph_props.FirstLineIndent, 'fi%s' ) |
---|
193 | settings.append( paragraph_props.LeftIndent, 'li%s' ) |
---|
194 | settings.append( paragraph_props.RightIndent, 'ri%s' ) |
---|
195 | |
---|
196 | if paragraph_props.SpaceBetweenLines : |
---|
197 | if paragraph_props.SpaceBetweenLines < 0 : |
---|
198 | settings.append( paragraph_props.SpaceBetweenLines, r'sl%s\slmult0' ) |
---|
199 | else : |
---|
200 | settings.append( paragraph_props.SpaceBetweenLines, r'sl%s\slmult1' ) |
---|
201 | |
---|
202 | def _RendTextPropertySet( self, text_props, settings ) : |
---|
203 | if not text_props : return |
---|
204 | |
---|
205 | if text_props.Expansion : |
---|
206 | settings.append( text_props.Expansion, 'expndtw%s' ) |
---|
207 | |
---|
208 | settings.append( text_props.Bold, 'b' ) |
---|
209 | settings.append( text_props.Italic, 'i' ) |
---|
210 | settings.append( text_props.Underline, 'ul' ) |
---|
211 | settings.append( text_props.DottedUnderline, 'uld' ) |
---|
212 | settings.append( text_props.DoubleUnderline, 'uldb' ) |
---|
213 | settings.append( text_props.WordUnderline, 'ulw' ) |
---|
214 | |
---|
215 | settings.append( self._font_map.get( text_props.Font, False ), 'f%s' ) |
---|
216 | settings.append( text_props.Size, 'fs%s' ) |
---|
217 | settings.append( self._colour_map.get( text_props.Colour, False ), 'cf%s' ) |
---|
218 | |
---|
219 | if text_props.Frame : |
---|
220 | frame = text_props.Frame |
---|
221 | settings.append( 'chbrdr' ) |
---|
222 | settings.append( BorderStyleMap[ frame.Style ] ) |
---|
223 | settings.append( frame.Width , 'brdrw%s' ) |
---|
224 | settings.append( self._colour_map.get( frame.Colour, False ), 'brdrcf%s' ) |
---|
225 | |
---|
226 | # |
---|
227 | # All of the Write* functions will write to the internal file object |
---|
228 | # |
---|
229 | # the _ ones probably don't need to be used by anybody outside |
---|
230 | # but the other ones like WriteTextElement could be used in the Custom |
---|
231 | # callback. |
---|
232 | def Write( self, document, fout ) : |
---|
233 | # write all of the standard stuff based upon the first document |
---|
234 | self._doc = document |
---|
235 | self._fout = fout |
---|
236 | self._WriteDocument () |
---|
237 | self._WriteColours () |
---|
238 | self._WriteFonts () |
---|
239 | self._WriteStyleSheet() |
---|
240 | |
---|
241 | settings = Settings() |
---|
242 | self._RendPageProperties( self._doc.Sections[ 0 ], settings, in_section=False ) |
---|
243 | self._write( repr( settings ) ) |
---|
244 | |
---|
245 | # handle the simplest case first, we don't need to do anymore mucking around |
---|
246 | # with section headers, etc we can just rip the document out |
---|
247 | if len( document.Sections ) == 1 : |
---|
248 | self._WriteSection( document.Sections[ 0 ], |
---|
249 | is_first = True, |
---|
250 | add_header = False ) |
---|
251 | |
---|
252 | else : |
---|
253 | for section_idx, section in enumerate( document.Sections ) : |
---|
254 | is_first = section_idx == 0 |
---|
255 | add_header = True |
---|
256 | self._WriteSection( section, is_first, add_header ) |
---|
257 | |
---|
258 | self._write( '}' ) |
---|
259 | |
---|
260 | del self._fout, self._doc, self._CurrentStyle |
---|
261 | |
---|
262 | def _write( self, data, *params ) : |
---|
263 | #---------------------------------- |
---|
264 | # begin modification |
---|
265 | # by Herbert Weinhandl |
---|
266 | # to convert accented characters |
---|
267 | # to their rtf-compatible form |
---|
268 | #for c in range( 128, 256 ) : |
---|
269 | # data = data.replace( chr(c), "\'%x" % c) |
---|
270 | # end modification |
---|
271 | # |
---|
272 | # This isn't the right place for this as it is going to do |
---|
273 | # this loop for all sorts of writes, including settings, control codes, etc. |
---|
274 | # |
---|
275 | # I will create a def _WriteText (or something) method that is used when the |
---|
276 | # actual string that is to be viewed in the document is written, this can then |
---|
277 | # do the final accented character check. |
---|
278 | # |
---|
279 | # I left it here so that I remember to do the right thing when I have time |
---|
280 | #---------------------------------- |
---|
281 | |
---|
282 | if params : data = data % params |
---|
283 | self._fout.write( data ) |
---|
284 | |
---|
285 | def _WriteDocument( self ) : |
---|
286 | settings = Settings() |
---|
287 | |
---|
288 | assert Languages.IsValid ( self._doc.DefaultLanguage ) |
---|
289 | assert ViewKind.IsValid ( self._doc.ViewKind ) |
---|
290 | assert ViewZoomKind.IsValid( self._doc.ViewZoomKind ) |
---|
291 | assert ViewScale.IsValid ( self._doc.ViewScale ) |
---|
292 | |
---|
293 | settings.append( self._doc.DefaultLanguage, 'deflang%s' ) |
---|
294 | settings.append( self._doc.ViewKind , 'viewkind%s' ) |
---|
295 | settings.append( self._doc.ViewZoomKind , 'viewzk%s' ) |
---|
296 | settings.append( self._doc.ViewScale , 'viewscale%s' ) |
---|
297 | |
---|
298 | self._write( "{\\rtf1\\ansi\\ansicpg1252\\deff0%s\n" % settings ) |
---|
299 | |
---|
300 | def _WriteColours( self ) : |
---|
301 | self._write( r"{\colortbl ;" ) |
---|
302 | |
---|
303 | self._colour_map = {} |
---|
304 | offset = 0 |
---|
305 | for colour in self._doc.StyleSheet.Colours : |
---|
306 | self._write( r'\red%s\green%s\blue%s;', colour.Red, colour.Green, colour.Blue ) |
---|
307 | self._colour_map[ colour ] = offset + 1 |
---|
308 | offset += 1 |
---|
309 | self._write( "}\n" ) |
---|
310 | |
---|
311 | def _WriteFonts( self ) : |
---|
312 | self._write( r'{\fonttbl' ) |
---|
313 | |
---|
314 | self._font_map = {} |
---|
315 | offset = 0 |
---|
316 | for font in self._doc.StyleSheet.Fonts : |
---|
317 | pitch = '' |
---|
318 | panose = '' |
---|
319 | alternate = '' |
---|
320 | if font.Pitch : pitch = r'\fprq%s' % font.Pitch |
---|
321 | if font.Panose : panose = r'{\*\panose %s}' % font.Panose |
---|
322 | if font.Alternate : alternate = r'{\*\falt %s}' % font.Alternate.Name |
---|
323 | |
---|
324 | self._write( r'{\f%s\f%s%s\fcharset%s%s %s%s;}', |
---|
325 | offset, |
---|
326 | font.Family, |
---|
327 | pitch, |
---|
328 | font.CharacterSet, |
---|
329 | panose, |
---|
330 | font.Name, |
---|
331 | alternate ) |
---|
332 | |
---|
333 | self._font_map[ font ] = offset |
---|
334 | offset += 1 |
---|
335 | |
---|
336 | self._write( "}\n" ) |
---|
337 | |
---|
338 | def _WriteStyleSheet( self ) : |
---|
339 | self._write( r"{\stylesheet" ) |
---|
340 | |
---|
341 | # TO DO: character styles, does anybody actually use them? |
---|
342 | |
---|
343 | offset_map = {} |
---|
344 | for idx, style in enumerate( self._doc.StyleSheet.ParagraphStyles ) : |
---|
345 | offset_map[ style ] = idx |
---|
346 | |
---|
347 | # paragraph styles |
---|
348 | self.paragraph_style_map = {} |
---|
349 | for idx, style in enumerate( self._doc.StyleSheet.ParagraphStyles ) : |
---|
350 | |
---|
351 | if idx == 0 : |
---|
352 | default = style |
---|
353 | else : |
---|
354 | self._write( '\n' ) |
---|
355 | |
---|
356 | settings = Settings() |
---|
357 | |
---|
358 | # paragraph properties |
---|
359 | self._RendParagraphPropertySet( style.ParagraphPropertySet, settings ) |
---|
360 | self._RendFramePropertySet ( style.FramePropertySet, settings ) |
---|
361 | self._RendShadingPropertySet ( style.ShadingPropertySet, settings ) |
---|
362 | |
---|
363 | # text properties |
---|
364 | self._RendTextPropertySet ( style.TextStyle.TextPropertySet, settings ) |
---|
365 | self._RendShadingPropertySet( style.TextStyle.ShadingPropertySet, settings ) |
---|
366 | |
---|
367 | # have to take |
---|
368 | based_on = '\\sbasedon%s' % offset_map.get( style.BasedOn, 0 ) |
---|
369 | next = '\\snext%s' % offset_map.get( style.Next, 0 ) |
---|
370 | |
---|
371 | inln = '\\s%s%s' % ( idx, settings ) |
---|
372 | self._write( "{%s%s%s %s;}", inln, based_on, next, style.Name ) |
---|
373 | |
---|
374 | self.paragraph_style_map[ style ] = inln |
---|
375 | |
---|
376 | # if now style is specified for the first paragraph to be written, this one |
---|
377 | # will be used |
---|
378 | self._CurrentStyle = self.paragraph_style_map[ default ] |
---|
379 | |
---|
380 | self._write( "}\n" ) |
---|
381 | |
---|
382 | def _WriteSection( self, section, is_first, add_header ) : |
---|
383 | |
---|
384 | def WriteHF( hf, rtfword ) : |
---|
385 | #if not hf : return |
---|
386 | |
---|
387 | # if we don't have anything in the header/footer then include |
---|
388 | # a blank paragraph, this stops it from picking up the header/footer |
---|
389 | # from the previous section |
---|
390 | # if not hf : hf = [ Paragraph( '' ) ] |
---|
391 | if not hf : hf = [] |
---|
392 | |
---|
393 | self._write( '{\\%s' % rtfword ) |
---|
394 | self._WriteElements( hf ) |
---|
395 | self._write( '}\n' ) |
---|
396 | |
---|
397 | settings = Settings() |
---|
398 | |
---|
399 | if not is_first : |
---|
400 | # we need to finish off the preceding section |
---|
401 | # and reset all of our defaults back to standard |
---|
402 | settings.append( 'sect' ) |
---|
403 | |
---|
404 | # reset to our defaults |
---|
405 | settings.append( 'sectd' ) |
---|
406 | |
---|
407 | if add_header : |
---|
408 | settings.append( SectionBreakTypeMap[ section.BreakType ] ) |
---|
409 | self._RendPageProperties( section, settings, in_section=True ) |
---|
410 | |
---|
411 | settings.append( section.HeaderY, 'headery%s' ) |
---|
412 | settings.append( section.FooterY, 'footery%s' ) |
---|
413 | |
---|
414 | # write all of these out now as we need to do a write elements in the |
---|
415 | # next section |
---|
416 | self._write( repr( settings ) ) |
---|
417 | |
---|
418 | # finally after all that has settled down we can do the |
---|
419 | # headers and footers |
---|
420 | if section.FirstHeader or section.FirstFooter : |
---|
421 | # include the titlepg flag if the first page has a special format |
---|
422 | self._write( r'\titlepg' ) |
---|
423 | WriteHF( section.FirstHeader, 'headerf' ) |
---|
424 | WriteHF( section.FirstFooter, 'footerf' ) |
---|
425 | |
---|
426 | WriteHF( section.Header, 'header' ) |
---|
427 | WriteHF( section.Footer, 'footer' ) |
---|
428 | |
---|
429 | # and at last the contents of the section that actually appear on the page |
---|
430 | self._WriteElements( section ) |
---|
431 | |
---|
432 | def _WriteElements( self, elements ) : |
---|
433 | new_line = '' |
---|
434 | for element in elements : |
---|
435 | self._write( new_line ) |
---|
436 | new_line = '\n' |
---|
437 | |
---|
438 | clss = element.__class__ |
---|
439 | |
---|
440 | if clss == Paragraph : |
---|
441 | self.WriteParagraphElement( element ) |
---|
442 | |
---|
443 | elif clss == Table : |
---|
444 | self.WriteTableElement( element ) |
---|
445 | |
---|
446 | elif ininstance(element, StringType) : |
---|
447 | self.WriteParagraphElement( Paragraph( element ) ) |
---|
448 | |
---|
449 | elif clss in [ RawCode, Image ] : |
---|
450 | self.WriteRawCode( element ) |
---|
451 | |
---|
452 | #elif clss == List : |
---|
453 | # self._HandleListElement( element ) |
---|
454 | |
---|
455 | elif self.WriteCustomElement : |
---|
456 | self.WriteCustomElement( self, element ) |
---|
457 | |
---|
458 | else : |
---|
459 | raise Exception( "Don't know how to handle elements of type %s" % clss ) |
---|
460 | |
---|
461 | def WriteParagraphElement( self, paragraph_elem, tag_prefix='', tag_suffix=r'\par', opening='{', closing='}' ) : |
---|
462 | |
---|
463 | # the tag_prefix and the tag_suffix take care of paragraphs in tables. A |
---|
464 | # paragraph in a table requires and extra tag at the front (intbl) and we |
---|
465 | # don't want the ending tag everytime. We want it for all paragraphs but |
---|
466 | # the last. |
---|
467 | |
---|
468 | overrides = Settings() |
---|
469 | self._RendParagraphPropertySet( paragraph_elem.Properties, overrides ) |
---|
470 | self._RendFramePropertySet ( paragraph_elem.Frame, overrides ) |
---|
471 | self._RendShadingPropertySet ( paragraph_elem.Shading, overrides ) |
---|
472 | |
---|
473 | # when writing the RTF the style is carried from the previous paragraph to the next, |
---|
474 | # so if the currently written paragraph has a style then make it the current one, |
---|
475 | # otherwise leave it as it was |
---|
476 | self._CurrentStyle = self.paragraph_style_map.get( paragraph_elem.Style, self._CurrentStyle ) |
---|
477 | |
---|
478 | self._write( r'%s\pard\plain%s %s%s ' % ( opening, tag_prefix, self._CurrentStyle, overrides ) ) |
---|
479 | |
---|
480 | for element in paragraph_elem : |
---|
481 | |
---|
482 | if isinstance( element, StringType ) : |
---|
483 | self._write( element ) |
---|
484 | |
---|
485 | elif isinstance( element, RawCode ) : |
---|
486 | self._write( element.Data ) |
---|
487 | |
---|
488 | elif isinstance( element, Text ) : |
---|
489 | self.WriteTextElement( element ) |
---|
490 | |
---|
491 | elif isinstance( element, Inline ) : |
---|
492 | self.WriteInlineElement( element ) |
---|
493 | |
---|
494 | elif element == TAB : |
---|
495 | self._write( r'\tab ' ) |
---|
496 | |
---|
497 | elif element == LINE : |
---|
498 | self._write( r'\line ' ) |
---|
499 | |
---|
500 | elif self.WriteCustomElement : |
---|
501 | self.WriteCustomElement( self, element ) |
---|
502 | |
---|
503 | else : |
---|
504 | raise Exception( 'Don\'t know how to handle %s' % element ) |
---|
505 | |
---|
506 | self._write( tag_suffix + closing ) |
---|
507 | |
---|
508 | def WriteRawCode( self, raw_elem ) : |
---|
509 | self._write( raw_elem.Data ) |
---|
510 | |
---|
511 | def WriteTextElement( self, text_elem ) : |
---|
512 | overrides = Settings() |
---|
513 | |
---|
514 | self._RendTextPropertySet ( text_elem.Properties, overrides ) |
---|
515 | self._RendShadingPropertySet( text_elem.Shading, overrides, 'ch' ) |
---|
516 | |
---|
517 | # write the wrapper and then let the custom handler have a go |
---|
518 | if overrides : self._write( '{%s ' % repr( overrides ) ) |
---|
519 | |
---|
520 | # if the data is just a string then we can now write it |
---|
521 | if isinstance( text_elem.Data, StringType ) : |
---|
522 | self._write( text_elem.Data or '' ) |
---|
523 | |
---|
524 | elif text_elem.Data == TAB : |
---|
525 | self._write( r'\tab ' ) |
---|
526 | |
---|
527 | else : |
---|
528 | self.WriteCustomElement( self, text_elem.Data ) |
---|
529 | |
---|
530 | if overrides : self._write( '}' ) |
---|
531 | |
---|
532 | def WriteInlineElement( self, inline_elem ) : |
---|
533 | overrides = Settings() |
---|
534 | |
---|
535 | self._RendTextPropertySet ( inline_elem.Properties, overrides ) |
---|
536 | self._RendShadingPropertySet( inline_elem.Shading, overrides, 'ch' ) |
---|
537 | |
---|
538 | # write the wrapper and then let the custom handler have a go |
---|
539 | if overrides : self._write( '{%s ' % repr( overrides ) ) |
---|
540 | |
---|
541 | for element in inline_elem : |
---|
542 | # if the data is just a string then we can now write it |
---|
543 | if isinstance( element, StringType ) : |
---|
544 | self._write( element ) |
---|
545 | |
---|
546 | elif isinstance( element, RawCode ) : |
---|
547 | self._write( element.Data ) |
---|
548 | |
---|
549 | elif element == TAB : |
---|
550 | self._write( r'\tab ' ) |
---|
551 | |
---|
552 | elif element == LINE : |
---|
553 | self._write( r'\line ' ) |
---|
554 | |
---|
555 | else : |
---|
556 | self.WriteCustomElement( self, element ) |
---|
557 | |
---|
558 | if overrides : self._write( '}' ) |
---|
559 | |
---|
560 | def WriteText( self, text ) : |
---|
561 | self._write( text or '' ) |
---|
562 | |
---|
563 | def WriteTableElement( self, table_elem ) : |
---|
564 | |
---|
565 | vmerge = [ False ] * table_elem.ColumnCount |
---|
566 | for height, cells in table_elem.Rows : |
---|
567 | |
---|
568 | # calculate the right hand edge of the cells taking into account the spans |
---|
569 | offset = table_elem.LeftOffset or 0 |
---|
570 | cellx = [] |
---|
571 | cell_idx = 0 |
---|
572 | for cell in cells : |
---|
573 | cellx.append( offset + sum( table_elem.ColumnWidths[ : cell_idx + cell.Span ] ) ) |
---|
574 | cell_idx += cell.Span |
---|
575 | |
---|
576 | self._write( r'{\trowd' ) |
---|
577 | |
---|
578 | settings = Settings() |
---|
579 | |
---|
580 | # the spec says that this value is mandatory and I think that 108 is the default value |
---|
581 | # so I'll take care of it here |
---|
582 | settings.append( table_elem.GapBetweenCells or 108, 'trgaph%s' ) |
---|
583 | settings.append( TableAlignmentMap[ table_elem.Alignment ] ) |
---|
584 | settings.append( height, 'trrh%s' ) |
---|
585 | settings.append( table_elem.LeftOffset, 'trleft%s' ) |
---|
586 | |
---|
587 | width = table_elem.LeftOffset or 0 |
---|
588 | for idx, cell in enumerate( cells ) : |
---|
589 | self._RendFramePropertySet ( cell.Frame, settings, 'cl' ) |
---|
590 | |
---|
591 | # cells don't have margins so I don't know why I was doing this |
---|
592 | # I think it might have an affect in some versions of some WPs. |
---|
593 | #self._RendMarginsPropertySet( cell.Margins, settings, 'cl' ) |
---|
594 | |
---|
595 | # if we are starting to merge or if this one is the first in what is |
---|
596 | # probably a series of merges then start the vertical merging |
---|
597 | if cell.StartVerticalMerge or (cell.VerticalMerge and not vmerge[ idx ]) : |
---|
598 | settings.append( 'clvmgf' ) |
---|
599 | vmerge[ idx ] = True |
---|
600 | |
---|
601 | elif cell.VerticalMerge : |
---|
602 | #..continuing a merge |
---|
603 | settings.append( 'clvmrg' ) |
---|
604 | |
---|
605 | else : |
---|
606 | #..no merging going on so make sure that it is off |
---|
607 | vmerge[ idx ] = False |
---|
608 | |
---|
609 | # for any cell in the next row that is covered by this span we |
---|
610 | # need to run off the vertical merging as we don't want them |
---|
611 | # merging up into this spanned cell |
---|
612 | for vmerge_idx in range( idx + 1, idx + cell.Span - 1 ) : |
---|
613 | vmerge[ vmerge_idx ] = False |
---|
614 | |
---|
615 | settings.append( CellAlignmentMap[ cell.Alignment ] ) |
---|
616 | settings.append( CellFlowMap[ cell.Flow ] ) |
---|
617 | |
---|
618 | # this terminates the definition of a cell and represents the right most edge of the cell from the left margin |
---|
619 | settings.append( cellx[ idx ], 'cellx%s' ) |
---|
620 | |
---|
621 | self._write( repr( settings ) ) |
---|
622 | |
---|
623 | for cell in cells : |
---|
624 | if len( cell ) : |
---|
625 | last_idx = len( cell ) - 1 |
---|
626 | for element_idx, element in enumerate( cell ) : |
---|
627 | # wrap plain strings in paragraph tags |
---|
628 | if isinstance( element, StringType ) : |
---|
629 | element = Paragraph( element ) |
---|
630 | |
---|
631 | # don't forget the prefix or else word crashes and does all sorts of strange things |
---|
632 | if element_idx == last_idx : |
---|
633 | self.WriteParagraphElement( element, tag_prefix=r'\intbl', tag_suffix='', opening='', closing='' ) |
---|
634 | |
---|
635 | else : |
---|
636 | self.WriteParagraphElement( element, tag_prefix=r'\intbl', opening='', closing='' ) |
---|
637 | |
---|
638 | self._write( r'\cell' ) |
---|
639 | |
---|
640 | else : |
---|
641 | self._write( r'\pard\intbl\cell' ) |
---|
642 | |
---|
643 | self._write( '\\row}\n' ) |
---|
644 | |
---|