# -*- coding: utf-8 -*-
"""novelWriter HTML Text Converter
novelWriter – HTML Text Converter
===================================
Extends the Tokenizer class to write HTML
File History:
Created: 2019-05-07 [0.0.1]
This file is a part of novelWriter
Copyright 2020, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
import logging
import re
import nw
from nw.core.tokenizer import Tokenizer
from nw.constants import nwUnicode, nwLabels, nwKeyWords
logger = logging.getLogger(__name__)
class ToHtml(Tokenizer):
M_PREVIEW = 0 # Tweak output for the DocViewer
M_EXPORT = 1 # Tweak output for saving to HTML or printing
M_EBOOK = 2 # Tweak output for converting to epub
def __init__(self, theProject, theParent):
Tokenizer.__init__(self, theProject, theParent)
self.genMode = self.M_EXPORT
self.repDict = {
"<" : "<",
">" : ">",
"&" : "&",
"\t" : " ",
nwUnicode.U_ENDASH : nwUnicode.H_ENDASH,
nwUnicode.U_EMDASH : nwUnicode.H_EMDASH,
nwUnicode.U_HELLIP : nwUnicode.H_HELLIP,
nwUnicode.U_NBSP : nwUnicode.H_NBSP,
}
return
##
# Setters
##
def setPreview(self, forPreview, doComments):
"""If we're using this class to generate markdown preview, we
need to make a few changes to formatting, which is managed by
these flags.
"""
if forPreview:
self.genMode = self.M_PREVIEW
self.doKeywords = True
self.doComments = doComments
self.repDict["\t"] = " "*8
return
##
# Class Methods
##
def doAutoReplace(self):
"""Extend the auto-replace to also properly encode some unicode
characters into their respective HTML entities.
"""
Tokenizer.doAutoReplace(self)
xRep = re.compile("|".join([re.escape(k) for k in self.repDict.keys()]), flags=re.DOTALL)
self.theText = xRep.sub(lambda x: self.repDict[x.group(0)], self.theText)
return
def doPostProcessing(self):
"""Reverse the html entities replacement on the markdown text.
Otherwise, all the &something; bits will also be in there.
"""
if self.genMode == self.M_PREVIEW:
# Doesn't matter for preview as we don't use the markdown
return
revDict = dict(map(reversed, self.repDict.items()))
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
def doConvert(self):
"""Convert the list of text tokens into a HTML document saved
to theResult.
"""
htmlTags = {
self.FMT_B_B : "",
self.FMT_B_E : "",
self.FMT_I_B : "",
self.FMT_I_E : "",
self.FMT_U_B : "",
self.FMT_U_E : "",
}
self.theResult = ""
thisPar = []
parStyle = ""
tmpResult = []
for tType, tText, tFormat, tStyle in self.theTokens:
# Styles
aStyle = []
if tStyle is not None:
if tStyle & self.A_LEFT:
aStyle.append("text-align: left;")
if tStyle & self.A_RIGHT:
aStyle.append("text-align: right;")
if tStyle & self.A_CENTRE:
aStyle.append("text-align: center;")
if 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_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:
aStyle.append("page-break-before: avoid;")
if tStyle & self.A_PBA:
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:
aStyle.append("page-break-after: avoid;")
if len(aStyle) > 0:
hStyle = " style='%s'" % (" ".join(aStyle))
else:
hStyle = ""
# Process TextType
if tType == self.T_EMPTY:
if len(thisPar) > 0:
tTemp = "".join(thisPar)
tmpResult.append("
%s
\n" % (parStyle, tTemp.rstrip()))
thisPar = []
parStyle = ""
elif tType == self.T_HEAD1:
tHead = tText.replace(r"\\", "
")
tmpResult.append("%s
\n" % (hStyle, tHead))
elif tType == self.T_HEAD2:
tHead = tText.replace(r"\\", "
")
tmpResult.append("%s
\n" % (hStyle, tHead))
elif tType == self.T_HEAD3:
tHead = tText.replace(r"\\", "
")
tmpResult.append("%s
\n" % (hStyle, tHead))
elif tType == self.T_HEAD4:
tHead = tText.replace(r"\\", "
")
tmpResult.append("%s
\n" % (hStyle, tHead))
elif tType == self.T_SEP:
tmpResult.append("%s
\n" % (hStyle, tText))
elif tType == self.T_SKIP:
tmpResult.append("
\n" % hStyle)
elif tType == self.T_TEXT:
tTemp = tText
parStyle = hStyle
for xPos, xLen, xFmt in reversed(tFormat):
tTemp = tTemp[:xPos]+htmlTags[xFmt]+tTemp[xPos+xLen:]
if tText.endswith(" "):
thisPar.append(tTemp.rstrip()+"
")
else:
thisPar.append(tTemp.rstrip()+" ")
elif tType == self.T_SYNOPSIS and self.doSynopsis:
tmpResult.append(self._formatSynopsis(tText))
elif tType == self.T_COMMENT and self.doComments:
tmpResult.append(self._formatComments(tText))
elif tType == self.T_KEYWORD and self.doKeywords:
tmpResult.append(self._formatKeywords(tText))
self.theResult = "".join(tmpResult)
tmpResult = []
return
##
# Internal Functions
##
def _formatSynopsis(self, tText):
"""Apply HTML formatting to synopsis.
"""
if self.genMode == self.M_EXPORT:
return "Synopsis: %s
\n" % tText
else:
return "\n" % tText
def _formatComments(self, tText):
"""Apply HTML formatting to comments.
"""
if self.genMode == self.M_EXPORT:
return "\n" % tText
else:
return "\n" % tText
def _formatKeywords(self, tText):
"""Apply HTML formatting to keywords.
"""
tText = "@"+tText
isValid, theBits, thePos = self.theParent.theIndex.scanThis(tText)
if not isValid or not theBits:
return ""
retText = ""
refTags = []
if theBits[0] in nwLabels.KEY_NAME:
retText += "%s: " % nwLabels.KEY_NAME[theBits[0]]
if self.genMode == self.M_PREVIEW:
for tTag in theBits[1:]:
refTags.append("%s" % (
theBits[0][1:], tTag, tTag
))
retText += ", ".join(refTags)
else:
if theBits[0] == nwKeyWords.TAG_KEY:
retText += "%s" % (
theBits[1], theBits[1]
)
else:
for tTag in theBits[1:]:
refTags.append("%s" % (
tTag, tTag
))
retText += ", ".join(refTags)
return "%s
" % retText
# END Class ToHtml