initial commit

This commit is contained in:
Massimo Di Pierro committed 2011-11-22 23:30:42 -06:00
commit d421c1321b
580 files changed
+115527

No files matched your search

+158
View File
@@ -0,0 +1,158 @@
class ViewKind :
"""An integer (0-5) that represents the view mode of the document."""
NONE = 0
PageLayout = 1
Outline = 2
MasterDocument = 3
Normal = 4
OnlineLayout = 5
DEFAULT = PageLayout
def _IsValid( cls, value ) :
return value in [ 0, 1, 2, 3, 4, 5 ]
IsValid = classmethod( _IsValid )
class ViewScale :
"""Zoom level of the document; the N argument is a value representing a percentage (the default is 100)."""
def _IsValid( cls, value ) :
return value is None or (0 < value < 101)
IsValid = classmethod( _IsValid )
class ViewZoomKind :
"""An integer (0 to 2) that represents the zoom kind of the document."""
NONE = 0
FullPage = 1
BestFit = 2
def _IsValid( cls, value ) :
return value in [ None, 0, 1, 2 ]
IsValid = classmethod( _IsValid )
class Languages :
NoLanguage = 1024
Albanian = 1052
Arabic = 1025
Bahasa = 1057
BelgianDutch = 2067
BelgianFrench = 2060
BrazilianPortuguese = 1046
Bulgarian = 1026
Catalan = 1027
CroatoSerbianLatin = 1050
Czech = 1029
Danish = 1030
Dutch = 1043
EnglishAustralian = 3081
EnglishUK = 2057
EnglishUS = 1033
Finnish = 1035
French = 1036
FrenchCanadian = 3084
German = 1031
Greek = 1032
Hebrew = 1037
Hungarian = 1038
Icelandic = 1039
Italian = 1040
Japanese = 1041
Korean = 1042
NorwegianBokmal = 1044
NorwegianNynorsk = 2068
Polish = 1045
Portuguese = 2070
RhaetoRomanic = 1047
Romanian = 1048
Russian = 1049
SerboCroatianCyrillic = 2074
SimplifiedChinese = 2052
Slovak = 1051
SpanishCastilian = 1034
SpanishMexican = 2058
Swedish = 1053
SwissFrench = 4108
SwissGerman = 2055
SwissItalian = 2064
Thai = 1054
TraditionalChinese = 1028
Turkish = 1055
Urdu = 1056
SesothoSotho = 1072
Afrikaans = 1078
Zulu = 1077
Xhosa = 1076
Venda = 1075
Tswana = 1074
Tsonga = 1073
FarsiPersian = 1065
Codes = [ 1024,
1052,
1025,
1057,
2067,
2060,
1046,
1026,
1027,
1050,
1029,
1030,
1043,
3081,
2057,
1033,
1035,
1036,
3084,
1031,
1032,
1037,
1038,
1039,
1040,
1041,
1042,
1044,
2068,
1045,
2070,
1047,
1048,
1049,
2074,
2052,
1051,
1034,
2058,
1053,
4108,
2055,
2064,
1054,
1028,
1055,
1056,
1072,
1078,
1077,
1076,
1075,
1074,
1073,
1065 ]
# make it Australian as that is what I use most of the time
DEFAULT = EnglishAustralian
def _IsValid( cls, value ) :
return value in cls.Codes
IsValid = classmethod( _IsValid )
if __name__ == '__main__' :
PrintHexTable()
+757
View File
@@ -0,0 +1,757 @@
from types import IntType, FloatType, LongType, StringTypes
from copy import deepcopy
from binascii import hexlify
from Constants import *
from Styles import *
class UnhandledParamError( Exception ) :
def __init__( self, param ) :
Exception.__init__( self, "Don't know what to do with param %s" % param )
# red green blue
StandardColours = Colours()
StandardColours.append( Colour( 'Black', 0, 0, 0 ) )
StandardColours.append( Colour( 'Blue', 0, 0, 255 ) )
StandardColours.append( Colour( 'Turquoise', 0, 255, 255 ) )
StandardColours.append( Colour( 'Green', 0, 255, 0 ) )
StandardColours.append( Colour( 'Pink', 255, 0, 255 ) )
StandardColours.append( Colour( 'Red', 255, 0, 0 ) )
StandardColours.append( Colour( 'Yellow', 255, 255, 0 ) )
StandardColours.append( Colour( 'White', 255, 255, 255 ) )
StandardColours.append( Colour( 'Blue Dark', 0, 0, 128 ) )
StandardColours.append( Colour( 'Teal', 0, 128, 128 ) )
StandardColours.append( Colour( 'Green Dark', 0, 128, 0 ) )
StandardColours.append( Colour( 'Violet', 128, 0, 128 ) )
StandardColours.append( Colour( 'Red Dark', 128, 0, 0 ) )
StandardColours.append( Colour( 'Yellow Dark', 128, 128, 0 ) )
StandardColours.append( Colour( 'Grey Dark', 128, 128, 128 ) )
StandardColours.append( Colour( 'Grey', 192, 192, 192 ) )
StandardFonts = Fonts()
StandardFonts.append( Font( 'Arial' , 'swiss' , 0, 2, '020b0604020202020204' ) )
StandardFonts.append( Font( 'Arial Black' , 'swiss' , 0, 2, '020b0a04020102020204' ) )
StandardFonts.append( Font( 'Arial Narrow' , 'swiss' , 0, 2, '020b0506020202030204' ) )
StandardFonts.append( Font( 'Bitstream Vera Sans Mono', 'modern', 0, 1, '020b0609030804020204' ) )
StandardFonts.append( Font( 'Bitstream Vera Sans' , 'swiss' , 0, 2, '020b0603030804020204' ) )
StandardFonts.append( Font( 'Bitstream Vera Serif' , 'roman' , 0, 2, '02060603050605020204' ) )
StandardFonts.append( Font( 'Book Antiqua' , 'roman' , 0, 2, '02040602050305030304' ) )
StandardFonts.append( Font( 'Bookman Old Style' , 'roman' , 0, 2, '02050604050505020204' ) )
StandardFonts.append( Font( 'Castellar' , 'roman' , 0, 2, '020a0402060406010301' ) )
StandardFonts.append( Font( 'Century Gothic' , 'swiss' , 0, 2, '020b0502020202020204' ) )
StandardFonts.append( Font( 'Comic Sans MS' , 'script', 0, 2, '030f0702030302020204' ) )
StandardFonts.append( Font( 'Courier New' , 'modern', 0, 1, '02070309020205020404' ) )
StandardFonts.append( Font( 'Franklin Gothic Medium' , 'swiss' , 0, 2, '020b0603020102020204' ) )
StandardFonts.append( Font( 'Garamond' , 'roman' , 0, 2, '02020404030301010803' ) )
StandardFonts.append( Font( 'Georgia' , 'roman' , 0, 2, '02040502050405020303' ) )
StandardFonts.append( Font( 'Haettenschweiler' , 'swiss' , 0, 2, '020b0706040902060204' ) )
StandardFonts.append( Font( 'Impact' , 'swiss' , 0, 2, '020b0806030902050204' ) )
StandardFonts.append( Font( 'Lucida Console' , 'modern', 0, 1, '020b0609040504020204' ) )
StandardFonts.append( Font( 'Lucida Sans Unicode' , 'swiss' , 0, 2, '020b0602030504020204' ) )
StandardFonts.append( Font( 'Microsoft Sans Serif' , 'swiss' , 0, 2, '020b0604020202020204' ) )
StandardFonts.append( Font( 'Monotype Corsiva' , 'script', 0, 2, '03010101010201010101' ) )
StandardFonts.append( Font( 'Palatino Linotype' , 'roman' , 0, 2, '02040502050505030304' ) )
StandardFonts.append( Font( 'Papyrus' , 'script', 0, 2, '03070502060502030205' ) )
StandardFonts.append( Font( 'Sylfaen' , 'roman' , 0, 2, '010a0502050306030303' ) )
StandardFonts.append( Font( 'Symbol' , 'roman' , 2, 2, '05050102010706020507' ) )
StandardFonts.append( Font( 'Tahoma' , 'swiss' , 0, 2, '020b0604030504040204' ) )
StandardFonts.append( Font( 'Times New Roman' , 'roman' , 0, 2, '02020603050405020304' ) )
StandardFonts.append( Font( 'Trebuchet MS' , 'swiss' , 0, 2, '020b0603020202020204' ) )
StandardFonts.append( Font( 'Verdana' , 'swiss' , 0, 2, '020b0604030504040204' ) )
StandardFonts.Castellar.SetAlternate( StandardFonts.Georgia )
"""
Found the following definition at http://www.pbdr.com/vbtips/gen/convtwip.htm
Twips are screen-independent units used to ensure that the placement and
proportion of screen elements in your screen application are the same on all
display systems. A twip is a unit of screen measurement equal to 1/20 of a
printer's point. The conversion between twips and
inches/centimeters/millimeters is as follows:
There are approximately 1440 twips to a inch (the length of a screen item
measuring one inch when printed).
As there are 2.54 centimeters to 1 inch, then there are approximately 567
twips to a centimeter (the length of a screen item measuring one centimeter
when printed).
Or in millimeters, as there are 25.4 millimeters to 1 inch, therefore there
are approximately 56.7 twips to a millimeter (the length of a screen item
measuring one millimeter when printed)."""
# Width default is 12240, Height default is 15840
StandardPaper = Papers()
StandardPaper.append( Paper( 'LETTER' , 1, 'Letter 8 1/2 x 11 in' , 12240, 15840 ) )
StandardPaper.append( Paper( 'LETTERSMALL' , 2, 'Letter Small 8 1/2 x 11 in' , 12240, 15840 ) )
StandardPaper.append( Paper( 'TABLOID' , 3, 'Tabloid 11 x 17 in' , 15840, 24480 ) )
StandardPaper.append( Paper( 'LEDGER' , 4, 'Ledger 17 x 11 in' , 24480, 15840 ) )
StandardPaper.append( Paper( 'LEGAL' , 5, 'Legal 8 1/2 x 14 in' , 12240, 20160 ) )
StandardPaper.append( Paper( 'STATEMENT' , 6, 'Statement 5 1/2 x 8 1/2 in' , 7920, 12240 ) )
StandardPaper.append( Paper( 'EXECUTIVE' , 7, 'Executive 7 1/4 x 10 1/2 in' , 10440, 15120 ) )
StandardPaper.append( Paper( 'A3' , 8, 'A3 297 x 420 mm' , 16838, 23811 ) )
StandardPaper.append( Paper( 'A4' , 9, 'A4 210 x 297 mm' , 11907, 16838 ) )
StandardPaper.append( Paper( 'A4SMALL' , 10, 'A4 Small 210 x 297 mm' , 11907, 16838 ) )
StandardPaper.append( Paper( 'A5' , 11, 'A5 148 x 210 mm' , 8391, 11907 ) )
StandardPaper.append( Paper( 'B4' , 12, 'B4 (JIS) 250 x 354' , 14175, 20072 ) )
StandardPaper.append( Paper( 'B5' , 13, 'B5 (JIS) 182 x 257 mm' , 10319, 14572 ) )
StandardPaper.append( Paper( 'FOLIO' , 14, 'Folio 8 1/2 x 13 in' , 12240, 18720 ) )
StandardPaper.append( Paper( 'QUARTO' , 15, 'Quarto 215 x 275 mm' , 12191, 15593 ) )
StandardPaper.append( Paper( '10X14' , 16, '10x14 in' , 14400, 20160 ) )
StandardPaper.append( Paper( '11X17' , 17, '11x17 in' , 15840, 24480 ) )
StandardPaper.append( Paper( 'NOTE' , 18, 'Note 8 1/2 x 11 in' , 12240, 15840 ) )
StandardPaper.append( Paper( 'ENV_9' , 19, 'Envelope #9 3 7/8 x 8 7/8' , 5580, 12780 ) )
StandardPaper.append( Paper( 'ENV_10' , 20, 'Envelope #10 4 1/8 x 9 1/2' , 5940, 13680 ) )
StandardPaper.append( Paper( 'ENV_11' , 21, 'Envelope #11 4 1/2 x 10 3/8' , 6480, 14940 ) )
StandardPaper.append( Paper( 'ENV_12' , 22, 'Envelope #12 4 3/4 x 11' , 6840, 15840 ) )
StandardPaper.append( Paper( 'ENV_14' , 23, 'Envelope #14 5 x 11 1/2' , 7200, 16560 ) )
StandardPaper.append( Paper( 'CSHEET' , 24, 'C size sheet 18 x 24 in' , 29520, 34560 ) )
StandardPaper.append( Paper( 'DSHEET' , 25, 'D size sheet 22 x 34 in' , 31680, 48960 ) )
StandardPaper.append( Paper( 'ESHEET' , 26, 'E size sheet 34 x 44 in' , 48960, 63360 ) )
StandardPaper.append( Paper( 'ENV_DL' , 27, 'Envelope DL 110 x 220mm' , 6237, 12474 ) )
StandardPaper.append( Paper( 'ENV_C5' , 28, 'Envelope C5 162 x 229 mm' , 9185, 12984 ) )
StandardPaper.append( Paper( 'ENV_C3' , 29, 'Envelope C3 324 x 458 mm' , 18371, 25969 ) )
StandardPaper.append( Paper( 'ENV_C4' , 30, 'Envelope C4 229 x 324 mm' , 12984, 18371 ) )
StandardPaper.append( Paper( 'ENV_C6' , 31, 'Envelope C6 114 x 162 mm' , 6464, 9185 ) )
StandardPaper.append( Paper( 'ENV_C65' , 32, 'Envelope C65 114 x 229 mm' , 6464, 12984 ) )
StandardPaper.append( Paper( 'ENV_B4' , 33, 'Envelope B4 250 x 353 mm' , 14175, 20015 ) )
StandardPaper.append( Paper( 'ENV_B5' , 34, 'Envelope B5 176 x 250 mm' , 9979, 14175 ) )
StandardPaper.append( Paper( 'ENV_B6' , 35, 'Envelope B6 176 x 125 mm' , 9979, 7088 ) )
StandardPaper.append( Paper( 'ENV_ITALY' , 36, 'Envelope 110 x 230 mm' , 6237, 13041 ) )
StandardPaper.append( Paper( 'ENV_MONARCH' , 37, 'Envelope Monarch 3.875 x 7.5 in' , 5580, 10800 ) )
StandardPaper.append( Paper( 'ENV_PERSONAL' , 38, '6 3/4 Envelope 3 5/8 x 6 1/2 in' , 5220, 9360 ) )
StandardPaper.append( Paper( 'FANFOLD_US' , 39, 'US Std Fanfold 14 7/8 x 11 in' , 21420, 15840 ) )
StandardPaper.append( Paper( 'FANFOLD_STD_GERMAN' , 40, 'German Std Fanfold 8 1/2 x 12 in' , 12240, 17280 ) )
StandardPaper.append( Paper( 'FANFOLD_LGL_GERMAN' , 41, 'German Legal Fanfold 8 1/2 x 13 in' , 12240, 18720 ) )
#
# Finally a StyleSheet in which all of this stuff is put together
#
class StyleSheet :
def __init__( self, colours=None, fonts=None ) :
self.Colours = colours or deepcopy( StandardColours )
self.Fonts = fonts or deepcopy( StandardFonts )
self.TextStyles = AttributedList()
self.ParagraphStyles = AttributedList()
class Section( list ) :
NONE = 1
COLUMN = 2
PAGE = 3
EVEN = 4
ODD = 5
BREAK_TYPES = [ NONE, COLUMN, PAGE, EVEN, ODD ]
def __init__( self, paper=None, margins=None, break_type=None, headery=None, footery=None, landscape=None, first_page_number=None ) :
super( Section, self ).__init__()
self.Paper = paper or StandardPaper.A4
self.SetMargins( margins )
self.Header = []
self.Footer = []
self.FirstHeader = []
self.FirstFooter = []
self.SetBreakType( break_type or self.NONE )
self.SetHeaderY( headery )
self.SetFooterY( footery )
self.SetLandscape( landscape )
self.SetFirstPageNumber( first_page_number )
def TwipsToRightMargin( self ) :
return self.Paper.Width - ( self.Margins.Left + self.Margins.Right )
def SetMargins( self, value ) :
self.Margins = value or MarginsPropertySet( top=1000, left=1200, bottom=1000, right=1200 )
self.Width = self.Paper.Width - ( self.Margins.Left + self.Margins.Right )
def SetBreakType( self, value ) :
assert value in self.BREAK_TYPES
self.BreakType = value
return self
def SetHeaderY( self, value ) :
self.HeaderY = value
return self
def SetFooterY( self, value ) :
self.FooterY = value
return self
def SetLandscape( self, value ) :
self.Landscape = False
if value : self.Landscape = True
return self
def SetFirstPageNumber( self, value ) :
self.FirstPageNumber = value
return self
def MakeDefaultStyleSheet( ) :
result = StyleSheet()
NormalText = TextStyle( TextPropertySet( result.Fonts.Arial, 22 ) )
ps = ParagraphStyle( 'Normal',
NormalText.Copy(),
ParagraphPropertySet( space_before = 60,
space_after = 60 ) )
result.ParagraphStyles.append( ps )
ps = ParagraphStyle( 'Normal Short',
NormalText.Copy() )
result.ParagraphStyles.append( ps )
NormalText.TextPropertySet.SetSize( 32 )
ps = ParagraphStyle( 'Heading 1',
NormalText.Copy(),
ParagraphPropertySet( space_before = 240,
space_after = 60 ) )
result.ParagraphStyles.append( ps )
NormalText.TextPropertySet.SetSize( 24 ).SetBold( True )
ps = ParagraphStyle( 'Heading 2',
NormalText.Copy(),
ParagraphPropertySet( space_before = 240,
space_after = 60 ) )
result.ParagraphStyles.append( ps )
# Add some more in that are based on the normal template but that
# have some indenting set that makes them suitable for doing numbered
normal_numbered = result.ParagraphStyles.Normal.Copy()
normal_numbered.SetName( 'Normal Numbered' )
normal_numbered.ParagraphPropertySet.SetFirstLineIndent( TabPropertySet.DEFAULT_WIDTH * -1 )
normal_numbered.ParagraphPropertySet.SetLeftIndent ( TabPropertySet.DEFAULT_WIDTH )
result.ParagraphStyles.append( normal_numbered )
normal_numbered2 = result.ParagraphStyles.Normal.Copy()
normal_numbered2.SetName( 'Normal Numbered 2' )
normal_numbered2.ParagraphPropertySet.SetFirstLineIndent( TabPropertySet.DEFAULT_WIDTH * -1 )
normal_numbered2.ParagraphPropertySet.SetLeftIndent ( TabPropertySet.DEFAULT_WIDTH * 2 )
result.ParagraphStyles.append( normal_numbered2 )
## LIST STYLES
for idx, indent in [ (1, TabPS.DEFAULT_WIDTH ),
(2, TabPS.DEFAULT_WIDTH * 2),
(3, TabPS.DEFAULT_WIDTH * 3) ] :
indent = TabPropertySet.DEFAULT_WIDTH
ps = ParagraphStyle( 'List %s' % idx,
TextStyle( TextPropertySet( result.Fonts.Arial, 22 ) ),
ParagraphPropertySet( space_before = 60,
space_after = 60,
first_line_indent = -indent,
left_indent = indent) )
result.ParagraphStyles.append( ps )
return result
class TAB : pass
class LINE : pass
class RawCode :
def __init__( self, data ) :
self.Data = data
PAGE_NUMBER = RawCode( r'{\field{\fldinst page}}' )
TOTAL_PAGES = RawCode( r'{\field{\fldinst numpages}}' )
SECTION_PAGES = RawCode( r'{\field{\fldinst sectionpages}}' )
ARIAL_BULLET = RawCode( r'{\f2\'95}' )
def _get_jpg_dimensions( fin ):
"""
converted from: http://dev.w3.org/cvsweb/Amaya/libjpeg/rdjpgcom.c?rev=1.2
"""
M_SOF0 = chr( 0xC0 ) # /* Start Of Frame N */
M_SOF1 = chr( 0xC1 ) # /* N indicates which compression process */
M_SOF2 = chr( 0xC2 ) # /* Only SOF0-SOF2 are now in common use */
M_SOF3 = chr( 0xC3 ) #
M_SOF5 = chr( 0xC5 ) # /* NB: codes C4 and CC are NOT SOF markers */
M_SOF6 = chr( 0xC6 ) #
M_SOF7 = chr( 0xC7 ) #
M_SOF9 = chr( 0xC9 ) #
M_SOF10 = chr( 0xCA ) #
M_SOF11 = chr( 0xCB ) #
M_SOF13 = chr( 0xCD ) #
M_SOF14 = chr( 0xCE ) #
M_SOF15 = chr( 0xCF ) #
M_SOI = chr( 0xD8 ) # /* Start Of Image (beginning of datastream) */
M_EOI = chr( 0xD9 ) # /* End Of Image (end of datastream) */
M_FF = chr( 0xFF )
MARKERS = [ M_SOF0, M_SOF1, M_SOF2, M_SOF3,
M_SOF5, M_SOF6, M_SOF7, M_SOF9,
M_SOF10,M_SOF11, M_SOF13, M_SOF14,
M_SOF15 ]
def get_length() :
b1 = fin.read( 1 )
b2 = fin.read( 1 )
return (ord(b1) << 8) + ord(b2)
def next_marker() :
# markers come straight after an 0xFF so skip everything
# up to the first 0xFF that we find
while fin.read(1) != M_FF :
pass
# there can be more than one 0xFF as they can be used
# for padding so we are now looking for the first byte
# that isn't an 0xFF, this will be the marker
while True :
result = fin.read(1)
if result != M_FF :
return result
raise Exception( 'Invalid JPEG' )
# BODY OF THE FUNCTION
if not ((fin.read(1) == M_FF) and (fin.read(1) == M_SOI)) :
raise Exception( 'Invalid Jpeg' )
while True :
marker = next_marker()
# the marker is always followed by two bytes representing the length of the data field
length = get_length ()
if length < 2 : raise Exception( "Erroneous JPEG marker length" )
# if it is a compression process marker then it will contain the dimension of the image
if marker in MARKERS :
# the next byte is the data precision, just skip it
fin.read(1)
# bingo
image_height = get_length()
image_width = get_length()
return image_width, image_height
# just skip whatever data it contains
fin.read( length - 2 )
raise Exception( 'Invalid JPEG, end of stream reached' )
_PNG_HEADER = '\x89\x50\x4e'
def _get_png_dimensions( data ) :
if data[0:3] != _PNG_HEADER :
raise Exception( 'Invalid PNG image' )
width = (ord(data[18]) * 256) + (ord(data[19]))
height = (ord(data[22]) * 256) + (ord(data[23]))
return width, height
def _get_emf_dimensions( fin ):
import struct
def get_DWORD():
return struct.unpack("<L",fin.read(4))[0]
def get_LONG():
return struct.unpack("<l",fin.read(4))[0]
def get_WORD():
return struct.unpack("<H",fin.read(2))[0]
class Empty:
pass
header = Empty()
header.RecordType = get_DWORD() # Record type
header.RecordSize = get_DWORD() # Size of the record in bytes
header.BoundsLeft = get_LONG() # Left inclusive bounds
header.BoundsTop = get_LONG() # Top inclusive bounds
header.BoundsRight = get_LONG() # Right inclusive bounds
header.BoundsBottom = get_LONG() # Bottom inclusive bounds
header.FrameLeft = get_LONG() # Left side of inclusive picture frame
header.FrameTop = get_LONG() # Top side of inclusive picture frame
header.FrameRight = get_LONG() # Right side of inclusive picture frame
header.FrameBottom = get_LONG() # Bottom side of inclusive picture frame
header.Signature = get_DWORD() # Signature ID (always 0x464D4520)
header.Version = get_DWORD() # Version of the metafile
header.Size = get_DWORD() # Size of the metafile in bytes
header.NumOfRecords = get_DWORD() # Number of records in the metafile
header.NumOfHandles = get_WORD() # Number of handles in the handle table
header.Reserved = get_WORD() # Not used (always 0)
header.SizeOfDescrip = get_DWORD() # Size of description string in WORDs
header.OffsOfDescrip = get_DWORD() # Offset of description string in metafile
header.NumPalEntries = get_DWORD() # Number of color palette entries
header.WidthDevPixels = get_LONG() # Width of reference device in pixels
header.HeightDevPixels = get_LONG() # Height of reference device in pixels
header.WidthDevMM = get_LONG() # Width of reference device in millimeters
header.HeightDevMM = get_LONG() # Height of reference device in millimeters
if 0:
klist = header.__dict__.keys()
klist.sort()
for k in klist:
print "%20s:%s" % (k,header.__dict__[k])
dw = header.FrameRight-header.FrameLeft
dh = header.FrameBottom-header.FrameTop
# convert from 0.01mm units to 1/72in units
return int(dw * 72.0/2540.0), int(dh * 72.0/2540.0)
class Image( RawCode ) :
# Need to add in the width and height in twips as it crashes
# word xp with these values. Still working out the most
# efficient way of getting these values.
# \picscalex100\picscaley100\piccropl0\piccropr0\piccropt0\piccropb0
# picwgoal900\pichgoal281
PNG_LIB = 'pngblip'
JPG_LIB = 'jpegblip'
EMF_LIB = 'emfblip'
PICT_TYPES = { 'png' : PNG_LIB,
'jpg' : JPG_LIB,
'emf' : EMF_LIB}
def __init__( self, infile, **kwargs ) :
if hasattr( infile, 'read' ):
fin = infile
if 'datatype' not in kwargs.keys():
msg = "If passing in a file object, you must also specify type='xxx' where xxx is one of %s" % self.PICT_TYPES.keys()
raise ValueError,msg
file_name = kwargs.pop('datatype')
else:
fin = file( infile, 'rb' )
file_name = infile
pict_type = self.PICT_TYPES[ file_name[ -3 : ].lower() ]
if pict_type == self.PNG_LIB :
width, height = _get_png_dimensions( fin.read( 100 ) )
elif pict_type == self.JPG_LIB :
width, height = _get_jpg_dimensions( fin )
elif pict_type == self.EMF_LIB :
width, height = _get_emf_dimensions( fin )
# if user specified height or width but not both, then
# scale unspecified dimension to maintain aspect ratio
if ('width' in kwargs) and ('height' not in kwargs):
height = int(height * float(kwargs['width'])/width)
elif ('height' in kwargs) and ('width' not in kwargs):
width = int(width * float(kwargs['height'])/height)
width = kwargs.pop('width',width)
height = kwargs.pop('height', height)
codes = [ pict_type,
'picwgoal%s' % (width * 20),
'pichgoal%s' % (height * 20) ]
# let user specify global scaling
scale = kwargs.pop('scale',100)
for kwarg, code, default in [ ( 'scale_x', 'scalex', scale ),
( 'scale_y', 'scaley', scale ),
( 'crop_left', 'cropl', '0' ),
( 'crop_right', 'cropr', '0' ),
( 'crop_top', 'cropt', '0' ),
( 'crop_bottom', 'cropb', '0' ) ] :
codes.append( 'pic%s%s' % ( code, kwargs.pop( kwarg, default ) ) )
# reset back to the start of the file to get all of it and now
# turn it into hex.
fin.seek( 0, 0 )
image = hexlify( fin.read() )
fin.close()
data = []
for i in range( 0, len( image ), 128 ) :
data.append( image[ i : i + 128 ] )
data = r'{\pict{\%s}%s}' % ( '\\'.join( codes ), '\n'.join( data ) )
RawCode.__init__( self, data )
def ToRawCode( self, var_name ) :
return '%s = RawCode( """%s""" )' % ( var_name, self.Data )
class Text :
def __init__( self, *params ) :
self.Data = None
self.Style = None
self.Properties = None
self.Shading = None
for param in params :
if isinstance( param, TextStyle ) : self.Style = param
elif isinstance( param, TextPS ) : self.Properties = param
elif isinstance( param, ShadingPS ) : self.Shading = param
else :
# otherwise let the rendering custom handler sort it out itself
self.Data = param
def SetData( self, value ) :
self.Data = value
class Inline( list ) :
def __init__( self, *params ) :
super( Inline, self ).__init__()
self.Style = None
self.Properties = None
self.Shading = None
self._append = super( Inline, self ).append
for param in params :
if isinstance( param, TextStyle ) : self.Style = param
elif isinstance( param, TextPS ) : self.Properties = param
elif isinstance( param, ShadingPS ) : self.Shading = param
else :
# otherwise we add to it to our list of elements and let
# the rendering custom handler sort it out itself.
self.append( param )
def append( self, *params ) :
# filter out any that are explicitly None
[ self._append( param ) for param in params if param is not None ]
class Paragraph( list ) :
def __init__( self, *params ) :
super( Paragraph, self ).__init__()
self.Style = None
self.Properties = None
self.Frame = None
self.Shading = None
self._append = super( Paragraph, self ).append
for param in params :
if isinstance( param, ParagraphStyle ) : self.Style = param
elif isinstance( param, ParagraphPS ) : self.Properties = param
elif isinstance( param, FramePS ) : self.Frame = param
elif isinstance( param, ShadingPS ) : self.Shading = param
else :
# otherwise we add to it to our list of elements and let
# the rendering custom handler sort it out itself.
self.append( param )
def append( self, *params ) :
# filter out any that are explicitly None
[ self._append( param ) for param in params if param is not None ]
def insert( self, index, value ) :
if value is not None :
super( Paragraph, self ).insert( index, value )
class Table :
LEFT = 1
RIGHT = 2
CENTER = 3
ALIGNMENT = [ LEFT, RIGHT, CENTER ]
NO_WRAPPING = 1
WRAP_AROUND = 2
WRAPPING = [ NO_WRAPPING, WRAP_AROUND ]
# trrh height of row, 0 means automatically adjust, use negative for an absolute
# trgaph is half of the space between a table cell in width, reduce this one
# to get a really tiny column
def __init__( self, *column_widths, **kwargs ) :
self.Rows = []
self.SetAlignment ( kwargs.pop( 'alignment', self.LEFT ) )
self.SetLeftOffset ( kwargs.pop( 'left_offset', None ) )
self.SetGapBetweenCells( kwargs.pop( 'gap_between_cells', None ) )
self.SetColumnWidths ( *column_widths )
assert not kwargs, 'invalid keyword args %s' % kwargs
def SetAlignment( self, value ) :
assert value is None or value in self.ALIGNMENT
self.Alignment = value or self.LEFT
return self
def SetLeftOffset( self, value ) :
self.LeftOffset = value
return self
def SetGapBetweenCells( self, value ) :
self.GapBetweenCells = value
return self
def SetColumnWidths( self, *column_widths ) :
self.ColumnWidths = column_widths
self.ColumnCount = len( column_widths )
return self
def AddRow( self, *cells ) :
height = None
if isinstance( cells[ 0 ], (IntType, FloatType, LongType) ):
height = int( cells[ 0 ] )
cells = cells[ 1 : ]
# make sure all of the spans add up to the number of columns
# otherwise the table will get corrupted
if self.ColumnCount != sum( [ cell.Span for cell in cells ] ) :
raise Exception( 'ColumnCount != the total of this row\'s cell.Spans.' )
self.Rows.append( ( height, cells ) )
append = AddRow
class Cell( list ) :
"""
\clvertalt Text is top-aligned in cell (the default).
\clvertalc Text is centered vertically in cell.
\clvertalb Text is bottom-aligned in cell.
\cltxlrtb Vertical text aligned left (direction bottom up).
\cltxtbrl Vertical text aligned right (direction top down).
"""
ALIGN_TOP = 1
ALIGN_CENTER = 2
ALIGN_BOTTOM = 3
FLOW_LR_TB = 1
FLOW_RL_TB = 2
FLOW_LR_BT = 3
FLOW_VERTICAL_LR_TB = 4
FLOW_VERTICAL_TB_RL = 5
def __init__( self, *params, **kwargs ) :
super( Cell, self ).__init__()
self.SetFrame ( None )
self.SetMargins( None )
self.SetAlignment( kwargs.get( 'alignment', self.ALIGN_TOP ) )
self.SetFlow ( kwargs.get( 'flow' , self.FLOW_LR_TB ) )
self.SetSpan ( kwargs.get( 'span', 1 ) )
self.SetStartVerticalMerge( kwargs.get( 'start_vertical_merge', False ) )
self.SetVerticalMerge ( kwargs.get( 'vertical_merge', False ) )
self._append = super( Cell, self ).append
for param in params :
if isinstance( param, StringType ) : self.append ( param )
elif isinstance( param, Paragraph ) : self.append ( param )
elif isinstance( param, FramePS ) : self.SetFrame ( param )
elif isinstance( param, MarginsPS ) : self.SetMargins( param )
def SetFrame( self, value ) :
self.Frame = value
return self
def SetMargins( self, value ) :
self.Margins = value
return self
def SetAlignment( self, value ) :
assert value in [ self.ALIGN_TOP, self.ALIGN_CENTER, self.ALIGN_BOTTOM ] #, self.ALIGN_TEXT_TOP_DOWN, self.ALIGN_TEXT_BOTTOM_UP ]
self.Alignment = value
def SetFlow( self, value ) :
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 ]
self.Flow = value
def SetSpan( self, value ) :
# must be a positive integer
self.Span = int( max( value, 1 ) )
return self
def SetStartVerticalMerge( self, value ) :
self.StartVerticalMerge = False
if value :
self.StartVerticalMerge = True
return self
def SetVerticalMerge( self, value ) :
self.VerticalMerge = False
if value :
self.VerticalMerge = True
return self
def append( self, *params ) :
[ self._append( param ) for param in params ]
class Document :
def __init__( self, style_sheet=None, default_language=None, view_kind=None, view_zoom_kind=None, view_scale=None ) :
self.StyleSheet = style_sheet or MakeDefaultStyleSheet()
self.Sections = AttributedList( Section )
self.SetTitle( None )
self.DefaultLanguage = default_language or Languages.DEFAULT
self.ViewKind = view_kind or ViewKind.DEFAULT
self.ViewZoomKind = view_zoom_kind
self.ViewScale = view_scale
def NewSection( self, *params, **kwargs ) :
result = Section( *params, **kwargs )
self.Sections.append( result )
return result
def SetTitle( self, value ) :
self.Title = value
return self
def Copy( self ) :
result = Document( style_sheet = self.StyleSheet.Copy(),
default_language = self.DefaultLanguage,
view_kind = self.ViewKind,
view_zoom_kind = self.ViewZoomKind,
view_scale = self.ViewScale )
result.SetTitle( self.Title )
result.Sections = self.Sections.Copy()
return result
def TEXT( *params, **kwargs ) :
text_props = TextPropertySet()
text_props.SetFont ( kwargs.get( 'font', None ) )
text_props.SetSize ( kwargs.get( 'size', None ) )
text_props.SetBold ( kwargs.get( 'bold', False ) )
text_props.SetItalic ( kwargs.get( 'italic', False ) )
text_props.SetUnderline( kwargs.get( 'underline', False ) )
text_props.SetColour ( kwargs.get( 'colour', None ) )
if len( params ) == 1 :
return Text( params[ 0 ], text_props )
result = Inline( text_props )
apply( result.append, params )
return result
def B( *params ) :
text_props = TextPropertySet( bold=True )
if len( params ) == 1 :
return Text( params[ 0 ], text_props )
result = Inline( text_props )
apply( result.append, params )
return result
def I( *params ) :
text_props = TextPropertySet( italic=True )
if len( params ) == 1 :
return Text( params[ 0 ], text_props )
result = Inline( text_props )
apply( result.append, params )
return result
def U( *params ) :
text_props = TextPropertySet( underline=True )
if len( params ) == 1 :
return Text( params[ 0 ], text_props )
result = Inline( text_props )
apply( result.append, params )
return result
+489
View File
@@ -0,0 +1,489 @@
"""
PropertySets group common attributes together, each property set is used to control a specific part of the rendering.
PropertySets can be used in different elements of the document.
For example the FramePropertySet is used in paragraphs, tables, cells, etc.
The TextPropertySet can be used for text or in a Paragraph Style.
"""
from types import StringType
from copy import deepcopy
#
# We need some basic Type like fonts, colours and paper definitions
#
def MakeAttributeName( value ) :
assert value and type( value ) is StringType
value = value.replace( ' ', '' )
return value
class AttributedList( list ) :
def __init__( self, accepted_type=None ) :
super( AttributedList, self ).__init__()
self.AcceptedType = accepted_type
self._append = super( AttributedList, self ).append
def append( self, *values ) :
for value in values :
if self.AcceptedType : assert isinstance( value, self.AcceptedType )
self._append( value )
name = getattr( value, 'Name', None )
if name :
name = MakeAttributeName( value.Name )
setattr( self, name, value )
def __deepcopy__( self, memo ) :
result = self.__class__()
result.append( *self[:] )
return result
class Colour :
def __init__( self, name, red, green, blue ) :
self.SetName ( name )
self.SetRed ( red )
self.SetGreen( green )
self.SetBlue ( blue )
def SetName( self, value ) :
self.Name = value
return self
def SetRed( self, value ) :
self.Red = value
return self
def SetGreen( self, value ) :
self.Green = value
return self
def SetBlue( self, value ) :
self.Blue = value
return self
class Colours( AttributedList ) :
def __init__( self ) :
super( Colours, self ).__init__( Colour )
class Font :
def __init__( self, name, family, character_set = 0, pitch = None, panose = None, alternate = None ) :
self.SetName ( name )
self.SetFamily ( family )
self.SetCharacterSet( character_set )
self.SetPitch ( pitch )
self.SetPanose ( panose )
self.SetAlternate ( alternate )
def SetName( self, value ) :
self.Name = value
return self
def SetFamily( self, value ) :
self.Family = value
return self
def SetCharacterSet( self, value ) :
self.CharacterSet = value
return self
def SetPitch( self, value ) :
self.Pitch = value
return self
def SetPanose( self, value ) :
self.Panose = value
return self
def SetAlternate( self, value ) :
self.Alternate = value
return self
class Fonts( AttributedList ) :
def __init__( self ) :
super( Fonts, self ).__init__( Font )
class Paper :
def __init__( self, name, code, description, width, height ) :
self.SetName ( name )
self.SetCode ( code )
self.SetDescription( description )
self.SetWidth ( width )
self.SetHeight ( height )
def SetName( self, value ) :
self.Name = value
return self
def SetCode( self, value ) :
self.Code = value
return self
def SetDescription( self, value ) :
self.Description = value
return self
def SetWidth( self, value ) :
self.Width = value
return self
def SetHeight( self, value ) :
self.Height = value
return self
class Papers( AttributedList ) :
def __init__( self ) :
super( Papers, self ).__init__( Paper )
#
# Then we have property sets which represent different aspects of Styles
#
class MarginsPropertySet :
def __init__( self, top=None, left=None, bottom=None, right=None ) :
self.SetTop ( top )
self.SetLeft ( left )
self.SetBottom( bottom )
self.SetRight ( right )
def SetTop( self, value ) :
self.Top = value
return self
def SetLeft( self, value ) :
self.Left = value
return self
def SetBottom( self, value ) :
self.Bottom = value
return self
def SetRight( self, value ) :
self.Right = value
return self
class ShadingPropertySet :
HORIZONTAL = 1
VERTICAL = 2
FORWARD_DIAGONAL = 3
BACKWARD_DIAGONAL = 4
VERTICAL_CROSS = 5
DIAGONAL_CROSS = 6
DARK_HORIZONTAL = 7
DARK_VERTICAL = 8
DARK_FORWARD_DIAGONAL = 9
DARK_BACKWARD_DIAGONAL = 10
DARK_VERTICAL_CROSS = 11
DARK_DIAGONAL_CROSS = 12
PATTERNS = [ HORIZONTAL,
VERTICAL,
FORWARD_DIAGONAL,
BACKWARD_DIAGONAL,
VERTICAL_CROSS,
DIAGONAL_CROSS,
DARK_HORIZONTAL,
DARK_VERTICAL,
DARK_FORWARD_DIAGONAL,
DARK_BACKWARD_DIAGONAL,
DARK_VERTICAL_CROSS,
DARK_DIAGONAL_CROSS ]
def __init__( self, shading=None, pattern=None, foreground=None, background=None ) :
self.SetShading ( shading )
self.SetForeground( foreground )
self.SetBackground( background )
self.SetPattern ( pattern )
def __deepcopy__( self, memo ) :
return ShadingPropertySet( self.Shading,
self.Foreground,
self.Background,
self.Pattern )
def SetShading( self, value ) :
self.Shading = value
return self
def SetPattern( self, value ) :
assert value is None or value in self.PATTERNS
self.Pattern = value
return self
def SetForeground( self, value ) :
assert not value or isinstance( value, Colour )
self.Foreground = value
return self
def SetBackground( self, value ) :
assert not value or isinstance( value, Colour )
self.Background = value
return self
class BorderPropertySet :
SINGLE = 1
DOUBLE = 2
SHADOWED = 3
DOUBLED = 4
DOTTED = 5
DASHED = 6
HAIRLINE = 7
STYLES = [ SINGLE, DOUBLE, SHADOWED, DOUBLED, DOTTED, DASHED, HAIRLINE ]
def __init__( self, width=None, style=None, colour=None, spacing=None ) :
self.SetWidth ( width )
self.SetStyle ( style or self.SINGLE )
self.SetColour ( colour )
self.SetSpacing( spacing )
def SetWidth( self, value ) :
self.Width = value
return self
def SetStyle( self, value ) :
assert value is None or value in self.STYLES
self.Style = value
return self
def SetColour( self, value ) :
assert value is None or isinstance( value, Colour )
self.Colour = value
return self
def SetSpacing( self, value ) :
self.Spacing = value
return self
class FramePropertySet :
def __init__( self, top=None, left=None, bottom=None, right=None ) :
self.SetTop ( top )
self.SetLeft ( left )
self.SetBottom( bottom )
self.SetRight ( right )
def SetTop( self, value ) :
assert value is None or isinstance( value, BorderPropertySet )
self.Top = value
return self
def SetLeft( self, value ) :
assert value is None or isinstance( value, BorderPropertySet )
self.Left = value
return self
def SetBottom( self, value ) :
assert value is None or isinstance( value, BorderPropertySet )
self.Bottom = value
return self
def SetRight( self, value ) :
assert value is None or isinstance( value, BorderPropertySet )
self.Right = value
return self
class TabPropertySet :
DEFAULT_WIDTH = 720
LEFT = 1
RIGHT = 2
CENTER = 3
DECIMAL = 4
ALIGNMENT = [ LEFT, RIGHT, CENTER, DECIMAL ]
DOTS = 1
HYPHENS = 2
UNDERLINE = 3
THICK_LINE = 4
EQUAL_SIGN = 5
LEADERS = [ DOTS, HYPHENS, UNDERLINE, THICK_LINE, EQUAL_SIGN ]
def __init__( self, width=None, alignment=None, leader=None ) :
self.SetWidth ( width )
self.SetAlignment( alignment or self.LEFT )
self.SetLeader ( leader )
def SetWidth( self, value ) :
self.Width = value
return self
def SetAlignment( self, value ) :
assert value in self.ALIGNMENT
self.Alignment = value
return self
def SetLeader( self, value ) :
assert not value or value in self.LEADERS
self.Leader = value
return self
class TextPropertySet :
def __init__( self, font=None, size=None, bold=None, italic=None, underline=None, colour=None, frame=None, expansion=None ) :
self.SetFont ( font )
self.SetSize ( size )
self.SetBold ( bold or False )
self.SetItalic ( italic or False )
self.SetUnderline ( underline or False )
self.SetColour( colour )
self.SetFrame ( frame )
self.SetStrikeThrough ( False )
self.SetDottedUnderline( False )
self.SetDoubleUnderline( False )
self.SetWordUnderline ( False )
self.SetExpansion ( expansion )
def Copy( self ) :
return deepcopy( self )
def __deepcopy__( self, memo ) :
# the font must remain a reference to the same font that we are looking at
# so we want to stop the recursiveness at this point and return an object
# with the right references.
result = TextPropertySet( self.Font,
self.Size,
self.Bold,
self.Italic,
self.Underline,
self.Colour,
deepcopy( self.Frame, memo ) )
result.SetStrikeThrough( self.StrikeThrough )
return result
def SetFont( self, value ) :
assert not value or isinstance( value, Font )
self.Font = value
return self
def SetSize( self, value ) :
self.Size = value
return self
def SetBold( self, value ) :
self.Bold = False
if value : self.Bold = True
return self
def SetItalic( self, value ) :
self.Italic = False
if value : self.Italic = True
return self
def SetUnderline( self, value ) :
self.Underline = False
if value : self.Underline = True
return self
def SetColour( self, value ) :
assert value is None or isinstance( value, Colour )
self.Colour = value
return self
def SetFrame( self, value ) :
assert value is None or isinstance( value, BorderPropertySet )
self.Frame = value
return self
def SetStrikeThrough( self, value ) :
self.StrikeThrough = False
if value : self.StrikeThrough = True
return self
def SetDottedUnderline( self, value ) :
self.DottedUnderline = False
if value : self.DottedUnderline = True
return self
def SetDoubleUnderline( self, value ) :
self.DoubleUnderline = False
if value : self.DoubleUnderline = True
return self
def SetWordUnderline( self, value ) :
self.WordUnderline = False
if value : self.WordUnderline = True
return self
def SetExpansion( self, value ) :
self.Expansion = value
return self
class ParagraphPropertySet :
LEFT = 1
RIGHT = 2
CENTER = 3
JUSTIFY = 4
DISTRIBUTE = 5
ALIGNMENT = [ LEFT, RIGHT, CENTER, JUSTIFY, DISTRIBUTE ]
def __init__( self, alignment=None, space_before=None, space_after=None, tabs=None, first_line_indent=None, left_indent=None, right_indent=None, page_break_before=None ) :
self.SetAlignment ( alignment or self.LEFT )
self.SetSpaceBefore( space_before )
self.SetSpaceAfter ( space_after )
self.Tabs = []
if tabs : apply( self.SetTabs, tabs )
self.SetFirstLineIndent( first_line_indent or None )
self.SetLeftIndent ( left_indent or None )
self.SetRightIndent ( right_indent or None )
self.SetPageBreakBefore( page_break_before )
self.SetSpaceBetweenLines( None )
def Copy( self ) :
return deepcopy( self )
def SetAlignment( self, value ) :
assert not value or value in self.ALIGNMENT
self.Alignment = value or self.LEFT
return self
def SetSpaceBefore( self, value ) :
self.SpaceBefore = value
return self
def SetSpaceAfter( self, value ) :
self.SpaceAfter = value
return self
def SetTabs( self, *params ) :
self.Tabs = params
return self
def SetFirstLineIndent( self, value ) :
self.FirstLineIndent = value
return self
def SetLeftIndent( self, value ) :
self.LeftIndent = value
return self
def SetRightIndent( self, value ) :
self.RightIndent = value
return self
def SetSpaceBetweenLines( self, value ) :
self.SpaceBetweenLines = value
return self
def SetPageBreakBefore( self, value ) :
self.PageBreakBefore = False
if value : self.PageBreakBefore = True
return self
# Some short cuts to make the code a bit easier to read
MarginsPS = MarginsPropertySet
ShadingPS = ShadingPropertySet
BorderPS = BorderPropertySet
FramePS = FramePropertySet
TabPS = TabPropertySet
TextPS = TextPropertySet
ParagraphPS = ParagraphPropertySet
+19
View File
@@ -0,0 +1,19 @@
Version 0.46
Added EMF support.
Added more sophisticated scaling options.
See examples2.py for both.
Grant Edwards, grante@users.sourceforge.net
Version 0.45
Finally, image support!!! Handles PNGs and JPGs.
See examples2.py for the gory details.
Simon Cusack, scusack@sourceforge.net
+639
View File
@@ -0,0 +1,639 @@
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' )
+94
View File
@@ -0,0 +1,94 @@
"""
A Styles is a collection of PropertySets that can be applied to a particular RTF element.
At present there are only two, Text and Paragraph but ListStyles will be added soon too.
"""
from PropertySets import *
class TextStyle :
def __init__( self, text_props, name=None, shading_props=None ) :
self.SetTextPropertySet ( text_props )
self.SetName ( name )
self.SetShadingPropertySet( shading_props )
def Copy( self ) :
return deepcopy( self )
def SetName( self, value ) :
self.Name = value
return self
def SetTextPropertySet( self, value ) :
assert isinstance( value, TextPropertySet )
self.TextPropertySet = value
return self
def SetShadingPropertySet( self, value ) :
assert value is None or isinstance( value, ShadingPropertySet )
self.ShadingPropertySet = value or ShadingPropertySet()
return self
class ParagraphStyle :
def __init__( self, name, text_style, paragraph_props=None, frame_props=None, shading_props=None ) :
# A style must have Font and a Font Size but the Text property set doesn't
# make these mandatory so that they can be used for overrides so at this point
# we need to make sure that that we have these values set
if not text_style.TextPropertySet.Font : raise Exception( 'Paragraph Styles must have a Font specified.' )
if not text_style.TextPropertySet.Size : raise Exception( 'Paragraph Styles must have a Font Size specified.' )
self.SetName ( name )
self.SetTextStyle ( text_style )
self.SetParagraphPropertySet( paragraph_props )
self.SetFramePropertySet ( frame_props )
self.SetShadingPropertySet ( shading_props )
self.SetBasedOn( None )
self.SetNext ( None )
def Copy( self ) :
return deepcopy( self )
def SetName( self, value ) :
self.Name = value
return self
def SetTextStyle( self, value ) :
assert isinstance( value, TextStyle )
self.TextStyle = value
return self
def SetParagraphPropertySet( self, value ) :
assert value is None or isinstance( value, ParagraphPropertySet )
self.ParagraphPropertySet = value or ParagraphPropertySet()
return self
def SetFramePropertySet( self, value ) :
assert value is None or isinstance( value, FramePropertySet )
self.FramePropertySet = value or FramePropertySet()
return self
def SetShadingPropertySet( self, value ) :
"""Set the background shading for the paragraph."""
assert value is None or isinstance( value, ShadingPropertySet )
self.ShadingPropertySet = value or ShadingPropertySet()
return self
def SetBasedOn( self, value ) :
"""Set the Paragraph Style that this one is based on."""
assert not value or isinstance( value, ParagraphStyle )
self.BasedOn = value
return self
def SetNext( self, value ) :
"""Set the Paragraph Style that should follow this one."""
assert not value or isinstance( value, ParagraphStyle )
self.Next = value
return self
+12
View File
@@ -0,0 +1,12 @@
from PropertySets import *
from Elements import *
from Styles import *
from Renderer import *
def dumps(doc):
import cStringIO
s=cStringIO.StringIO()
r=Renderer()
r.Write(doc,s)
return s.getvalue()