mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-09-28 07:21:31 +12:00
25adfd2437
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
400 lines
16 KiB
Python
400 lines
16 KiB
Python
#!/usr/bin/env python
|
|
# --!-- coding: utf8 --!--
|
|
import re
|
|
|
|
from PyQt5.QtGui import QTextCharFormat, QFont
|
|
from PyQt5.QtWidgets import qApp, QVBoxLayout, QCheckBox, QWidget, QHBoxLayout, QLabel, QSpinBox, QComboBox
|
|
|
|
from manuskript.exporter.manuskript.markdown import markdown, markdownSettings
|
|
from manuskript.ui.collapsibleGroupBox2 import collapsibleGroupBox2
|
|
|
|
import logging
|
|
LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
class abstractPlainText(markdown):
|
|
name = "SUBCLASSME"
|
|
description = "SUBCLASSME"
|
|
exportVarName = "SUBCLASSME"
|
|
toFormat = "SUBCLASSME"
|
|
icon = "SUBCLASSME"
|
|
exportFilter = "SUBCLASSME"
|
|
exportDefaultSuffix = ".SUBCLASSME"
|
|
|
|
def __init__(self, exporter):
|
|
self.exporter = exporter
|
|
|
|
def settingsWidget(self):
|
|
# Get pandoc major version to determine valid command line options
|
|
p = re.compile(r'pandoc (\d+)\.(\d+).*')
|
|
m = p.match(self.exporter.version())
|
|
if m:
|
|
majorVersion = m.group(1)
|
|
minorVersion = m.group(2)
|
|
else:
|
|
majorVersion = ""
|
|
minorVersion = ""
|
|
w = pandocSettings(self, majorVersion, minorVersion, toFormat=self.toFormat)
|
|
w.loadSettings()
|
|
return w
|
|
|
|
def src(self, settingsWidget):
|
|
return markdown.output(self, settingsWidget)
|
|
|
|
def output(self, settingsWidget, outputfile=None):
|
|
args = settingsWidget.runnableSettings()
|
|
src = self.src(settingsWidget)
|
|
return self.exporter.convert(src, args, outputfile)
|
|
|
|
def preview(self, settingsWidget, previewWidget):
|
|
settings = settingsWidget.getSettings()
|
|
|
|
# Save settings
|
|
settingsWidget.writeSettings()
|
|
|
|
# Prepares text edit
|
|
self.preparesTextEditViewMarkdown(previewWidget, settingsWidget.settings)
|
|
self.preparesTextEditView(previewWidget, settings["Preview"]["PreviewFont"])
|
|
|
|
r = self.output(settingsWidget)
|
|
|
|
previewWidget.setPlainText(r)
|
|
|
|
|
|
def versionAsInt(version):
|
|
if version is None:
|
|
return 0
|
|
|
|
try:
|
|
return int(version)
|
|
except ValueError:
|
|
return 0
|
|
|
|
|
|
def versionToIntArray(version):
|
|
if version is None:
|
|
return [0, 0]
|
|
|
|
p = re.compile(r'(\d+)\.(\d+).*')
|
|
m = p.match(version)
|
|
if m:
|
|
majorVersion = m.group(1)
|
|
minorVersion = m.group(2)
|
|
else:
|
|
majorVersion = ""
|
|
minorVersion = ""
|
|
|
|
return [ versionAsInt(majorVersion), versionAsInt(minorVersion) ]
|
|
|
|
|
|
class pandocSetting:
|
|
def __init__(self, arg, type, format, label, widget=None, default=None, min=None, max=None, vals=None, suffix="",
|
|
minVersion=None, maxVersion=None, specific=False, toc=False):
|
|
self.arg = arg # start with EXT for extensions
|
|
self.type = type
|
|
self.label = label
|
|
self.formats = format
|
|
if "html" in self.formats:
|
|
self.formats += " slidy dzslides revealjs slideous s5 html5"
|
|
self.widget = widget
|
|
self.default = default
|
|
self.min = min
|
|
self.max = max
|
|
self.vals = vals.split("|") if vals else []
|
|
self.suffix = suffix
|
|
self.minVersion = versionToIntArray(minVersion)
|
|
self.maxVersion = versionToIntArray(maxVersion)
|
|
self.specific = specific
|
|
self.toc = toc
|
|
|
|
def isValid(self, format):
|
|
"""Return whether the specific setting is active with the given format."""
|
|
|
|
# Empty formats means all
|
|
if self.formats == "":
|
|
return True
|
|
|
|
# "html" in "html markdown latex"
|
|
elif format in self.formats:
|
|
return True
|
|
|
|
# "markdown_strict" in "html markdown latex"
|
|
elif [f for f in self.formats.split(" ") if format in f]:
|
|
return True
|
|
|
|
return False
|
|
|
|
def isCompatible(self, majorVersion, minorVersion):
|
|
majorNumber = versionAsInt(majorVersion)
|
|
minorNumber = versionAsInt(minorVersion)
|
|
|
|
if (majorNumber < self.minVersion[0]) or ((majorNumber == self.minVersion[0]) and
|
|
(minorNumber < self.minVersion[1])):
|
|
return False
|
|
|
|
if (self.maxVersion[0] == 0) and (self.maxVersion[1] == 0):
|
|
return True
|
|
|
|
return (majorNumber < self.maxVersion[0]) or ((majorNumber == self.maxVersion[0]) and
|
|
(minorNumber <= self.maxVersion[1]))
|
|
|
|
def isSpecific(self):
|
|
return self.specific
|
|
|
|
def isTOC(self):
|
|
return self.toc
|
|
|
|
|
|
class pandocSettings(markdownSettings):
|
|
|
|
settingsList = {
|
|
# General
|
|
"standalone": pandocSetting("--standalone", "checkbox", "",
|
|
qApp.translate("Export", "Standalone document (not just a fragment)"),
|
|
default=True),
|
|
"TOC": pandocSetting("--toc", "checkbox", "",
|
|
qApp.translate("Export", "Include a table of contents."), toc=True),
|
|
|
|
"TOC-depth": pandocSetting("--toc-depth=", "number", "",
|
|
qApp.translate("Export", "Number of sections level to include in TOC: "),
|
|
default=3, min=1, max=6, toc=True, minVersion="1.10"),
|
|
# pandoc v1 only
|
|
"smart": pandocSetting("--smart", "checkbox", "",
|
|
qApp.translate("Export", "Typographically correct output"),
|
|
maxVersion="1.19.2.4"),
|
|
# pandoc v1 only
|
|
"normalize": pandocSetting("--normalize", "checkbox", "",
|
|
qApp.translate("Export", "Normalize the document (cleaner)"),
|
|
minVersion="1.8", maxVersion="1.19.2.4"),
|
|
# pandoc v1.5 to 2.7.3
|
|
"base-header": pandocSetting("--base-header-level=", "number", "",
|
|
qApp.translate("Export", "Specify the base level for headers: "),
|
|
default=1, min=1, minVersion="1.5", maxVersion="2.7.3"),
|
|
# pandoc v2.8+
|
|
"shift-heading": pandocSetting("--shift-heading-level-by=", "number", "",
|
|
qApp.translate("Export", "Specify the base level for headers: "),
|
|
default=0, min=0, minVersion="2.8"),
|
|
"disable-YAML": pandocSetting("EXT-yaml_metadata_block", "checkbox", "",
|
|
qApp.translate("Export", "Disable YAML metadata block.\nUse that if you get YAML related error."),
|
|
minVersion="1.12"),
|
|
|
|
# Specific
|
|
"ref-link": pandocSetting("--reference-links", "checkbox", "markdown rst",
|
|
qApp.translate("Export", "Use reference-style links instead of inline links"),
|
|
specific=True),
|
|
# pandoc v1.9 to v2.11.1
|
|
"atx": pandocSetting("--atx-headers", "checkbox", "markdown asciidoc",
|
|
qApp.translate("Export", "Use ATX-style headers"), specific=True,
|
|
minVersion="1.9", maxVersion="2.11.1"),
|
|
# pandoc v2.11.2+
|
|
"atx-heading": pandocSetting("--markdown-headings=atx|setext", "checkbox", "markdown asciidoc",
|
|
qApp.translate("Export", "Use ATX-style headers"), specific=True,
|
|
minVersion="2.11.2"),
|
|
"self-contained": pandocSetting("--self-contained", "checkbox", "html",
|
|
qApp.translate("Export", "Self-contained HTML files, with no dependencies"),
|
|
specific=True, minVersion="1.9"),
|
|
"q-tags": pandocSetting("--html-q-tags", "checkbox", "html",
|
|
qApp.translate("Export", "Use <q> tags for quotes in HTML"), specific=True,
|
|
minVersion="1.10"),
|
|
# pandoc v1 only
|
|
"latex-engine": pandocSetting("--latex-engine=", "combo", "pdf",
|
|
qApp.translate("Export", "LaTeX engine used to produce the PDF."),
|
|
vals="pdflatex|lualatex|xelatex", specific=True,
|
|
minVersion="1.9", maxVersion="1.19.2.4"),
|
|
# pandoc v2
|
|
"pdf-engine": pandocSetting("--pdf-engine=", "combo", "pdf",
|
|
qApp.translate("Export", "LaTeX engine used to produce the PDF."),
|
|
vals="pdflatex|lualatex|xelatex", minVersion="2.0", specific=True),
|
|
"epub3": pandocSetting("EXTepub3", "checkbox", "epub",
|
|
qApp.translate("Export", "Convert to ePUB3"), specific=True,
|
|
minVersion="1.10"),
|
|
|
|
# PDF
|
|
"latex-ps": pandocSetting("--variable=papersize:", "combo", "pdf latex", # FIXME: does not work with default template
|
|
qApp.translate("Export", "Paper size:"),
|
|
vals="letter|A4|A5", specific=True, minVersion="1.4"),
|
|
"latex-fs": pandocSetting("--variable=fontsize:", "number", "pdf latex", # FIXME: does not work with default template
|
|
qApp.translate("Export", "Font size:"),
|
|
min=8, max=88, default=12, suffix="pt", specific=True, minVersion="1.4"),
|
|
"latex-class": pandocSetting("--variable=documentclass:", "combo", "pdf latex",
|
|
qApp.translate("Export", "Class:"),
|
|
vals="article|report|book|memoir", specific=True, minVersion="1.4"),
|
|
"latex-ls": pandocSetting("--variable=linestretch:", "combo", "pdf latex",
|
|
qApp.translate("Export", "Line spacing:"),
|
|
vals="1|1.25|1.5|2", specific=True, minVersion="1.4"),
|
|
|
|
# FIXME: complete with http://pandoc.org/README.html#variables-for-latex
|
|
}
|
|
|
|
|
|
def __init__(self, _format, majorVersion="", minorVersion="", toFormat=None, parent=None):
|
|
markdownSettings.__init__(self, _format, parent)
|
|
|
|
self.format = toFormat
|
|
self.majorVersion = majorVersion
|
|
self.minorVersion = minorVersion
|
|
|
|
dropSettings = []
|
|
|
|
for key, setting in self.settingsList.items():
|
|
if not setting.isCompatible(self.majorVersion, self.minorVersion):
|
|
dropSettings.append(key)
|
|
|
|
LOGGER.info(f'Using pandoc settings: {self.majorVersion}.{self.minorVersion}, dropping: {dropSettings}')
|
|
|
|
for key in dropSettings:
|
|
self.settingsList.pop(key, None)
|
|
|
|
w = QWidget(self)
|
|
w.setLayout(QVBoxLayout())
|
|
self.grpPandocGeneral = self.collapsibleGroupBox(self.tr("General"), w)
|
|
self.grpPandocSpecific = self.collapsibleGroupBox(self.tr("Custom settings for {}").format(self.format), w)
|
|
self.grpPandocTOC = self.collapsibleGroupBox(self.tr("Table of Content"), w)
|
|
|
|
for key, setting in self.settingsList.items():
|
|
if setting.isTOC():
|
|
self.addSettingsWidget(key, self.grpPandocTOC)
|
|
elif setting.isSpecific():
|
|
self.addSettingsWidget(key, self.grpPandocSpecific)
|
|
else:
|
|
self.addSettingsWidget(key, self.grpPandocGeneral)
|
|
|
|
self.toolBox.insertItem(self.toolBox.count() - 1, w, "Pandoc")
|
|
self.toolBox.layout().setSpacing(0) # Not sure why this is needed, but hey...
|
|
|
|
self.getSettings()
|
|
|
|
def collapsibleGroupBox(self, title, parent):
|
|
g = collapsibleGroupBox2(title=title)
|
|
parent.layout().addWidget(g)
|
|
g.setLayout(QVBoxLayout())
|
|
return g
|
|
|
|
def addSettingsWidget(self, settingsName, parent):
|
|
|
|
if not settingsName in self.settingsList:
|
|
return
|
|
|
|
s = self.settingsList[settingsName]
|
|
|
|
if not s.isValid(self.format):
|
|
# That setting is not available for that export format
|
|
return
|
|
|
|
if "checkbox" in s.type:
|
|
s.widget = QCheckBox(s.label)
|
|
if s.default:
|
|
s.widget.setChecked(True)
|
|
parent.layout().addWidget(s.widget)
|
|
|
|
elif "number" in s.type:
|
|
l = QHBoxLayout()
|
|
label = QLabel(s.label, parent)
|
|
label.setWordWrap(True)
|
|
l.addWidget(label, 8)
|
|
s.widget = QSpinBox()
|
|
s.widget.setValue(s.default if s.default else 0)
|
|
if s.min:
|
|
s.widget.setMinimum(s.min)
|
|
if s.max:
|
|
s.widget.setMaximum(s.max)
|
|
if s.suffix:
|
|
s.widget.setSuffix(s.suffix)
|
|
l.addWidget(s.widget, 2)
|
|
parent.layout().addLayout(l)
|
|
|
|
elif "combo" in s.type:
|
|
l = QHBoxLayout()
|
|
label = QLabel(s.label, parent)
|
|
label.setWordWrap(True)
|
|
l.addWidget(label, 6)
|
|
s.widget = QComboBox()
|
|
s.widget.addItems(s.vals)
|
|
l.addWidget(s.widget, 2)
|
|
parent.layout().addLayout(l)
|
|
|
|
def updateFromSettings(self):
|
|
markdownSettings.updateFromSettings(self)
|
|
|
|
# s = self.settings["Preview"]
|
|
# val = s.get("MarkdownHighlighter", False)
|
|
# self.chkMarkdownHighlighter.setChecked(val)
|
|
|
|
if not "Pandoc" in self.settings:
|
|
return
|
|
|
|
for name in self.settingsList:
|
|
s = self.settingsList[name]
|
|
|
|
if s.isValid(self.format):
|
|
if s.type == "checkbox" and name in self.settings["Pandoc"]:
|
|
s.widget.setChecked(self.settings["Pandoc"][name])
|
|
elif s.type == "number" and name in self.settings["Pandoc"]:
|
|
s.widget.setValue(int(self.settings["Pandoc"][name]))
|
|
elif s.type == "combo" and name in self.settings["Pandoc"]:
|
|
s.widget.setCurrentText(self.settings["Pandoc"][name])
|
|
|
|
|
|
def getSettings(self):
|
|
self.settings = markdownSettings.getSettings(self)
|
|
|
|
P = self.settings.get("Pandoc", {})
|
|
|
|
for name in self.settingsList:
|
|
s = self.settingsList[name]
|
|
|
|
if s.isValid(self.format):
|
|
if s.type == "checkbox":
|
|
P[name] = s.widget.isChecked()
|
|
elif s.type == "number":
|
|
P[name] = str(s.widget.value())
|
|
elif s.type == "combo":
|
|
P[name] = s.widget.currentText()
|
|
|
|
self.settings["Pandoc"] = P
|
|
|
|
return self.settings
|
|
|
|
def runnableSettings(self):
|
|
|
|
# First we get extensions (where arg starts with EXT)
|
|
extensions = ""
|
|
toFormat = self.format
|
|
for name in self.settingsList:
|
|
s = self.settingsList[name]
|
|
if s.arg[:3] == "EXT" and s.isValid(self.format):
|
|
if name == "disable-YAML" and s.widget.isChecked():
|
|
extensions += "-yaml_metadata_block"
|
|
if name == "epub3" and s.widget.isChecked():
|
|
toFormat = "epub3"
|
|
|
|
r = ["--from=markdown" + extensions,
|
|
"--to={}".format(toFormat)]
|
|
|
|
# Add every command
|
|
for name in self.settingsList:
|
|
s = self.settingsList[name]
|
|
|
|
if s.arg[:3] == "EXT":
|
|
continue
|
|
|
|
if s.isValid(self.format):
|
|
rr = ""
|
|
if s.type == "checkbox":
|
|
if s.widget.isChecked():
|
|
rr = s.arg
|
|
elif s.type == "number":
|
|
rr = "{}{}".format(
|
|
s.arg,
|
|
str(s.widget.value())
|
|
)
|
|
elif s.type == "combo":
|
|
rr = "{}{}".format(
|
|
s.arg,
|
|
s.widget.currentText()
|
|
)
|
|
|
|
if rr:
|
|
r.append(rr+s.suffix)
|
|
return r
|
|
|