Browse Source

Initial Commit

master
Dmytro Yeroshkin 1 week ago
parent
commit
bdb2d54159
  1. 146
      novel_compiler/novel_compiler.py
  2. 6
      novel_compiler/settings.json
  3. BIN
      novel_compiler/template.docx

146
novel_compiler/novel_compiler.py

@ -0,0 +1,146 @@
import argparse
from docxtpl import DocxTemplate, RichText
import markdown
from novel_stats.novel_stats import count_words
import tempfile
import MarkdownPP
import json
TITLE_MARKER = '# '
AUTHOR_MARKER = '### '
CHAPTER_MARKER = '## '
STATUS_MARKER = '[status]: # '
ACT_MARKER = '[act]: # '
COMMENT_MARKER = '[//]: # ' # Strandard markdown comment marker, supported by pandoc and calibre's ebook-convert
class Chapter:
def __init__(self, heading):
self.heading = heading
self.paragraphs = []
def md_re_parser(md_paragraph):
# Correct xml tags
pre = '<w:r><w:t xml:space="preserve">'
post = '</w:t></w:r>'
it_pre = '<w:r><w:rPr><w:i/></w:rPr><w:t xml:space="preserve">'
bf_pre = '<w:r><w:rPr><w:b/></w:rPr><w:t xml:space="preserve">'
bfit_pre = '<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">'
# Tag replacement
html = markdown.markdown(md_paragraph)
html = html.replace('<p>', pre)
html = html.replace('</p>', post)
html = html.replace('<strong><em>', post+bfit_pre)
html = html.replace('</strong></em>', post+pre)
html = html.replace('<em>', post+it_pre)
html = html.replace('</em>', post+pre)
html = html.replace('<strong>', post+bf_pre)
html = html.replace('</strong>', post+pre)
# xml cleanup
while pre+post in html:
html = html.replace(pre+post,'')
# convert to a rich text paragraph
par = RichText()
par.xml = html
if len(html) == 0:
print(md_paragraph)
return par
def novel_parser(source_file, context = None):
if not context:
context = {'author_address': 'Street\nTown, State ZIP\nCountry',
'author_email': 'name@email.com',
'author_phone': 'PhoneNumber(s)',
'author_website': 'https://www.author.com'}
context['chapters'] = []
wc = 0
chapter = Chapter('')
for line in source_file:
if line.startswith(TITLE_MARKER):
title = line[len(TITLE_MARKER):].strip('()\n')
context['project_title'] = title
wc += count_words(title)
elif line.startswith(AUTHOR_MARKER):
author_name = line[len(AUTHOR_MARKER):].strip('()\n')
if 'author_name' not in context:
context['author_name'] = author_name
context['penname'] = author_name
elif line.startswith(CHAPTER_MARKER):
if chapter.heading or chapter.paragraphs:
context['chapters'].append(chapter)
chapter = Chapter(line[len(CHAPTER_MARKER):].strip('()\n'))
wc += count_words(chapter.heading)
elif line.startswith(STATUS_MARKER) or line.startswith(ACT_MARKER) or line.startswith(COMMENT_MARKER):
pass
else:
stripped = line.strip()
if stripped:
wc += count_words(stripped)
chapter.paragraphs.append(md_re_parser(stripped))
context['chapters'].append(chapter)
source_file.close()
context['wc_1000'] = f'{(wc//1000)*1000:,}'
if 'header' not in context:
context['header'] = context['author_name'].split()[-1] + ' - ' + context['project_title'] + ' - '
return context
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'markdown_file',
type=argparse.FileType('r'),
help='The markdown file for the novel, main file if a multi-file novel',
)
parser.add_argument(
'-pp',
action='store_true',
help='run markdown pre-processor, this allows for a multi-file input (e.g. each chapter in its own file), but requires the MarkdownPP python library',
)
parser.add_argument(
'--settings',
'-s',
type=argparse.FileType('r'),
help='setting json file',
)
parser.add_argument(
'--template',
'-t',
type=argparse.FileType('r'),
help='template docx file',
)
parser.add_argument(
'--output',
'-o',
type=argparse.FileType('w'),
help='output docx file',
)
arguments = parser.parse_args()
arguments.template.close()
doc = DocxTemplate(arguments.template.name)
if arguments.pp:
mdfile = tempfile.TemporaryFile(mode='w+')
MarkdownPP.MarkdownPP(
input=arguments.markdown_file, output=mdfile, modules=list(MarkdownPP.modules)
)
mdfile.seek(0)
else:
mdfile = arguments.markdown_file
if arguments.settings:
context = json.load(arguments.settings)
else:
context = None
context = novel_parser(mdfile, context = context)
doc.render(context)
arguments.output.close()
doc.save(arguments.output.name)
if __name__ == '__main__':
main()

6
novel_compiler/settings.json

@ -0,0 +1,6 @@
{
"author_address": "Address",
"author_email": "Email",
"author_phone": "Phone number",
"author_website": "Website"
}

BIN
novel_compiler/template.docx

Binary file not shown.
Loading…
Cancel
Save