Compare commits

...

6 Commits
0.1 ... master

Author SHA1 Message Date
2571dcec5b Set a minimum font size to PPI/6 to make sure text stays readable 2022-01-15 02:40:38 +01:00
Marco van Dijk
4ab8f6c836 Replaced prints with logging module 2021-07-27 16:19:08 +02:00
Marco van Dijk
30109a0a7f Option to prefer even amount of pages
Tweaked whitespace allowed to prefer 4 pages big over 2 pages with very small text
2021-07-27 15:34:55 +02:00
Marco van Dijk
568054b986 Variable margins based on page number 2021-07-27 14:48:57 +02:00
Marco van Dijk
9992b7ac36 Added max pages 2021-07-27 14:18:30 +02:00
Marco van Dijk
f26e80b245 Added minimum page config entry
Fixed expected height calculation to either take the whitespace in the input file or a set margin in between sections
Changed default settings slightly
2021-07-23 23:09:05 +02:00
6 changed files with 183 additions and 126 deletions

View File

@ -30,13 +30,14 @@ def initConfig():
# Else load defaults # Else load defaults
else: else:
config['input'] = {'inputFolders': os.getcwd(), config['input'] = {'inputFolders': os.getcwd(),
'maxDepth': 2, 'maxDepth': 1,
'readtxt': 1, 'readtxt': 1,
'readraw': 1 'readraw': 1
} }
config['options'] = {'exporttoimg': 1, config['options'] = {'exporttoimg': 1,
'exporttotxt': 0, 'exporttotxt': 0,
'exporttoraw': 0 'exporttoraw': 0,
'logLevel': 3
} }
config['output'] = {'metafontfamily': 'fonts/CourierPrime-Regular.ttf', config['output'] = {'metafontfamily': 'fonts/CourierPrime-Regular.ttf',
'metaFontWeight': 32, 'metaFontWeight': 32,
@ -46,14 +47,17 @@ def initConfig():
'backgroundColour': '255,255,255', 'backgroundColour': '255,255,255',
'fontColour': '0,0,0', 'fontColour': '0,0,0',
'metadataColour': '128,128,128', 'metadataColour': '128,128,128',
'topMargin': 50, 'verticalMargin': 50,
'leftMargin': 50, 'horizontalMargin': 100,
'rightMargin': 50, 'extraHorizontalMargin': 100,
'tryToShrinkRatio' : 0.25, 'tryToShrinkRatio' : 0.4,
'lowestwhitespaceonwidthratioallowed': 0.90, 'shortestlinewhitespaceratioallowed': 0.95,
'highestwhitespaceonwidthratioallowed': 0.40, 'longestlinewhitespaceratioallowed': 0.30,
'keepEmptyLines': 1, 'keepEmptyLines': 1,
'writeheaderfile': 0 'writeheaderfile': 0,
'minPages': 2,
'maxPages': 4,
'preferEvenPageNumbers': 0
} }
# (if CMD arguments: load CMD arguments to override specific settings) # (if CMD arguments: load CMD arguments to override specific settings)
with open('config.ini', 'w') as configfile: with open('config.ini', 'w') as configfile:

View File

@ -9,13 +9,11 @@
# #
# @section notes Notes # @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
import lib.config import lib.config
from PIL import ImageFont from PIL import ImageFont
import logging
A4 = {'width': 210, 'height': 297} A4 = {'width': 210, 'height': 297}
A5 = {'width': 210, 'height': 148} A5 = {'width': 210, 'height': 148}
@ -47,30 +45,30 @@ def readSourceFile(inputFile):
def isTablatureData(inputString): def isTablatureData(inputString):
if not inputString: if not inputString:
return return
#print("Checking '{}' for line type".format(inputString)) logging.debug("Checking '{}' for line type".format(inputString))
# Assume tablature line if any character {/, #, (, ), } # Assume tablature line if any character {/, #, (, ), }
tablatureSpecificCharacterString = r"/#" tablatureSpecificCharacterString = r"/#"
if any(elem in inputString for elem in tablatureSpecificCharacterString): if any(elem in inputString for elem in tablatureSpecificCharacterString):
#print("'{}' is a tablature line, since it contains a tablature specific character".format(inputString)) logging.debug("'{}' is a tablature line, since it contains a tablature specific character".format(inputString))
return True return True
# Assume LYRIC line if any TEXT character OTHER THAN {a, b, c, d, e, f, g, h, b, x, m, j, n} # Assume LYRIC line if any TEXT character OTHER THAN {a, b, c, d, e, f, g, h, b, x, m, j, n}
lyricSpecificCharacterString = r"abcdefghbxmjn" lyricSpecificCharacterString = r"abcdefghbxmjn"
for char in inputString: for char in inputString:
if char.isalpha(): if char.isalpha():
if not char.lower() in lyricSpecificCharacterString: if not char.lower() in lyricSpecificCharacterString:
#print("'{}' is a LYRIC line, since it contains lyric specific text characters".format(inputString)) logging.debug("'{}' is a lyric line, since it contains lyric specific text characters".format(inputString))
return False return False
# Assume tablature line if any digit # 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 tablature line, since it contains a number".format(inputString)) logging.debug("'{}' is a tablature line, since it contains a number".format(inputString))
return True return True
# Assume LYRIC line if any character {.} # Assume LYRIC line if any character {.}
lyricSpecialChars = r"." lyricSpecialChars = r"."
if any(elem in inputString for elem in lyricSpecialChars): if any(elem in inputString for elem in lyricSpecialChars):
#print("'{}' is a LYRIC line, since it contains lyric specific special characters".format(inputString)) logging.debug("'{}' is a lyric line, since it contains lyric specific special characters".format(inputString))
return False return False
# Else warn and assume tablature line # Else warn and assume tablature line
#print("Unable to identify if '{}' is a lyric or tablature line. Assuming it is a tablature line. Please improve the isTablatureData function".format(inputString)) # logging.warn("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 """!@brief Class containing Section specific data
@ -100,20 +98,23 @@ class Section:
heightSum = 0 heightSum = 0
maxWidth = 0 maxWidth = 0
# consider section title # consider section title
logging.debug("Init size with header '{}'".format(self.header))
headerWidth, headerHeight = fontTablature.getsize(self.header) headerWidth, headerHeight = fontTablature.getsize(self.header)
heightSum += headerHeight heightSum += headerHeight
maxWidth = headerWidth maxWidth = headerWidth
#print("With header, dimensions of section '{}' start at {}H{}B".format(self.header[:-2], heightSum, maxWidth))
while lineIterator < amountOfLines: while lineIterator < amountOfLines:
# Get chord&lyric line dimensions # Get chord&lyric line dimensions
lyricTextWidth, lyricTextHeight = fontLyrics.getsize(self.lyrics[lineIterator]) lyricTextWidth, lyricTextHeight = fontLyrics.getsize(self.lyrics[lineIterator])
tablatureTextWidth, chordTextHeight = fontTablature.getsize(self.tablatures[lineIterator]) tablatureTextWidth, chordTextHeight = fontTablature.getsize(self.tablatures[lineIterator])
heightSum += lyricTextHeight + chordTextHeight heightSum += lyricTextHeight + chordTextHeight
if lyricTextWidth > maxWidth: if lyricTextWidth > maxWidth:
logging.debug("Found line '{}' with a width of {}".format(self.lyrics[lineIterator], lyricTextWidth))
maxWidth = lyricTextWidth maxWidth = lyricTextWidth
if tablatureTextWidth > maxWidth: if tablatureTextWidth > maxWidth:
logging.debug("Found line '{}' with a width of {}".format(self.tablatures[lineIterator], tablatureTextWidth))
maxWidth = tablatureTextWidth maxWidth = tablatureTextWidth
lineIterator += 1 lineIterator += 1
logging.debug("Setting section to W:{} H:{}".format(maxWidth, heightSum))
self.expectedWidth = maxWidth self.expectedWidth = maxWidth
self.expectedHeight = heightSum self.expectedHeight = heightSum
@ -134,7 +135,7 @@ class Section:
continue continue
# Determine lyric or tablature line # Determine lyric or tablature line
currentIsTablature = isTablatureData(line) currentIsTablature = isTablatureData(line)
#print("Have line {} isTab={}, isLyric={}".format(line, currentIsTablature, not currentIsTablature)) logging.debug("Have line {} isTab={}, isLyric={}".format(line, currentIsTablature, not currentIsTablature))
# Initially just fill in the first line correctly # Initially just fill in the first line correctly
if isFirstLine: if isFirstLine:
isFirstLine = False isFirstLine = False
@ -146,16 +147,16 @@ class Section:
# we need to insert an empty line of the other type # we need to insert an empty line of the other type
elif currentIsTablature == prevWasTablature: elif currentIsTablature == prevWasTablature:
if currentIsTablature: if currentIsTablature:
#print("Inserting empty Lyric line") logging.debug("Inserting empty Lyric line")
self.tablatures.append(line) self.tablatures.append(line)
self.lyrics.append("") self.lyrics.append("")
else: else:
#print("Inserting empty tablature line") logging.debug("Inserting empty tablature line")
self.lyrics.append(line) self.lyrics.append(line)
self.tablatures.append("") self.tablatures.append("")
# also insert the current line # also insert the current line
elif currentIsTablature: elif currentIsTablature:
#print("Inserting empty Lyric line") logging.debug("Inserting empty Lyric line")
self.tablatures.append(line) self.tablatures.append(line)
else: else:
self.lyrics.append(line) self.lyrics.append(line)
@ -163,7 +164,7 @@ class Section:
prevWasTablature = currentIsTablature prevWasTablature = currentIsTablature
# Simple check to see if it probably exported correctly # Simple check to see if it probably exported correctly
if abs(len(self.lyrics) - len(self.tablatures)) > 1: if abs(len(self.lyrics) - len(self.tablatures)) > 1:
print("Unable to parse section {}, since there is a mismatch between the amount of lyrics ({}) and tablature ({}) lines.".format(self.header, len(self.lyrics), len(self.tablatures))) logging.error("Unable to parse section {}, since there is a mismatch between the amount of lyrics ({}) and tablature ({}) lines.".format(self.header, len(self.lyrics), len(self.tablatures)))
return return
# Add a trailing empty line if necessary # Add a trailing empty line if necessary
elif len(self.lyrics) > len(self.tablatures): elif len(self.lyrics) > len(self.tablatures):
@ -203,7 +204,9 @@ class Song:
# Flag for succesfully parsed # Flag for succesfully parsed
self.isParsed = False self.isParsed = False
configObj = lib.config.config['output'] configObj = lib.config.config['output']
self.topMargin = int(configObj['topMargin']) self.verticalMargin = int(configObj['verticalMargin'])
self.horizontalMargin = int(configObj['horizontalMargin'])
self.extraHorizontalMargin = int(configObj['extraHorizontalMargin'])
self.fontColour = tuple(int(var) for var in configObj['fontColour'].split(',')) self.fontColour = tuple(int(var) for var in configObj['fontColour'].split(','))
self.backgroundColour = tuple(int(var) for var in configObj['backgroundColour'].split(',')) self.backgroundColour = tuple(int(var) for var in configObj['backgroundColour'].split(','))
self.metadataColour = tuple(int(var) for var in configObj['metadataColour'].split(',')) self.metadataColour = tuple(int(var) for var in configObj['metadataColour'].split(','))
@ -214,9 +217,7 @@ class Song:
# With a PPI of 72, a font size of 14-18 is a good starting point (PPI / 4 or 4.5) # With a PPI of 72, a font size of 14-18 is a good starting point (PPI / 4 or 4.5)
# Since font size is then shrunk and grown to fit whitespace we do not need to be as accurate # Since font size is then shrunk and grown to fit whitespace we do not need to be as accurate
# PPI of 144 -> fontSize of 32 # PPI of 144 -> fontSize of 32
self.fontSize = int(self.ppi / 4.5) self.fontSize = int(self.ppi / 4)
self.leftMargin = int(configObj['leftMargin'])
self.rightMargin = int(configObj['rightMargin'])
self.fontLyrics = ImageFont.truetype(configObj['lyricfontfamily'], self.fontSize) self.fontLyrics = ImageFont.truetype(configObj['lyricfontfamily'], self.fontSize)
self.fontTablature = ImageFont.truetype(configObj['tablaturefontfamliy'], self.fontSize) self.fontTablature = ImageFont.truetype(configObj['tablaturefontfamliy'], self.fontSize)
self.fontFamilyLyrics = configObj['lyricfontfamily'] self.fontFamilyLyrics = configObj['lyricfontfamily']
@ -229,14 +230,18 @@ class Song:
self.tryToShrinkRatio = float(configObj['tryToShrinkRatio']) self.tryToShrinkRatio = float(configObj['tryToShrinkRatio'])
# Setting this makes sure that the largest section on the page fills at least this percentage of total width # Setting this makes sure that the largest section on the page fills at least this percentage of total width
# The higher this is, the more it is allowed to shrink # The higher this is, the more it is allowed to shrink
self.lowestWhitespaceOnWidthRatioAllowed = float(configObj['lowestWhitespaceOnWidthRatioAllowed']) self.longestLineWhitespaceRatioAllowed = float(configObj['longestLineWhitespaceRatioAllowed'])
# Some sections are very small, so the highest whitespace can be very large. # Some lines are very small, so the highest whitespace can be very large.
# It is advised to keep this value relatively small # It is advised to keep this ratio high for that reason
self.hightestWhitespaceOnWidthRatioAllowed = float(configObj['highestwhitespaceonwidthratioallowed']) self.shortestLineWhitespaceRatioAllowed = float(configObj['shortestLineWhitespaceRatioAllowed'])
# Strip empty lines from input or keep em # Strip empty lines from input or keep em
self.keepEmptyLines = configObj['keepEmptyLines'] == '1' self.keepEmptyLines = configObj['keepEmptyLines'] == '1'
# Strip empty lines from input or keep em # Strip empty lines from input or keep em
self.writeMetadata = configObj['writeheaderfile'] == '1' self.writeMetadata = configObj['writeheaderfile'] == '1'
# Don't go under this number
self.minPages = int(configObj['minPages'])
self.preferEvenPageNumbers = int(configObj['preferEvenPageNumbers'])
self.maxPages = max(int(configObj['minPages']), int(configObj['maxPages']))
"""!@brief Calculates dimensions of metadata """!@brief Calculates dimensions of metadata
@ -245,7 +250,7 @@ class Song:
""" """
def calculateMetadataDimensions(self): def calculateMetadataDimensions(self):
# metadata starts topMargin removed from top # metadata starts topMargin removed from top
currentHeight = self.topMargin currentHeight = self.verticalMargin
maxWidth = 0 maxWidth = 0
for line in self.metadata.split('\n'): for line in self.metadata.split('\n'):
line = line.rstrip() line = line.rstrip()
@ -253,11 +258,12 @@ class Song:
continue continue
metadataTextWidth, metadataTextHeight = self.fontMetadata.getsize(line) metadataTextWidth, metadataTextHeight = self.fontMetadata.getsize(line)
if metadataTextWidth > maxWidth: if metadataTextWidth > maxWidth:
logging.debug("Found line '{}' with a width of {}".format(line, metadataTextWidth))
maxWidth = metadataTextWidth maxWidth = metadataTextWidth
currentHeight += metadataTextHeight currentHeight += metadataTextHeight
self.metadataWidth = maxWidth self.metadataWidth = maxWidth
self.metadataHeight = currentHeight self.metadataHeight = currentHeight
#print("metadata dimensions are {}h : {}w".format(currentHeight, maxWidth)) logging.debug("metadata dimensions are {}h : {}w".format(currentHeight, maxWidth))
"""!@brief Resizes all sections by a specified amount """!@brief Resizes all sections by a specified amount
Also recalculates all section sizes afterwards Also recalculates all section sizes afterwards
@ -265,7 +271,7 @@ class Song:
@return None @return None
""" """
def resizeAllSections(self, mutator): def resizeAllSections(self, mutator):
#print("Resizing font by {} to {}".format(mutator, self.fontSize)) logging.debug("Resizing font by {} to {}".format(mutator, self.fontSize))
self.fontSize += mutator self.fontSize += mutator
self.fontLyrics = ImageFont.truetype(self.fontFamilyLyrics, self.fontSize) self.fontLyrics = ImageFont.truetype(self.fontFamilyLyrics, self.fontSize)
self.fontTablature = ImageFont.truetype(self.fontFamilyTablature, self.fontSize) self.fontTablature = ImageFont.truetype(self.fontFamilyTablature, self.fontSize)
@ -294,10 +300,10 @@ class Song:
def fitSectionsByWidth(self): def fitSectionsByWidth(self):
self.prerenderSections() self.prerenderSections()
while not self.checkOverflowX(): while not self.checkOverflowX():
#print("Resizing down to prevent overflow on the width of the page") logging.debug("Resizing down to prevent overflow on the width of the page")
self.resizeAllSections(-1) self.resizeAllSections(-1)
while not self.checkOverflowMetadata(): while not self.checkOverflowMetadata():
#print("Resizing down to prevent overflow on the width of the page") logging.debug("Resizing down to prevent metadata overflow on the width of the page")
self.resizeMetadata(-1) self.resizeMetadata(-1)
"""!@brief Checks whether we are overflowing on the width of the page """!@brief Checks whether we are overflowing on the width of the page
@ -305,8 +311,8 @@ class Song:
""" """
def checkOverflowX(self): def checkOverflowX(self):
for section in self.sections: for section in self.sections:
if section.expectedWidth > self.imageWidth - self.leftMargin - self.rightMargin: if section.expectedWidth > self.imageWidth - self.extraHorizontalMargin - self.horizontalMargin - self.horizontalMargin:
print("There is an overflow on width: this section has a width of {}, but we have {} ({}-{}-{}) amount of space".format(section.expectedWidth, self.imageWidth - self.leftMargin - self.rightMargin, self.imageWidth, self.leftMargin, self.rightMargin)) logging.debug("There is an overflow on width: this section has a width of {}, but we have {} ({}-{}-{}*2) amount of space".format(section.expectedWidth, self.imageWidth - self.extraHorizontalMargin - self.horizontalMargin - self.horizontalMargin, self.imageWidth, self.extraHorizontalMargin, self.horizontalMargin))
return False return False
return True return True
@ -314,32 +320,37 @@ class Song:
@return True if everything OK, False if overflowing @return True if everything OK, False if overflowing
""" """
def checkOverflowMetadata(self): def checkOverflowMetadata(self):
if self.metadataWidth > self.imageWidth - self.leftMargin - self.rightMargin: if self.metadataWidth > self.imageWidth - self.extraHorizontalMargin - self.horizontalMargin - self.horizontalMargin:
return False return False
return True return True
"""!@brief Checks whether we can increase the font size without creating more pages """!@brief Resizes the page to fit reminaing whitespace, and increases the number of pages to stay above the minimum amount of pages
@return None @return None
""" """
def increaseWhileSameAmountOfPages(self): def increaseToMinPages(self):
targetPageAmount = len(self.pages) targetPageAmount = max(len(self.pages), self.minPages)
if (targetPageAmount % 2) != 0 and self.preferEvenPageNumbers:
targetPageAmount += 1
logging.info("Increasing target page amount to {} to make it an even number".format(targetPageAmount))
originalFontsize = self.fontSize originalFontsize = self.fontSize
self.resizeAllSections(1) logging.debug("Starting font size increase with {} pages and {} font size".format(targetPageAmount, originalFontsize))
self.resizeAllSections(+1)
self.sectionsToPages() self.sectionsToPages()
currentPageAmount = len(self.pages) currentPageAmount = len(self.pages)
# Increase fontSize as long as we do not add a page # Increase fontSize as long as we stay under the target max pages
while currentPageAmount <= targetPageAmount and self.checkOverflowX(): while ((currentPageAmount <= targetPageAmount) and self.checkOverflowX()):
self.resizeAllSections(+1) self.resizeAllSections(+1)
self.sectionsToPages() self.sectionsToPages()
currentPageAmount = len(self.pages) currentPageAmount = len(self.pages)
logging.debug("Current page amount is {} with font size {}".format(currentPageAmount, self.fontSize))
# Now undo latest increase to go back to target page amount # Now undo latest increase to go back to target page amount
self.resizeAllSections(-1) self.resizeAllSections(-1)
self.sectionsToPages() self.sectionsToPages()
currentPageAmount = len(self.pages) currentPageAmount = len(self.pages)
if targetPageAmount != currentPageAmount: if targetPageAmount != currentPageAmount:
print("Oops! While resizing up we changed the amount of pages from {} to {}".format(targetPageAmount, currentPageAmount)) logging.warning("Oops! While resizing up we changed the amount of pages from {} to {}".format(targetPageAmount, currentPageAmount))
if self.fontSize != originalFontsize: if self.fontSize != originalFontsize:
print("Managed to change the font size from {} to {}".format(originalFontsize, self.fontSize)) logging.debug("Managed to change the font size from {} to {}".format(originalFontsize, self.fontSize))
"""!@brief Tries to fill in the whitespace on the current render """!@brief Tries to fill in the whitespace on the current render
@ -350,6 +361,8 @@ class Song:
def canFillWhitespace(self): def canFillWhitespace(self):
amountOfPages = len(self.pages) amountOfPages = len(self.pages)
currentPageIt = 0 currentPageIt = 0
totalHorizontalMargin = self.extraHorizontalMargin + self.horizontalMargin + self.horizontalMargin
imageWidthWithoutMargins = self.imageWidth - totalHorizontalMargin
if not amountOfPages: if not amountOfPages:
return False return False
# Stop resizing if we are creating too much widespace on the width # Stop resizing if we are creating too much widespace on the width
@ -357,19 +370,25 @@ class Song:
biggestWhitespace = -1 biggestWhitespace = -1
for page in self.pages: for page in self.pages:
for section in page.sections: for section in page.sections:
whitespaceOnWidth = self.imageWidth - self.leftMargin - self.rightMargin - section.expectedWidth # We have 2* horizontal whitespace
whitespaceOnWidth = self.imageWidth - totalHorizontalMargin - section.expectedWidth
if whitespaceOnWidth < smallestWhitespace: if whitespaceOnWidth < smallestWhitespace:
smallestWhitespace = whitespaceOnWidth smallestWhitespace = whitespaceOnWidth
if whitespaceOnWidth > biggestWhitespace: if whitespaceOnWidth > biggestWhitespace:
biggestWhitespace = whitespaceOnWidth biggestWhitespace = whitespaceOnWidth
# Sections vary in width, some are very small to begin with # Sections vary in width, some are very small to begin with
# Since (almost empty) lines will result in large whitespace sizes, we are less strict on checking that logging.debug("The shortest line has {} whitespace, the largest line {}. The image is {} wide with {} total horizontal margins (={}), resulting in a {} min ratio and {} max ratio, with a min limit of {} and a max limit of {}".format(biggestWhitespace, smallestWhitespace, self.imageWidth, totalHorizontalMargin, imageWidthWithoutMargins, biggestWhitespace / imageWidthWithoutMargins, smallestWhitespace / imageWidthWithoutMargins, self.shortestLineWhitespaceRatioAllowed, self.longestLineWhitespaceRatioAllowed))
if biggestWhitespace / self.imageWidth > self.lowestWhitespaceOnWidthRatioAllowed: # Make sure small lines fill the page enough
print("Stopping resizing down, since the smallest section has {}% whitespace on the width of the image".format((biggestWhitespace / self.imageWidth )* 100)) if biggestWhitespace / imageWidthWithoutMargins > self.shortestLineWhitespaceRatioAllowed:
logging.debug("Stopping resizing down, since the smallest section has {}% whitespace on the width of the image".format((biggestWhitespace / imageWidthWithoutMargins )* 100))
return False return False
# But the largest section on the page should be able to fit at least half of the available page # Make sure the longest lines fill the page enough
if smallestWhitespace / self.imageWidth > self.hightestWhitespaceOnWidthRatioAllowed: if smallestWhitespace / imageWidthWithoutMargins > self.longestLineWhitespaceRatioAllowed:
print("Stopping resizing down, since we largest section has {}% whitespace on the width of the image".format((smallestWhitespace / self.imageWidth )* 100)) logging.debug("Stopping resizing down, since we largest section has {}% whitespace on the width of the image".format((smallestWhitespace / imageWidthWithoutMargins )* 100))
return False
# Make sure the longest lines fill the page enough
if self.fontSize < int(self.ppi / 6):
logging.debug("Stopping resizing down, since the font size is becoming too small at {}".format(self.fontSize))
return False return False
# get first section on next page, if we have a next page to begin with # get first section on next page, if we have a next page to begin with
while currentPageIt < amountOfPages - 1: while currentPageIt < amountOfPages - 1:
@ -379,8 +398,8 @@ class Song:
whitespace = self.imageHeight - curPage.totalHeight whitespace = self.imageHeight - curPage.totalHeight
amountWeAreShort = nextFirstSection.expectedHeight - whitespace amountWeAreShort = nextFirstSection.expectedHeight - whitespace
shortInPercentages = amountWeAreShort / self.imageHeight shortInPercentages = amountWeAreShort / self.imageHeight
#print("Whitespace {} vs next section height {}".format(whitespace, nextFirstSection.expectedHeight)) logging.debug("Whitespace {} vs next section height {}".format(whitespace, nextFirstSection.expectedHeight))
#print("We are {} short to fit the next image (total image height {} => {}% of total height)".format(amountWeAreShort, self.imageHeight, shortInPercentages*100)) logging.debug("We are {} short to fit the next image (total image height {} => {}% of total height)".format(amountWeAreShort, self.imageHeight, shortInPercentages*100))
# Since we also resize based on minimum required whitespaces, we can be a bit more aggressive with this # Since we also resize based on minimum required whitespaces, we can be a bit more aggressive with this
if shortInPercentages < self.tryToShrinkRatio: if shortInPercentages < self.tryToShrinkRatio:
return True return True
@ -392,22 +411,28 @@ class Song:
@return None @return None
""" """
def sectionsToPages(self): def sectionsToPages(self):
# If we are keeping whitespace, don't count the whitespace in between sections
sectionWhitespace = self.verticalMargin
if self.keepEmptyLines:
sectionWhitespace = 0
self.prerenderSections() self.prerenderSections()
self.pages = [] self.pages = []
# First page contains metadata # First page contains metadata
currentHeight = self.topMargin currentHeight = self.verticalMargin
currentHeight += self.metadataHeight currentHeight += self.metadataHeight
currentHeight += self.topMargin currentHeight += sectionWhitespace
curPage = Page() curPage = Page()
# Now fit all sections # Now fit all sections
for section in self.sections: for section in self.sections:
if (section.expectedHeight == -1 or section.expectedWidth == -1): if (section.expectedHeight == -1 or section.expectedWidth == -1):
print("Warning: this file was not processed correctly. The expected dimensions are not set") logging.critical("Warning: this file was not processed correctly. The expected dimensions are not set")
self.pages = []
return
# See if the section would fit on the current page - if it does not, we have a filled page # See if the section would fit on the current page - if it does not, we have a filled page
if currentHeight + section.expectedHeight > self.imageHeight: if currentHeight + section.expectedHeight > self.imageHeight:
curPage.totalHeight = currentHeight curPage.totalHeight = currentHeight
self.pages.append(curPage) self.pages.append(curPage)
currentHeight = self.topMargin currentHeight = sectionWhitespace
curPage = Page() curPage = Page()
# Add setion header size and size of lines of data # Add setion header size and size of lines of data
headerWidth, headerHeight = self.fontTablature.getsize(section.header) headerWidth, headerHeight = self.fontTablature.getsize(section.header)
@ -415,7 +440,7 @@ class Song:
currentHeight += section.expectedHeight currentHeight += section.expectedHeight
curPage.sections.append(section) curPage.sections.append(section)
# Margin between each section # Margin between each section
currentHeight += self.topMargin currentHeight += sectionWhitespace
# No more sections left, so the current buffered image is ready to be written to file # No more sections left, so the current buffered image is ready to be written to file
curPage.totalHeight = currentHeight curPage.totalHeight = currentHeight
self.pages.append(curPage) self.pages.append(curPage)
@ -431,23 +456,23 @@ class Song:
# While not EOF: build sections until new section found. # While not EOF: build sections until 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") logging.error("Cannot parse input file, since it is not delimited by '[<sectionName>]' entries")
return return
# Start with metadata # Start with metadata
self.metadata = parseData[:delimiterIndex] self.metadata = parseData[:delimiterIndex]
print("Set '{}' as metadata".format(self.metadata)) logging.debug("Set '{}' as metadata".format(self.metadata))
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
lines = parseData.splitlines(True) lines = parseData.splitlines(True)
if not len(lines): if not len(lines):
return return
#print("We found {} lines of data".format(len(lines))) logging.debug("We found {} lines of data".format(len(lines)))
# Init first section by popping the delimiter # Init first section by popping the delimiter
thisSection = Section() thisSection = Section()
thisSection.header = lines.pop(0) thisSection.header = lines.pop(0)
# First line is always tab->lyric # First line is always tab->lyric
isTabLine = True isTabLine = True
print("First header is '{}'".format(thisSection.header)) logging.debug("First header is '{}'".format(thisSection.header))
for line in lines: for line in lines:
# If it is a [header], it is a new section # If it is a [header], it is a new section
if line[0] == '[': if line[0] == '[':
@ -456,20 +481,20 @@ class Song:
if thisSection.isParsed: if thisSection.isParsed:
self.sections.append(thisSection) self.sections.append(thisSection)
else: else:
print("Aborting parse due to section not being parseable.") logging.error("Aborting parse due to section not being parseable.")
return return
# Reset, new section # Reset, new section
thisSection = Section() thisSection = Section()
thisSection.header = line thisSection.header = line
#print("Header is '{}'".format(thisSection.header)) logging.debug("Header is '{}'".format(thisSection.header))
isTabLine = True isTabLine = True
# Else is has lines in order tabline->lyricline->repeat # Else is has lines in order tabline->lyricline->repeat
elif isTabLine: elif isTabLine:
#print("Adding Tabline is '{}'".format(line)) logging.debug("Adding Tabline is '{}'".format(line))
thisSection.tablatures.append(line) thisSection.tablatures.append(line)
isTabLine = False isTabLine = False
else: else:
#print("Adding Lyricline is '{}'".format(line)) logging.debug("Adding Lyricline is '{}'".format(line))
thisSection.lyrics.append(line) thisSection.lyrics.append(line)
isTabLine = True isTabLine = True
# Add final section data # Add final section data
@ -477,7 +502,7 @@ class Song:
if thisSection.isParsed: if thisSection.isParsed:
self.sections.append(thisSection) self.sections.append(thisSection)
else: else:
print("Aborting parse due to section not being parseable.") logging.error("Aborting parse due to section not being parseable.")
return return
self.isParsed = True self.isParsed = True
@ -489,15 +514,15 @@ class Song:
self.rawData = readSourceFile(self.inputFile) self.rawData = readSourceFile(self.inputFile)
# Clean up input # Clean up input
parseData = stripEmptyLines(self.rawData, self.keepEmptyLines) parseData = stripEmptyLines(self.rawData, self.keepEmptyLines)
#print("Clean data='{}'\n".format(parseData)) logging.debug("Clean data='{}'\n".format(parseData))
# While not EOF: build sections until new section found. # While not EOF: build sections until 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") logging.error("Cannot parse input file, since it is not delimited by '[<sectionName>]' entries")
return return
# Start with metadata # Start with metadata
self.metadata = parseData[:delimiterIndex] self.metadata = parseData[:delimiterIndex]
#print("Set '{}' as metadata".format(self.metadata)) logging.debug("Set '{}' as metadata".format(self.metadata))
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:
@ -506,7 +531,7 @@ class Song:
# Get header on the 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, delimiter did not match '[<sectionName>]'") logging.error("Cannot parse input file, delimiter did not match '[<sectionName>]'")
return return
# Skip the ']\r\n' characters # Skip the ']\r\n' characters
thisSection.header = parseData[:delimiterIndex+3] thisSection.header = parseData[:delimiterIndex+3]
@ -521,14 +546,14 @@ class Song:
else: else:
# Set thisSection's data and remove it from the buffer # Set thisSection's data and remove it from the buffer
thisSection.rawData = parseData[:delimiterIndex] thisSection.rawData = parseData[:delimiterIndex]
#print("set rawData of '{}' to this section".format(thisSection.rawData)) logging.debug("set rawData of '{}' to this section".format(thisSection.rawData))
parseData = parseData[delimiterIndex:] parseData = parseData[delimiterIndex:]
# Finally parse section data # Finally parse section data
thisSection.initSections() thisSection.initSections()
if thisSection.isParsed: if thisSection.isParsed:
self.sections.append(thisSection) self.sections.append(thisSection)
else: else:
print("Aborting parse due to section not being parseable.") logging.error("Aborting parse due to section not being parseable.")
return return
self.isParsed = True self.isParsed = True

View File

@ -17,6 +17,7 @@
import lib.dataStructures import lib.dataStructures
import lib.config import lib.config
import os import os
import logging
"""!@brief Creates and inits a Song object """!@brief Creates and inits a Song object
This function creates a new Song object and sets the internal variables correctly This function creates a new Song object and sets the internal variables correctly
@ -32,7 +33,7 @@ def initSong(filePath):
thisSong.outputLocation = filePath[:filePath.rfind('.')] thisSong.outputLocation = filePath[:filePath.rfind('.')]
# title is just the name of the .txt file # title is just the name of the .txt file
thisSong.title = thisSong.outputLocation[filePath.rfind('/')+1:] thisSong.title = thisSong.outputLocation[filePath.rfind('/')+1:]
#print("Finished init for input file '{}'.\nBase output folder is '{}'\nSong title is '{}'\n".format(thisSong.inputFile, thisSong.outputLocation, thisSong.title)) logging.debug("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 Creates a list of files found in a directory and its subdirectories """!@brief Creates a list of files found in a directory and its subdirectories
@ -43,7 +44,7 @@ def initSong(filePath):
""" """
def walkDirectory(root, depth): def walkDirectory(root, depth):
pathList = [] pathList = []
#print("Walking directory '{}'".format(root)) logging.debug("Walking directory '{}'".format(root))
def do_scan(start_dir,output,depth=2): def do_scan(start_dir,output,depth=2):
for f in os.listdir(start_dir): for f in os.listdir(start_dir):
ff = os.path.join(start_dir,f) ff = os.path.join(start_dir,f)
@ -73,10 +74,10 @@ def getSongObjects():
for inputFolder in configObj['inputfolders'].split(','): for inputFolder in configObj['inputfolders'].split(','):
for filePath in walkDirectory(inputFolder, recursionDepth): for filePath in walkDirectory(inputFolder, recursionDepth):
if ((filePath[filePath.find('.'):] == ".txt" ) and configObj['readtxt'] == '1') or ((filePath[filePath.find('.'):] == ".rawtxt" ) and configObj['readraw'] == '1'): if ((filePath[filePath.find('.'):] == ".txt" ) and configObj['readtxt'] == '1') or ((filePath[filePath.find('.'):] == ".rawtxt" ) and configObj['readraw'] == '1'):
#print("Found supported file '{}'".format(filePath)) logging.debug("Found supported file '{}'".format(filePath))
txtFileLocations.append(filePath) txtFileLocations.append(filePath)
#else: else:
#print("Skipping file '{}' for it is not a supported file".format(filePath)) logging.debug("Skipping file '{}' for it is not a supported file".format(filePath))
# create list of Song objects # create list of Song objects
while(txtFileLocations): while(txtFileLocations):
filePath = txtFileLocations.pop() filePath = txtFileLocations.pop()

53
main.py
View File

@ -14,11 +14,7 @@
# as best as it can, shrinking or growing sections to fit the remaining space # as best as it can, shrinking or growing sections to fit the remaining space
# #
# @section notes Notes # @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
@ -27,6 +23,7 @@ import lib.transpose
import lib.config import lib.config
import output2img import output2img
import output2txt import output2txt
import logging
def main(): def main():
# Init config file # Init config file
@ -39,34 +36,53 @@ def main():
exportToTxt = configObj['exporttotxt'] == '1' exportToTxt = configObj['exporttotxt'] == '1'
exportToRaw = configObj['exporttoraw'] == '1' exportToRaw = configObj['exporttoraw'] == '1'
logLevel = int(configObj['loglevel'])
if logLevel == 1:
logLevel = logging.CRITICAL
elif logLevel == 2:
logLevel = logging.ERROR
elif logLevel == 3:
logLevel = logging.WARNING
elif logLevel == 4:
logLevel = logging.INFO
else:
logLevel = logging.DEBUG
logging.basicConfig()
logging.root.setLevel(logLevel)
logging.debug('Starting')
for song in songs:
logging.info("Found song '{}' at '{}'".format(song.title, song.inputFile))
# Convert all songs into sections # Convert all songs into sections
for song in songs: for song in songs:
print("Start parsing of file '{}'...".format(song.inputFile)) logging.info("Start parsing song '{}'...".format(song.title))
# Initialise internal data structures # Initialise internal data structures
print("song file extension {}".format(song.fileExtension)) logging.debug("song file extension {}".format(song.fileExtension))
if song.fileExtension == 'txt': if song.fileExtension == 'txt':
song.initSections() song.initSections()
elif song.fileExtension == 'rawtxt': elif song.fileExtension == 'rawtxt':
song.initPreprocessed() song.initPreprocessed()
else: else:
print("File extension '{}' not supported. Skipping...".format(song.fileExtension)) logging.warning("File extension '{}' not supported. Skipping...".format(song.fileExtension))
continue continue
# If input is .raw output. If output to raw is set, overwrite itself # If input is .raw output. If output to raw is set, overwrite itself
# ready quickly using rules # ready quickly using rules
if not song.isParsed: if not song.isParsed:
print("Song was not initialized correctly. Skipping...") logging.error("Song was not initialized correctly. Skipping...")
continue continue
if exportToTxt: if exportToTxt:
# Create subdirectory where we will output our images # Create subdirectory where we will output our images
targetDirectory = song.outputLocation + "-txt" targetDirectory = song.outputLocation + "-txt"
print("Successfully parsed file. Writing output to '{}'\n".format(targetDirectory)) logging.info("Successfully parsed file. Writing output to '{}'\n".format(targetDirectory))
# Write out metadata and sections, as many as can fit on one page # Write out metadata and sections, as many as can fit on one page
output2txt.outputToTxt(targetDirectory, False, song) output2txt.outputToTxt(targetDirectory, False, song)
if exportToRaw: if exportToRaw:
# Create subdirectory where we will output our images # Create subdirectory where we will output our images
targetDirectory = song.outputLocation + "-txt" targetDirectory = song.outputLocation + "-txt"
print("Successfully parsed file. Writing output to '{}'\n".format(targetDirectory)) logging.info("Successfully parsed file. Writing output to '{}'\n".format(targetDirectory))
# Write out metadata and sections, as many as can fit on one page # Write out metadata and sections, as many as can fit on one page
output2txt.outputToTxt(targetDirectory, True, song) output2txt.outputToTxt(targetDirectory, True, song)
if exportToImg: if exportToImg:
@ -75,18 +91,23 @@ def main():
# Prerender: calculate Pages, and move sections into Pages # Prerender: calculate Pages, and move sections into Pages
song.sectionsToPages() song.sectionsToPages()
# Optimalisation: try to fill whitespace # Optimalisation: try to fill whitespace
while song.canFillWhitespace(): while len(song.pages) > song.maxPages:
print("Resizing down to fit whitespace more efficiently") logging.debug("Resizing down since we have {} pages and want {} pages".format(len(song.pages), song.maxPages))
song.resizeAllSections(-1) song.resizeAllSections(-1)
song.sectionsToPages() song.sectionsToPages()
# Optimalisation: increase font size as long as the amount of pages does not increase or we cause an overflow on width while song.canFillWhitespace():
song.increaseWhileSameAmountOfPages() logging.debug("Resizing down to fill remaining vertical whitespace")
song.resizeAllSections(-1)
song.sectionsToPages()
# Optimalisation: increase font size to fit target page amount
song.increaseToMinPages()
# Parse as PNG a4 # Parse as PNG a4
# Create subdirectory where we will output our images # Create subdirectory where we will output our images
targetDirectory = song.outputLocation + "-a4-png" targetDirectory = song.outputLocation + "-a4-png"
print("Successfully parsed file. Writing output to '{}'\n".format(targetDirectory)) logging.info("Successfully parsed file. Writing output to '{}'\n".format(targetDirectory))
# Write out metadata and sections, as many as can fit on one page # Write out metadata and sections, as many as can fit on one page
output2img.outputToImage(targetDirectory, song) output2img.outputToImage(targetDirectory, song)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
logging.debug('Finished')

View File

@ -11,11 +11,10 @@
# @section notes Notes # @section notes Notes
# - # -
# #
# @section todo TODO
# - Various prints should be printed at specific log levels, to easily switch between debug, info or warnings only
import os import os
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
import logging
"""!@brief Exports the song object to images """!@brief Exports the song object to images
This function renders the metadata and sections This function renders the metadata and sections
@ -30,69 +29,80 @@ def outputToImage(folderLocation, songObj):
# Create target Directory if doesn'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 ") logging.info("Directory {} Created ".format(folderLocation))
#else: else:
#print("Directory " , folderLocation , " already exists") logging.debug("Directory {} already exists".format(folderLocation))
# Init image info # Init image info
imageNumber = 1 imageNumber = 1
currentHeight = songObj.topMargin currentHeight = songObj.verticalMargin
# New Image # New Image
a4image = Image.new('RGB',(songObj.imageWidth, songObj.imageHeight),(songObj.backgroundColour)) a4image = Image.new('RGB',(songObj.imageWidth, songObj.imageHeight),(songObj.backgroundColour))
draw = ImageDraw.Draw(a4image) draw = ImageDraw.Draw(a4image)
# Add extra whitespace on the left if this is an even page
# The whitespace on the right for uneven pages is handled elsewhere, by limiting the maximum horizontal size
horizontalMargin = songObj.horizontalMargin
if (imageNumber % 2) == 0:
horizontalMargin += songObj.extraHorizontalMargin
# Write metadata # Write metadata
for line in songObj.metadata.split('\n'): for line in songObj.metadata.split('\n'):
# remove any unwanted characters from metadata # remove any unwanted characters from metadata
line = line.rstrip() line = line.rstrip()
if not line and not songObj.keepEmptyLines: if not line and not songObj.keepEmptyLines:
continue continue
#print("meta line '{}'".format(line)) logging.debug("Metadata '{}'".format(line))
metadataTextWidth, metadataTextHeight = songObj.fontMetadata.getsize(line) metadataTextWidth, metadataTextHeight = songObj.fontMetadata.getsize(line)
draw.text((songObj.leftMargin,currentHeight), line, fill=songObj.metadataColour, font=songObj.fontMetadata) draw.text((horizontalMargin, currentHeight), line, fill=songObj.metadataColour, font=songObj.fontMetadata)
currentHeight += metadataTextHeight currentHeight += metadataTextHeight
# Draw all pages # Draw all pages
for page in songObj.pages: for page in songObj.pages:
# Margin between metadata and the first section / section and top of page # Margin between metadata and the first section / section and top of page
currentHeight += songObj.topMargin currentHeight += songObj.verticalMargin
for section in page.sections: for section in page.sections:
# Reset section specific variables # Reset section specific variables
lineIterator = 0 lineIterator = 0
amountOfLines = len(section.lyrics) amountOfLines = len(section.lyrics)
if (amountOfLines != len(section.tablatures)): if (amountOfLines != len(section.tablatures)):
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)) logging.critical("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
if (section.expectedHeight == -1 or section.expectedWidth == -1): if (section.expectedHeight == -1 or section.expectedWidth == -1):
print("Cannot write this section to file, since it was not processed correctly. The expected dimensions are not set. Aborting...") logging.critical("Cannot write this section to file, since it was not processed correctly. The expected dimensions are not set. Aborting...")
return return
# write section title # write section title
headerWidth, headerHeight = songObj.fontTablature.getsize(section.header) headerWidth, headerHeight = songObj.fontTablature.getsize(section.header)
draw.text((songObj.leftMargin,currentHeight), section.header, fill=songObj.fontColour, font=songObj.fontTablature) draw.text((horizontalMargin ,currentHeight), section.header, fill=songObj.fontColour, font=songObj.fontTablature)
currentHeight += headerHeight currentHeight += headerHeight
# Write each line tablature&lyric data # Write each line tablature&lyric data
while lineIterator < amountOfLines: while lineIterator < amountOfLines:
#print("Printing tablatures line {} and lyrics line {}".format(section.tablatures[lineIterator], section.lyrics[lineIterator])) logging.debug("Printing tablatures line {} and lyrics line {}".format(section.tablatures[lineIterator], section.lyrics[lineIterator]))
# Get tablatures&lyric line # Get tablatures&lyric line
lyricTextWidth, lyricTextHeight = songObj.fontLyrics.getsize(section.lyrics[lineIterator]) lyricTextWidth, lyricTextHeight = songObj.fontLyrics.getsize(section.lyrics[lineIterator])
tablatureTextWidth, tablatureTextHeight = songObj.fontTablature.getsize(section.tablatures[lineIterator]) tablatureTextWidth, tablatureTextHeight = songObj.fontTablature.getsize(section.tablatures[lineIterator])
# add to image file # add to image file
draw.text((songObj.leftMargin,currentHeight), section.tablatures[lineIterator], fill=songObj.fontColour, font=songObj.fontTablature) draw.text((horizontalMargin ,currentHeight), section.tablatures[lineIterator], fill=songObj.fontColour, font=songObj.fontTablature)
currentHeight += tablatureTextHeight currentHeight += tablatureTextHeight
draw.text((songObj.leftMargin,currentHeight), section.lyrics[lineIterator], fill=songObj.fontColour, font=songObj.fontLyrics) draw.text((horizontalMargin ,currentHeight), section.lyrics[lineIterator], fill=songObj.fontColour, font=songObj.fontLyrics)
currentHeight += lyricTextHeight currentHeight += lyricTextHeight
lineIterator += 1 lineIterator += 1
#print("currentheight={}".format(currentHeight)) logging.debug("currentheight={}".format(currentHeight))
# If we stripped al whitespace, we need to add whitespace between sections # If we stripped al whitespace, we need to add whitespace between sections
if not songObj.keepEmptyLines: if not songObj.keepEmptyLines:
currentHeight += songObj.topMargin currentHeight += songObj.verticalMargin
# Got all sections in the page, so write it # Got all sections in the page, so write it
outputLocation = folderLocation + "/" + songObj.title + '-' + str(imageNumber) + ".png" outputLocation = folderLocation + "/" + songObj.title + '-' + str(imageNumber) + ".png"
a4image.save(outputLocation) a4image.save(outputLocation)
a4image = Image.new('RGB',(songObj.imageWidth, songObj.imageHeight),(songObj.backgroundColour)) a4image = Image.new('RGB',(songObj.imageWidth, songObj.imageHeight),(songObj.backgroundColour))
draw = ImageDraw.Draw(a4image) draw = ImageDraw.Draw(a4image)
currentHeight = songObj.topMargin currentHeight = songObj.verticalMargin
imageNumber += 1 imageNumber += 1
# Add extra whitespace on the left if this is an even page
# The whitespace on the right for uneven pages is handled elsewhere, by limiting the maximum horizontal size
horizontalMargin = songObj.horizontalMargin
if (imageNumber % 2) == 0:
horizontalMargin += songObj.extraHorizontalMargin

View File

@ -8,13 +8,9 @@
# Generates PNG images of a specific dimension (currently A4) of tablature data # 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) # Dynamically resizes specific sections to maximize using the entire paper (and avoid awkward page flips)
# #
# @section notes Notes
# -
#
# @section todo TODO
# - Various prints should be printed at specific log levels, to easily switch between debug, info or warnings only
import os import os
import logging
"""!@brief Exports the song object to a txt file """!@brief Exports the song object to a txt file
Perfect to use as source file for any program which requires Perfect to use as source file for any program which requires
@ -34,9 +30,9 @@ def outputToTxt(folderLocation, printRaw, songObj):
# Create target Directory if doesn'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 ") logging.info("Directory {} Created ".format(folderLocation))
#else: else:
#print("Directory " , folderLocation , " already exists") logging.debug("Directory {} already exists".format(folderLocation))
output = "" output = ""
emptyLines = [] emptyLines = []
@ -51,7 +47,7 @@ def outputToTxt(folderLocation, printRaw, songObj):
# remove any unwanted characters from metadata # remove any unwanted characters from metadata
if not songObj.keepEmptyLines and not line: if not songObj.keepEmptyLines and not line:
continue continue
#print("meta line '{}'".format(line)) logging.debug("Metadata '{}'".format(line))
output += line output += line
metadataLines.append(lineCounter) metadataLines.append(lineCounter)
lineCounter += 1 lineCounter += 1
@ -67,7 +63,7 @@ def outputToTxt(folderLocation, printRaw, songObj):
lineIterator = 0 lineIterator = 0
amountOfLines = len(section.lyrics) amountOfLines = len(section.lyrics)
if (amountOfLines != len(section.tablatures)): if (amountOfLines != len(section.tablatures)):
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)) logging.critical("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
# write section title # write section title
output += section.header.rstrip() + '\r\n' output += section.header.rstrip() + '\r\n'