diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py
index ab7e0c81..980ac6e6 100644
--- a/nw/core/tohtml.py
+++ b/nw/core/tohtml.py
@@ -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 = (
+ "\n"
+ "\n"
+ "
\n"
+ "\n"
+ "{projTitle:s}\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "{bodyText:s}\n"
+ "\n"
+ "\n"
+ "\n"
+ ).format(
+ projTitle = self.theProject.projName,
+ htmlStyle = "\n".join(theStyle),
+ bodyText = bodyText,
+ )
+ outFile.write(theHtml)
+
+ return
+
+ def replaceTabs(self, nSpaces=8, spaceChar=" "):
+ """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):
diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py
index cc6aeb58..a1ffe11f 100644
--- a/nw/core/tokenizer.py
+++ b/nw/core/tokenizer.py
@@ -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
diff --git a/nw/core/toodt.py b/nw/core/toodt.py
index 4eb8d57b..936c7faf 100644
--- a/nw/core/toodt.py
+++ b/nw/core/toodt.py
@@ -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"'{self.textFont}'"
+ 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)
diff --git a/nw/gui/build.py b/nw/gui/build.py
index 56190cec..d05aa0cc 100644
--- a/nw/gui/build.py
+++ b/nw/gui/build.py
@@ -79,7 +79,6 @@ class GuiBuildNovel(QDialog):
self.htmlText = [] # List of html documents
self.htmlStyle = [] # List of html styles
- self.nwdText = [] # List of markdown documents
self.htmlSize = 0 # Size of the html document
self.buildTime = 0 # The timestamp of the last build
@@ -532,7 +531,6 @@ class GuiBuildNovel(QDialog):
else:
self.htmlText = []
self.htmlStyle = []
- self.nwdText = []
self.buildTime = 0
return False
@@ -552,34 +550,26 @@ class GuiBuildNovel(QDialog):
textSize = self.textSize.value()
replaceTabs = self.replaceTabs.isChecked()
- tStart = int(time())
-
self.htmlText = []
self.htmlStyle = []
- self.nwdText = []
self.htmlSize = 0
+ # Build Preview
+ # =============
+
makeHtml = ToHtml(self.theProject, self.theParent)
- self._doBuild(makeHtml)
-
+ self._doBuild(makeHtml, isPreview=True)
if replaceTabs:
- htmlText = []
- eightSpace = " "*8
- for aLine in self.htmlText:
- htmlText.append(aLine.replace("\t", eightSpace))
- self.htmlText = htmlText
+ makeHtml.replaceTabs()
- nwdText = []
- for aLine in self.nwdText:
- nwdText.append(aLine.replace("\t", " "))
- self.nwdText = nwdText
-
- tEnd = int(time())
- logger.debug("Built project in %.3f ms" % (1000*(tEnd - tStart)))
+ self.htmlText = makeHtml.fullHTML
self.htmlStyle = makeHtml.getStyleSheet()
- self.buildTime = tEnd
+ self.htmlSize = makeHtml.getFullResultSize()
+ self.buildTime = int(time())
+
+ # Load Preview
+ # ============
- # Load the preview document with the html data
self.docView.setTextFont(textFont, textSize)
self.docView.setJustify(justifyText)
if noStyling:
@@ -600,9 +590,11 @@ class GuiBuildNovel(QDialog):
return
- def _doBuild(self, bldObj):
+ def _doBuild(self, bldObj, isPreview=False, doConvert=True):
"""Rund the build with a specific build object.
"""
+ tStart = int(time())
+
# Get Settings
fmtTitle = self.fmtTitle.text().strip()
fmtChapter = self.fmtChapter.text().strip()
@@ -644,7 +636,6 @@ class GuiBuildNovel(QDialog):
if isHtml:
bldObj.setStyles(not noStyling)
- self.htmlSize = 0
if isOdt:
bldObj.setColourHeaders(not noStyling)
@@ -667,31 +658,27 @@ class GuiBuildNovel(QDialog):
if noteRoot:
# Add headers for root folders of notes
bldObj.addRootHeading(tItem.itemHandle)
- bldObj.doConvert()
- if isHtml:
- self.htmlText.append(bldObj.getResult())
- self.nwdText.append(bldObj.getFilteredMarkdown())
- self.htmlSize += bldObj.getResultSize()
+ if doConvert:
+ bldObj.doConvert()
elif self._checkInclude(tItem, noteFiles, novelFiles, ignoreFlag):
bldObj.setText(tItem.itemHandle)
bldObj.doAutoReplace()
bldObj.tokenizeText()
bldObj.doHeaders()
- bldObj.doConvert()
+ if doConvert:
+ bldObj.doConvert()
bldObj.doPostProcessing()
- if isHtml:
- self.htmlText.append(bldObj.getResult())
- self.nwdText.append(bldObj.getFilteredMarkdown())
- self.htmlSize += bldObj.getResultSize()
except Exception as e:
logger.error("Failed to generate html of document '%s'" % tItem.itemHandle)
logger.error(str(e))
- self.docView.setText((
- "Failed to generate preview. "
- "Document with title '%s' could not be parsed."
- ) % tItem.itemName)
+ if isPreview:
+ self.docView.setText((
+ "Failed to generate preview. "
+ "Document with title '%s' could not be parsed."
+ ) % tItem.itemName)
+
return False
# Update progress bar, also for skipped items
@@ -700,6 +687,9 @@ class GuiBuildNovel(QDialog):
if isOdt:
bldObj.closeDocument()
+ tEnd = int(time())
+ logger.debug("Built project in %.3f ms" % (1000*(tEnd - tStart)))
+
if bldObj.errData:
self.theParent.makeAlert((
"There were problems when building the project:"
@@ -751,63 +741,59 @@ class GuiBuildNovel(QDialog):
def _saveDocument(self, theFormat):
"""Save the document to various formats.
"""
+ replaceTabs = self.replaceTabs.isChecked()
+
byteFmt = QByteArray()
fileExt = ""
textFmt = ""
- outTool = ""
- # Create the settings
+ # Settings
+ # ========
+
if theFormat == self.FMT_ODT:
fileExt = "odt"
textFmt = "Open Document"
- outTool = "NW_ODT"
elif theFormat == self.FMT_FODT:
fileExt = "fodt"
textFmt = "Flat Open Document"
- outTool = "NW_ODT"
elif theFormat == self.FMT_PDF:
fileExt = "pdf"
textFmt = "PDF"
- outTool = "QtPrint"
elif theFormat == self.FMT_HTM:
fileExt = "htm"
textFmt = "Plain HTML"
- outTool = "NW"
elif theFormat == self.FMT_MD:
byteFmt.append("markdown")
fileExt = "md"
textFmt = "Markdown"
- outTool = "Qt"
elif theFormat == self.FMT_NWD:
fileExt = "nwd"
textFmt = "%s Markdown" % nw.__package__
- outTool = "NW"
elif theFormat == self.FMT_TXT:
byteFmt.append("plaintext")
fileExt = "txt"
textFmt = "Plain Text"
- outTool = "Qt"
elif theFormat == self.FMT_JSON_H:
fileExt = "json"
textFmt = "JSON + %s HTML" % nw.__package__
- outTool = "NW"
elif theFormat == self.FMT_JSON_M:
fileExt = "json"
textFmt = "JSON + %s Markdown" % nw.__package__
- outTool = "NW"
else:
return False
- # Generate the file name
+ # Generate File Name
+ # ==================
+
if fileExt:
cleanName = makeFileNameSafe(self.theProject.projName)
@@ -830,109 +816,109 @@ class GuiBuildNovel(QDialog):
else:
return False
- # Do the actual writing
- wSuccess = False
+ # Build and Write
+ # ===============
+
errMsg = ""
- if outTool == "Qt":
+ wSuccess = False
+
+ if theFormat == self.FMT_MD or theFormat == self.FMT_TXT:
docWriter = QTextDocumentWriter()
docWriter.setFileName(savePath)
docWriter.setFormat(byteFmt)
wSuccess = docWriter.write(self.docView.qDocument)
- elif outTool == "NW":
+ elif theFormat == self.FMT_HTM:
+ makeHtml = ToHtml(self.theProject, self.theParent)
+ self._doBuild(makeHtml)
+ if replaceTabs:
+ makeHtml.replaceTabs()
+
try:
- with open(savePath, mode="w", encoding="utf8") as outFile:
- if theFormat == self.FMT_HTM:
- # Write novelWriter HTML data
- theStyle = self.htmlStyle.copy()
- theStyle.append(r"article {width: 800px; margin: 40px auto;}")
- bodyText = "".join(self.htmlText)
- bodyText = bodyText.replace("\t", " ")
-
- theHtml = (
- "\n"
- "\n"
- "\n"
- "\n"
- "{projTitle:s}\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "{bodyText:s}\n"
- "\n"
- "\n"
- "\n"
- ).format(
- projTitle = self.theProject.projName,
- htmlStyle = "\n".join(theStyle),
- bodyText = bodyText,
- )
- outFile.write(theHtml)
-
- elif theFormat == self.FMT_NWD:
- # Write novelWriter markdown data
- for aLine in self.nwdText:
- outFile.write(aLine)
-
- elif theFormat == self.FMT_JSON_H or theFormat == self.FMT_JSON_M:
- jsonData = {
- "meta" : {
- "workingTitle" : self.theProject.projName,
- "novelTitle" : self.theProject.bookTitle,
- "authors" : self.theProject.bookAuthors,
- "buildTime" : self.buildTime,
- }
- }
-
- if theFormat == self.FMT_JSON_H:
- theBody = []
- for htmlPage in self.htmlText:
- theBody.append(htmlPage.rstrip("\n").split("\n"))
- jsonData["text"] = {
- "css" : self.htmlStyle,
- "html" : theBody,
- }
- elif theFormat == self.FMT_JSON_M:
- theBody = []
- for nwdPage in self.nwdText:
- theBody.append(nwdPage.split("\n"))
- jsonData["text"] = {
- "nwd" : theBody,
- }
-
- outFile.write(json.dumps(jsonData, indent=2))
-
+ makeHtml.saveHTML5(savePath)
wSuccess = True
-
except Exception as e:
errMsg = str(e)
- elif outTool == "NW_ODT":
+ elif theFormat == self.FMT_NWD:
+ makeNwd = ToHtml(self.theProject, self.theParent)
+ makeNwd.setKeepMarkdown(True)
+ self._doBuild(makeNwd, doConvert=False)
+ if replaceTabs:
+ makeNwd.replaceTabs(spaceChar=" ")
- if theFormat == self.FMT_FODT:
- makeOdt = ToOdt(self.theProject, self.theParent, isFlat=True)
- self._doBuild(makeOdt)
- try:
- makeOdt.saveFlatXML(savePath)
+ try:
+ with open(savePath, mode="w", encoding="utf8") as outFile:
+ for nwdPage in makeNwd.theMarkdown:
+ outFile.write(nwdPage)
+ wSuccess = True
+ except Exception as e:
+ errMsg = str(e)
+
+ elif theFormat == self.FMT_FODT:
+ makeOdt = ToOdt(self.theProject, self.theParent, isFlat=True)
+ self._doBuild(makeOdt)
+ try:
+ makeOdt.saveFlatXML(savePath)
+ wSuccess = True
+ except Exception as e:
+ errMsg = str(e)
+
+ elif theFormat == self.FMT_ODT:
+ makeOdt = ToOdt(self.theProject, self.theParent, isFlat=False)
+ self._doBuild(makeOdt)
+ try:
+ makeOdt.saveOpenDocText(savePath)
+ wSuccess = True
+ except Exception as e:
+ errMsg = str(e)
+
+ elif theFormat == self.FMT_JSON_H or theFormat == self.FMT_JSON_M:
+ jsonData = {
+ "meta" : {
+ "workingTitle" : self.theProject.projName,
+ "novelTitle" : self.theProject.bookTitle,
+ "authors" : self.theProject.bookAuthors,
+ "buildTime" : self.buildTime,
+ }
+ }
+
+ if theFormat == self.FMT_JSON_H:
+ makeHtml = ToHtml(self.theProject, self.theParent)
+ self._doBuild(makeHtml)
+ if replaceTabs:
+ makeHtml.replaceTabs()
+
+ theBody = []
+ for htmlPage in makeHtml.fullHTML:
+ theBody.append(htmlPage.rstrip("\n").split("\n"))
+ jsonData["text"] = {
+ "css" : self.htmlStyle,
+ "html" : theBody,
+ }
+
+ elif theFormat == self.FMT_JSON_M:
+ makeNwd = ToHtml(self.theProject, self.theParent)
+ makeNwd.setKeepMarkdown(True)
+ self._doBuild(makeNwd, doConvert=False)
+ if replaceTabs:
+ makeNwd.replaceTabs(spaceChar=" ")
+
+ theBody = []
+ for nwdPage in makeNwd.theMarkdown:
+ theBody.append(nwdPage.split("\n"))
+ jsonData["text"] = {
+ "nwd" : theBody,
+ }
+
+ try:
+ with open(savePath, mode="w", encoding="utf8") as outFile:
+ outFile.write(json.dumps(jsonData, indent=2))
wSuccess = True
+ except Exception as e:
+ errMsg = str(e)
- except Exception as e:
- errMsg = str(e)
-
- elif theFormat == self.FMT_ODT:
- makeOdt = ToOdt(self.theProject, self.theParent, isFlat=False)
- self._doBuild(makeOdt)
- try:
- makeOdt.saveOpenDocText(savePath)
- wSuccess = True
-
- except Exception as e:
- errMsg = str(e)
-
- elif outTool == "QtPrint" and theFormat == self.FMT_PDF:
+ elif theFormat == self.FMT_PDF:
try:
thePrinter = QPrinter()
thePrinter.setOutputFormat(QPrinter.PdfFormat)
@@ -1021,13 +1007,10 @@ class GuiBuildNovel(QDialog):
if "htmlStyle" in theData.keys():
self.htmlStyle = theData["htmlStyle"]
dataCount += 1
- if "nwdText" in theData.keys():
- self.nwdText = theData["nwdText"]
- dataCount += 1
if "buildTime" in theData.keys():
self.buildTime = theData["buildTime"]
- return dataCount == 3
+ return dataCount == 2
def _saveCache(self):
"""Save the current data to cache.
@@ -1040,7 +1023,6 @@ class GuiBuildNovel(QDialog):
outFile.write(json.dumps({
"htmlText" : self.htmlText,
"htmlStyle" : self.htmlStyle,
- "nwdText" : self.nwdText,
"buildTime" : self.buildTime,
}, indent=2))
except Exception as e: