plantuml_md.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. #!/usr/bin/env python
  2. """
  3. PlantUML_ Extension for Python-Markdown_
  4. ========================================
  5. Syntax:
  6. ::uml:: [format="png|svg"] [classes="class1 class2 ..."] [alt="text for alt"]
  7. PlantUML script diagram
  8. ::end-uml::
  9. Example:
  10. ::uml:: format="png" classes="uml myDiagram" alt="My super diagram"
  11. Goofy -> MickeyMouse: calls
  12. Goofy <-- MickeyMouse: responds
  13. ::end-uml::
  14. Options are optional, but if present must be specified in the order format, classes, alt.
  15. The option value may be enclosed in single or double quotes.
  16. .. _Python-Markdown: http://pythonhosted.org/Markdown/
  17. .. _PlantUML: http://plantuml.sourceforge.net/
  18. """
  19. import os
  20. import re
  21. import markdown
  22. from markdown.util import etree
  23. from .generateUmlDiagram import generate_uml_image
  24. # For details see https://pythonhosted.org/Markdown/extensions/api.html#blockparser
  25. class PlantUMLBlockProcessor(markdown.blockprocessors.BlockProcessor):
  26. # Regular expression inspired by the codehilite Markdown plugin
  27. RE = re.compile(r'''::uml::
  28. \s*(format=(?P<quot>"|')(?P<format>\w+)(?P=quot))?
  29. \s*(classes=(?P<quot1>"|')(?P<classes>[\w\s]+)(?P=quot1))?
  30. \s*(alt=(?P<quot2>"|')(?P<alt>[\w\s"']+)(?P=quot2))?
  31. ''', re.VERBOSE)
  32. # Regular expression for identify end of UML script
  33. RE_END = re.compile(r'::end-uml::\s*$')
  34. def test(self, parent, block):
  35. return self.RE.search(block)
  36. def run(self, parent, blocks):
  37. block = blocks.pop(0)
  38. text = block
  39. # Parse configuration params
  40. m = self.RE.search(block)
  41. format = m.group('format') if m.group('format') else self.config['format']
  42. classes = m.group('classes') if m.group('classes') else self.config['classes']
  43. alt = m.group('alt') if m.group('alt') else self.config['alt']
  44. # Read blocks until end marker found
  45. while blocks and not self.RE_END.search(block):
  46. block = blocks.pop(0)
  47. text = text + '\n' + block
  48. else:
  49. if not blocks:
  50. raise RuntimeError("[plantuml] UML block not closed, text is:\n"+text)
  51. # Remove block header and footer
  52. text = re.sub(self.RE, "", re.sub(self.RE_END, "", text))
  53. path = os.path.abspath(os.path.join('output', 'images'))
  54. if not os.path.exists(path):
  55. os.makedirs(path)
  56. # Generate image from PlantUML script
  57. imageurl = self.config['siteurl']+'/'+generate_uml_image(path, text, format)
  58. # Create image tag and append to the document
  59. etree.SubElement(parent, "img", src=imageurl, alt=alt, attrib={'class':classes})
  60. # For details see https://pythonhosted.org/Markdown/extensions/api.html#extendmarkdown
  61. class PlantUMLMarkdownExtension(markdown.Extension):
  62. # For details see https://pythonhosted.org/Markdown/extensions/api.html#configsettings
  63. def __init__(self, *args, **kwargs):
  64. self.config = {
  65. 'classes': ["uml","Space separated list of classes for the generated image. Default uml."],
  66. 'alt' : ["uml diagram", "Text to show when image is not available."],
  67. 'format' : ["png", "Format of image to generate (png or svg). Default png."],
  68. 'siteurl': ["", "URL of document, used as a prefix for the image diagram."]
  69. }
  70. super(PlantUMLMarkdownExtension, self).__init__(*args, **kwargs)
  71. def extendMarkdown(self, md, md_globals):
  72. blockprocessor = PlantUMLBlockProcessor(md.parser)
  73. blockprocessor.config = self.getConfigs()
  74. md.parser.blockprocessors.add('plantuml', blockprocessor, '>code')
  75. def makeExtension(**kwargs):
  76. return PlantUMLMarkdownExtension(**kwargs)