summaryrefslogtreecommitdiff
path: root/bin/reportlab/platypus/frames.py
diff options
context:
space:
mode:
authorpinky <>2007-01-07 23:35:16 +0000
committerpinky <>2007-01-07 23:35:16 +0000
commit6488c0b0da779d567e0ae6a7778f1fa2de2a5ccb (patch)
treec86e4674f48c408d2a4a2aebfd15e29d4b269dce /bin/reportlab/platypus/frames.py
parent87b2a6bebceba02de5b98c2bd963cbb2e363c453 (diff)
RL2
bzr revid: pinky-1da086d6ffc903bddd20216fdc81b76f38293992
Diffstat (limited to 'bin/reportlab/platypus/frames.py')
-rw-r--r--bin/reportlab/platypus/frames.py206
1 files changed, 206 insertions, 0 deletions
diff --git a/bin/reportlab/platypus/frames.py b/bin/reportlab/platypus/frames.py
new file mode 100644
index 00000000000..510c14706be
--- /dev/null
+++ b/bin/reportlab/platypus/frames.py
@@ -0,0 +1,206 @@
+#Copyright ReportLab Europe Ltd. 2000-2004
+#see license.txt for license details
+#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/platypus/frames.py
+
+__version__=''' $Id: frames.py 2852 2006-05-08 15:04:15Z rgbecker $ '''
+
+__doc__="""
+"""
+
+import logging
+logger = logging.getLogger('reportlab.platypus')
+
+_geomAttr=('x1', 'y1', 'width', 'height', 'leftPadding', 'bottomPadding', 'rightPadding', 'topPadding')
+from reportlab import rl_config
+_FUZZ=rl_config._FUZZ
+
+class Frame:
+ '''
+ A Frame is a piece of space in a document that is filled by the
+ "flowables" in the story. For example in a book like document most
+ pages have the text paragraphs in one or two frames. For generality
+ a page might have several frames (for example for 3 column text or
+ for text that wraps around a graphic).
+
+ After creation a Frame is not usually manipulated directly by the
+ applications program -- it is used internally by the platypus modules.
+
+ Here is a diagramatid abstraction for the definitional part of a Frame
+
+ width x2,y2
+ +---------------------------------+
+ | l top padding r | h
+ | e +-------------------------+ i | e
+ | f | | g | i
+ | t | | h | g
+ | | | t | h
+ | p | | | t
+ | a | | p |
+ | d | | a |
+ | | | d |
+ | +-------------------------+ |
+ | bottom padding |
+ +---------------------------------+
+ (x1,y1) <-- lower left corner
+
+ NOTE!! Frames are stateful objects. No single frame should be used in
+ two documents at the same time (especially in the presence of multithreading.
+ '''
+ def __init__(self, x1, y1, width,height, leftPadding=6, bottomPadding=6,
+ rightPadding=6, topPadding=6, id=None, showBoundary=0,
+ overlapAttachedSpace=None,_debug=None):
+ self.id = id
+ self._debug = _debug
+
+ #these say where it goes on the page
+ self.__dict__['_x1'] = x1
+ self.__dict__['_y1'] = y1
+ self.__dict__['_width'] = width
+ self.__dict__['_height'] = height
+
+ #these create some padding.
+ self.__dict__['_leftPadding'] = leftPadding
+ self.__dict__['_bottomPadding'] = bottomPadding
+ self.__dict__['_rightPadding'] = rightPadding
+ self.__dict__['_topPadding'] = topPadding
+
+ # if we want a boundary to be shown
+ self.showBoundary = showBoundary
+
+ if overlapAttachedSpace is None: overlapAttachedSpace = rl_config.overlapAttachedSpace
+ self._oASpace = overlapAttachedSpace
+ self._geom()
+ self._reset()
+
+ def __getattr__(self,a):
+ if a in _geomAttr: return self.__dict__['_'+a]
+ raise AttributeError, a
+
+ def __setattr__(self,a,v):
+ if a in _geomAttr:
+ self.__dict__['_'+a] = v
+ self._geom()
+ else:
+ self.__dict__[a] = v
+
+ def _geom(self):
+ self._x2 = self._x1 + self._width
+ self._y2 = self._y1 + self._height
+ #efficiency
+ self._y1p = self._y1 + self._bottomPadding
+ #work out the available space
+ self._aW = self._x2 - self._x1 - self._leftPadding - self._rightPadding
+ self._aH = self._y2 - self._y1p - self._topPadding
+
+ def _reset(self):
+ #drawing starts at top left
+ self._x = self._x1 + self._leftPadding
+ self._y = self._y2 - self._topPadding
+ self._atTop = 1
+ self._prevASpace = 0
+
+ # these two should NOT be set on a frame.
+ # they are used when Indenter flowables want
+ # to adjust edges e.g. to do nested lists
+ self._leftExtraIndent = 0.0
+ self._rightExtraIndent = 0.0
+
+ def _getAvailableWidth(self):
+ return self._aW - self._leftExtraIndent - self._rightExtraIndent
+
+ def _add(self, flowable, canv, trySplit=0):
+ """ Draws the flowable at the current position.
+ Returns 1 if successful, 0 if it would not fit.
+ Raises a LayoutError if the object is too wide,
+ or if it is too high for a totally empty frame,
+ to avoid infinite loops"""
+ if getattr(flowable,'frameAction',None):
+ flowable.frameAction(self)
+ return 1
+
+ y = self._y
+ p = self._y1p
+ s = 0
+ aW = self._getAvailableWidth()
+ if not self._atTop:
+ s =flowable.getSpaceBefore()
+ if self._oASpace:
+ s = max(s-self._prevASpace,0)
+ h = y - p - s
+ if h>0:
+ flowable._frame = self
+ flowable.canv = canv #so they can use stringWidth etc
+ w, h = flowable.wrap(aW, h)
+ del flowable.canv, flowable._frame
+ else:
+ return 0
+
+ h += s
+ y -= h
+
+ if y < p-_FUZZ:
+ if not rl_config.allowTableBoundsErrors and ((h>self._aH or w>aW) and not trySplit):
+ raise "LayoutError", "Flowable %s (%sx%s points) too large for frame (%sx%s points)." % (
+ flowable.__class__, w,h, aW,self._aH)
+ return 0
+ else:
+ #now we can draw it, and update the current point.
+ flowable._frame = self
+ flowable.drawOn(canv, self._x + self._leftExtraIndent, y, _sW=aW-w)
+ if self._debug: logger.debug('drew %s' % flowable.identity())
+ del flowable._frame
+ s = flowable.getSpaceAfter()
+ y -= s
+ if self._oASpace: self._prevASpace = s
+ if y!=self._y: self._atTop = 0
+ self._y = y
+ return 1
+
+ add = _add
+
+ def split(self,flowable,canv):
+ '''Ask the flowable to split using up the available space.'''
+ y = self._y
+ p = self._y1p
+ s = 0
+ if not self._atTop: s = flowable.getSpaceBefore()
+ flowable.canv = canv #some flowables might need this
+ r = flowable.split(self._aW, y-p-s)
+ del flowable.canv
+ return r
+
+ def drawBoundary(self,canv):
+ "draw the frame boundary as a rectangle (primarily for debugging)."
+ from reportlab.lib.colors import Color, CMYKColor, toColor
+ sb = self.showBoundary
+ isColor = type(sb) in (type(''),type(()),type([])) or isinstance(sb,Color)
+ if isColor:
+ sb = toColor(sb,self)
+ if sb is self: isColor = 0
+ else:
+ canv.saveState()
+ canv.setStrokeColor(sb)
+ canv.rect(
+ self._x1,
+ self._y1,
+ self._x2 - self._x1,
+ self._y2 - self._y1
+ )
+ if isColor: canv.restoreState()
+
+ def addFromList(self, drawlist, canv):
+ """Consumes objects from the front of the list until the
+ frame is full. If it cannot fit one object, raises
+ an exception."""
+
+ if self._debug: logger.debug("enter Frame.addFromlist() for frame %s" % self.id)
+ if self.showBoundary:
+ self.drawBoundary(canv)
+
+ while len(drawlist) > 0:
+ head = drawlist[0]
+ if self.add(head,canv,trySplit=0):
+ del drawlist[0]
+ else:
+ #leave it in the list for later
+ break