source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/contrib/pyrtf/Elements.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 32.3 KB
Line 
1from __future__ import print_function
2from copy import  deepcopy
3from binascii import  hexlify
4
5from .Constants import  *
6from .Styles import  *
7
8if PY2:
9    NumberTypes = (int, float, long)
10    StringType = basestring
11else:
12    NumberTypes = (int, float)
13    StringType = str
14
15class UnhandledParamError( Exception ) :
16    def __init__( self, param ) :
17        Exception.__init__( self, "Don't know what to do with param %s" % param )
18
19#                                               red green blue
20StandardColours = Colours()
21StandardColours.append( Colour( 'Black',         0,    0,   0 ) )
22StandardColours.append( Colour( 'Blue',          0,    0, 255 ) )
23StandardColours.append( Colour( 'Turquoise',     0,  255, 255 ) )
24StandardColours.append( Colour( 'Green',         0,  255,   0 ) )
25StandardColours.append( Colour( 'Pink',        255,    0, 255 ) )
26StandardColours.append( Colour( 'Red',         255,    0,   0 ) )
27StandardColours.append( Colour( 'Yellow',      255,  255,   0 ) )
28StandardColours.append( Colour( 'White',       255,  255, 255 ) )
29StandardColours.append( Colour( 'Blue Dark',     0,    0, 128 ) )
30StandardColours.append( Colour( 'Teal',          0,  128, 128 ) )
31StandardColours.append( Colour( 'Green Dark',    0,  128,   0 ) )
32StandardColours.append( Colour( 'Violet',      128,    0, 128 ) )
33StandardColours.append( Colour( 'Red Dark',    128,    0,   0 ) )
34StandardColours.append( Colour( 'Yellow Dark', 128,  128,   0 ) )
35StandardColours.append( Colour( 'Grey Dark',   128,  128, 128 ) )
36StandardColours.append( Colour( 'Grey',        192,  192, 192 ) )
37
38StandardFonts = Fonts()
39StandardFonts.append( Font( 'Arial'                   , 'swiss' , 0, 2, '020b0604020202020204' ) )
40StandardFonts.append( Font( 'Arial Black'             , 'swiss' , 0, 2, '020b0a04020102020204' ) )
41StandardFonts.append( Font( 'Arial Narrow'            , 'swiss' , 0, 2, '020b0506020202030204' ) )
42StandardFonts.append( Font( 'Bitstream Vera Sans Mono', 'modern', 0, 1, '020b0609030804020204' ) )
43StandardFonts.append( Font( 'Bitstream Vera Sans'     , 'swiss' , 0, 2, '020b0603030804020204' ) )
44StandardFonts.append( Font( 'Bitstream Vera Serif'    , 'roman' , 0, 2, '02060603050605020204' ) )
45StandardFonts.append( Font( 'Book Antiqua'            , 'roman' , 0, 2, '02040602050305030304' ) )
46StandardFonts.append( Font( 'Bookman Old Style'       , 'roman' , 0, 2, '02050604050505020204' ) )
47StandardFonts.append( Font( 'Castellar'               , 'roman' , 0, 2, '020a0402060406010301' ) )
48StandardFonts.append( Font( 'Century Gothic'          , 'swiss' , 0, 2, '020b0502020202020204' ) )
49StandardFonts.append( Font( 'Comic Sans MS'           , 'script', 0, 2, '030f0702030302020204' ) )
50StandardFonts.append( Font( 'Courier New'             , 'modern', 0, 1, '02070309020205020404' ) )
51StandardFonts.append( Font( 'Franklin Gothic Medium'  , 'swiss' , 0, 2, '020b0603020102020204' ) )
52StandardFonts.append( Font( 'Garamond'                , 'roman' , 0, 2, '02020404030301010803' ) )
53StandardFonts.append( Font( 'Georgia'                 , 'roman' , 0, 2, '02040502050405020303' ) )
54StandardFonts.append( Font( 'Haettenschweiler'        , 'swiss' , 0, 2, '020b0706040902060204' ) )
55StandardFonts.append( Font( 'Impact'                  , 'swiss' , 0, 2, '020b0806030902050204' ) )
56StandardFonts.append( Font( 'Lucida Console'          , 'modern', 0, 1, '020b0609040504020204' ) )
57StandardFonts.append( Font( 'Lucida Sans Unicode'     , 'swiss' , 0, 2, '020b0602030504020204' ) )
58StandardFonts.append( Font( 'Microsoft Sans Serif'    , 'swiss' , 0, 2, '020b0604020202020204' ) )
59StandardFonts.append( Font( 'Monotype Corsiva'        , 'script', 0, 2, '03010101010201010101' ) )
60StandardFonts.append( Font( 'Palatino Linotype'       , 'roman' , 0, 2, '02040502050505030304' ) )
61StandardFonts.append( Font( 'Papyrus'                 , 'script', 0, 2, '03070502060502030205' ) )
62StandardFonts.append( Font( 'Sylfaen'                 , 'roman' , 0, 2, '010a0502050306030303' ) )
63StandardFonts.append( Font( 'Symbol'                  , 'roman' , 2, 2, '05050102010706020507' ) )
64StandardFonts.append( Font( 'Tahoma'                  , 'swiss' , 0, 2, '020b0604030504040204' ) )
65StandardFonts.append( Font( 'Times New Roman'         , 'roman' , 0, 2, '02020603050405020304' ) )
66StandardFonts.append( Font( 'Trebuchet MS'            , 'swiss' , 0, 2, '020b0603020202020204' ) )
67StandardFonts.append( Font( 'Verdana'                 , 'swiss' , 0, 2, '020b0604030504040204' ) )
68
69StandardFonts.Castellar.SetAlternate( StandardFonts.Georgia )
70
71"""
72Found the following definition at http://www.pbdr.com/vbtips/gen/convtwip.htm
73
74Twips are screen-independent units used to ensure that the placement and
75proportion of screen elements in your screen application are the same on all
76display systems. A twip is a unit of screen measurement equal to 1/20 of a
77printer's point. The conversion between twips and
78inches/centimeters/millimeters is as follows:
79
80There are approximately 1440 twips to a inch (the length of a screen item
81measuring one inch when printed).
82
83As there are 2.54 centimeters to 1 inch, then there are approximately 567
84twips to a centimeter (the length of a screen item measuring one centimeter
85when printed).
86
87Or in millimeters, as there are 25.4 millimeters to 1 inch, therefore there
88are approximately 56.7 twips to a millimeter (the length of a screen item
89measuring one millimeter when printed)."""
90
91# Width default is 12240, Height default is 15840
92StandardPaper = Papers()
93StandardPaper.append( Paper( 'LETTER'             ,  1,  'Letter 8 1/2 x 11 in'               ,   12240,  15840 ) )
94StandardPaper.append( Paper( 'LETTERSMALL'        ,  2,  'Letter Small 8 1/2 x 11 in'         ,   12240,  15840 ) )
95StandardPaper.append( Paper( 'TABLOID'            ,  3,  'Tabloid 11 x 17 in'                 ,   15840,  24480 ) )
96StandardPaper.append( Paper( 'LEDGER'             ,  4,  'Ledger 17 x 11 in'                  ,   24480,  15840 ) )
97StandardPaper.append( Paper( 'LEGAL'              ,  5,  'Legal 8 1/2 x 14 in'                ,   12240,  20160 ) )
98StandardPaper.append( Paper( 'STATEMENT'          ,  6,  'Statement 5 1/2 x 8 1/2 in'         ,    7920,  12240 ) )
99StandardPaper.append( Paper( 'EXECUTIVE'          ,  7,  'Executive 7 1/4 x 10 1/2 in'        ,   10440,  15120 ) )
100StandardPaper.append( Paper( 'A3'                 ,  8,  'A3 297 x 420 mm'                    ,   16838,  23811 ) )
101StandardPaper.append( Paper( 'A4'                 ,  9,  'A4 210 x 297 mm'                    ,   11907,  16838 ) )
102StandardPaper.append( Paper( 'A4SMALL'            , 10,  'A4 Small 210 x 297 mm'              ,   11907,  16838 ) )
103StandardPaper.append( Paper( 'A5'                 , 11,  'A5 148 x 210 mm'                    ,    8391,  11907 ) )
104StandardPaper.append( Paper( 'B4'                 , 12,  'B4 (JIS) 250 x 354'                 ,   14175,  20072 ) )
105StandardPaper.append( Paper( 'B5'                 , 13,  'B5 (JIS) 182 x 257 mm'              ,   10319,  14572 ) )
106StandardPaper.append( Paper( 'FOLIO'              , 14,  'Folio 8 1/2 x 13 in'                ,   12240,  18720 ) )
107StandardPaper.append( Paper( 'QUARTO'             , 15,  'Quarto 215 x 275 mm'                ,   12191,  15593 ) )
108StandardPaper.append( Paper( '10X14'              , 16,  '10x14 in'                           ,   14400,  20160 ) )
109StandardPaper.append( Paper( '11X17'              , 17,  '11x17 in'                           ,   15840,  24480 ) )
110StandardPaper.append( Paper( 'NOTE'               , 18,  'Note 8 1/2 x 11 in'                 ,   12240,  15840 ) )
111StandardPaper.append( Paper( 'ENV_9'              , 19,  'Envelope #9 3 7/8 x 8 7/8'          ,    5580,  12780 ) )
112StandardPaper.append( Paper( 'ENV_10'             , 20,  'Envelope #10 4 1/8 x 9 1/2'         ,    5940,  13680 ) )
113StandardPaper.append( Paper( 'ENV_11'             , 21,  'Envelope #11 4 1/2 x 10 3/8'        ,    6480,  14940 ) )
114StandardPaper.append( Paper( 'ENV_12'             , 22,  'Envelope #12 4 3/4 x 11'            ,    6840,  15840 ) )
115StandardPaper.append( Paper( 'ENV_14'             , 23,  'Envelope #14 5 x 11 1/2'            ,    7200,  16560 ) )
116StandardPaper.append( Paper( 'CSHEET'             , 24,  'C size sheet 18 x 24 in'            ,   29520,  34560 ) )
117StandardPaper.append( Paper( 'DSHEET'             , 25,  'D size sheet 22 x 34 in'            ,   31680,  48960 ) )
118StandardPaper.append( Paper( 'ESHEET'             , 26,  'E size sheet 34 x 44 in'            ,   48960,  63360 ) )
119StandardPaper.append( Paper( 'ENV_DL'             , 27,  'Envelope DL 110 x 220mm'            ,    6237,  12474 ) )
120StandardPaper.append( Paper( 'ENV_C5'             , 28,  'Envelope C5 162 x 229 mm'           ,    9185,  12984 ) )
121StandardPaper.append( Paper( 'ENV_C3'             , 29,  'Envelope C3  324 x 458 mm'          ,   18371,  25969 ) )
122StandardPaper.append( Paper( 'ENV_C4'             , 30,  'Envelope C4  229 x 324 mm'          ,   12984,  18371 ) )
123StandardPaper.append( Paper( 'ENV_C6'             , 31,  'Envelope C6  114 x 162 mm'          ,    6464,   9185 ) )
124StandardPaper.append( Paper( 'ENV_C65'            , 32,  'Envelope C65 114 x 229 mm'          ,    6464,  12984 ) )
125StandardPaper.append( Paper( 'ENV_B4'             , 33,  'Envelope B4  250 x 353 mm'          ,   14175,  20015 ) )
126StandardPaper.append( Paper( 'ENV_B5'             , 34,  'Envelope B5  176 x 250 mm'          ,    9979,  14175 ) )
127StandardPaper.append( Paper( 'ENV_B6'             , 35,  'Envelope B6  176 x 125 mm'          ,    9979,   7088 ) )
128StandardPaper.append( Paper( 'ENV_ITALY'          , 36,  'Envelope 110 x 230 mm'              ,    6237,  13041 ) )
129StandardPaper.append( Paper( 'ENV_MONARCH'        , 37,  'Envelope Monarch 3.875 x 7.5 in'    ,    5580,  10800 ) )
130StandardPaper.append( Paper( 'ENV_PERSONAL'       , 38,  '6 3/4 Envelope 3 5/8 x 6 1/2 in'    ,    5220,   9360 ) )
131StandardPaper.append( Paper( 'FANFOLD_US'         , 39,  'US Std Fanfold 14 7/8 x 11 in'      ,   21420,  15840 ) )
132StandardPaper.append( Paper( 'FANFOLD_STD_GERMAN' , 40,  'German Std Fanfold 8 1/2 x 12 in'   ,   12240,  17280 ) )
133StandardPaper.append( Paper( 'FANFOLD_LGL_GERMAN' , 41,  'German Legal Fanfold 8 1/2 x 13 in' ,   12240,  18720 ) )
134
135#
136#   Finally a StyleSheet in which all of this stuff is put together
137#
138class StyleSheet :
139    def __init__( self, colours=None, fonts=None ) :
140
141        self.Colours = colours or deepcopy( StandardColours )
142        self.Fonts   = fonts   or deepcopy( StandardFonts   )
143
144        self.TextStyles      = AttributedList()
145        self.ParagraphStyles = AttributedList()
146
147class Section( list ) :
148    NONE   = 1
149    COLUMN = 2
150    PAGE   = 3
151    EVEN   = 4
152    ODD    = 5
153    BREAK_TYPES = [ NONE, COLUMN, PAGE, EVEN, ODD ]
154
155    def __init__( self, paper=None, margins=None, break_type=None, headery=None, footery=None, landscape=None, first_page_number=None ) :
156        super( Section, self ).__init__()
157
158        self.Paper   = paper   or StandardPaper.A4
159        self.SetMargins( margins )
160
161        self.Header = []
162        self.Footer = []
163        self.FirstHeader = []
164        self.FirstFooter = []
165
166        self.SetBreakType( break_type or self.NONE )
167        self.SetHeaderY( headery )
168        self.SetFooterY( footery )
169        self.SetLandscape( landscape )
170        self.SetFirstPageNumber( first_page_number )
171
172    def TwipsToRightMargin( self ) :
173        return self.Paper.Width - ( self.Margins.Left + self.Margins.Right )
174
175    def SetMargins( self, value ) :
176        self.Margins = value or MarginsPropertySet( top=1000, left=1200, bottom=1000, right=1200 )
177        self.Width   = self.Paper.Width - ( self.Margins.Left + self.Margins.Right )
178
179    def SetBreakType( self, value ) :
180        assert value in self.BREAK_TYPES
181        self.BreakType = value
182        return self
183
184    def SetHeaderY( self, value ) :
185        self.HeaderY = value
186        return self
187
188    def SetFooterY( self, value ) :
189        self.FooterY = value
190        return self
191
192    def SetLandscape( self, value ) :
193        self.Landscape = False
194        if value : self.Landscape = True
195        return self
196
197    def SetFirstPageNumber( self, value ) :
198        self.FirstPageNumber = value
199        return self
200
201def MakeDefaultStyleSheet( ) :
202    result = StyleSheet()
203
204    NormalText = TextStyle( TextPropertySet( result.Fonts.Arial, 22 ) )
205
206    ps = ParagraphStyle( 'Normal',
207                         NormalText.Copy(),
208                         ParagraphPropertySet( space_before = 60,
209                                               space_after  = 60 ) )
210    result.ParagraphStyles.append( ps )
211
212    ps = ParagraphStyle( 'Normal Short',
213                         NormalText.Copy() )
214    result.ParagraphStyles.append( ps )
215
216    NormalText.TextPropertySet.SetSize( 32 )
217    ps = ParagraphStyle( 'Heading 1',
218                         NormalText.Copy(),
219                         ParagraphPropertySet( space_before = 240,
220                                               space_after  = 60 ) )
221    result.ParagraphStyles.append( ps )
222
223    NormalText.TextPropertySet.SetSize( 24 ).SetBold( True )
224    ps = ParagraphStyle( 'Heading 2',
225                         NormalText.Copy(),
226                         ParagraphPropertySet( space_before = 240,
227                                               space_after  = 60 ) )
228    result.ParagraphStyles.append( ps )
229
230    #   Add some more in that are based on the normal template but that
231    #   have some indenting set that makes them suitable for doing numbered
232    normal_numbered = result.ParagraphStyles.Normal.Copy()
233    normal_numbered.SetName( 'Normal Numbered' )
234    normal_numbered.ParagraphPropertySet.SetFirstLineIndent( TabPropertySet.DEFAULT_WIDTH * -1 )
235    normal_numbered.ParagraphPropertySet.SetLeftIndent     ( TabPropertySet.DEFAULT_WIDTH )
236
237    result.ParagraphStyles.append( normal_numbered )
238
239    normal_numbered2 = result.ParagraphStyles.Normal.Copy()
240    normal_numbered2.SetName( 'Normal Numbered 2' )
241    normal_numbered2.ParagraphPropertySet.SetFirstLineIndent( TabPropertySet.DEFAULT_WIDTH * -1 )
242    normal_numbered2.ParagraphPropertySet.SetLeftIndent     ( TabPropertySet.DEFAULT_WIDTH *  2 )
243
244    result.ParagraphStyles.append( normal_numbered2 )
245
246    ## LIST STYLES
247    for idx, indent in [ (1, TabPS.DEFAULT_WIDTH    ),
248                         (2, TabPS.DEFAULT_WIDTH * 2),
249                         (3, TabPS.DEFAULT_WIDTH * 3) ] :
250        indent = TabPropertySet.DEFAULT_WIDTH
251        ps = ParagraphStyle( 'List %s' % idx,
252                             TextStyle( TextPropertySet( result.Fonts.Arial, 22 ) ),
253                             ParagraphPropertySet( space_before = 60,
254                                                   space_after  = 60,
255                                                   first_line_indent = -indent,
256                                                   left_indent       = indent) )
257        result.ParagraphStyles.append( ps )
258
259    return result
260
261class TAB  : pass
262class LINE : pass
263
264class RawCode :
265    def __init__( self, data ) :
266        self.Data = data
267
268PAGE_NUMBER   = RawCode( r'{\field{\fldinst page}}'   )
269TOTAL_PAGES   = RawCode( r'{\field{\fldinst numpages}}' )
270SECTION_PAGES = RawCode( r'{\field{\fldinst sectionpages}}' )
271ARIAL_BULLET  = RawCode( r'{\f2\'95}' )
272
273def _get_jpg_dimensions( fin ):
274    """
275    converted from: http://dev.w3.org/cvsweb/Amaya/libjpeg/rdjpgcom.c?rev=1.2
276    """
277
278    M_SOF0   = chr( 0xC0 )  #   /* Start Of Frame N */
279    M_SOF1   = chr( 0xC1 )  #   /* N indicates which compression process */
280    M_SOF2   = chr( 0xC2 )  #   /* Only SOF0-SOF2 are now in common use */
281    M_SOF3   = chr( 0xC3 )  #
282    M_SOF5   = chr( 0xC5 )  #   /* NB: codes C4 and CC are NOT SOF markers */
283    M_SOF6   = chr( 0xC6 )  #
284    M_SOF7   = chr( 0xC7 )  #
285    M_SOF9   = chr( 0xC9 )  #
286    M_SOF10  = chr( 0xCA )  #
287    M_SOF11  = chr( 0xCB )  #
288    M_SOF13  = chr( 0xCD )  #
289    M_SOF14  = chr( 0xCE )  #
290    M_SOF15  = chr( 0xCF )  #
291    M_SOI    = chr( 0xD8 )  #   /* Start Of Image (beginning of datastream) */
292    M_EOI    = chr( 0xD9 )  #   /* End Of Image (end of datastream) */
293
294    M_FF = chr( 0xFF )
295
296    MARKERS = [ M_SOF0, M_SOF1,  M_SOF2,  M_SOF3,
297                M_SOF5, M_SOF6,  M_SOF7,  M_SOF9,
298                M_SOF10,M_SOF11, M_SOF13, M_SOF14,
299                M_SOF15 ]
300
301    def get_length() :
302        b1 = fin.read( 1 )
303        b2 = fin.read( 1 )
304        return (ord(b1) << 8) + ord(b2)
305
306    def next_marker() :
307        #  markers come straight after an 0xFF so skip everything
308        #  up to the first 0xFF that we find
309        while fin.read(1) != M_FF :
310            pass
311
312        #  there can be more than one 0xFF as they can be used
313        #  for padding so we are now looking for the first byte
314        #  that isn't an 0xFF, this will be the marker
315        while True :
316            result = fin.read(1)
317            if result != M_FF :
318                return result
319
320        raise Exception( 'Invalid JPEG' )
321
322    #  BODY OF THE FUNCTION
323    if not ((fin.read(1) == M_FF) and (fin.read(1) == M_SOI)) :
324        raise Exception( 'Invalid Jpeg' )
325
326    while True :
327        marker = next_marker()
328
329        #  the marker is always followed by two bytes representing the length of the data field
330        length = get_length ()
331        if length < 2 : raise Exception( "Erroneous JPEG marker length" )
332
333        #  if it is a compression process marker then it will contain the dimension of the image
334        if marker in MARKERS :
335            #  the next byte is the data precision, just skip it
336            fin.read(1)
337
338            #  bingo
339            image_height = get_length()
340            image_width  = get_length()
341            return image_width, image_height
342
343        #  just skip whatever data it contains
344        fin.read( length - 2 )
345
346    raise Exception( 'Invalid JPEG, end of stream reached' )
347
348
349_PNG_HEADER = '\x89\x50\x4e'
350def _get_png_dimensions( data ) :
351    if data[0:3] != _PNG_HEADER :
352        raise Exception( 'Invalid PNG image' )
353
354    width  = (ord(data[18]) * 256) + (ord(data[19]))
355    height = (ord(data[22]) * 256) + (ord(data[23]))
356    return width, height
357
358def _get_emf_dimensions( fin ):
359    import struct
360    def get_DWORD():
361        return struct.unpack("<L",fin.read(4))[0]
362    def get_LONG():
363        return struct.unpack("<l",fin.read(4))[0]
364    def get_WORD():
365        return struct.unpack("<H",fin.read(2))[0]
366    class Empty:
367        pass
368    header = Empty()
369    header.RecordType = get_DWORD()      # Record type
370    header.RecordSize = get_DWORD()      # Size of the record in bytes
371    header.BoundsLeft = get_LONG()       # Left inclusive bounds
372    header.BoundsTop = get_LONG()        # Top inclusive bounds
373    header.BoundsRight = get_LONG()      # Right inclusive bounds
374    header.BoundsBottom = get_LONG()     # Bottom inclusive bounds
375    header.FrameLeft = get_LONG()        # Left side of inclusive picture frame
376    header.FrameTop = get_LONG()         # Top side of inclusive picture frame
377    header.FrameRight = get_LONG()       # Right side of inclusive picture frame
378    header.FrameBottom = get_LONG()      # Bottom side of inclusive picture frame
379    header.Signature = get_DWORD()       # Signature ID (always 0x464D4520)
380    header.Version = get_DWORD()         # Version of the metafile
381    header.Size = get_DWORD()            # Size of the metafile in bytes
382    header.NumOfRecords = get_DWORD()    # Number of records in the metafile
383    header.NumOfHandles = get_WORD()     # Number of handles in the handle table
384    header.Reserved = get_WORD()         # Not used (always 0)
385    header.SizeOfDescrip = get_DWORD()   # Size of description string in WORDs
386    header.OffsOfDescrip = get_DWORD()   # Offset of description string in metafile
387    header.NumPalEntries = get_DWORD()   # Number of color palette entries
388    header.WidthDevPixels = get_LONG()   # Width of reference device in pixels
389    header.HeightDevPixels = get_LONG()  # Height of reference device in pixels
390    header.WidthDevMM = get_LONG()       # Width of reference device in millimeters
391    header.HeightDevMM = get_LONG()      # Height of reference device in millimeters
392
393    if 0:
394        klist = sorted(header.__dict__.keys())
395        for k in klist:
396            print("%20s:%s" % (k,header.__dict__[k]))
397
398    dw = header.FrameRight-header.FrameLeft
399    dh = header.FrameBottom-header.FrameTop
400
401    # convert from 0.01mm units to 1/72in units
402    return int(dw * 72.0/2540.0), int(dh * 72.0/2540.0)
403
404class Image( RawCode ) :
405
406    #  Need to add in the width and height in twips as it crashes
407    #  word xp with these values.  Still working out the most
408    #  efficient way of getting these values.
409    # \picscalex100\picscaley100\piccropl0\piccropr0\piccropt0\piccropb0
410    # picwgoal900\pichgoal281
411
412    PNG_LIB = 'pngblip'
413    JPG_LIB = 'jpegblip'
414    EMF_LIB = 'emfblip'
415    PICT_TYPES = { 'png' : PNG_LIB,
416                   'jpg' : JPG_LIB,
417                   'emf' : EMF_LIB}
418
419    def __init__( self, infile, **kwargs ) :
420
421        if hasattr( infile, 'read' ):
422            fin = infile
423            if 'datatype' not in kwargs.keys():
424                msg = "If passing in a file object, you must also specify type='xxx' where xxx is one of %s" % self.PICT_TYPES.keys()
425                raise ValueError(msg)
426            file_name = kwargs.pop('datatype')
427        else:
428            fin = file( infile, 'rb' )
429            file_name = infile
430
431        pict_type = self.PICT_TYPES[ file_name[ -3 : ].lower() ]
432        if pict_type == self.PNG_LIB :
433            width, height = _get_png_dimensions( fin.read( 100 ) )
434        elif pict_type == self.JPG_LIB :
435            width, height = _get_jpg_dimensions( fin )
436        elif pict_type == self.EMF_LIB :
437            width, height = _get_emf_dimensions( fin )
438
439
440        # if user specified height or width but not both, then
441        # scale unspecified dimension to maintain aspect ratio
442
443        if ('width' in kwargs) and ('height' not in kwargs):
444            height = int(height * float(kwargs['width'])/width)
445        elif ('height' in kwargs) and ('width' not in kwargs):
446            width = int(width * float(kwargs['height'])/height)
447
448        width  = kwargs.pop('width',width)
449        height = kwargs.pop('height', height)
450
451        codes = [ pict_type,
452                  'picwgoal%s' % (width  * 20),
453                  'pichgoal%s' % (height * 20) ]
454        # let user specify global scaling
455        scale = kwargs.pop('scale',100)
456
457        for kwarg, code, default in [ ( 'scale_x',     'scalex', scale ),
458                                      ( 'scale_y',     'scaley', scale ),
459                                      ( 'crop_left',   'cropl',    '0' ),
460                                      ( 'crop_right',  'cropr',    '0' ),
461                                      ( 'crop_top',    'cropt',    '0' ),
462                                      ( 'crop_bottom', 'cropb',    '0' ) ] :
463            codes.append( 'pic%s%s' % ( code, kwargs.pop( kwarg, default ) ) )
464
465
466        #  reset back to the start of the file to get all of it and now
467        #  turn it into hex.
468        fin.seek( 0, 0 )
469        image = hexlify( fin.read() )
470        fin.close()
471        data = []
472        for i in range( 0, len( image ), 128 ) :
473            data.append( image[ i : i + 128 ] )
474
475        data = r'{\pict{\%s}%s}' % ( '\\'.join( codes ), '\n'.join( data ) )
476        RawCode.__init__( self, data )
477
478    def ToRawCode( self, var_name ) :
479        return '%s = RawCode( """%s""" )' % ( var_name, self.Data )
480
481class Text :
482    def __init__( self, *params ) :
483        self.Data       = None
484        self.Style      = None
485        self.Properties = None
486        self.Shading    = None
487
488        for param in params :
489            if   isinstance( param, TextStyle  ) : self.Style      = param
490            elif isinstance( param, TextPS     ) : self.Properties = param
491            elif isinstance( param, ShadingPS  ) : self.Shading    = param
492            else :
493                #   otherwise let the rendering custom handler sort it out itself
494                self.Data = param
495
496    def SetData( self, value ) :
497        self.Data = value
498
499class Inline( list ) :
500    def __init__( self, *params ) :
501        super( Inline, self ).__init__()
502
503        self.Style      = None
504        self.Properties = None
505        self.Shading    = None
506
507        self._append = super( Inline, self ).append
508
509        for param in params :
510            if   isinstance( param, TextStyle  ) : self.Style      = param
511            elif isinstance( param, TextPS     ) : self.Properties = param
512            elif isinstance( param, ShadingPS  ) : self.Shading    = param
513            else :
514                #   otherwise we add to it to our list of elements and let
515                #   the rendering custom handler sort it out itself.
516                self.append( param )
517
518    def append( self, *params ) :
519        #   filter out any that are explicitly None
520        [ self._append( param ) for param in params if param is not None ]
521
522class Paragraph( list ) :
523    def __init__( self, *params ) :
524        super( Paragraph, self ).__init__()
525
526        self.Style      = None
527        self.Properties = None
528        self.Frame      = None
529        self.Shading    = None
530
531        self._append = super( Paragraph, self ).append
532
533        for param in params :
534            if   isinstance( param, ParagraphStyle ) : self.Style      = param
535            elif isinstance( param, ParagraphPS    ) : self.Properties = param
536            elif isinstance( param, FramePS        ) : self.Frame      = param
537            elif isinstance( param, ShadingPS      ) : self.Shading    = param
538            else :
539                #   otherwise we add to it to our list of elements and let
540                #   the rendering custom handler sort it out itself.
541                self.append( param )
542
543    def append( self, *params ) :
544        #   filter out any that are explicitly None
545        [ self._append( param ) for param in params if param is not None ]
546
547    def insert( self, index, value ) :
548        if value is not None :
549            super( Paragraph, self ).insert( index, value )
550
551class Table :
552    LEFT    = 1
553    RIGHT   = 2
554    CENTER  = 3
555    ALIGNMENT = [ LEFT, RIGHT, CENTER ]
556
557    NO_WRAPPING = 1
558    WRAP_AROUND = 2
559    WRAPPING = [ NO_WRAPPING, WRAP_AROUND ]
560
561    #   trrh height of row, 0 means automatically adjust, use negative for an absolute
562    #   trgaph is half of the space between a table cell in width, reduce this one
563    #   to get a really tiny column
564
565    def __init__( self, *column_widths, **kwargs ) :
566
567        self.Rows = []
568
569        self.SetAlignment      ( kwargs.pop( 'alignment',         self.LEFT ) )
570        self.SetLeftOffset     ( kwargs.pop( 'left_offset',       None      ) )
571        self.SetGapBetweenCells( kwargs.pop( 'gap_between_cells', None      ) )
572        self.SetColumnWidths   ( *column_widths         )
573
574        assert not kwargs, 'invalid keyword args %s' % kwargs
575
576    def SetAlignment( self, value ) :
577        assert value is None or value in self.ALIGNMENT
578        self.Alignment = value or self.LEFT
579        return self
580
581    def SetLeftOffset( self, value ) :
582        self.LeftOffset = value
583        return self
584
585    def SetGapBetweenCells( self, value ) :
586        self.GapBetweenCells = value
587        return self
588
589    def SetColumnWidths( self, *column_widths ) :
590        self.ColumnWidths = column_widths
591        self.ColumnCount  = len( column_widths )
592        return self
593
594    def AddRow( self, *cells ) :
595        height = None
596        if isinstance( cells[ 0 ], NumberTypes ):
597            height = int( cells[ 0 ] )
598            cells  = cells[ 1 : ]
599
600        #  make sure all of the spans add up to the number of columns
601        #  otherwise the table will get corrupted
602        if self.ColumnCount != sum( [ cell.Span for cell in cells ] ) :
603            raise Exception( 'ColumnCount != the total of this row\'s cell.Spans.' )
604
605        self.Rows.append( ( height, cells ) )
606
607    append = AddRow
608
609class Cell( list ) :
610
611    """
612    \clvertalt  Text is top-aligned in cell (the default).
613    \clvertalc  Text is centered vertically in cell.
614    \clvertalb  Text is bottom-aligned in cell.
615    \cltxlrtb   Vertical text aligned left (direction bottom up).
616    \cltxtbrl   Vertical text aligned right (direction top down).
617    """
618
619    ALIGN_TOP    = 1
620    ALIGN_CENTER = 2
621    ALIGN_BOTTOM = 3
622
623    FLOW_LR_TB          = 1
624    FLOW_RL_TB          = 2
625    FLOW_LR_BT          = 3
626    FLOW_VERTICAL_LR_TB = 4
627    FLOW_VERTICAL_TB_RL = 5
628
629    def __init__( self, *params, **kwargs ) :
630        super( Cell, self ).__init__()
631
632        self.SetFrame  ( None )
633        self.SetMargins( None )
634
635        self.SetAlignment( kwargs.get( 'alignment', self.ALIGN_TOP  ) )
636        self.SetFlow     ( kwargs.get( 'flow'     , self.FLOW_LR_TB ) )
637        self.SetSpan     ( kwargs.get( 'span',      1               ) )
638
639        self.SetStartVerticalMerge( kwargs.get( 'start_vertical_merge', False ) )
640        self.SetVerticalMerge     ( kwargs.get( 'vertical_merge',       False ) )
641
642        self._append = super( Cell, self ).append
643
644        for param in params :
645            if   isinstance( param, StringType ) : self.append    ( param )
646            elif isinstance( param, Paragraph  ) : self.append    ( param )
647            elif isinstance( param, FramePS    ) : self.SetFrame  ( param )
648            elif isinstance( param, MarginsPS  ) : self.SetMargins( param )
649
650    def SetFrame( self, value ) :
651        self.Frame = value
652        return self
653
654    def SetMargins( self, value ) :
655        self.Margins = value
656        return self
657
658    def SetAlignment( self, value ) :
659        assert value in [ self.ALIGN_TOP, self.ALIGN_CENTER, self.ALIGN_BOTTOM ] #, self.ALIGN_TEXT_TOP_DOWN, self.ALIGN_TEXT_BOTTOM_UP ]
660        self.Alignment = value
661
662    def SetFlow( self, value ) :
663        assert value in [ self.FLOW_LR_TB, self.FLOW_RL_TB, self.FLOW_LR_BT, self.FLOW_VERTICAL_LR_TB, self.FLOW_VERTICAL_TB_RL ]
664        self.Flow = value
665
666    def SetSpan( self, value ) :
667        #  must be a positive integer
668        self.Span = int( max( value, 1 ) )
669        return self
670
671    def SetStartVerticalMerge( self, value ) :
672        self.StartVerticalMerge = False
673        if value :
674            self.StartVerticalMerge = True
675        return self
676
677    def SetVerticalMerge( self, value ) :
678        self.VerticalMerge  = False
679        if value :
680            self.VerticalMerge = True
681        return self
682
683    def append( self, *params ) :
684        [ self._append( param ) for param in params ]
685
686class Document :
687    def __init__( self, style_sheet=None, default_language=None, view_kind=None, view_zoom_kind=None, view_scale=None ) :
688        self.StyleSheet = style_sheet or MakeDefaultStyleSheet()
689        self.Sections = AttributedList( Section )
690
691        self.SetTitle( None )
692
693        self.DefaultLanguage = default_language or Languages.DEFAULT
694        self.ViewKind        = view_kind        or ViewKind.DEFAULT
695        self.ViewZoomKind    = view_zoom_kind
696        self.ViewScale       = view_scale
697
698    def NewSection( self, *params, **kwargs ) :
699        result = Section( *params, **kwargs )
700        self.Sections.append( result )
701        return result
702
703    def SetTitle( self, value ) :
704        self.Title = value
705        return self
706
707    def Copy( self ) :
708        result = Document( style_sheet      = self.StyleSheet.Copy(),
709                           default_language = self.DefaultLanguage,
710                           view_kind        = self.ViewKind,
711                           view_zoom_kind   = self.ViewZoomKind,
712                           view_scale       = self.ViewScale )
713        result.SetTitle( self.Title )
714        result.Sections = self.Sections.Copy()
715
716        return result
717
718def TEXT( *params, **kwargs ) :
719    text_props = TextPropertySet()
720    text_props.SetFont     ( kwargs.get( 'font',      None  ) )
721    text_props.SetSize     ( kwargs.get( 'size',      None  ) )
722    text_props.SetBold     ( kwargs.get( 'bold',      False ) )
723    text_props.SetItalic   ( kwargs.get( 'italic',    False ) )
724    text_props.SetUnderline( kwargs.get( 'underline', False ) )
725    text_props.SetColour   ( kwargs.get( 'colour',    None  ) )
726
727    if len( params ) == 1 :
728        return Text( params[ 0 ], text_props )
729
730    result = Inline( text_props )
731    result.append(*params)
732    return result
733
734def B( *params ) :
735    text_props = TextPropertySet( bold=True )
736
737    if len( params ) == 1 :
738        return Text( params[ 0 ], text_props )
739
740    result = Inline( text_props )
741    result.append(*params)
742    return result
743
744def I( *params ) :
745    text_props = TextPropertySet( italic=True )
746
747    if len( params ) == 1 :
748        return Text( params[ 0 ], text_props )
749
750    result = Inline( text_props )
751    result.append(*params)
752    return result
753
754def U( *params ) :
755    text_props = TextPropertySet( underline=True )
756
757    if len( params ) == 1 :
758        return Text( params[ 0 ], text_props )
759
760    result = Inline( text_props )
761    result.append(*params)
762    return result
763
Note: See TracBrowser for help on using the repository browser.