Added optimalisation to resize down to fit whitespace better

Also increased default font size, since we resize down if needed
A lower default font size will speed up the program
fixes #8
This commit is contained in:
Marco van Dijk 2021-07-09 16:20:11 +02:00
parent 051ee1dee2
commit bf0352a600
3 changed files with 45 additions and 7 deletions

View File

@ -36,7 +36,7 @@ def initConfig():
'metaFontWeight': 8, 'metaFontWeight': 8,
'lyricfontfamily': 'fonts/CourierPrime-Regular.ttf', 'lyricfontfamily': 'fonts/CourierPrime-Regular.ttf',
'tablaturefontfamliy': 'fonts/CourierPrime-Bold.ttf', 'tablaturefontfamliy': 'fonts/CourierPrime-Bold.ttf',
'songFontWeight': 14, 'songFontWeight': 16,
'imageWidth': 595, 'imageHeight': 842, # A4 at 72dpi 'imageWidth': 595, 'imageHeight': 842, # A4 at 72dpi
'backgroundColour': '255,255,255', 'backgroundColour': '255,255,255',
'fontColour': '0,0,0', 'fontColour': '0,0,0',

View File

@ -100,6 +100,7 @@ class Section:
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])
@ -123,8 +124,11 @@ class Section:
# So we have to insert empty lines if we have subsequent tablature or lyric lines # So we have to insert empty lines if we have subsequent tablature or lyric lines
lines = self.rawData.split('\r\n') lines = self.rawData.split('\r\n')
for line in lines: for line in lines:
if not len(line):
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))
# Initially just fill in the first line correctly # Initially just fill in the first line correctly
if isFirstLine: if isFirstLine:
isFirstLine = False isFirstLine = False
@ -153,7 +157,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 tablature and lyric lines.") 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)))
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):
@ -225,6 +229,7 @@ class Song:
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))
"""!@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
@ -232,6 +237,7 @@ class Song:
@return None @return None
""" """
def resizeAllSections(self, mutator): def resizeAllSections(self, mutator):
print("Resizing font by {} to {}".format(mutator, self.fontSize))
self.fontSize += mutator self.fontSize += mutator
self.fontLyrics = ImageFont.truetype(self.configObj['lyricfontfamily'], self.fontSize) self.fontLyrics = ImageFont.truetype(self.configObj['lyricfontfamily'], self.fontSize)
self.fontTablature = ImageFont.truetype(self.configObj['tablaturefontfamliy'], self.fontSize) self.fontTablature = ImageFont.truetype(self.configObj['tablaturefontfamliy'], self.fontSize)
@ -251,8 +257,8 @@ class Song:
def fitSectionsByWidth(self): def fitSectionsByWidth(self):
self.prerenderSections() self.prerenderSections()
while not self.checkOverflowX(): while not self.checkOverflowX():
#print("Overflowing on width of the page. Decreasing font size by 2...") #print("Resizing down to prevent overflow on the width of the page")
self.resizeAllSections(-2) self.resizeAllSections(-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
@return True if everything OK, False if overflowing @return True if everything OK, False if overflowing
@ -260,14 +266,43 @@ 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.leftMargin - self.rightMargin:
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))
return False return False
return True return True
"""!@brief Tries to fill in the whitespace on the current render
It will compare the size of existing whitespace with the size of the first section on the next page
While the amount we are short is within 10% of the current image height, resize down
@return True if we should resize down, False if we are fine
"""
def canFillWhitespace(self):
amountOfPages = len(self.pages)
currentPageIt = 0
if not amountOfPages:
return False
# get first section on next page, if we have a next page to begin with
while currentPageIt < amountOfPages - 1:
curPage = self.pages[currentPageIt]
nextPage = self.pages[currentPageIt + 1]
nextFirstSection = nextPage.sections[0]
whitespace = self.imageHeight - curPage.totalHeight
amountWeAreShort = nextFirstSection.expectedHeight - whitespace
shortInPercentages = amountWeAreShort / self.imageHeight
# Take a 10% range
#print("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))
if shortInPercentages < 0.10:
return True
currentPageIt += 1
return False
"""!@brief Fits current sections into pages """!@brief Fits current sections into pages
@return None @return None
""" """
def sectionsToPages(self): def sectionsToPages(self):
self.prerenderSections() self.prerenderSections()
self.pages = []
# First page contains metadata # First page contains metadata
currentHeight = self.topMargin currentHeight = self.topMargin
currentHeight += self.metadataHeight currentHeight += self.metadataHeight

View File

@ -41,13 +41,16 @@ def main():
song.fitSectionsByWidth() song.fitSectionsByWidth()
# Prerender: calculate Pages, and move sections into Pages # Prerender: calculate Pages, and move sections into Pages
song.sectionsToPages() song.sectionsToPages()
# Optimalisation: check for whitespace, check verhouding of whitespace&first section on next page # Optimalisation: try to fill whitespace
# TODO while song.canFillWhitespace():
print("Resizing down to fit whitespace more efficiently")
song.resizeAllSections(-1)
song.sectionsToPages()
# Parse as PNG a4 # Parse as PNG a4
if song.isParsed: if song.isParsed:
# 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 '{}'".format(targetDirectory)) print("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)