+2
-2
@@ -48,7 +48,7 @@ templates_path = ["_templates"]
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = [".rst", ".md"]
|
||||
source_suffix = ".rst"
|
||||
source_suffix = ".txt"
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = "index"
|
||||
@@ -150,7 +150,7 @@ man_pages = [
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, "novelWriter", "novelWriter Documentation",
|
||||
author, "novelWriter", "One line description of project.",
|
||||
author, "novelWriter", "Markdown-like editor for novels.",
|
||||
"Miscellaneous"),
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
##################
|
||||
Exporting Projects
|
||||
##################
|
||||
|
||||
The novelWriter project can be exported in various formats using the export tool available from :menuselection:`Project --> Export Project` or by pressing :kbd:`F5`.
|
||||
|
||||
**************
|
||||
File Selection
|
||||
**************
|
||||
|
||||
Which files are selected for export can be controlled from the "Selection" section.
|
||||
The check box for "Novel files" will select any file that isn't classified as a note.
|
||||
This is useful when exporting a document containing the novel itself.
|
||||
|
||||
It is also possible to select the "Note files" for export, with or without the "Novel files".
|
||||
Comments can also optionally be included in the export for those formats that supports this.
|
||||
|
||||
**************
|
||||
Export Formats
|
||||
**************
|
||||
|
||||
Currently, five formats are supported for exporting.
|
||||
|
||||
novelWriter Markdown
|
||||
====================
|
||||
|
||||
This is simply a concatenation of the files selected by the filters.
|
||||
The files in the project are stacked together in the order they appear in the tree view, with comments, tags, etc. included.
|
||||
This is a useful format for exporting the project for later import back into novelWriter.
|
||||
|
||||
Plain Text
|
||||
==========
|
||||
|
||||
The plain text export format writes a simple ``.txt`` file without any formatting at all.
|
||||
Word-wrapping to a fixed column width is supported.
|
||||
It does, however, respect the centering of text if the :guilabel:`Fixed width` setting is larger than ``0``.
|
||||
|
||||
Markdown
|
||||
========
|
||||
|
||||
The markdown export formats writes a file using the valid markdown formats used by novelWriter, and converts comments to preformatted blocks by indenting it with four spaces.
|
||||
Word-wrapping to a fixed column width is supported.
|
||||
|
||||
HTML5 Document
|
||||
==============
|
||||
|
||||
The html5 export format writes a single ``.htm`` file with a minimal style settings block.
|
||||
The style setting block sets a maximum page width of ``768px``.
|
||||
Comments are exported as indented text boxes with a light yellow background.
|
||||
|
||||
LaTeX Document
|
||||
==============
|
||||
|
||||
The latex export formats generats a simple ``.tex`` file using a ``report`` document format.
|
||||
All chapters and sections are exported as unnumbered, and the internal header numbering formatting of novelWriter is used instead.
|
||||
Word-wrapping to a fixed column width in the LaTeX source is supported.
|
||||
|
||||
*****************
|
||||
Header Formatting
|
||||
*****************
|
||||
|
||||
The chapter, scene and section headers for novel files can receive some special treatment using the formats under :guilabel:`Chapter Headings` and :guilabel:`Other Headings`.
|
||||
The chapters can either be of numbered or unnumbered type, the latter being suitable for prologues, epilogues, interludes, etc.
|
||||
The headers can be generated using free text in combination with replace tags.
|
||||
The tooltip will explain which tags are available for each heading type.
|
||||
|
||||
.. note::
|
||||
Header formatting only applies to novel files.
|
||||
Headings in note files will will be left as-is, but heading levels 1 through 4 are converted to the correct heading level in the respective output formats.
|
||||
|
||||
Numbered Chapters
|
||||
=================
|
||||
|
||||
Numbered chapters can be automatically assigned a number from 1 and upwards.
|
||||
The number is inserted in place of the ``%num%`` tag.
|
||||
Alternatively, the number can be translated to a word by using the ``%numword%`` tag.
|
||||
The word numbers feature is currently only supported in English, and works for the range from "One" to "Nine Hundred and Ninety-Nine".
|
||||
|
||||
Unnumbered Chapters
|
||||
===================
|
||||
|
||||
Unnumbered chapters only support the ``%title%`` tag for auto-replacement.
|
||||
|
||||
Scenes and Sections
|
||||
===================
|
||||
|
||||
Both scenes and sections support the ``%title%`` tag.
|
||||
In addition, scenes behave differently than the other headings when the format does not contain any auto-replace tags.
|
||||
If the text in the box is any static text, the heading formatter will treat it as a scene separator, and insert that text as centred text between scenes, and ignoring the first scene of a chapter if the chapter heading is not followed by any text.
|
||||
|
||||
Leaving these fields blank will disable any output of scene and section headers or separators to the exported file.
|
||||
@@ -1,10 +1,12 @@
|
||||
####################################
|
||||
Welcome to novelWriter Documentation
|
||||
====================================
|
||||
####################################
|
||||
|
||||
This is the documentation for novelWriter |version|.
|
||||
|
||||
********
|
||||
Contents
|
||||
^^^^^^^^
|
||||
********
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
@@ -14,10 +16,12 @@ Contents
|
||||
projects
|
||||
structure
|
||||
notes
|
||||
export
|
||||
technical
|
||||
|
||||
******************
|
||||
Indices and Tables
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
******************
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
@@ -1,77 +0,0 @@
|
||||
|
||||
User Interface
|
||||
==============
|
||||
|
||||
The user interface is kept as simple as possible to avoid distractions.
|
||||
The main window contains a tree vew pane with the entire structure of the project, and a small details panel below it to display additional information.
|
||||
|
||||
Editing a document can be done by either double-clicking on it, or hitting the return key when the item is selected.
|
||||
This will open the source editor which uses a simplified markdown format described in the section below.
|
||||
|
||||
The document can also be viewed as html with all the comments and commands stripped out. To view a document, simply press Ctrl+R or select a file and go to :menuselection:`Document --> View Document` in the menu.
|
||||
The document viewed does not need to be the same document currently being edited.
|
||||
|
||||
Markdown Format
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
the document editor uses a simplified markdown format.
|
||||
That is, it supports basic formatting like bold, italics and underline, as well as four levels of headings.
|
||||
The formats are listed below.
|
||||
|
||||
In addition to these standard markdown features, the editor also allows for comments, that is text that is ignored by the word counter and not exported or seen in the document viewer.
|
||||
The editor also has a minimal set of commands used for setting tags and references between files.
|
||||
|
||||
* ``# Title``: Heading level one. The space after the # is mandatory.
|
||||
* ``## Title``: Heading level two. The space after the # is mandatory.
|
||||
* ``### Title``: Heading level three. The space after the # is mandatory.
|
||||
* ``#### Title``: Heading level four. The space after the # is mandatory.
|
||||
* ``**text**``: The text is renderred as bold text.
|
||||
* ``_text_``:The text is renderred as italics text.
|
||||
* ``__text__``: The text is renderred as underlined text.
|
||||
* ``% text...``: A comment. The text is not exported, seen in viewer, or counted towards word counts.
|
||||
* ``@keyword: value``: A keyword argument followed by a value, or a comma separated list of values.
|
||||
|
||||
Keyboard Shortcuts
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
All features are available as keyboard shortcuts.
|
||||
These are as following:
|
||||
|
||||
* ``Ctrl+Shift+O``: Open a project.
|
||||
* ``Ctrl+Shift+S``: Save the current project.
|
||||
* ``Ctrl+Shift+W``: Close the current project.
|
||||
* ``Ctrl+Shift+,``: Change project settings.
|
||||
* ``Ctrl+Shift+N``: Create new folder.
|
||||
* ``Ctrl+E, F2``: If in tree view, edit a document or folder settings.
|
||||
* ``Ctrl+Del``: If in tree view, move a document to trash, or delete a folder.
|
||||
* ``Ctrl+Q``: Exit novelWriter.
|
||||
* ``Ctrl+N``: Create new document.
|
||||
* ``Ctrl+O``: Open selected document.
|
||||
* ``Return``: If in tree view, open a document for editing.
|
||||
* ``Ctrl+S``: Save the current document in the editor.
|
||||
* ``Ctrl+W``: Close the current document in the editor.
|
||||
* ``Ctrl+R``: If in tree view, open a document for viewing. If in editor pane, open current document for viewing.
|
||||
* ``Ctrl+Shift+R``: Close the document view pane.
|
||||
* ``Ctrl+Z``: Undo latest changes.
|
||||
* ``Ctrl+Y``: Redo latest undo.
|
||||
* ``Ctrl+C``: Copy selected text to clipboard.
|
||||
* ``Ctrl+X``: Cut selected text to clipboard.
|
||||
* ``Ctrl+V``: Paste text from clipboard to cursor position.
|
||||
* ``Ctrl+A``: Select all text in document.
|
||||
* ``Ctrl+Shift+A``: Select all text in current paragraph.
|
||||
* ``Ctrl+1``: Switch focus to tree view pane.
|
||||
* ``Ctrl+2``: Switch focus to document editor pane.
|
||||
* ``Ctrl+3``: Switch focus to document viewer pane.
|
||||
* ``Ctrl+T``: Show project timeline.
|
||||
* ``Ctrl+B``: Format selected text, or word under cursor, as bold.
|
||||
* ``Ctrl+I``: Format selected text, or word under cursor, as italic.
|
||||
* ``Ctrl+U``: Format selected text, or word under cursor, as underline.
|
||||
* ``Ctrl+D``: Wrap selected text, or word under cursor, in double quotes.
|
||||
* ``Ctrl+Shift+D``: Wrap selected text, or word under cursor, in single quotes.
|
||||
* ``Ctrl+Shift+Up``: Move item one step up in the tree view.
|
||||
* ``Ctrl+Shift+Down``: Move item one step down in the tree view.
|
||||
* ``Ctrl+F7``: Toggle spell checking.
|
||||
* ``F7``: Re-run spell checker.
|
||||
* ``Ctrl+.``: Correct word under cursor.
|
||||
* ``F9``: Re-build project indices.
|
||||
* ``F1``: Open documentation.
|
||||
@@ -0,0 +1,95 @@
|
||||
##############
|
||||
User Interface
|
||||
##############
|
||||
|
||||
The user interface is kept as simple as possible to avoid distractions.
|
||||
The main window contains a tree vew pane with the entire structure of the project, and a small details panel below it to display additional information.
|
||||
|
||||
Editing a document can be done by either double-clicking on it, or hitting the return key when the item is selected.
|
||||
This will open the source editor which uses a simplified markdown format described in the section below.
|
||||
|
||||
The document can also be viewed as html with all the comments and commands stripped out. To view a document, simply press Ctrl+R or select a file and go to :menuselection:`Document --> View Document` in the menu.
|
||||
The document viewed does not need to be the same document currently being edited.
|
||||
|
||||
***************
|
||||
Markdown Format
|
||||
***************
|
||||
|
||||
the document editor uses a simplified markdown format.
|
||||
That is, it supports basic formatting like bold, italics and underline, as well as four levels of headings.
|
||||
The formats are listed below.
|
||||
|
||||
In addition to these standard markdown features, the editor also allows for comments, that is text that is ignored by the word counter and not exported or seen in the document viewer.
|
||||
The editor also has a minimal set of commands used for setting tags and references between files.
|
||||
|
||||
.. csv-table:: Formatting Syntax
|
||||
:header: "Format", "Description"
|
||||
:widths: 15, 70
|
||||
|
||||
"``# Title``", "Heading level one. The space after the # is mandatory."
|
||||
"``## Title``", "Heading level two. The space after the # is mandatory."
|
||||
"``### Title``", "Heading level three. The space after the # is mandatory."
|
||||
"``#### Title``", "Heading level four. The space after the # is mandatory."
|
||||
"``**text**``", "The text is renderred as bold text."
|
||||
"``_text_``", "The text is renderred as italics text."
|
||||
"``__text__``", "The text is renderred as underlined text."
|
||||
"``% text...``", "A comment. The text is not exported, seen in viewer, or counted towards word counts."
|
||||
"``@keyword: value``", "A keyword argument followed by a value, or a comma separated list of values."
|
||||
|
||||
******************
|
||||
Keyboard Shortcuts
|
||||
******************
|
||||
|
||||
All features are available as keyboard shortcuts.
|
||||
These are as following:
|
||||
|
||||
.. csv-table:: Keyboard Shortcuts
|
||||
:header: "Shortcut", "Description"
|
||||
:widths: 15, 70
|
||||
|
||||
":kbd:`Ctrl-.`", "Correct word under cursor."
|
||||
":kbd:`Ctrl-1`", "Switch focus to tree view pane."
|
||||
":kbd:`Ctrl-2`", "Switch focus to document editor pane."
|
||||
":kbd:`Ctrl-3`", "Switch focus to document viewer pane."
|
||||
":kbd:`Ctrl-A`", "Select all text in document."
|
||||
":kbd:`Ctrl-B`", "Format selected text, or word under cursor, as bold."
|
||||
":kbd:`Ctrl-C`", "Copy selected text to clipboard."
|
||||
":kbd:`Ctrl-D`", "Wrap selected text, or word under cursor, in double quotes."
|
||||
":kbd:`Ctrl-E`", "If in tree view, edit a document or folder settings."
|
||||
":kbd:`Ctrl-F`", "Open the search bar and search for selected word, if any is selected."
|
||||
":kbd:`Ctrl-H`", "Open the search and replace bar and search for selected word, if any is selected."
|
||||
":kbd:`Ctrl-I`", "Format selected text, or word under cursor, as italic."
|
||||
":kbd:`Ctrl-N`", "Create new document."
|
||||
":kbd:`Ctrl-O`", "Open selected document."
|
||||
":kbd:`Ctrl-Q`", "Exit novelWriter."
|
||||
":kbd:`Ctrl-R`", "If in tree view, open a document for viewing. If in editor pane, open current document for viewing."
|
||||
":kbd:`Ctrl-S`", "Save the current document in the editor."
|
||||
":kbd:`Ctrl-T`", "Show project timeline."
|
||||
":kbd:`Ctrl-U`", "Format selected text, or word under cursor, as underline."
|
||||
":kbd:`Ctrl-V`", "Paste text from clipboard to cursor position."
|
||||
":kbd:`Ctrl-W`", "Close the current document in the editor."
|
||||
":kbd:`Ctrl-X`", "Cut selected text to clipboard."
|
||||
":kbd:`Ctrl-Y`", "Redo latest undo."
|
||||
":kbd:`Ctrl-Z`", "Undo latest changes."
|
||||
":kbd:`Ctrl-F7`", "Toggle spell checking."
|
||||
":kbd:`Ctrl-Del`", "If in tree view, move a document to trash, or delete a folder."
|
||||
":kbd:`Ctrl-Shift-,`", "Change project settings."
|
||||
":kbd:`Ctrl-Shift-1`", "Replace occurrence of word in current document, and search for next occurrence."
|
||||
":kbd:`Ctrl-Shift-A`", "Select all text in current paragraph."
|
||||
":kbd:`Ctrl-Shift-D`", "Wrap selected text, or word under cursor, in single quotes."
|
||||
":kbd:`Ctrl-Shift-I`", "Import text to the current document from a text file."
|
||||
":kbd:`Ctrl-Shift-N`", "Create new folder."
|
||||
":kbd:`Ctrl-Shift-O`", "Open a project."
|
||||
":kbd:`Ctrl-Shift-R`", "Close the document view pane."
|
||||
":kbd:`Ctrl-Shift-S`", "Save the current project."
|
||||
":kbd:`Ctrl-Shift-W`", "Close the current project."
|
||||
":kbd:`Ctrl-Shift-Up`", "Move item one step up in the tree view."
|
||||
":kbd:`Ctrl-Shift-Down`", "Move item one step down in the tree view."
|
||||
":kbd:`F1`", "Open documentation."
|
||||
":kbd:`F2`", "Alternative to :kbd:`Ctrl-E`."
|
||||
":kbd:`F3`", "Find next occurrence of word in current document."
|
||||
":kbd:`F5`", "Export project dialog."
|
||||
":kbd:`F7`", "Re-run spell checker."
|
||||
":kbd:`F9`", "Re-build project indices."
|
||||
":kbd:`Shift-F3`", "Find previous occurrence of word in current document."
|
||||
":kbd:`Return`", "If in tree view, open a document for editing."
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
############
|
||||
Introduction
|
||||
============
|
||||
############
|
||||
|
||||
novelWriter is a simple multi-document plain text editor using a modified
|
||||
markdown syntax to apply simple formatting. Additional features are available
|
||||
@@ -1,12 +1,14 @@
|
||||
########################
|
||||
Supporting Files (Notes)
|
||||
========================
|
||||
########################
|
||||
|
||||
Supporting files, or notes, are any files stored in root folders that are not the Novel root folder.
|
||||
These files are intended for summaries and outlines of the various plot elements, characters, locations, and so on, of the novel.
|
||||
These are not required, but making at least minimal files for each such element makes it possible to use the timeline view feature to see how each element intersects with each section of the novel itself.
|
||||
|
||||
*********
|
||||
File Tags
|
||||
^^^^^^^^^
|
||||
*********
|
||||
|
||||
Each note file can have a tag associated with it,
|
||||
The format of a tag is ``@tag: tagname``, where tagname is a unique identifier.
|
||||
@@ -1,5 +1,6 @@
|
||||
##############
|
||||
Novel Projects
|
||||
==============
|
||||
##############
|
||||
|
||||
A novelWriter project requires a dedicated folder for storing its files.
|
||||
See the Technical Information section for further details.
|
||||
@@ -10,8 +11,9 @@ A list of recently opened projects is also maintained and can be selected from t
|
||||
The project specific settings are available in :menuselection:`Project --> Project Settings`.
|
||||
See further details below.
|
||||
|
||||
*****************
|
||||
Project Structure
|
||||
^^^^^^^^^^^^^^^^^
|
||||
*****************
|
||||
|
||||
Projects are structured into a set of root folders, visible in the left side tree view panel.
|
||||
|
||||
@@ -36,7 +38,7 @@ Deleted files will be moved into a special "Trash" root folder.
|
||||
Currently, these files cannot be permanently deleted from the project.
|
||||
|
||||
Orphaned Documents
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
==================
|
||||
|
||||
In the event the editor crashes or otherwise exits without saving the project state, files that have been added to the project tree and are saved to disk will appear in a special "Orphaned Items" root folder next time the application is started.
|
||||
These orphaned files will not have any meta data associated with them, so the title and other information has to be set again, and the files moved back to the correct location in the project.
|
||||
@@ -44,14 +46,15 @@ Using Project Folders
|
||||
|
||||
Folders, aside from root folders, have no structural significance to the project. They are there purely as a way for the user to organise the files in meaningful sections and to be able to close them in the tree view. When processing the files in the novel, the folders are ignored.
|
||||
|
||||
****************
|
||||
Project Settings
|
||||
^^^^^^^^^^^^^^^^
|
||||
****************
|
||||
|
||||
The project settings can be accessed from the :menuselection:`Project --> Project Settings` menu entry.
|
||||
This will open a dialog box.
|
||||
|
||||
Settings Tab
|
||||
~~~~~~~~~~~~
|
||||
============
|
||||
|
||||
The Settings tab holds the project title and author settings.
|
||||
Working Title can be set to a different title than the Book Title.
|
||||
@@ -62,7 +65,7 @@ The Book Authors text box takes one author per line.
|
||||
The line breaks matter in that this is converted to a list for later correct formatting.
|
||||
|
||||
Status Tab
|
||||
~~~~~~~~~~
|
||||
==========
|
||||
|
||||
Each file of type NOVEL can be given a status level, signified by a coloured icon.
|
||||
These are purely there for the user's convenience, and you are not required to use them for any other feature to work.
|
||||
@@ -71,7 +74,7 @@ The intention is to use this list to set what stage of writing you are on, altho
|
||||
Note that status levels currently in use by a file cannot be deleted.
|
||||
|
||||
Importance Tab
|
||||
~~~~~~~~~~~~~~
|
||||
==============
|
||||
|
||||
Each file of types PLOT, CHARACTER, WORLD, TIMELINE, OBJECT or CUSTOM can be given an importance level, signified by a coloured icon like for status level.
|
||||
These are also purely there for the user's convenience, and you are not required to use them for any other feature to work.
|
||||
@@ -81,7 +84,7 @@ Again, these can in principle be used for whatever you want.
|
||||
Note that importance levels currently in use by a file cannot be deleted.
|
||||
|
||||
Auto-Replace Tab
|
||||
~~~~~~~~~~~~~~~~
|
||||
================
|
||||
|
||||
A set of automatically replaced keywords can be added in this tab.
|
||||
The keywords in the left column wile be replaced by the text in the right column when documents are opened in the viewer.
|
||||
@@ -91,8 +94,9 @@ Note that a keyword cannot contain any spaces.
|
||||
The angle brackets are added by default, and when used in the text are a part of the keyword to be replaced.
|
||||
This is to ensure that parts of the text isn't unintentionally replaced by the content of the list.
|
||||
|
||||
*************
|
||||
Writing Files
|
||||
^^^^^^^^^^^^^
|
||||
*************
|
||||
|
||||
New document files can be created from the Document menu, or by pressing Ctrl+N while in the tree view pane.
|
||||
This will create a new, empty file, and open the item settings dialog where the filename and various other settings can be set.
|
||||
@@ -100,8 +104,9 @@ This dialog can also be opened again later from either the menu, :menuselection:
|
||||
|
||||
The different classes of documents have some restrictions.
|
||||
|
||||
******
|
||||
Backup
|
||||
^^^^^^
|
||||
******
|
||||
|
||||
An automatic backup system is built into novelWriter.
|
||||
In order to use it, a backup path to where the backups are to be stored needs to be provided in :menuselection:`Tools --> Preferences`.
|
||||
@@ -1,12 +1,13 @@
|
||||
|
||||
#################
|
||||
Project Structure
|
||||
=================
|
||||
#################
|
||||
|
||||
This section concerns files under the Novel type root folder.
|
||||
There are some restrictions and features that only applies to these type of files.
|
||||
|
||||
**********************
|
||||
Importance of Headings
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
**********************
|
||||
|
||||
Subfolders under root folders have no impact on the structure of the novel itself.
|
||||
The structure is instead dictated by the heading level.
|
||||
@@ -23,8 +24,9 @@ The different header levels are interpreted as specific section types of the nov
|
||||
* ``### Header3``: Header level 3 signifies a scene level partition.
|
||||
* ``#### Header4``: Header level 4 signifies a sub-scene level partition.
|
||||
|
||||
**************
|
||||
Tag References
|
||||
^^^^^^^^^^^^^^
|
||||
**************
|
||||
|
||||
Each section, indicated by a heading, can contain references to tags set in the supporting files of the project.
|
||||
See the File Tags section.
|
||||
@@ -62,8 +64,9 @@ the syntax highlighter will alert the user that only the correct keywords are us
|
||||
If the index of defined tags is out of date, press F9 to regenerate it, or select :menuselection:`Tools --> Rebuild Indices` from the menu.
|
||||
In general, the index for a file is regenerated when a file is saved, so this shouldn't normally be necessary.
|
||||
|
||||
*****************
|
||||
Novel File Layout
|
||||
^^^^^^^^^^^^^^^^^
|
||||
*****************
|
||||
|
||||
Files that exist under the NOVEL type root folder can have a number of layouts set.
|
||||
See overview below.
|
||||
@@ -1,14 +1,15 @@
|
||||
|
||||
#####################
|
||||
Technical Information
|
||||
=====================
|
||||
#####################
|
||||
|
||||
This section contains details of how novelWriter stores and handles the project data.
|
||||
|
||||
******************
|
||||
How Data is Stored
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
******************
|
||||
|
||||
Main Project File
|
||||
~~~~~~~~~~~~~~~~~
|
||||
=================
|
||||
|
||||
The project itself requires a dedicated folder for storing its files.
|
||||
The main project file is stored as an XML file with the name ``nwProject.nwx``.
|
||||
@@ -21,7 +22,7 @@ It is important to keep this file backed up.
|
||||
The project XML file is suitable for diff tools and version control, although a timesetamp is set in the meta section on line 2 each time the file is saved.
|
||||
|
||||
Project Documents
|
||||
~~~~~~~~~~~~~~~~~
|
||||
=================
|
||||
|
||||
The project documents are saved in folders staring with ``data_``.
|
||||
Each document has a file handle taken from the first 13 characters of a SHA256 hash of the system time.
|
||||
+2
-2
@@ -87,8 +87,8 @@ class Config:
|
||||
self.doReplaceDots = True
|
||||
self.wordCountTimer = 5.0
|
||||
|
||||
self.fmtSingleQuotes = ["‘","’"]
|
||||
self.fmtDoubleQuotes = ["“","”"]
|
||||
self.fmtSingleQuotes = ["\u2018","\u2019"]
|
||||
self.fmtDoubleQuotes = ["\u201c","\u201d"]
|
||||
|
||||
self.spellLanguage = "en_GB"
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Concatenated File
|
||||
|
||||
novelWriter – Concatenated File
|
||||
=================================
|
||||
Concatenate the standard novelWriter files to a single file
|
||||
|
||||
File History:
|
||||
Created: 2019-10-26 [0.3.1]
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import nw
|
||||
|
||||
from os import path
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from nw.convert.file.text import TextFile
|
||||
from nw.convert.tokenizer import Tokenizer
|
||||
from nw.enum import nwAlert, nwItemLayout
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ConcatFile(TextFile):
|
||||
|
||||
def __init__(self, theProject, theParent):
|
||||
TextFile.__init__(self, theProject, theParent)
|
||||
|
||||
self.theConv = Tokenizer(self.theProject, self.theParent)
|
||||
|
||||
return
|
||||
|
||||
def addText(self, tHandle):
|
||||
|
||||
logger.verbose("Parsing content of item '%s'" % tHandle)
|
||||
|
||||
theItem = self.theProject.getItem(tHandle)
|
||||
isNone = theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
||||
isNote = theItem.itemLayout == nwItemLayout.NOTE
|
||||
isNovel = not isNone and not isNote
|
||||
|
||||
if isNone:
|
||||
return False
|
||||
if isNote and not self.expNotes:
|
||||
return False
|
||||
if isNovel and not self.expNovel:
|
||||
return False
|
||||
|
||||
self.theConv.setText(tHandle)
|
||||
|
||||
theResult = self.theConv.theText
|
||||
|
||||
if theResult is not None and self.outFile is not None:
|
||||
self.outFile.write(theResult.rstrip())
|
||||
self.outFile.write("\n\n")
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _doOpenFile(self, filePath):
|
||||
try:
|
||||
self.outFile = open(filePath,mode="wt+")
|
||||
except Exception as e:
|
||||
self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _doCloseFile(self):
|
||||
if self.outFile is not None:
|
||||
self.outFile.close()
|
||||
return True
|
||||
|
||||
# END Class ConcatFile
|
||||
@@ -13,9 +13,9 @@
|
||||
import logging
|
||||
import nw
|
||||
|
||||
from nw.convert.textfile import TextFile
|
||||
from nw.convert.tohtml import ToHtml
|
||||
from nw.enum import nwAlert
|
||||
from nw.convert.file.text import TextFile
|
||||
from nw.convert.text.tohtml import ToHtml
|
||||
from nw.enum import nwAlert
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -34,15 +34,25 @@ class HtmlFile(TextFile):
|
||||
|
||||
def _doOpenFile(self, filePath):
|
||||
try:
|
||||
self.outFile = open(filePath,mode="w+")
|
||||
self.outFile = open(filePath,mode="wt+")
|
||||
self.outFile.write("<!DOCTYPE html>\n")
|
||||
self.outFile.write("<html>\n")
|
||||
self.outFile.write("<head>\n")
|
||||
self.outFile.write("<style>\n")
|
||||
self.outFile.write(" pre {background-color: #ffff99;}\n")
|
||||
self.outFile.write(" #page {\n")
|
||||
self.outFile.write(" margin: 40px auto;\n")
|
||||
self.outFile.write(" max-width: 769px;\n")
|
||||
self.outFile.write(" }\n")
|
||||
self.outFile.write(" .comment {\n")
|
||||
self.outFile.write(" background-color: #fbfabd;\n")
|
||||
self.outFile.write(" border: 1px solid #b4b000;\n")
|
||||
self.outFile.write(" margin: 10px 20px;\n")
|
||||
self.outFile.write(" padding: 6px;\n")
|
||||
self.outFile.write(" }\n")
|
||||
self.outFile.write("</style>\n")
|
||||
self.outFile.write("</head>\n")
|
||||
self.outFile.write("<body>\n")
|
||||
self.outFile.write("<article id='page'>\n")
|
||||
except Exception as e:
|
||||
self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR)
|
||||
return False
|
||||
@@ -50,6 +60,7 @@ class HtmlFile(TextFile):
|
||||
|
||||
def _doCloseFile(self):
|
||||
if self.outFile is not None:
|
||||
self.outFile.write("</article>\n")
|
||||
self.outFile.write("</body>\n")
|
||||
self.outFile.write("</html>\n")
|
||||
self.outFile.close()
|
||||
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter LaTeX File
|
||||
|
||||
novelWriter – LaTeX File
|
||||
==========================
|
||||
Writes the project to a LaTeX file
|
||||
|
||||
File History:
|
||||
Created: 2019-10-24 [0.3.1]
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import nw
|
||||
|
||||
from nw.convert.file.text import TextFile
|
||||
from nw.convert.text.tolatex import ToLaTeX
|
||||
from nw.enum import nwAlert
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class LaTeXFile(TextFile):
|
||||
|
||||
def __init__(self, theProject, theParent):
|
||||
TextFile.__init__(self, theProject, theParent)
|
||||
|
||||
self.theConv = ToLaTeX(self.theProject, self.theParent)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _doOpenFile(self, filePath):
|
||||
|
||||
try:
|
||||
self.outFile = open(filePath,mode="wt+")
|
||||
self.outFile.write(r"\documentclass[12pt]{report}\n")
|
||||
self.outFile.write(r"\usepackage[utf8]{inputenc}\n")
|
||||
self.outFile.write(r"\begin{document}\n")
|
||||
except Exception as e:
|
||||
self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _doCloseFile(self):
|
||||
|
||||
if self.outFile is not None:
|
||||
self.outFile.write(r"\end{document}\n")
|
||||
self.outFile.close()
|
||||
|
||||
return True
|
||||
|
||||
# END Class LaTeXFile
|
||||
@@ -13,9 +13,9 @@
|
||||
import logging
|
||||
import nw
|
||||
|
||||
from nw.convert.textfile import TextFile
|
||||
from nw.convert.tomarkdown import ToMarkdown
|
||||
from nw.enum import nwAlert
|
||||
from nw.convert.file.text import TextFile
|
||||
from nw.convert.text.tomarkdown import ToMarkdown
|
||||
from nw.enum import nwAlert
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -34,7 +34,7 @@ class MarkdownFile(TextFile):
|
||||
|
||||
def _doOpenFile(self, filePath):
|
||||
try:
|
||||
self.outFile = open(filePath,mode="w+")
|
||||
self.outFile = open(filePath,mode="wt+")
|
||||
except Exception as e:
|
||||
self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR)
|
||||
return False
|
||||
@@ -16,8 +16,8 @@ import nw
|
||||
from os import path
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from nw.convert.tokenizer import Tokenizer
|
||||
from nw.enum import nwAlert, nwItemLayout
|
||||
from nw.convert.text.totext import ToText
|
||||
from nw.enum import nwAlert, nwItemLayout
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -34,9 +34,8 @@ class TextFile():
|
||||
self.theText = ""
|
||||
self.expNovel = True
|
||||
self.expNotes = False
|
||||
self.winEnding = False
|
||||
|
||||
self.theConv = Tokenizer(self.theProject, self.theParent)
|
||||
self.theConv = ToText(self.theProject, self.theParent)
|
||||
self.makeAlert = self.theParent.makeAlert
|
||||
|
||||
self.setComments(False)
|
||||
@@ -140,9 +139,6 @@ class TextFile():
|
||||
self.theConv.doHeaders()
|
||||
self.theConv.doConvert()
|
||||
|
||||
if self.winEnding:
|
||||
self.theConv.windowsEndings()
|
||||
|
||||
if self.theConv.theResult is not None and self.outFile is not None:
|
||||
self.outFile.write(self.theConv.theResult)
|
||||
|
||||
@@ -157,11 +153,8 @@ class TextFile():
|
||||
that uses a different file format that requires a different approach.
|
||||
"""
|
||||
try:
|
||||
self.outFile = open(filePath,mode="w+")
|
||||
if self.winEnding:
|
||||
self.outFile.write("\r\n\r\n")
|
||||
else:
|
||||
self.outFile.write("\n\n")
|
||||
self.outFile = open(filePath,mode="wt+")
|
||||
self.outFile.write("\n\n")
|
||||
except Exception as e:
|
||||
self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR)
|
||||
return False
|
||||
@@ -28,9 +28,13 @@ class ToHtml(Tokenizer):
|
||||
Tokenizer.doAutoReplace(self)
|
||||
|
||||
repDict = {
|
||||
"<" : "<",
|
||||
">" : ">",
|
||||
"&" : "&",
|
||||
"<" : "<",
|
||||
">" : ">",
|
||||
"&" : "&",
|
||||
"\u2013" : "&endash;",
|
||||
"\u2014" : "$emdash;",
|
||||
"\u2500" : "$emdash;",
|
||||
"\u2026" : "…",
|
||||
}
|
||||
xRep = re.compile("|".join([re.escape(k) for k in repDict.keys()]), flags=re.DOTALL)
|
||||
self.theText = xRep.sub(lambda x: repDict[x.group(0)], self.theText)
|
||||
@@ -88,7 +92,7 @@ class ToHtml(Tokenizer):
|
||||
thisPar.append(tTemp)
|
||||
|
||||
elif tType == self.T_COMMENT and self.doComments:
|
||||
self.theResult += "<pre>%s</pre>\n" % tText
|
||||
self.theResult += "<div class='comment'>%s</div>\n" % tText
|
||||
|
||||
elif tType == self.T_COMMAND and self.doCommands:
|
||||
self.theResult += "<pre>%s</pre>\n" % tText
|
||||
@@ -0,0 +1,155 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter LaTeX Converter
|
||||
|
||||
novelWriter – LaTeX Converter
|
||||
===============================
|
||||
Extends the Tokenizer class to write LaTeX
|
||||
|
||||
File History:
|
||||
Created: 2019-10-24 [0.3.1]
|
||||
|
||||
"""
|
||||
|
||||
import textwrap
|
||||
import logging
|
||||
import re
|
||||
import nw
|
||||
|
||||
from nw.convert.tokenizer import Tokenizer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ToLaTeX(Tokenizer):
|
||||
|
||||
def __init__(self, theProject, theParent):
|
||||
Tokenizer.__init__(self, theProject, theParent)
|
||||
return
|
||||
|
||||
def doAutoReplace(self):
|
||||
Tokenizer.doAutoReplace(self)
|
||||
|
||||
repDict = {
|
||||
"\u2013" : "--",
|
||||
"\u2014" : "---",
|
||||
"\u2500" : "---",
|
||||
"\u2026" : "...",
|
||||
}
|
||||
xRep = re.compile("|".join([re.escape(k) for k in repDict.keys()]), flags=re.DOTALL)
|
||||
self.theText = xRep.sub(lambda x: repDict[x.group(0)], self.theText)
|
||||
|
||||
return
|
||||
|
||||
def doConvert(self):
|
||||
|
||||
texTags = {
|
||||
self.FMT_B_B : r"\textbf{",
|
||||
self.FMT_B_E : r"}",
|
||||
self.FMT_I_B : r"\textit{",
|
||||
self.FMT_I_E : r"}",
|
||||
self.FMT_U_B : r"\underline{",
|
||||
self.FMT_U_E : r"}",
|
||||
}
|
||||
|
||||
if self.wordWrap > 0:
|
||||
tWrap = textwrap.TextWrapper(
|
||||
width = self.wordWrap,
|
||||
initial_indent = "",
|
||||
subsequent_indent = "",
|
||||
expand_tabs = True,
|
||||
replace_whitespace = True,
|
||||
fix_sentence_endings = False,
|
||||
break_long_words = True,
|
||||
drop_whitespace = True,
|
||||
break_on_hyphens = True,
|
||||
tabsize = 8,
|
||||
max_lines = None
|
||||
)
|
||||
tComm = textwrap.TextWrapper(
|
||||
width = self.wordWrap-2,
|
||||
initial_indent = "",
|
||||
subsequent_indent = "",
|
||||
expand_tabs = True,
|
||||
replace_whitespace = True,
|
||||
fix_sentence_endings = False,
|
||||
break_long_words = True,
|
||||
drop_whitespace = True,
|
||||
break_on_hyphens = True,
|
||||
tabsize = 8,
|
||||
max_lines = None
|
||||
)
|
||||
|
||||
self.theResult = ""
|
||||
thisPar = []
|
||||
for tType, tText, tFormat, tAlign in self.theTokens:
|
||||
|
||||
begText = ""
|
||||
endText = "\n"
|
||||
if tAlign == self.A_CENTRE:
|
||||
begText = "\\begin{center}\n"
|
||||
endText = "\\end{center}\n\n"
|
||||
|
||||
# First check if we have a comment or plain text, as they need some
|
||||
# extra replacing before we proceed to wrapping and final formatting.
|
||||
if tType == self.T_COMMENT:
|
||||
tText = "%% %s" % tText
|
||||
|
||||
elif tType == self.T_TEXT:
|
||||
tTemp = tText
|
||||
for xPos, xLen, xFmt in reversed(tFormat):
|
||||
tTemp = tTemp[:xPos]+texTags[xFmt]+tTemp[xPos+xLen:]
|
||||
tText = tTemp
|
||||
|
||||
tLen = len(tText)
|
||||
|
||||
# The text can now be word wrapped, if we have requested this and it's needed.
|
||||
if self.wordWrap > 0 and tLen > self.wordWrap:
|
||||
if tType == self.T_COMMENT:
|
||||
aText = tComm.wrap(tText)
|
||||
tText = "\n% ".join(aText)
|
||||
else:
|
||||
tText = tWrap.fill(tText)
|
||||
|
||||
# Then the text can receive final formatting before we append it to the results.
|
||||
# We also store text lines in a buffer and merge them only when we find an empty line,
|
||||
# indicating a new paragraph.
|
||||
if tType == self.T_EMPTY:
|
||||
if len(thisPar) > 0:
|
||||
self.theResult += begText
|
||||
self.theResult += "%s\n" % tWrap.fill(" ".join(thisPar))
|
||||
self.theResult += endText
|
||||
thisPar = []
|
||||
|
||||
elif tType == self.T_HEAD1:
|
||||
self.theResult += begText
|
||||
self.theResult += "{\\Huge %s}\n" % tText
|
||||
self.theResult += endText
|
||||
|
||||
elif tType == self.T_HEAD2:
|
||||
self.theResult += "\\chapter*{%s}\n\n" % tText
|
||||
|
||||
elif tType == self.T_HEAD3:
|
||||
self.theResult += "\\section*{%s}\n\n" % tText
|
||||
|
||||
elif tType == self.T_HEAD4:
|
||||
self.theResult += "\\subsection*{%s}\n\n" % tText
|
||||
|
||||
elif tType == self.T_SEP:
|
||||
self.theResult += begText
|
||||
self.theResult += "%s\n" % tText
|
||||
self.theResult += endText
|
||||
|
||||
elif tType == self.T_TEXT:
|
||||
thisPar.append(tText)
|
||||
|
||||
elif tType == self.T_PBREAK:
|
||||
self.theResult += "\\newpage\n\n"
|
||||
|
||||
elif tType == self.T_COMMENT and self.doComments:
|
||||
self.theResult += "%s\n\n" % tText
|
||||
|
||||
elif tType == self.T_COMMAND and self.doCommands:
|
||||
self.theResult += "%% @%s\n\n" % tText
|
||||
|
||||
return
|
||||
|
||||
# END Class ToLaTeX
|
||||
@@ -0,0 +1,119 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Plain Text Converter
|
||||
|
||||
novelWriter – Plain Text Converter
|
||||
====================================
|
||||
Extends the Tokenizer class to convert to plain text
|
||||
|
||||
File History:
|
||||
Created: 2019-10-26 [0.3.1]
|
||||
|
||||
"""
|
||||
|
||||
import textwrap
|
||||
import logging
|
||||
import re
|
||||
import nw
|
||||
|
||||
from nw.convert.tokenizer import Tokenizer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ToText(Tokenizer):
|
||||
|
||||
def __init__(self, theProject, theParent):
|
||||
Tokenizer.__init__(self, theProject, theParent)
|
||||
return
|
||||
|
||||
def doConvert(self):
|
||||
"""Converts the tokenized text into plain text.
|
||||
"""
|
||||
|
||||
if self.wordWrap > 0:
|
||||
tWrap = textwrap.TextWrapper(
|
||||
width = self.wordWrap,
|
||||
initial_indent = "",
|
||||
subsequent_indent = "",
|
||||
expand_tabs = True,
|
||||
replace_whitespace = True,
|
||||
fix_sentence_endings = False,
|
||||
break_long_words = True,
|
||||
drop_whitespace = True,
|
||||
break_on_hyphens = True,
|
||||
tabsize = 8,
|
||||
max_lines = None
|
||||
)
|
||||
|
||||
self.theResult = ""
|
||||
thisPar = []
|
||||
for tType, tText, tFormat, tAlign in self.theTokens:
|
||||
|
||||
# First check if we have a comment or plain text, as they need some
|
||||
# extra replacing before we proceed to wrapping and final formatting.
|
||||
if tType == self.T_COMMENT:
|
||||
tText = "[%s]" % tText
|
||||
|
||||
elif tType == self.T_TEXT:
|
||||
tTemp = tText
|
||||
for xPos, xLen, xFmt in reversed(tFormat):
|
||||
tTemp = tTemp[:xPos]+tTemp[xPos+xLen:]
|
||||
tText = tTemp
|
||||
|
||||
tLen = len(tText)
|
||||
|
||||
# The text can now be word wrapped, if we have requested this and it's needed.
|
||||
if tAlign == self.A_CENTRE:
|
||||
if self.wordWrap > 0:
|
||||
if tLen > self.wordWrap:
|
||||
aText = tWrap.wrap(tText)
|
||||
for n in range(len(aText)):
|
||||
aText[n] = self._centreText(aText[n],self.wordWrap)
|
||||
tText = "\n".join(aText)
|
||||
else:
|
||||
tText = self._centreText(tText,self.wordWrap)
|
||||
else:
|
||||
if self.wordWrap > 0 and tLen > self.wordWrap:
|
||||
tText = tWrap.fill(tText)
|
||||
|
||||
# Then the text can receive final formatting before we append it to the results.
|
||||
# We also store text lines in a buffer and merge them only when we find an empty line,
|
||||
# indicating a new paragraph.
|
||||
if tType == self.T_EMPTY:
|
||||
if len(thisPar) > 0:
|
||||
self.theResult += "%s\n\n" % " ".join(thisPar)
|
||||
thisPar = []
|
||||
|
||||
elif tType == self.T_HEAD1:
|
||||
uLine = "="*min(tLen,self.wordWrap)
|
||||
if tAlign == self.A_CENTRE:
|
||||
uLine = self._centreText(uLine,self.wordWrap)
|
||||
self.theResult += "%s\n%s\n\n" % (tText,uLine)
|
||||
|
||||
elif tType == self.T_HEAD2:
|
||||
uLine = "~"*min(tLen,self.wordWrap)
|
||||
self.theResult += "%s\n%s\n\n" % (tText,uLine)
|
||||
|
||||
elif tType == self.T_HEAD3:
|
||||
uLine = "-"*min(tLen,self.wordWrap)
|
||||
self.theResult += "%s\n%s\n\n" % (tText,uLine)
|
||||
|
||||
elif tType == self.T_HEAD4:
|
||||
self.theResult += "%s\n\n" % tText
|
||||
|
||||
elif tType == self.T_SEP:
|
||||
if self.wordWrap > 0 and tLen < self.wordWrap:
|
||||
tText = self._centreText(tText,self.wordWrap)
|
||||
self.theResult += "%s\n\n" % tText
|
||||
|
||||
elif tType == self.T_TEXT:
|
||||
thisPar.append(tText)
|
||||
|
||||
elif tType == self.T_COMMENT and self.doComments:
|
||||
self.theResult += "%s\n\n" % tText
|
||||
|
||||
elif tType == self.T_COMMAND and self.doCommands:
|
||||
self.theResult += "%s\n\n" % tText
|
||||
|
||||
return
|
||||
|
||||
# END Class ToText
|
||||
+22
-113
@@ -26,27 +26,28 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class Tokenizer():
|
||||
|
||||
FMT_B_B = 1 # Begin bold
|
||||
FMT_B_E = 2 # End bold
|
||||
FMT_I_B = 3 # Begin italics
|
||||
FMT_I_E = 4 # End italics
|
||||
FMT_U_B = 5 # Begin underline
|
||||
FMT_U_E = 6 # End underline
|
||||
FMT_B_B = 1 # Begin bold
|
||||
FMT_B_E = 2 # End bold
|
||||
FMT_I_B = 3 # Begin italics
|
||||
FMT_I_E = 4 # End italics
|
||||
FMT_U_B = 5 # Begin underline
|
||||
FMT_U_E = 6 # End underline
|
||||
|
||||
T_EMPTY = 1 # Empty line (new paragraph)
|
||||
T_COMMENT = 2 # Comment line
|
||||
T_COMMAND = 3 # Command line
|
||||
T_HEAD1 = 4 # Header 1 (title)
|
||||
T_HEAD2 = 5 # Header 2 (chapter)
|
||||
T_HEAD3 = 6 # Header 3 (scene)
|
||||
T_HEAD4 = 7 # Header 4
|
||||
T_TEXT = 8 # Text line
|
||||
T_SEP = 9 # Scene separator
|
||||
T_EMPTY = 1 # Empty line (new paragraph)
|
||||
T_COMMENT = 2 # Comment line
|
||||
T_COMMAND = 3 # Command line
|
||||
T_HEAD1 = 4 # Header 1 (title)
|
||||
T_HEAD2 = 5 # Header 2 (chapter)
|
||||
T_HEAD3 = 6 # Header 3 (scene)
|
||||
T_HEAD4 = 7 # Header 4
|
||||
T_TEXT = 8 # Text line
|
||||
T_SEP = 9 # Scene separator
|
||||
T_PBREAK = 10 # Page break
|
||||
|
||||
A_LEFT = 1 # Left aligned
|
||||
A_RIGHT = 2 # Right aligned
|
||||
A_CENTRE = 3 # Centred
|
||||
A_JUSTIFY = 4 # Justified
|
||||
A_LEFT = 1 # Left aligned
|
||||
A_RIGHT = 2 # Right aligned
|
||||
A_CENTRE = 3 # Centred
|
||||
A_JUSTIFY = 4 # Justified
|
||||
|
||||
def __init__(self, theProject, theParent):
|
||||
|
||||
@@ -262,6 +263,7 @@ class Tokenizer():
|
||||
self.theTokens[n] = (tType,tTemp,None,self.A_LEFT)
|
||||
|
||||
# For title page and partitions, we need to centre all text
|
||||
# and for some formats, we need a page break
|
||||
if isTitle or isPart:
|
||||
for n in range(len(self.theTokens)):
|
||||
tToken = self.theTokens[n]
|
||||
@@ -270,101 +272,8 @@ class Tokenizer():
|
||||
tFormat = tToken[2]
|
||||
self.theTokens[n] = (tType,tText,tFormat,self.A_CENTRE)
|
||||
|
||||
return
|
||||
self.theTokens[n] = (self.T_PBREAK,"",None,self.A_LEFT)
|
||||
|
||||
def doConvert(self):
|
||||
"""Converts the tokenized text into plain text.
|
||||
"""
|
||||
|
||||
if self.wordWrap > 0:
|
||||
tWrap = textwrap.TextWrapper(
|
||||
width = self.wordWrap,
|
||||
initial_indent = "",
|
||||
subsequent_indent = "",
|
||||
expand_tabs = True,
|
||||
replace_whitespace = True,
|
||||
fix_sentence_endings = False,
|
||||
break_long_words = True,
|
||||
drop_whitespace = True,
|
||||
break_on_hyphens = True,
|
||||
tabsize = 8,
|
||||
max_lines = None
|
||||
)
|
||||
|
||||
self.theResult = ""
|
||||
thisPar = []
|
||||
for tType, tText, tFormat, tAlign in self.theTokens:
|
||||
|
||||
# First check if we have a comment or plain text, as they need some
|
||||
# extra replacing before we proceed to wrapping and final formatting.
|
||||
if tType == self.T_COMMENT:
|
||||
tText = "[%s]" % tText
|
||||
|
||||
elif tType == self.T_TEXT:
|
||||
tTemp = tText
|
||||
for xPos, xLen, xFmt in reversed(tFormat):
|
||||
tTemp = tTemp[:xPos]+tTemp[xPos+xLen:]
|
||||
tText = tTemp
|
||||
|
||||
tLen = len(tText)
|
||||
|
||||
# The text can now be word wrapped, if we have requested this and it's needed.
|
||||
if tAlign == self.A_CENTRE:
|
||||
if self.wordWrap > 0:
|
||||
if tLen > self.wordWrap:
|
||||
aText = tWrap.wrap(tText)
|
||||
for n in range(len(aText)):
|
||||
aText[n] = self._centreText(aText[n],self.wordWrap)
|
||||
tText = "\n".join(aText)
|
||||
else:
|
||||
tText = self._centreText(tText,self.wordWrap)
|
||||
else:
|
||||
if self.wordWrap > 0 and tLen > self.wordWrap:
|
||||
tText = tWrap.fill(tText)
|
||||
|
||||
# Then the text can receive final formatting before we append it to the results.
|
||||
# We also store text lines in a buffer and merge them only when we find an empty line,
|
||||
# indicating a new paragraph.
|
||||
if tType == self.T_EMPTY:
|
||||
if len(thisPar) > 0:
|
||||
self.theResult += "%s\n\n" % " ".join(thisPar)
|
||||
thisPar = []
|
||||
|
||||
elif tType == self.T_HEAD1:
|
||||
uLine = "="*min(tLen,self.wordWrap)
|
||||
if tAlign == self.A_CENTRE:
|
||||
uLine = self._centreText(uLine,self.wordWrap)
|
||||
self.theResult += "%s\n%s\n\n" % (tText,uLine)
|
||||
|
||||
elif tType == self.T_HEAD2:
|
||||
uLine = "~"*min(tLen,self.wordWrap)
|
||||
self.theResult += "%s\n%s\n\n" % (tText,uLine)
|
||||
|
||||
elif tType == self.T_HEAD3:
|
||||
uLine = "-"*min(tLen,self.wordWrap)
|
||||
self.theResult += "%s\n%s\n\n" % (tText,uLine)
|
||||
|
||||
elif tType == self.T_HEAD4:
|
||||
self.theResult += "%s\n\n" % tText
|
||||
|
||||
elif tType == self.T_SEP:
|
||||
if self.wordWrap > 0 and tLen < self.wordWrap:
|
||||
tText = self._centreText(tText,self.wordWrap)
|
||||
self.theResult += "%s\n\n" % tText
|
||||
|
||||
elif tType == self.T_TEXT:
|
||||
thisPar.append(tText)
|
||||
|
||||
elif tType == self.T_COMMENT and self.doComments:
|
||||
self.theResult += "%s\n\n" % tText
|
||||
|
||||
elif tType == self.T_COMMAND and self.doCommands:
|
||||
self.theResult += "%s\n\n" % tText
|
||||
|
||||
return
|
||||
|
||||
def windowsEndings(self):
|
||||
self.theResult = self.theResult.replace("\n","\r\n")
|
||||
return
|
||||
|
||||
##
|
||||
|
||||
+4
-4
@@ -422,15 +422,15 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
elif self.mainConf.doReplaceDash and theTwo == "--":
|
||||
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 2)
|
||||
theCursor.insertText("–")
|
||||
theCursor.insertText("\u2013")
|
||||
|
||||
elif self.mainConf.doReplaceDash and theTwo == "–-":
|
||||
elif self.mainConf.doReplaceDash and theTwo == "\u2013-":
|
||||
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 2)
|
||||
theCursor.insertText("—")
|
||||
theCursor.insertText("\u2014")
|
||||
|
||||
elif self.mainConf.doReplaceDots and theThree == "...":
|
||||
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 3)
|
||||
theCursor.insertText("…")
|
||||
theCursor.insertText("\u2026")
|
||||
|
||||
return
|
||||
|
||||
|
||||
+3
-3
@@ -17,9 +17,9 @@ from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QTextBrowser
|
||||
from PyQt5.QtGui import QTextOption, QFont, QPalette, QColor
|
||||
|
||||
from nw.convert.tokenizer import Tokenizer
|
||||
from nw.convert.tohtml import ToHtml
|
||||
from nw.enum import nwItemType
|
||||
from nw.convert.tokenizer import Tokenizer
|
||||
from nw.convert.text.tohtml import ToHtml
|
||||
from nw.enum import nwItemType
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
+39
-24
@@ -23,14 +23,16 @@ from PyQt5.QtWidgets import (
|
||||
QLabel, QComboBox, QLineEdit, QPushButton, QFileDialog, QProgressBar, QSpinBox
|
||||
)
|
||||
|
||||
from nw.project.document import NWDoc
|
||||
from nw.tools.translate import numberToWord
|
||||
from nw.tools.optlaststate import OptLastState
|
||||
from nw.convert.textfile import TextFile
|
||||
from nw.convert.htmlfile import HtmlFile
|
||||
from nw.convert.markdownfile import MarkdownFile
|
||||
from nw.constants import nwFiles
|
||||
from nw.enum import nwItemType
|
||||
from nw.project.document import NWDoc
|
||||
from nw.tools.translate import numberToWord
|
||||
from nw.tools.optlaststate import OptLastState
|
||||
from nw.convert.file.text import TextFile
|
||||
from nw.convert.file.html import HtmlFile
|
||||
from nw.convert.file.markdown import MarkdownFile
|
||||
from nw.convert.file.latex import LaTeXFile
|
||||
from nw.convert.file.concat import ConcatFile
|
||||
from nw.constants import nwFiles
|
||||
from nw.enum import nwItemType
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -127,6 +129,10 @@ class GuiExport(QDialog):
|
||||
outFile = MarkdownFile(self.theProject, self.theParent)
|
||||
elif eFormat == GuiExportMain.FMT_HTML:
|
||||
outFile = HtmlFile(self.theProject, self.theParent)
|
||||
elif eFormat == GuiExportMain.FMT_TEX:
|
||||
outFile = LaTeXFile(self.theProject, self.theParent)
|
||||
elif eFormat == GuiExportMain.FMT_NWD:
|
||||
outFile = ConcatFile(self.theProject, self.theParent)
|
||||
|
||||
if outFile is None:
|
||||
return False
|
||||
@@ -203,13 +209,15 @@ class GuiExport(QDialog):
|
||||
|
||||
class GuiExportMain(QWidget):
|
||||
|
||||
FMT_TXT = 1
|
||||
FMT_MD = 2
|
||||
FMT_HTML = 3
|
||||
FMT_EBOOK = 4
|
||||
FMT_ODT = 5
|
||||
FMT_TEX = 6
|
||||
FMT_NWD = 1 # novelWriter markdown
|
||||
FMT_TXT = 2 # Plain text file
|
||||
FMT_MD = 3 # Markdown file
|
||||
FMT_HTML = 4 # HTML file
|
||||
FMT_EBOOK = 5 # E-book friendly HTML
|
||||
FMT_ODT = 6 # Open document
|
||||
FMT_TEX = 7 # LaTeX file
|
||||
FMT_EXT = {
|
||||
FMT_NWD : ".nwd",
|
||||
FMT_TXT : ".txt",
|
||||
FMT_MD : ".md",
|
||||
FMT_HTML : ".htm",
|
||||
@@ -218,6 +226,10 @@ class GuiExportMain(QWidget):
|
||||
FMT_TEX : ".tex",
|
||||
}
|
||||
FMT_HELP = {
|
||||
FMT_NWD : (
|
||||
"Exports a document using the novelWriter markdown format. "
|
||||
"The files selected by the filters are appended as-is, including comments and other settings."
|
||||
),
|
||||
FMT_TXT : (
|
||||
"Exports a plain text file. "
|
||||
"All formatting is stripped and comments are in square brackets."
|
||||
@@ -228,7 +240,7 @@ class GuiExportMain(QWidget):
|
||||
),
|
||||
FMT_HTML : (
|
||||
"Exports a plain html5 file. "
|
||||
"Comments are converted to preformatted text blocks."
|
||||
"Comments are wrapped in blocks with a yellow background colour."
|
||||
),
|
||||
FMT_EBOOK : (
|
||||
"Exports an html5 file that can be converted to eBook with Calibre. "
|
||||
@@ -343,12 +355,13 @@ class GuiExportMain(QWidget):
|
||||
self.outputHelp.setAlignment(Qt.AlignTop)
|
||||
|
||||
self.outputFormat = QComboBox(self)
|
||||
self.outputFormat.addItem("Plain Text (.txt)", self.FMT_TXT)
|
||||
self.outputFormat.addItem("Markdown (.md)", self.FMT_MD)
|
||||
self.outputFormat.addItem("HTML5 (.htm)", self.FMT_HTML)
|
||||
# self.outputFormat.addItem("HTML5 for eBook (.htm)", self.FMT_EBOOK)
|
||||
# self.outputFormat.addItem("Open Document (.odt)", self.FMT_ODT)
|
||||
# self.outputFormat.addItem("LaTeX for PDF (.tex)", self.FMT_TEX)
|
||||
self.outputFormat.addItem("novelWriter Markdown (.nwd)", self.FMT_NWD)
|
||||
self.outputFormat.addItem("Plain Text (.txt)", self.FMT_TXT)
|
||||
self.outputFormat.addItem("Markdown (.md)", self.FMT_MD)
|
||||
self.outputFormat.addItem("HTML5 (.htm)", self.FMT_HTML)
|
||||
# self.outputFormat.addItem("HTML5 for eBook (.htm)", self.FMT_EBOOK)
|
||||
# self.outputFormat.addItem("Open Document (.odt)", self.FMT_ODT)
|
||||
self.outputFormat.addItem("LaTeX for PDF (.tex)", self.FMT_TEX)
|
||||
self.outputFormat.currentIndexChanged.connect(self._updateFormat)
|
||||
|
||||
optIdx = self.outputFormat.findData(self.optState.getSetting("eFormat"))
|
||||
@@ -371,7 +384,7 @@ class GuiExportMain(QWidget):
|
||||
self.fixedWidth.setMaximum(999)
|
||||
self.fixedWidth.setSingleStep(1)
|
||||
self.fixedWidth.setValue(self.optState.getSetting("fixWidth"))
|
||||
self.fixedWidth.setToolTip("0 disables the feature. Applies to .txt, .md and .tex files.")
|
||||
self.fixedWidth.setToolTip("Applies to .txt, .md and .tex files. 0 disables the feature.")
|
||||
|
||||
self.addSettingsForm.addWidget(QLabel("Fixed width"), 0, 0)
|
||||
self.addSettingsForm.addWidget(self.fixedWidth, 0, 1)
|
||||
@@ -413,11 +426,13 @@ class GuiExportMain(QWidget):
|
||||
currDir = ""
|
||||
|
||||
extFilter = [
|
||||
"novelWriter document files (*.nwd)",
|
||||
"Text files (*.txt)",
|
||||
"Markdown files (*.md)",
|
||||
"HTML files (*.htm *.html)",
|
||||
"Open document files (*.odt)",
|
||||
# "Open document files (*.odt)",
|
||||
"LaTeX files (*.tex)",
|
||||
"All files (*.*)",
|
||||
]
|
||||
|
||||
dlgOpt = QFileDialog.Options()
|
||||
@@ -450,7 +465,7 @@ class ExportLastState(OptLastState):
|
||||
self.theState = {
|
||||
"wNovel" : True,
|
||||
"wNotes" : False,
|
||||
"eFormat" : 2,
|
||||
"eFormat" : 1,
|
||||
"fixWidth" : 80,
|
||||
"wComments" : False,
|
||||
"chFormat" : "Chapter %numword%",
|
||||
|
||||
Reference in New Issue
Block a user