--- 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
เป็นตัวขึ้นบรรทัดใหม่ได้เลย 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 ยังไงกันแน่
top

Left

2030
12
02122232
031323
0414
ตารางจะเริ่มที่มุมบนซ้าย -- เริ่มด้วย 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` ไม่ใช่ `
` เหมือนใน `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 * รายละเอียดของคำสั่งมีดังนี้
file function description
report_tags school.templatetags.report_tags
shift_display แปลงค่า
  • 0 -> รอบเช้า และ
  • 1 -> รอบบ่าย
coarse_grade_display แสดงชั้นปีแบบคร่าวๆ โดยจะใช้ค่าชั้นปีแบบใดก็ได้ ผลเป็นแบบหยาบเสมอ โดยจะแสดงเป็น
  • เตรียมอนุบาล
  • อนุบาล
  • ประถมศึกษา
  • มัธยมศึกษาตอนต้น
  • มัธยมศึกษาตอนปลาย
  • ประกาศนียบัตรวิชาชีพ
  • ปวส.
  • ปวท.
fine_grade_display แสดงชั้นปีแบบเจาะจงชั้นปี

โดยจะแสดงเป็น

  • เตรียมอนุบาล
  • อ.1, อ.2, อ.3
  • ป.1, ป.2, ป.3, ป.4, ป.5, ป.6,
  • ม.1, ม.2, ม.3, ม.4, ม.5, ม.6,
  • ปวช.1-3
  • ปวส.1-3
  • ปวท.1-2
course_display แสดงหลักสูตรของสายวิชาชีพของแต่ละห้อง
education_level_display แสดงวุฒิที่ได้รับการบรรจุของบุคลากร เช่น ป.เอก (ครู) ป.เอก
extra_tags core.templatetags.extra_tags
intcomma2 เติม , ให้กับหลักพัน และถ้าค่าเป็นทศนิยมก็จะตัดเหลือแค่ 2 หลักให้อัตโนมัติ
intword_th แสดงจำนวนเงินเป็นภาษาไทย
date_abbv_th 11 พ.ย. 2556
date_full_th 5 ธันวาคม 2556
month_abbv_th ม.ค. 2556
month_full_th กุมภาพันธ์ 2556
year_th 2556
system_status_display