-
Notifications
You must be signed in to change notification settings - Fork 15
Pandoc filter execute #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
22cf37b
35e828f
1444ffa
aecb596
1fe39bd
c1f0726
2a8cc1c
94dde06
0ab87b4
8cb897a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| #!/bin/env python | ||
|
|
||
| """ | ||
| Pandoc filter to run all imagemagick code in code blocks | ||
| """ | ||
|
|
||
| import os | ||
| import sys | ||
| import hashlib | ||
| from pandocfilters import toJSONFilter, Image, CodeBlock, Para | ||
| from subprocess import Popen, PIPE | ||
|
|
||
| DEBUG = False | ||
|
|
||
| IMAGEDIR = '_images' | ||
|
|
||
| # List of imagemagick tools; only commands in this list will be allowed to be run by the filter | ||
| IM_COMMANDS = ('animate', 'compare', 'composite', 'conjure', 'convert', 'display', 'identify', 'import', 'mogrify', 'montage', 'stream', 'echo', 'printf') | ||
| IM_IMAGE_TYPES = ('png', 'jpg', 'gif', 'tif') | ||
|
|
||
| # Needed for writing unicode to stdout/stderr: | ||
| reload(sys) | ||
| sys.setdefaultencoding('utf-8') | ||
|
|
||
| def sha1(x): | ||
| return hashlib.sha1(x.encode(sys.getfilesystemencoding())).hexdigest() | ||
|
|
||
| def execute(code): | ||
| """ | ||
| Executes the code. | ||
| Performs some sanity checks first. | ||
| """ | ||
| # if DEBUG: | ||
| # sys.stderr.write('-'*100 + '\n') | ||
| code = remove_slash(code) | ||
| for line in code: | ||
| if DEBUG: | ||
| sys.stderr.write(line + u'\n') | ||
| commandline = line.split() | ||
| if commandline[0] not in IM_COMMANDS: | ||
| sys.stderr.write("Not a ImageMagick command: %s\n" % commandline[0]) | ||
| return | ||
| image_name = commandline[-1] | ||
| image_type = os.path.splitext(image_name)[1][1:].lower() | ||
| if os.path.isfile(image_name): | ||
| sys.stderr.write("Image already exists: '%s'\n" % image_name) | ||
| return os.path.join('..', IMAGEDIR, image_name) | ||
| if image_type not in IM_IMAGE_TYPES: | ||
| sys.stderr.write("Not a ImageMagick Image Type: %s\n" % image_type) | ||
| return | ||
| p = Popen(' '.join(commandline), close_fds=True, stdout=PIPE, stderr=PIPE, shell=True) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If shell is true, then write all (unaltered codeblock) to |
||
| out, err = p.communicate() | ||
| if out and DEBUG: | ||
| sys.stderr.write(out + '\n') | ||
| if err: | ||
| sys.stderr.write(err + '\n') | ||
| return os.path.join('..', IMAGEDIR, image_name) | ||
|
|
||
| def remove_slash(text): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another YAGNI. If pandoc content is JSON, then we should use pythons internal |
||
| """ | ||
| Joins lines, removing line-extending Backslashes ('\') from the text code | ||
| :returns: list of strings | ||
| """ | ||
| retval = [] | ||
| cummulative_line = '' | ||
| for line in text.split('\n'): | ||
| line = line.strip() | ||
| if line.endswith('\\'): | ||
| line = line.strip('\\') | ||
| cummulative_line += line | ||
| else: | ||
| cummulative_line += line | ||
| # execute(cummulative_line) | ||
| retval.append(cummulative_line) | ||
| cummulative_line = '' | ||
| return retval | ||
|
|
||
| def magick(key, value, format, meta): | ||
| """ | ||
| Filter to scan CodeBlocks for ImageMagick code and execute it to generate images. | ||
|
|
||
| Example CodeBlock: | ||
| ~~~{generate_image=True include_image=False} | ||
| convert -size 40x20 xc:red xc:blue -append -rotate 90 append_rotate.gif | ||
| ~~~ | ||
|
|
||
| All images are created in the '_images' directory (might need to be created first) | ||
| Existing images will not be overwritten - we might want to add a command to the makefile like clean_images | ||
|
|
||
| The filter will not blindly execute any codeblock, but expects a key/value pair: | ||
|
|
||
| generate_image [True|False] | ||
| Another switch enables the automatic generation of HTML code including the tag for the generated image: | ||
|
|
||
| include_image [True|False] | ||
|
|
||
| Example CodeBlock: | ||
|
|
||
| ~~~{generate_image=True include_image=False} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If these values will be boolean in nature, then I would argue them as flags, and can be classnames The the check would be as simple as.. if 'generate_image' in classes:
# Do work |
||
| convert -size 40x20 xc:red xc:blue -append -rotate 90 append_rotate.gif | ||
| ~~~ | ||
|
|
||
| Note: Using fenced code blocks | ||
| see http://pandoc.org/README.html#fenced-code-blocks | ||
| and https://github.com/jgm/pandoc/issues/673 | ||
|
|
||
| Current limitations: | ||
|
|
||
| - Image names need to be unique throughout the whole tree. | ||
| - Complex commands, such as using perl/grep/pipes etc. | ||
| - Will need to change image location for those examples which already explicitly include the image (ex. examples at the top of the antialising file) | ||
| """ | ||
| if key == 'CodeBlock': | ||
| [[ident, classes, keyvals], code] = value | ||
| keyvals = dict(keyvals) | ||
| if keyvals and keyvals.has_key('generate_image') and keyvals['generate_image']: | ||
|
|
||
| # outfile = os.path.join(IMAGEDIR, sha1(code)) | ||
| # if format == "latex": | ||
| # filetype = "pdf" | ||
| # else: | ||
| # filetype = "png" | ||
| # src = outfile + '.' + filetype | ||
| if not os.path.isdir(IMAGEDIR): | ||
| try: | ||
| os.mkdir(IMAGEDIR) | ||
| sys.stderr.write('Created directory %s' % IMAGEDIR) | ||
| except OSError: | ||
| sys.stderr.write('Could not create directory %s' % IMAGEDIR) | ||
| return | ||
| # sys.stderr.write("classes: " + ','.join(classes) + '\n') | ||
| # sys.stderr.write(str(zip(keyvals)) + '\n') | ||
|
|
||
| # sys.stderr.write('Created image ' + src + '\n') | ||
| prev_dir = os.getcwd() | ||
| os.chdir(IMAGEDIR) | ||
| image = execute(code) | ||
| os.chdir(prev_dir) | ||
| if image: | ||
| if keyvals and keyvals.has_key('include_image') and keyvals['include_image']: | ||
| return [CodeBlock(("", [], []), code), Para([Image([], [image, "generated my ImageMagick"])])] | ||
| else: | ||
| return [CodeBlock(("", [], []), code)] | ||
| else: | ||
| return | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| toJSONFilter(magick) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
YAGNI. We can assume all flagged are intended for shell consumption. No need to white-list anything.