Skip to content

Commit a89b3fa

Browse files
committed
create-draft: Create correct cover and titlepage SVGs, and update cover and titlepage SVG styling
1 parent 46b1fd7 commit a89b3fa

File tree

7 files changed

+211
-75
lines changed

7 files changed

+211
-75
lines changed

build-images

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,8 @@ def clean_inkscape_svg(filename, clean_path):
2121
svg = regex.sub(r"<defs[^>]*?/>", "", svg, flags=regex.MULTILINE | regex.DOTALL)
2222
svg = regex.sub(r"xmlns:(dc|cc|rdf)=\"[^\"]*?\"", "", svg, flags=regex.MULTILINE | regex.DOTALL)
2323

24-
# Inkscape includes font and letter-spacing info even though we've removed font information
25-
svg = regex.sub(r"font-.+?:.+?([;\"])", "\\1", svg)
26-
svg = regex.sub(r"text-anchor:.+?([;\"])", "\\1", svg)
27-
svg = regex.sub(r"letter-spacing:.+?([;\"])", "\\1", svg)
28-
svg = regex.sub(r";+", ";", svg)
29-
svg = regex.sub(r" style=\";", " style=\"", svg)
30-
svg = regex.sub(r" style=\"\"", "", svg)
24+
# Inkscape includes CSS even though we've removed font information
25+
svg = regex.sub(r" style=\".*?\"", "", svg)
3126

3227
file.seek(0)
3328
file.write(svg)
@@ -113,16 +108,23 @@ def main():
113108

114109
# Embed cover.jpg
115110
with open(dest_cover_svg_filename, "r+", encoding="utf-8") as file:
116-
svg = file.read()
117-
118-
svg = regex.sub(r"xlink:href=\".*?cover\.jpg", "xlink:href=\"data:image/jpeg;base64," + source_cover_jpg_base64, svg, flags=regex.MULTILINE | regex.DOTALL)
111+
svg = regex.sub(r"xlink:href=\".*?cover\.jpg", "xlink:href=\"data:image/jpeg;base64," + source_cover_jpg_base64, file.read(), flags=regex.MULTILINE | regex.DOTALL)
119112

120113
file.seek(0)
121114
file.write(svg)
122115
file.truncate()
123116

124117
clean_inkscape_svg(dest_cover_svg_filename, clean_path)
125118

119+
# For the cover we want to keep the path.title-box style, and add an additional
120+
# style to color our new paths white
121+
with open(dest_cover_svg_filename, "r+", encoding="utf-8") as file:
122+
svg = regex.sub(r"<style.+?</style>", "<style type=\"text/css\">\n\t\tpath{\n\t\t\tfill: #fff;\n\t\t}\n\n\t\t.title-box{\n\t\t\tfill: #000;\n\t\t\tfill-opacity: .75;\n\t\t}\n\t</style>", file.read(), flags=regex.DOTALL)
123+
124+
file.seek(0)
125+
file.write(svg)
126+
file.truncate()
127+
126128
if args.verbose:
127129
print(" OK")
128130
else:
@@ -143,6 +145,14 @@ def main():
143145

144146
clean_inkscape_svg(dest_titlepage_svg_filename, clean_path)
145147

148+
# For the titlepage we want to remove all styles, since they are not used anymore
149+
with open(dest_titlepage_svg_filename, "r+", encoding="utf-8") as file:
150+
svg = regex.sub(r"<style.+?</style>[\n\t]+", "", file.read(), flags=regex.DOTALL)
151+
152+
file.seek(0)
153+
file.write(svg)
154+
file.truncate()
155+
146156
if args.verbose:
147157
print(" OK")
148158
else:

create-draft

Lines changed: 115 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,47 @@ from subprocess import call
77
import requests
88
import git
99
import regex
10+
import unicodedata
1011
import se
1112
import se.formatting
1213
import se.epub
1314
from bs4 import BeautifulSoup, UnicodeDammit
1415

15-
def calculate_titlepage_lines(str, ratio):
16-
canvas_width = se.TITLEPAGE_WIDTH - (se.TITLEPAGE_HORIZONTAL_PADDING * 2)
17-
output_words = []
16+
17+
def get_word_widths(str, target_height):
18+
words = []
1819
for word in reversed(str.split()):
1920
width = 0
2021

2122
for char in word:
22-
width += (se.LEAGUE_SPARTAN_80_WIDTHS[char] / ratio) + se.TITLEPAGE_KERNING + se.TITLEPAGE_AVERAGE_SPACING
23+
# Convert accented characters to unaccented characters
24+
char = regex.sub(r"\p{M}", "", unicodedata.normalize("NFKD", char))
25+
width += (se.LEAGUE_SPARTAN_100_WIDTHS[char] * target_height / 100) + se.LEAGUE_SPARTAN_KERNING + se.LEAGUE_SPARTAN_AVERAGE_SPACING
2326

24-
width = width - se.TITLEPAGE_KERNING - se.TITLEPAGE_AVERAGE_SPACING
27+
width = width - se.LEAGUE_SPARTAN_KERNING - se.LEAGUE_SPARTAN_AVERAGE_SPACING
2528

26-
output_words.append({"word": word, "width": width})
29+
words.append({"word": word, "width": width})
2730

31+
return words
32+
33+
def calculate_titlepage_lines(str, target_height, canvas_width):
34+
words = get_word_widths(str, target_height)
2835
lines = []
2936
current_line = ""
3037
current_width = 0
31-
canvas_width = se.TITLEPAGE_WIDTH - (se.TITLEPAGE_HORIZONTAL_PADDING * 2)
32-
for word in output_words:
38+
39+
for word in words:
3340
if current_width == 0:
34-
current_width = current_width + word["width"]
41+
current_width = word["width"]
3542
else:
36-
current_width = current_width + (se.LEAGUE_SPARTAN_80_WIDTHS[" "] / ratio) + word["width"]
43+
current_width = current_width + (se.LEAGUE_SPARTAN_100_WIDTHS[" "] * target_height / 100) + word["width"]
3744

3845
if current_width < canvas_width:
3946
current_line = word["word"] + " " + current_line
4047
else:
4148
lines.append(current_line.strip())
4249
current_line = word["word"]
43-
current_width = 0
50+
current_width = word["width"]
4451

4552
lines.append(current_line.strip())
4653

@@ -60,6 +67,7 @@ def generate_titlepage_svg(title, authors, contributors, title_string, templates
6067
"""
6168

6269
svg = ""
70+
canvas_width = se.TITLEPAGE_WIDTH - (se.TITLEPAGE_HORIZONTAL_PADDING * 2)
6371

6472
if not isinstance(authors, list):
6573
authors = [authors]
@@ -68,27 +76,21 @@ def generate_titlepage_svg(title, authors, contributors, title_string, templates
6876
with open(os.path.join(templates_path, "titlepage.svg" ), "r", encoding="utf-8") as file:
6977
svg = file.read()
7078

71-
# Store the template <text> elements for later use
72-
title_line_html = regex.search(r"<text.+?>TITLE LINE", svg)[0].replace("TITLE LINE", "")
73-
author_line_html = regex.search(r"<text.+?>AUTHOR LINE", svg)[0].replace("AUTHOR LINE", "")
74-
contributor_descriptor_html = regex.search(r"<text.+?>CONTRIBUTOR DESCRIPTOR", svg)[0].replace("CONTRIBUTOR DESCRIPTOR", "")
75-
contributor_line_html = regex.search(r"<text.+?>CONTRIBUTOR LINE", svg)[0].replace("CONTRIBUTOR LINE", "")
76-
7779
# Remove the template text elements from the SVG source, we'll write out to it later
7880
svg = regex.sub(r"\s*<text.+</svg>", "</svg>", svg, flags=regex.DOTALL).strip()
7981

8082
# Calculate the title lines
81-
title_lines = calculate_titlepage_lines(title.upper(), 1)
83+
title_lines = calculate_titlepage_lines(title.upper(), se.TITLEPAGE_TITLE_HEIGHT, canvas_width)
8284

8385
# Calculate the author lines
8486
authors_lines = []
8587
for author in authors:
86-
authors_lines.append(calculate_titlepage_lines(author.upper(), se.TITLEPAGE_LEAGUE_SPARTAN_60_RATIO))
88+
authors_lines.append(calculate_titlepage_lines(author.upper(), se.TITLEPAGE_AUTHOR_HEIGHT, canvas_width))
8789

8890
# Calculate the contributor lines
8991
contributor_lines = []
9092
for descriptor, contributor in contributors.items():
91-
contributor_lines.append([descriptor, calculate_titlepage_lines(contributor.upper(), se.TITLEPAGE_LEAGUE_SPARTAN_40_RATIO)])
93+
contributor_lines.append([descriptor, calculate_titlepage_lines(contributor.upper(), se.TITLEPAGE_CONTRIBUTOR_HEIGHT, canvas_width)])
9294

9395
# Construct the output
9496
text_elements = ""
@@ -97,7 +99,7 @@ def generate_titlepage_svg(title, authors, contributors, title_string, templates
9799
# Add the title
98100
for line in title_lines:
99101
y += se.TITLEPAGE_TITLE_HEIGHT
100-
text_elements += "\t" + title_line_html.replace("y=\"\"", "y=\"{}\"".format(y)) + line + "</text>\n"
102+
text_elements += "\t<text class=\"title\" x=\"700\" y=\"{:.0f}\">{}</text>\n".format(y, line)
101103
y += se.TITLEPAGE_TITLE_MARGIN
102104

103105
y -= se.TITLEPAGE_TITLE_MARGIN
@@ -108,7 +110,7 @@ def generate_titlepage_svg(title, authors, contributors, title_string, templates
108110
for author_lines in authors_lines:
109111
for line in author_lines:
110112
y += se.TITLEPAGE_AUTHOR_HEIGHT
111-
text_elements += "\t" + author_line_html.replace("y=\"\"", "y=\"{}\"".format(y)) + line + "</text>\n"
113+
text_elements += "\t<text class=\"author\" x=\"700\" y=\"{:.0f}\">{}</text>\n".format(y, line)
112114
y += se.TITLEPAGE_AUTHOR_MARGIN
113115

114116
y -= se.TITLEPAGE_AUTHOR_MARGIN
@@ -118,26 +120,107 @@ def generate_titlepage_svg(title, authors, contributors, title_string, templates
118120
y += se.TITLEPAGE_CONTRIBUTORS_SPACING
119121
for contributor in contributor_lines:
120122
y += se.TITLEPAGE_CONTRIBUTOR_DESCRIPTOR_HEIGHT
121-
text_elements += "\t" + contributor_descriptor_html.replace("y=\"\"", "y=\"{}\"".format(y)) + contributor[0] + "</text>\n"
123+
text_elements += "\t<text class=\"contributor-descriptor\" x=\"700\" y=\"{:.0f}\">{}</text>\n".format(y, contributor[0])
122124
y += se.TITLEPAGE_CONTRIBUTOR_MARGIN
123125

124126
for person in contributor[1]:
125127
y += se.TITLEPAGE_CONTRIBUTOR_HEIGHT
126-
text_elements += "\t" + contributor_line_html.replace("y=\"\"", "y=\"{}\"".format(y)) + person + "</text>\n"
128+
text_elements += "\t<text class=\"contributor\" x=\"700\" y=\"{:.0f}\">{}</text>\n".format(y, person)
127129
y += se.TITLEPAGE_CONTRIBUTOR_MARGIN
128130

129131
y -= se.TITLEPAGE_CONTRIBUTOR_MARGIN
130132

131133
y += se.TITLEPAGE_CONTRIBUTOR_DESCRIPTOR_MARGIN
132134

133-
y -= se.TITLEPAGE_CONTRIBUTOR_DESCRIPTOR_MARGIN + se.TITLEPAGE_CONTRIBUTOR_MARGIN
135+
y -= se.TITLEPAGE_CONTRIBUTOR_DESCRIPTOR_MARGIN
136+
else:
137+
# Remove unused CSS
138+
svg = regex.sub(r"\n\t\t\.contributor-descriptor{.+?}\n", "", svg, flags=regex.DOTALL)
139+
svg = regex.sub(r"\n\t\t\.contributor{.+?}\n", "", svg, flags=regex.DOTALL)
134140

135141
y += se.TITLEPAGE_VERTICAL_PADDING
136142

137-
svg = svg.replace("</title>", "</title>\n" + text_elements).replace("HEIGHT\"", "{}\"".format(y)).replace("TITLESTRING", title_string)
143+
svg = svg.replace("</svg>", "\n" + text_elements + "</svg>").replace("TITLESTRING", title_string)
144+
svg = regex.sub(r"viewBox=\".+?\"", "viewBox=\"0 0 1400 {:.0f}\"".format(y), svg)
138145

139146
return svg
140147

148+
def generate_cover_svg(title, authors, title_string, templates_path):
149+
svg = ""
150+
canvas_width = se.COVER_TITLE_BOX_WIDTH - (se.COVER_TITLE_BOX_PADDING * 2)
151+
152+
if not isinstance(authors, list):
153+
authors = [authors]
154+
155+
# Read our template SVG to get some values before we begin
156+
with open(os.path.join(templates_path, "cover.svg" ), "r", encoding="utf-8") as file:
157+
svg = file.read()
158+
159+
# Remove the template text elements from the SVG source, we'll write out to it later
160+
svg = regex.sub(r"\s*<text.+</svg>", "</svg>", svg, flags=regex.DOTALL).strip()
161+
162+
# Calculate the title lines
163+
title_height = se.COVER_TITLE_HEIGHT
164+
title_class = "title"
165+
title_lines = calculate_titlepage_lines(title.upper(), title_height, canvas_width)
166+
167+
if len(title_lines) > 2:
168+
title_height = se.COVER_TITLE_SMALL_HEIGHT
169+
title_class = "title-small"
170+
title_lines = calculate_titlepage_lines(title.upper(), title_height, canvas_width)
171+
172+
if len(title_lines) > 2:
173+
title_height = se.COVER_TITLE_XSMALL_HEIGHT
174+
title_class = "title-xsmall"
175+
title_lines = calculate_titlepage_lines(title.upper(), title_height, canvas_width)
176+
177+
# Calculate the author lines
178+
authors_lines = []
179+
for author in authors:
180+
authors_lines.append(calculate_titlepage_lines(author.upper(), se.COVER_AUTHOR_HEIGHT, canvas_width))
181+
182+
# Construct the output
183+
text_elements = ""
184+
y = se.COVER_TITLE_BOX_Y + \
185+
+ ((se.COVER_TITLE_BOX_HEIGHT \
186+
- ( (len(title_lines) * title_height) \
187+
+ ( (len(title_lines) - 1) * se.COVER_TITLE_MARGIN ) \
188+
+ se.COVER_AUTHOR_SPACING \
189+
+ (len(authors_lines) * se.COVER_AUTHOR_HEIGHT) \
190+
)) / 2)
191+
192+
# Add the title
193+
for line in title_lines:
194+
y += title_height
195+
text_elements += "\t<text class=\"{}\" x=\"700\" y=\"{:.0f}\">{}</text>\n".format(title_class, y, line)
196+
y += se.COVER_TITLE_MARGIN
197+
198+
y -= se.COVER_TITLE_MARGIN
199+
200+
# Add the author(s)
201+
y += se.COVER_AUTHOR_SPACING
202+
203+
for author_lines in authors_lines:
204+
for line in author_lines:
205+
y += se.COVER_AUTHOR_HEIGHT
206+
text_elements += "\t<text class=\"author\" x=\"700\" y=\"{:.0f}\">{}</text>\n".format(y, line)
207+
y += se.COVER_AUTHOR_MARGIN
208+
209+
y -= se.COVER_AUTHOR_MARGIN
210+
211+
# Remove unused CSS
212+
if title_class != "title":
213+
svg = regex.sub(r"\n\n\t\t\.title\{.+?\}", "", svg, flags=regex.DOTALL)
214+
215+
if title_class != "title-small":
216+
svg = regex.sub(r"\n\n\t\t\.title-small\{.+?\}", "", svg, flags=regex.DOTALL)
217+
218+
if title_class != "title-xsmall":
219+
svg = regex.sub(r"\n\n\t\t\.title-xsmall\{.+?\}", "", svg, flags=regex.DOTALL)
220+
221+
svg = svg.replace("</svg>", "\n" + text_elements + "</svg>").replace("TITLESTRING", title_string)
222+
223+
return svg
141224

142225
def get_wikipedia_url(string, get_nacoaf_url=False):
143226
# We try to get the Wikipedia URL by the subject by taking advantage of the fact that Wikipedia's special search will redirect you immediately
@@ -318,13 +401,7 @@ def main():
318401
shutil.copy(os.path.normpath(templates_path + "/titlepage.xhtml"), os.path.normpath(repo_name + "/src/epub/text/"))
319402
shutil.copy(os.path.normpath(templates_path + "/uncopyright.xhtml"), os.path.normpath(repo_name + "/src/epub/text/"))
320403
shutil.copy(os.path.normpath(templates_path + "/titlepage.svg"), os.path.normpath(repo_name + "/images/"))
321-
322-
if len(args.title) < 15:
323-
shutil.copy(os.path.normpath(templates_path + "/cover-short.svg"), os.path.normpath(repo_name + "/images/cover.svg"))
324-
elif len(args.title) >= 30:
325-
shutil.copy(os.path.normpath(templates_path + "/cover-long.svg"), os.path.normpath(repo_name + "/images/cover.svg"))
326-
else:
327-
shutil.copy(os.path.normpath(templates_path + "/cover.svg"), os.path.normpath(repo_name + "/images/cover.svg"))
404+
shutil.copy(os.path.normpath(templates_path + "/cover.svg"), os.path.normpath(repo_name + "/images/cover.svg"))
328405

329406
# Try to find Wikipedia links if possible
330407
author_wiki_url, author_nacoaf_url = get_wikipedia_url(args.author, True)
@@ -349,6 +426,11 @@ def main():
349426
with open(os.path.join(repo_name, "images", "titlepage.svg"), "w") as file:
350427
file.write(generate_titlepage_svg(args.title, args.author, contributors, title_string, templates_path))
351428

429+
# Create the cover SVG
430+
with open(os.path.join(repo_name, "images", "cover.svg"), "w") as file:
431+
authors = ["Rudolph Eric Raspe", "Willam Cervantes"]
432+
file.write(generate_cover_svg(args.title, authors, title_string, templates_path))
433+
352434
if args.pg_url:
353435
se.replace_in_file(os.path.normpath(repo_name + "/src/epub/text/imprint.xhtml"), "PGLINK", args.pg_url)
354436

@@ -390,7 +472,6 @@ def main():
390472
file.write(colophon_xhtml)
391473
file.truncate()
392474

393-
394475
with open(os.path.join(repo_name, "src", "epub", "content.opf"), "r+", encoding="utf-8") as file:
395476
metadata_xhtml = file.read()
396477

se/__init__.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,17 @@
2222
SE_GENRES = ["Adventure", "Autobiography", "Biography", "Childrens", "Comedy", "Drama", "Fantasy", "Fiction", "Horror", "Memoir", "Mystery", "Nonfiction", "Philosophy", "Poetry", "Romance", "Satire", "Science Fiction", "Shorts", "Spirituality", "Tragedy", "Travel"]
2323
MESSAGE_TYPE_WARNING = 1
2424
MESSAGE_TYPE_ERROR = 2
25-
TITLEPAGE_KERNING = 5
26-
TITLEPAGE_AVERAGE_SPACING = 7 # Guess at average default spacing between letters
25+
COVER_TITLE_BOX_Y = 1620 # In px; note that in SVG, Y starts from the TOP of the image
26+
COVER_TITLE_BOX_HEIGHT = 430
27+
COVER_TITLE_BOX_WIDTH = 1300
28+
COVER_TITLE_BOX_PADDING = 100
29+
COVER_TITLE_MARGIN = 20
30+
COVER_TITLE_HEIGHT = 80
31+
COVER_TITLE_SMALL_HEIGHT = 60
32+
COVER_TITLE_XSMALL_HEIGHT = 50
33+
COVER_AUTHOR_SPACING = 60
34+
COVER_AUTHOR_HEIGHT = 40
35+
COVER_AUTHOR_MARGIN = 20
2736
TITLEPAGE_WIDTH = 1400
2837
TITLEPAGE_VERTICAL_PADDING = 50
2938
TITLEPAGE_HORIZONTAL_PADDING = 100
@@ -37,9 +46,9 @@
3746
TITLEPAGE_CONTRIBUTOR_HEIGHT = 40 # Height of each contributor line
3847
TITLEPAGE_CONTRIBUTOR_MARGIN = 20 # Space between contributor descriptor and contributor line, and between sequential contributor lines
3948
TITLEPAGE_CONTRIBUTOR_DESCRIPTOR_MARGIN = 80 # Space between last contributor line and next contributor descriptor (if more than one contributor descriptor)
40-
LEAGUE_SPARTAN_80_WIDTHS = {" ": 40, "A": 78.596, "B": 54.550, "C": 67.181, "D": 61.287, "E": 44.164, "F": 44.632, "G": 73.263, "H": 60.070, "I": 17.591, "J": 42.105, "K": 70.269, "L": 44.164, "M": 85.520, "N": 66.058, "O": 77.754, "P": 54.550, "Q": 79.064, "R": 63.532, "S": 58.105, "T": 54.269, "U": 60.257, "V": 78.596, "W": 107.696, "X": 81.029, "Y": 74.480, "Z": 68.959, ".": 21.427, ",": 21.427, "/": 52.865, "\\": 52.865, "-": 30.129, ":": 21.427, ";": 21.427, "’": 19.462, "!": 21.427, "?": 51.462, "&": 81.497, "0": 62.784, "1": 30.316, "2": 60.164, "3": 57.637, "4": 63.439, "5": 56.140, "6": 59.415, "7": 61.567, "8": 57.731, "9": 59.415}
41-
TITLEPAGE_LEAGUE_SPARTAN_60_RATIO = 1.333333
42-
TITLEPAGE_LEAGUE_SPARTAN_40_RATIO = 2
49+
LEAGUE_SPARTAN_KERNING = 5 # In px
50+
LEAGUE_SPARTAN_AVERAGE_SPACING = 7 # Guess at average default spacing between letters, in px
51+
LEAGUE_SPARTAN_100_WIDTHS = {" ": 40.0, "A": 98.245, "B": 68.1875, "C": 83.97625, "D": 76.60875, "E": 55.205, "F": 55.79, "G": 91.57875, "H": 75.0875, "I": 21.98875, "J": 52.631254, "K": 87.83625, "L": 55.205, "M": 106.9, "N": 82.5725, "O": 97.1925, "P": 68.1875, "Q": 98.83, "R": 79.41599, "S": 72.63125, "T": 67.83625, "U": 75.32125, "V": 98.245, "W": 134.62, "X": 101.28625, "Y": 93.1, "Z": 86.19875, ".": 26.78375, ",": 26.78375, "/": 66.08125, "\\": 66.08125, "-": 37.66125, ":": 26.78375, ";": 26.78375, "’": 24.3275, "!": 26.78375, "?": 64.3275, "&": 101.87125, "0": 78.48, "1": 37.895, "2": 75.205, "3": 72.04625, "4": 79.29875, "5": 70.175, "6": 74.26875, "7": 76.95875, "8": 72.16375, "9": 74.26875}
4352

4453
class SeError(Exception):
4554
pass

templates/cover-long.svg

Lines changed: 0 additions & 9 deletions
This file was deleted.

templates/cover-short.svg

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)