ReportLab 導出 PDF(文檔創建)
ReportLab 導出 PDF(頁面布局)
ReportLab 導出 PDF(圖文表格)
PLATYPUS - 頁面布局和排版
- 1. 設計目標
- 2. 開始
- 3. Flowables
- 3.1. Flowable.draw()
- 3.2. Flowable.drawOn(canvas,x,y)
- 3.3. Flowable.wrap(availWidth, availHeight)
- 3.4. Flowable.split(self, availWidth, availheight)
- 4. 流動定位的準則
- 5. Frames
- 5.1. Frame.addFromList(drawlist, canvas)
- 5.2. Frame.split(flowable,canv)
- 5.3. Frame.drawBoundary(canvas)
- 6. 文檔和模板
- 6.1. BaseDocTemplate.addPageTemplates(self,pageTemplates)
- 6.2. BaseDocTemplate.build(self, flowables, filename=None,canvasmaker=canvas.Canvas)
- 6.3. BaseDocTemplate.afterInit(self)
- 6.4. BaseDocTemplate.afterPage(self)
- 6.5. BaseDocTemplate.beforeDocument(self)
- 6.6. BaseDocTemplate.beforePage(self)
- 6.7. BaseDocTemplate.filterFlowables(self,flowables)
- 6.8. BaseDocTemplate.afterFlowable(self, flowable)
1. 設計目標
Platypus 是"Page Layout and Typography Using Scripts"的縮寫。它是一個高水平的頁面布局庫, 讓你可以用最少的努力以編程方式創建復雜的文檔。
Platypus 的設計力求將 "高層次 "的布局決定與文檔內容盡可能分開 。例如,段落使用段落樣式,頁面使用頁面模板,目的是讓數百個有數千頁的文件可以按照不同的樣式規格重新格式化,只需在一個包含段落樣式和頁面布局規格的共享文件中修改幾行即可。
Platypus的整體設計可以認為有幾個層次,自上而下,這些是:
? DocTemplates 作為文檔的最外層容器。
? PageTemplates 作為各種頁面布局的規格。
? Frames 頁面中可包含流動文本或圖形的區域規格。
? Flowables 對應"flowed into the document"流入文檔的文本或圖形元素(即圖像、段落和表格等內容,但不包括頁腳或固定頁面圖形等內容)。
pdfgen.Canvas 為最終從其他圖層接收文檔繪畫的最低層。
上面的插圖形象地說明了 DocTemplate、PageTemplate 和 Flowables 的概念 。然而,它具有欺騙性,因為每一個 PageTemplate 實際上可以指定任何數量的頁面的格式(而不是像從圖中推斷的那樣只指定一個)。
DocTemplate 包含一個或多個 PageTemplate,每個 PageTemplate 包含一個或多個Frame。
Flowables 是指可以 flowed(流入) Frame的東西,例如 Paragraph 或 Table。
要使用 platypus,你需要從 DocTemplate 類中創建一個文檔,并向其 build 方法傳遞一個 Flowables列表。document 的 build 方法知道如何將 flowable 列表處理成合理的東西。
在內部,DocTemplate 類使用各種事件來實現頁面布局和格式化。每個事件都有一個對應的處理方法,稱為 handle_XXX ,其中 XXX 是事件名稱。一個典型的事件是 frameBegin,它發生在機械開始第一次使用一個框架的時候。
Platypus 故事由一系列基本元素組成,這些元素被稱為 Flowables,它們驅動著數據驅動的 Platypus格式化引擎。為了修改引擎的行為,一種特殊的可流式元素 ActionFlowables 告訴布局引擎,例如,跳到下一列或者換成另一個 PageTemplate。
2. 開始
考慮以下代碼序列,它為 Platypus 提供了一個非常簡單的 "hello world "例子。
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.rl_config import defaultPageSize
from reportlab.lib.units import inchPAGE_HEIGHT=defaultPageSize[1]
PAGE_WIDTH=defaultPageSize[0]styles = getSampleStyleSheet()
Title = "Hello world"
pageinfo = "platypus example"# 首頁
def myFirstPage(canvas, doc):canvas.saveState()# 標題canvas.setFont('Times-Bold',16)canvas.drawCentredString(PAGE_WIDTH/2.0, PAGE_HEIGHT-108, Title)# 頁腳 填充固定字符canvas.setFont('Times-Roman',9)canvas.drawString(inch, 0.75 * inch, "First Page / %s" % pageinfo)canvas.restoreState()# 非首頁
def myLaterPages(canvas, doc):canvas.saveState()# 頁腳 填充頁碼canvas.setFont('Times-Roman',9)canvas.drawString(inch, 0.75 * inch, "Page %d %s" % (doc.page, pageinfo))canvas.restoreState()def main(filename: str):doc = SimpleDocTemplate(filename)# 與標題間隔Story = [Spacer(1, 2*inch)]style = styles["Normal"]for i in range(100):bogustext = ("This is Paragraph number %s. " % i) * 5p = Paragraph(bogustext, style)Story.append(p)# 段落之間間隔Story.append(Spacer(1, 0.2*inch))# 添加doc.build(Story, onFirstPage=myFirstPage, onLaterPages=myLaterPages)if __name__ == '__main__':main(filename='example.pdf')
我們創建一個"store"并構建文檔。請注意,我們在這里使用的是"canned"(罐頭)文檔模板,它是預建的頁面模板。
我們還使用了預建的段落樣式 。
我們在這里只使用了兩種類型的"flowables"–Spacers和Paragraphs 。第一個Spacer確保段落跳過標題字符串。
![]() | ![]() | ![]() |
---|
3. Flowables
Flowables 是可以被繪制的東西,它有wrap, draw和可能的split方法。Flowable 是一個抽象的基類,用于繪制事物,一個實例知道它的大小,并在它自己的坐標系中繪制(這需要基 API 在調用 Flowable.draw方法時提供一個絕對坐標系)。要獲得一個實例,使用 f=Flowable()。
注意: Flowable 類是一個抽象類,通常只作為基類使用.
為了說明使用 Flowables 的一般方式,我們將展示如何在畫布上使用和繪制衍生類 Paragraph。
def main(filename):from reportlab.lib.styles import getSampleStyleSheetfrom reportlab.platypus import Paragraphfrom reportlab.pdfgen.canvas import CanvasstyleSheet = getSampleStyleSheet()style = styleSheet['BodyText']P=Paragraph('This is a very silly example',style)canv = Canvas(filename)aW = 460 # available width and heightaH = 800w,h = P.wrap(aW, aH) # find required spaceif w<=aW and h<=aH:P.drawOn(canv,0,aH)aH = aH - h # reduce the available heightcanv.save()else:raise ValueError("Not enough room")if __name__ == '__main__':main(filename='example.pdf')
3.1. Flowable.draw()
這將被調用來要求 flowable 實際渲染自己。Flowable類沒有實現draw。調用代碼應該確保 flowable 有一個屬性canv,它pdfgen.Canvas,它應該被繪制到Canvas上,并且Canvas處于一個適當的狀態(就翻譯、旋轉等而言)。通常這個方法只在內部被drawOn方法調用,派生類必須實現這個方法。派生類必須實現這個方法。
3.2. Flowable.drawOn(canvas,x,y)
這是控制引擎用來將flowable渲染到特定畫布的方法。它處理轉換為畫布坐標(x,y),并確保flowable有一個canv屬性,這樣draw方法(在基類中沒有實現)就可以在一個絕對坐標框架中渲染。
3.3. Flowable.wrap(availWidth, availHeight)
在詢問對象的大小、繪制或其他什么之前,這個函數將被包圍的框架調用。它返回實際使用的尺寸。
3.4. Flowable.split(self, availWidth, availheight)
當wrap失敗時,更復雜的框架會調用這個函數。愚蠢的flowables應該返回[],這意味著它們無法拆分。聰明的flowables應該自己拆分并返回一個flowables列表。客戶端代碼要確保避免重復嘗試拆分。如果空間足夠,拆分方法應該返回[self]。否則,flowable應該重新排列,并返回一個按順序考慮的flowable列表[f0,…]。實現的拆分方法應該避免改變self,因為這將允許復雜的布局機制在一個可流動的列表上進行多次遞。
4. 流動定位的準則
有兩種方法,默認情況下返回零,為可流動物的垂直間距提供指導。
Flowable.getSpaceAfter(self):
Flowable.getSpaceBefore(self):
這些方法會返回flowable后面或前面應該有多少空間。這些空間不屬于flowable本身,也就是說,flowable的draw方法在渲染時不應該考慮它。控制程序將使用返回的值來確定上下文中特定flowable需要多少空間。
所有的flowables都有一個hAlign屬性:(‘LEFT’,‘RIGHT’,‘CENTER’或’CENTRE’)。對于占滿整個框架寬度的段落,這個屬性沒有影響。對于小于框架寬度的表格、圖像或其他對象,這決定了它們的水平位置。
下面的章節將涵蓋最重要的特定類型的可流動文件,段落和表格。
5. Frames
Frames是活動的容器,它本身就包含在PageTemplate中,Frames有一個位置和大小,并保持一個剩余可繪制空間的概念。如:
Frame(x1, y1, width, height, leftPadding=6, bottomPadding=6, rightPadding=6, topPadding=6, id=None, showBoundary=0)
創建一個左下角坐標為(x1,y1)的Frame實例(在使用時相對于畫布),尺寸為 width x height。Padding參數是用于減少繪畫空間的正量。參數id是運行時使用的標識符,例如"LeftColumn"或"RightColumn"等。如果showBoundary參數是非零,那么框架的邊界將在運行時被繪制出來(這有時很有用)。
Frames可以直接與canvases和flowables一起使用來創建文檔。Frame.addFromList方法為你處理wrap 和 drawOn調用。你不需要所有的Platypus引擎來獲得有用的東西到PDF中。
def main(filename): from reportlab.pdfgen.canvas import Canvasfrom reportlab.lib.styles import getSampleStyleSheetfrom reportlab.lib.units import inchfrom reportlab.platypus import Paragraph, Framestyles = getSampleStyleSheet()styleN = styles['Normal']styleH = styles['Heading1']story = []#add some flowablesstory.append(Paragraph("This is a Heading",styleH))story.append(Paragraph("This is a paragraph in <i>Normal</i> style.",styleN))c = Canvas(filename)f = Frame(inch, inch, 6*inch, 9*inch, showBoundary=1)f.addFromList(story,c)c.save()if __name__ == '__main__':main(filename='example.pdf')
5.1. Frame.addFromList(drawlist, canvas)
消耗drawlist前面的Flowables,直到幀滿為止。如果不能容納一個對象,則引發一個異常。
5.2. Frame.split(flowable,canv)
要求flowable使用可用空間進行分割,并返回flowable的列表。
5.3. Frame.drawBoundary(canvas)
將框架邊界畫成一個矩形(主要用于調試)。
6. 文檔和模板
BaseDocTemplate類
實現了文檔格式化的基本機制。該類的一個實例包含了一個或多個PageTemplate的列表,這些PageTemplate可用于描述單頁信息的布局。build方法可用于處理Flowables列表,以生成一個PDF文檔。
from reportlab.lib.pagesizes import A4
from reportlab.platypus import BaseDocTemplate
from reportlab.lib.units import inchBaseDocTemplate(filename,pagesize=A4,pageTemplates=[],showBoundary=0, # 控制是否繪制Frame的邊界,這對于調試來說是很有用的leftMargin=inch,rightMargin=inch,topMargin=inch,bottomMargin=inch,allowSplitting=1, # allowSplitting參數決定了內置方法是否應該嘗試split單個Flowables跨越Frametitle=None,author=None,_pageBreakQuick=1, # 參數決定了在結束頁面之前,是否應該嘗試結束頁面上的所有框架encrypt=None # encrypt 參數決定了是否對文檔進行加密,以及如何加密)
創建一個適合創建基本文檔的文檔模板。它帶有相當多的內部機制,但沒有默認的頁面模板。所需的filename可以是一個字符串,一個用于接收創建的PDF文檔的文件名;也可以是一個有write方法的對象,如 BytesIO 或 file 或 socket。
showBoundary控制是否繪制Frame的邊界,這對于調試來說是很有用的。
allowSplitting參數決定了內置方法是否應該嘗試split單個Flowables跨越Frame。
_pageBreakQuick參數決定了在結束頁面之前,是否應該嘗試結束頁面上的所有框架。
encrypt 參數決定了是否對文檔進行加密,以及如何加密。默認情況下,文檔是不加密的。如果encrypt是一個字符串對象,那么它將作為pdf的用戶密碼。如果encrypt是一個reportlab.lib.pdfencrypt.StandardEncryption的實例,那么這個對象就被用來加密pdf。這允許對加密設置進行更精細的控制。
PageTemplate類
是一個語義相當簡單的容器類。每個實例都包含一個Frames的列表,并且有一些方法應該在每個頁面的開始和結束時被調用。
PageTemplate(id=None, frames=[], onPage=_doNothing, onPageEnd=_doNothing)
用于初始化一個實例,frames參數應該是一個Frames的列表,而可選的onPage和onPageEnd參數是可調用的,它們的簽名應該是 def XXX(canvas,document),其中canvas和document是正在繪制的畫布和文檔。這些例程的目的是用來繪制頁面的非流動(即標準)部分。
這些屬性函數與純虛擬方法 PageTemplate.beforPage 和 PageTemplate.afterPage完全平行,這兩個方法的簽名是 beforPage(self,canvas,document)。這些方法允許使用類派生來定義標準行為,而屬性則允許改變實例。在運行時,id 參數用于執行 PageTemplate 的切換,所以 id=‘FirstPage’ 或 id='TwoColumns’是典型的。
6.1. BaseDocTemplate.addPageTemplates(self,pageTemplates)
此方法用于在現有文檔中添加一個或一系列PageTemplate。
6.2. BaseDocTemplate.build(self, flowables, filename=None,canvasmaker=canvas.Canvas)
這是應用程序程序員感興趣的主要方法。假設文檔實例被正確設置,build方法將story以flowables
列表的形式接收(flowables參數),并在列表中循環,將flowables列表一次一個地強制通過格式化
機制。實際上,這使得BaseDocTemplate實例發出對實例handle_XXX方法的調用來處理各種事件。
6.3. BaseDocTemplate.afterInit(self)
這個方法在基類初始化后被調用;派生類可以覆蓋該方法來添加默認的PageTemplates。
6.4. BaseDocTemplate.afterPage(self)
這是在頁面處理后,緊接著當前頁面模板的afterDrawPage方法被調用。一個派生類可以使用這個方
法來做一些依賴于頁面信息的事情,比如字典頁面上的首字和尾字。
6.5. BaseDocTemplate.beforeDocument(self)
在對文檔進行任何處理之前,但在處理機制準備好之后,就會調用這個函數,因此它可以用來對實
例的pdfgen.canvas等進行處理。因此,它可以用來對實例的pdfgen.canvas等進行操作。
6.6. BaseDocTemplate.beforePage(self)
這是在頁面處理開始時,在當前頁面模板的beforeDrawPage方法之前調用的。它可以用來重置頁面
特定的信息持有者。
6.7. BaseDocTemplate.filterFlowables(self,flowables)
在主 handle_flowable 方法開始時,調用這個函數來過濾flowables。在返回時,如果flowables[0]
被設置為None,則會被丟棄,主方法立即返回。
6.8. BaseDocTemplate.afterFlowable(self, flowable)
在flowable被渲染后調用。有興趣的類可以使用這個鉤子來收集特定頁面或框架上存在的信息。