Basic export to plain text now works, but many options not connected yet
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Text File
|
||||
|
||||
novelWriter – Text File
|
||||
=========================
|
||||
Writes the project to a plain text file
|
||||
|
||||
File History:
|
||||
Created: 2019-10-18 [0.2.3]
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import nw
|
||||
|
||||
from os import path
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from nw.convert.tokenizer import Tokenizer
|
||||
from nw.enum import nwAlert
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class TextFile():
|
||||
|
||||
def __init__(self, theProject, theParent):
|
||||
|
||||
self.mainConf = nw.CONFIG
|
||||
self.theProject = theProject
|
||||
self.theParent = theParent
|
||||
self.fileExt = "txt"
|
||||
|
||||
self.outFile = None
|
||||
self.fileName = ""
|
||||
self.theText = ""
|
||||
self.doComments = False
|
||||
self.doMeta = False
|
||||
self.wordWrap = 80
|
||||
self.winEnding = False
|
||||
|
||||
self.makeAlert = self.theParent.makeAlert
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Setters
|
||||
##
|
||||
|
||||
def setComments(self, doComments):
|
||||
self.doComments = doComments
|
||||
return
|
||||
|
||||
def setMeta(self, doMeta):
|
||||
self.doMeta = doMeta
|
||||
return
|
||||
|
||||
##
|
||||
# Core Methods
|
||||
##
|
||||
|
||||
def openFile(self, saveTo, baseName):
|
||||
|
||||
fileName = "%s.%s" % (baseName.strip(),self.fileExt)
|
||||
filePath = path.join(saveTo,fileName)
|
||||
|
||||
self.fileName = fileName
|
||||
|
||||
if path.isfile(filePath)and self.mainConf.showGUI:
|
||||
msgBox = QMessageBox()
|
||||
msgRes = msgBox.question(
|
||||
self.theParent, "Overwrite", ("File '%s' already exists.<br>Do you want to overwrite it?" % fileName)
|
||||
)
|
||||
if msgRes != QMessageBox.Yes:
|
||||
return False
|
||||
|
||||
self._doOpenFile(filePath)
|
||||
|
||||
return True
|
||||
|
||||
def closeFile(self):
|
||||
self._doCloseFile()
|
||||
return True
|
||||
|
||||
def addText(self, tHandle):
|
||||
|
||||
logger.verbose("Parsing content of item '%s'" % tHandle)
|
||||
|
||||
aDoc = Tokenizer(self.theProject, self.theParent)
|
||||
aDoc.setText(tHandle)
|
||||
aDoc.doAutoReplace()
|
||||
aDoc.tokenizeText()
|
||||
|
||||
aDoc.setComments(self.doComments)
|
||||
aDoc.setCommands(self.doMeta)
|
||||
aDoc.setWordWrap(self.wordWrap)
|
||||
|
||||
aDoc.doConvert()
|
||||
|
||||
theText = ""
|
||||
if aDoc.theResult is not None:
|
||||
theText = aDoc.theResult
|
||||
|
||||
if self.winEnding:
|
||||
theText = theText.replace("\n","\r\n")
|
||||
|
||||
self.outFile.write(theText)
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
# 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):
|
||||
self.outFile.close()
|
||||
return True
|
||||
|
||||
# END Class OutFile
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
"""
|
||||
|
||||
import textwrap
|
||||
import logging
|
||||
import re
|
||||
import nw
|
||||
@@ -41,6 +42,25 @@ class Tokenizer():
|
||||
self.theTokens = None
|
||||
self.theResult = None
|
||||
|
||||
self.wordWrap = 80
|
||||
self.doComments = False
|
||||
self.doCommands = False
|
||||
|
||||
return
|
||||
|
||||
def setComments(self, doComments):
|
||||
self.doComments = doComments
|
||||
return
|
||||
|
||||
def setCommands(self, doCommands):
|
||||
self.doCommands = doCommands
|
||||
return
|
||||
|
||||
def setWordWrap(self, wordWrap):
|
||||
if wordWrap >= 0:
|
||||
self.wordWrap = wordWrap
|
||||
else:
|
||||
self.wordWrap = 0
|
||||
return
|
||||
|
||||
def setText(self, theHandle, theText=None):
|
||||
@@ -129,4 +149,61 @@ class Tokenizer():
|
||||
|
||||
return
|
||||
|
||||
def doConvert(self):
|
||||
"""Converts the tokenized text into plain text.
|
||||
"""
|
||||
|
||||
self.theResult = ""
|
||||
thisPar = []
|
||||
for tType, tText, tFormat in self.theTokens:
|
||||
|
||||
# First check if we have a comment or plain text, as they need some
|
||||
# extra replacing before we proceed
|
||||
if tType == "comment":
|
||||
tText = "[%s]" % tText
|
||||
|
||||
elif tType == "text":
|
||||
tTemp = tText
|
||||
for xPos, xLen, xFmt in reversed(tFormat):
|
||||
tTemp = tTemp[:xPos]+tTemp[xPos+xLen:]
|
||||
tText = tTemp
|
||||
|
||||
# The text can now be word wrapped, if we have requested this
|
||||
if self.wordWrap > 0:
|
||||
tText = textwrap.fill(tText, width=self.wordWrap)
|
||||
|
||||
# Then the text can receive final formatting before we append it
|
||||
# to the results. We store text bits in a buffer and merge them only
|
||||
# when we find an empty line, indicating a new paragraph
|
||||
if tType == "empty":
|
||||
if len(thisPar) > 0:
|
||||
self.theResult += "%s\n\n" % " ".join(thisPar)
|
||||
thisPar = []
|
||||
|
||||
elif tType == "header1":
|
||||
uLine = "="*min(len(tText),self.wordWrap)
|
||||
self.theResult += "%s\n%s\n\n" % (tText,uLine)
|
||||
|
||||
elif tType == "header2":
|
||||
uLine = "~"*min(len(tText),self.wordWrap)
|
||||
self.theResult += "%s\n%s\n\n" % (tText,uLine)
|
||||
|
||||
elif tType == "header3":
|
||||
uLine = "-"*min(len(tText),self.wordWrap)
|
||||
self.theResult += "%s\n%s\n\n" % (tText,uLine)
|
||||
|
||||
elif tType == "header4":
|
||||
self.theResult += "%s\n\n" % tText
|
||||
|
||||
elif tType == "text":
|
||||
thisPar.append(tText)
|
||||
|
||||
elif tType == "comment" and self.doComments:
|
||||
self.theResult += "%s\n\n" % tText
|
||||
|
||||
elif tType == "command" and self.doCommands:
|
||||
self.theResult += "%s\n\n" % tText
|
||||
|
||||
return
|
||||
|
||||
# END Class Tokenizer
|
||||
|
||||
Reference in New Issue
Block a user