Reshuffle build and preview in Build Novel Project tool

This commit is contained in:
Veronica K. B. Olsen
2021-01-28 17:04:14 +01:00
parent 750d8b0a25
commit c3a1f25894
4 changed files with 317 additions and 344 deletions
+73 -8
View File
@@ -61,6 +61,8 @@ class ToHtml(Tokenizer):
self.reReverse = []
self._buildRegEx()
self.fullHTML = []
return
##
@@ -90,6 +92,11 @@ class ToHtml(Tokenizer):
# Class Methods
##
def getFullResultSize(self):
"""Return the size of the full HTML result.
"""
return sum([len(x) for x in self.fullHTML])
def doAutoReplace(self):
"""Extend the auto-replace to also properly encode some unicode
characters into their respective HTML entities.
@@ -108,9 +115,12 @@ class ToHtml(Tokenizer):
if self.genMode == self.M_PREVIEW:
# Doesn't matter for preview as we don't use the markdown
return
self.theMarkdown = self.reReverse.sub(
lambda x: self.revDict[x.group(0)], self.theMarkdown
)
if self.keepMarkdown:
self.theMarkdown[-1] = self.reReverse.sub(
lambda x: self.revDict[x.group(0)], self.theMarkdown[-1]
)
return
def doConvert(self):
@@ -165,21 +175,28 @@ class ToHtml(Tokenizer):
if tStyle is not None and self.cssStyles:
if tStyle & self.A_LEFT:
aStyle.append("text-align: left;")
if tStyle & self.A_RIGHT:
elif tStyle & self.A_RIGHT:
aStyle.append("text-align: right;")
if tStyle & self.A_CENTRE:
elif tStyle & self.A_CENTRE:
aStyle.append("text-align: center;")
if tStyle & self.A_JUSTIFY:
elif tStyle & self.A_JUSTIFY:
aStyle.append("text-align: justify;")
if tStyle & self.A_PBB:
aStyle.append("page-break-before: always;")
if tStyle & self.A_PBB_AUT:
elif tStyle & self.A_PBB_AUT:
aStyle.append("page-break-before: auto;")
if tStyle & self.A_PBA:
aStyle.append("page-break-after: always;")
if tStyle & self.A_PBA_AUT:
elif tStyle & self.A_PBA_AUT:
aStyle.append("page-break-after: auto;")
if tStyle & self.A_Z_BTMMRG:
aStyle.append("margin-bottom: 0;")
if tStyle & self.A_Z_TOPMRG:
aStyle.append("margin-top: 0;")
if len(aStyle) > 0:
hStyle = " style='%s'" % (" ".join(aStyle))
else:
@@ -255,6 +272,54 @@ class ToHtml(Tokenizer):
self.theResult = "".join(tmpResult)
tmpResult = []
if self.genMode != self.M_PREVIEW:
self.fullHTML.append(self.theResult)
return
def saveHTML5(self, savePath):
"""Save the data to an .html file.
"""
with open(savePath, mode="w", encoding="utf8") as outFile:
theStyle = self.getStyleSheet()
theStyle.append("article {width: 800px; margin: 40px auto;}")
bodyText = "".join(self.fullHTML)
bodyText = bodyText.replace("\t", "	")
theHtml = (
"<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
"<meta charset='utf-8'>\n"
"<title>{projTitle:s}</title>\n"
"</head>\n"
"<style>\n"
"{htmlStyle:s}\n"
"</style>\n"
"<body>\n"
"<article>\n"
"{bodyText:s}\n"
"</article>\n"
"</body>\n"
"</html>\n"
).format(
projTitle = self.theProject.projName,
htmlStyle = "\n".join(theStyle),
bodyText = bodyText,
)
outFile.write(theHtml)
return
def replaceTabs(self, nSpaces=8, spaceChar="&nbsp;"):
"""Replace tabs with spaces in the html.
"""
htmlText = []
eightSpace = spaceChar*nSpaces
for aLine in self.fullHTML:
htmlText.append(aLine.replace("\t", eightSpace))
self.fullHTML = htmlText
return
def getStyleSheet(self):
+99 -184
View File
@@ -70,6 +70,8 @@ class Tokenizer():
A_PBB_AUT = 0x0020 # Page break before auto
A_PBA = 0x0040 # Page break after always
A_PBA_AUT = 0x0080 # Page break after auto
A_Z_TOPMRG = 0x0100 # Zero top margin
A_Z_BTMMRG = 0x0200 # Zero bottom margin
def __init__(self, theProject, theParent):
@@ -77,12 +79,14 @@ class Tokenizer():
self.theParent = theParent
# Data Variables
self.theText = None # The raw text to be tokenized
self.theText = "" # The raw text to be tokenized
self.theHandle = None # The handle associated with the text
self.theItem = None # The NWItem associated with the handle
self.theTokens = None # The list of the processed tokens
self.theResult = None # The result text after conversion
self.theMarkdown = None # The result text in novelWriter markdown
self.theTokens = [] # The list of the processed tokens
self.theResult = "" # The result of the last document
self.keepMarkdown = False # Whether to keep the markdown text
self.theMarkdown = [] # The result novelWriter markdown of all documents
# User Settings
self.textFont = "Serif" # Output text font
@@ -96,12 +100,13 @@ class Tokenizer():
self.doKeywords = False # Also process keywords like tags and references
## Title Margins
self.marginTitle = (1.00, 0.50)
self.marginHead1 = (1.00, 0.50)
self.marginHead2 = (0.85, 0.50)
self.marginHead3 = (0.58, 0.50)
self.marginHead4 = (0.58, 0.50)
self.marginText = (0.00, 0.58)
self.marginTitle = (1.000, 0.500)
self.marginHead1 = (1.000, 0.500)
self.marginHead2 = (0.834, 0.500)
self.marginHead3 = (0.584, 0.500)
self.marginHead4 = (0.584, 0.500)
self.marginText = (0.000, 0.584)
self.marginMeta = (0.000, 0.584)
## Title Formats
self.fmtTitle = "%title%" # Formatting for titles
@@ -202,6 +207,10 @@ class Tokenizer():
self.marginText = (float(mUpper), float(mLower))
return
def setMetaMargins(self, mUpper, mLower):
self.marginMeta = (float(mUpper), float(mLower))
return
def setLinkHeaders(self, linkHeaders):
self.linkHeaders = linkHeaders
return
@@ -222,6 +231,10 @@ class Tokenizer():
self.doKeywords = doKeywords
return
def setKeepMarkdown(self, keepMarkdown):
self.keepMarkdown = keepMarkdown
return
##
# Class Methods
##
@@ -241,7 +254,8 @@ class Tokenizer():
self.theTokens.append((
self.T_TITLE, 0, theTitle, None, self.A_PBB | self.A_CENTRE
))
self.theMarkdown = "# %s\n\n" % theTitle
if self.keepMarkdown:
self.theMarkdown.append("# %s\n\n" % theTitle)
return True
@@ -283,23 +297,6 @@ class Tokenizer():
return True
def getResult(self):
"""Return the result from the conversion.
"""
return self.theResult
def getResultSize(self):
"""Return the size of the result from the conversion.
"""
if self.theResult is None:
return 0
return len(self.theResult)
def getFilteredMarkdown(self):
"""Return the novelWriter markdown after the filters have been applied.
"""
return self.theMarkdown
def doAutoReplace(self):
"""Run through the user's auto-replace dictionary.
"""
@@ -352,7 +349,6 @@ class Tokenizer():
]
self.theTokens = []
self.theMarkdown = ""
tmpMarkdown = []
nLine = 0
for aLine in self.theText.splitlines():
@@ -361,88 +357,61 @@ class Tokenizer():
# Tag lines starting with specific characters
if len(aLine.strip()) == 0:
self.theTokens.append((
self.T_EMPTY,
nLine,
"",
None,
self.A_NONE
self.T_EMPTY, nLine, "", None, self.A_NONE
))
tmpMarkdown.append("\n")
if self.keepMarkdown:
tmpMarkdown.append("\n")
elif aLine[0] == "%":
cLine = aLine[1:].lstrip()
synTag = cLine[:9].lower()
if synTag == "synopsis:":
self.theTokens.append((
self.T_SYNOPSIS,
nLine,
cLine[9:].strip(),
None,
self.A_NONE
self.T_SYNOPSIS, nLine, cLine[9:].strip(), None, self.A_NONE
))
if self.doSynopsis:
if self.doSynopsis and self.keepMarkdown:
tmpMarkdown.append("%s\n" % aLine)
else:
self.theTokens.append((
self.T_COMMENT,
nLine,
aLine[1:].strip(),
None,
self.A_NONE
self.T_COMMENT, nLine, aLine[1:].strip(), None, self.A_NONE
))
if self.doComments:
if self.doComments and self.keepMarkdown:
tmpMarkdown.append("%s\n" % aLine)
elif aLine[0] == "@":
self.theTokens.append((
self.T_KEYWORD,
nLine,
aLine[1:].strip(),
None,
self.A_NONE
self.T_KEYWORD, nLine, aLine[1:].strip(), None, self.A_NONE
))
if self.doKeywords:
if self.doKeywords and self.keepMarkdown:
tmpMarkdown.append("%s\n" % aLine)
elif aLine[:2] == "# ":
self.theTokens.append((
self.T_HEAD1,
nLine,
aLine[2:].strip(),
None,
self.A_NONE
self.T_HEAD1, nLine, aLine[2:].strip(), None, self.A_NONE
))
tmpMarkdown.append("%s\n" % aLine)
if self.keepMarkdown:
tmpMarkdown.append("%s\n" % aLine)
elif aLine[:3] == "## ":
self.theTokens.append((
self.T_HEAD2,
nLine,
aLine[3:].strip(),
None,
self.A_NONE
self.T_HEAD2, nLine, aLine[3:].strip(), None, self.A_NONE
))
tmpMarkdown.append("%s\n" % aLine)
if self.keepMarkdown:
tmpMarkdown.append("%s\n" % aLine)
elif aLine[:4] == "### ":
self.theTokens.append((
self.T_HEAD3,
nLine,
aLine[4:].strip(),
None,
self.A_NONE
self.T_HEAD3, nLine, aLine[4:].strip(), None, self.A_NONE
))
tmpMarkdown.append("%s\n" % aLine)
if self.keepMarkdown:
tmpMarkdown.append("%s\n" % aLine)
elif aLine[:5] == "#### ":
self.theTokens.append((
self.T_HEAD4,
nLine,
aLine[5:].strip(),
None,
self.A_NONE
self.T_HEAD4, nLine, aLine[5:].strip(), None, self.A_NONE
))
tmpMarkdown.append("%s\n" % aLine)
if self.keepMarkdown:
tmpMarkdown.append("%s\n" % aLine)
else:
if not self.doBodyText:
@@ -465,26 +434,44 @@ class Tokenizer():
# sorted by position
fmtPos = sorted(fmtPos, key=itemgetter(0))
self.theTokens.append((
self.T_TEXT,
nLine,
aLine,
fmtPos,
self.A_NONE
self.T_TEXT, nLine, aLine, fmtPos, self.A_NONE
))
tmpMarkdown.append("%s\n" % aLine)
if self.keepMarkdown:
tmpMarkdown.append("%s\n" % aLine)
# Always add an empty line at the end
self.theTokens.append((
self.T_EMPTY,
nLine,
"",
None,
self.A_NONE
self.T_EMPTY, nLine, "", None, self.A_NONE
))
tmpMarkdown.append("\n")
if self.keepMarkdown:
tmpMarkdown.append("\n")
self.theMarkdown = "".join(tmpMarkdown)
tmpMarkdown = []
if self.keepMarkdown:
self.theMarkdown.append("".join(tmpMarkdown))
# Second Pass
# ===========
# Some items need a second pass
pToken = (self.T_EMPTY, 0, "", None, self.A_NONE)
nToken = (self.T_EMPTY, 0, "", None, self.A_NONE)
tCount = len(self.theTokens)
for n, tToken in enumerate(self.theTokens):
if n > 0:
pToken = self.theTokens[n-1]
if n < tCount - 1:
nToken = self.theTokens[n+1]
if tToken[0] == self.T_KEYWORD:
aStyle = tToken[4]
if pToken[0] == self.T_KEYWORD:
aStyle |= self.A_Z_TOPMRG
if nToken[0] == self.T_KEYWORD:
aStyle |= self.A_Z_BTMMRG
self.theTokens[n] = (
tToken[0], tToken[1], tToken[2], tToken[3], aStyle
)
return
@@ -499,9 +486,7 @@ class Tokenizer():
# For novel files, we need to handle chapter numbering, scene
# numbering, and scene breaks
if self.isNovel:
for n in range(len(self.theTokens)):
tToken = self.theTokens[n]
for n, tToken in enumerate(self.theTokens):
# In case we see text before a scene, we reset the flag
if tToken[0] == self.T_TEXT:
@@ -513,11 +498,7 @@ class Tokenizer():
tTemp = self._formatHeading(self.fmtTitle, tToken[2])
self.theTokens[n] = (
tToken[0],
tToken[1],
tTemp,
None,
self.A_NONE
tToken[0], tToken[1], tTemp, None, self.A_NONE
)
elif tToken[0] == self.T_HEAD2:
@@ -535,11 +516,7 @@ class Tokenizer():
# Format the chapter header
self.theTokens[n] = (
tToken[0],
tToken[1],
tTemp,
None,
self.A_PBB
tToken[0], tToken[1], tTemp, None, self.A_PBB
)
# Set scene variables
@@ -556,53 +533,29 @@ class Tokenizer():
tTemp = self._formatHeading(self.fmtScene, tToken[2])
if tTemp == "" and self.hideScene:
self.theTokens[n] = (
self.T_EMPTY,
tToken[1],
"",
None,
self.A_NONE
self.T_EMPTY, tToken[1], "", None, self.A_NONE
)
elif tTemp == "" and not self.hideScene:
if self.firstScene:
self.theTokens[n] = (
self.T_EMPTY,
tToken[1],
"",
None,
self.A_NONE
self.T_EMPTY, tToken[1], "", None, self.A_NONE
)
else:
self.theTokens[n] = (
self.T_SKIP,
tToken[1],
"",
None,
self.A_NONE
self.T_SKIP, tToken[1], "", None, self.A_NONE
)
elif tTemp == self.fmtScene:
if self.firstScene:
self.theTokens[n] = (
self.T_EMPTY,
tToken[1],
"",
None,
self.A_NONE
self.T_EMPTY, tToken[1], "", None, self.A_NONE
)
else:
self.theTokens[n] = (
self.T_SEP,
tToken[1],
tTemp,
None,
self.A_CENTRE
self.T_SEP, tToken[1], tTemp, None, self.A_CENTRE
)
else:
self.theTokens[n] = (
tToken[0],
tToken[1],
tTemp,
None,
self.A_NONE
tToken[0], tToken[1], tTemp, None, self.A_NONE
)
# Definitely no longer the first scene
@@ -615,35 +568,19 @@ class Tokenizer():
tTemp = self._formatHeading(self.fmtSection, tToken[2])
if tTemp == "" and self.hideSection:
self.theTokens[n] = (
self.T_EMPTY,
tToken[1],
"",
None,
self.A_NONE
self.T_EMPTY, tToken[1], "", None, self.A_NONE
)
elif tTemp == "" and not self.hideSection:
self.theTokens[n] = (
self.T_SKIP,
tToken[1],
"",
None,
self.A_NONE
self.T_SKIP, tToken[1], "", None, self.A_NONE
)
elif tTemp == self.fmtSection:
self.theTokens[n] = (
self.T_SEP,
tToken[1],
tTemp,
None,
self.A_CENTRE
self.T_SEP, tToken[1], tTemp, None, self.A_CENTRE
)
else:
self.theTokens[n] = (
tToken[0],
tToken[1],
tTemp,
None,
self.A_NONE
tToken[0], tToken[1], tTemp, None, self.A_NONE
)
# For title page and partitions, we need to centre all text.
@@ -654,28 +591,18 @@ class Tokenizer():
for n, tToken in enumerate(self.theTokens):
if tToken[0] == self.T_HEAD1:
if self.isTitle:
aStyle = self.A_PBB_AUT | self.A_CENTRE
self.theTokens[n] = (
self.T_TITLE,
tToken[1],
tToken[2],
tToken[3],
self.A_PBB_AUT | self.A_CENTRE
self.T_TITLE, tToken[1], tToken[2], tToken[3], aStyle
)
else:
aStyle = self.A_PBB | self.A_CENTRE
self.theTokens[n] = (
tToken[0],
tToken[1],
tToken[2],
tToken[3],
self.A_PBB | self.A_CENTRE
tToken[0], tToken[1], tToken[2], tToken[3], aStyle
)
else:
self.theTokens[n] = (
tToken[0],
tToken[1],
tToken[2],
tToken[3],
self.A_CENTRE
tToken[0], tToken[1], tToken[2], tToken[3], self.A_CENTRE
)
# Add a page break after the last entry
@@ -683,11 +610,7 @@ class Tokenizer():
if n >= 0:
tToken = self.theTokens[n]
self.theTokens[n] = (
tToken[0],
tToken[1],
tToken[2],
tToken[3],
tToken[4] | self.A_PBA
tToken[0], tToken[1], tToken[2], tToken[3], tToken[4] | self.A_PBA
)
# A single page is always left-aligned and starts on a fresh
@@ -696,19 +619,11 @@ class Tokenizer():
for n, tToken in enumerate(self.theTokens):
if n == 0:
self.theTokens[n] = (
tToken[0],
tToken[1],
tToken[2],
tToken[3],
self.A_LEFT | self.A_PBB
tToken[0], tToken[1], tToken[2], tToken[3], self.A_LEFT | self.A_PBB
)
else:
self.theTokens[n] = (
tToken[0],
tToken[1],
tToken[2],
tToken[3],
self.A_LEFT
tToken[0], tToken[1], tToken[2], tToken[3], self.A_LEFT
)
return True
+20 -9
View File
@@ -116,6 +116,7 @@ class ToOdt(Tokenizer):
self._mTopHead4 = "0.247cm"
self._mTopHead = "0.423cm"
self._mTopText = "0.000cm"
self._mTopMeta = "0.000cm"
self._mBotTitle = "0.212cm"
self._mBotHead1 = "0.212cm"
@@ -124,6 +125,7 @@ class ToOdt(Tokenizer):
self._mBotHead4 = "0.212cm"
self._mBotHead = "0.212cm"
self._mBotText = "0.247cm"
self._mBotMeta = "0.106cm"
## Colour
self._colHead12 = None
@@ -170,7 +172,7 @@ class ToOdt(Tokenizer):
self._fontFamily = self.textFont
if len(self.textFont.split()) > 1:
self._fontFamily = f"&apos;{self.textFont}&apos;"
self._fontFamily = f"'{self.textFont}'"
self._fontPitch = "fixed" if self.textFixed else "variable"
self._fSizeTitle = f"{round(2.50 * self.textSize):d}pt"
@@ -188,6 +190,7 @@ class ToOdt(Tokenizer):
self._mTopHead4 = self._emToCm(self.marginHead4[0])
self._mTopHead = self._emToCm(self.marginHead4[0])
self._mTopText = self._emToCm(self.marginText[0])
self._mTopMeta = self._emToCm(self.marginMeta[0])
self._mBotTitle = self._emToCm(self.marginTitle[1])
self._mBotHead1 = self._emToCm(self.marginHead1[1])
@@ -196,13 +199,14 @@ class ToOdt(Tokenizer):
self._mBotHead4 = self._emToCm(self.marginHead4[1])
self._mBotHead = self._emToCm(self.marginHead4[1])
self._mBotText = self._emToCm(self.marginText[1])
self._mBotMeta = self._emToCm(self.marginMeta[1])
if self.colourHead:
self._colHead12 = "#2a6099"
self._opaHead12 = "100%"
self._colHead34 = "#444444"
self._opaHead34 = "100%"
self._colMetaTx = "#666666"
self._colMetaTx = "#813709"
self._opaMetaTx = "100%"
self._lineHeight = f"{round(100 * self.lineHeight):d}%"
@@ -302,21 +306,28 @@ class ToOdt(Tokenizer):
if tStyle is not None:
if tStyle & self.A_LEFT:
oStyle.setTextAlign("left")
if tStyle & self.A_RIGHT:
elif tStyle & self.A_RIGHT:
oStyle.setTextAlign("right")
if tStyle & self.A_CENTRE:
elif tStyle & self.A_CENTRE:
oStyle.setTextAlign("center")
if tStyle & self.A_JUSTIFY:
elif tStyle & self.A_JUSTIFY:
oStyle.setTextAlign("justify")
if tStyle & self.A_PBB:
oStyle.setBreakBefore("page")
if tStyle & self.A_PBB_AUT:
elif tStyle & self.A_PBB_AUT:
oStyle.setBreakBefore("auto")
if tStyle & self.A_PBA:
oStyle.setBreakAfter("page")
if tStyle & self.A_PBA_AUT:
elif tStyle & self.A_PBA_AUT:
oStyle.setBreakAfter("auto")
if tStyle & self.A_Z_BTMMRG:
oStyle.setMarginBottom("0.000cm")
if tStyle & self.A_Z_TOPMRG:
oStyle.setMarginTop("0.000cm")
# Process Text Types
if tType == self.T_EMPTY:
if hasHardBreak and parStyle is not None:
@@ -740,8 +751,8 @@ class ToOdt(Tokenizer):
oStyle.setDisplayName("Text Meta")
oStyle.setParentStyleName("Standard")
oStyle.setClass("text")
oStyle.setMarginTop(self._mTopText)
oStyle.setMarginBottom(self._mBotText)
oStyle.setMarginTop(self._mTopMeta)
oStyle.setMarginBottom(self._mBotMeta)
oStyle.setLineHeight(self._lineHeight)
oStyle.setFontName(self.textFont)
oStyle.setFontFamily(self._fontFamily)