mirror of
https://github.com/stronk-dev/Guitar-Sheet-Parser.git
synced 2025-07-05 16:35:10 +02:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
2571dcec5b | |||
|
4ab8f6c836 | ||
|
30109a0a7f | ||
|
568054b986 | ||
|
9992b7ac36 | ||
|
f26e80b245 |
@ -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:
|
||||||
|
@ -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.sectionsToPages()
|
|
||||||
currentPageAmount = len(self.pages)
|
|
||||||
# Increase fontSize as long as we do not add a page
|
|
||||||
while currentPageAmount <= targetPageAmount and self.checkOverflowX():
|
|
||||||
self.resizeAllSections(+1)
|
self.resizeAllSections(+1)
|
||||||
self.sectionsToPages()
|
self.sectionsToPages()
|
||||||
currentPageAmount = len(self.pages)
|
currentPageAmount = len(self.pages)
|
||||||
|
# Increase fontSize as long as we stay under the target max pages
|
||||||
|
while ((currentPageAmount <= targetPageAmount) and self.checkOverflowX()):
|
||||||
|
self.resizeAllSections(+1)
|
||||||
|
self.sectionsToPages()
|
||||||
|
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
|
||||||
|
|
||||||
|
@ -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
53
main.py
@ -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')
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user