def columnandcellformate(sheet_name,bold = 0,font_color = '#000000',bg_color = '#ffffff',align = '' ,bottom = 0 ,top = 3,right = 0,left = 0,font_size = 10 ,starcolumn = 0, endrow = 0 ):
global sheet_format
sheet_format=sheet_name.add_format({
'bottom':bottom,
'top' : top,
'bg_color':bg_color,
'font_color' : font_color,
'align':align,
'font_size':font_size,
'bold': bold,
'font_name':'Batang'
})
What is default value of top,bottom,right,left, My function is making cell top,bottom,right and left blank
I think your default background color may have been causing some issues with the cell borders. I've added a few conditions based on whether you want these called by your function or not. These conditions make use of Format Methods such as format.set_bg_color(), format.set_bottom() (see docs for more information on these). They only provide a background color if you change it from the default.
import xlsxwriter
def columnandcellformate(bold = 0, font_color = '#000000', bg_color = 'none', align = '' , bottom = 999, top = 999, right = 999, left = 999, font_size = 10):
global sheet_format
sheet_format=workbook.add_format({
'font_color' : font_color,
'align': align,
'font_size': font_size,
'bold': bold,
'font_name': 'Batang'
})
if bg_color != 'none':
sheet_format.set_bg_color(bg_color)
if bottom != 999:
sheet_format.set_bottom(bottom)
if top != 999:
sheet_format.set_top(top)
if right != 999:
sheet_format.set_right(right)
if left != 999:
sheet_format.set_left(left)
workbook = xlsxwriter.Workbook('test.xlsx')
ws = workbook.add_worksheet('test_1')
columnandcellformate()
ws.write('B1', 'foo', sheet_format)
columnandcellformate(bold = 1, font_color = '#9C0006', bg_color = '#FFC7CE', align = '', bottom = 2, top = 1, right = 1, left = 1, font_size = 10)
ws.write('B3', 'bar', sheet_format)
workbook.close()
Expected Output:
The default values for format properties are almost all 0/False. See the initialization code for a format object.
I am in the process of converting the database from pfaf.org (plant for a future) into a pdf book.
I have hit a bit of a stumbling block regarding pageTemplates.
Each plant may begin on a left or a right aligned page; and may potentially be two or more pages.
I have two templates for the first plant page (left and right) and also two more templates for the potential subsequent pages.
Currently this is handled as follows (for instance):
for i, plant in enumerate(plants):
#the first plant is printed on a right-hand page
if i % 2 == 0:
template = 'left'
second = 'right'
else:
template = 'right'
second = 'left'
Story.append(nextPageTemplate(template,'*', 'secondary_'+template, 'secondary_'+second))
Story.append(PageBreak())
#append various paragraphs, jumping between frames, etc...
The code (as you can probably tell) works fine for single page plants.
It also works (semi) as expected for multi page plants.
However, as you can also probably see, a two (or four,etc) page plant will break the template arrangement, because the above code assumes page position based upon plant number, rather than page number.
I can't see a solution for this in the location of the code above (i.e during the Story.append cycle) - as at that point I can not tell whether the plant has used more than one page, and thus what page I am currently on, as such.
I hoped that I could perhaps tweak the nextPageTemplate structure from my custom docTemplate, but I can't figure out if this is possible.
Is it? Or is there another solution? Would really appreciate any help. Have been reading all over, but the best examples I can find don't quite cover this scenario.
Any questions, please ask.
Thank you
Thank you, Nitzle:
The trouble is that I don't know how many pages each plant will take up.
For instance - a new plant starts on an odd page so I give it a cycle of templates ('right', '*', 'secondaryLeft', 'secondaryRight'). [the secondary pages are just a single frame with appropriate margin.]
If that plant is one page long, no problem, the next plant will have the template cycle opposite to the above.
However, if the plant has, say, two pages, it will cause the following plant to fall again on an odd page again and thus the template cycle should not change... I hope this makes sense.
This is the circumstance I am having trouble solving... If I do as you say, it doesn't allow for multiple page plants. Most of my code is as follows; I have tried to slim it down a little though :) hopefully it still contains all relevant stuff and not too much unnecessary.
import os
import sys
import MySQLdb
from reportlab.platypus import Spacer, Image, Table, TableStyle, PageBreak, FrameBreak, paraparser
from reportlab.platypus.doctemplate import BaseDocTemplate, PageTemplate, NextPageTemplate, _doNothing
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.platypus.frames import Frame
from reportlab.platypus.flowables import KeepInFrame
from reportlab.platypus.paragraph import Paragraph
from reportlab.lib.units import mm, cm
from reportlab.lib.pagesizes import A4, A5
from reportlab.lib.enums import TA_JUSTIFY, TA_CENTER, TA_RIGHT
from reportlab.lib.styles import StyleSheet1, ParagraphStyle as PS
from reportlab.lib import colors
from reportlab.graphics.shapes import Drawing, Rect, String
from reportlab.pdfbase.pdfmetrics import registerFont, stringWidth
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.rl_config import warnOnMissingFontGlyphs
warnOnMissingFontGlyphs = 0
registerFont(TTFont('Museo_', '/home/wuwei/.fonts/Museo300-Regular.ttf'))
registerFont(TTFont('Museo_M', '/home/wuwei/.fonts/Museo500-Regular.ttf'))
registerFont(TTFont('Trebuchet', '/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS.ttf'))
registerFont(TTFont('Trebuchet_I', '/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS_Italic.ttf'))
## SOME VARIABLE DEFINITIONS ##
titleFont = "Museo_M"
subtitleFont = "Museo_"
stdFont = "Trebuchet"
stdItalic = "Trebuchet_I"
#stdSize = 14
"""CREATE GLOBALS"""
_w, _h = A4
_head_w = 17.5*cm
_head_pad = 0.2*cm
_main_w = 17.2*cm
_budge = 0.3*cm
_left_margin = 1.5*cm
_right_margin = 2.0*cm
_top_margin = 1.5*cm
_bottom_margin = 2.0*cm
_latinFontS = 18
#reset superFraction to style 'common name' placement
paraparser.superFraction = 0.15
paraparser.sizeDelta = 0
###########################################################################################################
######################################### ###############################################
######################################## DB FUNCTIONS #############################################
######################################### ###############################################
###########################################################################################################
def connectToDB():
try:
connection = MySQLdb.connect (host = "localhost",
user = "root",
passwd = "****************",
db = "pfaf")
except MySQLdb.Error, e:
print "I guess, either you don't have a local copy of the pfaf db"
print "or something is wrong with your connection details."
print "Error %d: %s" % (e.args[0], e.args[1])
sys.exit (1)
return connection
def close(item, exit=0):
#used to close both database cursors and connections
item.close()
if exit == 1:
sys.exit (0)
def runQuery(q, conn):
results = conn.cursor(MySQLdb.cursors.DictCursor)
results.execute (q)
return results
def Fetch(results, fetchAll=0):
if fetchAll:
print "fetchAll"
# FETCHALL option:
rows = results.fetchall()
#cursor.close()
#conn.close()
'''for row in rows:
print "%s, %s" % (row["Latin Name"], row["Hardyness"])
print "%d rows were returned" % results.rowcount'''
return rows
else:
# FETCHONE option:
##--- Print some debug info to command line ---##
print "Latin Name - Common Name - Hardyness"
while (1):
row = results.fetchone()
if row == None:
break
latin_name = row["Latin Name"]
common_name = row["Common name"]
hardyness = row["Hardyness"]
family = row["Family"]
synonyms = row["Synonyms"]
##--- Print some more useful debug info to command line ---##
print "%s - %s - %s" % (latin_name, common_name, hardyness)
print row
if results.rowcount != 1:
print "%d rows were returned" % results.rowcount
else:
print "%d row was returned" % results.rowcount
return row
###########################################################################################################
######################################### ###############################################
######################################## STORY PROCESSING #############################################
######################################### ###############################################
###########################################################################################################
def drawBorders(canv, side):
canv.saveState()
d = Drawing(0,0)
#header border#
r = Rect( side-_budge, _h-(2.4*cm), _head_w+(_budge*2), 1.2*cm, rx=5, ry=5 )
r.strokeColor = colors.black
r.fillColor = colors.white
r.strokeWidth = 1.5
d.add(r)
#hardyness border#
rad = 5
hWidth = 1.4*cm
if side == _left_margin:
hPos = -rad
else:
hPos = _w - hWidth + rad
r = Rect( hPos, _h-(3.8*cm), hWidth, 1.2*cm, rx=rad, ry=rad )
r.strokeColor = colors.black
r.fillColor = colors.white
r.strokeWidth = 1.5
d.add(r)
d.drawOn(canv, 0, 0)
canv.restoreState()
def drawFooter(canv, doc):
canv.saveState()
canv.setFont(stdFont,10)
canv.drawCentredString((_w/2.0), 1.5*cm, "%d - %s" % (doc.page, doc.latinName))
canv.restoreState()
class LeftPageTemplate(PageTemplate):
def __init__(self):
#allow a bigger margin on the right for binding
latinF = Frame(_left_margin, 27.5*cm, _head_w, 0.8*cm, id='latinL', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
hardyF = Frame(0.1*cm, 26.05*cm, cm, cm, id='hardL', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
synF = Frame(_left_margin, 26.65*cm, _main_w, 0.55*cm, id='synL', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
otherF = Frame(_left_margin, 22.1*cm, 12.4*cm, 4.5*cm, id='otherL', showBoundary=1)
calF = Frame(14.2*cm, 22.1*cm, 4.5*cm, 4.5*cm, id='calL', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
flowF = Frame(_left_margin, 2.0*cm, _main_w, 19.85*cm, id='flowL', showBoundary=1)
PageTemplate.__init__(self,
id='left',
frames=[latinF, hardyF, synF, otherF, calF, flowF],
pagesize=A4)
def beforeDrawPage(self, canv, doc):
drawBorders(canv, _left_margin)
def afterDrawPage(self, canv, doc):
drawFooter(canv, doc)
class RightPageTemplate(PageTemplate):
def __init__(self):
#allow a bigger margin on the left for binding
latinF = Frame(_right_margin, 27.5*cm, _head_w, 0.8*cm, id='latinR', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
hardyF = Frame(_w-1.1*cm, 26.05*cm, cm, cm, id='hardR', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
synF = Frame(_right_margin+_budge, 26.65*cm, _main_w, 0.55*cm, id='synR', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
calF = Frame(_right_margin+_budge, 22.1*cm, 4.5*cm, 4.5*cm, id='calR', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
otherF = Frame(_right_margin+5.1*cm, 22.1*cm, 12.4*cm, 4.5*cm, id='otherR', showBoundary=1)
flowF = Frame(_right_margin+_budge, 2.0*cm, _main_w, 19.85*cm, id='flowR', showBoundary=1)
PageTemplate.__init__(self,
id='right',
frames=[latinF, hardyF, synF, otherF, calF, flowF],
pagesize=A4)
def beforeDrawPage(self, canv, doc):
drawBorders(canv, _right_margin)
def afterDrawPage(self, canv, doc):
drawFooter(canv, doc)
class MyDocTemplate(BaseDocTemplate):
_invalidInitArgs = ('pageTemplates',)
def __init__(self, filename, **kw):
self.allowSplitting = 0
BaseDocTemplate.__init__(self, filename, **kw)
self.latinName = "(none initially)"
self.latinWidth = 0 #(none initially)
def afterInit(self):
self._calc() #in case we have changed margin sizes etc
self.leftMargin = _left_margin
self.rightMargin = _right_margin
self.topMargin = _top_margin
self.bottomMargin = _bottom_margin
self.width = _w - self.leftMargin - self.rightMargin
self.height = _h - self.topMargin - self.bottomMargin
frameStd = Frame(cm, self.bottomMargin, (_w - 2*cm), (_h - 3*cm), id='cvr', showBoundary=0)
frameToC = Frame(self.rightMargin, self.bottomMargin, self.width, self.height, id='tocFrame', showBoundary=0)
frameL = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='secLeftFrame', showBoundary=1)
frameR = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='secRightFrame', showBoundary=1)
self.addPageTemplates( [PageTemplate(id='Cover', frames=frameStd, onPage=coverPage, pagesize=self.pagesize),
PageTemplate(id='ToC', frames=frameToC, onPage=tocPage, pagesize=self.pagesize),
PageTemplate(id='blank', frames=frameStd, onPage=_doNothing, pagesize=self.pagesize),
LeftPageTemplate(),
RightPageTemplate(),
PageTemplate(id='secondary_left', frames=frameL, onPage=_doNothing, pagesize=self.pagesize),
PageTemplate(id='secondary_right', frames=frameR, onPage=_doNothing, pagesize=self.pagesize)
] )
def afterFlowable(self, flowable):
"""Registers ToC entries - and captures latin name for footer"""
if isinstance(flowable, Paragraph):
style = flowable.style.name
key = None
firstWord = style.split('_',1)[0]
if (style == 'LatinName') or (style == 'LatinNameR') or (firstWord == 'LatinName'):
level = 0
key = 'latin-%s' % self.seq.nextf('LatinName')
self.canv.bookmarkPage(key)
wholeStr = flowable.getPlainText()
if self.page % 2 == 0: #even numbers are on left pages
latinOnly = wholeStr.split('\xc2\xa0\xc2\xa0')[0] #looks for '  ' as divider
else:
latinOnly = wholeStr.split('\xc2\xa0\xc2\xa0')[1]
self.latinName = latinOnly
E = [level, latinOnly, self.page]
if key is not None: E.append(key)
self.notify('TOCEntry', tuple(E))
'''elif (style == 'CommonName'):
self.commonName = flowable.getPlainText()
self.commonWidth = stringWidth(self.commonName, styles['common'].fontName, styles['common'].fontSize)'''
else:
return
""" coverPage and otherPages are intended for non-flowing (i.e standard) parts of the pages """
def coverPage(canvas, doc):
Title = "Plants for a Future"
pageinfo = "The full database collected as a printable book"
canvas.setTitle(Title + " : " + pageinfo)
print "creating cover page..."
canvas.saveState()
d = Drawing(0,0)
r = Rect( 0, 0, 12*cm, 4*cm, rx=5, ry=5 )
r.strokeColor = colors.black
r.fillColor = colors.white
r.strokeWidth = 3
d.add(r)
d.drawOn(canvas, (_w/2.0)-6*cm, _h-(6.2*cm))
canvas.setFont(stdFont, 30)
canvas.drawCentredString(_w/2.0, _h-108, Title)
canvas.setFont(stdItalic, 14)
canvas.drawCentredString(_w/2.0, _h-150, pageinfo)
canvas.restoreState()
def tocPage(canvas, doc):
canvas.saveState()
canvas.setFont(stdFont,10)
canvas.drawCentredString((_w/2.0), 1.5*cm, "Table of Contents")
canvas.restoreState()
def getMedicinal(plant):
p = plant
initial = p["Medicinal"]
return initial
""" Run after 'Story' has been fully populated """
def go():
doc = MyDocTemplate('result01.pdf')
passes = doc.multiBuild(Story)
########################################################################
"""Build StyleSheet"""
styles = buildStyle()
h1 = PS(name = 'HeadingOne',
fontName = stdFont,
fontSize = 14,
leading = 16)
h2 = PS(name = 'HeadingTwo',
fontName = stdFont,
fontSize = 12,
leading = 14,
leftIndent = 1*cm)
Story=[]
a = Story.append
a(NextPageTemplate('blank'))
a(PageBreak())
a(NextPageTemplate('ToC'))
a(PageBreak())
toc = TableOfContents()
toc.levelStyles = [ h1, h2 ]
a(toc)
a(NextPageTemplate('blank'))
a(PageBreak())
"""###LEFT PAGES SHOULD BE STYLED RIGHT-ALIGNED, AND RIGHT PAGES LEFT-ALIGNED###"""
#print type(plants)
for i, plant in enumerate(plants):
### THIS INITIAL CHECK BREAKS AS IT NEEDS TO BE BASED ON PAGE NUMBER, NOT PLANT NUMBER!!! ###
if i %2 == 0: #IF FIRST PLANT APPEARS ON A RIGHTSIDE PAGE, ELSE REVERSE THE R and L
page='R'
template = 'right'
second = 'left'
else:
page='L'
template ='left'
second = 'right'
#FIRST THINGS FIRST:
#Make sure the page templates flow nicely for each plant "chapter"
a(NextPageTemplate([template, '*', ('secondary_'+template), ('secondary_'+second) ]))
a(PageBreak())
'''CAPTURE PLANT INFO IN OBJECTS'''
p = plant
'''for info in plant:
print info, p[info]'''
'''Header'''
latin = p["Latin Name"]
common = p["Common name"]
family = p["Family"]
syn = p["Synonyms"]
"""X. congestum. (Lour.)Merrill. X. racemosum. Miq. Apactis japonica. Croton congestum.
Flacourtia japonica. Walp. Hisingera japonica. H. racemosa."""
hardy = str(p["Hardyness"])
'''Basic Info'''
author = p["Author"]
botanicalrefs = p["Botanical references"]
width = p["Width"]
height = p["Height"]
habit = p["Habit"]
planttype = clean("Deciduous/Evergreen", p)
plantrange = p["Range"]
habitat = p["Habitat"]
soil = clean("Soil", plant)
shade = p["Shade"]
moisture = p["Moisture"]
drained = p["Well-drained"]
nf = p["Nitrogen fixer"]
pH = p["pH"]
acid = p["Acid"]
alkaline = p["Alkaline"]
saline = p["Saline"]
wind = p["Wind"]
rate = clean("Growth rate", plant)
pollution = p["Pollution"]
poorsoil = p["Poor soil"]
drought = p["Drought"]
heavyclay = p["Heavy clay"]
tender = clean("FrostTender", plant)
inleaf = p["In leaf"]
flowering = p["Flowering time"]
seedripens = p["Seed ripens"]
flowertype = p["Flower Type"]
pollinators = p["Pollinators"]
selffertile = clean("Self-fertile", plant)
hazards = p["Known hazards"]
rating_edible = p["Rating"]
rating_med = p["Medicinal Rating"]
edibleuses = p["Edible uses"]
medicinaluses = getMedicinal(plant)
otheruses = p["Uses notes"]
#the following encoding allows for special characters such as degree symbol
cultivation = unicode(p["Cultivation details"], 'latin-1')#'ISO-8859-1')
propagation = p["Propagation 1"]
scented = p["Scented"] #boolean - requires further lookup in `ScentedPlants` table
string = '''%s is %s %s growing to %gm by %gm at a %s rate.<br/>
It's habitats are %s <br/><br/> Range: %s
<br/><br/>
Suitable for %s soils. <br/><br/>
Shade: %s, Moisture: %s <br/>
Well-drained: %d, Nitrogen fixer: %d <br/> ph: %s <br/>
Acid: %d, Alkaline: %d, Saline: %d <br/>
Wind: %s
<br/><br/>
Author: %s <br/> Botanical References: %s''' % (
latin, planttype, habit.lower(), width, height, rate,
habitat[0].lower()+habitat[1:], plantrange,
soil, shade, moisture, drained,
nf, pH, acid, alkaline, saline, wind, author, botanicalrefs )
string = unicode(string, 'latin-1')
latinW = stringWidth(latin, styles['latin'].fontName, styles['latin'].fontSize)
commonW = stringWidth(common, styles['common'].fontName, styles['common'].fontSize)
if (latinW + commonW + (_head_pad*3)) > _head_w:
styleName = "LatinName_" + str(i)
latinStyle = PS( name=styleName,
parent=styles['Normal'],
fontName=titleFont,
fontSize=_latinFontS,
leading=22,
spaceAfter=0)
j = 1
#should the latin string be too long, attempt to shrink until it fits
while (latinW + commonW + (_head_pad*3)) > _head_w:
#change the font size until ok...
latinStyle.fontSize = _latinFontS -j
latinW = stringWidth(latin, latinStyle.fontName, latinStyle.fontSize)
j += 0.2
else:
latinStyle = styles['LatinName']
if page == 'L':
headerText = '''<para align="left">
%s
<font face="%s" size="%d"> <super>%s</super></font>
</para>''' % (latin, subtitleFont, 12, common)
else:
headerText = '''<para align="right">
<font face="%s" size="%d"><super>%s</super> </font>
%s
</para>''' % (subtitleFont, 12, common, latin)
latinPara = Paragraph(headerText, latinStyle)
a(FrameBreak('latin'+page))
a(latinPara)
a(FrameBreak('syn'+page))
a(KeepInFrame(_main_w, 1.5*cm,
[Paragraph(syn, styles['syn'+page])],
mode="shrink")) #can be shrink, truncate or overflow
a(FrameBreak('hard'+page))
a(Paragraph(hardy, styles['hardy']))
a(FrameBreak('cal'+page))
#SHALL BE ULTIMATELY POPULATED VIA DATABASE#
greyOut = [ [0,0,1,1,1,1,1,0,0,0,0,0], [0,0,0,0,0,1,1,1,1,0,0,0], [0,0,0,0,0,0,0,0,1,1,1,0] ]
cal = drawCalendar(greyOut)
a(cal)
a(FrameBreak('flow'+page))
a(Paragraph(string, styles['Normal']))
a(Paragraph("Edible Uses", styles['title']))
a(Paragraph("Medicinal Uses", styles['title']))
a(Paragraph("Other Uses", styles['title']))
a(Paragraph("Cultivation", styles['title']))
a(Paragraph(cultivation, styles['Normal']))
a(Paragraph("Propagation", styles['title']))
a(Paragraph(propagation, styles['Normal']))
##ASSEMBLE PDF###
go()
If you're just switching between a "left" and a "right" template, you could try using the _handle_nextPageTemplate method of the BaseDocTemplate class. One way to keep track of page number would be to use the afterPage hook to increment a page number.
from reportlab.platypus import BaseDocTemplate
class MyDocTemplate(BaseDocTemplate):
def __init__(self, *args, **kwargs):
BaseDocTemplate.__init__(self, *args, **kwargs)
self.__pageNum = 1
def afterPage(self):
"""Called after all flowables have been drawn on a page"""
# Increment pageNum since the page has been completed
self.__pageNum += 1
# If the page number is even, force "left-side" template
if self.__pageNum % 2 == 0:
self._handle_nextPageTemplate('left_template')
else:
self._handle_nextPageTemplate('right_template')
I haven't tested the code above, but you may need to use beforePage instead depending on how it checks page template order.