From bdb2d54159906ad6108bfeb4202c1723b3b96afc Mon Sep 17 00:00:00 2001 From: Dmytro Yeroshkin Date: Tue, 11 Jan 2022 17:01:20 +0100 Subject: [PATCH] Initial Commit --- novel_compiler/novel_compiler.py | 146 +++++++++++++++++++++++++++++++ novel_compiler/settings.json | 6 ++ novel_compiler/template.docx | Bin 0 -> 21940 bytes 3 files changed, 152 insertions(+) create mode 100644 novel_compiler/novel_compiler.py create mode 100644 novel_compiler/settings.json create mode 100644 novel_compiler/template.docx diff --git a/novel_compiler/novel_compiler.py b/novel_compiler/novel_compiler.py new file mode 100644 index 0000000..f2ac649 --- /dev/null +++ b/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 = '' + post = '' + it_pre = '' + bf_pre = '' + bfit_pre = '' + + # Tag replacement + html = markdown.markdown(md_paragraph) + html = html.replace('

', pre) + html = html.replace('

', post) + html = html.replace('', post+bfit_pre) + html = html.replace('', post+pre) + html = html.replace('', post+it_pre) + html = html.replace('', post+pre) + html = html.replace('', post+bf_pre) + html = html.replace('', 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() \ No newline at end of file diff --git a/novel_compiler/settings.json b/novel_compiler/settings.json new file mode 100644 index 0000000..908ec34 --- /dev/null +++ b/novel_compiler/settings.json @@ -0,0 +1,6 @@ +{ +"author_address": "Address", +"author_email": "Email", +"author_phone": "Phone number", +"author_website": "Website" +} \ No newline at end of file diff --git a/novel_compiler/template.docx b/novel_compiler/template.docx new file mode 100644 index 0000000000000000000000000000000000000000..801e5e50f6a24f9a95f47b2c9b0240361704f2b2 GIT binary patch literal 21940 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