mirror of
https://github.com/stronk-dev/Guitar-Sheet-Parser.git
synced 2025-07-05 08:25:09 +02:00
parent
aea24b5eb9
commit
5be0f83c0d
@ -1,11 +1,21 @@
|
|||||||
# This file returns tablature for chords in different positions and voicings
|
#!/usr/bin/env python3
|
||||||
|
##
|
||||||
# TODO: we need to itemize all chords in the song and major/minor/diminshed/augmented/dom7/maj7/etc (we can support more voicings as we go)
|
# @file chordFinder.py
|
||||||
|
#
|
||||||
|
# @brief This file returns tablature for chords in different positions and voicings
|
||||||
|
#
|
||||||
|
# @section description Description
|
||||||
|
# -
|
||||||
|
#
|
||||||
|
# @section notes Notes
|
||||||
|
# - File might never be created in the first place, since we might create a lookup table using data from existing available API's
|
||||||
|
#
|
||||||
|
# @section todo TODO
|
||||||
|
# - we need to itemize all chords in the song and major/minor/diminshed/augmented/dom7/maj7/etc (we can support more voicings as we go)
|
||||||
# then for each chord, get location for each (C A G E D) shape
|
# then for each chord, get location for each (C A G E D) shape
|
||||||
# for each shape, generate finger position tab, like so:
|
# for each shape, generate finger position tab, like so:
|
||||||
# B x24442
|
# B x24442
|
||||||
# C#m x46654
|
# C#m x46654
|
||||||
# Amaj7 x02120
|
# Amaj7 x02120
|
||||||
# F#m 244222
|
# F#m 244222
|
||||||
# Am6 x04555
|
# Am6 x04555
|
||||||
#
|
|
||||||
|
@ -1,8 +1,24 @@
|
|||||||
# !/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
# This file hosts the classes for storing song data
|
##
|
||||||
|
# @file dataStructures.py
|
||||||
|
#
|
||||||
|
# @brief This file contains the internal data structures required for each tablature file
|
||||||
|
#
|
||||||
|
# @section description Description
|
||||||
|
# -
|
||||||
|
#
|
||||||
|
# @section notes Notes
|
||||||
|
#
|
||||||
|
# @section todo TODO
|
||||||
|
# - Move helper functions like stripEmptyLines to a separate file for
|
||||||
|
# - Move read functions to separate input functions (also to support more types of inputs)
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# TODO: move to separate file with helper functions like this
|
"""!@brief Removes empty lines and makes sure every line ends with \r\n
|
||||||
|
@param inputString raw txt input
|
||||||
|
@return string of parsed input
|
||||||
|
"""
|
||||||
def stripEmptyLines(inputString):
|
def stripEmptyLines(inputString):
|
||||||
nonEmptyLines = ""
|
nonEmptyLines = ""
|
||||||
lines = inputString.split("\n")
|
lines = inputString.split("\n")
|
||||||
@ -10,20 +26,28 @@ def stripEmptyLines(inputString):
|
|||||||
if line.strip() != "":
|
if line.strip() != "":
|
||||||
nonEmptyLines += line + "\r\n"
|
nonEmptyLines += line + "\r\n"
|
||||||
return nonEmptyLines
|
return nonEmptyLines
|
||||||
# read .txt input TODO: move to separate input functions if we want to support multiple types of inputs some day, like web or PDF
|
|
||||||
|
"""!@brief Opens a .txt file and loads it's contents into buffer
|
||||||
|
@param inputFile path to .txt file
|
||||||
|
@return .txt file raw contents
|
||||||
|
"""
|
||||||
def readSourceFile(inputFile):
|
def readSourceFile(inputFile):
|
||||||
with open(inputFile, 'r') as file:
|
with open(inputFile, 'r') as file:
|
||||||
return file.read()
|
return file.read()
|
||||||
|
|
||||||
def isChordType(inputString):
|
"""!@brief Returns whether the string is a line of lyrics or a line of tablature data
|
||||||
|
@param inputString single line of text
|
||||||
|
@return True if it is tablature data, False if it is lyric data
|
||||||
|
"""
|
||||||
|
def isTablatureData(inputString):
|
||||||
if not inputString:
|
if not inputString:
|
||||||
return
|
return
|
||||||
#print("Checking '{}' for line type".format(inputString))
|
#print("Checking '{}' for line type".format(inputString))
|
||||||
# Assume CHORD line if any NUMBER character
|
# Assume tablature line if any digit
|
||||||
if any(char.isdigit() for char in inputString):
|
if any(char.isdigit() for char in inputString):
|
||||||
#print("'{}' is a CHORD line, since it contains a number".format(inputString))
|
#print("'{}' is a CHORD line, since it contains a number".format(inputString))
|
||||||
return True
|
return True
|
||||||
# Assume CHORD line if any character {/, #, (, ), }
|
# Assume tablature line if any character {/, #, (, ), }
|
||||||
chordSpecificCharacterString = r"/#"
|
chordSpecificCharacterString = r"/#"
|
||||||
if any(elem in inputString for elem in chordSpecificCharacterString):
|
if any(elem in inputString for elem in chordSpecificCharacterString):
|
||||||
#print("'{}' is a CHORD line, since it contains a chord specific character".format(inputString))
|
#print("'{}' is a CHORD line, since it contains a chord specific character".format(inputString))
|
||||||
@ -41,10 +65,11 @@ def isChordType(inputString):
|
|||||||
#print("'{}' is a LYRIC line, since it contains lyric specific special characters".format(inputString))
|
#print("'{}' is a LYRIC line, since it contains lyric specific special characters".format(inputString))
|
||||||
return False
|
return False
|
||||||
# Else warn and assume chord line
|
# Else warn and assume chord line
|
||||||
#print("Unable to identify if '{}' is a lyric or chord line. Assuming it is a chord line. Please improve the isChordType function".format(inputString))
|
#print("Unable to identify if '{}' is a lyric or tablature line. Assuming it is a tablature line. Please improve the isTablatureData function".format(inputString))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
"""!@brief Class containing Section specific data
|
||||||
|
"""
|
||||||
class Section:
|
class Section:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# List of lines of lyrics strings
|
# List of lines of lyrics strings
|
||||||
@ -58,6 +83,9 @@ class Section:
|
|||||||
# Flag for succesfully parsed
|
# Flag for succesfully parsed
|
||||||
self.isParsed = False
|
self.isParsed = False
|
||||||
|
|
||||||
|
"""!@brief Converts raw buffered data into separate Lyric and tablature lines
|
||||||
|
@return None
|
||||||
|
"""
|
||||||
# Parses self.rawData into lyrics and chord strings
|
# Parses self.rawData into lyrics and chord strings
|
||||||
def parseMe(self):
|
def parseMe(self):
|
||||||
isFirstLine = True
|
isFirstLine = True
|
||||||
@ -66,7 +94,7 @@ class Section:
|
|||||||
lines = self.rawData.split('\r\n')
|
lines = self.rawData.split('\r\n')
|
||||||
for line in lines:
|
for line in lines:
|
||||||
# Determine lyric or chord line
|
# Determine lyric or chord line
|
||||||
currentIsChord = isChordType(line)
|
currentIsChord = isTablatureData(line)
|
||||||
# Initially just fill in the first line correctly
|
# Initially just fill in the first line correctly
|
||||||
if isFirstLine:
|
if isFirstLine:
|
||||||
isFirstLine = False
|
isFirstLine = False
|
||||||
@ -91,20 +119,21 @@ class Section:
|
|||||||
self.chords.append(line)
|
self.chords.append(line)
|
||||||
else:
|
else:
|
||||||
self.lyrics.append(line)
|
self.lyrics.append(line)
|
||||||
|
# move on to next line, save current type
|
||||||
prevWasChord = currentIsChord
|
prevWasChord = currentIsChord
|
||||||
# Simple check to see if it worked
|
# Simple check to see if it probably exported correctly
|
||||||
if abs(len(self.lyrics) - len(self.chords)) > 1:
|
if abs(len(self.lyrics) - len(self.chords)) > 1:
|
||||||
print("Unable to parse section, since there is a mismatch between the amount of chord and lyric lines.")
|
print("Unable to parse section, since there is a mismatch between the amount of chord and lyric lines.")
|
||||||
return
|
return
|
||||||
# Add a final empty line if necessary
|
# Add a trailing empty line if necessary
|
||||||
elif len(self.lyrics) > len(self.chords):
|
elif len(self.lyrics) > len(self.chords):
|
||||||
self.chords.append("")
|
self.chords.append("")
|
||||||
elif len(self.lyrics) < len(self.chords):
|
elif len(self.lyrics) < len(self.chords):
|
||||||
self.lyrics.append("")
|
self.lyrics.append("")
|
||||||
self.isParsed = True
|
self.isParsed = True
|
||||||
|
|
||||||
|
"""!@brief Class containing Song specific data
|
||||||
|
"""
|
||||||
class Song:
|
class Song:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# Src file
|
# Src file
|
||||||
@ -122,14 +151,16 @@ class Song:
|
|||||||
# Flag for succesfully parsed
|
# Flag for succesfully parsed
|
||||||
self.isParsed = False
|
self.isParsed = False
|
||||||
|
|
||||||
# Parses self.rawData into Section objects and metadata
|
"""!@brief Parses self.rawData into Section objects and metadata
|
||||||
|
@return None
|
||||||
|
"""
|
||||||
def parseMe(self):
|
def parseMe(self):
|
||||||
# Fill raw data
|
# Get raw data
|
||||||
self.rawData = readSourceFile(self.inputFile)
|
self.rawData = readSourceFile(self.inputFile)
|
||||||
# Clean up input
|
# Clean up input
|
||||||
parseData = stripEmptyLines(self.rawData)
|
parseData = stripEmptyLines(self.rawData)
|
||||||
#print("Clean data='{}'\n".format(parseData))
|
#print("Clean data='{}'\n".format(parseData))
|
||||||
# While !EOF: build sections (untill []).
|
# While not EOF: build sections untill new section found.
|
||||||
delimiterIndex = parseData.find("[")
|
delimiterIndex = parseData.find("[")
|
||||||
if delimiterIndex == -1:
|
if delimiterIndex == -1:
|
||||||
print("Cannot parse input file, since it is not delimited by '[<sectionName>]' entries")
|
print("Cannot parse input file, since it is not delimited by '[<sectionName>]' entries")
|
||||||
@ -140,20 +171,21 @@ class Song:
|
|||||||
parseData = parseData[delimiterIndex:]
|
parseData = parseData[delimiterIndex:]
|
||||||
# We are now at the start of the first section, at the '[' character
|
# We are now at the start of the first section, at the '[' character
|
||||||
while parseData:
|
while parseData:
|
||||||
|
# Init new Section object
|
||||||
thisSection = Section()
|
thisSection = Section()
|
||||||
# Get first line
|
# Get header on the first line
|
||||||
delimiterIndex = parseData.find("]\r\n")
|
delimiterIndex = parseData.find("]\r\n")
|
||||||
if delimiterIndex == -1:
|
if delimiterIndex == -1:
|
||||||
print("Cannot parse input file, delimitor did not match '[<sectionName>]'")
|
print("Cannot parse input file, delimitor did not match '[<sectionName>]'")
|
||||||
return
|
return
|
||||||
# Set header to first line
|
# Skip the ']\r\n' characters
|
||||||
thisSection.header = parseData[:delimiterIndex+3]
|
thisSection.header = parseData[:delimiterIndex+3]
|
||||||
parseData = parseData[delimiterIndex+3:]
|
parseData = parseData[delimiterIndex+3:]
|
||||||
# Find next section
|
# Find next section
|
||||||
delimiterIndex = parseData.find("[")
|
delimiterIndex = parseData.find("[")
|
||||||
# If EOF, current buffer is final section
|
# If EOF, current buffer is final section
|
||||||
if delimiterIndex == -1:
|
if delimiterIndex == -1:
|
||||||
# Set current section data to remaining buffer
|
# Set thisSection's data to remaining buffer
|
||||||
thisSection.rawData = parseData
|
thisSection.rawData = parseData
|
||||||
parseData = ""
|
parseData = ""
|
||||||
else:
|
else:
|
||||||
|
@ -1,11 +1,34 @@
|
|||||||
# !/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
# Iterate through input folders and create a list of Song objects
|
##
|
||||||
|
# @file initSongs.py
|
||||||
|
#
|
||||||
|
# @brief Iterate through input folders and create a list of Song objects
|
||||||
|
#
|
||||||
|
# @section description Description
|
||||||
|
# Initializes the Song objects for each supported input file found
|
||||||
|
# Currently only supports .txt files, which are read as-is into a string
|
||||||
|
#
|
||||||
|
# @section notes Notes
|
||||||
|
# -
|
||||||
|
#
|
||||||
|
# @section todo TODO
|
||||||
|
# - Set a max recursion depth on the os.walk function
|
||||||
|
# - Support both paths to folders (like now) and to files directly
|
||||||
|
# When the input is a file, check if it is .txt and init it
|
||||||
|
# - Input locations should be set in a config file (init to CWD, overwrite by CMD arguments)
|
||||||
|
|
||||||
import lib.dataStructures
|
import lib.dataStructures
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# For now manually whitelist folders to convert
|
# For now manually whitelist folders to convert
|
||||||
whitelist = ["/mnt/koios/Band/1-sugmesties", "/mnt/koios/Band/2-oefenen", "/mnt/koios/Band/3-uitgewerkt"]
|
whitelist = ["/mnt/koios/Band/1-sugmesties", "/mnt/koios/Band/2-oefenen", "/mnt/koios/Band/3-uitgewerkt"]
|
||||||
|
|
||||||
|
"""!@brief Creates and inits a Song object
|
||||||
|
This function creates a new Song object and sets the internal variables correctly
|
||||||
|
Output folder name is derived from the name of the input file
|
||||||
|
@param filePath path to the input file
|
||||||
|
@return intialised Song object
|
||||||
|
"""
|
||||||
def initSong(filePath):
|
def initSong(filePath):
|
||||||
thisSong = lib.dataStructures.Song()
|
thisSong = lib.dataStructures.Song()
|
||||||
thisSong.inputFile = filePath
|
thisSong.inputFile = filePath
|
||||||
@ -16,14 +39,18 @@ def initSong(filePath):
|
|||||||
#print("Finished init for input file '{}'.\nBase output folder is '{}'\nSong title is '{}'\n".format(thisSong.inputFile, thisSong.outputLocation, thisSong.title))
|
#print("Finished init for input file '{}'.\nBase output folder is '{}'\nSong title is '{}'\n".format(thisSong.inputFile, thisSong.outputLocation, thisSong.title))
|
||||||
return thisSong
|
return thisSong
|
||||||
|
|
||||||
|
"""!@brief Returns the list of all Song objects created
|
||||||
|
This function gets all supported input files in the specified input location(s)
|
||||||
|
For each of these files it creates a Song object, ready to be read and then parsed
|
||||||
|
@return list of intialised Song objects
|
||||||
|
"""
|
||||||
def getSongObjects():
|
def getSongObjects():
|
||||||
# path to song folders, which MAY contain a .txt source file
|
# path to song folders, which MAY contain a .txt source file
|
||||||
txtFileLocations = []
|
txtFileLocations = []
|
||||||
# list of Song objects
|
# list of Song objects
|
||||||
songList = []
|
songList = []
|
||||||
|
|
||||||
# get all subdirectories
|
# go through all input locations. find .txt files.
|
||||||
for inputFolder in whitelist:
|
for inputFolder in whitelist:
|
||||||
for root, dirs, files in os.walk(inputFolder):
|
for root, dirs, files in os.walk(inputFolder):
|
||||||
for name in files:
|
for name in files:
|
||||||
@ -34,7 +61,7 @@ def getSongObjects():
|
|||||||
#else:
|
#else:
|
||||||
#print("Skipping file '{}' for it is not a .txt file".format(name))
|
#print("Skipping file '{}' for it is not a .txt file".format(name))
|
||||||
|
|
||||||
# go through all input locations. find .txt files. for each .txt file initSong. return list
|
# create list of Song objects
|
||||||
while(txtFileLocations):
|
while(txtFileLocations):
|
||||||
filePath = txtFileLocations.pop()
|
filePath = txtFileLocations.pop()
|
||||||
if (filePath != ""):
|
if (filePath != ""):
|
||||||
|
@ -1,10 +1,21 @@
|
|||||||
# This file takes a string corresponding to chord data and transposes it
|
#!/usr/bin/env python3
|
||||||
|
##
|
||||||
|
# @file transpose.py
|
||||||
|
#
|
||||||
|
# @brief This file takes a string corresponding to chord data and transposes it
|
||||||
|
#
|
||||||
|
# @section description Description
|
||||||
|
# -
|
||||||
|
#
|
||||||
|
# @section notes Notes
|
||||||
|
# -
|
||||||
|
#
|
||||||
|
# @section todo TODO
|
||||||
|
# - take a line of chord data, for each string enclosed in whitespace or tabs:
|
||||||
|
# ignore if its in major or minor, just take the ROOT of the chord
|
||||||
|
# then get its index in the slider
|
||||||
|
# then add/subtract transposition amount and loop around the slider if it goes over
|
||||||
|
# make sure to keep line width persistent:
|
||||||
|
# if from E to Eb for example, remove a whitespace
|
||||||
|
# if from Eb to D for example, add a whitespace
|
||||||
slider = ['E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B', 'C', 'Db', 'D', 'Eb']
|
slider = ['E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B', 'C', 'Db', 'D', 'Eb']
|
||||||
# TODO: take a line of chord data, for each string enclosed in whitespace or tabs:
|
|
||||||
# ignore if its in major or minor, just take the ROOT of the chord
|
|
||||||
# then get its index in the slider
|
|
||||||
# then add/subtract transposition amount and loop around the slider if it goes over
|
|
||||||
# make sure to keep line width persistent:
|
|
||||||
# if from E to Eb for example, remove a whitespace
|
|
||||||
# if from Eb to D for example, add a whitespace
|
|
||||||
|
24
main.py
24
main.py
@ -1,5 +1,25 @@
|
|||||||
# !/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
# This program converts all songs in a given directory to a printable format
|
##
|
||||||
|
# @file main.py
|
||||||
|
#
|
||||||
|
# @brief This program converts supported tablature source files to a printable format
|
||||||
|
#
|
||||||
|
# @section description Description
|
||||||
|
# Creates Song objects of all tablatures it can find in a given directory or its subdirectories
|
||||||
|
# Supported inputs currently: Any .txt file, as long as each section has a corresponding [<sectionName>] delimiter
|
||||||
|
# Supported outputs currently: PNG format
|
||||||
|
# Song objects are then parsed into separate metadata information and sections
|
||||||
|
# Sections contain lines of lyric and corresponding tablature data
|
||||||
|
# The program then tries to fit these sections within the chosen output dimensions (currently A4)
|
||||||
|
# as best as it can, shrinking or growing sections to fit the remaining space
|
||||||
|
#
|
||||||
|
# @section notes Notes
|
||||||
|
# - Splitting raw text into lyric and tablature info is very basic at the moment.
|
||||||
|
# We need a better way to classify & split the various channels (raw tab, lyrics, chords, more?) that can be expected in tablature
|
||||||
|
#
|
||||||
|
# @section todo TODO
|
||||||
|
# - Various prints should be printed at specific log levels, to easily switch between debug, info or warnings only
|
||||||
|
|
||||||
import lib.chordFinder
|
import lib.chordFinder
|
||||||
import lib.dataStructures
|
import lib.dataStructures
|
||||||
import lib.initSongs
|
import lib.initSongs
|
||||||
|
@ -1,5 +1,21 @@
|
|||||||
# !/usr/bin/python
|
#!/usr/bin/env python3
|
||||||
# This program converts Song objects to imgs printable on A4 paper
|
##
|
||||||
|
# @file output2img.py
|
||||||
|
#
|
||||||
|
# @brief This program converts the internal data structure to an image file
|
||||||
|
#
|
||||||
|
# @section description Description
|
||||||
|
# Generates PNG images of a specific dimension (currently A4) of tablature data
|
||||||
|
# Dynamically resizes specific sections to maximize using the entire paper (and avoid awkward page flips)
|
||||||
|
#
|
||||||
|
# @section notes Notes
|
||||||
|
# -
|
||||||
|
#
|
||||||
|
# @section todo TODO
|
||||||
|
# - A lot of this stuff is hardcoded. We want to write default fonts, sizes, colours, margins, dimensions, wanted amount of pages
|
||||||
|
# to a config file on first boot. Overwrite these if they get passed via CMD arguments (or manually by user)
|
||||||
|
# - Various prints should be printed at specific log levels, to easily switch between debug, info or warnings only
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import lib.dataStructures
|
import lib.dataStructures
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
@ -23,25 +39,38 @@ fontColour = ()
|
|||||||
topMargin = 10
|
topMargin = 10
|
||||||
leftMargin = 25
|
leftMargin = 25
|
||||||
|
|
||||||
# return expected height of rendering the complete current section
|
"""!@brief Calculates the height of rendered text
|
||||||
|
This function calculates the dimensions of each line of text
|
||||||
|
the section contains and returns the sum
|
||||||
|
@param section lib.dataStructures.Section object
|
||||||
|
@return the total height of the section
|
||||||
|
"""
|
||||||
def calcSectionHeight(section):
|
def calcSectionHeight(section):
|
||||||
lineIterator = 0
|
lineIterator = 0
|
||||||
amountOfLines = len(section.lyrics)
|
amountOfLines = len(section.lyrics)
|
||||||
heightSum = 0
|
heightSum = 0
|
||||||
# add section title
|
# consider section title
|
||||||
headerWidth, headerHeight = fontChords.getsize(section.header)
|
headerWidth, headerHeight = fontChords.getsize(section.header)
|
||||||
heightSum += headerHeight
|
heightSum += headerHeight
|
||||||
while lineIterator < amountOfLines:
|
while lineIterator < amountOfLines:
|
||||||
# Get chord&lyric line
|
# Get chord&lyric line dimensions
|
||||||
lyricTextWidth, lyricTextHeight = fontLyrics.getsize(section.lyrics[lineIterator])
|
lyricTextWidth, lyricTextHeight = fontLyrics.getsize(section.lyrics[lineIterator])
|
||||||
chordTextWidth, chordTextHeight = fontChords.getsize(section.chords[lineIterator])
|
chordTextWidth, chordTextHeight = fontChords.getsize(section.chords[lineIterator])
|
||||||
heightSum += lyricTextHeight + chordTextHeight
|
heightSum += lyricTextHeight + chordTextHeight
|
||||||
lineIterator += 1
|
lineIterator += 1
|
||||||
|
|
||||||
return heightSum
|
return heightSum
|
||||||
|
|
||||||
|
"""!@brief Exports the song object to images
|
||||||
|
This function renders the metadata and sections
|
||||||
|
of a given Song object, and exports it as PNG to the destination folder.
|
||||||
|
It will create the folder if it does not exist yet.
|
||||||
|
It will overwrite existing images, but will not clear old images
|
||||||
|
@param folderLocation path to where we want the images
|
||||||
|
@param songObj lib.dataStructures.Song object
|
||||||
|
@return None
|
||||||
|
"""
|
||||||
def outputToImage(folderLocation, songObj):
|
def outputToImage(folderLocation, songObj):
|
||||||
# Create target Directory if don't exist
|
# Create target Directory if doesn't exist
|
||||||
if not os.path.exists(folderLocation):
|
if not os.path.exists(folderLocation):
|
||||||
os.mkdir(folderLocation)
|
os.mkdir(folderLocation)
|
||||||
print("Directory " , folderLocation , " Created ")
|
print("Directory " , folderLocation , " Created ")
|
||||||
@ -58,7 +87,7 @@ def outputToImage(folderLocation, songObj):
|
|||||||
|
|
||||||
# Write metadata
|
# Write metadata
|
||||||
for line in songObj.metadata.split('\n'):
|
for line in songObj.metadata.split('\n'):
|
||||||
# remove any unwanted characters from metadat
|
# remove any unwanted characters from metadata
|
||||||
line = line.rstrip()
|
line = line.rstrip()
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
@ -66,16 +95,19 @@ def outputToImage(folderLocation, songObj):
|
|||||||
metadataTextWidth, metadataTextHeight = fontMetadata.getsize(line)
|
metadataTextWidth, metadataTextHeight = fontMetadata.getsize(line)
|
||||||
draw.text((leftMargin,currentHeight), line, fill=(128, 128, 128), font=fontMetadata)
|
draw.text((leftMargin,currentHeight), line, fill=(128, 128, 128), font=fontMetadata)
|
||||||
currentHeight += metadataTextHeight
|
currentHeight += metadataTextHeight
|
||||||
|
# Margin between metadata and the first section
|
||||||
currentHeight += topMargin
|
currentHeight += topMargin
|
||||||
|
# Iterate over each section
|
||||||
|
# NOTE: sections might be split into lists of pages containing a list of sections
|
||||||
|
# This change will occur when we add an arranger which resizes sections to fit pages better
|
||||||
for section in songObj.sections:
|
for section in songObj.sections:
|
||||||
|
# Reset section specific variables
|
||||||
lineIterator = 0
|
lineIterator = 0
|
||||||
amountOfLines = len(section.lyrics)
|
amountOfLines = len(section.lyrics)
|
||||||
if (amountOfLines != len(section.chords)):
|
if (amountOfLines != len(section.chords)):
|
||||||
print("Cannot write this section to file, since it was not processed correctly. There are {} chord lines and {} lyric lines. Aborting...".format(len(section.chords), amountOfLines))
|
print("Cannot write this section to file, since it was not processed correctly. There are {} tablature lines and {} lyric lines. Aborting...".format(len(section.chords), amountOfLines))
|
||||||
return
|
return
|
||||||
# See if it can fit on the current page - if it does not, write & reset
|
# See if the section would fit on the current page - if it does not, write current buffered image & make the next image ready
|
||||||
if currentHeight + calcSectionHeight(section) > imageHeight:
|
if currentHeight + calcSectionHeight(section) > imageHeight:
|
||||||
#print("overflow! starting with a new image")
|
#print("overflow! starting with a new image")
|
||||||
outputLocation = folderLocation + "/" + str(imageNumber) + ".png"
|
outputLocation = folderLocation + "/" + str(imageNumber) + ".png"
|
||||||
@ -84,11 +116,11 @@ def outputToImage(folderLocation, songObj):
|
|||||||
currentHeight = topMargin
|
currentHeight = topMargin
|
||||||
a4image = Image.new('RGB',(imageWidth, imageHeight),(background))
|
a4image = Image.new('RGB',(imageWidth, imageHeight),(background))
|
||||||
draw = ImageDraw.Draw(a4image)
|
draw = ImageDraw.Draw(a4image)
|
||||||
|
# write section title
|
||||||
# add section title
|
|
||||||
headerWidth, headerHeight = fontChords.getsize(section.header)
|
headerWidth, headerHeight = fontChords.getsize(section.header)
|
||||||
draw.text((leftMargin,currentHeight), section.header, fill=(0, 0, 0), font=fontChords)
|
draw.text((leftMargin,currentHeight), section.header, fill=(0, 0, 0), font=fontChords)
|
||||||
currentHeight += headerHeight
|
currentHeight += headerHeight
|
||||||
|
# Write each line tablature&lyric data
|
||||||
while lineIterator < amountOfLines:
|
while lineIterator < amountOfLines:
|
||||||
#print("Printing chord line {} and lyrics line {}".format(section.chords[lineIterator], section.lyrics[lineIterator]))
|
#print("Printing chord line {} and lyrics line {}".format(section.chords[lineIterator], section.lyrics[lineIterator]))
|
||||||
# Get chord&lyric line
|
# Get chord&lyric line
|
||||||
@ -101,9 +133,9 @@ def outputToImage(folderLocation, songObj):
|
|||||||
currentHeight += lyricTextHeight
|
currentHeight += lyricTextHeight
|
||||||
lineIterator += 1
|
lineIterator += 1
|
||||||
#print("currentheight={}".format(currentHeight))
|
#print("currentheight={}".format(currentHeight))
|
||||||
|
# Margin between each section
|
||||||
currentHeight += topMargin
|
currentHeight += topMargin
|
||||||
|
# No more sections left, so the current buffered image is ready to be written to file
|
||||||
# Write remaining image to file as well
|
|
||||||
outputLocation = folderLocation + "/" + str(imageNumber) + ".png"
|
outputLocation = folderLocation + "/" + str(imageNumber) + ".png"
|
||||||
a4image.save(outputLocation)
|
a4image.save(outputLocation)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user