markdown-to-confluence 0.5.3__py3-none-any.whl → 0.5.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {markdown_to_confluence-0.5.3.dist-info → markdown_to_confluence-0.5.5.dist-info}/METADATA +275 -208
  2. markdown_to_confluence-0.5.5.dist-info/RECORD +57 -0
  3. {markdown_to_confluence-0.5.3.dist-info → markdown_to_confluence-0.5.5.dist-info}/WHEEL +1 -1
  4. md2conf/__init__.py +1 -1
  5. md2conf/__main__.py +61 -189
  6. md2conf/api.py +35 -69
  7. md2conf/attachment.py +4 -3
  8. md2conf/clio.py +226 -0
  9. md2conf/compatibility.py +5 -0
  10. md2conf/converter.py +239 -147
  11. md2conf/csf.py +89 -9
  12. md2conf/drawio/extension.py +3 -3
  13. md2conf/drawio/render.py +2 -0
  14. md2conf/extension.py +4 -0
  15. md2conf/external.py +25 -8
  16. md2conf/frontmatter.py +18 -6
  17. md2conf/image.py +17 -14
  18. md2conf/latex.py +8 -1
  19. md2conf/markdown.py +68 -1
  20. md2conf/mermaid/render.py +1 -1
  21. md2conf/options.py +95 -24
  22. md2conf/plantuml/extension.py +7 -7
  23. md2conf/plantuml/render.py +6 -7
  24. md2conf/png.py +10 -6
  25. md2conf/processor.py +24 -3
  26. md2conf/publisher.py +193 -36
  27. md2conf/reflection.py +74 -0
  28. md2conf/scanner.py +16 -6
  29. md2conf/serializer.py +12 -1
  30. md2conf/svg.py +131 -109
  31. md2conf/toc.py +72 -0
  32. md2conf/xml.py +45 -0
  33. markdown_to_confluence-0.5.3.dist-info/RECORD +0 -55
  34. {markdown_to_confluence-0.5.3.dist-info → markdown_to_confluence-0.5.5.dist-info}/entry_points.txt +0 -0
  35. {markdown_to_confluence-0.5.3.dist-info → markdown_to_confluence-0.5.5.dist-info}/licenses/LICENSE +0 -0
  36. {markdown_to_confluence-0.5.3.dist-info → markdown_to_confluence-0.5.5.dist-info}/top_level.txt +0 -0
  37. {markdown_to_confluence-0.5.3.dist-info → markdown_to_confluence-0.5.5.dist-info}/zip-safe +0 -0
  38. /md2conf/{puppeteer-config.json → mermaid/puppeteer-config.json} +0 -0
md2conf/svg.py CHANGED
@@ -9,6 +9,7 @@ Copyright 2022-2026, Levente Hunyadi
9
9
  import logging
10
10
  import re
11
11
  from pathlib import Path
12
+ from typing import overload
12
13
 
13
14
  import lxml.etree as ET
14
15
 
@@ -19,6 +20,10 @@ LOGGER = logging.getLogger(__name__)
19
20
  SVG_NAMESPACE = "http://www.w3.org/2000/svg"
20
21
 
21
22
 
23
+ class SVGParseError(RuntimeError):
24
+ pass
25
+
26
+
22
27
  def _check_svg(root: ElementType) -> bool:
23
28
  "Tests if the element is a plain or scoped SVG element."
24
29
 
@@ -31,7 +36,7 @@ def _check_svg(root: ElementType) -> bool:
31
36
  return qname.localname == "svg" and (not qname.namespace or qname.namespace == SVG_NAMESPACE)
32
37
 
33
38
 
34
- def _extract_dimensions_from_root(root: ElementType) -> tuple[int | None, int | None]:
39
+ def _extract_dimensions_from_root(root: ElementType) -> tuple[int, int] | None:
35
40
  """
36
41
  Extracts width and height from an SVG root element.
37
42
 
@@ -40,11 +45,11 @@ def _extract_dimensions_from_root(root: ElementType) -> tuple[int | None, int |
40
45
  2. The viewBox attribute if width/height are not specified
41
46
 
42
47
  :param root: The root element of the SVG document.
43
- :returns: A tuple of (width, height) in pixels, or (None, None) if dimensions cannot be determined.
48
+ :returns: A tuple of (width, height) in pixels, or `None` if dimensions cannot be determined.
44
49
  """
45
50
 
46
51
  if not _check_svg(root):
47
- return None, None
52
+ raise SVGParseError("SVG file does not have an <svg> root element")
48
53
 
49
54
  width_attr = root.get("width")
50
55
  height_attr = root.get("height")
@@ -52,69 +57,86 @@ def _extract_dimensions_from_root(root: ElementType) -> tuple[int | None, int |
52
57
  width = _parse_svg_length(width_attr) if width_attr else None
53
58
  height = _parse_svg_length(height_attr) if height_attr else None
54
59
 
55
- # If width/height not specified, try to derive from viewBox
60
+ # if width/height not specified, try to derive from view-box
61
+ if width is None or height is None:
62
+ viewbox_attr = root.get("viewBox")
63
+ if viewbox_attr:
64
+ viewbox = _parse_viewbox(viewbox_attr)
65
+ if viewbox is not None:
66
+ vb_width, vb_height = viewbox
67
+ if width is not None:
68
+ height = width * vb_height // vb_width
69
+ elif height is not None:
70
+ width = height * vb_width // vb_height
71
+ else:
72
+ width = vb_width
73
+ height = vb_height
74
+
56
75
  if width is None or height is None:
57
- viewbox = root.get("viewBox")
58
- if viewbox:
59
- vb_width, vb_height = _parse_viewbox(viewbox)
60
- if width is None:
61
- width = vb_width
62
- if height is None:
63
- height = vb_height
76
+ return None
64
77
 
65
78
  return width, height
66
79
 
67
80
 
68
- def get_svg_dimensions(path: Path) -> tuple[int | None, int | None]:
81
+ @overload
82
+ def get_svg_dimensions(svg: Path) -> tuple[int, int] | None:
69
83
  """
70
84
  Extracts width and height from an SVG file.
71
85
 
72
86
  Attempts to read dimensions from:
73
- 1. Explicit width/height attributes on the root <svg> element
74
- 2. The viewBox attribute if width/height are not specified
75
-
76
- :param path: Path to the SVG file.
77
- :returns: A tuple of (width, height) in pixels, or (None, None) if dimensions cannot be determined.
78
- """
79
87
 
80
- try:
81
- tree = ET.parse(str(path))
82
- root = tree.getroot()
83
- width, height = _extract_dimensions_from_root(root)
84
- if width is None and height is None:
85
- LOGGER.warning("SVG file %s does not have an <svg> root element", path)
86
- return width, height
88
+ 1. Explicit `width` and `height` attributes on the root `<svg>` element
89
+ 2. The `viewBox` attribute if `width` or `height` is not specified
87
90
 
88
- except ET.XMLSyntaxError as ex:
89
- LOGGER.warning("Failed to parse SVG file %s: %s", path, ex)
90
- return None, None
91
- except Exception as ex:
92
- LOGGER.warning("Unexpected error reading SVG dimensions from %s: %s", path, ex)
93
- return None, None
91
+ :param svg: Path to the SVG file.
92
+ :returns: A tuple of (width, height) in pixels, or `None` if dimensions cannot be determined.
93
+ """
94
+ ...
94
95
 
95
96
 
96
- def get_svg_dimensions_from_bytes(data: bytes) -> tuple[int | None, int | None]:
97
+ @overload
98
+ def get_svg_dimensions(svg: bytes | str) -> tuple[int, int] | None:
97
99
  """
98
100
  Extracts width and height from SVG data in memory.
99
101
 
100
102
  Attempts to read dimensions from:
101
- 1. Explicit width/height attributes on the root <svg> element
102
- 2. The viewBox attribute if width/height are not specified
103
103
 
104
- :param data: The SVG content as bytes.
105
- :returns: A tuple of (width, height) in pixels, or (None, None) if dimensions cannot be determined.
106
- """
104
+ 1. Explicit `width` and `height` attributes on the root `<svg>` element
105
+ 2. The `viewBox` attribute if `width` or `height` is not specified
107
106
 
108
- try:
109
- root = ET.fromstring(data)
110
- return _extract_dimensions_from_root(root)
111
-
112
- except ET.XMLSyntaxError as ex:
113
- LOGGER.warning("Failed to parse SVG data: %s", ex)
114
- return None, None
115
- except Exception as ex:
116
- LOGGER.warning("Unexpected error reading SVG dimensions from data: %s", ex)
117
- return None, None
107
+ :param svg: The SVG content as bytes.
108
+ :returns: A tuple of (width, height) in pixels, or `None` if dimensions cannot be determined.
109
+ """
110
+ ...
111
+
112
+
113
+ def get_svg_dimensions(svg: Path | bytes | str) -> tuple[int, int] | None:
114
+ if isinstance(svg, Path):
115
+ path = svg
116
+ try:
117
+ tree = ET.parse(path)
118
+ root = tree.getroot()
119
+ return _extract_dimensions_from_root(root)
120
+ except OSError as ex:
121
+ LOGGER.warning("Failed to open SVG file: %s", path, exc_info=ex)
122
+ return None
123
+ except ET.XMLSyntaxError as ex:
124
+ LOGGER.warning("Failed to parse SVG file: %s", path, exc_info=ex)
125
+ return None
126
+ except SVGParseError as ex:
127
+ LOGGER.warning("Failed to extract dimensions from SVG file: %s", path, exc_info=ex)
128
+ return None
129
+ else:
130
+ data = svg
131
+ try:
132
+ root = ET.fromstring(data)
133
+ return _extract_dimensions_from_root(root)
134
+ except ET.XMLSyntaxError as ex:
135
+ LOGGER.warning("Failed to parse SVG data", exc_info=ex)
136
+ return None
137
+ except SVGParseError as ex:
138
+ LOGGER.warning("Failed to extract dimensions from SVG data", exc_info=ex)
139
+ return None
118
140
 
119
141
 
120
142
  def _serialize_svg_opening_tag(root: ElementType) -> str:
@@ -128,7 +150,7 @@ def _serialize_svg_opening_tag(root: ElementType) -> str:
128
150
  # Build the opening tag from element name and attributes
129
151
  root_tag = root.tag
130
152
  if not isinstance(root_tag, str):
131
- raise TypeError("expected: tag names as `str`")
153
+ raise SVGParseError("expected: tag names as `str`")
132
154
  tag_name = ET.QName(root_tag).localname
133
155
  parts = [f"<{tag_name}"]
134
156
 
@@ -180,64 +202,59 @@ def fix_svg_dimensions(data: bytes) -> bytes:
180
202
  :returns: The modified SVG content with explicit dimensions, or original data if modification fails.
181
203
  """
182
204
 
183
- try:
184
- text = data.decode("utf-8")
185
-
186
- # Skip SVGs with foreignObject - Confluence has issues rendering
187
- # foreignObject content when explicit width/height are set on the SVG
188
- if "<foreignObject" in text:
189
- LOGGER.debug("Skipping dimension fix for SVG with foreignObject elements")
190
- return data
191
-
192
- # Parse the SVG to extract root element attributes
193
- root = ET.fromstring(data)
205
+ # Skip SVGs with foreignObject - Confluence has issues rendering
206
+ # foreignObject content when explicit width/height are set on the SVG
207
+ if b"<foreignObject" in data:
208
+ LOGGER.debug("Skipping dimension fix for SVG with foreignObject elements")
209
+ return data
194
210
 
195
- # Verify it's an SVG element
196
- if not _check_svg(root):
197
- return data
211
+ # Parse the SVG to extract root element attributes
212
+ root = ET.fromstring(data)
198
213
 
199
- # Check if we need to fix (has width="100%" or similar percentage)
200
- width_attr = root.get("width")
201
- if width_attr != "100%":
202
- # Check if it already has a valid numeric width
203
- if width_attr is not None and _parse_svg_length(width_attr) is not None:
204
- return data # Already has numeric width
214
+ # Verify it's an SVG element
215
+ if not _check_svg(root):
216
+ return data
205
217
 
206
- # Get viewBox dimensions
207
- viewbox = root.get("viewBox")
208
- if not viewbox:
209
- return data
218
+ # Check if we need to fix (has width="100%" or similar percentage)
219
+ width_attr = root.get("width")
220
+ if width_attr != "100%":
221
+ # Check if it already has a valid numeric width
222
+ if width_attr is not None and _parse_svg_length(width_attr) is not None:
223
+ return data # Already has numeric width
224
+
225
+ # Get viewBox dimensions
226
+ viewbox_attr = root.get("viewBox")
227
+ if not viewbox_attr:
228
+ return data
210
229
 
211
- vb_width, vb_height = _parse_viewbox(viewbox)
212
- if vb_width is None or vb_height is None:
213
- return data
230
+ viewbox = _parse_viewbox(viewbox_attr)
231
+ if viewbox is None:
232
+ return data
233
+ vb_width, vb_height = viewbox
214
234
 
215
- # Extract the original opening tag from the text
216
- svg_tag_match = re.search(r"<svg\b[^>]*>", text)
217
- if not svg_tag_match:
218
- return data
235
+ # Extract the original opening tag from the text
236
+ svg_tag_match = re.search(rb"<svg\b[^>]*>", data)
237
+ if not svg_tag_match:
238
+ return data
219
239
 
220
- original_tag = svg_tag_match.group(0)
240
+ original_tag = svg_tag_match.group(0)
221
241
 
222
- # Modify the root element's attributes
223
- root.set("width", str(vb_width))
242
+ # Modify the root element's attributes
243
+ root.set("width", str(vb_width))
224
244
 
225
- # Set height if missing or if it's a percentage
226
- height_attr = root.get("height")
227
- if height_attr is None or height_attr == "100%":
228
- root.set("height", str(vb_height))
245
+ # Set height if missing or if it's a percentage
246
+ height_attr = root.get("height")
247
+ if height_attr is None or height_attr == "100%":
248
+ root.set("height", str(vb_height))
229
249
 
230
- # Serialize just the opening tag with modified attributes
231
- new_tag = _serialize_svg_opening_tag(root)
250
+ # Serialize just the opening tag with modified attributes
251
+ new_tag = _serialize_svg_opening_tag(root).encode("utf-8")
232
252
 
233
- # Replace the original opening tag with the new one
234
- text = text.replace(original_tag, new_tag, 1)
253
+ # Replace the original opening tag with the new one
254
+ return data.replace(original_tag, new_tag, 1)
235
255
 
236
- return text.encode("utf-8")
237
256
 
238
- except Exception as ex:
239
- LOGGER.warning("Unexpected error fixing SVG dimensions: %s", ex)
240
- return data
257
+ _MEASURE_REGEXP = re.compile(r"^([+-]?(?:\d+\.?\d*|\.\d+))(%|px|pt|em|ex|in|cm|mm|pc)?$", re.IGNORECASE)
241
258
 
242
259
 
243
260
  def _parse_svg_length(value: str) -> int | None:
@@ -257,7 +274,7 @@ def _parse_svg_length(value: str) -> int | None:
257
274
  value = value.strip()
258
275
 
259
276
  # Match number with optional unit
260
- match = re.match(r"^([+-]?(?:\d+\.?\d*|\.\d+))(%|px|pt|em|ex|in|cm|mm|pc)?$", value, re.IGNORECASE)
277
+ match = _MEASURE_REGEXP.match(value)
261
278
  if not match:
262
279
  return None
263
280
 
@@ -294,32 +311,32 @@ def _parse_svg_length(value: str) -> int | None:
294
311
  return int(round(pixels))
295
312
 
296
313
 
297
- def _parse_viewbox(viewbox: str) -> tuple[int | None, int | None]:
314
+ def _parse_viewbox(viewbox: str) -> tuple[int, int] | None:
298
315
  """
299
316
  Parses an SVG viewBox attribute and extracts width and height.
300
317
 
301
318
  :param viewbox: The viewBox string (e.g., "0 0 100 200").
302
- :returns: A tuple of (width, height) in pixels, or (None, None) if parsing fails.
319
+ :returns: A tuple of (width, height) in pixels, or `None` if parsing fails.
303
320
  """
304
321
 
305
322
  if not viewbox:
306
- return None, None
323
+ return None
307
324
 
308
325
  # viewBox format: "min-x min-y width height"
309
326
  # Values can be separated by whitespace and/or commas
310
- parts = re.split(r"[\s,]+", viewbox.strip())
327
+ parts = re.split(r"\s*,\s*|\s+", viewbox.strip())
311
328
  if len(parts) != 4:
312
- return None, None
329
+ return None
313
330
 
314
331
  try:
315
332
  width = int(round(float(parts[2])))
316
333
  height = int(round(float(parts[3])))
317
334
  return width, height
318
335
  except ValueError:
319
- return None, None
336
+ return None
320
337
 
321
338
 
322
- def fix_svg_get_dimensions(image_data: bytes) -> tuple[bytes, int | None, int | None]:
339
+ def fix_svg_get_dimensions(data: bytes) -> tuple[bytes, tuple[int, int] | None]:
323
340
  """
324
341
  Post-processes SVG diagram data by fixing dimensions and extracting metadata.
325
342
 
@@ -328,14 +345,19 @@ def fix_svg_get_dimensions(image_data: bytes) -> tuple[bytes, int | None, int |
328
345
  1. fixes SVG dimensions (converts percentage-based to explicit pixels), and
329
346
  2. extracts width/height from the SVG.
330
347
 
331
- :param image_data: Raw SVG data as bytes.
348
+ :param data: Raw SVG data as bytes.
332
349
  :returns: Tuple of update raw data, image width, image height.
333
350
  """
334
351
 
335
- # fix SVG to have explicit width/height instead of percentages
336
- image_data = fix_svg_dimensions(image_data)
337
-
338
- # extract dimensions from the fixed SVG
339
- width, height = get_svg_dimensions_from_bytes(image_data)
352
+ try:
353
+ # fix SVG to have explicit width/height instead of percentages
354
+ data = fix_svg_dimensions(data)
340
355
 
341
- return image_data, width, height
356
+ # extract dimensions from the fixed SVG
357
+ return data, get_svg_dimensions(data)
358
+ except ET.XMLSyntaxError as ex:
359
+ LOGGER.warning("Failed to parse SVG data", exc_info=ex)
360
+ return data, None
361
+ except SVGParseError as ex:
362
+ LOGGER.warning("Failed to extract dimensions from SVG data", exc_info=ex)
363
+ return data, None
md2conf/toc.py CHANGED
@@ -6,7 +6,9 @@ Copyright 2022-2026, Levente Hunyadi
6
6
  :see: https://github.com/hunyadi/md2conf
7
7
  """
8
8
 
9
+ import re
9
10
  from dataclasses import dataclass
11
+ from typing import Iterable, Iterator
10
12
 
11
13
 
12
14
  @dataclass(eq=True)
@@ -86,3 +88,73 @@ class TableOfContentsBuilder:
86
88
  return self.tree[0].text
87
89
  else:
88
90
  return None
91
+
92
+
93
+ _FENCED_CODE_REGEXP = re.compile(r"^\s*(?:`{3,}|~{3,})", re.MULTILINE)
94
+ _ATX_HEADING_REGEXP = re.compile(r"^(#{1,6})\s+(.*?)$", re.MULTILINE)
95
+ _SETEXT_HEADING_REGEXP = re.compile(r"^(=+|-+)\s*?$", re.MULTILINE)
96
+
97
+
98
+ def headings(lines: Iterable[str]) -> Iterator[tuple[int, str]]:
99
+ fence_marker: str | None = None
100
+ heading_text: str | None = None
101
+
102
+ for line in lines:
103
+ # fenced code blocks
104
+ fence_match = _FENCED_CODE_REGEXP.match(line)
105
+ if fence_match:
106
+ marker = fence_match.group()
107
+ if fence_marker is None:
108
+ fence_marker = marker
109
+ elif marker == fence_marker:
110
+ fence_marker = None
111
+ heading_text = None
112
+ continue
113
+
114
+ if fence_marker is not None:
115
+ heading_text = None
116
+ continue
117
+
118
+ # ATX headings
119
+ atx = _ATX_HEADING_REGEXP.match(line)
120
+ if atx is not None:
121
+ level = len(atx.group(1))
122
+ title = atx.group(2).rstrip().rstrip("#").rstrip() # remove decorative text: `## Section 1.2 ##`
123
+ yield level, title
124
+
125
+ heading_text = None
126
+ continue
127
+
128
+ # Setext headings
129
+ setext = _SETEXT_HEADING_REGEXP.match(line)
130
+ if setext is not None and heading_text is not None:
131
+ match setext.group(1)[0:1]:
132
+ case "=":
133
+ level = 1
134
+ case "-":
135
+ level = 2
136
+ case _:
137
+ level = 0
138
+ yield level, heading_text.rstrip()
139
+
140
+ heading_text = None
141
+ continue
142
+
143
+ # candidate for Setext title
144
+ heading_text = line
145
+
146
+
147
+ def unique_title(content: str) -> str | None:
148
+ """
149
+ Returns a proposed document title.
150
+
151
+ The proposed title is text of the top-level heading if and only if that heading is unique.
152
+
153
+ :returns: Title text, or `None` if no title can be inferred.
154
+ """
155
+
156
+ builder = TableOfContentsBuilder()
157
+ for heading in headings(content.splitlines(keepends=True)): # spellchecker:disable-line
158
+ level, text = heading
159
+ builder.add(level, text)
160
+ return builder.get_title()
md2conf/xml.py CHANGED
@@ -106,6 +106,51 @@ def element_to_text(node: ElementType) -> str:
106
106
  return "".join(node.itertext()).strip()
107
107
 
108
108
 
109
+ def remove_element(child: ElementType) -> None:
110
+ """
111
+ Removes a child element, taking care of its tail text.
112
+
113
+ This function may be unsafe when called in the body of a loop of a live collection iterator, i.e. use
114
+
115
+ ```
116
+ for child in list(node): ...
117
+ ```
118
+
119
+ instead of
120
+
121
+ ```
122
+ for child in node: ...
123
+ ```
124
+ """
125
+
126
+ parent = child.getparent()
127
+ if parent is None:
128
+ return
129
+
130
+ # preserve any text that comes after the heading (tail text)
131
+ tail = child.tail
132
+
133
+ # if there was tail text, attach it to the previous sibling's tail or to the parent's text if this was the first child
134
+ if tail:
135
+ index = parent.index(child)
136
+ if index > 0:
137
+ # append to previous sibling's tail
138
+ prev_sibling = parent[index - 1]
139
+ if prev_sibling.tail:
140
+ prev_sibling.tail += tail
141
+ else:
142
+ prev_sibling.tail = tail
143
+ else:
144
+ # no previous sibling, append to parent's text
145
+ if parent.text:
146
+ parent.text += tail
147
+ else:
148
+ parent.text = tail
149
+
150
+ # remove the element
151
+ parent.remove(child)
152
+
153
+
109
154
  def unwrap_substitute(name: str, root: ElementType) -> None:
110
155
  """
111
156
  Substitutes all occurrences of an element with its contents.
@@ -1,55 +0,0 @@
1
- markdown_to_confluence-0.5.3.dist-info/licenses/LICENSE,sha256=SEEBf2BMI1LUHnDvyHnV6L12A6zTAOQcsyMvaawAXWo,1077
2
- md2conf/__init__.py,sha256=CqQHkYFE1AAs4GhMm1nConbt3V7wksgDXzqm5mg6F6I,402
3
- md2conf/__main__.py,sha256=Wxz6Rx1Sx_J0FbaCY2HpByHqTMbxDEs1v5VzfdQh32U,13891
4
- md2conf/api.py,sha256=GVPElSP9qILA5IaEvtKaxjsoRQSsGMMoRhCxjjzLadM,42703
5
- md2conf/attachment.py,sha256=Nc3qGDENWBnsI6OVwMLXnk0EyEITpvov9MluDFD90ZI,1689
6
- md2conf/coalesce.py,sha256=YHnqFwow5wCj6OQ3oosig01D2lxWusAScMF4HAUO2-g,1305
7
- md2conf/collection.py,sha256=ukN74VCa4HaGSh6tLXpLd0j_UNPywcnKI0X7usgdSCo,824
8
- md2conf/compatibility.py,sha256=4ZNN6VLqxSbI1kowdsPproGZqwxBISys4Z22vBfe6Z8,687
9
- md2conf/converter.py,sha256=C6cROI8bUswyNZ1jSC0lf_9J-S6ojrXLbrkSDALuKR8,63065
10
- md2conf/csf.py,sha256=6H9G-5cZyyWMJr0tFskPNiWdQ2Ehq-V8EhlvvxhukWY,6582
11
- md2conf/emoticon.py,sha256=0g4rkx3d58xU4nnLak5ms7i0FSDnq0WJrLVFRgGyLC8,542
12
- md2conf/entities.dtd,sha256=M6NzqL5N7dPs_eUA_6sDsiSLzDaAacrx9LdttiufvYU,30215
13
- md2conf/environment.py,sha256=TfNEz3Pyw9qe7f8i7e_kph16c09fhZ4cLNZZzIjmI18,3892
14
- md2conf/extension.py,sha256=5d2CtfULwiqlqwpOqbvlVu2KQ-kVBkDDYAhnHXbq79I,2227
15
- md2conf/external.py,sha256=ZRL0mCY02Gp4XfqRdbap2YdOihSpY2LA6tdGJBfC-FQ,1693
16
- md2conf/formatting.py,sha256=ygL59VgpioX069axEX-7XjKs0sUjTfIZiBE5fWmITxc,4557
17
- md2conf/frontmatter.py,sha256=iWtn_oXoLQxvCsdI3OXs1ylWGmB-gc7mMLpSGg113i4,1888
18
- md2conf/image.py,sha256=udlnUY_xOmI-PJAqWqXBF7hAO3pj_VW7u5hXhBw4HcA,5260
19
- md2conf/latex.py,sha256=haZKkUxSEcPj3fVmiIVZAwgszqNqGLk1GQ7i8KGHpo0,2226
20
- md2conf/local.py,sha256=eY3WpY-lNzLZeAfxX1ACVEhuzz0HDYX_sNQogJfkqcM,3673
21
- md2conf/markdown.py,sha256=4Km-AbQH04nDgPF0ijo-Ld7o8jTPXzENIMn7P1qIk0o,3148
22
- md2conf/matcher.py,sha256=Xg4YSb87iPkCzhKuKytBut6NOkEab3IM-AjzXbwy64U,6774
23
- md2conf/metadata.py,sha256=NOjbCIrwLgTIIeNgmo7w5JXuT-pxOXBGSg-irfdpokk,976
24
- md2conf/options.py,sha256=MJka7h0ly6D5QQNXEi9ios1uQbqHQpyn07XciHV-mF8,4385
25
- md2conf/png.py,sha256=wbo8tgm8Vxi56q5PLoTutZBU6qF73KJ6TJbs7G7LPG4,6166
26
- md2conf/processor.py,sha256=yqRb1Njn8XWxx22byq5nrobeH9AOp55mHTVoBUJlN2A,10203
27
- md2conf/publisher.py,sha256=6WfyZxWB59PrOro6wgkgXuFcnf7AqarlUfHo-Twzu9k,8509
28
- md2conf/puppeteer-config.json,sha256=-dMTAN_7kNTGbDlfXzApl0KJpAWna9YKZdwMKbpOb60,159
29
- md2conf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- md2conf/scanner.py,sha256=wvh5IlD2VnAwulqY6PUe33k1D8Uag0kqTs9pQ-YFq8g,3736
31
- md2conf/serializer.py,sha256=W4_yLJfT3vLw0PUg88lpUEnvn64CjaX3ZaKgIrwcxfw,1786
32
- md2conf/svg.py,sha256=b98hxpVL-yFojln8NunxQRJSabMdfvazo7FkFySjALY,11054
33
- md2conf/text.py,sha256=cnYV_JQp_v91LbQHo3qvxcEuhIdaPjCjkmLOKINcNv4,1736
34
- md2conf/toc.py,sha256=HCtnREEucmGfqzwq-Dk6Q4ZgxW5Z8nZk6lg8UfhcoI0,2413
35
- md2conf/uri.py,sha256=my0deyR5SlppJrYCbXF1Zz94QA1JT-HTWe9pKw7AJ_A,1158
36
- md2conf/xml.py,sha256=uaaUDs0hfluNX74dfkY_Dxu1KmeNDGogpGRGpUVEfE4,5526
37
- md2conf/drawio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
- md2conf/drawio/extension.py,sha256=-k0pAhIZXDjiKeHlTJl2MGWMYpG-PmXK91-_vBMQZnE,4389
39
- md2conf/drawio/render.py,sha256=veSu5gjm5ggLnmaH7uvH9qNeOygBJpqhSKK_LJs0QTk,8581
40
- md2conf/mermaid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
- md2conf/mermaid/config.py,sha256=5Dec2QcdB_GtnuXIW6nhJK8J5caduNZU1oz1mcmmb44,376
42
- md2conf/mermaid/extension.py,sha256=1drXVM_KbS00dcjSCRru0wwbil4zq3aR81dHMhfe7zA,4021
43
- md2conf/mermaid/render.py,sha256=cGq3hOemgsbh6btt7uJaRxzH4EmGis0K6_qNW5YrhIk,1834
44
- md2conf/mermaid/scanner.py,sha256=oIpaNxiZBNcmggnjlyYGcIVOXcYQWjf1lEVdyIwE4xE,1379
45
- md2conf/plantuml/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- md2conf/plantuml/config.py,sha256=j0ONhkzmAPagh00ltamTKlVEvXa6R284We9pDxRy-5U,378
47
- md2conf/plantuml/extension.py,sha256=-WEL6Ssa3Kz_7D_AQSnB1da0bylyYZivmRQwlOBfYcM,6281
48
- md2conf/plantuml/render.py,sha256=fwwKJIv_q1Fm9Vd8af7OcEG-5MkojZ-fxrQgA33grE8,3818
49
- md2conf/plantuml/scanner.py,sha256=Oso6VbHVuMaPMKMazQc_bf4hhOT5WeJN5WiVPM8peyM,1347
50
- markdown_to_confluence-0.5.3.dist-info/METADATA,sha256=7r3TveKAaJPBk14SGcJHuCXuDYD0rt7KUK6a_A8p1gE,43775
51
- markdown_to_confluence-0.5.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
52
- markdown_to_confluence-0.5.3.dist-info/entry_points.txt,sha256=F1zxa1wtEObtbHS-qp46330WVFLHdMnV2wQ-ZorRmX0,50
53
- markdown_to_confluence-0.5.3.dist-info/top_level.txt,sha256=_FJfl_kHrHNidyjUOuS01ngu_jDsfc-ZjSocNRJnTzU,8
54
- markdown_to_confluence-0.5.3.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
55
- markdown_to_confluence-0.5.3.dist-info/RECORD,,