diff --git a/novelwriter/mbuild/__init__.py b/novelwriter/mbuild/__init__.py
deleted file mode 100644
index 5a2fd13d..00000000
--- a/novelwriter/mbuild/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""
-novelWriter – Manuscript Build Init File
-"""
diff --git a/novelwriter/mbuild/docbuild.py b/novelwriter/mbuild/docbuild.py
deleted file mode 100644
index a6ad864b..00000000
--- a/novelwriter/mbuild/docbuild.py
+++ /dev/null
@@ -1,241 +0,0 @@
-"""
-novelWriter – Build Document Tool
-=================================
-A class to build one or more novelWriter files to a single document
-
-File History:
-Created: 2022-12-01 [2.1b1]
-
-This file is a part of novelWriter
-Copyright 2018–2023, 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 novelwriter
-
-from PyQt5.QtGui import QFont, QFontInfo
-
-from novelwriter.error import formatException
-from novelwriter.core.tomd import ToMarkdown
-from novelwriter.core.toodt import ToOdt
-from novelwriter.core.tohtml import ToHtml
-
-logger = logging.getLogger(__name__)
-
-
-class NWBuildDocument:
-
- def __init__(self, project):
-
- self._conf = novelwriter.CONFIG
- self._project = project
- self._build = {}
- self._documents = []
- self._error = None
-
- return
-
- ##
- # Properties
- ##
-
- @property
- def error(self):
- return self._error
-
- @property
- def buildLength(self):
- return len(self._documents)
-
- ##
- # Setters
- ##
-
- def setBuildConfig(self, config):
- """Set the build config dictionary.
- """
- self._build = config
- return
-
- def addDocument(self, tHandle):
- """Add a document to the build queue.
- """
- self._documents.append(tHandle)
- return
-
- ##
- # Methods
- ##
-
- def iterBuildOpenDocument(self, savePath, isFlat):
- """Build an Open Document file.
- """
- makeOdt = ToOdt(self._project, isFlat=isFlat)
- self._setupBuild(makeOdt)
- makeOdt.initDocument()
-
- for i, tHandle in enumerate(self._documents):
- yield i, self._doBuild(makeOdt, tHandle)
-
- makeOdt.closeDocument()
-
- self._error = None
- try:
- if isFlat:
- makeOdt.saveFlatXML(savePath)
- else:
- makeOdt.saveOpenDocText(savePath)
- except Exception as exc:
- self._error = formatException(exc)
-
- return
-
- def iterBuildHTML(self, savePath):
- """Build an HTML file.
- """
- makeHtml = ToHtml(self._project)
- self._setupBuild(makeHtml)
-
- if self._build.get("process.replaceTabs", False):
- makeHtml.replaceTabs()
-
- for i, tHandle in enumerate(self._documents):
- yield i, self._doBuild(makeHtml, tHandle)
-
- self._error = None
- try:
- makeHtml.saveHTML5(savePath)
- except Exception as exc:
- self._error = formatException(exc)
-
- return
-
- def iterBuildMarkdown(self, savePath, extendedMd):
- """Build a Markdown file.
- """
- makeMd = ToMarkdown(self._project)
- self._setupBuild(makeMd)
-
- if extendedMd:
- makeMd.setGitHubMarkdown()
- else:
- makeMd.setStandardMarkdown()
-
- if self._build.get("process.replaceTabs", False):
- makeMd.replaceTabs(nSpaces=4, spaceChar=" ")
-
- for i, tHandle in enumerate(self._documents):
- yield i, self._doBuild(makeMd, tHandle)
-
- self._error = None
- try:
- makeMd.saveMarkdown(savePath)
- except Exception as exc:
- self._error = formatException(exc)
-
- return
-
- ##
- # Internal Functions
- ##
-
- def _setupBuild(self, bldObj):
- """Configure the build object.
- """
- # Get Settings
- fmtTitle = self._build.get("format.fmtTitle", "%title%")
- fmtChapter = self._build.get("format.fmtChapter", "%title%")
- fmtUnnumbered = self._build.get("format.fmtUnnumbered", "%title%")
- fmtScene = self._build.get("format.fmtScene", "%title%")
- fmtSection = self._build.get("format.fmtSection", "%title%")
- buildLang = self._build.get("format.buildLang", "en_GB")
- hideScene = self._build.get("format.hideScene", False)
- hideSection = self._build.get("format.hideSection", False)
- textFont = self._build.get("format.textFont", self._conf.textFont)
- textSize = self._build.get("format.textSize", self._conf.textSize)
- lineHeight = self._build.get("format.lineHeight", 1.15)
- justifyText = self._build.get("format.justifyText", False)
- noStyling = self._build.get("format.noStyling", False)
- replaceUCode = self._build.get("format.replaceUCode", False)
- incSynopsis = self._build.get("filter.includeSynopsis", False)
- incComments = self._build.get("filter.includeComments", False)
- incKeywords = self._build.get("filter.includeKeywords", False)
- includeBody = self._build.get("filter.includeBody", True)
-
- # The language lookup dict is reloaded if needed
- self._project.setProjectLang(buildLang)
-
- # Get font information
- fontInfo = QFontInfo(QFont(textFont, textSize))
- textFixed = fontInfo.fixedPitch()
-
- bldObj.setTitleFormat(fmtTitle)
- bldObj.setChapterFormat(fmtChapter)
- bldObj.setUnNumberedFormat(fmtUnnumbered)
- bldObj.setSceneFormat(fmtScene, hideScene)
- bldObj.setSectionFormat(fmtSection, hideSection)
-
- bldObj.setFont(textFont, textSize, textFixed)
- bldObj.setJustify(justifyText)
- bldObj.setLineHeight(lineHeight)
-
- bldObj.setSynopsis(incSynopsis)
- bldObj.setComments(incComments)
- bldObj.setKeywords(incKeywords)
- bldObj.setBodyText(includeBody)
-
- if isinstance(bldObj, ToHtml):
- bldObj.setStyles(not noStyling)
- bldObj.setReplaceUnicode(replaceUCode)
-
- if isinstance(bldObj, ToOdt):
- bldObj.setColourHeaders(not noStyling)
- bldObj.setLanguage(buildLang)
-
- return
-
- def _doBuild(self, bldObj, tHandle):
- """Build a single document and add it to the build object.
- """
- self._error = None
- tItem = self._project.tree[tHandle]
- if tItem is None:
- self._error = f"Build: Unknown item '{tHandle}'"
- logger.error(self._error)
- return False
-
- try:
- if tItem.isRootType() and not tItem.isNovelLike():
- bldObj.addRootHeading(tItem.itemHandle)
- bldObj.doConvert()
- elif tItem.isFileType():
- bldObj.setText(tHandle)
- bldObj.doPreProcessing()
- bldObj.tokenizeText()
- bldObj.doHeaders()
- bldObj.doConvert()
- bldObj.doPostProcessing()
- else:
- logger.info(f"Build: Skipping '{tHandle}'")
-
- except Exception:
- self._error = f"Build: Failed to build '{tHandle}'"
- logger.error(self._error)
- return False
-
- return True
-
-# END Class NWBuildDocument
diff --git a/tests/test_mbuild/test_mbuild_docbuild.py b/tests/test_mbuild/test_mbuild_docbuild.py
deleted file mode 100644
index ca1dea4b..00000000
--- a/tests/test_mbuild/test_mbuild_docbuild.py
+++ /dev/null
@@ -1,282 +0,0 @@
-"""
-novelWriter – Manuscript Builder Class Tests
-============================================
-
-This file is a part of novelWriter
-Copyright 2018–2023, 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 pytest
-
-from shutil import copyfile
-
-from mock import causeException, causeOSError
-from tools import ODT_IGNORE, cmpFiles
-
-from novelwriter.core.project import NWProject
-from novelwriter.mbuild.docbuild import NWBuildDocument
-
-BUILD_CONF = {
- "format.fmtTitle": "Title: %title%",
- "format.fmtChapter": "Chapter: %title%",
- "format.fmtUnnumbered": "%title%",
- "format.fmtScene": "Scene: %title%",
- "format.fmtSection": "Section: %title%",
- "format.buildLang": "en_GB",
- "format.hideScene": False,
- "format.hideSection": False,
- "format.textFont": "Arial",
- "format.textSize": 12,
- "format.lineHeight": 1.5,
- "format.justifyText": True,
- "format.noStyling": False,
- "format.replaceUCode": False,
- "filter.includeSynopsis": True,
- "filter.includeComments": True,
- "filter.includeKeywords": True,
- "filter.includeBody": True,
- "process.replaceTabs": True,
-}
-
-
-@pytest.mark.core
-def testMBuildDocBuild_OpenDocument(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths):
- """Test builing an open document manuscript.
- """
- theProject = NWProject(mockGUI)
- theProject.openProject(prjLipsum)
-
- docBuild = NWBuildDocument(theProject)
- docBuild.setBuildConfig(BUILD_CONF)
-
- for tItem in theProject.tree:
- docBuild.addDocument(tItem.itemHandle)
-
- assert docBuild.buildLength == 21
-
- # Check FODT Build
- # ================
-
- docFile = fncPath / "Lorem Ipsum.fodt"
- tstFile = tstPaths.outDir / "mBuildDocBuild_OpenDocument_Lorem_Ipsum.fodt"
- cmpFile = tstPaths.refDir / "mBuildDocBuild_OpenDocument_Lorem_Ipsum.fodt"
-
- count = 0
- error = []
- for _, success in docBuild.iterBuildOpenDocument(docFile, True):
- count += 1 if success else 0
- if docBuild.error:
- error.append(docBuild.error)
-
- assert count == 21
- assert error == []
-
- copyfile(docFile, tstFile)
- assert cmpFiles(tstFile, cmpFile, ignoreStart=ODT_IGNORE)
-
- # Check ODT Build
- # ===============
-
- docFile = fncPath / "Lorem Ipsum.odt"
-
- count = 0
- error = []
- for _, success in docBuild.iterBuildOpenDocument(docFile, False):
- count += 1 if success else 0
- if docBuild.error:
- error.append(docBuild.error)
-
- assert count == 21
- assert error == []
-
- assert docFile.is_file()
-
- # Check Error Handling
- # ====================
-
- with monkeypatch.context() as mp:
- mp.setattr("builtins.open", causeOSError)
-
- docFile = fncPath / "Lorem Ipsum Err.fodt"
- for _ in docBuild.iterBuildOpenDocument(docFile, True):
- pass
-
- assert docBuild.error == "OSError: Mock OSError"
- assert not docFile.is_file()
-
- # Check Build Issues
- # ==================
-
- with monkeypatch.context() as mp:
- mp.setattr("novelwriter.core.toodt.ToOdt.doConvert", causeException)
-
- docBuild.addDocument("0000000000000")
- assert docBuild.buildLength == 22
-
- count = 0
- error = []
- docFile = fncPath / "Lorem Ipsum Err.fodt"
- for _, success in docBuild.iterBuildOpenDocument(docFile, True):
- count += 1 if success else 0
- if docBuild.error:
- error.append(docBuild.error)
-
- assert count == 3
- assert error == [
- "Build: Failed to build '7a992350f3eb6'",
- "Build: Failed to build '8c58a65414c23'",
- "Build: Failed to build '88d59a277361b'",
- "Build: Failed to build 'db7e733775d4d'",
- "Build: Failed to build 'fb609cd8319dc'",
- "Build: Failed to build '88243afbe5ed8'",
- "Build: Failed to build 'f96ec11c6a3da'",
- "Build: Failed to build '846352075de7d'",
- "Build: Failed to build '441420a886d82'",
- "Build: Failed to build 'eb103bc70c90c'",
- "Build: Failed to build 'f8c0562e50f1b'",
- "Build: Failed to build '47666c91c7ccf'",
- "Build: Failed to build '67a8707f2f249'",
- "Build: Failed to build '4c4f28287af27'",
- "Build: Failed to build '6c6afb1247750'",
- "Build: Failed to build '2426c6f0ca922'",
- "Build: Failed to build '60bdf227455cc'",
- "Build: Failed to build '04468803b92e1'",
- "Build: Unknown item '0000000000000'",
- ]
-
-# END Test testMBuildDocBuild_OpenDocument
-
-
-@pytest.mark.core
-def testMBuildDocBuild_HTML(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths):
- """Test builing an HTML manuscript.
- """
- theProject = NWProject(mockGUI)
- theProject.openProject(prjLipsum)
-
- docBuild = NWBuildDocument(theProject)
- docBuild.setBuildConfig(BUILD_CONF)
-
- for tItem in theProject.tree:
- docBuild.addDocument(tItem.itemHandle)
-
- assert docBuild.buildLength == 21
-
- # Check HTML5 Build
- # =================
-
- docFile = fncPath / "Lorem Ipsum.htm"
- tstFile = tstPaths.outDir / "mBuildDocBuild_HTML5_Lorem_Ipsum.htm"
- cmpFile = tstPaths.refDir / "mBuildDocBuild_HTML5_Lorem_Ipsum.htm"
-
- count = 0
- error = []
- for _, success in docBuild.iterBuildHTML(docFile):
- count += 1 if success else 0
- if docBuild.error:
- error.append(docBuild.error)
-
- assert count == 21
- assert error == []
-
- copyfile(docFile, tstFile)
- assert cmpFiles(tstFile, cmpFile)
-
- # Check Error Handling
- # ====================
-
- with monkeypatch.context() as mp:
- mp.setattr("builtins.open", causeOSError)
-
- docFile = fncPath / "Lorem Ipsum Err.htm"
- for _ in docBuild.iterBuildHTML(docFile):
- pass
-
- assert docBuild.error == "OSError: Mock OSError"
- assert not docFile.is_file()
-
-# END Test testMBuildDocBuild_HTML
-
-
-@pytest.mark.core
-def testMBuildDocBuild_Markdown(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths):
- """Test builing an Markdown manuscript.
- """
- theProject = NWProject(mockGUI)
- theProject.openProject(prjLipsum)
-
- docBuild = NWBuildDocument(theProject)
- docBuild.setBuildConfig(BUILD_CONF)
-
- for tItem in theProject.tree:
- docBuild.addDocument(tItem.itemHandle)
-
- assert docBuild.buildLength == 21
-
- # Check Standard Markdown Build
- # =============================
-
- docFile = fncPath / "Lorem Ipsum Standard.md"
- tstFile = tstPaths.outDir / "mBuildDocBuild_Standard_Markdown_Lorem_Ipsum.md"
- cmpFile = tstPaths.refDir / "mBuildDocBuild_Standard_Markdown_Lorem_Ipsum.md"
-
- count = 0
- error = []
- for _, success in docBuild.iterBuildMarkdown(docFile, False):
- count += 1 if success else 0
- if docBuild.error:
- error.append(docBuild.error)
-
- assert count == 21
- assert error == []
-
- copyfile(docFile, tstFile)
- assert cmpFiles(tstFile, cmpFile)
-
- # Check Extended Markdown Build
- # =============================
-
- docFile = fncPath / "Lorem Ipsum Standard.md"
- tstFile = tstPaths.outDir / "mBuildDocBuild_Extended_Markdown_Lorem_Ipsum.md"
- cmpFile = tstPaths.refDir / "mBuildDocBuild_Extended_Markdown_Lorem_Ipsum.md"
-
- count = 0
- error = []
- for _, success in docBuild.iterBuildMarkdown(docFile, True):
- count += 1 if success else 0
- if docBuild.error:
- error.append(docBuild.error)
-
- assert count == 21
- assert error == []
-
- copyfile(docFile, tstFile)
- assert cmpFiles(tstFile, cmpFile)
-
- # Check Error Handling
- # ====================
-
- with monkeypatch.context() as mp:
- mp.setattr("builtins.open", causeOSError)
-
- docFile = fncPath / "Lorem Ipsum Err.md"
- for _ in docBuild.iterBuildMarkdown(docFile, False):
- pass
-
- assert docBuild.error == "OSError: Mock OSError"
- assert not docFile.is_file()
-
-# END Test testMBuildDocBuild_Markdown