diff --git a/src/markdown_syntax.py b/src/markdown_syntax.py index 801b4a8e..a9e0c478 100644 --- a/src/markdown_syntax.py +++ b/src/markdown_syntax.py @@ -8,10 +8,12 @@ CODE_BLOCK_START = r"```([a-z]+)" CODE_BLOCK_END = r"```" HEADING_PATTERN = r"^(#+) (.*)" -IMAGE_PATTERN = r'^!\[([^\]]+)\]\(([^ ]+) "([^\)]+)"\)' +# The title is delimited by the quotes, so it may contain any character except a quote (e.g. parentheses) +IMAGE_PATTERN = r'^!\[([^\]]+)\]\(([^ ]+) "([^"]+)"\)' ITALIC_START = ITALIC_END = "_" ITALIC_ALTERNATIVE_START = ITALIC_ALTERNATIVE_END = "*" -LINK_PATTERN = r"^\[([^\]]+)\]\(([^\)]+)\)" +# The URL may contain balanced parentheses (e.g. https://en.wikipedia.org/wiki/Foo_(disambiguation)), one level deep +LINK_PATTERN = r"^\[([^\]]+)\]\(((?:[^()]|\([^()]*\))*)\)" NUMBERED_LIST_PATTERN = r"^[0-9A-Za-z]+\. " STRIKETROUGH_START = STRIKETROUGH_END = "~~" TABLE_MARKER = "|" diff --git a/tests/test_markdown_converter.py b/tests/test_markdown_converter.py index 7a515e65..d31bb606 100644 --- a/tests/test_markdown_converter.py +++ b/tests/test_markdown_converter.py @@ -147,6 +147,13 @@ def test_image(self): self.assertEqual("image.png", image.attrib[xmltags.IMAGE_SRC]) self.assertEqual("description", image.attrib[xmltags.IMAGE_TITLE]) + @patch(read_text, Mock(return_value='![Anchor](image_(1).png "Figure 1 (cropped)")')) + def test_image_with_brackets(self): + """Test an image whose source and title contain brackets.""" + image = self.find(f".//{xmltags.IMAGE}") + self.assertEqual("image_(1).png", image.attrib[xmltags.IMAGE_SRC]) + self.assertEqual("Figure 1 (cropped)", image.attrib[xmltags.IMAGE_TITLE]) + @patch(read_text, Mock(return_value="* Bullet\n* list\n\n")) def test_bullet_list(self): """Test bullet list.""" @@ -227,3 +234,17 @@ def test_code_blok(self): """Test that a code block can be included in the Markdown.""" code_block = self.find(xmltags.CODE_BLOCK) self.assertEqual("print('Hello world')\n", code_block.text) + + @patch(read_text, Mock(return_value="See: [more info](https://more.info)\n")) + def test_url(self): + """Test a URL.""" + paragraph = self.find(xmltags.PARAGRAPH) + anchor_link = self.find(xmltags.ANCHOR, paragraph).attrib[xmltags.ANCHOR_LINK] + self.assertEqual("https://more.info", anchor_link) + + @patch(read_text, Mock(return_value="See: [more info](https://en.wikipedia.org/wiki/ZAP_(software))\n")) + def test_url_with_brackets(self): + """Test a URL with brackets.""" + paragraph = self.find(xmltags.PARAGRAPH) + anchor_link = self.find(xmltags.ANCHOR, paragraph).attrib[xmltags.ANCHOR_LINK] + self.assertEqual("https://en.wikipedia.org/wiki/ZAP_(software)", anchor_link)