123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- #Copyright ReportLab Europe Ltd. 2000-2017
- #see license.txt for license details
- #history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/lib/styles.py
- __version__='3.3.0'
- __doc__='''Classes for ParagraphStyle and similar things.
- A style is a collection of attributes, but with some extra features
- to allow 'inheritance' from a parent, and to ensure nobody makes
- changes after construction.
- ParagraphStyle shows all the attributes available for formatting
- paragraphs.
- getSampleStyleSheet() returns a stylesheet you can use for initial
- development, with a few basic heading and text styles.
- '''
- __all__=(
- 'PropertySet',
- 'ParagraphStyle',
- 'LineStyle',
- 'ListStyle',
- 'StyleSheet1',
- 'getSampleStyleSheet',
- )
- from reportlab.lib.colors import white, black
- from reportlab.lib.enums import TA_LEFT, TA_CENTER
- from reportlab.lib.fonts import tt2ps
- from reportlab.rl_config import canvas_basefontname as _baseFontName, \
- underlineWidth as _baseUnderlineWidth, \
- underlineOffset as _baseUnderlineOffset, \
- underlineGap as _baseUnderlineGap, \
- strikeWidth as _baseStrikeWidth, \
- strikeOffset as _baseStrikeOffset, \
- strikeGap as _baseStrikeGap, \
- spaceShrinkage as _spaceShrinkage, \
- platypus_link_underline as _platypus_link_underline, \
- hyphenationLang as _hyphenationLang, \
- hyphenationMinWordLength as _hyphenationMinWordLength, \
- uriWasteReduce as _uriWasteReduce, \
- embeddedHyphenation as _embeddedHyphenation
- _baseFontNameB = tt2ps(_baseFontName,1,0)
- _baseFontNameI = tt2ps(_baseFontName,0,1)
- _baseFontNameBI = tt2ps(_baseFontName,1,1)
- ###########################################################
- # This class provides an 'instance inheritance'
- # mechanism for its descendants, simpler than acquisition
- # but not as far-reaching
- ###########################################################
- class PropertySet:
- defaults = {}
- def __init__(self, name, parent=None, **kw):
- """When initialized, it copies the class defaults;
- then takes a copy of the attributes of the parent
- if any. All the work is done in init - styles
- should cost little to use at runtime."""
- # step one - validate the hell out of it
- assert 'name' not in self.defaults, "Class Defaults may not contain a 'name' attribute"
- assert 'parent' not in self.defaults, "Class Defaults may not contain a 'parent' attribute"
- if parent:
- assert parent.__class__ == self.__class__, "Parent style %s must have same class as new style %s" % (parent.__class__.__name__,self.__class__.__name__)
- #step two
- self.name = name
- self.parent = parent
- self.__dict__.update(self.defaults)
- #step two - copy from parent if any. Try to be
- # very strict that only keys in class defaults are
- # allowed, so they cannot inherit
- self.refresh()
- self._setKwds(**kw)
- def _setKwds(self,**kw):
- #step three - copy keywords if any
- for key, value in kw.items():
- self.__dict__[key] = value
- def __repr__(self):
- return "<%s '%s'>" % (self.__class__.__name__, self.name)
- def refresh(self):
- """re-fetches attributes from the parent on demand;
- use if you have been hacking the styles. This is
- used by __init__"""
- if self.parent:
- for key, value in self.parent.__dict__.items():
- if (key not in ['name','parent']):
- self.__dict__[key] = value
- def listAttrs(self, indent=''):
- print(indent + 'name =', self.name)
- print(indent + 'parent =', self.parent)
- keylist = list(self.__dict__.keys())
- keylist.sort()
- keylist.remove('name')
- keylist.remove('parent')
- for key in keylist:
- value = self.__dict__.get(key, None)
- print(indent + '%s = %s' % (key, value))
- def clone(self, name, parent=None, **kwds):
- r = self.__class__(name,parent)
- r.__dict__ = self.__dict__.copy()
- r.name = name
- r.parent = parent is None and self or parent
- r._setKwds(**kwds)
- return r
- class ParagraphStyle(PropertySet):
- defaults = {
- 'fontName':_baseFontName,
- 'fontSize':10,
- 'leading':12,
- 'leftIndent':0,
- 'rightIndent':0,
- 'firstLineIndent':0,
- 'alignment':TA_LEFT,
- 'spaceBefore':0,
- 'spaceAfter':0,
- 'bulletFontName':_baseFontName,
- 'bulletFontSize':10,
- 'bulletIndent':0,
- #'bulletColor':black,
- 'textColor': black,
- 'backColor':None,
- 'wordWrap':None, #None means do nothing special
- #CJK use Chinese Line breaking
- #LTR RTL use left to right / right to left
- #with support from pyfribi2 if available
- 'borderWidth': 0,
- 'borderPadding': 0,
- 'borderColor': None,
- 'borderRadius': None,
- 'allowWidows': 1,
- 'allowOrphans': 0,
- 'textTransform':None, #uppercase lowercase (captitalize not yet) or None or absent
- 'endDots':None, #dots on the last line of left/right justified paras
- #string or object with text and optional fontName, fontSize, textColor & backColor
- #dy
- 'splitLongWords':1, #make best efforts to split long words
- 'underlineWidth': _baseUnderlineWidth, #underline width default
- 'bulletAnchor': 'start', #where the bullet is anchored ie start, middle, end or numeric
- 'justifyLastLine': 0, #n allow justification on the last line for more than n words 0 means don't bother
- 'justifyBreaks': 0, #justify lines broken with <br/>
- 'spaceShrinkage': _spaceShrinkage, #allow shrinkage of percentage of space to fit on line
- 'strikeWidth': _baseStrikeWidth, #stroke width default
- 'underlineOffset': _baseUnderlineOffset, #fraction of fontsize to offset underlines
- 'underlineGap': _baseUnderlineGap, #gap for double/triple underline
- 'strikeOffset': _baseStrikeOffset, #fraction of fontsize to offset strikethrough
- 'strikeGap': _baseStrikeGap, #gap for double/triple strike
- 'linkUnderline': _platypus_link_underline,
- 'underlineColor': None,
- 'strikeColor': None,
- 'hyphenationLang': _hyphenationLang,
- #'hyphenationMinWordLength': _hyphenationMinWordLength,
- 'embeddedHyphenation': _embeddedHyphenation,
- 'uriWasteReduce': _uriWasteReduce,
- }
- class LineStyle(PropertySet):
- defaults = {
- 'width':1,
- 'color': black
- }
- def prepareCanvas(self, canvas):
- """You can ask a LineStyle to set up the canvas for drawing
- the lines."""
- canvas.setLineWidth(1)
- #etc. etc.
- class ListStyle(PropertySet):
- defaults = dict(
- leftIndent=18,
- rightIndent=0,
- bulletAlign='left',
- bulletType='1',
- bulletColor=black,
- bulletFontName='Helvetica',
- bulletFontSize=12,
- bulletOffsetY=0,
- bulletDedent='auto',
- bulletDir='ltr',
- bulletFormat=None,
- start=None, #starting value for a list; if a list then the start sequence
- )
- _stylesheet1_undefined = object()
- class StyleSheet1:
- """
- This may or may not be used. The idea is to:
-
- 1. slightly simplify construction of stylesheets;
-
- 2. enforce rules to validate styles when added
- (e.g. we may choose to disallow having both
- 'heading1' and 'Heading1' - actual rules are
- open to discussion);
-
- 3. allow aliases and alternate style lookup
- mechanisms
-
- 4. Have a place to hang style-manipulation
- methods (save, load, maybe support a GUI
- editor)
-
- Access is via getitem, so they can be
- compatible with plain old dictionaries.
- """
- def __init__(self):
- self.byName = {}
- self.byAlias = {}
- def __getitem__(self, key):
- try:
- return self.byAlias[key]
- except KeyError:
- try:
- return self.byName[key]
- except KeyError:
- raise KeyError("Style '%s' not found in stylesheet" % key)
- def get(self,key,default=_stylesheet1_undefined):
- try:
- return self[key]
- except KeyError:
- if default!=_stylesheet1_undefined: return default
- raise
- def __contains__(self, key):
- return key in self.byAlias or key in self.byName
- def has_key(self,key):
- return key in self
- def add(self, style, alias=None):
- key = style.name
- if key in self.byName:
- raise KeyError("Style '%s' already defined in stylesheet" % key)
- if key in self.byAlias:
- raise KeyError("Style name '%s' is already an alias in stylesheet" % key)
- if alias:
- if alias in self.byName:
- raise KeyError("Style '%s' already defined in stylesheet" % alias)
- if alias in self.byAlias:
- raise KeyError("Alias name '%s' is already an alias in stylesheet" % alias)
- #passed all tests? OK, add it
- self.byName[key] = style
- if alias:
- self.byAlias[alias] = style
- def list(self):
- styles = list(self.byName.items())
- styles.sort()
- alii = {}
- for (alias, style) in list(self.byAlias.items()):
- alii[style] = alias
- for (name, style) in styles:
- alias = alii.get(style, None)
- print(name, alias)
- style.listAttrs(' ')
- print()
- def testStyles():
- pNormal = ParagraphStyle('Normal',None)
- pNormal.fontName = _baseFontName
- pNormal.fontSize = 12
- pNormal.leading = 14.4
- pNormal.listAttrs()
- print()
- pPre = ParagraphStyle('Literal', pNormal)
- pPre.fontName = 'Courier'
- pPre.listAttrs()
- return pNormal, pPre
- def getSampleStyleSheet():
- """Returns a stylesheet object"""
- stylesheet = StyleSheet1()
- stylesheet.add(ParagraphStyle(name='Normal',
- fontName=_baseFontName,
- fontSize=10,
- leading=12)
- )
- stylesheet.add(ParagraphStyle(name='BodyText',
- parent=stylesheet['Normal'],
- spaceBefore=6)
- )
- stylesheet.add(ParagraphStyle(name='Italic',
- parent=stylesheet['BodyText'],
- fontName = _baseFontNameI)
- )
- stylesheet.add(ParagraphStyle(name='Heading1',
- parent=stylesheet['Normal'],
- fontName = _baseFontNameB,
- fontSize=18,
- leading=22,
- spaceAfter=6),
- alias='h1')
- stylesheet.add(ParagraphStyle(name='Title',
- parent=stylesheet['Normal'],
- fontName = _baseFontNameB,
- fontSize=18,
- leading=22,
- alignment=TA_CENTER,
- spaceAfter=6),
- alias='title')
- stylesheet.add(ParagraphStyle(name='Heading2',
- parent=stylesheet['Normal'],
- fontName = _baseFontNameB,
- fontSize=14,
- leading=18,
- spaceBefore=12,
- spaceAfter=6),
- alias='h2')
- stylesheet.add(ParagraphStyle(name='Heading3',
- parent=stylesheet['Normal'],
- fontName = _baseFontNameBI,
- fontSize=12,
- leading=14,
- spaceBefore=12,
- spaceAfter=6),
- alias='h3')
- stylesheet.add(ParagraphStyle(name='Heading4',
- parent=stylesheet['Normal'],
- fontName = _baseFontNameBI,
- fontSize=10,
- leading=12,
- spaceBefore=10,
- spaceAfter=4),
- alias='h4')
- stylesheet.add(ParagraphStyle(name='Heading5',
- parent=stylesheet['Normal'],
- fontName = _baseFontNameB,
- fontSize=9,
- leading=10.8,
- spaceBefore=8,
- spaceAfter=4),
- alias='h5')
- stylesheet.add(ParagraphStyle(name='Heading6',
- parent=stylesheet['Normal'],
- fontName = _baseFontNameB,
- fontSize=7,
- leading=8.4,
- spaceBefore=6,
- spaceAfter=2),
- alias='h6')
- stylesheet.add(ParagraphStyle(name='Bullet',
- parent=stylesheet['Normal'],
- firstLineIndent=0,
- spaceBefore=3),
- alias='bu')
- stylesheet.add(ParagraphStyle(name='Definition',
- parent=stylesheet['Normal'],
- firstLineIndent=0,
- leftIndent=36,
- bulletIndent=0,
- spaceBefore=6,
- bulletFontName=_baseFontNameBI),
- alias='df')
- stylesheet.add(ParagraphStyle(name='Code',
- parent=stylesheet['Normal'],
- fontName='Courier',
- fontSize=8,
- leading=8.8,
- firstLineIndent=0,
- leftIndent=36,
- hyphenationLang=''))
- stylesheet.add(ListStyle(name='UnorderedList',
- parent=None,
- leftIndent=18,
- rightIndent=0,
- bulletAlign='left',
- bulletType='1',
- bulletColor=black,
- bulletFontName='Helvetica',
- bulletFontSize=12,
- bulletOffsetY=0,
- bulletDedent='auto',
- bulletDir='ltr',
- bulletFormat=None,
- #start='circle square blackstar sparkle disc diamond'.split(),
- start=None,
- ),
- alias='ul')
- stylesheet.add(ListStyle(name='OrderedList',
- parent=None,
- leftIndent=18,
- rightIndent=0,
- bulletAlign='left',
- bulletType='1',
- bulletColor=black,
- bulletFontName='Helvetica',
- bulletFontSize=12,
- bulletOffsetY=0,
- bulletDedent='auto',
- bulletDir='ltr',
- bulletFormat=None,
- #start='1 a A i I'.split(),
- start=None,
- ),
- alias='ol')
- return stylesheet
|