<commit_before>"""
.t PLATYPUS Proposal

CVS Revision History
.beginPre Code
$Log: platprop.yml,v $
Revision 1.7  2000/05/17 22:18:09  rgbecker
Renamed BasicFrame to Frame

Revision 1.6  2000/05/17 16:30:09  rgbecker
Removal of SimpleFrame

Revision 1.5  2000/05/16 15:40:25  andy_robinson
It now actually says something - checking in before going home

Revision 1.4  2000/05/16 11:18:10  andy_robinson
Added the first of the demo drawings

Revision 1.3  2000/05/15 16:02:41  andy_robinson
More Paragraph Documentation

Revision 1.1  2000/05/15 10:58:54  andy_robinson
Platypus Proposal
.endPre

This document is only partly written.  Anticipated sections:
.bu     What is PLATYPUS about?
.bu     Frames and Flowables
.bu     Paragraphs, Paragraph Styles and Text
.bu     Document Templates


.h1 Introduction

.h2 What is this document about?

The ReportLab toolkit aims to be a new generation of reporting tool, with no limits to formatting.  We expect it to grow large, so have divided it into a number of subpackages with clear layers of functionality.

The most basic layer for mist users is the pdfgen subpackage.  This exposes a Canvas object with methods which correspond almost one-to-one with the page marking operators in the PDF file format.  This means that programmers can write code to do anything they want, and build up their own layers of reusable code on top.  However, many common requirements are not met by PDF directly - it does not do anything to help you wrap paragraphs or move down the page.  

PLATYPUS stands for "Page Layout and Typography Using Scripts".  It is the "next layer up" and deals with frames on pages, and "flowable objects" such as paragraphs, images or tables which can be placed within them.  Users will be able to generate "stories" containing paragraphs and other objects, and write "document templates" to process them, hopefully with very few limitations on the effects thatcan be achieved.

This was always an envisaged part of the package, but has taken a long time to get right.  The first try was done in July 1999 for the Open Source conference (in fact, it was needed to make the presentation slides), but was simply too hideous to release.  A second try was put together in November 1999 and progressively refined, appearing when PDFgen was renamed to ReportLab.  This made it possible to produce very basic flowing documents, but lacked many key features and still wasn't general enough.  

We have been discussing better implementations for several months and now think we have something promising.  <NAME> has just checked a trial implementation into CVS, and we have used it to create this document.  Our main goals in May and June are to get feedback on and solidify this API, and to use it to produce our own documentation.  We therefore invite comment from the ReportLab and Python community.   The content of this document will be updated as comments are received, and the formatting will hopefully get prettier as we learn how to make nice DocTemplates!

Please pay close attention - you will have to live with this for some time, and this I your chance to make sure it is clean and usable!


.h2 Use Cases

PLATYPUS should facilitate at least the following kinds of applications:
.bu     Text Wrapping:  you are writing code to create a form, but this needs a few paragraphs of justified, wrapped text.  You don't want to do the wrapping yourself!
.bu    Word-Processor style reports which automatically flow from one page to the next, with page headers and footers which can be related to the story content (e.g. page numbers, chapter headings in titles)
.bu    Database style reports where a large data set is divided into groups with group headers and footers, each occupying a rectangular region on the page, and which flow from page to page
.bu    Tables and box constructions in which each cell can contain flowing text, images and other graphic objects

Specifically, we would like to be able to build a book, or the Python Library Reference, automatically from source.

PLATYPUS is NOT designed for
.bu    Precision typesetting by hand - if you are hand-editing a document and want PDF, use something else (Word, FrameMaker, TeX) and distill the output to PDF instead.  ReportLab is about reports, where you want to automatically generate the same kind of document many times.

.bu   Mathematical typography.  While people no doubt could create some nice equation widgets in our framework, TEX will probably never be exceeded in this regard.

.bu     Quick and dirty database reports on Windows machines which go straight to a printer - Access, Crystal et al. handle this well already.


 
.h1 Frames and Flowables

There are several concepts in PLATYPUS and we will go a stage at a time.  We presume familiarity with the use of PDFgen package to create basic documents.  The stage we will save until last is how to create a flowing multi-page document from some data source.  The simplest thing you can do with PLATYPUS is to
place a frame on a page, add some content to it, and tell it to draw.  Let's
do this.  Here's some code which would create a frame and add two paragraphs to it:

.beginPre Code 
    canvas = Canvas('figure1.pdf')

    frame = Frame(
                    2*inch,     # x
                    4*inch,     # y at bottom
                    4*inch,     # width
                    5*inch     # height
                    )
    frame.showBoundary = 1  # helps us see what's going on

    bodyStyle = ParagraphStyle('Body',
                       fontName='Times-Roman',
                       fontSize=24,
                       leading=28,
                       spaceBefore=6)

    para1 = Paragraph('Spam spam spam spam. ' * 5, bodyStyle)
    para2 = Paragraph('Eggs eggs eggs. ' * 5, bodyStyle)

    mydata = [para1, para2]

    frame.addFromList(mydata,canvas)
    canvas.save()
.endPre

This will result in a page like the one below:

.custom platdemos PlatPropFigure1

Frame has an <i>addFromList</i> method to display content.  It consumes objects from the front of the list until no more can fit in its client area or until the list is empty.

Note that there is a certain amount of space between the frame and the paragraphs within it.  Frames have four 'padding' attributes which can control this. The attributes can be seen below in the method signature for initializing a frame.

.Comment  At the moment we have a Frame class which defines the space a frame will take up and which is used by DocTemplate; SimpleFrame descends from this and has the addFromList method to allow one to create and use a frame as we did above.  We hope to abolish the distinction so users interact with a class called Frame or BaseFrame.


.beginPre Code
class Frame:
	'''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)
	'''
	def __init__(self, x1, y1, width,height, leftPadding=6, bottomPadding=6,
			rightPadding=6, topPadding=6, id=None, showBoundary=0):

.endPre

The Padding attributes are formally unnecessary - you could just make the frame smaller - but are expected to make life easy when working with frames inside tables.  showBoundary is a debugging tool to draw a rectangle to show where the frame is.  <i>(Should the padding be zero by default to reduce surprises?)</i>


.h2 Flowable objects and frame packing

The things which go into frames must conform to the "Flowable" interface.  A conversation takes place between a Flowable object and a frame involving a number of steps.  First the packer calls flowable.wrap(availWidth, availheight) to say how much space is available.  The Flowable should reply with the width and height it wants.  If this fits, fine; it is told to draw itself in the right place.  If not, the packer will ask it to split (again with the same arguments). A dumb flowable will return an empty list indicating that it cannot split.  A smarter one such as a paragraph or a table might split itself into a number of separate flowables, the first of which should fit into the remaining space.

Users may want to implement new Flowable Objects.  For this reason we will provide some high level base classes - for example, a BaseFigure class which draws a blank rectangle and a numbered caption underneath; this could be subclassed and one Draw method added to produce a new kind of graphic.

A new flowable should implement the methods wrap(), split() and draw().  Here's a class called XBox which just draws a box of a given height and width with diagonal lines through it, centred within the frame, and some text in the centre:

.beginPre Code

    class Box:
        def __init__(self, width, height, text):
                """Size is specified up front so we just keep it handy"""
                self.width = width
                self.height = height
                self.text = text

	def wrap(self, availWidth, availHeight):
		"""This will be called by the enclosing frame before objects
		are asked their size, drawn or whatever.  It returns the
		size actually used."""
                # we know how far across to draw it later
                self.dx = 0.5* (availWidth - self.width)
		return (self.width, self.height)

	def split(self, availWidth, availheight):
		"""This will be called by more sophisticated frames when
		wrap fails. Stupid flowables should return []. Clever flowables
		should split themselves and return a list of flowables"""
		return []

	def draw(self):
                self.canv.translate(self.dx, 0)
		self.canv.rect(0, 0, self.width, self.height)
		self.canv.line(0, 0, self.width, self.height)
		self.canv.line(0, self.height, self.width, 0)

		#centre the text
		self.canv.setFont('Times-Roman',12)
		self.canv.drawCentredString(0.5*self.width, 0.5*self.height, self.text)

.endPre

The base Flowable class tried to make drawing easy - it saves and restores the canvas around whatever you do in draw(), so preceding objects cannot affect the way it is drawn, and translates the canvas so that (0,0) is at the lower left of the region the Flowable has requested.

Flowables also have two 'magic' attributes called spaceBefore and spaceAfter, which are zero by default.  These do not belong to the object itself and availHeight should exvude them.  spaceBefore will be added before all objects expect the first in a frame; spaceAfter is added after all objects except the last in a frame.  Paragraphs styles make heavy use of spaceBefore and spaceAfter, getting the information from their associated style objects.

.h2 Flowable objects provided "out of the box"

Here are the things in the package at the moment, or which we plan to add before a release.
.df <b>Paragraph</b> - you know what these are
.df <b>Preformatted</b> - this is for printing code such as the sample in this document.  In Preformatted text there are no font variations and the existing line breaks are used.  A Preformatted object is initialized just like a paragraph, but only uses some of the attributes of the ParagraphStyle.
.df <b>Image</b> - this takes an image file name and an optional width and height.  The latter will be taken from the image file at a scale of 1 point per pixel if no height and width are given.  The image is centred within the frame.
.df <b>Figure</b> - a base class for a rectangular drawing with a caption; people can easily override this to add new ones
.df <b>Spacer</b> - this takes a width and height and serves just to take up some space within the frame.  <i>Note that the width is spurious at the moment.  It is there in the vague hope that one day our packing algorithm starts working in two dimensions - we did not want to be accused of x-ism, even though x-ists we currently are.</i><commit_msg>Minor textual changes<commit_after>"""
.t PLATYPUS Proposal

CVS Revision History
.beginPre Code
$Log: platprop.yml,v $
Revision 1.8  2000/05/18 11:27:18  rgbecker
Minor textual changes

Revision 1.7  2000/05/17 22:18:09  rgbecker
Renamed BasicFrame to Frame

Revision 1.6  2000/05/17 16:30:09  rgbecker
Removal of SimpleFrame

Revision 1.5  2000/05/16 15:40:25  andy_robinson
It now actually says something - checking in before going home

Revision 1.4  2000/05/16 11:18:10  andy_robinson
Added the first of the demo drawings

Revision 1.3  2000/05/15 16:02:41  andy_robinson
More Paragraph Documentation

Revision 1.1  2000/05/15 10:58:54  andy_robinson
Platypus Proposal
.endPre

This document is only partly written.  Anticipated sections:
.bu     What is PLATYPUS about?
.bu     Frames and Flowables
.bu     Paragraphs, Paragraph Styles and Text
.bu     Document Templates


.h1 Introduction

.h2 What is this document about?

The ReportLab toolkit aims to be a new generation of reporting tool, with no limits to formatting.  We expect it to grow large, so have divided it into a number of subpackages with clear layers of functionality.

The most basic layer for mist users is the pdfgen subpackage.  This exposes a Canvas object with methods which correspond almost one-to-one with the page marking operators in the PDF file format.  This means that programmers can write code to do anything they want, and build up their own layers of reusable code on top.  However, many common requirements are not met by PDF directly - it does not do anything to help you wrap paragraphs or move down the page.  

PLATYPUS stands for "Page Layout and Typography Using Scripts".  It is the "next layer up" and deals with frames on pages, and "flowable objects" such as paragraphs, images or tables which can be placed within them.  Users will be able to generate "stories" containing paragraphs and other objects, and write "document templates" to process them, hopefully with very few limitations on the effects thatcan be achieved.

This was always an envisaged part of the package, but has taken a long time to get right.  The first try was done in July 1999 for the Open Source conference (in fact, it was needed to make the presentation slides), but was simply too hideous to release.  A second try was put together in November 1999 and progressively refined, appearing when PDFgen was renamed to ReportLab.  This made it possible to produce very basic flowing documents, but lacked many key features and still wasn't general enough.  

We have been discussing better implementations for several months and now think we have something promising.  <NAME> has just checked a trial implementation into CVS, and we have used it to create this document.  Our main goals in May and June are to get feedback on and solidify this API, and to use it to produce our own documentation.  We therefore invite comment from the ReportLab and Python community.   The content of this document will be updated as comments are received, and the formatting will hopefully get prettier as we learn how to make nice DocTemplates!

Please pay close attention - you will have to live with this for some time, and this I your chance to make sure it is clean and usable!


.h2 Use Cases

PLATYPUS should facilitate at least the following kinds of applications:
.bu     Text Wrapping:  you are writing code to create a form, but this needs a few paragraphs of justified, wrapped text.  You don't want to do the wrapping yourself!
.bu    Word-Processor style reports which automatically flow from one page to the next, with page headers and footers which can be related to the story content (e.g. page numbers, chapter headings in titles)
.bu    Database style reports where a large data set is divided into groups with group headers and footers, each occupying a rectangular region on the page, and which flow from page to page
.bu    Tables and box constructions in which each cell can contain flowing text, images and other graphic objects

Specifically, we would like to be able to build a book, or the Python Library Reference, automatically from source.

PLATYPUS is NOT designed for
.bu    Precision typesetting by hand - if you are hand-editing a document and want PDF, use something else (Word, FrameMaker, TeX) and distill the output to PDF instead.  ReportLab is about reports, where you want to automatically generate the same kind of document many times.

.bu   Mathematical typography.  While people no doubt could create some nice equation widgets in our framework, TEX will probably never be exceeded in this regard.

.bu     Quick and dirty database reports on Windows machines which go straight to a printer - Access, Crystal et al. handle this well already.


 
.h1 Frames and Flowables

There are several concepts in PLATYPUS and we will go a stage at a time.  We presume familiarity with the use of PDFgen package to create basic documents.  The stage we will save until last is how to create a flowing multi-page document from some data source.  The simplest thing you can do with PLATYPUS is to
place a frame on a page, add some content to it, and tell it to draw.  Let's
do this.  Here's some code which would create a frame and add two paragraphs to it:

.beginPre Code 
    canvas = Canvas('figure1.pdf')

    frame = Frame(
                    2*inch,     # x
                    4*inch,     # y at bottom
                    4*inch,     # width
                    5*inch     # height
                    )
    frame.showBoundary = 1  # helps us see what's going on

    bodyStyle = ParagraphStyle('Body',
                       fontName='Times-Roman',
                       fontSize=24,
                       leading=28,
                       spaceBefore=6)

    para1 = Paragraph('Spam spam spam spam. ' * 5, bodyStyle)
    para2 = Paragraph('Eggs eggs eggs. ' * 5, bodyStyle)

    mydata = [para1, para2]

    frame.addFromList(mydata,canvas)
    canvas.save()
.endPre

This will result in a page like the one below:

.custom platdemos PlatPropFigure1

Frame has an <i>addFromList</i> method to display content.  It consumes objects from the front of the list until no more can fit in its client area or until the list is empty.

Note that there is a certain amount of space between the frame and the paragraphs within it.  Frames have four 'padding' attributes which can control this. The attributes can be seen below in the method signature for initializing a frame.


.beginPre Code
class Frame:
	'''Abstraction for 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)
	'''
	def __init__(self, x1, y1, width,height, leftPadding=6, bottomPadding=6,
			rightPadding=6, topPadding=6, id=None, showBoundary=0):

.endPre

The Padding attributes are formally unnecessary - you could just make the frame smaller - but are expected to make life easy when working with frames inside tables.  showBoundary is a debugging tool to draw a rectangle to show where the frame is.  <i>(Should the padding be zero by default to reduce surprises?)</i>


.h2 Flowable objects and frame packing

The things which go into frames must conform to the "Flowable" interface.  A conversation takes place between a Flowable object and a frame involving a number of steps.  First the packer calls flowable.wrap(availWidth, availheight) to say how much space is available.  The Flowable should reply with the width and height it wants.  If this fits, fine; it is told to draw itself in the right place.  If not, the packer will ask it to split (again with the same arguments). A dumb flowable will return an empty list indicating that it cannot split.  A smarter one such as a paragraph or a table might split itself into a number of separate flowables, the first of which should fit into the remaining space.

Users may want to implement new Flowable Objects.  For this reason we will provide some high level base classes - for example, a BaseFigure class which draws a blank rectangle and a numbered caption underneath; this could be subclassed and one Draw method added to produce a new kind of graphic.

A new flowable should implement the methods wrap(), split() and draw().  Here's a class called XBox which just draws a box of a given height and width with diagonal lines through it, centred within the frame, and some text in the centre:

.beginPre Code

    class Box(Flowable):
        def __init__(self, width, height, text):
                """Size is specified up front so we just keep it handy"""
                self.width = width
                self.height = height
                self.text = text

	def wrap(self, availWidth, availHeight):
		"""This will be called by the enclosing frame before objects
		are asked their size, drawn or whatever.  It returns the
		size actually used."""
                # we know how far across to draw it later
                self.dx = 0.5* (availWidth - self.width)
		return (self.width, self.height)

	def split(self, availWidth, availheight):
		"""This will be called by more sophisticated frames when
		wrap fails. Stupid flowables should return []. Clever flowables
		should split themselves and return a list of flowables"""
		return []

	def draw(self):
                self.canv.translate(self.dx, 0)
		self.canv.rect(0, 0, self.width, self.height)
		self.canv.line(0, 0, self.width, self.height)
		self.canv.line(0, self.height, self.width, 0)

		#centre the text
		self.canv.setFont('Times-Roman',12)
		self.canv.drawCentredString(0.5*self.width, 0.5*self.height, self.text)

.endPre

The base Flowable class tried to make drawing easy - it saves and restores the canvas around whatever you do in draw(), so preceding objects cannot affect the way it is drawn, and translates the canvas so that (0,0) is at the lower left of the region the Flowable has requested.

Flowables also have two methods called getSpaceBefore and getSpaceAfter
which try to obtain values for spaceBefore and spaceAfter, first from the object directly
and if that fails then the object's style attribute (if any) is consulted. If all else
fails a default value of zero is assumed.
These attributes do not belong to the object itself and it should not make use of them in any
wrap, split or draw methods.
SpaceBefore will be added before all objects expect the first in a frame; spaceAfter
is added after all objects except the last in a frame.
Paragraph styles make heavy use of spaceBefore and spaceAfter,
getting the information from their associated style objects.

.beginPre Code

    class Dimbo(Flowable):
        def __init__(self, width, height, text):
            """Size is specified up front so we just keep it handy"""
            self.width = width
            self.height = height
            self.text = text
            self.spaceBefore = 72

        def wrap(self,avavailWidth,havailHeight):
            ''' doesn't make use of spaceBefore or spaceAfter'''
            return (self.width, self.height)

    ------

    d = Dimbo(144,144,'A bit of text')
    s = d.getSpaceBefore()   # is 72
    s = d.getSpaceAfter()    # is 0

.endPre

.h2 Flowable objects provided "out of the box"

Here are the things in the package at the moment, or which we plan to add before a release.
.df <b>Paragraph</b> - you know what these are
.df <b>Preformatted</b> - this is for printing code such as the sample in this document.  In Preformatted text there are no font variations and the existing line breaks are used.  A Preformatted object is initialized just like a paragraph, but only uses some of the attributes of the ParagraphStyle.
.df <b>Image</b> - this takes an image file name and an optional width and height.  The latter will be taken from the image file at a scale of 1 point per pixel if no height and width are given.  The image is centred within the frame.
.df <b>Figure</b> - a base class for a rectangular drawing with a caption; people can easily override this to add new ones
.df <b>Spacer</b> - this takes a width and height and serves just to take up some space within the frame.  <i>Note that the width is spurious at the moment.  It is there in the vague hope that one day our packing algorithm starts working in two dimensions - we did not want to be accused of x-ism, even though x-ists we currently are.</i>
{'fraction_non_alphanumeric': 0.05865932926586115, 'fraction_numerical': 0.01359811779718467, 'mean_word_length': 2.950535601764335, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1843345', 'n_tokens_mistral': 6785, 'n_tokens_neox': 6303, 'n_words': 3656}