Skip to content

Commit

Permalink
PR #552 + black all files
Browse files Browse the repository at this point in the history
  • Loading branch information
elapouya committed Jul 21, 2024
1 parent f3ba468 commit a50b52b
Show file tree
Hide file tree
Showing 43 changed files with 946 additions and 652 deletions.
2 changes: 1 addition & 1 deletion docxtpl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
@author: Eric Lapouyade
"""
__version__ = '0.17.0'
__version__ = "0.17.0"

# flake8: noqa
from .inline_image import InlineImage
Expand Down
107 changes: 65 additions & 42 deletions docxtpl/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,41 @@

from .template import DocxTemplate, TemplateError

TEMPLATE_ARG = 'template_path'
JSON_ARG = 'json_path'
OUTPUT_ARG = 'output_filename'
OVERWRITE_ARG = 'overwrite'
QUIET_ARG = 'quiet'
TEMPLATE_ARG = "template_path"
JSON_ARG = "json_path"
OUTPUT_ARG = "output_filename"
OVERWRITE_ARG = "overwrite"
QUIET_ARG = "quiet"


def make_arg_parser():
parser = argparse.ArgumentParser(
usage='python -m docxtpl [-h] [-o] [-q] {} {} {}'.format(TEMPLATE_ARG, JSON_ARG, OUTPUT_ARG),
description='Make docx file from existing template docx and json data.')
parser.add_argument(TEMPLATE_ARG,
type=str,
help='The path to the template docx file.')
parser.add_argument(JSON_ARG,
type=str,
help='The path to the json file with the data.')
parser.add_argument(OUTPUT_ARG,
type=str,
help='The filename to save the generated docx.')
parser.add_argument('-' + OVERWRITE_ARG[0], '--' + OVERWRITE_ARG,
action='store_true',
help='If output file already exists, overwrites without asking for confirmation')
parser.add_argument('-' + QUIET_ARG[0], '--' + QUIET_ARG,
action='store_true',
help='Do not display unnecessary messages')
usage="python -m docxtpl [-h] [-o] [-q] {} {} {}".format(
TEMPLATE_ARG, JSON_ARG, OUTPUT_ARG
),
description="Make docx file from existing template docx and json data.",
)
parser.add_argument(
TEMPLATE_ARG, type=str, help="The path to the template docx file."
)
parser.add_argument(
JSON_ARG, type=str, help="The path to the json file with the data."
)
parser.add_argument(
OUTPUT_ARG, type=str, help="The filename to save the generated docx."
)
parser.add_argument(
"-" + OVERWRITE_ARG[0],
"--" + OVERWRITE_ARG,
action="store_true",
help="If output file already exists, overwrites without asking for confirmation",
)
parser.add_argument(
"-" + QUIET_ARG[0],
"--" + QUIET_ARG,
action="store_true",
help="Do not display unnecessary messages",
)
return parser


Expand All @@ -43,18 +52,21 @@ def get_args(parser):
if e.code == 0:
raise SystemExit
else:
raise RuntimeError('Correct usage is:\n{parser.usage}'.format(parser=parser))
raise RuntimeError(
"Correct usage is:\n{parser.usage}".format(parser=parser)
)


def is_argument_valid(arg_name, arg_value, overwrite):
# Basic checks for the arguments
if arg_name == TEMPLATE_ARG:
return os.path.isfile(arg_value) and arg_value.endswith('.docx')
return os.path.isfile(arg_value) and arg_value.endswith(".docx")
elif arg_name == JSON_ARG:
return os.path.isfile(arg_value) and arg_value.endswith('.json')
return os.path.isfile(arg_value) and arg_value.endswith(".json")
elif arg_name == OUTPUT_ARG:
return arg_value.endswith('.docx') and check_exists_ask_overwrite(
arg_value, overwrite)
return arg_value.endswith(".docx") and check_exists_ask_overwrite(
arg_value, overwrite
)
elif arg_name in [OVERWRITE_ARG, QUIET_ARG]:
return arg_value in [True, False]

Expand All @@ -65,13 +77,18 @@ def check_exists_ask_overwrite(arg_value, overwrite):
# confirmed returns True, else raises OSError.
if os.path.exists(arg_value) and not overwrite:
try:
msg = 'File %s already exists, would you like to overwrite the existing file? (y/n)' % arg_value
if input(msg).lower() == 'y':
msg = (
"File %s already exists, would you like to overwrite the existing file? (y/n)"
% arg_value
)
if input(msg).lower() == "y":
return True
else:
raise OSError
except OSError:
raise RuntimeError('File %s already exists, please choose a different name.' % arg_value)
raise RuntimeError(
"File %s already exists, please choose a different name." % arg_value
)
else:
return True

Expand All @@ -87,7 +104,8 @@ def validate_all_args(parsed_args):
raise RuntimeError(
'The specified {arg_name} "{arg_value}" is not valid.'.format(
arg_name=arg_name, arg_value=arg_value
))
)
)


def get_json_data(json_path):
Expand All @@ -97,36 +115,41 @@ def get_json_data(json_path):
return json_data
except json.JSONDecodeError as e:
print(
'There was an error on line {e.lineno}, column {e.colno} while trying to parse file {json_path}'.format(
"There was an error on line {e.lineno}, column {e.colno} while trying to parse file {json_path}".format(
e=e, json_path=json_path
))
raise RuntimeError('Failed to get json data.')
)
)
raise RuntimeError("Failed to get json data.")


def make_docxtemplate(template_path):
try:
return DocxTemplate(template_path)
except TemplateError:
raise RuntimeError('Could not create docx template.')
raise RuntimeError("Could not create docx template.")


def render_docx(doc, json_data):
try:
doc.render(json_data)
return doc
except TemplateError:
raise RuntimeError('An error ocurred while trying to render the docx')
raise RuntimeError("An error ocurred while trying to render the docx")


def save_file(doc, parsed_args):
try:
output_path = parsed_args[OUTPUT_ARG]
doc.save(output_path)
if not parsed_args[QUIET_ARG]:
print('Document successfully generated and saved at {output_path}'.format(output_path=output_path))
print(
"Document successfully generated and saved at {output_path}".format(
output_path=output_path
)
)
except OSError as e:
print('{e.strerror}. Could not save file {e.filename}.'.format(e=e))
raise RuntimeError('Failed to save file.')
print("{e.strerror}. Could not save file {e.filename}.".format(e=e))
raise RuntimeError("Failed to save file.")


def main():
Expand All @@ -142,12 +165,12 @@ def main():
doc = render_docx(doc, json_data)
save_file(doc, parsed_args)
except RuntimeError as e:
print('Error: '+e.__str__())
print("Error: " + e.__str__())
return
finally:
if not parsed_args[QUIET_ARG]:
print('Exiting program!')
print("Exiting program!")


if __name__ == '__main__':
if __name__ == "__main__":
main()
32 changes: 21 additions & 11 deletions docxtpl/inline_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
from docx.oxml import OxmlElement, parse_xml
from docx.oxml.ns import qn


class InlineImage(object):
"""Class to generate an inline image
This is much faster than using Subdoc class.
"""

tpl = None
image_descriptor = None
width = None
Expand All @@ -25,17 +27,21 @@ def __init__(self, tpl, image_descriptor, width=None, height=None, anchor=None):

def _add_hyperlink(self, run, url, part):
# Create a relationship for the hyperlink
r_id = part.relate_to(url, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', is_external=True)
r_id = part.relate_to(
url,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
is_external=True,
)

# Find the <wp:docPr> and <pic:cNvPr> element
docPr = run.xpath('.//wp:docPr')[0]
cNvPr = run.xpath('.//pic:cNvPr')[0]
docPr = run.xpath(".//wp:docPr")[0]
cNvPr = run.xpath(".//pic:cNvPr")[0]

# Create the <a:hlinkClick> element
hlinkClick1 = OxmlElement('a:hlinkClick')
hlinkClick1.set(qn('r:id'), r_id)
hlinkClick2 = OxmlElement('a:hlinkClick')
hlinkClick2.set(qn('r:id'), r_id)
hlinkClick1 = OxmlElement("a:hlinkClick")
hlinkClick1.set(qn("r:id"), r_id)
hlinkClick2 = OxmlElement("a:hlinkClick")
hlinkClick2.set(qn("r:id"), r_id)

# Insert the <a:hlinkClick> element right after the <wp:docPr> element
docPr.append(hlinkClick1)
Expand All @@ -51,12 +57,16 @@ def _insert_image(self):
).xml
if self.anchor:
run = parse_xml(pic)
if run.xpath('.//a:blip'):
hyperlink = self._add_hyperlink(run, self.anchor, self.tpl.current_rendering_part)
if run.xpath(".//a:blip"):
hyperlink = self._add_hyperlink(
run, self.anchor, self.tpl.current_rendering_part
)
pic = hyperlink.xml

return '</w:t></w:r><w:r><w:drawing>%s</w:drawing></w:r><w:r>' \
'<w:t xml:space="preserve">' % pic
return (
"</w:t></w:r><w:r><w:drawing>%s</w:drawing></w:r><w:r>"
'<w:t xml:space="preserve">' % pic
)

def __unicode__(self):
return self._insert_image()
Expand Down
2 changes: 2 additions & 0 deletions docxtpl/listing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@author: Eric Lapouyade
"""
import six

try:
from html import escape
except ImportError:
Expand All @@ -19,6 +20,7 @@ class Listing(object):
use {{ mylisting }} in your template and
context={ mylisting:Listing(the_listing_with_newlines) }
"""

def __init__(self, text):
# If not a string : cast to string (ex: int, dict etc...)
if not isinstance(text, (six.text_type, six.binary_type)):
Expand Down
Loading

0 comments on commit a50b52b

Please sign in to comment.