ReportLab Paragraphs Reloaded-EuroPython 2008

43
ReportLab Paragraphs Reloaded Dinu C. Gherman [email protected] EuroPython Conference 2008-07-07, Vilnius

description

A presentation about alternative implementations for ReportLab paragraphs given at EuroPython 2008.

Transcript of ReportLab Paragraphs Reloaded-EuroPython 2008

Page 1: ReportLab Paragraphs Reloaded-EuroPython 2008

ReportLab Paragraphs Reloaded

Dinu C. [email protected]

EuroPython Conference 2008-07-07, Vilnius

Page 2: ReportLab Paragraphs Reloaded-EuroPython 2008

Agenda

• ReportLab Overview

• Paragraphs

• Paragraphs Reloaded & Examples

• Summary

Page 3: ReportLab Paragraphs Reloaded-EuroPython 2008

ReportLab Overview

Page 4: ReportLab Paragraphs Reloaded-EuroPython 2008

General Features

• Generating PDF with Python (+ some C)

• Content: fonts, graphics, images, tables

• PDF features: page transitions, hyperlinks, outline, …

• Graphics import as bitmaps and SVG (ext.)

• Graphics export as bitmaps, PDF, EPS, SVG

Page 5: ReportLab Paragraphs Reloaded-EuroPython 2008

Technologies

• PDF

• Type1 and TrueType fonts

• PIL

• libart

• Unicode (UTF-8)

Page 6: ReportLab Paragraphs Reloaded-EuroPython 2008

Meta Info

• Developped since ca. 1996

• By Andy Robinson, Robin Becker, et al.

• Current: SVN (v. 2.1 + 12 months)

• Open Source

• Closed Source: RML, PDF encryption, PDF import

Page 7: ReportLab Paragraphs Reloaded-EuroPython 2008

1st Layer: Canvas

• Corresponds to a single page with

• Text,

• Graphics,

• PDF features (hyperlinks, page transitions...)

• Can also be saved as a bitmap, EPS, SVG

Page 8: ReportLab Paragraphs Reloaded-EuroPython 2008

Example 1#!/usr/bin/env python# _*_ coding: UTF-8 _*_

from reportlab.pdfgen.canvas import Canvasfrom reportlab.lib.pagesizes import A4

canv = Canvas("helloworld.pdf", pagesize=A4)

### ...

canv.showPage() canv.save()

Page 9: ReportLab Paragraphs Reloaded-EuroPython 2008

Hello world!

###

from reportlab.lib import colorsfrom reportlab.lib.units import cm

# circlecanv.setStrokeColor(colors.green)canv.setFillColor(colors.red)canv.setLineWidth(0.5*cm)x, y = 10*cm, 10*cmcanv.circle(x, y, 5*cm, stroke=True, fill=True)

# textcanv.setFillColor(colors.yellow)canv.setFont("Helvetica", 36)canv.drawCentredString(x, y, u"Hello world!")

Page 10: ReportLab Paragraphs Reloaded-EuroPython 2008

2nd Layer: Platypus

• ”Page LAyout TYPography Using Scripts“

• Frames

• PageTemplate

• DocumentTemplate

• Content: Flowables & Styles (”Story“)

• Rendered as PDF document

Page 11: ReportLab Paragraphs Reloaded-EuroPython 2008

Structure

DocumentTemplate

PageTemplate

Frame

Flow.

Flow.

Frame

Frame

Flow.

Flow.

PageTemplateFrame

Flow.

Flow.

Flow.

Flow.Frame

Flow.

Flow.

Flow.

Flow.

Flow.

Page 12: ReportLab Paragraphs Reloaded-EuroPython 2008

• Content related: Paragraph, Preformatted, Image, Table, XBox, Spacer, …

• Logic related: FrameBreak, PageBreak, CondPageBreak, …

• Layout related: KeepWithNext, KeepInFrame, …

Flowables

Page 13: ReportLab Paragraphs Reloaded-EuroPython 2008

Example 2from reportlab.lib.units import cmfrom reportlab.lib.pagesizes import A4from reportlab.pdfgen.canvas import Canvasfrom reportlab.platypus import \ BaseDocTemplate, PageTemplate, \ Frame, Paragraphfrom reportlab.lib.styles import \ getSampleStyleSheet

from django.contrib.webdesign import \ lorem_ipsum as li

PAGESIZE = A4…

Page 14: ReportLab Paragraphs Reloaded-EuroPython 2008

class TwoColumnDocTemplate(BaseDocTemplate): "A simple two-column document template." def __init__(self, filename, **kw): m = margin = 2*cm tm = topMargin = 2*cm bm = bottomMargin = 2*cm cw, ch = (PAGESIZE[0]-2*m)/2., (PAGESIZE[1]-tm) leftCol = Frame(m, bm, cw-0.75*cm, ch-tm, leftPadding=0, topPadding=0, rightPadding=0, bottomPadding=0, id="left", showBoundary=True ) rightCol = Frame(cw+2.7*cm, bm, cw-0.75*cm, ch-tm, leftPadding=0, topPadding=0, rightPadding=0, bottomPadding=0, id="right", showBoundary=True ) apply(BaseDocTemplate.__init__, (self, filename), kw) allPages = PageTemplate("all", [leftCol, rightCol]) self.addPageTemplates([allPages])

Page 15: ReportLab Paragraphs Reloaded-EuroPython 2008

def generateTwoColDocument(outPath): "Create a 2-col doc with rand. text." stylesheet = getSampleStyleSheet() h1 = stylesheet['Heading1'] h2 = stylesheet['Heading2'] bt = stylesheet['BodyText']

st = story = [] p = li.words(3, common=False) p = p.capitalize() st.append(Paragraph(p, style=h1)) for i in range(10): p = li.words(5, common=False) p = p.capitalize() st.append(Paragraph(p, style=h2)) for j in range(3): p = li.paragraph() st.append(Paragraph(p, style=bt))

doc = TwoColumnDocTemplate(outPath, pagesize=PAGESIZE) doc.build(story)

Maiores cupiditate placeatDolorem perferendis accusantiumveniam optioQuidem explicabo officiis quasi soluta exercitationemblanditiis ullam nobis suscipit praesentium? Nostrumquaerat ullam recusandae esse nulla maiores eoseligendi adipisci consequatur dolores, sequi cumveniam dolor voluptates ipsa tempora, quibusdamnostrum quasi labore nobis perspiciatis illo eaque veropariatur quos? Ipsum odio ipsa quidem amet impeditrecusandae quis, corporis cupiditate laboriosam aliasratione tempore? Exercitationem voluptates aliasaspernatur odio laboriosam dolores unde vero.

Blanditiis quo at consectetur impedit, iure fugiat sedvoluptate nesciunt temporibus facilis, dignissimosaspernatur maiores assumenda nostrum, error nisiaccusamus officiis quo deleniti facere recusandaeanimi quidem ea excepturi, recusandae quasi dolornulla molestiae unde repellendus odit sint non officiaomnis.

Cupiditate minima pariatur itaque alias suscipit, nullaculpa atque deleniti id et tenetur aliquid unde qui velsint, cumque rerum maiores voluptate, quas et quia?Quam debitis praesentium vitae, nesciunt quasdistinctio quisquam, impedit tempora architecto aut?Perspiciatis et ullam sit commodi cumque optiopariatur, eum totam minus et quis, vero itaquemolestias eius sed eligendi veniam quae doloredoloribus, unde rem odit similique sapiente eveniet?

Cumque blanditiis ut facilis quasiAut omnis atque molestiae est inventore delectusipsam a commodi, tempora ratione unde quia nonpariatur nobis dolores, quisquam corporis iste quaeratillum atque ea sint doloribus iusto nemo, amet quidemnesciunt dolor commodi accusantium cumque ipsum,praesentium illum pariatur? Dicta nobis culpa placeattenetur repellat distinctio pariatur debitis praesentium,exercitationem repudiandae dolore et fugiat neque?Doloremque et non architecto quas, laudantiumrepellat a deserunt minima explicabo beatae possimus,facilis fugit similique ad porro sapiente suscipit oditalias quod qui excepturi, autem provident fugiatpariatur ullam perferendis ipsa fugit. Minus fugiat sintconsequatur delectus necessitatibus eius eum.

Voluptate sit ratione nobis aperiam omnis fugitaccusantium totam sequi accusamus, nulla perspiciatisut corporis eos possimus, ducimus id veniam earum,molestias ipsa dolorum aut eligendi doloremquelaudantium atque aliquam voluptatibus hic esse,explicabo amet assumenda eaque autem molestiaeexercitationem fugiat quasi sequi? Facilis tenetur quieveniet obcaecati saepe porro repellat doloremquemagni, dolor assumenda error recusandae rem

explicabo distinctio animi ad ipsam veniam enim,fugit quis recusandae amet eveniet praesentiumveniam obcaecati consectetur maxime delectus?

Harum molestias iure at nemo labore quod repellatquas ducimus eum similique, incidunt facilis itaqueobcaecati iusto fugiat, enim unde voluptas illo? Enimeaque repudiandae cupiditate ad accusantium, aliaserror tempore ducimus a dicta aut voluptates vitae,error cupiditate assumenda quia culpa id illum rerumipsam ipsum, quisquam blanditiis corporis fugiatexcepturi facilis quaerat exercitationem cum temporevoluptas repudiandae? Eos hic reiciendis excepturirecusandae ad esse.

Excepturi sed repellendus facereullamIpsum omnis deleniti saepe sapiente, quibusdamneque aspernatur delectus quasi iusto a facilis, teneturnostrum incidunt nesciunt veritatis, velit sunt nostrumquo quae necessitatibus? Delectus suscipit solutaveniam harum sunt, laudantium iusto voluptatibus,fuga facere nihil sed aspernatur placeat, impedit liberorepellendus laudantium nisi nihil eveniet reiciendis?Exercitationem nemo possimus debitis? Aliquidnesciunt cum natus eveniet obcaecati fugiatrepellendus, et numquam odit velit alias, dolor autquasi ut tempora itaque nemo earum animi, ipsasimilique natus inventore nobis sed quia eaque totamtempore incidunt dolorum, molestias rem blanditiis adautem.

Tempora voluptatibus quo dicta expedita temporeharum porro?

Cupiditate possimus dignissimos laborum consecteturlaudantium, illum ipsa perferendis adipisci veritatisnemo voluptate, assumenda molestias unde aperiamquisquam cumque debitis, ea incidunt odit expeditafacilis, molestiae quam sint doloremque officia nemoexplicabo qui. Libero maxime magnam, doloresquaerat quos nisi dolorem libero eaque at, nobis eaqueomnis labore libero magnam officia. Quis aperiamtempore perferendis asperiores pariatur cum,accusamus error ratione, consectetur minus saepeveritatis harum, similique aliquid exercitationem cumassumenda, natus unde non veniam quas illum enimamet sequi quis molestias.

Mollitia amet voluptatum facereblanditiisEt ullam placeat ut accusamus recusandae quidemtotam veritatis voluptatibus. Officia quae voluptatesnecessitatibus beatae atque laudantium voluptasvoluptate veritatis sequi vero, dolore asperiores atmolestias iusto eum ab necessitatibus, voluptatibusquis minus? Distinctio eos dolore corporis corruptiducimus facilis iste doloribus nisi eligendi.

Page 16: ReportLab Paragraphs Reloaded-EuroPython 2008

3rd Layer: RML

• ”Report Markup Language“

• Platypus in XML + Black Box + ¥$€

• See also: z3c.rml (S. Richter et al. – free)

• PageCatcher (more ¥$€) for PDF import

• See also: one of my next announcements…

Page 17: ReportLab Paragraphs Reloaded-EuroPython 2008

Paragraphs

Page 18: ReportLab Paragraphs Reloaded-EuroPython 2008

Features

• Subclass of the Flowable class

• Core class in Platypus (paragraph.py)

• No alternative (except para.py – R.I.P.)

• Central feature: linebreaks

• Knows style attributes

Page 19: ReportLab Paragraphs Reloaded-EuroPython 2008

Global Style Attributes

• fontName, fontSize, textColor, backColor

• firstLineIndent, leftIndent, rightIndent

• alignment, leading, (spaceBefore, spaceAfter)

• bulletColor, bulletFontName, bulletFontSize, bulletIndent, bulletOffsetY

• Sample: stylesheet = getSampleStyleSheet()bt = stylesheet['BodyText']bt.leading = 16bt.textColor = colors.blue

Page 20: ReportLab Paragraphs Reloaded-EuroPython 2008

Local Style Attributes

• XML tags in paragraph text:

• font, br, b, i, u, strong, strike, a, link, img

• superscript, subscript, greek

• seq, seqDefault, seqReset

• Sample:

t = "Hello <font color='red'>red</font> world!"p = Paragraph(t, style=myStyle)

Page 21: ReportLab Paragraphs Reloaded-EuroPython 2008

Lacking…

• Inline images (new in SVN)

• Hyphenation

• Kerning

• Code comprehensibility

• Code extensibility

”Reporting Solutions“!?Efficiency!?

Page 22: ReportLab Paragraphs Reloaded-EuroPython 2008

Paragraphs Reloaded

Page 23: ReportLab Paragraphs Reloaded-EuroPython 2008

New Approach

• New Flowable!

• MinimalParagraph class

• Easily extensible (shown in subclasses)

• Linebreaks (and not much more)!

• Critical methods: wrap, split, draw

Page 24: ReportLab Paragraphs Reloaded-EuroPython 2008

Sample: XBoxclass XBox(Flowable):

def __init__(self, width, height, text='A Box'): Flowable.__init__(self) self.width, self.height, self.text = width, height, text

def draw(self): canv, w, h = self.canv, self.width, self.height canv.rect(0, 0, w, h) canv.line(0, 0, w, h) canv.line(0, h, w, 0)

canv.setFont('Times-Roman', 12) canv.drawCentredString(0.5*w, 0.5*h, self.text)

# inherited by Flowable:

def wrap(self, availWidth, availHeight): return (self.width, self.height)

def split(self, availWidth, availheight): return []

Page 25: ReportLab Paragraphs Reloaded-EuroPython 2008

• Input text: "Hello world"

• Parsed: [{"text":"Hello"}, {"text":"world"}]

• Word widths: [{"text":"Hello", "width":10}, {text:"world", "width":11}]

• Placement: [{"text":"Hello", "width":10, "pos":(10, 5)}, {"text":"world", "width":11, "pos":(23, 5)}]

• Add attributes as needed

Key Idea: ”Events“

Page 26: ReportLab Paragraphs Reloaded-EuroPython 2008

• Global attributes: fontName, fontSize, leading, firstLineIndent, leftIndent, rightIndent, textColor, (spaceBefore, spaceAfter)

• No alignment (left only), …

• Focus on linebreaks and RL compliance

MinimalParagraph

Page 27: ReportLab Paragraphs Reloaded-EuroPython 2008

Alice was beginning to get very tired of sittingby her sister on the bank, and of havingnothing to do: once or twice she had peepedinto the book her sister was reading, but ithad no pictures or conversations in it, `andwhat is the use of a book,' thought Alice`without pictures or conversation?'

So she was considering in her own mind (aswell as she could, for the hot day made herfeel very sleepy and stupid), whether thepleasure of making a daisy-chain would beworth the trouble of getting up and pickingthe daisies, when suddenly a White Rabbitwith pink eyes ran close by her.

There was nothing so VERY remarkable inthat; nor did Alice think it so VERY much outof the way to hear the Rabbit say to itself,`Oh dear! Oh dear! I shall be late!' (when shethought it over afterwards, it occurred to herthat she ought to have wondered at this, butat the time it all seemed quite natural); butwhen the Rabbit actually TOOK A WATCHOUT OF ITS WAISTCOAT- POCKET, andlooked at it, and then hurried on, Alicestarted to her feet, for it flashed across hermind that she had never before seen a rabbitwith either a waistcoat-pocket, or a watch totake out of it, and burning with curiosity, sheran across the field after it, and fortunatelywas just in time to see it pop down a largerabbit-hole under the hedge.

In another moment down went Alice after it,never once considering how in the world shewas to get out again.

The rabbit-hole went straight on like a tunnelfor some way, and then dipped suddenlydown, so suddenly that Alice had not amoment to think about stopping herselfbefore she found herself falling down a verydeep well.

Either the well was very deep, or she fell veryslowly, for she had plenty of time as shewent down to look about her and to wonderwhat was going to happen next. First, shetried to look down and make out what shewas coming to, but it was too dark to seeanything; then she looked at the sides of the

well, and noticed that they were filled withcupboards and book-shelves; here and thereshe saw maps and pictures hung upon pegs.She took down a jar from one of the shelvesas she passed; it was labelled `ORANGEMARMALADE', but to her greatdisappointment it was empty: she did not liketo drop the jar for fear of killing somebody, somanaged to put it into one of the cupboardsas she fell past it.

`Well!' thought Alice to herself, `after such afall as this, I shall think nothing of tumblingdown stairs! How brave they'll all think me athome! Why, I wouldn't say anything about it,even if I fell off the top of the house!' (Whichwas very likely true.)

Down, down, down. Would the fall NEVERcome to an end! `I wonder how many milesI've fallen by this time?' she said aloud. `Imust be getting somewhere near the centreof the earth. Let me see: that would be fourthousand miles down, I think--' (for, you see,Alice had learnt several things of this sort inher lessons in the schoolroom, and thoughthis was not a VERY good opportunity forshowing off her knowledge, as there was noone to listen to her, still it was good practiceto say it over) `--yes, that's about the rightdistance--but then I wonder what Latitude orLongitude I've got to?' (Alice had no ideawhat Latitude was, or Longitude either, butthought they were nice grand words to say.)

Presently she began again. `I wonder if Ishall fall right THROUGH the earth! Howfunny it'll seem to come out among thepeople that walk with their heads downward!The Antipathies, I think--' (she was ratherglad there WAS no one listening, this time,as it didn't sound at all the right word) `--but Ishall have to ask them what the name of thecountry is, you know. Please, Ma'am, is thisNew Zealand or Australia?' (and she tried tocurtsey as she spoke--fancy CURTSEYINGas you're falling through the air! Do you thinkyou could manage it?) `And what an ignorantlittle girl she'll think me for asking! No, it'llnever do to ask: perhaps I shall see it writtenup somewhere.'

MinimalParagraph

Page 28: ReportLab Paragraphs Reloaded-EuroPython 2008

ColouredParagraph#!/bin/env/python# -*- coding: utf-8 -*-

"An example for a coloured paragraph \subclass."

import random

from reportlab.lib import colors

from minimalparagraph import \ MinimalParagraph

RL_COLORS = [v for (k, v) in \ colors.__dict__.items() \ if isinstance(v, colors.Color)]

class ColouredParagraph(MinimalParagraph): "A colourful tiny subclass \ of MinimalParagraph."

def draw(self): "Render words in paragraph on randomly \ coloured background."

if not self.words: return canvas = self.canv style = self.style

canvas.saveState()

canvas.setFont(style.fontName, style.fontSize)

for word in self.words: if "meta" in word or not "pos" in word: continue text, (x, y) = word["text"], word["pos"] col = random.choice(RL_COLORS) canvas.setStrokeColor(col) canvas.setFillColor(col) canvas.rect(x, y - self.dy, word["width"], style.fontSize, fill=True, stroke=True) canvas.setFillColor(style.textColor) canvas.drawString(x, y - self.dy, text)

canvas.restoreState()

Page 29: ReportLab Paragraphs Reloaded-EuroPython 2008

!"#$% &'( )# *+(, -+( .+(,/ 0 $#")-123 4")5-1- 467*- .-8+" (# 8-( 1+(5-1 ,6--9:/ +")$-"( #" ,+:7"8 (# 5-1,-6;/ 7" + )1-+<: ,#1( #;$+:/ =># *+(, -+( .+(,2 ># *+(, -+( .+(,23 +"),#<-(7<-,/ =># .+(, -+( *+(,23 ;#1/ :#' ,--/ +,,5- *#'6)"3( +",$-1 -7(5-1 ?'-,(7#"/ 7( )7)"3(<'*5 <+((-1 $57*5 $+: ,5- 9'( 7(% @5- ;-6((5+( ,5- $+, )#A7"8 #;;/ +") 5+) B',( .-8'" (#)1-+< (5+( ,5- $+, $+6!7"8 5+") 7" 5+") $7(5>7"+5/ +") ,+:7"8 (# 5-1 C-1: -+1"-,(6:/ =D#$/>7"+5/ (-66 <- (5- (1'(5E )7) :#' -C-1 -+( +.+(23 $5-" ,'))-"6:/ (5'<9F (5'<9F )#$",5- *+<- '9#" + 5-+9 #; ,(7*!, +") )1: 6-+C-,/+") (5- ;+66 $+, #C-1%

467*- $+, "#( + .7( 5'1(/ +") ,5- B'<9-) '9 #"(# 5-1 ;--( 7" + <#<-"(E ,5- 6##!-) '9/ .'( 7($+, +66 )+1! #C-15-+)G .-;#1- 5-1 $+, +"#(5-16#"8 9+,,+8-/ +") (5- H57(- I+..7( $+, ,(766 7",785(/ 5'11:7"8 )#$" 7(% J5-1- $+, "#( +<#<-"( (# .- 6#,(E +$+: $-"( 467*- 67!- (5-$7")/ +") $+, B',( 7" (7<- (# 5-+1 7( ,+:/ +, 7(('1"-) + *#1"-1/ =K5 <: -+1, +") $57,!-1,/5#$ 6+(- 7(3, 8-((7"8F3 @5- $+, *6#,- .-57") 7($5-" ,5- ('1"-) (5- *#1"-1/ .'( (5- I+..7($+, "# 6#"8-1 (# .- ,--"E ,5- ;#'") 5-1,-6; 7"+ 6#"8/ 6#$ 5+66/ $57*5 $+, 67( '9 .: + 1#$ #;6+<9, 5+"87"8 ;1#< (5- 1##;%

J5-1- $-1- )##1, +66 1#'") (5- 5+66/ .'( (5-:$-1- +66 6#*!-)G +") $5-" 467*- 5+) .--" +66(5- $+: )#$" #"- ,7)- +") '9 (5- #(5-1/(1:7"8 -C-1: )##1/ ,5- $+6!-) ,+)6: )#$" (5-<7))6-/ $#")-17"8 5#$ ,5- $+, -C-1 (# 8-(#'( +8+7"%

@'))-"6: ,5- *+<- '9#" + 67((6- (51--L6-88-)(+.6-/ +66 <+)- #; ,#67) 86+,,G (5-1- $+,"#(57"8 #" 7( -M*-9( + (7": 8#6)-" !-:/ +")467*-3, ;71,( (5#'85( $+, (5+( 7( <785( .-6#"8(# #"- #; (5- )##1, #; (5- 5+66G .'(/ +6+,F -7(5-1(5- 6#*!, $-1- (## 6+18-/ #1 (5- !-: $+, (##,<+66/ .'( +( +": 1+(- 7( $#'6) "#( #9-" +": #;(5-<% N#$-C-1/ #" (5- ,-*#") (7<- 1#'")/,5- *+<- '9#" + 6#$ *'1(+7" ,5- 5+) "#("#(7*-) .-;#1-/ +") .-57") 7( $+, + 67((6- )##1+.#'( ;7;(--" 7"*5-, 5785E ,5- (17-) (5- 67((6-8#6)-" !-: 7" (5- 6#*!/ +") (# 5-1 81-+()-6785( 7( ;7((-)F

467*- #9-"-) (5- )##1 +") ;#'") (5+( 7( 6-)7"(# + ,<+66 9+,,+8-/ "#( <'*5 6+18-1 (5+" +

1+(L5#6-E ,5- !"-6( )#$" +") 6##!-) +6#"8 (5-9+,,+8- 7"(# (5- 6#C-67-,( 8+1)-" :#' -C-1,+$% N#$ ,5- 6#"8-) (# 8-( #'( #; (5+( )+1!5+66/ +") $+")-1 +.#'( +<#"8 (5#,- .-), #;.1785( ;6#$-1, +") (5#,- *##6 ;#'"(+7",/ .'(,5- *#'6) "#( -C-" 8-( 5-1 5-+) (51#'85 (5-)##1$+:G =+") -C-" 7; <: 5-+) $#'6) 8#(51#'85/3 (5#'85( 9##1 467*-/ =7( $#'6) .- #;C-1: 67((6- ',- $7(5#'( <: ,5#'6)-1,% K5/ 5#$ 0$7,5 0 *#'6) ,5'( '9 67!- + (-6-,*#9-F 0 (57"! 0*#'6)/ 7; 0 #"6: !"#$ 5#$ (# .-87"%3 O#1/ :#',--/ ,# <+": #'(L#;L(5-L$+: (57"8, 5+)5+99-"-) 6+(-6:/ (5+( 467*- 5+) .-8'" (# (57"!(5+( C-1: ;-$ (57"8, 7")--) $-1- 1-+66:7<9#,,7.6-%

J5-1- ,--<-) (# .- "# ',- 7" $+7(7"8 .: (5-67((6- )##1/ ,# ,5- $-"( .+*! (# (5- (+.6-/ 5+6;5#97"8 ,5- <785( ;7") +"#(5-1 !-: #" 7(/ #1 +(+": 1+(- + .##! #; 1'6-, ;#1 ,5'((7"8 9-#96- '967!- (-6-,*#9-,E (57, (7<- ,5- ;#'") + 67((6-.#((6- #" 7(/ P=$57*5 *-1(+7"6: $+, "#( 5-1-.-;#1-/3 ,+7) 467*-/Q +") 1#'") (5- "-*! #; (5-.#((6- $+, + 9+9-1 6+.-6/ $7(5 (5- $#1),=>I0DR ST3 .-+'(7;'66: 917"(-) #" 7( 7" 6+18-6-((-1,%

0( $+, +66 C-1: $-66 (# ,+: =>17"! <-/3 .'( (5-$7,- 67((6- 467*- $+, "#( 8#7"8 (# )# JN4J 7" +5'11:% =D#/ 0366 6##! ;71,(/3 ,5- ,+7)/ =+") ,--$5-(5-1 7(3, <+1!-) U9#7,#"U #1 "#(3G ;#1 ,5-5+) 1-+) ,-C-1+6 "7*- 67((6- 57,(#17-, +.#'(*576)1-" $5# 5+) 8#( .'1"(/ +") -+(-" '9 .:$76) .-+,(, +") #(5-1 '"96-+,+"( (57"8,/ +66.-*+',- (5-: HKVW> "#( 1-<-<.-1 (5-,7<96- 1'6-, (5-71 ;17-"), 5+) (+'85( (5-<E,'*5 +,/ (5+( + 1-)L5#( 9#!-1 $766 .'1" :#' 7;:#' 5#6) 7( (## 6#"8G +") (5+( 7; :#' *'( :#'1;7"8-1 XTIY )--96: $7(5 + !"7;-/ 7( ','+66:.6--),G +") ,5- 5+) "-C-1 ;#18#((-" (5+(/ 7;:#' )17"! <'*5 ;1#< + .#((6- <+1!-) =9#7,#"/37( 7, +6<#,( *-1(+7" (# )7,+81-- $7(5 :#'/,##"-1 #1 6+(-1%

N#$-C-1/ (57, .#((6- $+, DKJ <+1!-)=9#7,#"/3 ,# 467*- C-"('1-) (# (+,(- 7(/ +");7")7"8 7( C-1: "7*-/ P7( 5+)/ 7" ;+*(/ + ,#1( #;<7M-) ;6+C#'1 #; *5-11:L(+1(/ *',(+1)/97"-L+996-/ 1#+,( ('1!-:/ (#;;--/ +") 5#(.'((-1-) (#+,(/Q ,5- C-1: ,##" ;7"7,5-) 7( #;;%

Z Z Z Z Z Z Z

ColouredParagraph

Page 30: ReportLab Paragraphs Reloaded-EuroPython 2008

!"#$% &'( )# *+(, -+( .+(,/ 0 $#")-123 4")5-1- 467*- .-8+" (# 8-( 1+(5-1 ,6--9:/ +")$-"( #" ,+:7"8 (# 5-1,-6;/ 7" + )1-+<: ,#1( #;$+:/ =># *+(, -+( .+(,2 ># *+(, -+( .+(,23 +"),#<-(7<-,/ =># .+(, -+( *+(,23 ;#1/ :#' ,--/ +,,5- *#'6)"3( +",$-1 -7(5-1 ?'-,(7#"/ 7( )7)"3(<'*5 <+((-1 $57*5 $+: ,5- 9'( 7(% @5- ;-6((5+( ,5- $+, )#A7"8 #;;/ +") 5+) B',( .-8'" (#)1-+< (5+( ,5- $+, $+6!7"8 5+") 7" 5+") $7(5>7"+5/ +") ,+:7"8 (# 5-1 C-1: -+1"-,(6:/ =D#$/>7"+5/ (-66 <- (5- (1'(5E )7) :#' -C-1 -+( +.+(23 $5-" ,'))-"6:/ (5'<9F (5'<9F )#$",5- *+<- '9#" + 5-+9 #; ,(7*!, +") )1: 6-+C-,/+") (5- ;+66 $+, #C-1%

467*- $+, "#( + .7( 5'1(/ +") ,5- B'<9-) '9 #"(# 5-1 ;--( 7" + <#<-"(E ,5- 6##!-) '9/ .'( 7($+, +66 )+1! #C-15-+)G .-;#1- 5-1 $+, +"#(5-16#"8 9+,,+8-/ +") (5- H57(- I+..7( $+, ,(766 7",785(/ 5'11:7"8 )#$" 7(% J5-1- $+, "#( +<#<-"( (# .- 6#,(E +$+: $-"( 467*- 67!- (5-$7")/ +") $+, B',( 7" (7<- (# 5-+1 7( ,+:/ +, 7(('1"-) + *#1"-1/ =K5 <: -+1, +") $57,!-1,/5#$ 6+(- 7(3, 8-((7"8F3 @5- $+, *6#,- .-57") 7($5-" ,5- ('1"-) (5- *#1"-1/ .'( (5- I+..7($+, "# 6#"8-1 (# .- ,--"E ,5- ;#'") 5-1,-6; 7"+ 6#"8/ 6#$ 5+66/ $57*5 $+, 67( '9 .: + 1#$ #;6+<9, 5+"87"8 ;1#< (5- 1##;%

J5-1- $-1- )##1, +66 1#'") (5- 5+66/ .'( (5-:$-1- +66 6#*!-)G +") $5-" 467*- 5+) .--" +66(5- $+: )#$" #"- ,7)- +") '9 (5- #(5-1/(1:7"8 -C-1: )##1/ ,5- $+6!-) ,+)6: )#$" (5-<7))6-/ $#")-17"8 5#$ ,5- $+, -C-1 (# 8-(#'( +8+7"%

@'))-"6: ,5- *+<- '9#" + 67((6- (51--L6-88-)(+.6-/ +66 <+)- #; ,#67) 86+,,G (5-1- $+,"#(57"8 #" 7( -M*-9( + (7": 8#6)-" !-:/ +")467*-3, ;71,( (5#'85( $+, (5+( 7( <785( .-6#"8(# #"- #; (5- )##1, #; (5- 5+66G .'(/ +6+,F -7(5-1(5- 6#*!, $-1- (## 6+18-/ #1 (5- !-: $+, (##,<+66/ .'( +( +": 1+(- 7( $#'6) "#( #9-" +": #;(5-<% N#$-C-1/ #" (5- ,-*#") (7<- 1#'")/,5- *+<- '9#" + 6#$ *'1(+7" ,5- 5+) "#("#(7*-) .-;#1-/ +") .-57") 7( $+, + 67((6- )##1+.#'( ;7;(--" 7"*5-, 5785E ,5- (17-) (5- 67((6-8#6)-" !-: 7" (5- 6#*!/ +") (# 5-1 81-+()-6785( 7( ;7((-)F

467*- #9-"-) (5- )##1 +") ;#'") (5+( 7( 6-)7"(# + ,<+66 9+,,+8-/ "#( <'*5 6+18-1 (5+" +

1+(L5#6-E ,5- !"-6( )#$" +") 6##!-) +6#"8 (5-9+,,+8- 7"(# (5- 6#C-67-,( 8+1)-" :#' -C-1,+$% N#$ ,5- 6#"8-) (# 8-( #'( #; (5+( )+1!5+66/ +") $+")-1 +.#'( +<#"8 (5#,- .-), #;.1785( ;6#$-1, +") (5#,- *##6 ;#'"(+7",/ .'(,5- *#'6) "#( -C-" 8-( 5-1 5-+) (51#'85 (5-)##1$+:G =+") -C-" 7; <: 5-+) $#'6) 8#(51#'85/3 (5#'85( 9##1 467*-/ =7( $#'6) .- #;C-1: 67((6- ',- $7(5#'( <: ,5#'6)-1,% K5/ 5#$ 0$7,5 0 *#'6) ,5'( '9 67!- + (-6-,*#9-F 0 (57"! 0*#'6)/ 7; 0 #"6: !"#$ 5#$ (# .-87"%3 O#1/ :#',--/ ,# <+": #'(L#;L(5-L$+: (57"8, 5+)5+99-"-) 6+(-6:/ (5+( 467*- 5+) .-8'" (# (57"!(5+( C-1: ;-$ (57"8, 7")--) $-1- 1-+66:7<9#,,7.6-%

J5-1- ,--<-) (# .- "# ',- 7" $+7(7"8 .: (5-67((6- )##1/ ,# ,5- $-"( .+*! (# (5- (+.6-/ 5+6;5#97"8 ,5- <785( ;7") +"#(5-1 !-: #" 7(/ #1 +(+": 1+(- + .##! #; 1'6-, ;#1 ,5'((7"8 9-#96- '967!- (-6-,*#9-,E (57, (7<- ,5- ;#'") + 67((6-.#((6- #" 7(/ P=$57*5 *-1(+7"6: $+, "#( 5-1-.-;#1-/3 ,+7) 467*-/Q +") 1#'") (5- "-*! #; (5-.#((6- $+, + 9+9-1 6+.-6/ $7(5 (5- $#1),=>I0DR ST3 .-+'(7;'66: 917"(-) #" 7( 7" 6+18-6-((-1,%

0( $+, +66 C-1: $-66 (# ,+: =>17"! <-/3 .'( (5-$7,- 67((6- 467*- $+, "#( 8#7"8 (# )# JN4J 7" +5'11:% =D#/ 0366 6##! ;71,(/3 ,5- ,+7)/ =+") ,--$5-(5-1 7(3, <+1!-) U9#7,#"U #1 "#(3G ;#1 ,5-5+) 1-+) ,-C-1+6 "7*- 67((6- 57,(#17-, +.#'(*576)1-" $5# 5+) 8#( .'1"(/ +") -+(-" '9 .:$76) .-+,(, +") #(5-1 '"96-+,+"( (57"8,/ +66.-*+',- (5-: HKVW> "#( 1-<-<.-1 (5-,7<96- 1'6-, (5-71 ;17-"), 5+) (+'85( (5-<E,'*5 +,/ (5+( + 1-)L5#( 9#!-1 $766 .'1" :#' 7;:#' 5#6) 7( (## 6#"8G +") (5+( 7; :#' *'( :#'1;7"8-1 XTIY )--96: $7(5 + !"7;-/ 7( ','+66:.6--),G +") ,5- 5+) "-C-1 ;#18#((-" (5+(/ 7;:#' )17"! <'*5 ;1#< + .#((6- <+1!-) =9#7,#"/37( 7, +6<#,( *-1(+7" (# )7,+81-- $7(5 :#'/,##"-1 #1 6+(-1%

N#$-C-1/ (57, .#((6- $+, DKJ <+1!-)=9#7,#"/3 ,# 467*- C-"('1-) (# (+,(- 7(/ +");7")7"8 7( C-1: "7*-/ P7( 5+)/ 7" ;+*(/ + ,#1( #;<7M-) ;6+C#'1 #; *5-11:L(+1(/ *',(+1)/97"-L+996-/ 1#+,( ('1!-:/ (#;;--/ +") 5#(.'((-1-) (#+,(/Q ,5- C-1: ,##" ;7"7,5-) 7( #;;%

Z Z Z Z Z Z Z

HighlightedParagraph

Page 31: ReportLab Paragraphs Reloaded-EuroPython 2008

!"# $%& '( )*+ ,#*& &% &"# &)-.# &% /#)0'1#"#10#.2 -3 4&5 )*+ 2%'*+ &")&5 )0 *#)1.3 )0 0"#6%'.+ $'#005 0"# ,)0 *%, )-%'& &,% 2##& "4$"5)*+ ,)0 $%4*$ %* 0"14*74*$ 1)(4+.38 0"# 0%%*2%'*+ %'& &")& &"# 6)'0# %2 &"40 ,)0 &"# 2)*0"# ,)0 "%.+4*$5 )*+ 0"# +1%((#+ 4& ")0&4.359'0& 4* &4/# &% ):%4+ 0"14*74*$ ),)3 ).&%$#&";#1<

=>")& ?@! ) *)11%, #06)(#AB 0)4+ @.46#5 ) $%%++#). 214$"&#*#+ )& &"# 0'++#* 6")*$#5 -'&:#13 $.)+ &% 24*+ "#10#.2 0&4.. 4* #C40&#*6#D =)*+*%, 2%1 &"# $)1+#*AB )*+ 0"# 1)* ,4&" )..0(##+ -)67 &% &"# .4&&.# +%%18 -'&5 ).)0A &"#.4&&.# +%%1 ,)0 0"'& )$)4*5 )*+ &"# .4&&.# $%.+#*7#3 ,)0 .34*$ %* &"# $.)00 &)-.# )0 -#2%1#5=)*+ &"4*$0 )1# ,%10# &")* #:#15B &"%'$"& &"#(%%1 6"4.+5 =2%1 E *#:#1 ,)0 0% 0/).. )0 &"40-#2%1#5 *#:#1A @*+ E +#6.)1# 4&B0 &%% -)+5 &")& 4&40AB

@0 0"# 0)4+ &"#0# ,%1+0 "#1 2%%& 0.4((#+5 )*+4* )*%&"#1 /%/#*&5 0(.)0"A 0"# ,)0 '( &% "#16"4* 4* 0).& ,)&#1< F#1 2410& 4+#) ,)0 &")& 0"#")+ 0%/#"%, 2)..#* 4*&% &"# 0#)5 =)*+ 4* &")&6)0# E 6)* $% -)67 -3 1)4.,)35B 0"# 0)4+ &%"#10#.2< [email protected]# ")+ -##* &% &"# 0#)04+# %*6# 4*"#1 .42#5 )*+ ")+ 6%/# &% &"# $#*#1). 6%*6.';04%*5 &")& ,"#1#:#1 3%' $% &% %* &"# H*$.40"6%)0& 3%' 24*+ ) *'/-#1 %2 -)&"4*$ /)6"4*#04* &"# 0#)5 0%/# 6"4.+1#* +4$$4*$ 4* &"# 0)*+,4&" ,%%+#* 0()+#05 &"#* ) 1%, %2 .%+$4*$"%'0#05 )*+ -#"4*+ &"#/ ) 1)4.,)3 0&)&4%*<IF%,#:#15 0"# 0%%* /)+# %'& &")& 0"# ,)0 4*&"# (%%. %2 &#)10 ,"46" 0"# ")+ ,#(& ,"#*0"# ,)0 *4*# 2##& "4$"<

=E ,40" E ")+*B& 614#+ 0% /'6"AB 0)4+ @.46#5 )00"# 0,)/ )-%'&5 &134*$ &% 24*+ "#1 ,)3 %'&< =E0").. -# ('*40"#+ 2%1 4& *%,5 E 0'((%0#5 -3-#4*$ +1%,*#+ 4* /3 %,* &#)10A >")& ?EJJ -#) K'##1 &"4*$5 &% -# 0'1#A F%,#:#15 #:#13&"4*$40 K'##1 &%;+)3<B

L'0& &"#* 0"# "#)1+ 0%/#&"4*$ 0(.)0"4*$)-%'& 4* &"# (%%. ) .4&&.# ,)3 %225 )*+ 0"#0,)/ *#)1#1 &% /)7# %'& ,")& 4& ,)08 )& 2410&0"# &"%'$"& 4& /'0& -# ) ,).1'0 %1 "4((%(%&;)/'05 -'& &"#* 0"# 1#/#/-#1#+ "%, 0/)..0"# ,)0 *%,5 )*+ 0"# 0%%* /)+# %'& &")& 4&,)0 %*.3 ) /%'0# &")& ")+ 0.4((#+ 4* .47#"#10#.2<

=?%'.+ 4& -# %2 )*3 '0#5 *%,5B &"%'$"& @.46#5=&% 0(#)7 &% &"40 /%'0#M H:#13&"4*$ 40 0%%'&;%2;&"#;,)3 +%,* "#1#5 &")& E 0"%'.+ &"4*7:#13 .47#.3 4& 6)* &).78 )& )*3 1)&#5 &"#1#B0 *%")1/ 4* &134*$<B !% 0"# -#$)*8 =N O%'0#5 +%3%' 7*%, &"# ,)3 %'& %2 &"40 (%%.M E )/ :#13&41#+ %2 0,4//4*$ )-%'& "#1#5 N O%'0#[email protected]# &"%'$"& &"40 /'0& -# &"# 14$"& ,)3 %20(#)74*$ &% ) /%'0#8 0"# ")+ *#:#1 +%*#0'6" ) &"4*$ -#2%1#5 -'& 0"# 1#/#/-#1#+"):4*$ 0##* 4* "#1 -1%&"#1B0 J)&4* P1)//)15 =@/%'0#;;%2 ) /%'0#;;&% ) /%'0#;;) /%'0#;;N/%'0#ABI >"# O%'0# .%%7#+ )& "#1 1)&"#1 4*;K'404&4:#.35 )*+ 0##/#+ &% "#1 &% ,4*7 ,4&"%*# %2 4&0 .4&&.# #3#05 -'& 4& 0)4+ *%&"4*$<

=Q#1")(0 4& +%#0*B& '*+#10&)*+ H*$.40"5B&"%'$"& @.46#D =E +)1#0)3 4&B0 ) R1#*6" /%'0#56%/# %:#1 ,4&" ?4..4)/ &"# S%*K'#1%1<B GR%15,4&" ).. "#1 7*%,.#+$# %2 "40&%135 @.46# ")+ *%:#13 6.#)1 *%&4%* "%, .%*$ )$% )*3&"4*$ ")+")((#*#+<I !% 0"# -#$)* )$)4*8 =N' #0& /)6")&&#MB ,"46" ,)0 &"# 2410& 0#*&#*6# 4* "#1R1#*6" .#00%*;-%%7< >"# O%'0# $):# )0'++#* .#)( %'& %2 &"# ,)&#15 )*+ 0##/#+ &%K'4:#1 ).. %:#1 ,4&" 214$"&< =N"5 E -#$ 3%'1()1+%*AB 614#+ @.46# ")0&4.35 )21)4+ &")& 0"# ")+"'1& &"# (%%1 )*4/).B0 2##.4*$0< =E K'4&# 2%1$%&3%' +4+*B& .47# 6)&0<B

=T%& .47# 6)&0AB 614#+ &"# O%'0#5 4* ) 0"14..5 ()0;04%*)&# :%46#< =?%'.+ UNV .47# 6)&0 42 3%' ,#1#/#MB

HyphenatedParagraph

Page 32: ReportLab Paragraphs Reloaded-EuroPython 2008

!"# $%& '( )*+ ,#*& &% &"# &)-.# &% /#)0'1#"#10#.2 -3 4&5 )*+ 2%'*+ &")&5 )0 *#)1.3 )0 0"#6%'.+ $'#005 0"# ,)0 *%, )-%'& &,% 2##& "4$"5)*+ ,)0 $%4*$ %* 0"14*74*$ 1)(4+.38 0"# 0%%*2%'*+ %'& &")& &"# 6)'0# %2 &"40 ,)0 &"# 2)*0"# ,)0 "%.+4*$5 )*+ 0"# +1%((#+ 4& ")0&4.359'0& 4* &4/# &% ):%4+ 0"14*74*$ ),)3 ).&%$#&";#1<

=>")& ?@! ) *)11%, #06)(#AB 0)4+ @.46#5 ) $%%++#). 214$"&#*#+ )& &"# 0'++#* 6")*$#5 -'&:#13 $.)+ &% 24*+ "#10#.2 0&4.. 4* #C40&#*6#D =)*+*%, 2%1 &"# $)1+#*AB )*+ 0"# 1)* ,4&" )..0(##+ -)67 &% &"# .4&&.# +%%18 -'&5 ).)0A &"#.4&&.# +%%1 ,)0 0"'& )$)4*5 )*+ &"# .4&&.# $%.+#*7#3 ,)0 .34*$ %* &"# $.)00 &)-.# )0 -#2%1#5=)*+ &"4*$0 )1# ,%10# &")* #:#15B &"%'$"& &"#(%%1 6"4.+5 =2%1 E *#:#1 ,)0 0% 0/).. )0 &"40-#2%1#5 *#:#1A @*+ E +#6.)1# 4&B0 &%% -)+5 &")& 4&40AB

@0 0"# 0)4+ &"#0# ,%1+0 "#1 2%%& 0.4((#+5 )*+4* )*%&"#1 /%/#*&5 0(.)0"A 0"# ,)0 '( &% "#16"4* 4* 0).& ,)&#1< F#1 2410& 4+#) ,)0 &")& 0"#")+ 0%/#"%, 2)..#* 4*&% &"# 0#)5 =)*+ 4* &")&6)0# E 6)* $% -)67 -3 1)4.,)35B 0"# 0)4+ &%"#10#.2< [email protected]# ")+ -##* &% &"# 0#)04+# %*6# 4*"#1 .42#5 )*+ ")+ 6%/# &% &"# $#*#1). 6%*6.';04%*5 &")& ,"#1#:#1 3%' $% &% %* &"# H*$.40"6%)0& 3%' 24*+ ) *'/-#1 %2 -)&"4*$ /)6"4*#04* &"# 0#)5 0%/# 6"4.+1#* +4$$4*$ 4* &"# 0)*+,4&" ,%%+#* 0()+#05 &"#* ) 1%, %2 .%+$4*$"%'0#05 )*+ -#"4*+ &"#/ ) 1)4.,)3 0&)&4%*<IF%,#:#15 0"# 0%%* /)+# %'& &")& 0"# ,)0 4*&"# (%%. %2 &#)10 ,"46" 0"# ")+ ,#(& ,"#*0"# ,)0 *4*# 2##& "4$"<

=E ,40" E ")+*B& 614#+ 0% /'6"AB 0)4+ @.46#5 )00"# 0,)/ )-%'&5 &134*$ &% 24*+ "#1 ,)3 %'&< =E0").. -# ('*40"#+ 2%1 4& *%,5 E 0'((%0#5 -3-#4*$ +1%,*#+ 4* /3 %,* &#)10A >")& ?EJJ -#) K'##1 &"4*$5 &% -# 0'1#A F%,#:#15 #:#13&"4*$40 K'##1 &%;+)3<B

L'0& &"#* 0"# "#)1+ 0%/#&"4*$ 0(.)0"4*$)-%'& 4* &"# (%%. ) .4&&.# ,)3 %225 )*+ 0"#0,)/ *#)1#1 &% /)7# %'& ,")& 4& ,)08 )& 2410&0"# &"%'$"& 4& /'0& -# ) ,).1'0 %1 "4((%(%&;)/'05 -'& &"#* 0"# 1#/#/-#1#+ "%, 0/)..0"# ,)0 *%,5 )*+ 0"# 0%%* /)+# %'& &")& 4&,)0 %*.3 ) /%'0# &")& ")+ 0.4((#+ 4* .47#"#10#.2<

=?%'.+ 4& -# %2 )*3 '0#5 *%,5B &"%'$"& @.46#5=&% 0(#)7 &% &"40 /%'0#M H:#13&"4*$ 40 0%%'&;%2;&"#;,)3 +%,* "#1#5 &")& E 0"%'.+ &"4*7:#13 .47#.3 4& 6)* &).78 )& )*3 1)&#5 &"#1#B0 *%")1/ 4* &134*$<B !% 0"# -#$)*8 =N O%'0#5 +%3%' 7*%, &"# ,)3 %'& %2 &"40 (%%.M E )/ :#13&41#+ %2 0,4//4*$ )-%'& "#1#5 N O%'0#[email protected]# &"%'$"& &"40 /'0& -# &"# 14$"& ,)3 %20(#)74*$ &% ) /%'0#8 0"# ")+ *#:#1 +%*#0'6" ) &"4*$ -#2%1#5 -'& 0"# 1#/#/-#1#+"):4*$ 0##* 4* "#1 -1%&"#1B0 J)&4* P1)//)15 =@/%'0#;;%2 ) /%'0#;;&% ) /%'0#;;) /%'0#;;N/%'0#ABI >"# O%'0# .%%7#+ )& "#1 1)&"#1 4*;K'404&4:#.35 )*+ 0##/#+ &% "#1 &% ,4*7 ,4&"%*# %2 4&0 .4&&.# #3#05 -'& 4& 0)4+ *%&"4*$<

=Q#1")(0 4& +%#0*B& '*+#10&)*+ H*$.40"5B&"%'$"& @.46#D =E +)1#0)3 4&B0 ) R1#*6" /%'0#56%/# %:#1 ,4&" ?4..4)/ &"# S%*K'#1%1<B GR%15,4&" ).. "#1 7*%,.#+$# %2 "40&%135 @.46# ")+ *%:#13 6.#)1 *%&4%* "%, .%*$ )$% )*3&"4*$ ")+")((#*#+<I !% 0"# -#$)* )$)4*8 =N' #0& /)6")&&#MB ,"46" ,)0 &"# 2410& 0#*&#*6# 4* "#1R1#*6" .#00%*;-%%7< >"# O%'0# $):# )0'++#* .#)( %'& %2 &"# ,)&#15 )*+ 0##/#+ &%K'4:#1 ).. %:#1 ,4&" 214$"&< =N"5 E -#$ 3%'1()1+%*AB 614#+ @.46# ")0&4.35 )21)4+ &")& 0"# ")+"'1& &"# (%%1 )*4/).B0 2##.4*$0< =E K'4&# 2%1$%&3%' +4+*B& .47# 6)&0<B

=T%& .47# 6)&0AB 614#+ &"# O%'0#5 4* ) 0"14..5 ()0;04%*)&# :%46#< =?%'.+ UNV .47# 6)&0 42 3%' ,#1#/#MB

HyphenlightedParagraph

Page 33: ReportLab Paragraphs Reloaded-EuroPython 2008

• Input text: "Hello file://smiley.jpeg world"

• Events: [{"text":"Hello", …}, {"text":"file://smiley.jpg", "type":"Image", "width":16, "height":16}, {"text":"world", …}]

• draw method picks and renders image events…

IconizedParagraph

Page 34: ReportLab Paragraphs Reloaded-EuroPython 2008

!"#!$ " %"#& "'( #$"#)* +,-. /01, " 234*,& .341'356 74# (3 !"#* ,"# %"#*& 8 53'(,-9) :'($,-, :/0!, %,;"' #3 ;,# -"#$,- */,,<.& "'(5,'# 3' *".0'; #3 $,-*,/=& 0' " (-,"2. *3-# 3=5".& >?3 !"#* ,"# %"#*9 ?3 !"#* ,"# %"#*9) "'(*32,#02,*& >?3 %"#* ,"# !"#*9) =3-& .34 *,,& "**$, !34/(')# "'*5,- ,0#$,- @4,*#03'& 0#(0(')# 24!$ 2"##,- 5$0!$ 5". *$, <4# 0#6 A$,=,/# #$"# *$, 5"* (3B0'; 3==& "'( $"( C4*#%,;4' #3 (-,"2 #$"# *$, 5"* 5"/10'; $"'( 0'$"'( 50#$ ?0'"$& "'( *".0'; #3 $,- +,-.,"-',*#/.& >D35& ?0'"$& #,// 2, #$, #-4#$E (0(.34 ,+,- ,"# " %"#9) 5$,' *4((,'/.& #$42<F#$42<F (35' *$, !"2, 4<3' " $,"< 3= *#0!1*"'( (-. /,"+,*& "'( #$, ="// 5"* 3+,-6

:/0!, 5"* '3# " %0# $4-#& "'( *$, C42<,( 4< 3'#3 $,- =,,# 0' " 232,'#E *$, /331,( 4<& %4# 0#5"* "// ("-1 3+,-$,"(G %,=3-, $,- 5"* "'3#$,-/3'; <"**";,& "'( #$, H$0#, I"%%0# 5"* *#0// 0'*0;$#& $4--.0'; (35' 0#6 J$,-, 5"* '3# "232,'# #3 %, /3*#E "5". 5,'# :/0!, /01, #$,50'(& "'( 5"* C4*# 0' #02, #3 $,"- 0# *".& "* 0##4-',( " !3-',-& >K$ 2. ,"-* "'( 5$0*1,-*&$35 /"#, 0#)* ;,##0';F) A$, 5"* !/3*, %,$0'( 0#5$,' *$, #4-',( #$, !3-',-& %4# #$, I"%%0#5"* '3 /3';,- #3 %, *,,'E *$, =34'( $,-*,/= 0'" /3';& /35 $"//& 5$0!$ 5"* /0# 4< %. " -35 3=/"2<* $"';0'; =-32 #$, -33=6

J$,-, 5,-, (33-* "// -34'( #$, $"//& %4# #$,.5,-, "// /3!1,(G "'( 5$,' :/0!, $"( %,,' "//#$, 5". (35' 3', *0(, "'( 4< #$, 3#$,-&#-.0'; ,+,-. (33-& *$, 5"/1,( *"(/. (35' #$,20((/,& 53'(,-0'; $35 *$, 5"* ,+,- #3 ;,#34# ";"0'6

A4((,'/. *$, !"2, 4<3' " /0##/, #$-,,L/,;;,(#"%/,& "// 2"(, 3= *3/0( ;/"**G #$,-, 5"*'3#$0'; 3' 0# ,M!,<# " #0'. ;3/(,' 1,.& "'(:/0!,)* =0-*# #$34;$# 5"* #$"# 0# 20;$# %,/3';#3 3', 3= #$, (33-* 3= #$, $"//G %4#& "/"*F ,0#$,-#$, /3!1* 5,-, #33 /"-;,& 3- #$, 1,. 5"* #33*2"//& %4# "# "'. -"#, 0# 534/( '3# 3<,' "'. 3=#$,26 N35,+,-& 3' #$, *,!3'( #02, -34'(&*$, !"2, 4<3' " /35 !4-#"0' *$, $"( '3#'3#0!,( %,=3-,& "'( %,$0'( 0# 5"* " /0##/, (33-"%34# =0=#,,' 0'!$,* $0;$E *$, #-0,( #$, /0##/,;3/(,' 1,. 0' #$, /3!1& "'( #3 $,- ;-,"#(,/0;$# 0# =0##,(F

:/0!, 3<,',( #$, (33- "'( =34'( #$"# 0# /,(

0'#3 " *2"// <"**";,& '3# 24!$ /"-;,- #$"' "-"#L$3/,E *$, 1',/# (35' "'( /331,( "/3'; #$,<"**";, 0'#3 #$, /3+,/0,*# ;"-(,' .34 ,+,-*"56 N35 *$, /3';,( #3 ;,# 34# 3= #$"# ("-1$"//& "'( 5"'(,- "%34# "23'; #$3*, %,(* 3=%-0;$# =/35,-* "'( #$3*, !33/ =34'#"0'*& %4#*$, !34/( '3# ,+,' ;,# $,- $,"( #$-34;$ #$,(33-5".G >"'( ,+,' 0= 2. $,"( 534/( ;3#$-34;$&) #$34;$# <33- :/0!,& >0# 534/( %, 3=+,-. /0##/, 4*, 50#$34# 2. *$34/(,-*6 K$& $35 850*$ 8 !34/( *$4# 4< /01, " #,/,*!3<,F 8 #$0'1 8!34/(& 0= 8 3'/. 1'35 $35 #3 %,;0'6) O3-& .34*,,& *3 2"'. 34#L3=L#$,L5". #$0';* $"($"<<,',( /"#,/.& #$"# :/0!, $"( %,;4' #3 #$0'1#$"# +,-. =,5 #$0';* 0'(,,( 5,-, -,"//.02<3**0%/,6

J$,-, *,,2,( #3 %, '3 4*, 0' 5"0#0'; %. #$,/0##/, (33-& *3 *$, 5,'# %"!1 #3 #$, #"%/,& $"/=$3<0'; *$, 20;$# =0'( "'3#$,- 1,. 3' 0#& 3- "#"'. -"#, " %331 3= -4/,* =3- *$4##0'; <,3</, 4</01, #,/,*!3<,*E #$0* #02, *$, =34'( " /0##/,%3##/, 3' 0#& P>5$0!$ !,-#"0'/. 5"* '3# $,-,%,=3-,&) *"0( :/0!,&Q "'( -34'( #$, ',!1 3= #$,%3##/, 5"* " <"<,- /"%,/& 50#$ #$, 53-(*>?I8DR ST) %,"4#0=4//. <-0'#,( 3' 0# 0' /"-;,/,##,-*6

8# 5"* "// +,-. 5,// #3 *". >?-0'1 2,&) %4# #$,50*, /0##/, :/0!, 5"* '3# ;30'; #3 (3 JN:J 0' "$4--.6 >D3& 8)// /331 =0-*#&) *$, *"0(& >"'( *,,5$,#$,- 0#)* 2"-1,( U<30*3'U 3- '3#)G =3- *$,$"( -,"( *,+,-"/ '0!, /0##/, $0*#3-0,* "%34#!$0/(-,' 5$3 $"( ;3# %4-'#& "'( ,"#,' 4< %.50/( %,"*#* "'( 3#$,- 4'</,"*"'# #$0';*& "//%,!"4*, #$,. HKVW? '3# -,2,2%,- #$,*02</, -4/,* #$,0- =-0,'(* $"( #"4;$# #$,2E*4!$ "*& #$"# " -,(L$3# <31,- 50// %4-' .34 0=.34 $3/( 0# #33 /3';G "'( #$"# 0= .34 !4# .34-=0';,- XTIY (,,</. 50#$ " 1'0=,& 0# 4*4"//.%/,,(*G "'( *$, $"( ',+,- =3-;3##,' #$"#& 0=.34 (-0'1 24!$ =-32 " %3##/, 2"-1,( ><30*3'&)0# 0* "/23*# !,-#"0' #3 (0*";-,, 50#$ .34&*33',- 3- /"#,-6

N35,+,-& #$0* %3##/, 5"* DKJ 2"-1,(><30*3'&) *3 :/0!, +,'#4-,( #3 #"*#, 0#& "'(=0'(0'; 0# +,-. '0!,& P0# $"(& 0' ="!#& " *3-# 3=20M,( =/"+34- 3= !$,--.L#"-#& !4*#"-(&<0',L"<</,& -3"*# #4-1,.& #3==,,& "'( $3#%4##,-,( #3"*#&Q *$, +,-. *33' =0'0*$,( 0# 3==6

IconizedParagraph

Page 35: ReportLab Paragraphs Reloaded-EuroPython 2008

After a while, finding that nothing morehappened, she decided on going into the garden atonce; but, alas for poor Alice! when she got to thedoor, she found she had forgotten the little goldenkey, and when she went back to the table for it,she found she could not possibly reach it: shecould see it quite plainly through the glass, andshe tried her best to climb up one of the legs ofthe table, but it was too slippery; and when shehad tired herself out with trying, the poor littlething sat down and cried.

`Come, there's no use in crying like that!' saidAlice to herself, rather sharply; `I advise you toleave off this minute!' She generally gave herselfvery good advice, (though she very seldomfollowed it), and sometimes she scolded herselfso severely as to bring tears into her eyes; andonce she remembered trying to box her own earsfor having cheated herself in a game of croquetshe was playing against herself, for this curiouschild was very fond of pretending to be twopeople. `But it's no use now,' thought poor Alice,`to pretend to be two people! Why, there's hardlyenough of me left to make ONE respectableperson!'

Soon her eye fell on a little glass box that waslying under the table: she opened it, and found init a very small cake, on which the words `EATME' were beautifully marked in currants.`Well, I'll eat it,' said Alice, `and if it makes megrow larger, I can reach the key; and if it makesme grow smaller, I can creep under the door; soeither way I'll get into the garden, and I don't carewhich happens!'

She ate a little bit, and said anxiously toherself, `Which way? Which way?', holding herhand on the top of her head to feel which way itwas growing, and she was quite surprised to findthat she remained the same size: to be sure, thisgenerally happens when one eats cake, but Alicehad got so much into the way of expectingnothing but out-of-the-way things to happen,that it seemed quite dull and stupid for life to goon in the common way.

So she set to work, and very soon finished off thecake.

* * * * * * *

* * * * * *

* * * * * * *

CHAPTER II

The Pool of Tears

`Curiouser and curiouser!' cried Alice (shewas so much surprised, that for the moment shequite forgot how to speak good English); `nowI'm opening out like the largest telescope thatever was! Good-bye, feet!' (for when she lookeddown at her feet, they seemed to be almost out ofsight, they were getting so far off). `Oh, my poorlittle feet, I wonder who will put on your shoesand stockings for you now, dears? I'm sure _I_shan't be able! I shall be a great deal too far off totrouble myself about you: you must manage thebest way you can; --but I must be kind to them,'thought Alice, `or perhaps they won't walk theway I want to go! Let me see: I'll give them a newpair of boots every Christmas.'

And she went on planning to herself how shewould manage it. `They must go by the carrier,'she thought; `and how funny it'll seem, sendingpresents to one's own feet! And how odd thedirections will look!

ALICE'S RIGHT FOOT, ESQ. HEARTHRUG,NEAR THE FENDER, (WITH ALICE'S LOVE).

Oh dear, what nonsense I'm talking!'

Just then her head struck against the roof of thehall: in fact she was now more than nine feethigh, and she at once took up the little golden keyand hurried off to the garden door.

Poor Alice! It was as much as she could do, lyingdown on one side, to look through into the gardenwith one eye; but to get through was morehopeless than ever: she sat down and began to cryagain.

`You ought to be ashamed of yourself,' saidAlice, `a great girl like you,' (she might well saythis), `to go on crying in this way! Stop thismoment, I tell you!' But she went on all the same,shedding gallons of tears, until there was a largepool all round her, about four inches deep andreaching half down the hall.

GraphicsParagraph

Page 36: ReportLab Paragraphs Reloaded-EuroPython 2008

ReportLab Paragraph

If you need something thatisn't on the feature list,please check the mailing-listarchives. Lots of featuresare yet to be documented,but most of the functionalityhas been commented in themailing-list. If you have a

problem that has not beendealt with yet...

CountingParagraph

If0 you need something thatisn't1 on the feature list,please2 check the mailing-listarchives.3 Lots of featuresare4 yet to be documented,but5 most of the functionalityhas6 been commented in themailing-list.7 If you have a

problem0 that has not beendealt1 with yet...CountingParagraph

Page 37: ReportLab Paragraphs Reloaded-EuroPython 2008

Summary

Page 38: ReportLab Paragraphs Reloaded-EuroPython 2008

Code Lengthrlpara dinu$ pycount2.py *.py lines code doc comment blank file 2380 1831 66 338 145 para-svn.py 1432 1127 131 59 115 paragraph-svn.py 1299 998 129 58 114 paragraph-2.1.py

alterparagraphs dinu$ pycount2.py *paragraph.py lines code doc comment blank file 48 30 0 2 16 colouredparagraph.py 101 64 0 7 30 graphicsparagraph.py 75 53 0 3 19 highlightedparagraph.py 204 132 0 33 39 hyphenatedparagraph.py 264 182 0 32 50 hyphenlightedparagraph.py 186 128 0 11 47 iconizedparagraph.py 202 111 25 13 53 minimalparagraph.py 271 163 26 18 64 simpleparagraph.py 31 15 2 4 10 ttfparagraph.py

Page 39: ReportLab Paragraphs Reloaded-EuroPython 2008

Runtime$ run_performance_test.py --mnp 500 ../input/alice30.txt input file: ../input/alice30.txt#words: 28200#lines: 3854#paragraphs: 500Paragraph, time needed: 0.564515 (s) file size: 91901 (Byte)MinimalParagraph, time needed: 1.143239 (2.03 x) file size: 210104 (2.29 x)SimpleParagraph, time needed: 1.872415 (3.32 x) file size: 262991 (2.86 x)TTFParagraph, time needed: 1.635452 (2.90 x) file size: 244040 (2.66 x) ColouredParagraph, time needed: 2.368929 (4.20 x) file size: 544549 (5.93 x)HyphenatedParagraph, time needed: 4.455254 (4.86 x) file size: 363638 (2.51 x)HighlightedParagraph, time needed: 2.958230 (3.23 x) file size: 403546 (2.79 x)HyphenlightedParagraph, time needed: 4.993915 (5.45 x) file size: 413874 (2.86 x)IconizedParagraph, time needed: 1.979934 (3.51 x) file size: 270026 (2.94 x)GraphicsParagraph, time needed: 1.799298 (3.19 x) file size: 269538 (2.93 x)

Page 40: ReportLab Paragraphs Reloaded-EuroPython 2008

Current State

• Family of paragraph classes

• Usually domain specific (e.g. kerning)

• Small code size

• Ca. 2–3 x slower than RL (pure text)

• Easier extensible than RL

Page 41: ReportLab Paragraphs Reloaded-EuroPython 2008

Future

• Further Paragraph attributes

• KerningParagraph

• XMLParagraph (tags)

• General callback mechanism

• RTLParagraph? (Right to Left)

Page 43: ReportLab Paragraphs Reloaded-EuroPython 2008

Questions?

’I have answered three questions, and that is enough,‘Said his father; ’don't give yourself airs!

Do you think I can listen all day to such stuff?Be off, or I'll kick you down stairs!‘

“Alice‘s Adventures in Wonderland, Lewis Carroll