Я делаю свою первую программу с помощью ReportLab, где я не знаю заранее, когда разрывы страницы упадут, и у меня возникнут проблемы. Чтобы все было просто, я использую SimpleDocTemplate
. Мои потоки выглядят примерно так:
flowables = [Paragraph("Some title", style=headerParagraphStyle),
Spacer(0, 10),
Paragraph("first paragraph", style=bodyParagraphStyle),
Paragraph("second paragraph", style=bodyParagraphStyle),
...
Paragraph("nth paragraph", style=bodyParagraphStyle),
PageBreak(),
Paragraph("Some title", style=headerParagraphStyle),
Spacer(0, 10),
Paragraph("first paragraph", style=bodyParagraphStyle),
Paragraph("second paragraph", style=bodyParagraphStyle),
...
Paragraph("mth paragraph", style=bodyParagraphStyle),
PageBreak(),
...]
Когда я строю свой PDF файл, все идет хорошо, пока мои n
или m
или хотя бы несколько абзацев тела подходят на одной странице, но если они будут работать, я получаю следующее сообщение:
reportlab.platypus.doctemplate.LayoutError: Flowable <Paragraph at 0xb79800 frame=normal>20th paragraph: too large on page 3
Кажется, не может найти вескую причину, почему это происходит со мной. Какие-либо предложения? Он делает это, даже если я удаляю PageBreaks(). Все абзацы относительно короткие, в основном меньше одного предложения/строки.
ETA: Я отправляю весь код (с незначительными идентификаторами), который генерирует ошибку для меня. Я конвертировал его для чтения CSV файла, поэтому я также разместил его содержимое. Точная ошибка, которую генерирует этот код для меня при запуске:
Traceback (most recent call last): File "./spice_dev.py", line 355, in departmentReportDoc.build(eachDepartment.report, onFirstPage=onReportPage, onLaterPages=onReportPage) File "/usr/local/lib/python2.6/site-packages/reportlab/platypus/doctemplate.py", line 1010, in build BaseDocTemplate.build(self,flowables, canvasmaker=canvasmaker) File "/usr/local/lib/python2.6/site-packages/reportlab/platypus/doctemplate.py", line 777, in build self.handle_flowable(flowables) File "/usr/local/lib/python2.6/site-packages/reportlab/platypus/doctemplate.py", line 694, in handle_flowable raise LayoutError(ident) reportlab.platypus.doctemplate.LayoutError: Flowable Additional comments and suggestions for improvement: too large on page 3
Некоторая работа по отладке показывает, что ошибка является результатом этого абзаца (хотя это может быть и другие текущие потоки, в зависимости от длины содержимого), пытающиеся разбить, откладываться и затем все еще не могут быть разделены после вызова handle_frameEnd()
. Предложения?
spice.py:
#!/usr/local/bin/python
# vim: set fileencoding=latin-1
#import csv
import os
import os.path
import time
import numpy
import scipy.stats._support
#for debugging
import sys
import traceback
# imports for reportlab
from reportlab.platypus import *
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.pagesizes import LETTER, landscape, portrait
from reportlab.lib.units import inch
from reportlab.lib import colors
from reportlab.pdfgen import canvas
from reportlab.lib.enums import *
##########################
####### Constants ########
##########################
kNumberOfQuestions = 4
##########################
####### Reportlab ########
##########################
def _doNothing(canv, doc):
pass
headerParagraphStyle = ParagraphStyle("Header", fontName="Helvetica-Bold", fontSize=16, spaceAfter = .05*inch, alignment=TA_CENTER)
header2ParagraphStyle = ParagraphStyle("Header2", fontName="Helvetica-Bold", fontSize=14, spaceAfter = .1*inch, spaceBefore=.5*inch, alignment=TA_CENTER)
subheaderInfoParagraphStyle = ParagraphStyle("Subheader Info", fontName="Helvetica-Bold", fontSize=10, alignment=TA_CENTER)
questionParagraphStyle = ParagraphStyle("Question header", fontName="Helvetica-Bold", fontSize = 10, alignment=TA_LEFT)
commentParagraphStyle = ParagraphStyle("Comment", fontName="Helvetica", fontSize = 10, alignment=TA_LEFT)
instructorParagraphStyle = ParagraphStyle("Instructor Header", fontName="Helvetica", fontSize=10, alignment=TA_LEFT)
basicTableStyle = TableStyle(
[('FONT', (0, 0), (-1, 0), 'Helvetica-Bold', 9),
('ALIGN', (0, 0), (-1, 0), 'CENTER'),
('LINEBELOW', (0, 0), (-1, 0), 1, colors.black),
('INNERGRID', (0, 0), (-1, 0), 1, colors.black),
('FONT', (0, 1), (-1, -1), 'Helvetica', 9),
('ALIGN', (0, 1), (-1, 1), 'LEFT'),
('ALIGN', (1, 1), (-1, -1), 'CENTER'),
('LINEAFTER', (0, 1), (0, -1), 1, colors.black),
('LINEBEFORE', (1, 1), (-1, -1), 1, colors.black),
('ROWBACKGROUNDS', (0, 1), (-1, -1), (colors.white, (.9, .9, .9))),
('TOPPADDING', (1, 1), (-1, -1), 8)
])
statTableStyle = TableStyle(
[('FONT', (0, 0), (0, -1), 'Helvetica-Bold', 9),
('ALIGN', (0, 0), (0, -1), 'RIGHT'),
('INNERGRID', (0, 0), (-1, -1), 1, colors.black),
('FONT', (1, 0), (1, -1), 'Helvetica', 9),
('ALIGN', (1, 0), (1, -1), 'LEFT')
])
headerTableStyle = TableStyle(
[('FONT', (0, 0), (-1, 0), 'Helvetica', 8),
('FONT', (0, 1), (-1, 1), 'Helvetica-Bold', 8),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('LINEBELOW', (0, 0), (-1, 0), 1, colors.black),
('LINEAFTER', (0, 0), (-1, -1), 20, colors.white),
('LEFTPADDING', (0,0), (-1, -1), 15),
('RIGHTPADDING', (0, 0), (-1, -1), 15)
])
##########################
######## Classes #########
##########################
class Course:
def __init__(self):
self.prefix = ""
self.number = ""
self.section = ""
self.instructor = ""
self.email = ""
self.name = "None Found"
self.enrollment = 0
self.semester = ""
self.report = [] # subreport for the course
self.dataFile = []
def __repr__(self):
return self.prefix + " " + self.number + " " + self.section + " " + self.instructor
class Instructor:
def __init__(self):
self.name = ""
self.email = ""
self.courses = dict([])
self.report = []
self.dataFile = []
def __repr__(self):
return self.name + " " + self.email
class Department:
def __init__(self):
self.name = ""
self.instructors = dict([]) #contains Instructor objects
self.report = [] #subreport for the department
self.dataFile = []
def __repr__(self):
return self.name
class College:
def __init__(self):
self.name = ""
self.departments = dict([]) #contains Department objects
self.report = [] #subreport for the college
self.dataFile = []
def __repr__(self):
return self.name + ":" + `self.departments`
class University:
def __init__(self):
self.name = ""
self.colleges = dict([]) #contains College objects
def __repr__(self):
return self.name + ":" + `self.colleges`
#########################
### Utility Functions ###
#########################
def onReportPage(canv, doc):
#display general info at the top of every page
canv.setFont('Courier', 10)
canv.drawString(inch, 10.5*inch, "Wassamata U")
canv.drawString(inch, 10.35*inch, "Student Comments")
canv.drawString(5.75*inch, 10.5*inch, "Year/Term: " + uSemesterYear + "/" + uSemesterTerm)
canv.drawString(5.75*inch, 10.35*inch, " Semester: " + `semesterNumber`)
def xmlify(text):
return text.replace('&','&').replace('<','<').replace('>','>')
def semesterDescription(semesterNumber):
# reference semester is Spring 1964, i.e. semester 0
semesterNumber = semesterNumber / 10
year = `1964 + (semesterNumber / 3)`
term = "Spring"
if semesterNumber % 3 == 1:
term = "Summer"
elif semesterNumber % 3 == 2:
term = "Fall"
return year, term
#########################
######### Main ##########
#########################
# set up some options
# should have a ui later
# ugly, but quick
# used to get them on the top of every page
global uSemesterYear
global uSemesterTerm
print "\n"
university = University()
university.name = "Wassamata U"
commentsFile = open("spireport2.csv", "rb").read()
commentRecords = commentsFile.split("ô\r\n")
commentsArray = []
for commentRecord in commentRecords:
commentsArray.append(commentRecord.split("æ"))
commentsArray.pop()
print "Reading in the SPI file"
#read in data from file
for row in commentsArray:
#print row
# college
currentCollege = university.colleges.get(row[0])
if currentCollege == None:
currentCollege = College()
currentCollege.name = row[0].replace("/", " ")
university.colleges[row[0]] = currentCollege
#department
currentDepartment = currentCollege.departments.get(row[1])
if currentDepartment == None:
currentDepartment = Department()
currentDepartment.name = row[1].replace("/", " ")
currentCollege.departments[row[1]] = currentDepartment
#instructor
currentInstructor = currentDepartment.instructors.get(row[2] + row[3])
if currentInstructor == None:
currentInstructor = Instructor()
currentInstructor.name = row[3].replace("/", " ")
currentInstructor.email = row[2].replace("/", " ")
currentDepartment.instructors[row[2] + row[3]] = currentInstructor
#course
currentCourse = currentInstructor.courses.get(row[5] + row[6] + row[7])
if currentCourse == None:
currentCourse = Course()
currentCourse.prefix = row[5][:3]
currentCourse.number = row[5][3:]
currentCourse.section = row[6]
currentCourse.instructor = row[3]
currentCourse.email = row[2]
currentCourse.name = row[4]
currentCourse.semester = row[7]
currentCourse.enrollment = int(row[8])
currentInstructor.courses[row[5] + row[6] + row[7]] = currentCourse
data = row[9:9+kNumberOfQuestions]
currentCollege.dataFile.append(data) #split the data file by college for later
currentInstructor.dataFile.append(data)
currentDepartment.dataFile.append(data)
currentCourse.dataFile.append(data)
semesterNumber = int(university.colleges.values()[0].departments.values()[0].instructors.values()[0].courses.values()[0].semester)
uSemesterYear, uSemesterTerm = semesterDescription(semesterNumber)
reportDocContent = []
print "Processing the SPI comments"
for eachCollege in university.colleges.values():
print "\tProcessing " + eachCollege.name
collegeReportStartingIndex = len(reportDocContent)
reportDocContent.append(Spacer(0, 100))
reportDocContent.append(Paragraph("Student comments for " + eachCollege.name, headerParagraphStyle))
reportDocContent.append(PageBreak())
for eachDepartment in eachCollege.departments.values():
print "\t\tProcessing " + eachDepartment.name
departmentReportStartingIndex = len(reportDocContent)
reportDocContent.append(Spacer(0, 100))
reportDocContent.append(Paragraph("Student comments for " + eachDepartment.name, headerParagraphStyle))
reportDocContent.append(PageBreak())
for eachInstructor in eachDepartment.instructors.values():
print "\t\t\tProcessing " + eachInstructor.name
instructorReportStartingIndex = len(reportDocContent)
reportDocContent.append(Spacer(0, 100))
reportDocContent.append(Paragraph("Student comments for " + eachInstructor.name + ", " + eachInstructor.email, headerParagraphStyle))
reportDocContent.append(PageBreak())
for eachCourse in eachInstructor.courses.values():
courseReportStartingIndex = len(reportDocContent)
reportDocContent.append(Paragraph("<para leftIndent=54><b>Instructor Name:</b> " + eachCourse.instructor + "</para>", instructorParagraphStyle))
reportDocContent.append(Spacer(0, 10))
headerTableContent1 = [[eachDepartment.name + "/" + eachCollege.name, eachCourse.prefix + eachCourse.number + eachCourse.section, eachCourse.name], ["Department/School", "Course-Section Number", "Course Name"]]
headerTableContent2 = [[eachCourse.enrollment if eachCourse.enrollment > 0 else "Unknown",
len(eachCourse.dataFile),
("%.2f" % (float(len(eachCourse.dataFile))/eachCourse.enrollment*100) if eachCourse.enrollment != 0 else "0.00")],
["Number of Students Enrolled", "Number Responding", "% of Response"]]
reportDocContent.append(Table(headerTableContent1, style=headerTableStyle))
reportDocContent.append(Spacer(0, 10))
reportDocContent.append(Table(headerTableContent2, style=headerTableStyle))
i = 0
for i in range(0, kNumberOfQuestions):
question = ""
if i == 0:
question = "The thing(s) I like the MOST about this course:"
elif i == 1:
question = "The thing(s) I like the LEAST about this course:"
elif i == 2:
question = "What is your reaction to the method of evaluating your mastery of the course (i.e., testing, grading, out of class assignments (term papers), instructor feedback, etc.):"
elif i == 3:
question = "Additional comments and suggestions for improvement:"
reportDocContent.append(Spacer(0, 10))
reportDocContent.append(Paragraph(question, style=questionParagraphStyle))
reportDocContent.append(Spacer(0, 5))
commentParagraph = ""
for comments in eachCourse.dataFile:
if comments[i] != "":
commentParagraph += unicode(comments[i], 'latin-1') + "<br/>"
reportDocContent.append(Paragraph(commentParagraph, style=commentParagraphStyle))
eachCourse.report = reportDocContent[courseReportStartingIndex:]
reportDocContent.append(PageBreak())
eachInstructor.report = reportDocContent[instructorReportStartingIndex:]
eachDepartment.report = reportDocContent[departmentReportStartingIndex:]
eachCollege.report = reportDocContent[collegeReportStartingIndex:]
# build directory structure to put reports in
for eachCollege in university.colleges.values():
if (not os.path.exists(eachCollege.name)):
os.mkdir(eachCollege.name)
for eachDepartment in eachCollege.departments.values():
if (not os.path.exists(os.path.join(eachCollege.name, eachDepartment.name))):
os.mkdir(os.path.join(eachCollege.name, eachDepartment.name))
for eachInstructor in eachDepartment.instructors.values():
if (not os.path.exists(os.path.join(eachCollege.name, eachDepartment.name, eachInstructor.name + " - " + eachInstructor.email))):
os.mkdir(os.path.join(eachCollege.name, eachDepartment.name, eachInstructor.name + " - " + eachInstructor.email))
print "Building Comments Report PDFs"
for eachCollege in university.colleges.values():
print "\tBuilding Comments Report for " + eachCollege.name
collegeReportDoc = SimpleDocTemplate(os.path.join(eachCollege.name, eachCollege.name + " SPI Comments Report.pdf"), pagesize=portrait(LETTER), allowSplitting=1)
collegeReportDoc.leftMargin = .25*inch
collegeReportDoc.rightMargin = .25*inch
collegeReportDoc.bottomMargin = .25*inch
collegeReportDoc.build(eachCollege.report, onFirstPage=onReportPage, onLaterPages=onReportPage)
for eachDepartment in eachCollege.departments.values():
print "\t\tBuilding Comments Report for " + eachDepartment.name
departmentReportDoc = SimpleDocTemplate(os.path.join(eachCollege.name, eachDepartment.name, eachDepartment.name + " SPI Comments Report.pdf"), pagesize=portrait(LETTER), allowSplitting=1)
departmentReportDoc.leftMargin = .25*inch
departmentReportDoc.rightMargin = .25*inch
departmentReportDoc.bottomMargin = .25*inch
# import pdb; pdb.set_trace()
departmentReportDoc.build(eachDepartment.report, onFirstPage=onReportPage, onLaterPages=onReportPage)
print "\t\t\tBuilding Comments Reports for individual instructors"
for eachInstructor in eachDepartment.instructors.values():
instructorReportDoc = SimpleDocTemplate(os.path.join(eachCollege.name, eachDepartment.name, eachInstructor.name + " - " + eachInstructor.email, eachInstructor.name + " SPI Comments Report.pdf"), pagesize=portrait(LETTER), allowSplitting=1)
instructorReportDoc.leftMargin = .25*inch
instructorReportDoc.rightMargin = .25*inch
instructorReportDoc.bottomMargin = .25*inch
instructorReportDoc.build(eachInstructor.report, onFirstPage=onReportPage, onLaterPages=onReportPage)
#we do this one last because it the biggest; otherwise it'd be at the beginning of the pdf report generation process
print "\tBuilding SPI Report for University"
reportDoc = SimpleDocTemplate("SPI Comments Report.pdf", pagesize=portrait(LETTER), allowSplitting=1)
reportDoc.leftMargin = .25*inch
reportDoc.rightMargin = .25*inch
reportDoc.bottomMargin = .25*inch
reportDoc.build(reportDocContent, onFirstPage=onReportPage, onLaterPages=onReportPage)
spireport2.csv:
College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æsample commentsææi like itæô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205æone with a line break; let see what happensæææweird charactèrs áÈ-{ô College of Health & Public AffæCriminal Justice/Legal Studiesæ[email protected]æAn InstructoræFOUNDATIONS OF LAW ENFORCEMENTæCJE5021æ0001æ1370æ 7ææææô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205ææææô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205ææææô College of Health & Public AffæHealth Info Mgmtæ[email protected]æAn InstructoræMEDICAL TERMINOLOGYæHSC3537æ0M01æ1370æ 205ææææô College of Arts & HumanitiesæEnglishæ[email protected]æAn InstructoræHARLEM, HAITI, AND HAVANAæAML3615æ0001æ1370æ 35ææææô College of Arts & HumanitiesæEnglishæ[email protected]æAn InstructoræCONT AMERICAN WOMEN S FICTIONæAML3283æ0001æ1370æ 35ææææô College of Arts & HumanitiesæEnglishæ[email protected]æAn InstructoræPOST-WORLD WAR II FICTIONæLIT4303æ0M01æ1370æ 32ææææô College of Arts & HumanitiesæEnglishæ[email protected]æAn InstructoræMAJOR AMERICAN AUTHORSæAML4300æ0001æ1370æ 33ææææô College of Arts & HumanitiesæEnglishæ[email protected]æAn InstructoræPRACTICAL CRITICISMæENG3010æ0001æ1370æ 36ææææô