@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## Version 0.6 [2020-xx-xx]
|
## Version 0.6 [2020-xx-xx]
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
|
||||||
|
* Fixed a bug in validation of `@tag:` meta tags where one or more spaces before the `:` would still pass as valid tags, but the keyword index array would be missing those spaces in its counter. This mainly affected the highlighting of keywords, which would be misaligned. PR #206
|
||||||
|
|
||||||
**User Interface**
|
**User Interface**
|
||||||
|
|
||||||
* The Export Tool has been removed and replaced by a new tool called "Build Novel Project". The new tool has the same filtering options as the Export Tool, but with more formatting options for titles. It also has a preview window to display the generated document. A Save As button provides exports to HTML, novelWriter Markdown. plain text, PDF and Open Document format. LaTeX export has not been ported over, and interfacing with Pandoc is no longer supported either. Although, as before, the HTML export can be converted with Pandoc to other formats outside of novelWriter. The new tool also supports printing. PR #204
|
* The Export Tool has been removed and replaced by a new tool called "Build Novel Project". The new tool has the same filtering options as the Export Tool, but with more formatting options for titles. It also has a preview window to display the generated document. A Save As button provides exports to HTML, novelWriter Markdown. plain text, PDF and Open Document format. LaTeX export has not been ported over, and interfacing with Pandoc is no longer supported either. Although, as before, the HTML export can be converted with Pandoc to other formats outside of novelWriter. The new tool also supports printing. PR #204
|
||||||
|
|||||||
+95
-30
@@ -48,12 +48,16 @@ class ToHtml(Tokenizer):
|
|||||||
"<" : "<",
|
"<" : "<",
|
||||||
">" : ">",
|
">" : ">",
|
||||||
"&" : "&",
|
"&" : "&",
|
||||||
"\t" : " ",
|
"\t" : " "*2,
|
||||||
nwUnicode.U_ENDASH : nwUnicode.H_ENDASH,
|
nwUnicode.U_ENDASH : nwUnicode.H_ENDASH,
|
||||||
nwUnicode.U_EMDASH : nwUnicode.H_EMDASH,
|
nwUnicode.U_EMDASH : nwUnicode.H_EMDASH,
|
||||||
nwUnicode.U_HELLIP : nwUnicode.H_HELLIP,
|
nwUnicode.U_HELLIP : nwUnicode.H_HELLIP,
|
||||||
nwUnicode.U_NBSP : nwUnicode.H_NBSP,
|
nwUnicode.U_NBSP : nwUnicode.H_NBSP,
|
||||||
}
|
}
|
||||||
|
self.revDict = {}
|
||||||
|
self.reReplace = []
|
||||||
|
self.reReverse = []
|
||||||
|
self._buildRegEx()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -71,6 +75,7 @@ class ToHtml(Tokenizer):
|
|||||||
self.doKeywords = True
|
self.doKeywords = True
|
||||||
self.doComments = doComments
|
self.doComments = doComments
|
||||||
self.repDict["\t"] = " "*8
|
self.repDict["\t"] = " "*8
|
||||||
|
self._buildRegEx()
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -82,10 +87,9 @@ class ToHtml(Tokenizer):
|
|||||||
characters into their respective HTML entities.
|
characters into their respective HTML entities.
|
||||||
"""
|
"""
|
||||||
Tokenizer.doAutoReplace(self)
|
Tokenizer.doAutoReplace(self)
|
||||||
|
self.theText = self.reReplace.sub(
|
||||||
xRep = re.compile("|".join([re.escape(k) for k in self.repDict.keys()]), flags=re.DOTALL)
|
lambda x: self.repDict[x.group(0)], self.theText
|
||||||
self.theText = xRep.sub(lambda x: self.repDict[x.group(0)], self.theText)
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def doPostProcessing(self):
|
def doPostProcessing(self):
|
||||||
@@ -95,18 +99,15 @@ class ToHtml(Tokenizer):
|
|||||||
if self.genMode == self.M_PREVIEW:
|
if self.genMode == self.M_PREVIEW:
|
||||||
# Doesn't matter for preview as we don't use the markdown
|
# Doesn't matter for preview as we don't use the markdown
|
||||||
return
|
return
|
||||||
|
self.theMarkdown = self.reReverse.sub(
|
||||||
revDict = dict(map(reversed, self.repDict.items()))
|
lambda x: self.revDict[x.group(0)], self.theMarkdown
|
||||||
xRep = re.compile("|".join([re.escape(k) for k in revDict.keys()]), flags=re.DOTALL)
|
)
|
||||||
self.theMarkdown = xRep.sub(lambda x: revDict[x.group(0)], self.theMarkdown)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def doConvert(self):
|
def doConvert(self):
|
||||||
"""Convert the list of text tokens into a HTML document saved
|
"""Convert the list of text tokens into a HTML document saved
|
||||||
to theResult.
|
to theResult.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
htmlTags = {
|
htmlTags = {
|
||||||
self.FMT_B_B : "<strong>",
|
self.FMT_B_B : "<strong>",
|
||||||
self.FMT_B_E : "</strong>",
|
self.FMT_B_E : "</strong>",
|
||||||
@@ -116,11 +117,32 @@ class ToHtml(Tokenizer):
|
|||||||
self.FMT_U_E : "</u>",
|
self.FMT_U_E : "</u>",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.isNovel and self.genMode != self.M_PREVIEW:
|
||||||
|
# For novel files for export, we bump the titles one level
|
||||||
|
# up as this is more useful for printing and word processor
|
||||||
|
# imports.
|
||||||
|
h1 = "h1 class='title'"
|
||||||
|
h2 = "h1"
|
||||||
|
h3 = "h2"
|
||||||
|
h4 = "h3"
|
||||||
|
else:
|
||||||
|
h1 = "h1"
|
||||||
|
h2 = "h2"
|
||||||
|
h3 = "h3"
|
||||||
|
h4 = "h4"
|
||||||
|
|
||||||
|
alignHead = self.A_LEFT
|
||||||
|
if self.doJustify:
|
||||||
|
alignPar = self.A_JUSTIFY
|
||||||
|
else:
|
||||||
|
alignPar = self.A_LEFT
|
||||||
|
|
||||||
self.theResult = ""
|
self.theResult = ""
|
||||||
|
|
||||||
thisPar = []
|
thisPar = []
|
||||||
parStyle = ""
|
parStyle = None
|
||||||
tmpResult = []
|
tmpResult = []
|
||||||
|
hasHardBreak = False
|
||||||
for tType, tText, tFormat, tStyle in self.theTokens:
|
for tType, tText, tFormat, tStyle in self.theTokens:
|
||||||
|
|
||||||
# Styles
|
# Styles
|
||||||
@@ -136,20 +158,16 @@ class ToHtml(Tokenizer):
|
|||||||
aStyle.append("text-align: justify;")
|
aStyle.append("text-align: justify;")
|
||||||
if tStyle & self.A_PBB:
|
if tStyle & self.A_PBB:
|
||||||
aStyle.append("page-break-before: always;")
|
aStyle.append("page-break-before: always;")
|
||||||
if tStyle & self.A_PBB_L:
|
|
||||||
aStyle.append("page-break-before: left;")
|
|
||||||
if tStyle & self.A_PBB_R:
|
|
||||||
aStyle.append("page-break-before: right;")
|
|
||||||
if tStyle & self.A_PBB_AV:
|
if tStyle & self.A_PBB_AV:
|
||||||
aStyle.append("page-break-before: avoid;")
|
aStyle.append("page-break-before: avoid;")
|
||||||
|
if tStyle & self.A_PBB_NO:
|
||||||
|
aStyle.append("page-break-before: never;")
|
||||||
if tStyle & self.A_PBA:
|
if tStyle & self.A_PBA:
|
||||||
aStyle.append("page-break-after: always;")
|
aStyle.append("page-break-after: always;")
|
||||||
if tStyle & self.A_PBA_L:
|
|
||||||
aStyle.append("page-break-after: left;")
|
|
||||||
if tStyle & self.A_PBA_R:
|
|
||||||
aStyle.append("page-break-after: right;")
|
|
||||||
if tStyle & self.A_PBA_AV:
|
if tStyle & self.A_PBA_AV:
|
||||||
aStyle.append("page-break-after: avoid;")
|
aStyle.append("page-break-after: avoid;")
|
||||||
|
if tStyle & self.A_PBA_NO:
|
||||||
|
aStyle.append("page-break-after: never;")
|
||||||
|
|
||||||
if len(aStyle) > 0:
|
if len(aStyle) > 0:
|
||||||
hStyle = " style='%s'" % (" ".join(aStyle))
|
hStyle = " style='%s'" % (" ".join(aStyle))
|
||||||
@@ -158,41 +176,54 @@ class ToHtml(Tokenizer):
|
|||||||
|
|
||||||
# Process TextType
|
# Process TextType
|
||||||
if tType == self.T_EMPTY:
|
if tType == self.T_EMPTY:
|
||||||
|
if parStyle is None:
|
||||||
|
parStyle = ""
|
||||||
|
if hasHardBreak:
|
||||||
|
parClass = " class='break'"
|
||||||
|
else:
|
||||||
|
parClass = ""
|
||||||
if len(thisPar) > 0:
|
if len(thisPar) > 0:
|
||||||
tTemp = "".join(thisPar)
|
tTemp = "".join(thisPar)
|
||||||
tmpResult.append("<p%s>%s</p>\n" % (parStyle, tTemp.rstrip()))
|
tmpResult.append("<p%s%s>%s</p>\n" % (parStyle, parClass, tTemp.rstrip()))
|
||||||
thisPar = []
|
thisPar = []
|
||||||
parStyle = ""
|
parStyle = None
|
||||||
|
hasHardBreak = False
|
||||||
|
|
||||||
|
elif tType == self.T_TITLE:
|
||||||
|
tHead = tText.replace(r"\\", "<br/>")
|
||||||
|
tmpResult.append("<h1 class='title'%s>%s</h1>\n" % (hStyle, tHead))
|
||||||
|
|
||||||
elif tType == self.T_HEAD1:
|
elif tType == self.T_HEAD1:
|
||||||
tHead = tText.replace(r"\\", "<br/>")
|
tHead = tText.replace(r"\\", "<br/>")
|
||||||
tmpResult.append("<h1%s>%s</h1>\n" % (hStyle, tHead))
|
tmpResult.append("<%s%s>%s</%s>\n" % (h1, hStyle, tHead, h1))
|
||||||
|
|
||||||
elif tType == self.T_HEAD2:
|
elif tType == self.T_HEAD2:
|
||||||
tHead = tText.replace(r"\\", "<br/>")
|
tHead = tText.replace(r"\\", "<br/>")
|
||||||
tmpResult.append("<h2%s>%s</h2>\n" % (hStyle, tHead))
|
tmpResult.append("<%s%s>%s</%s>\n" % (h2, hStyle, tHead, h2))
|
||||||
|
|
||||||
elif tType == self.T_HEAD3:
|
elif tType == self.T_HEAD3:
|
||||||
tHead = tText.replace(r"\\", "<br/>")
|
tHead = tText.replace(r"\\", "<br/>")
|
||||||
tmpResult.append("<h3%s>%s</h3>\n" % (hStyle, tHead))
|
tmpResult.append("<%s%s>%s</%s>\n" % (h3, hStyle, tHead, h3))
|
||||||
|
|
||||||
elif tType == self.T_HEAD4:
|
elif tType == self.T_HEAD4:
|
||||||
tHead = tText.replace(r"\\", "<br/>")
|
tHead = tText.replace(r"\\", "<br/>")
|
||||||
tmpResult.append("<h4%s>%s</h4>\n" % (hStyle, tHead))
|
tmpResult.append("<%s%s>%s</%s>\n" % (h4, hStyle, tHead, h4))
|
||||||
|
|
||||||
elif tType == self.T_SEP:
|
elif tType == self.T_SEP:
|
||||||
tmpResult.append("<p%s>%s</p>\n" % (hStyle, tText))
|
tmpResult.append("<p class='sep'>%s</p>\n" % tText)
|
||||||
|
|
||||||
elif tType == self.T_SKIP:
|
elif tType == self.T_SKIP:
|
||||||
tmpResult.append("<p%s> </p>\n" % hStyle)
|
tmpResult.append("<p class='skip'> </p>\n")
|
||||||
|
|
||||||
elif tType == self.T_TEXT:
|
elif tType == self.T_TEXT:
|
||||||
tTemp = tText
|
tTemp = tText
|
||||||
parStyle = hStyle
|
if parStyle is None:
|
||||||
|
parStyle = hStyle
|
||||||
for xPos, xLen, xFmt in reversed(tFormat):
|
for xPos, xLen, xFmt in reversed(tFormat):
|
||||||
tTemp = tTemp[:xPos]+htmlTags[xFmt]+tTemp[xPos+xLen:]
|
tTemp = tTemp[:xPos]+htmlTags[xFmt]+tTemp[xPos+xLen:]
|
||||||
if tText.endswith(" "):
|
if tText.endswith(" "):
|
||||||
thisPar.append(tTemp.rstrip()+"<br/>")
|
thisPar.append(tTemp.rstrip()+"<br/>")
|
||||||
|
hasHardBreak = True
|
||||||
else:
|
else:
|
||||||
thisPar.append(tTemp.rstrip()+" ")
|
thisPar.append(tTemp.rstrip()+" ")
|
||||||
|
|
||||||
@@ -210,6 +241,29 @@ class ToHtml(Tokenizer):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def getStylesheet(self):
|
||||||
|
"""Generate a stylesheet appropriate for the current settings.
|
||||||
|
"""
|
||||||
|
theStyles = []
|
||||||
|
|
||||||
|
if self.doJustify:
|
||||||
|
theStyles.append(r"p {text-align: justify;}")
|
||||||
|
else:
|
||||||
|
theStyles.append(r"p {text-align: left;}")
|
||||||
|
|
||||||
|
theStyles.append(r"h1, h2 {color: rgb(66, 113, 174);}")
|
||||||
|
theStyles.append(r"h3, h4 {color: rgb(50, 50, 50);}")
|
||||||
|
theStyles.append(r"h1, h2, h3, h4 {page-break-after: avoid;}")
|
||||||
|
theStyles.append(r".title {font-size: 2.5em;}")
|
||||||
|
theStyles.append(r".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
||||||
|
theStyles.append(r".break {text-align: left;}")
|
||||||
|
theStyles.append(r".sep {text-align: center; margin-top: 1em; margin-bottom: 1em;}")
|
||||||
|
theStyles.append(r".skip {margin-top: 1em; margin-bottom: 1em;}")
|
||||||
|
theStyles.append(r".synopsis {font-style: italic;}")
|
||||||
|
theStyles.append(r".comment {font-style: italic; color: rgb(100, 100, 100);}")
|
||||||
|
|
||||||
|
return theStyles
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
@@ -233,7 +287,6 @@ class ToHtml(Tokenizer):
|
|||||||
def _formatKeywords(self, tText):
|
def _formatKeywords(self, tText):
|
||||||
"""Apply HTML formatting to keywords.
|
"""Apply HTML formatting to keywords.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tText = "@"+tText
|
tText = "@"+tText
|
||||||
isValid, theBits, thePos = self.theParent.theIndex.scanThis(tText)
|
isValid, theBits, thePos = self.theParent.theIndex.scanThis(tText)
|
||||||
if not isValid or not theBits:
|
if not isValid or not theBits:
|
||||||
@@ -263,4 +316,16 @@ class ToHtml(Tokenizer):
|
|||||||
|
|
||||||
return "<div>%s</div>" % retText
|
return "<div>%s</div>" % retText
|
||||||
|
|
||||||
|
def _buildRegEx(self):
|
||||||
|
"""Build the regular expressions
|
||||||
|
"""
|
||||||
|
self.revDict = dict(map(reversed, self.repDict.items()))
|
||||||
|
self.reReplace = re.compile(
|
||||||
|
"|".join([re.escape(k) for k in self.repDict.keys()]), flags=re.DOTALL
|
||||||
|
)
|
||||||
|
self.reReverse = re.compile(
|
||||||
|
"|".join([re.escape(k) for k in self.revDict.keys()]), flags=re.DOTALL
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
# END Class ToHtml
|
# END Class ToHtml
|
||||||
|
|||||||
+74
-48
@@ -34,7 +34,7 @@ from PyQt5.QtCore import QRegularExpression
|
|||||||
|
|
||||||
from nw.core.document import NWDoc
|
from nw.core.document import NWDoc
|
||||||
from nw.core.tools import numberToWord
|
from nw.core.tools import numberToWord
|
||||||
from nw.constants import nwItemLayout
|
from nw.constants import nwItemLayout, nwItemType
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -51,26 +51,26 @@ class Tokenizer():
|
|||||||
T_SYNOPSIS = 2 # Synopsis comment
|
T_SYNOPSIS = 2 # Synopsis comment
|
||||||
T_COMMENT = 3 # Comment line
|
T_COMMENT = 3 # Comment line
|
||||||
T_KEYWORD = 4 # Command line
|
T_KEYWORD = 4 # Command line
|
||||||
T_HEAD1 = 5 # Header 1 (title)
|
T_TITLE = 5 # Title
|
||||||
T_HEAD2 = 6 # Header 2 (chapter)
|
T_HEAD1 = 6 # Header 1
|
||||||
T_HEAD3 = 7 # Header 3 (scene)
|
T_HEAD2 = 7 # Header 2
|
||||||
T_HEAD4 = 8 # Header 4
|
T_HEAD3 = 8 # Header 3
|
||||||
T_TEXT = 9 # Text line
|
T_HEAD4 = 9 # Header 4
|
||||||
T_SEP = 10 # Scene separator
|
T_TEXT = 10 # Text line
|
||||||
T_SKIP = 11 # Paragraph break
|
T_SEP = 11 # Scene separator
|
||||||
|
T_SKIP = 12 # Paragraph break
|
||||||
|
|
||||||
A_LEFT = 1 # Left aligned
|
A_NONE = 0 # No special style
|
||||||
A_RIGHT = 2 # Right aligned
|
A_LEFT = 1 # Left aligned
|
||||||
A_CENTRE = 4 # Centred
|
A_RIGHT = 2 # Right aligned
|
||||||
A_JUSTIFY = 8 # Justified
|
A_CENTRE = 4 # Centred
|
||||||
A_PBB = 16 # Page break before
|
A_JUSTIFY = 8 # Justified
|
||||||
A_PBB_L = 32 # Page break before, left
|
A_PBB = 16 # Page break before always
|
||||||
A_PBB_R = 64 # Page break before, right
|
A_PBB_AV = 32 # Page break before avoid
|
||||||
A_PBB_AV = 128 # Page break, avoid
|
A_PBB_NO = 64 # Page break before never
|
||||||
A_PBA = 256 # Page break after
|
A_PBA = 128 # Page break after always
|
||||||
A_PBA_L = 512 # Page break after, left
|
A_PBA_AV = 256 # Page break after avoid
|
||||||
A_PBA_R = 1024 # Page break after, right
|
A_PBA_NO = 512 # Page break after avoid
|
||||||
A_PBA_AV = 2048 # Page break, avoid
|
|
||||||
|
|
||||||
def __init__(self, theProject, theParent):
|
def __init__(self, theProject, theParent):
|
||||||
|
|
||||||
@@ -198,6 +198,25 @@ class Tokenizer():
|
|||||||
# Class Methods
|
# Class Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def addRootHeading(self, theHandle):
|
||||||
|
"""Add a heading at the start if a new root folder.
|
||||||
|
"""
|
||||||
|
theItem = self.theProject.projTree[theHandle]
|
||||||
|
if theItem is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if theItem.itemType != nwItemType.ROOT:
|
||||||
|
return False
|
||||||
|
|
||||||
|
theTitle = "Notes: %s" % theItem.itemName
|
||||||
|
self.theTokens = []
|
||||||
|
self.theTokens.append((
|
||||||
|
self.T_TITLE, theTitle, None, self.A_PBB | self.A_CENTRE
|
||||||
|
))
|
||||||
|
self.theMarkdown = "# %s\n\n" % theTitle
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def setText(self, theHandle, theText=None):
|
def setText(self, theHandle, theText=None):
|
||||||
"""Set the text for the tokenizer from a handle. If theText is
|
"""Set the text for the tokenizer from a handle. If theText is
|
||||||
not set, load it from the file.
|
not set, load it from the file.
|
||||||
@@ -205,6 +224,8 @@ class Tokenizer():
|
|||||||
|
|
||||||
self.theHandle = theHandle
|
self.theHandle = theHandle
|
||||||
self.theItem = self.theProject.projTree[theHandle]
|
self.theItem = self.theProject.projTree[theHandle]
|
||||||
|
if self.theItem is None:
|
||||||
|
return
|
||||||
|
|
||||||
if theText is not None:
|
if theText is not None:
|
||||||
# If the text is set, just use that
|
# If the text is set, just use that
|
||||||
@@ -283,11 +304,6 @@ class Tokenizer():
|
|||||||
[None, self.FMT_U_B, None, self.FMT_U_E]
|
[None, self.FMT_U_B, None, self.FMT_U_E]
|
||||||
)]
|
)]
|
||||||
|
|
||||||
if self.doJustify:
|
|
||||||
defAlign = self.A_JUSTIFY
|
|
||||||
else:
|
|
||||||
defAlign = self.A_LEFT
|
|
||||||
|
|
||||||
self.theTokens = []
|
self.theTokens = []
|
||||||
self.theMarkdown = ""
|
self.theMarkdown = ""
|
||||||
tmpMarkdown = []
|
tmpMarkdown = []
|
||||||
@@ -296,7 +312,7 @@ class Tokenizer():
|
|||||||
# Tag lines starting with specific characters
|
# Tag lines starting with specific characters
|
||||||
if len(aLine.strip()) == 0:
|
if len(aLine.strip()) == 0:
|
||||||
self.theTokens.append((
|
self.theTokens.append((
|
||||||
self.T_EMPTY, "", None, None
|
self.T_EMPTY, "", None, self.A_NONE
|
||||||
))
|
))
|
||||||
tmpMarkdown.append("\n")
|
tmpMarkdown.append("\n")
|
||||||
|
|
||||||
@@ -304,45 +320,45 @@ class Tokenizer():
|
|||||||
cLine = aLine[1:].strip()
|
cLine = aLine[1:].strip()
|
||||||
if cLine.lower().startswith("synopsis:"):
|
if cLine.lower().startswith("synopsis:"):
|
||||||
self.theTokens.append((
|
self.theTokens.append((
|
||||||
self.T_SYNOPSIS, cLine[9:].strip(), None, defAlign
|
self.T_SYNOPSIS, cLine[9:].strip(), None, self.A_NONE
|
||||||
))
|
))
|
||||||
if self.doSynopsis:
|
if self.doSynopsis:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
else:
|
else:
|
||||||
self.theTokens.append((
|
self.theTokens.append((
|
||||||
self.T_COMMENT, aLine[1:].strip(), None, defAlign
|
self.T_COMMENT, aLine[1:].strip(), None, self.A_NONE
|
||||||
))
|
))
|
||||||
if self.doComments:
|
if self.doComments:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[0] == "@":
|
elif aLine[0] == "@":
|
||||||
self.theTokens.append((
|
self.theTokens.append((
|
||||||
self.T_KEYWORD, aLine[1:].strip(), None, self.A_LEFT
|
self.T_KEYWORD, aLine[1:].strip(), None, self.A_NONE
|
||||||
))
|
))
|
||||||
if self.doKeywords:
|
if self.doKeywords:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[:2] == "# ":
|
elif aLine[:2] == "# ":
|
||||||
self.theTokens.append((
|
self.theTokens.append((
|
||||||
self.T_HEAD1, aLine[2:].strip(), None, self.A_LEFT | self.A_PBB
|
self.T_HEAD1, aLine[2:].strip(), None, self.A_NONE
|
||||||
))
|
))
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[:3] == "## ":
|
elif aLine[:3] == "## ":
|
||||||
self.theTokens.append((
|
self.theTokens.append((
|
||||||
self.T_HEAD2, aLine[3:].strip(), None, self.A_LEFT | self.A_PBA_AV
|
self.T_HEAD2, aLine[3:].strip(), None, self.A_NONE
|
||||||
))
|
))
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[:4] == "### ":
|
elif aLine[:4] == "### ":
|
||||||
self.theTokens.append((
|
self.theTokens.append((
|
||||||
self.T_HEAD3, aLine[4:].strip(), None, self.A_LEFT | self.A_PBA_AV
|
self.T_HEAD3, aLine[4:].strip(), None, self.A_NONE
|
||||||
))
|
))
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[:5] == "#### ":
|
elif aLine[:5] == "#### ":
|
||||||
self.theTokens.append((
|
self.theTokens.append((
|
||||||
self.T_HEAD4, aLine[5:].strip(), None, self.A_LEFT | self.A_PBA_AV
|
self.T_HEAD4, aLine[5:].strip(), None, self.A_NONE
|
||||||
))
|
))
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
@@ -367,13 +383,13 @@ class Tokenizer():
|
|||||||
# sorted by position
|
# sorted by position
|
||||||
fmtPos = sorted(fmtPos, key=itemgetter(0))
|
fmtPos = sorted(fmtPos, key=itemgetter(0))
|
||||||
self.theTokens.append((
|
self.theTokens.append((
|
||||||
self.T_TEXT, aLine, fmtPos, defAlign
|
self.T_TEXT, aLine, fmtPos, self.A_NONE
|
||||||
))
|
))
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
# Always add an empty line at the end
|
# Always add an empty line at the end
|
||||||
self.theTokens.append((
|
self.theTokens.append((
|
||||||
self.T_EMPTY, "", None, None
|
self.T_EMPTY, "", None, self.A_NONE
|
||||||
))
|
))
|
||||||
tmpMarkdown.append("\n")
|
tmpMarkdown.append("\n")
|
||||||
|
|
||||||
@@ -391,8 +407,8 @@ class Tokenizer():
|
|||||||
if self.isNone or self.isNote:
|
if self.isNone or self.isNote:
|
||||||
return
|
return
|
||||||
|
|
||||||
# For novel files, we need to handle chapter numbering and scene
|
# For novel files, we need to handle chapter numbering, scene
|
||||||
# breaks
|
# numbering, and scene breaks
|
||||||
if self.isNovel:
|
if self.isNovel:
|
||||||
for n in range(len(self.theTokens)):
|
for n in range(len(self.theTokens)):
|
||||||
|
|
||||||
@@ -410,7 +426,7 @@ class Tokenizer():
|
|||||||
|
|
||||||
tText = self._formatHeading(self.fmtTitle, tText)
|
tText = self._formatHeading(self.fmtTitle, tText)
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
tType, tText, None, self.A_LEFT | self.A_PBB_R
|
tType, tText, None, self.A_NONE
|
||||||
)
|
)
|
||||||
|
|
||||||
elif tType == self.T_HEAD2:
|
elif tType == self.T_HEAD2:
|
||||||
@@ -426,7 +442,7 @@ class Tokenizer():
|
|||||||
|
|
||||||
# Format the chapter header
|
# Format the chapter header
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
tType, tText, None, self.A_LEFT | self.A_PBB_R
|
tType, tText, None, self.A_PBB
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set scene variables
|
# Set scene variables
|
||||||
@@ -443,21 +459,21 @@ class Tokenizer():
|
|||||||
tTemp = self._formatHeading(self.fmtScene, tText)
|
tTemp = self._formatHeading(self.fmtScene, tText)
|
||||||
if tTemp == "" and self.hideScene:
|
if tTemp == "" and self.hideScene:
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
self.T_EMPTY, "", None, None
|
self.T_EMPTY, "", None, self.A_NONE
|
||||||
)
|
)
|
||||||
elif tTemp == "" and not self.hideScene:
|
elif tTemp == "" and not self.hideScene:
|
||||||
if self.firstScene:
|
if self.firstScene:
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
self.T_EMPTY, "", None, None
|
self.T_EMPTY, "", None, self.A_NONE
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
self.T_SKIP, "", None, None
|
self.T_SKIP, "", None, self.A_NONE
|
||||||
)
|
)
|
||||||
elif tTemp == self.fmtScene:
|
elif tTemp == self.fmtScene:
|
||||||
if self.firstScene:
|
if self.firstScene:
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
self.T_EMPTY, "", None, None
|
self.T_EMPTY, "", None, self.A_NONE
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
@@ -465,7 +481,7 @@ class Tokenizer():
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
tType, tTemp, None, self.A_LEFT | self.A_PBA_AV
|
tType, tTemp, None, self.A_NONE
|
||||||
)
|
)
|
||||||
|
|
||||||
# Definitely no longer the first scene
|
# Definitely no longer the first scene
|
||||||
@@ -478,11 +494,11 @@ class Tokenizer():
|
|||||||
tTemp = self._formatHeading(self.fmtSection, tText)
|
tTemp = self._formatHeading(self.fmtSection, tText)
|
||||||
if tTemp == "" and self.hideSection:
|
if tTemp == "" and self.hideSection:
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
self.T_EMPTY, "", None, None
|
self.T_EMPTY, "", None, self.A_NONE
|
||||||
)
|
)
|
||||||
elif tTemp == "" and not self.hideSection:
|
elif tTemp == "" and not self.hideSection:
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
self.T_SKIP, "", None, None
|
self.T_SKIP, "", None, self.A_NONE
|
||||||
)
|
)
|
||||||
elif tTemp == self.fmtSection:
|
elif tTemp == self.fmtSection:
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
@@ -490,18 +506,28 @@ class Tokenizer():
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
tType, tTemp, None, self.A_LEFT | self.A_PBA_AV
|
tType, tTemp, None, self.A_NONE
|
||||||
)
|
)
|
||||||
|
|
||||||
# For title page and partitions, we need to centre all text.
|
# For title page and partitions, we need to centre all text.
|
||||||
# For partition, we also add a page break before, and for
|
# For partition, we also add a page break before, and for
|
||||||
# both types we always add a page break after the content.
|
# both types we always add a page break after the content.
|
||||||
|
# We also swap header level 1 with a title type instead.
|
||||||
if self.isTitle or self.isPart:
|
if self.isTitle or self.isPart:
|
||||||
for n, tToken in enumerate(self.theTokens):
|
for n, tToken in enumerate(self.theTokens):
|
||||||
tType = tToken[0]
|
tType = tToken[0]
|
||||||
tText = tToken[1]
|
tText = tToken[1]
|
||||||
tFormat = tToken[2]
|
tFormat = tToken[2]
|
||||||
if self.isTitle:
|
if tType == self.T_HEAD1:
|
||||||
|
if self.isTitle:
|
||||||
|
self.theTokens[n] = (
|
||||||
|
self.T_TITLE, tText, tFormat, self.A_PBB_NO | self.A_CENTRE
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.theTokens[n] = (
|
||||||
|
tType, tText, tFormat, self.A_PBB | self.A_CENTRE
|
||||||
|
)
|
||||||
|
else:
|
||||||
self.theTokens[n] = (
|
self.theTokens[n] = (
|
||||||
tType, tText, tFormat, self.A_CENTRE
|
tType, tText, tFormat, self.A_CENTRE
|
||||||
)
|
)
|
||||||
|
|||||||
+96
-83
@@ -34,7 +34,7 @@ from time import time
|
|||||||
from PyQt5.QtCore import Qt, QByteArray
|
from PyQt5.QtCore import Qt, QByteArray
|
||||||
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
|
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
|
||||||
from PyQt5.QtGui import (
|
from PyQt5.QtGui import (
|
||||||
QTextOption, QPalette, QColor, QTextDocumentWriter
|
QTextOption, QPalette, QColor, QTextDocumentWriter, QFont
|
||||||
)
|
)
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QVBoxLayout, QHBoxLayout, QTextBrowser, QPushButton, QLabel,
|
QDialog, QVBoxLayout, QHBoxLayout, QTextBrowser, QPushButton, QLabel,
|
||||||
@@ -44,7 +44,7 @@ from PyQt5.QtWidgets import (
|
|||||||
from nw.gui.additions import QSwitch
|
from nw.gui.additions import QSwitch
|
||||||
from nw.core import ToHtml
|
from nw.core import ToHtml
|
||||||
from nw.constants import (
|
from nw.constants import (
|
||||||
nwConst, nwFiles, nwAlert, nwItemType, nwItemLayout, nwItemClass
|
nwAlert, nwItemType, nwItemLayout, nwItemClass
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -69,9 +69,9 @@ class GuiBuildNovel(QDialog):
|
|||||||
self.theTheme = theParent.theTheme
|
self.theTheme = theParent.theTheme
|
||||||
self.optState = self.theProject.optState
|
self.optState = self.theProject.optState
|
||||||
|
|
||||||
self.htmlText = [] # List of html document
|
self.htmlText = [] # List of html document
|
||||||
self.nwdText = [] # List of markdown documents
|
self.htmlStyle = [] # List of html styles
|
||||||
self.textLayout = [] # List of nwItemLayout entries
|
self.nwdText = [] # List of markdown documents
|
||||||
|
|
||||||
self.setWindowTitle("Build Novel Project")
|
self.setWindowTitle("Build Novel Project")
|
||||||
self.setMinimumWidth(800)
|
self.setMinimumWidth(800)
|
||||||
@@ -320,11 +320,23 @@ class GuiBuildNovel(QDialog):
|
|||||||
tStart = time()
|
tStart = time()
|
||||||
|
|
||||||
self.htmlText = []
|
self.htmlText = []
|
||||||
|
self.htmlStyle = []
|
||||||
self.nwdText = []
|
self.nwdText = []
|
||||||
self.textLayout = []
|
|
||||||
|
|
||||||
for nItt, tItem in enumerate(self.theProject.projTree):
|
for nItt, tItem in enumerate(self.theProject.projTree):
|
||||||
if self._checkInclude(tItem, noteFiles, novelFiles, ignoreFlag):
|
|
||||||
|
noteRoot = noteFiles
|
||||||
|
noteRoot &= tItem.itemType == nwItemType.ROOT
|
||||||
|
noteRoot &= tItem.itemClass != nwItemClass.NOVEL
|
||||||
|
|
||||||
|
if noteRoot:
|
||||||
|
# Add headers for root folders of notes
|
||||||
|
makeHtml.addRootHeading(tItem.itemHandle)
|
||||||
|
makeHtml.doConvert()
|
||||||
|
self.htmlText.append(makeHtml.getResult())
|
||||||
|
self.nwdText.append(makeHtml.getFilteredMarkdown())
|
||||||
|
|
||||||
|
elif self._checkInclude(tItem, noteFiles, novelFiles, ignoreFlag):
|
||||||
makeHtml.setText(tItem.itemHandle)
|
makeHtml.setText(tItem.itemHandle)
|
||||||
makeHtml.doAutoReplace()
|
makeHtml.doAutoReplace()
|
||||||
makeHtml.tokenizeText()
|
makeHtml.tokenizeText()
|
||||||
@@ -333,16 +345,17 @@ class GuiBuildNovel(QDialog):
|
|||||||
makeHtml.doPostProcessing()
|
makeHtml.doPostProcessing()
|
||||||
self.htmlText.append(makeHtml.getResult())
|
self.htmlText.append(makeHtml.getResult())
|
||||||
self.nwdText.append(makeHtml.getFilteredMarkdown())
|
self.nwdText.append(makeHtml.getFilteredMarkdown())
|
||||||
self.textLayout.append(tItem.itemLayout)
|
|
||||||
|
|
||||||
# Update progress bar, also for skipped items
|
# Update progress bar, also for skipped items
|
||||||
self.buildProgress.setValue(nItt+1)
|
self.buildProgress.setValue(nItt+1)
|
||||||
|
|
||||||
tEnd = time()
|
tEnd = time()
|
||||||
logger.debug("Built project in %.3f ms" % (1000*(tEnd-tStart)))
|
logger.debug("Built project in %.3f ms" % (1000*(tEnd-tStart)))
|
||||||
|
self.htmlStyle = makeHtml.getStylesheet()
|
||||||
|
|
||||||
# Load the preview document with the html data
|
# Load the preview document with the html data
|
||||||
self.docView.setHtml("".join(self.htmlText))
|
self.docView.setStyleSheet(self.htmlStyle)
|
||||||
|
self.docView.setContent(self.htmlText)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -457,58 +470,53 @@ class GuiBuildNovel(QDialog):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Do the actual writing
|
# Do the actual writing
|
||||||
|
wSuccess = False
|
||||||
|
errMsg = ""
|
||||||
if outTool == "Qt":
|
if outTool == "Qt":
|
||||||
docWriter = QTextDocumentWriter()
|
docWriter = QTextDocumentWriter()
|
||||||
docWriter.setFileName(savePath)
|
docWriter.setFileName(savePath)
|
||||||
docWriter.setFormat(byteFmt)
|
docWriter.setFormat(byteFmt)
|
||||||
if docWriter.write(self.docView.qDocument):
|
wSuccess = docWriter.write(self.docView.qDocument)
|
||||||
self.theParent.makeAlert(
|
|
||||||
"Document successfully written in %s format to file: %s" % (
|
|
||||||
textFmt, savePath
|
|
||||||
), nwAlert.INFO
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.theParent.makeAlert(
|
|
||||||
"Failed to write document in %s format to file: %s" % (
|
|
||||||
textFmt, savePath
|
|
||||||
), nwAlert.ERROR
|
|
||||||
)
|
|
||||||
|
|
||||||
elif outTool == "NW":
|
elif outTool == "NW":
|
||||||
try:
|
try:
|
||||||
with open(savePath, mode="w", encoding="utf8") as outFile:
|
with open(savePath, mode="w", encoding="utf8") as outFile:
|
||||||
if theFormat == self.FMT_HTM:
|
if theFormat == self.FMT_HTM:
|
||||||
# Write novelWriter HTML data
|
# Write novelWriter HTML data
|
||||||
outFile.write("<!DOCTYPE html>\n")
|
theStyle = self.htmlStyle.copy()
|
||||||
outFile.write("<html>\n")
|
theStyle.append(r"article {width: 800px; margin: 40px auto;}")
|
||||||
outFile.write("<head>\n")
|
theHtml = (
|
||||||
outFile.write("<meta charset='utf-8'>\n")
|
"<!DOCTYPE html>\n"
|
||||||
outFile.write("</head>\n")
|
"<html>\n"
|
||||||
outFile.write("<body>\n")
|
"<head>\n"
|
||||||
outFile.write("<article style='width: 800px; margin: 40px auto'>\n")
|
"<meta charset='utf-8'>\n"
|
||||||
for aLine in self.htmlText:
|
"<title>{projTitle:s}</title>\n"
|
||||||
outFile.write(aLine)
|
"</head>\n"
|
||||||
outFile.write("</article>\n")
|
"<style>\n"
|
||||||
outFile.write("</body>\n")
|
"{htmlStyle:s}\n"
|
||||||
outFile.write("</html>\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 = "".join(self.htmlText),
|
||||||
|
)
|
||||||
|
outFile.write(theHtml)
|
||||||
|
|
||||||
elif theFormat == self.FMT_NWD:
|
elif theFormat == self.FMT_NWD:
|
||||||
# Write novelWriter markdown data
|
# Write novelWriter markdown data
|
||||||
for aLine in self.nwdText:
|
for aLine in self.nwdText:
|
||||||
outFile.write(aLine)
|
outFile.write(aLine)
|
||||||
|
|
||||||
self.theParent.makeAlert(
|
wSuccess = True
|
||||||
"Document successfully written in %s format to file: %s" % (
|
|
||||||
textFmt, savePath
|
|
||||||
), nwAlert.INFO
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.theParent.makeAlert(
|
errMsg = str(e)
|
||||||
"Failed to write document in %s format to file: %s" % (
|
|
||||||
textFmt, str(e)
|
|
||||||
), nwAlert.ERROR
|
|
||||||
)
|
|
||||||
|
|
||||||
elif outTool == "QtPrint" and theFormat == self.FMT_PDF:
|
elif outTool == "QtPrint" and theFormat == self.FMT_PDF:
|
||||||
try:
|
try:
|
||||||
@@ -520,23 +528,29 @@ class GuiBuildNovel(QDialog):
|
|||||||
thePrinter.setColorMode(QPrinter.Color)
|
thePrinter.setColorMode(QPrinter.Color)
|
||||||
thePrinter.setOutputFileName(savePath)
|
thePrinter.setOutputFileName(savePath)
|
||||||
self.docView.qDocument.print(thePrinter)
|
self.docView.qDocument.print(thePrinter)
|
||||||
self.theParent.makeAlert(
|
wSuccess = True
|
||||||
"Document successfully written in %s format to file: %s" % (
|
|
||||||
textFmt, savePath
|
|
||||||
), nwAlert.INFO
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.theParent.makeAlert(
|
errMsg - str(e)
|
||||||
"Failed to write document in %s format to file: %s" % (
|
|
||||||
textFmt, str(e)
|
|
||||||
), nwAlert.ERROR
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return False
|
errMsg = "Unknown format"
|
||||||
|
|
||||||
return True
|
# Report to user
|
||||||
|
if wSuccess:
|
||||||
|
self.theParent.makeAlert(
|
||||||
|
"%s file successfully written to:<br> %s" % (
|
||||||
|
textFmt, savePath
|
||||||
|
), nwAlert.INFO
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.theParent.makeAlert(
|
||||||
|
"Failed to write %s file. %s" % (
|
||||||
|
textFmt, errMsg
|
||||||
|
), nwAlert.ERROR
|
||||||
|
)
|
||||||
|
|
||||||
|
return wSuccess
|
||||||
|
|
||||||
def _printDocument(self):
|
def _printDocument(self):
|
||||||
"""Open the print preview dialog.
|
"""Open the print preview dialog.
|
||||||
@@ -550,7 +564,6 @@ class GuiBuildNovel(QDialog):
|
|||||||
"""Connect the print preview painter to the document viewer.
|
"""Connect the print preview painter to the document viewer.
|
||||||
"""
|
"""
|
||||||
thePrinter.setOrientation(QPrinter.Portrait)
|
thePrinter.setOrientation(QPrinter.Portrait)
|
||||||
thePrinter.setOutputFormat(QPrinter.NativeFormat | QPrinter.PdfFormat)
|
|
||||||
self.docView.qDocument.print(thePrinter)
|
self.docView.qDocument.print(thePrinter)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -612,7 +625,8 @@ class GuiBuildNovel(QDialog):
|
|||||||
if path.isfile(docPath):
|
if path.isfile(docPath):
|
||||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||||
helpText = inFile.read()
|
helpText = inFile.read()
|
||||||
self.docView.setText(helpText)
|
self.docView.setStyleSheet()
|
||||||
|
self.docView.setContent(helpText)
|
||||||
else:
|
else:
|
||||||
self.theParent.makeAlert(
|
self.theParent.makeAlert(
|
||||||
"Could not open help text file for Build Project.", nwAlert.ERROR
|
"Could not open help text file for Build Project.", nwAlert.ERROR
|
||||||
@@ -638,6 +652,14 @@ class GuiBuildNovelDocView(QTextBrowser):
|
|||||||
self.qDocument = self.document()
|
self.qDocument = self.document()
|
||||||
self.qDocument.setDocumentMargin(self.mainConf.textMargin)
|
self.qDocument.setDocumentMargin(self.mainConf.textMargin)
|
||||||
|
|
||||||
|
theFont = QFont()
|
||||||
|
if self.mainConf.textFont is None:
|
||||||
|
# If none is defined, set the default back to config
|
||||||
|
self.mainConf.textFont = self.qDocument.defaultFont().family()
|
||||||
|
theFont.setFamily(self.mainConf.textFont)
|
||||||
|
theFont.setPointSize(self.mainConf.textSize)
|
||||||
|
self.setFont(theFont)
|
||||||
|
|
||||||
theOpt = QTextOption()
|
theOpt = QTextOption()
|
||||||
if self.mainConf.doJustify:
|
if self.mainConf.doJustify:
|
||||||
theOpt.setAlignment(Qt.AlignJustify)
|
theOpt.setAlignment(Qt.AlignJustify)
|
||||||
@@ -648,7 +670,7 @@ class GuiBuildNovelDocView(QTextBrowser):
|
|||||||
docPalette.setColor(QPalette.Text, QColor( 0, 0, 0))
|
docPalette.setColor(QPalette.Text, QColor( 0, 0, 0))
|
||||||
self.setPalette(docPalette)
|
self.setPalette(docPalette)
|
||||||
|
|
||||||
self._makeStyleSheet()
|
self.setStyleSheet()
|
||||||
|
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
@@ -656,35 +678,26 @@ class GuiBuildNovelDocView(QTextBrowser):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def setText(self, theText):
|
def setContent(self, theText):
|
||||||
|
"""Set the content, either from text or list of text.
|
||||||
|
"""
|
||||||
|
if isinstance(theText, list):
|
||||||
|
theText = "".join(theText)
|
||||||
|
theText = theText.replace(" "," "*4)
|
||||||
self.setHtml(theText)
|
self.setHtml(theText)
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
def setStyleSheet(self, theStyles=[]):
|
||||||
# Internal Functions
|
"""Set the stylesheet for the preview document.
|
||||||
##
|
"""
|
||||||
|
if not theStyles:
|
||||||
|
theStyles.append(r"h1, h2 {color: rgb(66, 113, 174);}")
|
||||||
|
theStyles.append(r"h3, h4 {color: rgb(50, 50, 50);}")
|
||||||
|
theStyles.append(r"a {color: rgb(137, 89, 168);}")
|
||||||
|
theStyles.append(r"mark {background-color: rgb(240, 198, 116);}")
|
||||||
|
theStyles.append(r".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
||||||
|
|
||||||
def _makeStyleSheet(self):
|
self.qDocument.setDefaultStyleSheet("\n".join(theStyles))
|
||||||
|
|
||||||
styleSheet = (
|
|
||||||
"h1, h2 {"
|
|
||||||
" color: rgb(66, 113, 174);"
|
|
||||||
"}\n"
|
|
||||||
"h3, h4 {"
|
|
||||||
" color: rgb(50, 50, 50);"
|
|
||||||
"}\n"
|
|
||||||
"a {"
|
|
||||||
" color: rgb(137, 89, 168);"
|
|
||||||
"}\n"
|
|
||||||
"mark {"
|
|
||||||
" background-color: rgb(240, 198, 116);"
|
|
||||||
"}\n"
|
|
||||||
".tags {"
|
|
||||||
" color: rgb(245, 135, 31);"
|
|
||||||
" font-wright: bold;"
|
|
||||||
"}\n"
|
|
||||||
)
|
|
||||||
self.qDocument.setDefaultStyleSheet(styleSheet)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
%%~ edca4be2fcaf8:7031beac91f75:Part 1
|
||||||
|
# Part One
|
||||||
|
|
||||||
|
The first part.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="0.5" hexVersion="0x000500f0" fileVersion="1.0" saveCount="130" autoCount="17" timeStamp="2020-05-11 19:23:21">
|
<novelWriterXML appVersion="0.5.2" hexVersion="0x000502f0" fileVersion="1.0" saveCount="158" autoCount="21" timeStamp="2020-05-23 21:12:53">
|
||||||
<project>
|
<project>
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<title>Sample Project</title>
|
<title>Sample Project</title>
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<settings>
|
<settings>
|
||||||
<spellCheck>True</spellCheck>
|
<spellCheck>True</spellCheck>
|
||||||
<autoOutline>True</autoOutline>
|
<autoOutline>True</autoOutline>
|
||||||
<lastEdited>96b68994dfa3d</lastEdited>
|
<lastEdited>636b6aa9b697b</lastEdited>
|
||||||
<lastViewed>6a2d6d5f4f401</lastViewed>
|
<lastViewed>6a2d6d5f4f401</lastViewed>
|
||||||
<lastWordCount>875</lastWordCount>
|
<lastWordCount>875</lastWordCount>
|
||||||
<autoReplace>
|
<autoReplace>
|
||||||
@@ -20,12 +20,12 @@
|
|||||||
</autoReplace>
|
</autoReplace>
|
||||||
<titleFormat>
|
<titleFormat>
|
||||||
<title>%title%</title>
|
<title>%title%</title>
|
||||||
<chapter>Chapter %chnum%.\\%title%</chapter>
|
<chapter>Chapter %chnum%: %title%</chapter>
|
||||||
<unnumbered>%title%</unnumbered>
|
<unnumbered>%title%</unnumbered>
|
||||||
<scene>Scene %chnum%.%scnum%: %title%</scene>
|
<scene>* * *</scene>
|
||||||
<section></section>
|
<section></section>
|
||||||
<withSynopsis>True</withSynopsis>
|
<withSynopsis>True</withSynopsis>
|
||||||
<withComments>False</withComments>
|
<withComments>True</withComments>
|
||||||
<withKeywords>False</withKeywords>
|
<withKeywords>False</withKeywords>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
<entry blue="175" green="0" red="117">Main</entry>
|
<entry blue="175" green="0" red="117">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content count="20">
|
<content count="21">
|
||||||
<item handle="7031beac91f75" order="0" parent="None">
|
<item handle="7031beac91f75" order="0" parent="None">
|
||||||
<name>Novel</name>
|
<name>Novel</name>
|
||||||
<type>ROOT</type>
|
<type>ROOT</type>
|
||||||
@@ -65,7 +65,20 @@
|
|||||||
<paraCount>2</paraCount>
|
<paraCount>2</paraCount>
|
||||||
<cursorPos>78</cursorPos>
|
<cursorPos>78</cursorPos>
|
||||||
</item>
|
</item>
|
||||||
<item handle="e7ded148d6e4a" order="1" parent="7031beac91f75">
|
<item handle="edca4be2fcaf8" order="1" parent="7031beac91f75">
|
||||||
|
<name>Part 1</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>PARTITION</layout>
|
||||||
|
<charCount>0</charCount>
|
||||||
|
<wordCount>0</wordCount>
|
||||||
|
<paraCount>0</paraCount>
|
||||||
|
<cursorPos>0</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="e7ded148d6e4a" order="2" parent="7031beac91f75">
|
||||||
<name>A Folder</name>
|
<name>A Folder</name>
|
||||||
<type>FOLDER</type>
|
<type>FOLDER</type>
|
||||||
<class>NOVEL</class>
|
<class>NOVEL</class>
|
||||||
|
|||||||
Reference in New Issue
Block a user