Ivan Holmes
5 years ago
11 changed files with 582 additions and 514 deletions
-
61chordsheet/components/chordprogression.py
-
56chordsheet/components/guitarchart.py
-
4chordsheet/components/header.py
-
6chordsheet/document.py
-
49chordsheet/parsers.py
-
48chordsheet/primitives.py
-
415chordsheet/render.py
-
28chordsheet/tableView.py
-
2examples/ah.xml
-
289gui.py
-
12test.py
@ -1,61 +0,0 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
from reportlab.pdfgen import canvas |
|||
from reportlab.lib.units import mm |
|||
from reportlab.lib.pagesizes import A4 |
|||
from reportlab.pdfbase import pdfmetrics |
|||
from reportlab.pdfbase.ttfonts import TTFont |
|||
from reportlab.graphics.shapes import * |
|||
|
|||
from graphics.primitives import * |
|||
|
|||
class ChordProgression: |
|||
def __init__(self, currentCanvas, blockList, **kwargs): |
|||
self.currentCanvas = currentCanvas |
|||
|
|||
self.beatsHeight = kwargs.get('beatsHeight', 5*mm) |
|||
self.timeFontSize = kwargs.get('timeFontSize', 12) |
|||
self.chordNameFontSize = kwargs.get('chordNameFontSize', 18) |
|||
self.notesFontSize = kwargs.get('notesFontSize', 12) |
|||
self.unitWidth = kwargs.get('unitWidth', 10*mm) |
|||
self.unitHeight = kwargs.get('unitHeight', 20*mm) |
|||
|
|||
self.timeSignature = kwargs.get('timeSignature', 4) |
|||
def draw(self): |
|||
global cur_pos, margin, pagesize |
|||
writeText(self.currentCanvas, "Chord progression", size=18, align="left") |
|||
|
|||
v_origin = cur_pos + 2*mm + self.beatsHeight |
|||
h_origin = margin |
|||
|
|||
h_loc = 0 |
|||
v_loc = 0 |
|||
|
|||
maxWidth = int((((pagesize[0]-(2*margin))/self.unitWidth)//(self.timeSignature*2))*(self.timeSignature*2)) # use integer division to round maxWidth to nearest multiple of time signature |
|||
|
|||
for u in range(maxWidth+1): |
|||
s = 0 |
|||
x = u*self.unitWidth+margin |
|||
if u % self.timeSignature == 0: |
|||
e = -self.beatsHeight |
|||
else: |
|||
e = -self.beatsHeight/2 |
|||
drawVertLine(self.currentCanvas, s, e, x, h_origin, v_origin) |
|||
if u == maxWidth: # Avoid writing beat number after the final line |
|||
break |
|||
writeText(str((u % self.timeSignature)+1),size=self.timeFontSize, hpos=x+self.unitWidth/2, vpos=v_origin-self.beatsHeight) |
|||
|
|||
blockList = parseBlockList(self.blockList, maxWidth) |
|||
|
|||
for b in blockList: |
|||
if h_loc == maxWidth: |
|||
v_loc += 1 |
|||
h_loc = 0 |
|||
currentCanvas.rect(h_origin+(h_loc*self.unitWidth), v_origin+(v_loc*self.unitHeight), b[0]*self.unitWidth, self.unitHeight) |
|||
if b[2]: |
|||
writeText(currentCanvas, b[2], size=self.notesFontSize, hpos=h_origin+((h_loc+b[0]/2)*self.unitWidth), vpos=v_origin+((v_loc+1)*self.unitHeight)-(1.3*self.notesFontSize)) |
|||
v_offset = (v_loc*self.unitHeight+self.unitHeight/2)-self.chordNameFontSize/2 |
|||
writeText(currentCanvas, parseName(b[1]), size=self.chordNameFontSize, hpos=h_origin+((h_loc+b[0]/2)*self.unitWidth), vpos=v_origin+v_offset) |
|||
h_loc += b[0] |
|||
|
|||
cur_pos = v_origin+(v_loc+1)*self.unitHeight+self.beatsHeight |
@ -1,56 +0,0 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
from reportlab.pdfgen import canvas |
|||
from reportlab.lib.units import mm |
|||
from reportlab.lib.pagesizes import A4 |
|||
from reportlab.pdfbase import pdfmetrics |
|||
from reportlab.pdfbase.ttfonts import TTFont |
|||
from reportlab.graphics.shapes import * |
|||
|
|||
from graphics.primitives import * |
|||
|
|||
string_hz_sp = 20*mm |
|||
string_hz_gap = 2*mm |
|||
string_height = 5*mm |
|||
|
|||
def guitarChart(currentCanvas, string_hz_sp, string_hz_gap, string_height): |
|||
global cur_pos, margin, pagesize |
|||
|
|||
writeText("Guitar chord voicings", size=18, align="left") |
|||
|
|||
chartmargin = 15*mm |
|||
v_origin = cur_pos + 2*mm |
|||
h_origin = margin + chartmargin |
|||
nstrings = 6 |
|||
fontsize = 12 |
|||
|
|||
guitarChordList = [[chordList[q].guitar[r] for q in range(len(chordList)) if hasattr(chordList[q], 'guitar')] for r in range(6)] |
|||
guitarChordList.append([chordList[q].name for q in range(len(chordList))]) |
|||
|
|||
for i in range(nstrings+1): # i is the string currently being drawn |
|||
writeText(['e','B','G','D','A','E','Name'][i], size=fontsize, hpos=(h_origin), vpos=v_origin+(i*string_height), align='right') |
|||
|
|||
for j in range(len(ls.chords)): # j is which chord (0 is first chord, 1 is 2nd etc) |
|||
if j == 0: |
|||
charpos = string_hz_sp/2 |
|||
s = string_hz_gap |
|||
e = charpos-((c.stringWidth(chordList[i][j])/2)+string_hz_gap) |
|||
y = v_origin+(string_height*i)+string_height/2 |
|||
drawHorizLine(currentCanvas, s, e, y, h_origin, v_origin) |
|||
else: |
|||
charpos = string_hz_sp*(j+0.5) |
|||
s = charpos-string_hz_sp+(lastWidth/2+string_hz_gap) |
|||
e = charpos-((currentCanvas.stringWidth(chordList[i][j])/2)+string_hz_gap) |
|||
y = v_origin+(string_height*i)+string_height/2 |
|||
drawHorizLine(currentCanvas, s, e, y, h_origin, v_origin) |
|||
if j == len(ls.chords)-1: |
|||
s = charpos+(currentCanvas.stringWidth(chordList[i][j])/2+string_hz_gap) |
|||
e = charpos+string_hz_sp/2 |
|||
y = v_origin+(string_height*i)+string_height/2 |
|||
drawHorizLine(currentCanvas, s, e, y, h_origin, v_origin) |
|||
|
|||
writeText(chordList[i][j], size=fontsize, hpos=h_origin+charpos, vpos=v_origin+(i*string_height)) |
|||
|
|||
lastWidth = currentCanvas.stringWidth(chordList[i][j]) |
|||
|
|||
cur_pos += (string_height*(nstrings+2)) |
@ -1,4 +0,0 @@ |
|||
def header(): |
|||
writeText(ls.title.cdata, size=24) |
|||
writeText("Composer: {c}".format(c = ls.composer.cdata), size=12) |
|||
writeText("Arranger: {a}".format(a = ls.arranger.cdata), size=12) |
@ -1,30 +1,33 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
|
|||
def parseFingering(fingering, instrument): |
|||
""" |
|||
Converts fingerings into the list format that Chord objects understand. |
|||
""" |
|||
if instrument == 'guitar': |
|||
numStrings = 6 |
|||
if len(fingering) == numStrings: # if the fingering is entered in concise format e.g. xx4455 |
|||
output = list(fingering) |
|||
else: # if entered in long format e.g. x,x,10,10,11,11 |
|||
output = [x for x in fingering.split(',')] |
|||
if len(output) == numStrings: |
|||
return output |
|||
else: |
|||
raise Exception("Voicing <{}> is malformed.".format(fingering)) |
|||
else: |
|||
return [fingering] |
|||
""" |
|||
Converts fingerings into the list format that Chord objects understand. |
|||
""" |
|||
if instrument == 'guitar': |
|||
numStrings = 6 |
|||
if len(fingering) == numStrings: # if the fingering is entered in concise format e.g. xx4455 |
|||
output = list(fingering) |
|||
else: # if entered in long format e.g. x,x,10,10,11,11 |
|||
output = [x for x in fingering.split(',')] |
|||
if len(output) == numStrings: |
|||
return output |
|||
else: |
|||
raise Exception("Voicing <{}> is malformed.".format(fingering)) |
|||
else: |
|||
return [fingering] |
|||
|
|||
|
|||
# dictionary holding text to be replaced in chord names |
|||
nameReplacements = { "b":"♭", "#":"♯" } |
|||
nameReplacements = {"b": "♭", "#": "♯"} |
|||
|
|||
|
|||
def parseName(chordName): |
|||
""" |
|||
Replaces symbols in chord names. |
|||
""" |
|||
parsedName = chordName |
|||
for i, j in nameReplacements.items(): |
|||
parsedName = parsedName.replace(i, j) |
|||
return parsedName |
|||
""" |
|||
Replaces symbols in chord names. |
|||
""" |
|||
parsedName = chordName |
|||
for i, j in nameReplacements.items(): |
|||
parsedName = parsedName.replace(i, j) |
|||
return parsedName |
@ -1,48 +0,0 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
from reportlab.pdfgen import canvas |
|||
from reportlab.lib.units import mm |
|||
from reportlab.graphics.shapes import * |
|||
|
|||
def writeText(currentCanvas, style, string, size, vpos, **kwargs): |
|||
""" |
|||
Wrapper function to conveniently write text and return how much vertical space it took up. |
|||
""" |
|||
margin = style.leftMargin*style.unit |
|||
|
|||
align = kwargs.get('align', 'centre') |
|||
if align == 'centre' or align == 'center': |
|||
hpos = kwargs.get('hpos', style.pageSize[0]/2) |
|||
elif align == 'left': |
|||
hpos = kwargs.get('hpos', margin) |
|||
elif align == 'right': |
|||
hpos = kwargs.get('hpos', style.pageSize[0]-margin) |
|||
spacing = kwargs.get('spacing', style.lineSpacing) |
|||
|
|||
currentCanvas.setFont(style.font, size) |
|||
|
|||
if align == 'centre' or align == 'center': |
|||
currentCanvas.drawCentredString(hpos, vpos+(0.75*size*spacing),string) |
|||
elif align == 'left': |
|||
currentCanvas.drawString(hpos, vpos+(0.75*size*spacing),string) |
|||
elif align == 'right': |
|||
currentCanvas.drawString(hpos-currentCanvas.stringWidth(string), vpos+(0.75*size*spacing),string) |
|||
|
|||
return size*style.lineSpacing |
|||
|
|||
def drawHorizLine(currentCanvas, startpoint, endpoint, v_pos, h_origin, v_origin): |
|||
""" |
|||
Draw a horizontal line on the canvas taking origin point into account. |
|||
""" |
|||
x1 = h_origin+startpoint |
|||
x2 = h_origin+endpoint |
|||
currentCanvas.line(x1, v_pos, x2, v_pos) |
|||
|
|||
def drawVertLine(currentCanvas, startpoint, endpoint, h_pos, h_origin, v_origin): |
|||
""" |
|||
Draw a vertical line on the canvas taking origin point into account. |
|||
""" |
|||
y1 = v_origin+startpoint |
|||
y2 = v_origin+endpoint |
|||
currentCanvas.line(h_pos, y1, h_pos, y2) |
|||
|
@ -1,171 +1,290 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
from math import trunc |
|||
|
|||
from reportlab.pdfgen import canvas |
|||
from reportlab.lib.units import mm |
|||
from reportlab.graphics.shapes import * |
|||
from chordsheet.primitives import writeText, drawVertLine, drawHorizLine |
|||
from reportlab.lib.styles import getSampleStyleSheet |
|||
from reportlab.platypus import BaseDocTemplate, Spacer, Paragraph, Flowable, Frame, PageTemplate |
|||
|
|||
from chordsheet.document import Block |
|||
|
|||
|
|||
def writeText(canvas, style, string, size, vpos, width, **kwargs): |
|||
""" |
|||
Wrapper function to conveniently write text and return how much vertical space it took up. |
|||
""" |
|||
|
|||
align = kwargs.get('align', 'centre') |
|||
if align == 'centre' or align == 'center': |
|||
hpos = kwargs.get('hpos', width/2) |
|||
elif align == 'left': |
|||
hpos = kwargs.get('hpos', 0) |
|||
elif align == 'right': |
|||
hpos = kwargs.get('hpos', width) |
|||
spacing = kwargs.get('spacing', style.lineSpacing) |
|||
|
|||
canvas.setFont(style.font, size) |
|||
|
|||
if align == 'centre' or align == 'center': |
|||
canvas.drawCentredString(hpos, vpos-(0.75*size*spacing), string) |
|||
elif align == 'left': |
|||
canvas.drawString(hpos, vpos-(0.75*size*spacing), string) |
|||
elif align == 'right': |
|||
canvas.drawString(hpos-canvas.stringWidth(string), |
|||
vpos-(0.75*size*spacing), string) |
|||
|
|||
return size*style.lineSpacing |
|||
|
|||
|
|||
def splitBlocks(blockList, maxWidth): |
|||
h_loc = 0 |
|||
splitBlockList = [] |
|||
for i in range(len(blockList)): |
|||
c_orig = blockList[i].chord |
|||
n_orig = blockList[i].notes |
|||
if h_loc == maxWidth: |
|||
h_loc = 0 |
|||
if h_loc+blockList[i].length > maxWidth: |
|||
lengthList = [maxWidth - h_loc] |
|||
while sum(lengthList) < blockList[i].length: |
|||
if blockList[i].length - sum(lengthList) >= maxWidth: |
|||
lengthList.append(maxWidth) |
|||
else: |
|||
lengthList.append(blockList[i].length - sum(lengthList)) |
|||
|
|||
for l in lengthList: |
|||
splitBlockList.append(Block(l, chord=c_orig, notes=n_orig)) # create a block with the given length |
|||
|
|||
h_loc = lengthList[-1] |
|||
else: |
|||
splitBlockList.append(blockList[i]) |
|||
h_loc += blockList[i].length |
|||
return splitBlockList |
|||
|
|||
def guitarChart(currentCanvas, style, chordList, cur_pos): |
|||
title_height = writeText(currentCanvas, style, "Guitar chord voicings", 18, cur_pos, align="left") |
|||
cur_pos += title_height |
|||
|
|||
string_hz_sp = style.stringHzSp |
|||
string_hz_gap = style.stringHzGap |
|||
string_height = style.stringHeight |
|||
|
|||
margin = style.leftMargin*style.unit |
|||
pagesize = style.pageSize |
|||
|
|||
chartmargin = 15*mm |
|||
v_origin = cur_pos + 2*mm |
|||
h_origin = margin + chartmargin |
|||
nstrings = 6 |
|||
fontsize = 12 |
|||
|
|||
guitarChordList = [[chordList[q].voicings['guitar'][-(r+1)] for q in range(len(chordList)) if 'guitar' in chordList[q].voicings.keys()] for r in range(nstrings)] |
|||
guitarChordList.append([chordList[q].name for q in range(len(chordList)) if 'guitar' in chordList[q].voicings.keys()]) |
|||
|
|||
for i in range(nstrings+1): # i is the string currently being drawn |
|||
writeText(currentCanvas, style, ['e','B','G','D','A','E','Name'][i], fontsize, v_origin+(i*string_height), hpos=h_origin, align='right') |
|||
|
|||
for j in range(len(guitarChordList[-1])): # j is which chord (0 is first chord, 1 is 2nd etc) |
|||
if j == 0: |
|||
charpos = string_hz_sp/2 |
|||
s = string_hz_gap |
|||
e = charpos-((currentCanvas.stringWidth(guitarChordList[i][j])/2)+string_hz_gap) |
|||
y = v_origin+(string_height*i)+string_height/2 |
|||
drawHorizLine(currentCanvas, s, e, y, h_origin, v_origin) |
|||
else: |
|||
charpos = string_hz_sp*(j+0.5) |
|||
s = charpos-string_hz_sp+(lastWidth/2+string_hz_gap) |
|||
e = charpos-((currentCanvas.stringWidth(guitarChordList[i][j])/2)+string_hz_gap) |
|||
y = v_origin+(string_height*i)+string_height/2 |
|||
drawHorizLine(currentCanvas, s, e, y, h_origin, v_origin) |
|||
|
|||
if j == len(guitarChordList[-1])-1: |
|||
s = charpos+(currentCanvas.stringWidth(guitarChordList[i][j])/2+string_hz_gap) |
|||
e = charpos+string_hz_sp/2 |
|||
y = v_origin+(string_height*i)+string_height/2 |
|||
drawHorizLine(currentCanvas, s, e, y, h_origin, v_origin) |
|||
|
|||
writeText(currentCanvas, style, guitarChordList[i][j], fontsize, v_origin+(i*string_height), hpos=h_origin+charpos) |
|||
|
|||
lastWidth = currentCanvas.stringWidth(guitarChordList[i][j]) |
|||
|
|||
return (string_height*(nstrings+1)) + title_height # calculate the height of the block |
|||
|
|||
def chordProgression(currentCanvas, style, document, cur_pos): |
|||
margin = style.leftMargin*style.unit |
|||
unitWidth = style.unitWidth*style.unit |
|||
pagesize = style.pageSize |
|||
|
|||
title_height = writeText(currentCanvas, style, "Chord progression", 18, cur_pos, align="left") |
|||
cur_pos += title_height |
|||
|
|||
v_origin = cur_pos + 2*mm + style.beatsHeight |
|||
h_origin = margin |
|||
|
|||
h_loc = 0 |
|||
v_loc = 0 |
|||
|
|||
if (unitWidth * document.timeSignature * 2) >= ((pagesize[0]-(2*margin) + 1)): # adding 1 to allow for rounding errors |
|||
raise Exception("Beat width (unitWidth) is too high. It is {current} pt and can be a maximum of {max} pt".format(current = unitWidth, max = ((pagesize[0]-(2*margin)/(document.timeSignature * 2))))) |
|||
|
|||
maxWidth = int((((pagesize[0]-(2*margin))/unitWidth)//(document.timeSignature*2))*(document.timeSignature*2)) # use integer division to round maxWidth to nearest two bars |
|||
|
|||
for u in range(maxWidth+1): |
|||
s = 0 |
|||
x = u*unitWidth+margin |
|||
if u % document.timeSignature == 0: |
|||
e = -style.beatsHeight |
|||
else: |
|||
e = -style.beatsHeight/2 |
|||
drawVertLine(currentCanvas, s, e, x, h_origin, v_origin) |
|||
if u == maxWidth: # Avoid writing beat number after the final line |
|||
break |
|||
writeText(currentCanvas, style, str((u % document.timeSignature)+1), style.beatsFontSize, v_origin-style.beatsHeight, hpos=x+unitWidth/2) |
|||
|
|||
parsedBlockList = splitBlocks(document.blockList, maxWidth) |
|||
|
|||
for b in parsedBlockList: |
|||
if h_loc == maxWidth: |
|||
v_loc += 1 |
|||
h_loc = 0 |
|||
currentCanvas.rect(h_origin+(h_loc*unitWidth), v_origin+(v_loc*style.unitHeight), b.length*unitWidth, style.unitHeight) |
|||
if b.notes is not None: |
|||
writeText(currentCanvas, style, b.notes, style.notesFontSize, v_origin+((v_loc+1)*style.unitHeight)-(1.3*style.notesFontSize), hpos=h_origin+((h_loc+b.length/2)*unitWidth)) |
|||
v_offset = ((v_loc*style.unitHeight)+style.unitHeight/2)-style.chordNameFontSize/2 |
|||
if b.chord is not None: |
|||
writeText(currentCanvas, style, b.chord.name, style.chordNameFontSize, v_origin+v_offset, hpos=h_origin+((h_loc+b.length/2)*unitWidth)) |
|||
h_loc += b.length |
|||
|
|||
return v_origin + (v_loc+1)*style.unitHeight + style.beatsHeight + title_height # calculate the height of the generated chart |
|||
h_loc = 0 |
|||
splitBlockList = [] |
|||
for i in range(len(blockList)): |
|||
c_orig = blockList[i].chord |
|||
n_orig = blockList[i].notes |
|||
if h_loc == maxWidth: |
|||
h_loc = 0 |
|||
if h_loc+blockList[i].length > maxWidth: |
|||
lengthList = [maxWidth - h_loc] |
|||
while sum(lengthList) < blockList[i].length: |
|||
if blockList[i].length - sum(lengthList) >= maxWidth: |
|||
lengthList.append(maxWidth) |
|||
else: |
|||
lengthList.append(blockList[i].length - sum(lengthList)) |
|||
|
|||
for l in lengthList: |
|||
# create a block with the given length |
|||
splitBlockList.append(Block(l, chord=c_orig, notes=n_orig)) |
|||
|
|||
h_loc = lengthList[-1] |
|||
else: |
|||
splitBlockList.append(blockList[i]) |
|||
h_loc += blockList[i].length |
|||
return splitBlockList |
|||
|
|||
|
|||
class GuitarChart(Flowable): |
|||
""" |
|||
Flowable that draws a guitar chord voicing chart. |
|||
""" |
|||
|
|||
def __init__(self, style, chordList): |
|||
self.style = style |
|||
self.guitarChordList = [ |
|||
c for c in chordList if 'guitar' in c.voicings.keys()] |
|||
self.chartMargin = 15*mm |
|||
self.nStrings = 6 |
|||
self.headingSize = 18 |
|||
|
|||
self.spaceAfter = self.style.separatorSize * mm |
|||
|
|||
def wrap(self, availWidth, availHeight): |
|||
self.width = self.chartMargin + self.style.stringHzGap + self.style.stringHzSp * \ |
|||
(len(self.guitarChordList)) # calculate the width of the flowable |
|||
self.height = self.style.stringHeight * \ |
|||
(self.nStrings+1) + self.headingSize * \ |
|||
self.style.lineSpacing + 2*mm # and its height |
|||
return (self.width, self.height) |
|||
|
|||
def draw(self): |
|||
canvas = self.canv |
|||
title_height = writeText(canvas, self.style, "Guitar chord voicings", |
|||
self.headingSize, self.height, self.width, align="left") |
|||
|
|||
chartmargin = self.chartMargin |
|||
v_origin = self.height - title_height - 2*mm |
|||
h_origin = chartmargin |
|||
self.nStrings = 6 |
|||
fontsize = 12 |
|||
|
|||
stringList = [ |
|||
[c.voicings['guitar'][-(r+1)] for c in self.guitarChordList] for r in range(self.nStrings)] |
|||
stringList.append([c.name for c in self.guitarChordList]) |
|||
|
|||
for i in range(self.nStrings+1): # i is the string line currently being drawn |
|||
writeText(canvas, self.style, ['e', 'B', 'G', 'D', 'A', 'E', 'Name'][i], fontsize, v_origin-( |
|||
i*self.style.stringHeight), self.width, hpos=h_origin, align='right') |
|||
|
|||
# j is which chord (0 is first chord, 1 is 2nd etc) |
|||
for j in range(len(stringList[-1])): |
|||
currentWidth = canvas.stringWidth(stringList[i][j]) |
|||
if j == 0: |
|||
x = self.style.stringHzGap + chartmargin |
|||
l = self.style.stringHzSp/2 - self.style.stringHzGap - \ |
|||
((currentWidth/2)) - self.style.stringHzGap |
|||
y = v_origin-(self.style.stringHeight*i) - \ |
|||
self.style.stringHeight/2 |
|||
canvas.line(x, y, x+l, y) |
|||
else: |
|||
x = chartmargin + self.style.stringHzSp * \ |
|||
(j-0.5)+(lastWidth/2+self.style.stringHzGap) |
|||
l = self.style.stringHzSp - currentWidth / \ |
|||
2 - lastWidth/2 - self.style.stringHzGap*2 |
|||
y = v_origin-(self.style.stringHeight*i) - \ |
|||
self.style.stringHeight/2 |
|||
canvas.line(x, y, x+l, y) |
|||
|
|||
if j == len(stringList[-1])-1: |
|||
x = chartmargin + self.style.stringHzSp * \ |
|||
(j+0.5) + currentWidth/2 + self.style.stringHzGap |
|||
l = self.style.stringHzSp/2 - currentWidth/2 - self.style.stringHzGap |
|||
y = v_origin-(self.style.stringHeight*i) - \ |
|||
self.style.stringHeight/2 |
|||
canvas.line(x, y, x+l, y) |
|||
|
|||
writeText(canvas, self.style, stringList[i][j], fontsize, v_origin-( |
|||
i*self.style.stringHeight), self.width, hpos=chartmargin+self.style.stringHzSp*(j+0.5)) |
|||
|
|||
lastWidth = currentWidth |
|||
|
|||
|
|||
class ChordProgression(Flowable): |
|||
""" |
|||
Flowable that draws a chord progression made up of blocks. |
|||
""" |
|||
|
|||
def __init__(self, style, blockList, timeSignature): |
|||
self.style = style |
|||
self.blockList = blockList |
|||
self.timeSignature = timeSignature |
|||
self.headingSize = 18 |
|||
|
|||
self.spaceAfter = self.style.separatorSize * mm |
|||
|
|||
def wrap(self, availWidth, availHeight): |
|||
self.widthInBeats = 2 * self.timeSignature * \ |
|||
trunc((availWidth/(self.style.unitWidth*self.style.unit)) / |
|||
(2*self.timeSignature)) # width of each line, in beats |
|||
self.width = self.widthInBeats * self.style.unitWidth * self.style.unit |
|||
self.height = self.headingSize * self.style.lineSpacing + 2 * mm + self.style.beatsHeight + \ |
|||
self.style.unitHeight * \ |
|||
sum([b.length for b in self.blockList]) / self.widthInBeats |
|||
return(self.width, self.height) |
|||
|
|||
def draw(self): |
|||
canvas = self.canv |
|||
unitWidth = self.style.unitWidth*self.style.unit |
|||
|
|||
title_height = writeText(canvas, self.style, "Chord progression", |
|||
self.headingSize, self.height, self.width, align="left") |
|||
|
|||
v_origin = self.height - self.style.beatsHeight - title_height - 2*mm |
|||
h_origin = 0 |
|||
|
|||
h_loc = 0 |
|||
v_loc = 0 |
|||
|
|||
maxWidth = self.widthInBeats |
|||
|
|||
for u in range(maxWidth+1): |
|||
y = v_origin |
|||
x = u*unitWidth |
|||
if u % self.timeSignature == 0: |
|||
l = self.style.beatsHeight |
|||
else: |
|||
l = self.style.beatsHeight/2 |
|||
canvas.line(x, y, x, y+l) |
|||
if u == maxWidth: # Avoid writing beat number after the final line |
|||
break |
|||
writeText(canvas, self.style, str((u % self.timeSignature)+1), self.style.beatsFontSize, |
|||
v_origin+self.style.beatsHeight, self.width, hpos=x+unitWidth/2) |
|||
|
|||
parsedBlockList = splitBlocks(self.blockList, maxWidth) |
|||
|
|||
for b in parsedBlockList: |
|||
if h_loc == maxWidth: |
|||
v_loc += 1 |
|||
h_loc = 0 |
|||
canvas.rect(h_loc*unitWidth, v_origin-((v_loc+1)*self.style.unitHeight), |
|||
b.length*unitWidth, self.style.unitHeight) |
|||
if b.notes is not None: |
|||
writeText(canvas, self.style, b.notes, self.style.notesFontSize, v_origin-((v_loc+1)*self.style.unitHeight)+( |
|||
1.3*self.style.notesFontSize), self.width, hpos=h_origin+((h_loc+b.length/2)*unitWidth)) |
|||
v_offset = ((v_loc*self.style.unitHeight) + |
|||
self.style.unitHeight/2)-self.style.chordNameFontSize/2 |
|||
if b.chord is not None: |
|||
writeText(canvas, self.style, b.chord.name, self.style.chordNameFontSize, |
|||
v_origin-v_offset, self.width, hpos=h_origin+((h_loc+b.length/2)*unitWidth)) |
|||
h_loc += b.length |
|||
|
|||
|
|||
def guitarChartCheck(cL): |
|||
chordsPresent = False |
|||
for c in cL: |
|||
if 'guitar' in c.voicings.keys(): |
|||
chordsPresent = True |
|||
break |
|||
return chordsPresent |
|||
chordsPresent = False |
|||
for c in cL: |
|||
if 'guitar' in c.voicings.keys(): |
|||
chordsPresent = True |
|||
break |
|||
return chordsPresent |
|||
|
|||
def savePDF(document, style, pathToPDF): |
|||
|
|||
c = canvas.Canvas(pathToPDF, pagesize=style.pageSize, bottomup=0) |
|||
class TitleBlock(Flowable): |
|||
""" |
|||
Flowable that draws the title and other text at the top of the document. |
|||
""" |
|||
|
|||
def __init__(self, style, document): |
|||
self.style = style |
|||
self.lS = style.lineSpacing |
|||
|
|||
self.title = document.title |
|||
self.subtitle = document.subtitle |
|||
self.composer = document.composer |
|||
self.arranger = document.arranger |
|||
self.tempo = document.tempo |
|||
|
|||
curPos = style.topMargin*style.unit |
|||
self.spaceAfter = self.style.separatorSize * mm |
|||
|
|||
if document.title is not None: |
|||
curPos += writeText(c, style, document.title, 24, curPos) |
|||
def wrap(self, availWidth, availHeight): |
|||
self.width = availWidth |
|||
self.height = sum([self.style.titleFontSize * self.lS if self.title else 0, |
|||
self.style.subtitleFontSize * self.lS if self.subtitle else 0, |
|||
self.style.creditsFontSize * self.lS if self.composer else 0, |
|||
self.style.titleFontSize * self.lS if self.arranger else 0, |
|||
self.style.tempoFontSize * self.lS if self.tempo else 0]) |
|||
return(self.width, self.height) |
|||
|
|||
if document.subtitle is not None: |
|||
curPos += writeText(c, style, document.subtitle, 18, curPos) |
|||
def draw(self): |
|||
canvas = self.canv |
|||
curPos = self.height |
|||
|
|||
if document.composer is not None: |
|||
curPos += writeText(c, style, "Composer: {c}".format(c = document.composer), 12, curPos) |
|||
if self.title: |
|||
curPos -= writeText(canvas, self.style, |
|||
self.title, 24, curPos, self.width) |
|||
|
|||
if document.arranger is not None: |
|||
curPos += writeText(c, style, "Arranger: {a}".format(a = document.arranger), 12, curPos) |
|||
if self.subtitle: |
|||
curPos -= writeText(canvas, self.style, |
|||
self.subtitle, 18, curPos, self.width) |
|||
|
|||
if document.tempo is not None: |
|||
curPos += writeText(c, style, "♩ = {t} bpm".format(t = document.tempo), 12, curPos, align = "left") |
|||
if self.composer: |
|||
curPos -= writeText(canvas, self.style, |
|||
"Composer: {c}".format(c=self.composer), 12, curPos, self.width) |
|||
|
|||
curPos += style.separatorSize*style.unit |
|||
if self.arranger: |
|||
curPos -= writeText(canvas, self.style, |
|||
"Arranger: {a}".format(a=self.arranger), 12, curPos, self.width) |
|||
|
|||
if self.tempo: |
|||
curPos -= writeText(canvas, self.style, "♩ = {t} bpm".format( |
|||
t=self.tempo), 12, curPos, self.width, align="left") |
|||
|
|||
|
|||
def savePDF(document, style, pathToPDF): |
|||
template = PageTemplate(id='AllPages', frames=[Frame(style.leftMargin*mm, style.topMargin*mm, style.pageSize[0] - style.leftMargin*mm*2, style.pageSize[1] - style.topMargin*mm*2, |
|||
leftPadding=0, bottomPadding=0, rightPadding=0, topPadding=0)]) |
|||
|
|||
if guitarChartCheck(document.chordList): |
|||
curPos += guitarChart(c, style, document.chordList, curPos) |
|||
rlDocList = [] |
|||
rlDoc = BaseDocTemplate( |
|||
pathToPDF, pagesize=style.pageSize, pageTemplates=[template]) |
|||
|
|||
curPos += style.separatorSize*style.unit |
|||
if document.title: |
|||
rlDocList.append(TitleBlock(style, document)) |
|||
|
|||
if document.blockList: |
|||
curPos += chordProgression(c, style, document, curPos) |
|||
if guitarChartCheck(document.chordList): |
|||
rlDocList.append(GuitarChart(style, document.chordList)) |
|||
|
|||
curPos += style.separatorSize*style.unit |
|||
if document.blockList: |
|||
rlDocList.append(ChordProgression( |
|||
style, document.blockList, document.timeSignature)) |
|||
|
|||
c.save() |
|||
rlDoc.build(rlDocList) |
@ -0,0 +1,12 @@ |
|||
from reportlab.pdfbase import pdfmetrics |
|||
from reportlab.pdfbase.ttfonts import TTFont |
|||
|
|||
from chordsheet.document import Document, Style |
|||
from chordsheet.render import savePDF |
|||
|
|||
pdfmetrics.registerFont(TTFont('FreeSans', os.path.join('fonts', 'FreeSans.ttf'))) |
|||
|
|||
doc = Document.newFromXML('examples/example.xml') |
|||
style = Style() |
|||
|
|||
savePDF(doc, style, 'test.pdf') |
Write
Preview
Loading…
Cancel
Save
Reference in new issue