1 # markdown is released under the BSD license 2 # Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later) 3 # Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) 4 # Copyright 2004 Manfred Stienstra (the original version) 5 # 6 # All rights reserved. 7 # 8 # Redistribution and use in source and binary forms, with or without 9 # modification, are permitted provided that the following conditions are met: 10 # 11 # * Redistributions of source code must retain the above copyright 12 # notice, this list of conditions and the following disclaimer. 13 # * Redistributions in binary form must reproduce the above copyright 14 # notice, this list of conditions and the following disclaimer in the 15 # documentation and/or other materials provided with the distribution. 16 # * Neither the name of the <organization> nor the 17 # names of its contributors may be used to endorse or promote products 18 # derived from this software without specific prior written permission. 19 # 20 # THIS SOFTWARE IS PROVIDED BY THE PYTHON MARKDOWN PROJECT ''AS IS'' AND ANY 21 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 # DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTORS TO THE PYTHON MARKDOWN PROJECT 24 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 # POSSIBILITY OF SUCH DAMAGE. 31 32 33 """ 34 POST-PROCESSORS 35 ============================================================================= 36 37 Markdown also allows post-processors, which are similar to preprocessors in 38 that they need to implement a "run" method. However, they are run after core 39 processing. 40 41 """ 42 43 from __future__ import absolute_import 44 from __future__ import unicode_literals 45 from . import util 46 from . import odict 47 import re 48 49 50 def build_postprocessors(md_instance, **kwargs): 51 """ Build the default postprocessors for Markdown. """ 52 postprocessors = odict.OrderedDict() 53 postprocessors["raw_html"] = RawHtmlPostprocessor(md_instance) 54 postprocessors["amp_substitute"] = AndSubstitutePostprocessor() 55 postprocessors["unescape"] = UnescapePostprocessor() 56 return postprocessors 57 58 59 class Postprocessor(util.Processor): 60 """ 61 Postprocessors are run after the ElementTree it converted back into text. 62 63 Each Postprocessor implements a "run" method that takes a pointer to a 64 text string, modifies it as necessary and returns a text string. 65 66 Postprocessors must extend markdown.Postprocessor. 67 68 """ 69 70 def run(self, text): 71 """ 72 Subclasses of Postprocessor should implement a `run` method, which 73 takes the html document as a single text string and returns a 74 (possibly modified) string. 75 76 """ 77 pass 78 79 80 class RawHtmlPostprocessor(Postprocessor): 81 """ Restore raw html to the document. """ 82 83 def run(self, text): 84 """ Iterate over html stash and restore "safe" html. """ 85 for i in range(self.markdown.htmlStash.html_counter): 86 html, safe = self.markdown.htmlStash.rawHtmlBlocks[i] 87 if self.markdown.safeMode and not safe: 88 if str(self.markdown.safeMode).lower() == 'escape': 89 html = self.escape(html) 90 elif str(self.markdown.safeMode).lower() == 'remove': 91 html = '' 92 else: 93 html = self.markdown.html_replacement_text 94 if self.isblocklevel(html) and (safe or not self.markdown.safeMode): 95 text = text.replace("<p>%s</p>" % 96 (self.markdown.htmlStash.get_placeholder(i)), 97 html + "\n") 98 text = text.replace(self.markdown.htmlStash.get_placeholder(i), 99 html) 100 return text 101 102 def escape(self, html): 103 """ Basic html escaping """ 104 html = html.replace('&', '&') 105 html = html.replace('<', '<') 106 html = html.replace('>', '>') 107 return html.replace('"', '"') 108 109 def isblocklevel(self, html): 110 m = re.match(r'^\<\/?([^ >]+)', html) 111 if m: 112 if m.group(1)[0] in ('!', '?', '@', '%'): 113 # Comment, php etc... 114 return True 115 return util.isBlockLevel(m.group(1)) 116 return False 117 118 119 class AndSubstitutePostprocessor(Postprocessor): 120 """ Restore valid entities """ 121 122 def run(self, text): 123 text = text.replace(util.AMP_SUBSTITUTE, "&") 124 return text 125 126 127 class UnescapePostprocessor(Postprocessor): 128 """ Restore escaped chars """ 129 130 RE = re.compile('%s(\d+)%s' % (util.STX, util.ETX)) 131 132 def unescape(self, m): 133 return util.int2str(int(m.group(1))) 134 135 def run(self, text): 136 return self.RE.sub(self.unescape, text) 137