|
|
|
|
---
|
|
|
|
|
layout: post
|
|
|
|
|
type: handbook
|
|
|
|
|
title: "ReportLab 101"
|
|
|
|
|
date: 2013-10-29
|
|
|
|
|
tags: coding pdf reportlab
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Style
|
|
|
|
|
|
|
|
|
|
เราจะใช้เพียงแค่ font TH Sarabun New ครับ โดยจะมีให้เลือกแค่ 2 แบบ:-
|
|
|
|
|
|
|
|
|
|
* TH Sarabun New
|
|
|
|
|
* TH Sarabun New Bold
|
|
|
|
|
|
|
|
|
|
ส่วนแต่ละส่วนนั้น สามารถจัดการ style โดยการออกแบบ Style ของแต่ละส่วนก่อน เช่น ให้หัวกระดาษเป็นอย่างนึง, เนื้อหาอย่างนึง, ในตารางอีกแบบนึง, คำอธิบายใต้ตารางอีกแบบนึง เป็นต้น พยายามจัดการให้มันไม่ต้องมีแบบมาก เพราะ (1)ง่ายต่อการอ่าน code (2)ง่ายต่อต่อการแก้ไข (3)ไม่ฟุ่มเฟือย
|
|
|
|
|
|
|
|
|
|
เท่าที่ดูวิธีที่ง่ายที่สุดคือ ใช้ `ParagraphStyle` โดยเราสามารถจัดการได้โดย `import ParagraphStyle` ด้านบนของ file ก่อน
|
|
|
|
|
|
|
|
|
|
from core.views.pdfs import Paragraph, ParagraphStyle
|
|
|
|
|
|
|
|
|
|
จากนั้นเราก็จะสามารถกำหนด style ของตัวเองได้
|
|
|
|
|
|
|
|
|
|
style_x1 = ParagraphStyle(name="style_name",
|
|
|
|
|
fontName='TH Sarabun New',
|
|
|
|
|
fontSize=16,
|
|
|
|
|
alignment=TA_CENTER,
|
|
|
|
|
leading=16,
|
|
|
|
|
spaceAfter=8,)
|
|
|
|
|
|
|
|
|
|
`name="xxx"` เป็นสิ่งที่จำเป็นสำหรับ `ParagraphStyle` นอกนั้นก็กำหนดตามต้องการ ตรง `TA_CENTER` แปลว่า ให้มันอยู่ตรงกลาง ซึ่งก็ยังมี `TA_LEFT`, `TA_RIGHT`, `TA_JUSTIFY` ตามชื่อ โดยถ้าต้องการใช้ค่าพวกนี้ก็ต้อง import ด้วย เช่น
|
|
|
|
|
|
|
|
|
|
from reportlab.lib.enums import TA_CENTER, TA_LEFT
|
|
|
|
|
|
|
|
|
|
ที่ผ่านมาคือ การตั้งว่า style เราจะมีอะไรบ้างต่อไปก็จะเป็นการใช้ style หลักๆแล้วเราจะใช้ Python List (ก็อารมณ์ array นั่นแหละ) ในการเก็บข้อมูลแต่ละชุดๆไป จะอารมณ์ประมาณว่า
|
|
|
|
|
|
|
|
|
|
lines = [] # เริ่ม declare list ก่อน
|
|
|
|
|
lines.append(Paragraph("บรรทัดแรก", style_x1)) # อันนี้เหมือนเพิ่ม element ใน list
|
|
|
|
|
lines.append(Paragraph("บรรทัดที่สอง", style_x1)) # อันนี้เหมือนเพิ่ม element ใน list ต่อไป
|
|
|
|
|
|
|
|
|
|
เราก็จะได้ประมาณว่า
|
|
|
|
|
|
|
|
|
|
บรรทัดแรก
|
|
|
|
|
บรรทัดที่สอง
|
|
|
|
|
|
|
|
|
|
มันจะตรงกลางนะ แต่พิมในนี้ลำบาก ก็เลยทำเป็นเป็นแค่ตัวอย่าง
|
|
|
|
|
|
|
|
|
|
### Color
|
|
|
|
|
|
|
|
|
|
การกำหนดสีจากที่ใช้ `colors.lightgrey` อะไรไปก็ยังสามารถกำหนดสีเองได้โดย
|
|
|
|
|
|
|
|
|
|
from reportlab.lib.colors import Color
|
|
|
|
|
red50transparent = Color(100, 0, 0, alpha=0.5)
|
|
|
|
|
|
|
|
|
|
โดย `Color(สีแดง, สีเขียว, สีฟ้า, alpha=1)` โดยแต่ละสีมีค่าตั้งแต่ 0-255 และ alpha ก็มีค่าตั้งแต 0-1 (1 คือ ทึบ, 0 คือ ใสทะลุปรุโปร่ง) จากนั้นก็เอา `red50transparent` ไปแทนที่ `colors.lightgrey` ได้เลย
|
|
|
|
|
|
|
|
|
|
### ข้อสังเกต
|
|
|
|
|
|
|
|
|
|
1. การขึ้นบรรทัดใหม่ถ้าเป็น Paragraph เดียวกันมันจะตัดขึ้นบรรทัดใหม่ให้เอง โดยใช้ช่องว่างเป็นตัวตัด มันใช้ได้ดีกับภาษาอังกฤษ แต่ไม่ใช่ภาษาไทย ดังนั้นเราต้องตัดคำเองครับ ถ้าคิดว่าตัวอักษรมันมีจำนวนไม่แน่นอน เผื่อไว้เลยครับ ดีกว่ากลายเป็นฉีกคำประหลาดๆ ให้ด้านหลังว่างไว้เยอะหน่อยยังดีกว่ามาก
|
|
|
|
|
1. การขึ้นบรรทัดใหม่ สามารถใช้ html tag <br/> เป็นตัวขึ้นบรรทัดใหม่ได้เลย
|
|
|
|
|
1. แต่ละ Paragraph จะขึ้นบรรทัดใหม่เสมอ ค่ามันจะขึ้นกับ spaceBefore กับ spaceAfter
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Table
|
|
|
|
|
|
|
|
|
|
ตารางเป็นอีกส่วนที่มีความซับซ้อนพอสมควรในกรณีที่มี cell รวมกัน แต่ถ้าตามปกติก็ง่ายๆ ไม่มีอะไร มาดูตัวอย่างง่ายๆ แล้วค่อยไล่อธิบายบางทีอาจจะเข้าใจง่ายกว่า
|
|
|
|
|
|
|
|
|
|
data = [
|
|
|
|
|
['top\nLeft', '10', '20', '30'],
|
|
|
|
|
['01', '11', '21'],
|
|
|
|
|
['02', '12', '22', '32'],
|
|
|
|
|
['03', '13', '23', '33'],
|
|
|
|
|
['04', '14', '24', '34'],
|
|
|
|
|
]
|
|
|
|
|
t = Table(data, style=[
|
|
|
|
|
('FONTNAME', (0,0), (-1,-1), 'TH Sarabun New Bold'),
|
|
|
|
|
('GRID', (0,0), (-1,-1), 0.5 , colors.grey),
|
|
|
|
|
('BACKGROUND',(0,0),(1,1),colors.palegreen),
|
|
|
|
|
('SPAN',(0,0),(1,1)),
|
|
|
|
|
('BACKGROUND',(-2,-2),(-1,-1), colors.red),
|
|
|
|
|
('SPAN',(-2,-2),(-1,-1)),
|
|
|
|
|
])
|
|
|
|
|
lines.append(t)
|
|
|
|
|
|
|
|
|
|
data นั้นจะเป็นตัวเก็บข้อมูลไว้แสดงในตารางโดยจะเป็น list 2 หรือ 3 มิติ เรียกไม่ถูกเหมือนกัน แต่มันจะซ้อนกันอย่างนั้น โดยระดับแรกจะเป็นตัวข้อมูลแสดงของ row และระดับที่ 2 ก็คือจะเป็นตัวข้อมูลในระดับ column ของแต่ละ row โดยข้อมูลนั้นจะใช้ `Table` เป็นตัวแปลงให้เป็นตาราง (ซึ่งก็ต้อง import อีกนั่นแหละ)
|
|
|
|
|
|
|
|
|
|
from core.views.pdfs import Table
|
|
|
|
|
|
|
|
|
|
หลังจากนั้นก็ต้องไปต่อใน lines เหมือนกับที่ทำกับ Paragraph ไม่มีผิดครับ ความสับสนของตารางมันจะอยู่ตรงที่ style มากกว่า มาดูผลโดยประมาณกันก่อนว่า มันจะได้ ตารางใน pdf ยังไงกันแน่
|
|
|
|
|
|
|
|
|
|
<table class="wikitable"><tbody><tr><td style="background-color: #33dd33;" colspan="2" rowspan="2">top<p>Left</p></td><td>20</td><td>30</td></tr><tr><td>12</td><td></td></tr><tr><td>02</td><td>12</td><td>22</td><td>32</td></tr><tr><td>03</td><td>13</td><td style="background-color: #f66;" colspan="2" rowspan="2">23</td></tr><tr><td>04</td><td>14</td></tr></tbody></table>
|
|
|
|
|
|
|
|
|
|
ตารางจะเริ่มที่มุมบนซ้าย -- เริ่มด้วย column 0 และ row 0 --> (0, 0) ตามที่เห็นก็สีมั่วๆนะครับ ขี้เกียจครับ อย่างที่เห็นชัดๆ คือ จะมี cell merge กันอยู่ 2 ที่ คือ
|
|
|
|
|
|
|
|
|
|
1. มุมซ้ายบน ตำแหน่ง `(col, row) (0, 0)` ถึง `(1, 1)`
|
|
|
|
|
1. มุมขวาล่าง ตำแหน่ง `(col, row) (2, 3)` ถึง `(3, 4)` หรือมองอีกมุม (ขวาล่าง) คือ `(-2,-2),(-1,-1)`
|
|
|
|
|
|
|
|
|
|
จริงๆ ก็จะเห็นชัดเจนกว่า style มันเนี่ย ตรงที่ cell merge กัน ถูกกำหนดด้วย
|
|
|
|
|
|
|
|
|
|
('BACKGROUND',(0,0),(1,1),colors.palegreen),
|
|
|
|
|
('SPAN',(0,0),(1,1)),
|
|
|
|
|
|
|
|
|
|
`BACKGROUND` ก็ไม่ได้เกี่ยวอะไรกับเค้า เพียงแค่บอกสีครับ SPAN เป็นตัวบอกว่า เราจะรวม cell ไหนถึงตรงไหนกันแน่ โดยข้อมูลจะเป็นข้อมูลของ cell แรกเสมอครับ
|
|
|
|
|
|
|
|
|
|
### จัดตำแหน่ง
|
|
|
|
|
|
|
|
|
|
ถ้าต้องการจะจัดตำแหน่งอักษรของแต่ละ cell ก็เพิ่มใน style ของ Table ได้
|
|
|
|
|
|
|
|
|
|
('ALIGN',(0,0),(0,0),'CENTER'),
|
|
|
|
|
('VALIGN',(0,0),(0,0),'MIDDLE'),
|
|
|
|
|
|
|
|
|
|
* `ALIGN` คือ จัดตำแหน่งแนวนอน มีค่าเป็น `LEFT`, `RIGHT`, `CENTER` -- ค่า default เป็น `LEFT`
|
|
|
|
|
* `VALIGN` คือ จัดตำแหน่งแนวนอน มีค่าเป็น `TOP`, `MIDDLE`, `BOTTOM` -- ค่า default เป็น `BOTTOM`
|
|
|
|
|
|
|
|
|
|
ในกรณีที่ต้องการจัดการตำแหน่งของ cell ที่มีการรวม cell กำหนดตำแหน่งแค่ cell ที่มีข้อมูล (มุมขวาบน) ก็พอ
|
|
|
|
|
|
|
|
|
|
### ข้อสังเกต
|
|
|
|
|
|
|
|
|
|
1. `SPAN` สามารถกำหนด ตำแหน่งค่าติดลบได้ `('SPAN',(-2,-2),(-1,-1))` << โดยจะเป็นการคิดจากมุมขวาล่างมา ตรงข้ามกับค่าบวกที่คิดจากมุมซ้ายบน
|
|
|
|
|
1. `SPAN` จะไม่สนใจข้อมูลที่อยู่ในตำแหน่งอื่นเลย นอกจากตัวมุมบนขวาของที่รวม -- ตัวอื่นจะอารมณ์ถูกทับไปเลย โดยสังเกตได้จากตัวอย่าง row ที่ 2 กลายเป็นว่า 01, 11 หายไปเลย โดยตำแหน่ง 21 ก็ไม่ได้เปลี่ยนไป
|
|
|
|
|
1. ถ้า list มีไม่ครบในตาราง ตรงที่ขาดไปก็จะเป็นช่องว่าง
|
|
|
|
|
1. การขึ้นบรรทัดใหม่ในตารางใช้ escape key `\n` ไม่ใช่ `<br/>` เหมือนใน `Paragraph`
|
|
|
|
|
1. พวก `SPAN` ใน style ไม่ได้ต้องเรียงลำดับอะไรครับ จัดยังไงก็ได้ มันอ่านรู้เรื่อง แต่ที่ควรจะทำให้มันเป็นจากบนลงล่าง เป็นระบบหน่อยก็เพราะแค่เราจะกลับไปแก้ลำบากขึ้นเยอะ ถ้ามันไล่ไม่ได้ง่ายๆครับ
|
|
|
|
|
|
|
|
|
|
เท่านี้ก็น่าจะพอได้ idea ไปลองแล้วหล่ะ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## การแก้ใน .py
|
|
|
|
|
|
|
|
|
|
ใครใช้รายงานไหน จริงๆ ก็น่าจะมีอะไรอยู่ใน file พอสมควรแล้วครับ แต่ส่วนที่ต้องแก้ คือ ส่วนของ
|
|
|
|
|
|
|
|
|
|
def prep_to_pdf(self, context):
|
|
|
|
|
lines = []
|
|
|
|
|
......
|
|
|
|
|
.....
|
|
|
|
|
...
|
|
|
|
|
..
|
|
|
|
|
return render_to_pdf(lines)
|
|
|
|
|
|
|
|
|
|
แค่ส่วนจุดๆๆ นี้แหละครับ ที่ต้องแก้ไข (ซึ่งชื่อตัวแปร lines ก็เปลี่ยนได้ตามชอบใจครับ ที่ใช้ lines เพราะจะได้สอดคล้องกับตัวอย่างด้านบน)
|
|
|
|
|
|
|
|
|
|
### การใช้คำสั่งต่างๆ
|
|
|
|
|
|
|
|
|
|
โดยมากแล้วจะเป็นการเปรียบเทียบคำสั่งต่างๆ ของ templatetag ที่เห็นใน .html กับที่ต้องเขียนใน python (.py) ใน html นั้นจะมีการเรียกใช้งานตัวแปรและ function ที่ต่างออกไปจากปกติ
|
|
|
|
|
|
|
|
|
|
{{date}}
|
|
|
|
|
|
|
|
|
|
`{{ }}` ไว้สำหรับแสดงผลตัวแปรนั้นเป็น string
|
|
|
|
|
|
|
|
|
|
{{date|month_full_th}}
|
|
|
|
|
|
|
|
|
|
| เป็นการบ่งบอกว่า มีการใช้ function กับตัวแปร date โดยเราจะสามารถดูได้ว่า function นี้คืออะไรได้จากการ import function ข้างบน ซึ่งใน `templatetag` จะใช้คำสั่ง `{ % load yyyyy % }`
|
|
|
|
|
|
|
|
|
|
การใช้งาน function พวกนี้ใน python code นั้นจะสามารถทำได้โดย
|
|
|
|
|
|
|
|
|
|
from core.templatetags.extratags import intcomma2, intword_th, month_full_th
|
|
|
|
|
|
|
|
|
|
thb = 1423.223
|
|
|
|
|
print u'ราคา %s' % intcomma2(thb)
|
|
|
|
|
# จะได้ผลลัพธ์เป็น
|
|
|
|
|
ราคา 1,423.22
|
|
|
|
|
|
|
|
|
|
### คำสั่งอ้างอิง
|
|
|
|
|
|
|
|
|
|
templatetags ที่ใช้ใน .html ก็มีแตกต่างกันออกไปแต่หลักๆก็จะมีอยู่ 2 ที่ด้วยกันคือ
|
|
|
|
|
|
|
|
|
|
* `report_tags` -- ส่วนใหญ่จะเป็นการแปลงค่าแสดงผลที่เกี่ยวกับโรงเรียนทั้งหมด เช่น แสดงรอบ แสดงชั้นเป็นต้น
|
|
|
|
|
|
|
|
|
|
from school.templatetags.report_tags import *
|
|
|
|
|
* `extra_tags` -- เป็นการแปลงค่าเพื่อแสดงผลทั่วๆไป เช่น ใน , ในหลักพัน การพิมวันที่เป็นภาษาไทย หรือพิมจำนวนเงินเป็นภาษาไทย
|
|
|
|
|
|
|
|
|
|
from core.templatetags.extra_tags import *
|
|
|
|
|
|
|
|
|
|
รายละเอียดของคำสั่งมีดังนี้
|
|
|
|
|
|
|
|
|
|
<table class="wikitable"><tbody><tr><th> file</th><th> function</th><th> description</th></tr><tr><td style="background-color: #ffffcc;" rowspan="6">report_tags</td><td style="background-color: #ffffcc;" colspan="2"> school.templatetags.report_tags</td></tr><tr><td> shift_display</td><td> แปลงค่า<ul><li> 0 -> รอบเช้า และ</li><li> 1 -> รอบบ่าย</li></ul></td></tr><tr><td> coarse_grade_display</td><td> แสดงชั้นปีแบบคร่าวๆ โดยจะใช้ค่าชั้นปีแบบใดก็ได้ ผลเป็นแบบหยาบเสมอ โดยจะแสดงเป็น<ul><li> เตรียมอนุบาล</li><li> อนุบาล</li><li> ประถมศึกษา</li><li> มัธยมศึกษาตอนต้น</li><li> มัธยมศึกษาตอนปลาย</li><li> ประกาศนียบัตรวิชาชีพ</li><li> ปวส.</li><li> ปวท.</li></ul></td></tr><tr><td> fine_grade_display</td><td> แสดงชั้นปีแบบเจาะจงชั้นปี<p>โดยจะแสดงเป็น</p><ul><li> เตรียมอนุบาล</li><li> อ.1, อ.2, อ.3</li><li> ป.1, ป.2, ป.3, ป.4, ป.5, ป.6, </li><li> ม.1, ม.2, ม.3, ม.4, ม.5, ม.6, </li><li> ปวช.1-3</li><li> ปวส.1-3</li><li> ปวท.1-2</li></ul></td></tr><tr><td> course_display</td><td> แสดงหลักสูตรของสายวิชาชีพของแต่ละห้อง</td></tr><tr><td> education_level_display</td><td> แสดงวุฒิที่ได้รับการบรรจุของบุคลากร เช่น ป.เอก (ครู) ป.เอก</td></tr><tr><td style="background-color: #ffffcc;" rowspan="9"> extra_tags</td><td style="background-color: #ffffcc;" colspan="2"> core.templatetags.extra_tags</td></tr><tr><td> intcomma2</td><td> เติม , ให้กับหลักพัน และถ้าค่าเป็นทศนิยมก็จะตัดเหลือแค่ 2 หลักให้อัตโนมัติ</td></tr><tr><td> intword_th</td><td> แสดงจำนวนเงินเป็นภาษาไทย</td></tr><tr><td> date_abbv_th</td><td> 11 พ.ย. 2556</td></tr><tr><td> date_full_th</td><td> 5 ธันวาคม 2556</td></tr><tr><td> month_abbv_th</td><td> ม.ค. 2556</td></tr><tr><td> month_full_th</td><td> กุมภาพันธ์ 2556</td></tr><tr><td> year_th</td><td> 2556</td></tr><tr><td> system_status_display</td><td></td></tr></tbody></table>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|