From 72e11601a0586c430e9f57742d834864e202532f Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Tue, 10 Jan 2023 17:59:23 +0100 Subject: [PATCH] Complete the first version of the doc builder class and add test --- novelwriter/mbuild/docbuild.py | 96 ++++++++++++++++++++++- tests/test_mbuild/test_mbuild_docbuild.py | 72 +++++++++++++++++ 2 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 tests/test_mbuild/test_mbuild_docbuild.py diff --git a/novelwriter/mbuild/docbuild.py b/novelwriter/mbuild/docbuild.py index c5d1e111..f95dce5f 100644 --- a/novelwriter/mbuild/docbuild.py +++ b/novelwriter/mbuild/docbuild.py @@ -7,7 +7,7 @@ File History: Created: 2022-12-01 [2.1b1] This file is a part of novelWriter -Copyright 2018–2022, Veronica Berglyd Olsen +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 @@ -24,11 +24,14 @@ 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 -from novelwriter.core.tomd import ToMarkdown logger = logging.getLogger(__name__) @@ -37,6 +40,7 @@ class NWBuildDocument: def __init__(self, project): + self._conf = novelwriter.CONFIG self._project = project self._build = {} self._documents = [] @@ -48,6 +52,10 @@ class NWBuildDocument: # Properties ## + @property + def error(self): + return self._error + @property def buildLength(self): return len(self._documents) @@ -142,9 +150,89 @@ class NWBuildDocument: ## def _setupBuild(self, bldObj): - pass + """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) + bldObj.initDocument() + + return def _doBuild(self, bldObj, tHandle): - pass + """Build a single document and add it to the build object. + """ + 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(tItem.itemHandle) + 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 new file mode 100644 index 00000000..ee9da08c --- /dev/null +++ b/tests/test_mbuild/test_mbuild_docbuild.py @@ -0,0 +1,72 @@ +""" +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 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, +} + + +@pytest.mark.core +def testMBuildDocBuild_OpenDocument(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) + + count = 0 + error = [] + for _, success in docBuild.buildOpenDocument(fncPath / "Lorem Ipsum.fodt", True): + count += 1 if success else 0 + if docBuild.error: + error.append(docBuild.error) + + assert count == 21 + assert error == [] + +# END Test testMBuildDocBuild_OpenDocument