Add PDF format

This commit is contained in:
Veronica Berglyd Olsen
2024-10-16 17:25:46 +02:00
parent c8d7ccadb7
commit 17c93c2172
7 changed files with 74 additions and 72 deletions
+2
View File
@@ -250,6 +250,7 @@ class nwLabels:
nwBuildFmt.NWD: QT_TRANSLATE_NOOP("Constant", "novelWriter Markup (.txt)"), nwBuildFmt.NWD: QT_TRANSLATE_NOOP("Constant", "novelWriter Markup (.txt)"),
nwBuildFmt.STD_MD: QT_TRANSLATE_NOOP("Constant", "Standard Markdown (.md)"), nwBuildFmt.STD_MD: QT_TRANSLATE_NOOP("Constant", "Standard Markdown (.md)"),
nwBuildFmt.EXT_MD: QT_TRANSLATE_NOOP("Constant", "Extended Markdown (.md)"), nwBuildFmt.EXT_MD: QT_TRANSLATE_NOOP("Constant", "Extended Markdown (.md)"),
nwBuildFmt.PDF: QT_TRANSLATE_NOOP("Constant", "Portable Document Format (.pdf)"),
nwBuildFmt.J_HTML: QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter HTML (.json)"), nwBuildFmt.J_HTML: QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter HTML (.json)"),
nwBuildFmt.J_NWD: QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter Markup (.json)"), nwBuildFmt.J_NWD: QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter Markup (.json)"),
} }
@@ -260,6 +261,7 @@ class nwLabels:
nwBuildFmt.NWD: ".txt", nwBuildFmt.NWD: ".txt",
nwBuildFmt.STD_MD: ".md", nwBuildFmt.STD_MD: ".md",
nwBuildFmt.EXT_MD: ".md", nwBuildFmt.EXT_MD: ".md",
nwBuildFmt.PDF: ".pdf",
nwBuildFmt.J_HTML: ".json", nwBuildFmt.J_HTML: ".json",
nwBuildFmt.J_NWD: ".json", nwBuildFmt.J_NWD: ".json",
} }
+20 -13
View File
@@ -41,7 +41,7 @@ from novelwriter.formats.tohtml import ToHtml
from novelwriter.formats.tokenizer import Tokenizer from novelwriter.formats.tokenizer import Tokenizer
from novelwriter.formats.tomarkdown import ToMarkdown from novelwriter.formats.tomarkdown import ToMarkdown
from novelwriter.formats.toodt import ToOdt from novelwriter.formats.toodt import ToOdt
from novelwriter.formats.toqdoc import TextDocumentTheme, ToQTextDocument from novelwriter.formats.toqdoc import ToQTextDocument
from novelwriter.formats.toraw import ToRaw from novelwriter.formats.toraw import ToRaw
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -121,24 +121,15 @@ class NWBuildDocument:
self._queue.append(item.itemHandle) self._queue.append(item.itemHandle)
return return
def iterBuildPreview(self, theme: TextDocumentTheme) -> Iterable[tuple[int, bool]]: def iterBuildPreview(self) -> Iterable[tuple[int, bool]]:
"""Build a preview QTextDocument.""" """Build a preview QTextDocument."""
makeObj = ToQTextDocument(self._project) makeObj = ToQTextDocument(self._project)
filtered = self._setupBuild(makeObj) filtered = self._setupBuild(makeObj)
makeObj.initDocument()
self._count = True
self._outline = True self._outline = True
font = QFont() yield from self._iterBuild(makeObj, filtered)
font.fromString(self._build.getStr("format.textFont"))
makeObj.initDocument(font, theme)
for i, tHandle in enumerate(self._queue):
self._error = None
if filtered.get(tHandle, (False, 0))[0]:
yield i, self._doBuild(makeObj, tHandle)
else:
yield i, False
makeObj.appendFootnotes() makeObj.appendFootnotes()
@@ -191,6 +182,19 @@ class NWBuildDocument:
if self._build.getBool("format.replaceTabs"): if self._build.getBool("format.replaceTabs"):
makeObj.replaceTabs(nSpaces=4, spaceChar=" ") makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
elif bFormat == nwBuildFmt.PDF:
makeObj = ToQTextDocument(self._project)
filtered = self._setupBuild(makeObj)
makeObj.initDocument()
yield from self._iterBuild(makeObj, filtered)
makeObj.appendFootnotes()
else:
logger.error("Unsupported document format")
return
self._error = None self._error = None
self._cache = makeObj self._cache = makeObj
@@ -308,6 +312,9 @@ class NWBuildDocument:
scale*self._build.getFloat("format.rightMargin"), scale*self._build.getFloat("format.rightMargin"),
) )
if isinstance(bldObj, ToQTextDocument):
pass
filtered = self._build.buildItemFilter( filtered = self._build.buildItemFilter(
self._project, withRoots=self._build.getBool("text.addNoteHeadings") self._project, withRoots=self._build.getBool("text.addNoteHeadings")
) )
+3 -2
View File
@@ -184,8 +184,9 @@ class nwBuildFmt(Enum):
NWD = 3 NWD = 3
STD_MD = 4 STD_MD = 4
EXT_MD = 5 EXT_MD = 5
J_HTML = 6 PDF = 6
J_NWD = 7 J_HTML = 7
J_NWD = 8
class nwStatusShape(Enum): class nwStatusShape(Enum):
+35 -19
View File
@@ -31,14 +31,15 @@ from PyQt5.QtGui import (
QColor, QFont, QFontMetricsF, QTextBlockFormat, QTextCharFormat, QColor, QFont, QFontMetricsF, QTextBlockFormat, QTextCharFormat,
QTextCursor, QTextDocument QTextCursor, QTextDocument
) )
from PyQt5.QtPrintSupport import QPrinter
from novelwriter.constants import nwHeaders, nwHeadFmt, nwKeyWords, nwLabels, nwUnicode from novelwriter.constants import nwHeaders, nwHeadFmt, nwKeyWords, nwLabels, nwUnicode
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.formats.tokenizer import T_Formats, Tokenizer from novelwriter.formats.tokenizer import T_Formats, Tokenizer
from novelwriter.types import ( from novelwriter.types import (
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight, QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight,
QtBlack, QtPageBreakAfter, QtPageBreakBefore, QtTransparent, QtPageBreakAfter, QtPageBreakBefore, QtTransparent, QtVAlignNormal,
QtVAlignNormal, QtVAlignSub, QtVAlignSuper QtVAlignSub, QtVAlignSuper
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -47,18 +48,18 @@ T_TextStyle = tuple[QTextBlockFormat, QTextCharFormat]
class TextDocumentTheme: class TextDocumentTheme:
text: QColor = QtBlack text: QColor = QColor(0, 0, 0)
highlight: QColor = QtTransparent highlight: QColor = QColor(255, 255, 166)
head: QColor = QtBlack head: QColor = QColor(66, 113, 174)
comment: QColor = QtBlack comment: QColor = QColor(100, 100, 100)
note: QColor = QtBlack note: QColor = QColor(129, 55, 9)
code: QColor = QtBlack code: QColor = QColor(66, 113, 174)
modifier: QColor = QtBlack modifier: QColor = QColor(129, 55, 9)
keyword: QColor = QtBlack keyword: QColor = QColor(245, 135, 31)
tag: QColor = QtBlack tag: QColor = QColor(66, 113, 174)
optional: QColor = QtBlack optional: QColor = QColor(66, 113, 174)
dialog: QColor = QtBlack dialog: QColor = QColor(66, 113, 174)
altdialog: QColor = QtBlack altdialog: QColor = QColor(129, 55, 9)
def newBlock(cursor: QTextCursor, bFmt: QTextBlockFormat) -> None: def newBlock(cursor: QTextCursor, bFmt: QTextBlockFormat) -> None:
@@ -91,11 +92,21 @@ class ToQTextDocument(Tokenizer):
return return
def initDocument(self, font: QFont, theme: TextDocumentTheme) -> None: ##
"""Initialise all computed values of the document.""" # Setters
self._textFont = font ##
self._theme = theme
def setTheme(self, theme: TextDocumentTheme) -> None:
"""Set the document colour theme."""
self._theme = theme
return
##
# Class Methods
##
def initDocument(self) -> None:
"""Initialise all computed values of the document."""
self._document.setUndoRedoEnabled(False) self._document.setUndoRedoEnabled(False)
self._document.blockSignals(True) self._document.blockSignals(True)
self._document.clear() self._document.clear()
@@ -276,7 +287,12 @@ class ToQTextDocument(Tokenizer):
return return
def saveDocument(self, path: Path) -> None: def saveDocument(self, path: Path) -> None:
"""Not implemented.""" """Save the document to a PDF file."""
printer = QPrinter(QPrinter.PrinterMode.PrinterResolution)
printer.setOutputFormat(QPrinter.OutputFormat.PdfFormat)
printer.setPaperSize(QPrinter.PageSize.A4)
printer.setOutputFileName(str(path))
self._document.print(printer)
return return
def appendFootnotes(self) -> None: def appendFootnotes(self) -> None:
+3 -1
View File
@@ -211,7 +211,9 @@ class GuiDocViewer(QTextBrowser):
qDoc = ToQTextDocument(SHARED.project) qDoc = ToQTextDocument(SHARED.project)
qDoc.setJustify(CONFIG.doJustify) qDoc.setJustify(CONFIG.doJustify)
qDoc.setDialogueHighlight(True) qDoc.setDialogueHighlight(True)
qDoc.initDocument(CONFIG.textFont, self._docTheme) qDoc.setFont(CONFIG.textFont)
qDoc.setTheme(self._docTheme)
qDoc.initDocument()
qDoc.setKeywords(True) qDoc.setKeywords(True)
qDoc.setComments(CONFIG.viewComments) qDoc.setComments(CONFIG.viewComments)
qDoc.setSynopsis(CONFIG.viewSynopsis) qDoc.setSynopsis(CONFIG.viewSynopsis)
+2 -16
View File
@@ -45,7 +45,7 @@ from novelwriter.core.docbuild import NWBuildDocument
from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton, NToolDialog from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton, NToolDialog
from novelwriter.extensions.progressbars import NProgressCircle from novelwriter.extensions.progressbars import NProgressCircle
from novelwriter.formats.tokenizer import HeadingFormatter from novelwriter.formats.tokenizer import HeadingFormatter
from novelwriter.formats.toqdoc import TextDocumentTheme, ToQTextDocument from novelwriter.formats.toqdoc import ToQTextDocument
from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON
from novelwriter.tools.manusbuild import GuiManuscriptBuild from novelwriter.tools.manusbuild import GuiManuscriptBuild
from novelwriter.tools.manussettings import GuiBuildSettings from novelwriter.tools.manussettings import GuiBuildSettings
@@ -330,22 +330,8 @@ class GuiManuscript(NToolDialog):
docBuild = NWBuildDocument(SHARED.project, build) docBuild = NWBuildDocument(SHARED.project, build)
docBuild.queueAll() docBuild.queueAll()
theme = TextDocumentTheme()
theme.text = QColor(0, 0, 0)
theme.highlight = QColor(255, 255, 166)
theme.head = QColor(66, 113, 174)
theme.comment = QColor(100, 100, 100)
theme.note = QColor(129, 55, 9)
theme.code = QColor(66, 113, 174)
theme.modifier = QColor(129, 55, 9)
theme.keyword = QColor(245, 135, 31)
theme.tag = QColor(66, 113, 174)
theme.optional = QColor(66, 113, 174)
theme.dialog = QColor(66, 113, 174)
theme.altdialog = QColor(129, 55, 9)
self.docPreview.beginNewBuild(len(docBuild)) self.docPreview.beginNewBuild(len(docBuild))
for step, _ in docBuild.iterBuildPreview(theme): for step, _ in docBuild.iterBuildPreview():
self.docPreview.buildStep(step + 1) self.docPreview.buildStep(step + 1)
QApplication.processEvents() QApplication.processEvents()
+9 -21
View File
@@ -22,7 +22,7 @@ from __future__ import annotations
import pytest import pytest
from PyQt5.QtGui import QColor, QTextBlock, QTextCharFormat, QTextCursor from PyQt5.QtGui import QTextBlock, QTextCharFormat, QTextCursor
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.constants import nwUnicode from novelwriter.constants import nwUnicode
@@ -34,18 +34,6 @@ from novelwriter.types import (
) )
THEME = TextDocumentTheme() THEME = TextDocumentTheme()
THEME.text = QColor(0, 0, 0)
THEME.highlight = QColor(255, 255, 166)
THEME.head = QColor(66, 113, 174)
THEME.comment = QColor(100, 100, 100)
THEME.note = QColor(129, 55, 9)
THEME.code = QColor(66, 113, 174)
THEME.modifier = QColor(129, 55, 9)
THEME.keyword = QColor(245, 135, 31)
THEME.tag = QColor(66, 113, 174)
THEME.optional = QColor(66, 113, 174)
THEME.dialog = QColor(113, 140, 0)
THEME.altdialog = QColor(234, 183, 0)
def charFmtInBlock(block: QTextBlock, pos: int) -> QTextCharFormat: def charFmtInBlock(block: QTextBlock, pos: int) -> QTextCharFormat:
@@ -60,8 +48,8 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI):
"""Test header formats in the ToQTextDocument class.""" """Test header formats in the ToQTextDocument class."""
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
qdoc.initDocument(CONFIG.textFont, THEME) qdoc.initDocument()
qdoc.saveDocument("") # Doesn't do anything for this format # qdoc.saveDocument("") # Doesn't do anything for this format
qdoc._isNovel = True qdoc._isNovel = True
qdoc._isFirst = True qdoc._isFirst = True
@@ -137,7 +125,7 @@ def testFmtToQTextDocument_SeparatorSkip(mockGUI):
"""Test separator and skip in the ToQTextDocument class.""" """Test separator and skip in the ToQTextDocument class."""
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
qdoc.initDocument(CONFIG.textFont, THEME) qdoc.initDocument()
qdoc._isNovel = True qdoc._isNovel = True
qdoc._isFirst = True qdoc._isFirst = True
@@ -203,7 +191,7 @@ def testFmtToQTextDocument_NovelMeta(mockGUI):
"""Test novel meta formats in the ToQTextDocument class.""" """Test novel meta formats in the ToQTextDocument class."""
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
qdoc.initDocument(CONFIG.textFont, THEME) qdoc.initDocument()
qdoc._isNovel = True qdoc._isNovel = True
qdoc._isFirst = True qdoc._isFirst = True
@@ -280,7 +268,7 @@ def testFmtToQTextDocument_NoteMeta(mockGUI):
"""Test note meta formats in the ToQTextDocument class.""" """Test note meta formats in the ToQTextDocument class."""
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
qdoc.initDocument(CONFIG.textFont, THEME) qdoc.initDocument()
qdoc._isNovel = False qdoc._isNovel = False
qdoc._isFirst = True qdoc._isFirst = True
@@ -338,7 +326,7 @@ def testFmtToQTextDocument_TextBlockFormats(mockGUI):
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
qdoc.setFirstLineIndent(True, 2.0, False) qdoc.setFirstLineIndent(True, 2.0, False)
qdoc.initDocument(CONFIG.textFont, THEME) qdoc.initDocument()
qdoc._isNovel = True qdoc._isNovel = True
qdoc._isFirst = True qdoc._isFirst = True
@@ -460,7 +448,7 @@ def testFmtToQTextDocument_TextCharFormats(mockGUI):
assert qdoc.document.toPlainText() == "" assert qdoc.document.toPlainText() == ""
# Init # Init
qdoc.initDocument(CONFIG.textFont, THEME) qdoc.initDocument()
qdoc._isNovel = True qdoc._isNovel = True
qdoc._isFirst = True qdoc._isFirst = True
@@ -581,7 +569,7 @@ def testFmtToQTextDocument_Footnotes(mockGUI):
"""Test footnotes in the ToQTextDocument class.""" """Test footnotes in the ToQTextDocument class."""
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
qdoc.initDocument(CONFIG.textFont, THEME) qdoc.initDocument()
qdoc._isNovel = True qdoc._isNovel = True
qdoc._isFirst = True qdoc._isFirst = True