Documentation and ODT exporter updates (#859)
* Clean up a few things in the ODT exporter * Remove local HTML docs and instead add PDF docs * Update documentation
This commit is contained in:
committed by
GitHub
parent
eee0f5e4c9
commit
95c79e1c23
@@ -18,6 +18,8 @@ i18n/*.qph
|
|||||||
# Documentation
|
# Documentation
|
||||||
/docs/build/
|
/docs/build/
|
||||||
/nw/assets/help/html/
|
/nw/assets/help/html/
|
||||||
|
/nw/assets/help/manual.pdf
|
||||||
|
/UserManual.pdf
|
||||||
*.qch
|
*.qch
|
||||||
*.qhc
|
*.qhc
|
||||||
|
|
||||||
|
|||||||
+20
-41
@@ -4,20 +4,15 @@
|
|||||||
# This file does only contain a selection of the most common options. For a
|
# This file does only contain a selection of the most common options. For a
|
||||||
# full list see the documentation:
|
# full list see the documentation:
|
||||||
# http://www.sphinx-doc.org/en/master/config
|
# http://www.sphinx-doc.org/en/master/config
|
||||||
|
|
||||||
# -- Path setup --------------------------------------------------------------
|
|
||||||
|
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
|
||||||
#
|
#
|
||||||
# import sys
|
|
||||||
# sys.path.insert(0, os.path.abspath("."))
|
# -- Imports -----------------------------------------------------------------
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import sphinx_rtd_theme # noqa: F401
|
import sphinx_rtd_theme # noqa: F401
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project Information -----------------------------------------------------
|
||||||
|
|
||||||
project = "novelWriter"
|
project = "novelWriter"
|
||||||
copyright = "2018–2021, Veronica Berglyd Olsen"
|
copyright = "2018–2021, Veronica Berglyd Olsen"
|
||||||
@@ -28,7 +23,7 @@ version = "1.5"
|
|||||||
# The full version, including alpha/beta/rc tags
|
# The full version, including alpha/beta/rc tags
|
||||||
release = "1.5-alpha0"
|
release = "1.5-alpha0"
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General Configuration ---------------------------------------------------
|
||||||
|
|
||||||
os.environ["TZ"] = "Europe/Oslo"
|
os.environ["TZ"] = "Europe/Oslo"
|
||||||
time.tzset()
|
time.tzset()
|
||||||
@@ -45,7 +40,7 @@ language = None
|
|||||||
exclude_patterns = []
|
exclude_patterns = []
|
||||||
pygments_style = None
|
pygments_style = None
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------------
|
# -- Options for HTML Output -------------------------------------------------
|
||||||
|
|
||||||
html_theme = "sphinx_rtd_theme"
|
html_theme = "sphinx_rtd_theme"
|
||||||
html_logo = "images/novelwriter.png"
|
html_logo = "images/novelwriter.png"
|
||||||
@@ -64,48 +59,32 @@ html_css_files = [
|
|||||||
"css/custom.css",
|
"css/custom.css",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Custom sidebar templates, must be a dictionary that maps document names
|
# -- Options for HTMLHelp Output ---------------------------------------------
|
||||||
# to template names.
|
|
||||||
# The default sidebars (for documents that don"t match any pattern) are
|
|
||||||
# defined by theme itself. Builtin themes are using these templates by
|
|
||||||
# default: ``["localtoc.html", "relations.html", "sourcelink.html",
|
|
||||||
# "searchbox.html"]``.
|
|
||||||
# html_sidebars = {}
|
|
||||||
|
|
||||||
# -- Options for HTMLHelp output ---------------------------------------------
|
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = "novelWriterDoc"
|
htmlhelp_basename = "novelWriterDoc"
|
||||||
|
|
||||||
# -- Options for LaTeX output ------------------------------------------------
|
# -- Options for LaTeX Output ------------------------------------------------
|
||||||
|
|
||||||
latex_elements = {
|
latex_elements = {
|
||||||
# The paper size ("letterpaper" or "a4paper").
|
"papersize": "a4paper",
|
||||||
#
|
"pointsize": "11pt",
|
||||||
# "papersize": "letterpaper",
|
"preamble": (
|
||||||
|
"\\usepackage[utf8]{inputenc}\n"
|
||||||
# The font size ("10pt", "11pt" or "12pt").
|
"\\DeclareUnicodeCharacter{2212}{\\textendash}\n"
|
||||||
#
|
),
|
||||||
# "pointsize": "10pt",
|
"figure_align": "htbp",
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
|
||||||
#
|
|
||||||
# "preamble": "",
|
|
||||||
|
|
||||||
# Latex figure (float) alignment
|
|
||||||
#
|
|
||||||
# "figure_align": "htbp",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
# (source start file, target name, title,
|
# (source start file, target name, title,
|
||||||
# author, documentclass [howto, manual, or own class]).
|
# author, documentclass [howto, manual, or own class]).
|
||||||
latex_documents = [(
|
latex_documents = [(
|
||||||
master_doc, "novelWriter.tex", "novelWriter Documentation",
|
master_doc, "manual.tex", "novelWriter Documentation",
|
||||||
author, "manual"
|
author, "manual"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
# -- Options for manual page output ------------------------------------------
|
# -- Options for Man Page Output ---------------------------------------------
|
||||||
|
|
||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
@@ -113,7 +92,7 @@ man_pages = [(
|
|||||||
master_doc, "novelwriter", "novelWriter Documentation", [author], 1
|
master_doc, "novelwriter", "novelWriter Documentation", [author], 1
|
||||||
)]
|
)]
|
||||||
|
|
||||||
# -- Options for Texinfo output ----------------------------------------------
|
# -- Options for Texinfo Output ----------------------------------------------
|
||||||
|
|
||||||
# Grouping the document tree into Texinfo files. List of tuples
|
# Grouping the document tree into Texinfo files. List of tuples
|
||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
@@ -123,7 +102,7 @@ texinfo_documents = [(
|
|||||||
"novelWriter", "Markdown-like editor for novels.", "Miscellaneous"
|
"novelWriter", "Markdown-like editor for novels.", "Miscellaneous"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
# -- Options for Epub output -------------------------------------------------
|
# -- Options for EPub Output -------------------------------------------------
|
||||||
|
|
||||||
# Bibliographic Dublin Core info.
|
# Bibliographic Dublin Core info.
|
||||||
epub_title = project
|
epub_title = project
|
||||||
|
|||||||
+21
-18
@@ -2,29 +2,31 @@
|
|||||||
novelWriter |release|
|
novelWriter |release|
|
||||||
#####################
|
#####################
|
||||||
|
|
||||||
.. image:: https://github.com/vkbo/novelWriter/workflows/python%203.8/badge.svg?branch=main
|
.. only:: html
|
||||||
:target: https://github.com/vkbo/novelWriter/actions
|
|
||||||
:alt: Python Tests
|
|
||||||
|
|
||||||
.. image:: https://codecov.io/gh/vkbo/novelWriter/branch/main/graph/badge.svg
|
.. image:: https://github.com/vkbo/novelWriter/workflows/python%203.8/badge.svg?branch=main
|
||||||
:target: https://codecov.io/gh/vkbo/novelWriter
|
:target: https://github.com/vkbo/novelWriter/actions
|
||||||
:alt: Code Coverage
|
:alt: Python Tests
|
||||||
|
|
||||||
.. image:: https://readthedocs.org/projects/novelwriter/badge/?version=latest
|
.. image:: https://codecov.io/gh/vkbo/novelWriter/branch/main/graph/badge.svg
|
||||||
:target: https://novelwriter.readthedocs.io/en/latest/?badge=latest
|
:target: https://codecov.io/gh/vkbo/novelWriter
|
||||||
:alt: Documentation
|
:alt: Code Coverage
|
||||||
|
|
||||||
.. image:: https://img.shields.io/github/v/release/vkbo/novelwriter
|
.. image:: https://readthedocs.org/projects/novelwriter/badge/?version=latest
|
||||||
:target: https://github.com/vkbo/novelWriter/releases
|
:target: https://novelwriter.readthedocs.io/en/latest/?badge=latest
|
||||||
:alt: GitHub Release
|
:alt: Documentation
|
||||||
|
|
||||||
.. image:: https://img.shields.io/pypi/v/novelwriter
|
.. image:: https://img.shields.io/github/v/release/vkbo/novelwriter
|
||||||
:target: https://pypi.org/project/novelWriter/
|
:target: https://github.com/vkbo/novelWriter/releases
|
||||||
:alt: PyPI
|
:alt: GitHub Release
|
||||||
|
|
||||||
.. image:: https://img.shields.io/pypi/pyversions/novelwriter
|
.. image:: https://img.shields.io/pypi/v/novelwriter
|
||||||
:target: https://pypi.org/project/novelWriter/
|
:target: https://pypi.org/project/novelWriter/
|
||||||
:alt: Python Version
|
:alt: PyPI
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/pyversions/novelwriter
|
||||||
|
:target: https://pypi.org/project/novelWriter/
|
||||||
|
:alt: Python Version
|
||||||
|
|
||||||
**Last Updated:** |today|
|
**Last Updated:** |today|
|
||||||
|
|
||||||
@@ -101,6 +103,7 @@ for your operating system for further details.
|
|||||||
:caption: Under the Hood
|
:caption: Under the Hood
|
||||||
|
|
||||||
tech_storage
|
tech_storage
|
||||||
|
tech_tests
|
||||||
|
|
||||||
|
|
||||||
Indices and Tables
|
Indices and Tables
|
||||||
|
|||||||
@@ -157,13 +157,15 @@ The documentation can then be built from the ``docs`` folder in the source code
|
|||||||
If successful, the documentation should be available in the ``docs/build/html`` folder and you can
|
If successful, the documentation should be available in the ``docs/build/html`` folder and you can
|
||||||
open the ``index.html`` file in your browser.
|
open the ``index.html`` file in your browser.
|
||||||
|
|
||||||
You can also build the documentation by using the setup script:
|
You can also build the PDF documentation locally using the setup script:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
python setup.py docs
|
python setup.py pdfdocs
|
||||||
|
|
||||||
This does the same as the ``make help`` command, but in addition it copies the documentation into
|
This will build the documentation as a PDF using LaTeX. The file will then be copied into the
|
||||||
novelWriter's assets folder. This will make it possible to open the documentation from inside of
|
assets folder and made available in the :guilabel:`Help` menu in novelWriter. The Sphinx build
|
||||||
novelWriter by pressing :kbd:`F1` even without an internet connection, as it will instead open the
|
system has a few extra dependencies when building the PDF. Please check the `Sphinx Docs`_ if you
|
||||||
local copy.
|
have any issues.
|
||||||
|
|
||||||
|
.. _Sphinx Docs: https://www.sphinx-doc.org/
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ your own backup solution.
|
|||||||
extensions `.json` as JSON files are used to cache the index and various run-time settings and
|
extensions `.json` as JSON files are used to cache the index and various run-time settings and
|
||||||
are generally large files that change often. You'd also want to exclude the ``cache`` folder.
|
are generally large files that change often. You'd also want to exclude the ``cache`` folder.
|
||||||
|
|
||||||
The project XML file is indent-formatted, suitable for diff tools and version control since most of
|
The project XML file is indent-formatted, and is suitable for diff tools and version control since
|
||||||
the file will stay static, although a timesetamp is set in the meta section on line 2, and various
|
most of the file will stay static, although a timesetamp is set in the meta section on line 2, and
|
||||||
meta data entries incremented, on each save.
|
various meta data entries incremented, on each save.
|
||||||
|
|
||||||
|
|
||||||
Project Documents
|
Project Documents
|
||||||
@@ -45,7 +45,7 @@ Project Documents
|
|||||||
|
|
||||||
All the project documents are saved in a folder in the main project folder named ``content``. Each
|
All the project documents are saved in a folder in the main project folder named ``content``. Each
|
||||||
document has a file handle taken from the first 13 characters of a SHA256 hash of the system time
|
document has a file handle taken from the first 13 characters of a SHA256 hash of the system time
|
||||||
plus an incremented number when the document was first created. The documents are saved with a
|
when the document was first created, plus an incremented number. The documents are saved with a
|
||||||
filename assembled from this hash and the file extension ``.nwd``.
|
filename assembled from this hash and the file extension ``.nwd``.
|
||||||
|
|
||||||
If you wish to find the file system location of a document in the project, you can either look it
|
If you wish to find the file system location of a document in the project, you can either look it
|
||||||
@@ -60,7 +60,7 @@ what the user names the document within the project, or changes it to.
|
|||||||
|
|
||||||
Each document file contains a plain text version of the text from the editor. The file can in
|
Each document file contains a plain text version of the text from the editor. The file can in
|
||||||
principle be edited in any text editor, and is suitable for diffing and version control if so
|
principle be edited in any text editor, and is suitable for diffing and version control if so
|
||||||
desired. Just make sure the file remains in utf-8 encoding, otherwise unicode chatracters may
|
desired. Just make sure the file remains in utf-8 encoding, otherwise unicode characters may
|
||||||
become mangled when the file is opened in novelWriter again.
|
become mangled when the file is opened in novelWriter again.
|
||||||
|
|
||||||
Editing these files is generally not recommended outside of special circumstances, whatever they
|
Editing these files is generally not recommended outside of special circumstances, whatever they
|
||||||
@@ -115,7 +115,7 @@ project, and the word counts within each header section.
|
|||||||
|
|
||||||
The integrity of the index is checked when the file is loaded. It is possible to corrupt the index
|
The integrity of the index is checked when the file is loaded. It is possible to corrupt the index
|
||||||
if the file is manually edited and manipulated, so the check is important to avoid sudden crashes
|
if the file is manually edited and manipulated, so the check is important to avoid sudden crashes
|
||||||
of novelWriter. if the file contains errors, novelWriter will automatically build it anew. If the
|
of novelWriter. If the file contains errors, novelWriter will automatically build it anew. If the
|
||||||
check somehow fails and novelWriter keeps crashing, you can delete the file manually and rebuild
|
check somehow fails and novelWriter keeps crashing, you can delete the file manually and rebuild
|
||||||
the index. If this too fails, you have likely encountered a bug.
|
the index. If this too fails, you have likely encountered a bug.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
.. _a_pytest:
|
||||||
|
|
||||||
|
*************
|
||||||
|
Running Tests
|
||||||
|
*************
|
||||||
|
|
||||||
|
The novelWriter source code is well covered by tests. The test framework used for the development
|
||||||
|
is ``pytest`` with the use of an extension for Qt.
|
||||||
|
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
============
|
||||||
|
|
||||||
|
The dependencies for running the tests can be installed with:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
pip install -r requirements-dev.txt
|
||||||
|
|
||||||
|
This will install a couple of extra packages for coverage and test management. The minimum
|
||||||
|
requirement is just ``pytest`` and ``pytest-qt``.
|
||||||
|
|
||||||
|
|
||||||
|
Simple Test Run
|
||||||
|
===============
|
||||||
|
|
||||||
|
To run the tests, you simply need to execute the following from the root of the source folder:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
pytest
|
||||||
|
|
||||||
|
Since several of the tests involve opening up the novelWriter GUI, you may want to disable the GUI
|
||||||
|
for the duration of the test run. Moving your mouse while the tests are running may otherwise
|
||||||
|
interfere with the execution of some tests.
|
||||||
|
|
||||||
|
You can disable the renderring of the GUI by setting the flag ``export QT_QPA_PLATFORM=offscreen``,
|
||||||
|
or alternatively run the tests with the ``xvfb`` package, like so:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
xvfb-run pytest
|
||||||
|
|
||||||
|
|
||||||
|
Advanced Options
|
||||||
|
================
|
||||||
|
|
||||||
|
Adding the flag ``-v`` to the ``pytest`` command will increase verbosity of the test execution.
|
||||||
|
|
||||||
|
You can also add coverage report generation. For instance to HTML:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
xvfb-run pytest -v --cov=nw --cov-report=html
|
||||||
|
|
||||||
|
Other useful report formats are ``xml``, and ``term`` for terminal output.
|
||||||
|
|
||||||
|
You can also run tests per subpackage of novelWriter with the ``-m`` command. The available
|
||||||
|
subpackage groups are ``base``, ``core``, and ``gui``. Consider for instance:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
xvfb-run pytest -v --cov=nw --cov-report=html -m core
|
||||||
|
|
||||||
|
This will only run the tests of the "core" package, that is, all the classes that deal with the
|
||||||
|
project data of a novelWriter project. The "gui" tests, likewise, will run the tests for the GUI
|
||||||
|
components, and the "base" tests cover the bits in-between.
|
||||||
|
|
||||||
|
You can also filter the tests with the ``-k`` switch. The following will do the same as
|
||||||
|
``-m core``:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
xvfb-run pytest -v --cov=nw --cov-report=html -k testCore
|
||||||
|
|
||||||
|
All tests are named in such a way that you can filter them by adding more bits of the test names.
|
||||||
|
They all start with the word "test". Then comes the group: "Core", "Base", "Dlg", "Tool", or "Gui".
|
||||||
|
Finally comes the name of the class or module, which generally corresponds to a single source code
|
||||||
|
file. For instance, running the following will run all tests for the document editor:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
xvfb-run pytest -v --cov=nw --cov-report=html -k testGuiEditor
|
||||||
|
|
||||||
|
To run a single test, simply add the full test name to the ``-k`` switch.
|
||||||
@@ -80,7 +80,7 @@ The main shorcuts are as follows:
|
|||||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`W`", "Close the current project."
|
":kbd:`Ctrl`:kbd:`Shift`:kbd:`W`", "Close the current project."
|
||||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Z`", "Undo move of project tree item."
|
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Z`", "Undo move of project tree item."
|
||||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Del`", "If in the project tree, move a document to trash, or delete a folder."
|
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Del`", "If in the project tree, move a document to trash, or delete a folder."
|
||||||
":kbd:`F1`", "Open the documentation. This will either open the Qt Assistant, if available, or send you to the documentation website."
|
":kbd:`F1`", "Open the online user manual."
|
||||||
":kbd:`F2`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`Ctrl`:kbd:`E`)"
|
":kbd:`F2`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`Ctrl`:kbd:`E`)"
|
||||||
":kbd:`F3`", "Find next occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`G`)"
|
":kbd:`F3`", "Find next occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`G`)"
|
||||||
":kbd:`F5`", "Open the :guilabel:`Build Novel Project` dialog."
|
":kbd:`F5`", "Open the :guilabel:`Build Novel Project` dialog."
|
||||||
@@ -90,7 +90,7 @@ The main shorcuts are as follows:
|
|||||||
":kbd:`F9`", "Re-build the project index."
|
":kbd:`F9`", "Re-build the project index."
|
||||||
":kbd:`F10`", "Re-build the project outline."
|
":kbd:`F10`", "Re-build the project outline."
|
||||||
":kbd:`F11`", "Activate full screen mode."
|
":kbd:`F11`", "Activate full screen mode."
|
||||||
":kbd:`Shift`:kbd:`F1`", "Open the online documentation in the system default browser."
|
":kbd:`Shift`:kbd:`F1`", "Open the local user manual (PDF) if it is available."
|
||||||
":kbd:`Shift`:kbd:`F3`", "Find previous occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`Shift`:kbd:`G`.)"
|
":kbd:`Shift`:kbd:`F3`", "Find previous occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`Shift`:kbd:`G`.)"
|
||||||
":kbd:`Shift`:kbd:`F6`", "Open the :guilabel:`Project Details` dialog."
|
":kbd:`Shift`:kbd:`F6`", "Open the :guilabel:`Project Details` dialog."
|
||||||
":kbd:`Return`", "If in the project tree, open a document for editing."
|
":kbd:`Return`", "If in the project tree, open a document for editing."
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class Config:
|
|||||||
self.themeRoot = None # The full path to the nw/assets/themes folder
|
self.themeRoot = None # The full path to the nw/assets/themes folder
|
||||||
self.dictPath = None # The full path to the nw/assets/dict folder
|
self.dictPath = None # The full path to the nw/assets/dict folder
|
||||||
self.iconPath = None # The full path to the nw/assets/icons folder
|
self.iconPath = None # The full path to the nw/assets/icons folder
|
||||||
|
self.pdfDocs = None # The location of the PDF manual, if it exists
|
||||||
|
|
||||||
# Runtime Settings and Variables
|
# Runtime Settings and Variables
|
||||||
self.confChanged = False # True whenever the config has chenged, false after save
|
self.confChanged = False # True whenever the config has chenged, false after save
|
||||||
@@ -356,6 +357,16 @@ class Config:
|
|||||||
if self.spellLanguage is None:
|
if self.spellLanguage is None:
|
||||||
self.spellLanguage = "en"
|
self.spellLanguage = "en"
|
||||||
|
|
||||||
|
# Look for a PDF version of the manual
|
||||||
|
lookIn = [
|
||||||
|
os.path.join(self.assetPath, "help", "manual.pdf"),
|
||||||
|
os.path.join(self.appRoot, "UserManual.pdf"),
|
||||||
|
]
|
||||||
|
for pdfDocs in lookIn:
|
||||||
|
if os.path.isfile(pdfDocs):
|
||||||
|
self.pdfDocs = pdfDocs
|
||||||
|
break
|
||||||
|
|
||||||
logger.debug("Config initialisation complete")
|
logger.debug("Config initialisation complete")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
+22
-14
@@ -107,7 +107,7 @@ class ToOdt(Tokenizer):
|
|||||||
self.headerText = ""
|
self.headerText = ""
|
||||||
|
|
||||||
# Internal
|
# Internal
|
||||||
self._fontFamily = "'Liberation Sans'"
|
self._fontFamily = "'Liberation Serif'"
|
||||||
self._fontPitch = "variable"
|
self._fontPitch = "variable"
|
||||||
self._fSizeTitle = "30pt"
|
self._fSizeTitle = "30pt"
|
||||||
self._fSizeHead1 = "24pt"
|
self._fSizeHead1 = "24pt"
|
||||||
@@ -239,6 +239,9 @@ class ToOdt(Tokenizer):
|
|||||||
self._blockIndent = self._emToCm(self.blockIndent)
|
self._blockIndent = self._emToCm(self.blockIndent)
|
||||||
self._textAlign = "justify" if self.doJustify else "left"
|
self._textAlign = "justify" if self.doJustify else "left"
|
||||||
|
|
||||||
|
# Clear Errors
|
||||||
|
self._errData = []
|
||||||
|
|
||||||
# Document Header
|
# Document Header
|
||||||
# ===============
|
# ===============
|
||||||
|
|
||||||
@@ -546,8 +549,8 @@ class ToOdt(Tokenizer):
|
|||||||
rFmt += "_B%s b_ " % (" "*len(tText))
|
rFmt += "_B%s b_ " % (" "*len(tText))
|
||||||
if len(theBits) > 1:
|
if len(theBits) > 1:
|
||||||
if theBits[0] == nwKeyWords.TAG_KEY:
|
if theBits[0] == nwKeyWords.TAG_KEY:
|
||||||
rTxt += "%s" % theBits[1]
|
rTxt += theBits[1]
|
||||||
rFmt += "%s" % (" "*len(theBits[1]))
|
rFmt += " "*len(theBits[1])
|
||||||
else:
|
else:
|
||||||
tTags = ", ".join(theBits[1:])
|
tTags = ", ".join(theBits[1:])
|
||||||
rTxt += tTags
|
rTxt += tTags
|
||||||
@@ -585,12 +588,15 @@ class ToOdt(Tokenizer):
|
|||||||
tTemp = ""
|
tTemp = ""
|
||||||
xFmt = 0x00
|
xFmt = 0x00
|
||||||
pFmt = 0x00
|
pFmt = 0x00
|
||||||
|
pErr = 0
|
||||||
|
|
||||||
parProc = XMLParagraph(xElem)
|
parProc = XMLParagraph(xElem)
|
||||||
|
|
||||||
for i, c in enumerate(theText):
|
for i, c in enumerate(theText):
|
||||||
|
|
||||||
if theFmt[i] == "_":
|
if theFmt[i] == " ":
|
||||||
|
tTemp += c
|
||||||
|
elif theFmt[i] == "_":
|
||||||
continue
|
continue
|
||||||
elif theFmt[i] == "B":
|
elif theFmt[i] == "B":
|
||||||
xFmt |= X_BLD
|
xFmt |= X_BLD
|
||||||
@@ -604,9 +610,8 @@ class ToOdt(Tokenizer):
|
|||||||
xFmt |= X_DEL
|
xFmt |= X_DEL
|
||||||
elif theFmt[i] == "s":
|
elif theFmt[i] == "s":
|
||||||
xFmt &= M_DEL
|
xFmt &= M_DEL
|
||||||
|
else:
|
||||||
if theFmt[i] == " ":
|
pErr += 1
|
||||||
tTemp += c
|
|
||||||
|
|
||||||
if xFmt != pFmt:
|
if xFmt != pFmt:
|
||||||
if pFmt == 0x00:
|
if pFmt == 0x00:
|
||||||
@@ -624,6 +629,9 @@ class ToOdt(Tokenizer):
|
|||||||
else:
|
else:
|
||||||
parProc.appendSpan(tTemp, self._textStyle(pFmt))
|
parProc.appendSpan(tTemp, self._textStyle(pFmt))
|
||||||
|
|
||||||
|
if pErr > 0:
|
||||||
|
self._errData.append("Unknown format tag encountered")
|
||||||
|
|
||||||
nErr, errMsg = parProc.checkError()
|
nErr, errMsg = parProc.checkError()
|
||||||
if nErr > 0: # pragma: no cover
|
if nErr > 0: # pragma: no cover
|
||||||
# This one should only capture bugs
|
# This one should only capture bugs
|
||||||
@@ -652,23 +660,23 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
return newName
|
return newName
|
||||||
|
|
||||||
def _textStyle(self, styleCode):
|
def _textStyle(self, tFmt):
|
||||||
"""Return a text style for a given style code.
|
"""Return a text style for a given style code.
|
||||||
"""
|
"""
|
||||||
if styleCode in self._autoText:
|
if tFmt in self._autoText:
|
||||||
return self._autoText[styleCode][0]
|
return self._autoText[tFmt][0]
|
||||||
|
|
||||||
newName = "T%d" % (len(self._autoText) + 1)
|
newName = "T%d" % (len(self._autoText) + 1)
|
||||||
newStyle = ODTTextStyle()
|
newStyle = ODTTextStyle()
|
||||||
if styleCode & X_BLD:
|
if tFmt & X_BLD:
|
||||||
newStyle.setFontWeight("bold")
|
newStyle.setFontWeight("bold")
|
||||||
if styleCode & X_ITA:
|
if tFmt & X_ITA:
|
||||||
newStyle.setFontStyle("italic")
|
newStyle.setFontStyle("italic")
|
||||||
if styleCode & X_DEL:
|
if tFmt & X_DEL:
|
||||||
newStyle.setStrikeStyle("solid")
|
newStyle.setStrikeStyle("solid")
|
||||||
newStyle.setStrikeType("single")
|
newStyle.setStrikeType("single")
|
||||||
|
|
||||||
self._autoText[styleCode] = (newName, newStyle)
|
self._autoText[tFmt] = (newName, newStyle)
|
||||||
|
|
||||||
return newName
|
return newName
|
||||||
|
|
||||||
|
|||||||
+17
-29
@@ -23,11 +23,9 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import nw
|
import nw
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from http.client import HTTPSConnection
|
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
from urllib.request import pathname2url
|
from urllib.request import pathname2url
|
||||||
|
|
||||||
@@ -137,31 +135,13 @@ class GuiMainMenu(QMenuBar):
|
|||||||
QDesktopServices.openUrl(QUrl(theUrl))
|
QDesktopServices.openUrl(QUrl(theUrl))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _openDocumentation(self):
|
def _openUserManualFile(self):
|
||||||
"""Open the documentation, and select whether it should be the
|
"""Open the documentation in PDF format.
|
||||||
stable or latest version. If no internet connection, open
|
|
||||||
local.
|
|
||||||
"""
|
"""
|
||||||
if nw.__hexversion__[-2] == "f":
|
if self.mainConf.pdfDocs is None:
|
||||||
docsPath = "/en/stable/"
|
return False
|
||||||
else:
|
QDesktopServices.openUrl(QUrl(urljoin("file:", pathname2url(self.mainConf.pdfDocs))))
|
||||||
docsPath = "/en/latest/"
|
return True
|
||||||
|
|
||||||
try:
|
|
||||||
conn = HTTPSConnection(nw.__docurl__.replace("https://", ""))
|
|
||||||
conn.request("HEAD", docsPath)
|
|
||||||
resp = conn.getresponse()
|
|
||||||
hasAccess = resp.code == 200
|
|
||||||
except Exception:
|
|
||||||
hasAccess = False
|
|
||||||
|
|
||||||
docsFile = os.path.join(self.mainConf.assetPath, "help", "html", "index.html")
|
|
||||||
if hasAccess or not os.path.isfile(docsFile):
|
|
||||||
self._openWebsite(nw.__docurl__ + docsPath)
|
|
||||||
else:
|
|
||||||
self._openWebsite(urljoin("file:", pathname2url(docsFile)))
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Menu Builders
|
# Menu Builders
|
||||||
@@ -1106,13 +1086,21 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Help > Separator
|
# Help > Separator
|
||||||
self.helpMenu.addSeparator()
|
self.helpMenu.addSeparator()
|
||||||
|
|
||||||
# Help > Documentation
|
# Help > User Manual (Online)
|
||||||
self.aHelpDocs = QAction(self.tr("Documentation"), self)
|
self.aHelpDocs = QAction(self.tr("User Manual (Online)"), self)
|
||||||
self.aHelpDocs.setStatusTip(self.tr("Open documentation in browser"))
|
self.aHelpDocs.setStatusTip(self.tr("Open documentation in browser"))
|
||||||
self.aHelpDocs.triggered.connect(self._openDocumentation)
|
|
||||||
self.aHelpDocs.setShortcut("F1")
|
self.aHelpDocs.setShortcut("F1")
|
||||||
|
self.aHelpDocs.triggered.connect(lambda: self._openWebsite(nw.__docurl__))
|
||||||
self.helpMenu.addAction(self.aHelpDocs)
|
self.helpMenu.addAction(self.aHelpDocs)
|
||||||
|
|
||||||
|
# Help > User Manual (PDF)
|
||||||
|
if self.mainConf.pdfDocs is not None:
|
||||||
|
self.aPdfDocs = QAction(self.tr("User Manual (PDF)"), self)
|
||||||
|
self.aPdfDocs.setStatusTip(self.tr("Open PDF documentation"))
|
||||||
|
self.aPdfDocs.setShortcut("Shift+F1")
|
||||||
|
self.aPdfDocs.triggered.connect(self._openUserManualFile)
|
||||||
|
self.helpMenu.addAction(self.aPdfDocs)
|
||||||
|
|
||||||
# Help > Separator
|
# Help > Separator
|
||||||
self.helpMenu.addSeparator()
|
self.helpMenu.addSeparator()
|
||||||
|
|
||||||
|
|||||||
@@ -1373,6 +1373,8 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
# Help
|
# Help
|
||||||
self.addAction(self.mainMenu.aHelpDocs)
|
self.addAction(self.mainMenu.aHelpDocs)
|
||||||
|
if self.mainConf.pdfDocs is not None:
|
||||||
|
self.addAction(self.mainMenu.aPdfDocs)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -222,35 +222,51 @@ def buildQtDocs():
|
|||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Html Documentation Builder (docs)
|
# Html or PDF Documentation Builder (docs, docs_pdf)
|
||||||
##
|
##
|
||||||
|
|
||||||
def buildHtmlDocs():
|
def buildLocalDocs(bldFmt="HTML"):
|
||||||
"""This function will build the Sphinx HTML documentation. The files
|
"""This function will build the Sphinx HTML or PDF documentation.
|
||||||
are then copied into the nw/assets/help/html directory and can be
|
For HTML, the files are then copied into the nw/assets/help/html
|
||||||
included in builds.
|
directory and can be included in builds.
|
||||||
"""
|
"""
|
||||||
buildDir = os.path.join("docs", "build", "html")
|
|
||||||
helpDir = os.path.join("nw", "assets", "help", "html")
|
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
print("Building Documentation")
|
print("Building Documentation")
|
||||||
print("======================")
|
print("======================")
|
||||||
|
print("Format: %s" % bldFmt)
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
|
if bldFmt == "HTML":
|
||||||
|
buildDir = os.path.join("docs", "build", "html")
|
||||||
|
elif bldFmt == "PDF":
|
||||||
|
buildDir = os.path.join("docs", "build", "latex")
|
||||||
|
else:
|
||||||
|
print("Docs Build Error:")
|
||||||
|
return
|
||||||
|
|
||||||
buildFail = False
|
buildFail = False
|
||||||
try:
|
try:
|
||||||
subprocess.call(["make", "-C", "docs", "clean"])
|
subprocess.call(["make", "-C", "docs", "clean"])
|
||||||
subprocess.call(["make", "-C", "docs", "html"])
|
if bldFmt == "HTML":
|
||||||
|
subprocess.call(["make", "-C", "docs", "html"])
|
||||||
|
elif bldFmt == "PDF":
|
||||||
|
subprocess.call(["make", "-C", "docs", "latexpdf"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Docs Build Error:")
|
print("Docs Build Error:")
|
||||||
print(str(e))
|
print(str(e))
|
||||||
buildFail = True
|
buildFail = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if os.path.isdir(helpDir):
|
if bldFmt == "HTML":
|
||||||
shutil.rmtree(helpDir)
|
helpDir = os.path.join("nw", "assets", "help", "html")
|
||||||
shutil.copytree(buildDir, helpDir)
|
if os.path.isdir(helpDir):
|
||||||
|
shutil.rmtree(helpDir)
|
||||||
|
shutil.copytree(buildDir, helpDir)
|
||||||
|
elif bldFmt == "PDF":
|
||||||
|
os.rename(
|
||||||
|
os.path.join(buildDir, "manual.pdf"),
|
||||||
|
os.path.join("nw", "assets", "help", "manual.pdf")
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Docs Build Error:")
|
print("Docs Build Error:")
|
||||||
print(str(e))
|
print(str(e))
|
||||||
@@ -262,7 +278,11 @@ def buildHtmlDocs():
|
|||||||
print("")
|
print("")
|
||||||
print("Dependencies:")
|
print("Dependencies:")
|
||||||
print(" * pip install sphinx")
|
print(" * pip install sphinx")
|
||||||
print(" * pip install sphinx-rtd-theme")
|
if bldFmt == "HTML":
|
||||||
|
print(" * pip install sphinx-rtd-theme")
|
||||||
|
elif bldFmt == "PDF":
|
||||||
|
print(" * Package latexmk")
|
||||||
|
print(" * LaTeX build system")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
print("Documentation build: OK")
|
print("Documentation build: OK")
|
||||||
@@ -401,7 +421,7 @@ def makeMinimalPackage(targetOS):
|
|||||||
|
|
||||||
# Build docs
|
# Build docs
|
||||||
try:
|
try:
|
||||||
buildHtmlDocs()
|
buildLocalDocs(bldFmt="PDF")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Failed with error:")
|
print("Failed with error:")
|
||||||
print(str(e))
|
print(str(e))
|
||||||
@@ -440,7 +460,19 @@ def makeMinimalPackage(targetOS):
|
|||||||
if os.path.isfile(outFile):
|
if os.path.isfile(outFile):
|
||||||
os.unlink(outFile)
|
os.unlink(outFile)
|
||||||
|
|
||||||
rootFiles = ["LICENSE.md", "README.md", "CHANGELOG.md", "requirements.txt", "setup.py"]
|
# Add the manual also to the root
|
||||||
|
pdfDocs = os.path.join("nw", "assets", "help", "manual.pdf")
|
||||||
|
if os.path.isfile(pdfDocs):
|
||||||
|
os.rename(pdfDocs, "UserManual.pdf")
|
||||||
|
|
||||||
|
rootFiles = [
|
||||||
|
"CHANGELOG.md",
|
||||||
|
"LICENSE.md",
|
||||||
|
"README.md",
|
||||||
|
"requirements.txt",
|
||||||
|
"setup.py",
|
||||||
|
"UserManual.pdf",
|
||||||
|
]
|
||||||
|
|
||||||
with ZipFile(outFile, "w", compression=ZIP_DEFLATED, compresslevel=9) as zipObj:
|
with ZipFile(outFile, "w", compression=ZIP_DEFLATED, compresslevel=9) as zipObj:
|
||||||
|
|
||||||
@@ -1195,7 +1227,8 @@ if __name__ == "__main__":
|
|||||||
"",
|
"",
|
||||||
"Additional Builds:",
|
"Additional Builds:",
|
||||||
"",
|
"",
|
||||||
" docs Build the help documentation as HTML."
|
" htmldocs Build the help documentation as HTML.",
|
||||||
|
" pdfdocs Build the help documentation as PDF (requires LaTeX).",
|
||||||
" qthelp Build the help documentation for use with the Qt Assistant.",
|
" qthelp Build the help documentation for use with the Qt Assistant.",
|
||||||
" qtlupdate Update the translation files for internationalisation.",
|
" qtlupdate Update the translation files for internationalisation.",
|
||||||
" qtlrelease Build the language files for internationalisation.",
|
" qtlrelease Build the language files for internationalisation.",
|
||||||
@@ -1262,9 +1295,13 @@ if __name__ == "__main__":
|
|||||||
# Additional Builds
|
# Additional Builds
|
||||||
# =================
|
# =================
|
||||||
|
|
||||||
if "docs" in sys.argv:
|
if "htmldocs" in sys.argv:
|
||||||
sys.argv.remove("docs")
|
sys.argv.remove("htmldocs")
|
||||||
buildHtmlDocs()
|
buildLocalDocs(bldFmt="HTML")
|
||||||
|
|
||||||
|
if "pdfdocs" in sys.argv:
|
||||||
|
sys.argv.remove("pdfdocs")
|
||||||
|
buildLocalDocs(bldFmt="PDF")
|
||||||
|
|
||||||
if "qthelp" in sys.argv:
|
if "qthelp" in sys.argv:
|
||||||
sys.argv.remove("qthelp")
|
sys.argv.remove("qthelp")
|
||||||
|
|||||||
@@ -199,10 +199,10 @@ def testCoreToOdt_TextFormatting(mockGUI):
|
|||||||
|
|
||||||
# Incorrectly Formatted Text
|
# Incorrectly Formatted Text
|
||||||
theDoc.initDocument()
|
theDoc.initDocument()
|
||||||
theTxt = "A **few** _words"
|
theTxt = "A **few** _wordsXXX"
|
||||||
theFmt = " _b b_ I "
|
theFmt = " _b b_ I XXX"
|
||||||
theDoc._addTextPar("Standard", oStyle, theTxt, theFmt=theFmt)
|
theDoc._addTextPar("Standard", oStyle, theTxt, theFmt=theFmt)
|
||||||
assert theDoc.getErrors() == []
|
assert theDoc.getErrors() == ["Unknown format tag encountered"]
|
||||||
assert xmlToText(theDoc._xText) == (
|
assert xmlToText(theDoc._xText) == (
|
||||||
"<office:text>"
|
"<office:text>"
|
||||||
"<text:p text:style-name=\"Standard\">"
|
"<text:p text:style-name=\"Standard\">"
|
||||||
|
|||||||
Reference in New Issue
Block a user