Added export to html and markdown files

This commit is contained in:
Veronica K. B. Olsen
2019-10-19 14:59:32 +02:00
parent 24fe12f0d3
commit 16401f4c1f
7 changed files with 264 additions and 20 deletions
+57
View File
@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
"""novelWriter HTML File
novelWriter HTML File
=========================
Writes the project to a html file
File History:
Created: 2019-10-19 [0.3]
"""
import logging
import nw
from nw.convert.textfile import TextFile
from nw.convert.tohtml import ToHtml
logger = logging.getLogger(__name__)
class HtmlFile(TextFile):
def __init__(self, theProject, theParent):
TextFile.__init__(self, theProject, theParent)
self.theConv = ToHtml(self.theProject, self.theParent)
return
##
# Internal Functions
##
def _doOpenFile(self, filePath):
try:
self.outFile = open(filePath,mode="w+")
self.outFile.write("<!DOCTYPE html>\n")
self.outFile.write("<html>\n")
self.outFile.write("<head>\n")
self.outFile.write("<style>\n")
self.outFile.write(" pre {background-color: #ffff99;}\n")
self.outFile.write("</style>\n")
self.outFile.write("</head>\n")
self.outFile.write("<body>\n")
except Exception as e:
self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR)
return False
return True
def _doCloseFile(self):
if self.outFile is not None:
self.outFile.write("</body>\n")
self.outFile.write("</html>\n")
self.outFile.close()
return True
# END Class HtmlFile
+47
View File
@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
"""novelWriter Markdown File
novelWriter Markdown File
=============================
Writes the project to a markdown file
File History:
Created: 2019-10-19 [0.3]
"""
import logging
import nw
from nw.convert.textfile import TextFile
from nw.convert.tomarkdown import ToMarkdown
logger = logging.getLogger(__name__)
class MarkdownFile(TextFile):
def __init__(self, theProject, theParent):
TextFile.__init__(self, theProject, theParent)
self.theConv = ToMarkdown(self.theProject, self.theParent)
return
##
# Internal Functions
##
def _doOpenFile(self, filePath):
try:
self.outFile = open(filePath,mode="w+")
except Exception as e:
self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR)
return False
return True
def _doCloseFile(self):
if self.outFile is not None:
self.outFile.close()
return True
# END Class MarkdownFile
+1 -2
View File
@@ -28,7 +28,6 @@ class TextFile():
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
self.theProject = theProject self.theProject = theProject
self.theParent = theParent self.theParent = theParent
self.fileExt = "txt"
self.outFile = None self.outFile = None
self.fileName = "" self.fileName = ""
@@ -169,7 +168,7 @@ class TextFile():
return True return True
def _doCloseFile(self): def _doCloseFile(self):
"""This function closes a file, and is meant to be overloaded by the subclass for other """This function closes the file, and is meant to be overloaded by the subclass for other
file formats. file formats.
""" """
if self.outFile is not None: if self.outFile is not None:
+29 -6
View File
@@ -22,7 +22,6 @@ class ToHtml(Tokenizer):
def __init__(self, theProject, theParent): def __init__(self, theProject, theParent):
Tokenizer.__init__(self, theProject, theParent) Tokenizer.__init__(self, theProject, theParent)
return return
def doAutoReplace(self): def doAutoReplace(self):
@@ -53,24 +52,48 @@ class ToHtml(Tokenizer):
thisPar = [] thisPar = []
for tType, tText, tFormat, tAlign in self.theTokens: for tType, tText, tFormat, tAlign in self.theTokens:
aStyle = []
if tAlign == self.A_CENTRE:
aStyle.append("text-align: center;")
if len(aStyle) > 0:
hStyle = " style='%s'" % (" ".join(aStyle))
else:
hStyle = ""
if tType == self.T_EMPTY: if tType == self.T_EMPTY:
if len(thisPar) > 0: if len(thisPar) > 0:
self.theResult += "<p>%s</p>\n" % " ".join(thisPar) self.theResult += "<p%s>%s</p>\n" % (hStyle," ".join(thisPar))
thisPar = [] thisPar = []
elif tType == self.T_HEAD1: elif tType == self.T_HEAD1:
self.theResult += "<h1>%s</h1>\n" % tText self.theResult += "<h1%s>%s</h1>\n" % (hStyle,tText)
elif tType == self.T_HEAD2: elif tType == self.T_HEAD2:
self.theResult += "<h2>%s</h2>\n" % tText self.theResult += "<h2%s>%s</h2>\n" % (hStyle,tText)
elif tType == self.T_HEAD3: elif tType == self.T_HEAD3:
self.theResult += "<h3>%s</h3>\n" % tText print(tText)
self.theResult += "<h3%s>%s</h3>\n" % (hStyle,tText)
elif tType == self.T_HEAD4: elif tType == self.T_HEAD4:
self.theResult += "<h4>%s</h4>\n" % tText self.theResult += "<h4%s>%s</h4>\n" % (hStyle,tText)
elif tType == self.T_SEP:
self.theResult += "<div%s>%s</div>\n" % (hStyle,tText)
elif tType == self.T_TEXT: elif tType == self.T_TEXT:
tTemp = tText tTemp = tText
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:]
thisPar.append(tTemp) thisPar.append(tTemp)
elif tType == self.T_COMMENT and self.doComments:
self.theResult += "<pre>%s</pre>\n" % tText
elif tType == self.T_COMMAND and self.doCommands:
self.theResult += "<pre>%s</pre>\n" % tText
# print(self.theResult) # print(self.theResult)
return return
+2 -2
View File
@@ -247,7 +247,7 @@ class Tokenizer():
if self.firstScene: if self.firstScene:
self.theTokens[n] = (self.T_EMPTY,"",None,self.A_LEFT) self.theTokens[n] = (self.T_EMPTY,"",None,self.A_LEFT)
else: else:
self.theTokens[n] = (self.T_SEP,tTemp,None,self.A_LEFT) self.theTokens[n] = (self.T_SEP,tTemp,None,self.A_CENTRE)
else: else:
self.theTokens[n] = (tType,tTemp,None,self.A_LEFT) self.theTokens[n] = (tType,tTemp,None,self.A_LEFT)
self.firstScene = False self.firstScene = False
@@ -257,7 +257,7 @@ class Tokenizer():
if tTemp == "": if tTemp == "":
self.theTokens[n] = (self.T_EMPTY,"",None,self.A_LEFT) self.theTokens[n] = (self.T_EMPTY,"",None,self.A_LEFT)
elif tTemp == self.fmtSection: elif tTemp == self.fmtSection:
self.theTokens[n] = (self.T_SEP,tTemp,None,self.A_LEFT) self.theTokens[n] = (self.T_SEP,tTemp,None,self.A_CENTRE)
else: else:
self.theTokens[n] = (tType,tTemp,None,self.A_LEFT) self.theTokens[n] = (tType,tTemp,None,self.A_LEFT)
+112
View File
@@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
"""novelWriter Markdown Text Converter
novelWriter Markdown Text Converter
=======================================
Extends the Tokenizer class to write Markdown
File History:
Created: 2019-10-19 [0.3]
"""
import textwrap
import logging
import re
import nw
from nw.convert.tokenizer import Tokenizer
logger = logging.getLogger(__name__)
class ToMarkdown(Tokenizer):
def __init__(self, theProject, theParent):
Tokenizer.__init__(self, theProject, theParent)
return
def doConvert(self):
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 : "__",
}
if self.wordWrap > 0:
tWrap = textwrap.TextWrapper(
width = self.wordWrap,
initial_indent = "",
subsequent_indent = "",
expand_tabs = True,
replace_whitespace = True,
fix_sentence_endings = False,
break_long_words = True,
drop_whitespace = True,
break_on_hyphens = True,
tabsize = 8,
max_lines = None
)
self.theResult = ""
thisPar = []
for tType, tText, tFormat, tAlign in self.theTokens:
# First check if we have a comment or plain text, as they need some
# extra replacing before we proceed to wrapping and final formatting.
if tType == self.T_COMMENT:
tText = " %s" % tText
elif tType == self.T_TEXT:
tTemp = tText
for xPos, xLen, xFmt in reversed(tFormat):
tTemp = tTemp[:xPos]+tTemp[xPos+xLen:]
tText = tTemp
tLen = len(tText)
# The text can now be word wrapped, if we have requested this and it's needed.
if self.wordWrap > 0 and tLen > self.wordWrap:
if tType == self.T_COMMENT:
tText = textwrap.fill(tText.strip(),initial_indent=" ",subsequent_indent=" ")
else:
tText = tWrap.fill(tText)
# Then the text can receive final formatting before we append it to the results.
# We also store text lines in a buffer and merge them only when we find an empty line,
# indicating a new paragraph.
if tType == self.T_EMPTY:
if len(thisPar) > 0:
self.theResult += "%s\n\n" % " ".join(thisPar)
thisPar = []
elif tType == self.T_HEAD1:
self.theResult += "# %s\n\n" % tText
elif tType == self.T_HEAD2:
self.theResult += "## %s\n\n" % tText
elif tType == self.T_HEAD3:
self.theResult += "### %s\n\n" % tText
elif tType == self.T_HEAD4:
self.theResult += "#### %s\n\n" % tText
elif tType == self.T_SEP:
self.theResult += "%s\n\n" % tText
elif tType == self.T_TEXT:
thisPar.append(tText)
elif tType == self.T_COMMENT and self.doComments:
self.theResult += "%s\n\n" % tText
elif tType == self.T_COMMAND and self.doCommands:
self.theResult += "%s\n\n" % tText
return
# END Class ToMarkdown
+16 -10
View File
@@ -24,12 +24,14 @@ from PyQt5.QtWidgets import (
QLabel, QComboBox, QLineEdit, QPushButton, QFileDialog, QProgressBar, QSpinBox QLabel, QComboBox, QLineEdit, QPushButton, QFileDialog, QProgressBar, QSpinBox
) )
from nw.project.document import NWDoc from nw.project.document import NWDoc
from nw.tools.translate import numberToWord from nw.tools.translate import numberToWord
from nw.convert.textfile import TextFile from nw.convert.textfile import TextFile
from nw.common import checkString, checkBool, checkInt from nw.convert.htmlfile import HtmlFile
from nw.constants import nwFiles from nw.convert.markdownfile import MarkdownFile
from nw.enum import nwItemType from nw.common import checkString, checkBool, checkInt
from nw.constants import nwFiles
from nw.enum import nwItemType
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -121,6 +123,10 @@ class GuiExport(QDialog):
outFile = None outFile = None
if eFormat == GuiExportMain.FMT_TXT: if eFormat == GuiExportMain.FMT_TXT:
outFile = TextFile(self.theProject, self.theParent) outFile = TextFile(self.theProject, self.theParent)
elif eFormat == GuiExportMain.FMT_MD:
outFile = MarkdownFile(self.theProject, self.theParent)
elif eFormat == GuiExportMain.FMT_HTML:
outFile = HtmlFile(self.theProject, self.theParent)
if outFile is None: if outFile is None:
return False return False
@@ -138,11 +144,11 @@ class GuiExport(QDialog):
self.exportStatus.setText("Failed to open file for writing ...") self.exportStatus.setText("Failed to open file for writing ...")
return False return False
time.sleep(0.5)
nDone = 0 nDone = 0
for tHandle in self.theProject.treeOrder: for tHandle in self.theProject.treeOrder:
time.sleep(0.1)
self.exportProgress.setValue(nDone) self.exportProgress.setValue(nDone)
tItem = self.theProject.getItem(tHandle) tItem = self.theProject.getItem(tHandle)
@@ -338,8 +344,8 @@ class GuiExportMain(QWidget):
self.outputFormat = QComboBox(self) self.outputFormat = QComboBox(self)
self.outputFormat.addItem("Plain Text (.txt)", self.FMT_TXT) self.outputFormat.addItem("Plain Text (.txt)", self.FMT_TXT)
# self.outputFormat.addItem("Markdown (.md)", self.FMT_MD) self.outputFormat.addItem("Markdown (.md)", self.FMT_MD)
# self.outputFormat.addItem("HTML5 (.htm)", self.FMT_HTML) self.outputFormat.addItem("HTML5 (.htm)", self.FMT_HTML)
# self.outputFormat.addItem("HTML5 for eBook (.htm)", self.FMT_EBOOK) # self.outputFormat.addItem("HTML5 for eBook (.htm)", self.FMT_EBOOK)
# self.outputFormat.addItem("Open Document (.odt)", self.FMT_ODT) # self.outputFormat.addItem("Open Document (.odt)", self.FMT_ODT)
# self.outputFormat.addItem("LaTeX for PDF (.tex)", self.FMT_TEX) # self.outputFormat.addItem("LaTeX for PDF (.tex)", self.FMT_TEX)