markdown-to-confluence 0.5.1__tar.gz → 0.5.2__tar.gz

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 (117) hide show
  1. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/PKG-INFO +82 -9
  2. markdown_to_confluence-0.5.1/markdown_to_confluence.egg-info/PKG-INFO → markdown_to_confluence-0.5.2/README.md +77 -50
  3. markdown_to_confluence-0.5.1/README.md → markdown_to_confluence-0.5.2/markdown_to_confluence.egg-info/PKG-INFO +123 -4
  4. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/markdown_to_confluence.egg-info/SOURCES.txt +13 -0
  5. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/markdown_to_confluence.egg-info/requires.txt +4 -4
  6. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/__init__.py +1 -1
  7. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/__main__.py +56 -9
  8. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/api.py +28 -2
  9. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/converter.py +282 -38
  10. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/domain.py +10 -3
  11. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/latex.py +4 -4
  12. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/publisher.py +3 -0
  13. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/scanner.py +2 -2
  14. markdown_to_confluence-0.5.2/md2conf/svg.py +319 -0
  15. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/pyproject.toml +4 -4
  16. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/footnote.md +3 -0
  17. markdown_to_confluence-0.5.2/tests/source/skip_title_heading.md +13 -0
  18. markdown_to_confluence-0.5.2/tests/source/skip_title_heading_abstract.md +13 -0
  19. markdown_to_confluence-0.5.2/tests/source/skip_title_heading_frontmatter.md +13 -0
  20. markdown_to_confluence-0.5.2/tests/source/skip_title_heading_multiple.md +9 -0
  21. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/footnote.xml +19 -0
  22. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/images.xml +2 -2
  23. markdown_to_confluence-0.5.2/tests/target/skip_title_heading.xml +1 -0
  24. markdown_to_confluence-0.5.2/tests/target/skip_title_heading_abstract.xml +1 -0
  25. markdown_to_confluence-0.5.2/tests/target/skip_title_heading_abstract_removed.xml +1 -0
  26. markdown_to_confluence-0.5.2/tests/target/skip_title_heading_frontmatter.xml +1 -0
  27. markdown_to_confluence-0.5.2/tests/target/skip_title_heading_multiple.xml +1 -0
  28. markdown_to_confluence-0.5.2/tests/target/skip_title_heading_preserved.xml +1 -0
  29. markdown_to_confluence-0.5.2/tests/target/skip_title_heading_removed.xml +1 -0
  30. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/test_conversion.py +134 -0
  31. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/test_processor.py +2 -0
  32. markdown_to_confluence-0.5.2/tests/test_svg.py +167 -0
  33. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/test_unit.py +30 -1
  34. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/LICENSE +0 -0
  35. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/MANIFEST.in +0 -0
  36. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/markdown_to_confluence.egg-info/dependency_links.txt +0 -0
  37. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/markdown_to_confluence.egg-info/entry_points.txt +0 -0
  38. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/markdown_to_confluence.egg-info/top_level.txt +0 -0
  39. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/markdown_to_confluence.egg-info/zip-safe +0 -0
  40. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/collection.py +0 -0
  41. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/csf.py +0 -0
  42. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/drawio.py +0 -0
  43. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/emoticon.py +0 -0
  44. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/entities.dtd +0 -0
  45. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/environment.py +0 -0
  46. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/extra.py +0 -0
  47. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/local.py +0 -0
  48. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/markdown.py +0 -0
  49. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/matcher.py +0 -0
  50. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/mermaid.py +0 -0
  51. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/metadata.py +0 -0
  52. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/processor.py +0 -0
  53. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/puppeteer-config.json +0 -0
  54. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/py.typed +0 -0
  55. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/serializer.py +0 -0
  56. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/text.py +0 -0
  57. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/toc.py +0 -0
  58. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/uri.py +0 -0
  59. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/md2conf/xml.py +0 -0
  60. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/setup.cfg +0 -0
  61. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/setup.py +0 -0
  62. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/__init__.py +0 -0
  63. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/emoji.py +0 -0
  64. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/admonition.md +0 -0
  65. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/alert.md +0 -0
  66. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/alignment.md +0 -0
  67. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/anchors.md +0 -0
  68. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/basic.md +0 -0
  69. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/code.md +0 -0
  70. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/collapsed.md +0 -0
  71. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/fenced.md +0 -0
  72. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/figure/diagram.drawio +0 -0
  73. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/figure/diagram.drawio.png +0 -0
  74. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/figure/diagram.drawio.svg +0 -0
  75. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/figure/raster.png +0 -0
  76. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/figure/vector.svg +0 -0
  77. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/ignore.md +0 -0
  78. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/images/images.md +0 -0
  79. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/images.md +0 -0
  80. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/macro.md +0 -0
  81. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/math.md +0 -0
  82. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/mermaid.md +0 -0
  83. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/missing.md +0 -0
  84. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/sections.md +0 -0
  85. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/status.md +0 -0
  86. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/table.md +0 -0
  87. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/tags.md +0 -0
  88. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/tasklist.md +0 -0
  89. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/title.md +0 -0
  90. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/source/toc.md +0 -0
  91. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/admonition.xml +0 -0
  92. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/alert.xml +0 -0
  93. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/alignment.xml +0 -0
  94. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/anchors.xml +0 -0
  95. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/basic.xml +0 -0
  96. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/code.xml +0 -0
  97. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/collapsed.xml +0 -0
  98. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/fenced.xml +0 -0
  99. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/images/images.xml +0 -0
  100. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/macro.xml +0 -0
  101. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/math.xml +0 -0
  102. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/mermaid.xml +0 -0
  103. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/missing.xml +0 -0
  104. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/panel.xml +0 -0
  105. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/sections.xml +0 -0
  106. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/status.xml +0 -0
  107. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/table.xml +0 -0
  108. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/tags.xml +0 -0
  109. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/tasklist.xml +0 -0
  110. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/target/toc.xml +0 -0
  111. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/test_drawio.py +0 -0
  112. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/test_matcher.py +0 -0
  113. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/test_mermaid.py +0 -0
  114. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/test_scanner.py +0 -0
  115. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/test_text.py +0 -0
  116. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/test_xml.py +0 -0
  117. {markdown_to_confluence-0.5.1 → markdown_to_confluence-0.5.2}/tests/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: markdown-to-confluence
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: Publish Markdown files to Confluence wiki
5
5
  Author-email: Levente Hunyadi <hunyadi@gmail.com>
6
6
  Maintainer-email: Levente Hunyadi <hunyadi@gmail.com>
@@ -27,18 +27,18 @@ Requires-Dist: cattrs>=25.3
27
27
  Requires-Dist: lxml>=6.0
28
28
  Requires-Dist: markdown>=3.10
29
29
  Requires-Dist: orjson>=3.11
30
- Requires-Dist: pymdown-extensions>=10.17
30
+ Requires-Dist: pymdown-extensions>=10.19
31
31
  Requires-Dist: PyYAML>=6.0
32
32
  Requires-Dist: requests>=2.32
33
33
  Requires-Dist: truststore>=0.10
34
34
  Requires-Dist: typing-extensions>=4.15; python_version < "3.12"
35
35
  Provides-Extra: dev
36
- Requires-Dist: markdown_doc>=0.1.5; extra == "dev"
37
- Requires-Dist: types-lxml>=2025.8.25; extra == "dev"
36
+ Requires-Dist: markdown_doc>=0.1.6; extra == "dev"
37
+ Requires-Dist: types-lxml>=2025.11.25; extra == "dev"
38
38
  Requires-Dist: types-markdown>=3.10; extra == "dev"
39
39
  Requires-Dist: types-PyYAML>=6.0; extra == "dev"
40
40
  Requires-Dist: types-requests>=2.32; extra == "dev"
41
- Requires-Dist: mypy>=1.18; extra == "dev"
41
+ Requires-Dist: mypy>=1.19; extra == "dev"
42
42
  Requires-Dist: ruff>=0.14; extra == "dev"
43
43
  Provides-Extra: formulas
44
44
  Requires-Dist: matplotlib>=3.9; extra == "formulas"
@@ -215,6 +215,19 @@ Provide generated-by prompt text in the Markdown file with a tag:
215
215
 
216
216
  Alternatively, use the `--generated-by GENERATED_BY` option. The tag takes precedence.
217
217
 
218
+ The generated-by text can also be templated with the following variables:
219
+
220
+ - `%{filename}`: the name of the Markdown file
221
+ - `%{filepath}`: the path of the Markdown file relative to the *source root*
222
+
223
+ When publishing a directory hierarchy, the *source root* is the directory in which *md2conf* is launched. When publishing a single file, this is the directory in which the Markdown file resides.
224
+
225
+ It can be used with the CLI `--generated-by` option or directly in the files:
226
+
227
+ ```markdown
228
+ <!-- generated-by: Do not edit! Check out the file %{filepath} in the repo -->
229
+ ```
230
+
218
231
  ### Publishing a single page
219
232
 
220
233
  *md2conf* has two modes of operation: *single-page mode* and *directory mode*.
@@ -455,6 +468,58 @@ This is useful if you have a page in a hierarchy that participates in parent-chi
455
468
 
456
469
  If the `title` attribute (in the front-matter) or the topmost unique heading (in the document body) changes, the Confluence page title is updated. A warning is raised if the new title conflicts with the title of another page, and thus cannot be updated.
457
470
 
471
+ #### Avoiding duplicate titles
472
+
473
+ By default, when *md2conf* extracts a page title from the first unique heading in a Markdown document, the heading remains in the document body. This means the title appears twice on the Confluence page: once as the page title at the top, and once as the first heading in the content.
474
+
475
+ To avoid this duplication, use the `--skip-title-heading` option. When enabled, *md2conf* removes the first heading from the document body if it was used as the page title. This option only takes effect when:
476
+
477
+ 1. The title was extracted from the document's first unique heading (not from front-matter), AND
478
+ 2. There is exactly one top-level heading in the document.
479
+
480
+ If the title comes from the `title` attribute in front-matter, the heading is preserved in the document body regardless of this setting, as the heading and title are considered separate.
481
+
482
+ **Example without `--skip-title-heading` (default):**
483
+
484
+ Markdown:
485
+ ```markdown
486
+ # Installation Guide
487
+
488
+ Follow these steps...
489
+ ```
490
+
491
+ Confluence displays:
492
+ - Page title: "Installation Guide"
493
+ - Content: Starts with heading "Installation Guide", followed by "Follow these steps..."
494
+
495
+ **Example with `--skip-title-heading`:**
496
+
497
+ Same Markdown source, but Confluence displays:
498
+ - Page title: "Installation Guide"
499
+ - Content: Starts directly with "Follow these steps..." (heading removed)
500
+
501
+ **Edge case: Abstract or introductory text before the title:**
502
+
503
+ When a document has content before the first heading (like an abstract), removing the heading eliminates the visual separator between the introductory text and the main content:
504
+
505
+ ```markdown
506
+ This is an abstract paragraph providing context.
507
+
508
+ # Document Title
509
+
510
+ This is the main document content.
511
+ ```
512
+
513
+ With `--skip-title-heading`, the output becomes:
514
+ - Page title: "Document Title"
515
+ - Content: "This is an abstract paragraph..." flows directly into "This is the main document content..." (no heading separator)
516
+
517
+ While the structure remains semantically correct, the visual separation is lost. If you need to maintain separation, consider these workarounds:
518
+
519
+ 1. **Use a horizontal rule:** Add `---` after the abstract to create visual separation
520
+ 2. **Use an admonition block:** Wrap the abstract in an info/note block
521
+ 3. **Use front-matter title:** Set `title` in front-matter to keep the heading in the body
522
+
458
523
  ### Labels
459
524
 
460
525
  If a Markdown document has the front-matter attribute `tags`, *md2conf* assigns the specified tags to the Confluence page as labels.
@@ -571,7 +636,7 @@ options:
571
636
  -r ROOT_PAGE Root Confluence page to create new pages. If omitted, will raise exception when creating new pages.
572
637
  --keep-hierarchy Maintain source directory structure when exporting to Confluence.
573
638
  --flatten-hierarchy Flatten directories with no index.md or README.md when exporting to Confluence.
574
- --generated-by GENERATED_BY
639
+ --generated-by MARKDOWN
575
640
  Add prompt to pages (default: 'This page has been generated with a tool.').
576
641
  --no-generated-by Do not add 'generated by a tool' prompt to pages.
577
642
  --render-drawio Render draw.io diagrams as image files. (Installed utility required to covert.)
@@ -582,16 +647,24 @@ options:
582
647
  --no-render-latex Inline LaTeX formulas in Confluence page. (Marketplace app required to display.)
583
648
  --diagram-output-format {png,svg}
584
649
  Format for rendering Mermaid and draw.io diagrams (default: 'png').
650
+ --prefer-raster Prefer PNG over SVG when both exist (default: enabled).
651
+ --no-prefer-raster Use SVG files directly instead of preferring PNG equivalents.
585
652
  --heading-anchors Place an anchor at each section heading with GitHub-style same-page identifiers.
586
653
  --no-heading-anchors Don't place an anchor at each section heading.
587
654
  --ignore-invalid-url Emit a warning but otherwise ignore relative URLs that point to ill-specified locations.
588
- --local Write XHTML-based Confluence Storage Format files locally without invoking Confluence API.
589
- --headers KEY=VALUE [KEY=VALUE ...]
590
- Apply custom headers to all Confluence API requests.
655
+ --skip-title-heading Skip the first heading from document body when it is used as the page title (does not apply if title comes from front-matter).
656
+ --no-skip-title-heading
657
+ Keep the first heading in document body even when used as page title (default).
658
+ --title-prefix TEXT String to prepend to Confluence page title for each published page.
591
659
  --webui-links Enable Confluence Web UI links. (Typically required for on-prem versions of Confluence.)
592
660
  --alignment {center,left,right}
593
661
  Alignment for block-level images and formulas (default: 'center').
662
+ --max-image-width MAX_IMAGE_WIDTH
663
+ Maximum display width for images [px]. Wider images are scaled down for page display. Original size kept for full-size viewing.
594
664
  --use-panel Transform admonitions and alerts into a Confluence custom panel.
665
+ --local Write XHTML-based Confluence Storage Format files locally without invoking Confluence API.
666
+ --headers KEY=VALUE [KEY=VALUE ...]
667
+ Apply custom headers to all Confluence API requests.
595
668
  ```
596
669
 
597
670
  ### Confluence REST API v1 vs. v2
@@ -1,49 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: markdown-to-confluence
3
- Version: 0.5.1
4
- Summary: Publish Markdown files to Confluence wiki
5
- Author-email: Levente Hunyadi <hunyadi@gmail.com>
6
- Maintainer-email: Levente Hunyadi <hunyadi@gmail.com>
7
- License-Expression: MIT
8
- Project-URL: Homepage, https://github.com/hunyadi/md2conf
9
- Project-URL: Source, https://github.com/hunyadi/md2conf
10
- Keywords: markdown,converter,confluence
11
- Classifier: Development Status :: 5 - Production/Stable
12
- Classifier: Environment :: Console
13
- Classifier: Intended Audience :: End Users/Desktop
14
- Classifier: Operating System :: OS Independent
15
- Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
- Classifier: Programming Language :: Python :: 3.13
20
- Classifier: Programming Language :: Python :: 3.14
21
- Classifier: Programming Language :: Python :: 3 :: Only
22
- Classifier: Typing :: Typed
23
- Requires-Python: >=3.10
24
- Description-Content-Type: text/markdown
25
- License-File: LICENSE
26
- Requires-Dist: cattrs>=25.3
27
- Requires-Dist: lxml>=6.0
28
- Requires-Dist: markdown>=3.10
29
- Requires-Dist: orjson>=3.11
30
- Requires-Dist: pymdown-extensions>=10.17
31
- Requires-Dist: PyYAML>=6.0
32
- Requires-Dist: requests>=2.32
33
- Requires-Dist: truststore>=0.10
34
- Requires-Dist: typing-extensions>=4.15; python_version < "3.12"
35
- Provides-Extra: dev
36
- Requires-Dist: markdown_doc>=0.1.5; extra == "dev"
37
- Requires-Dist: types-lxml>=2025.8.25; extra == "dev"
38
- Requires-Dist: types-markdown>=3.10; extra == "dev"
39
- Requires-Dist: types-PyYAML>=6.0; extra == "dev"
40
- Requires-Dist: types-requests>=2.32; extra == "dev"
41
- Requires-Dist: mypy>=1.18; extra == "dev"
42
- Requires-Dist: ruff>=0.14; extra == "dev"
43
- Provides-Extra: formulas
44
- Requires-Dist: matplotlib>=3.9; extra == "formulas"
45
- Dynamic: license-file
46
-
47
1
  # Publish Markdown files to Confluence wiki
48
2
 
49
3
  Contributors to software projects typically write documentation in Markdown format and host Markdown files in collaborative version control systems (VCS) such as GitHub or GitLab to track changes and facilitate the review process. However, not everyone at a company has access to VCS, and documents are often circulated in Confluence wiki instead.
@@ -215,6 +169,19 @@ Provide generated-by prompt text in the Markdown file with a tag:
215
169
 
216
170
  Alternatively, use the `--generated-by GENERATED_BY` option. The tag takes precedence.
217
171
 
172
+ The generated-by text can also be templated with the following variables:
173
+
174
+ - `%{filename}`: the name of the Markdown file
175
+ - `%{filepath}`: the path of the Markdown file relative to the *source root*
176
+
177
+ When publishing a directory hierarchy, the *source root* is the directory in which *md2conf* is launched. When publishing a single file, this is the directory in which the Markdown file resides.
178
+
179
+ It can be used with the CLI `--generated-by` option or directly in the files:
180
+
181
+ ```markdown
182
+ <!-- generated-by: Do not edit! Check out the file %{filepath} in the repo -->
183
+ ```
184
+
218
185
  ### Publishing a single page
219
186
 
220
187
  *md2conf* has two modes of operation: *single-page mode* and *directory mode*.
@@ -455,6 +422,58 @@ This is useful if you have a page in a hierarchy that participates in parent-chi
455
422
 
456
423
  If the `title` attribute (in the front-matter) or the topmost unique heading (in the document body) changes, the Confluence page title is updated. A warning is raised if the new title conflicts with the title of another page, and thus cannot be updated.
457
424
 
425
+ #### Avoiding duplicate titles
426
+
427
+ By default, when *md2conf* extracts a page title from the first unique heading in a Markdown document, the heading remains in the document body. This means the title appears twice on the Confluence page: once as the page title at the top, and once as the first heading in the content.
428
+
429
+ To avoid this duplication, use the `--skip-title-heading` option. When enabled, *md2conf* removes the first heading from the document body if it was used as the page title. This option only takes effect when:
430
+
431
+ 1. The title was extracted from the document's first unique heading (not from front-matter), AND
432
+ 2. There is exactly one top-level heading in the document.
433
+
434
+ If the title comes from the `title` attribute in front-matter, the heading is preserved in the document body regardless of this setting, as the heading and title are considered separate.
435
+
436
+ **Example without `--skip-title-heading` (default):**
437
+
438
+ Markdown:
439
+ ```markdown
440
+ # Installation Guide
441
+
442
+ Follow these steps...
443
+ ```
444
+
445
+ Confluence displays:
446
+ - Page title: "Installation Guide"
447
+ - Content: Starts with heading "Installation Guide", followed by "Follow these steps..."
448
+
449
+ **Example with `--skip-title-heading`:**
450
+
451
+ Same Markdown source, but Confluence displays:
452
+ - Page title: "Installation Guide"
453
+ - Content: Starts directly with "Follow these steps..." (heading removed)
454
+
455
+ **Edge case: Abstract or introductory text before the title:**
456
+
457
+ When a document has content before the first heading (like an abstract), removing the heading eliminates the visual separator between the introductory text and the main content:
458
+
459
+ ```markdown
460
+ This is an abstract paragraph providing context.
461
+
462
+ # Document Title
463
+
464
+ This is the main document content.
465
+ ```
466
+
467
+ With `--skip-title-heading`, the output becomes:
468
+ - Page title: "Document Title"
469
+ - Content: "This is an abstract paragraph..." flows directly into "This is the main document content..." (no heading separator)
470
+
471
+ While the structure remains semantically correct, the visual separation is lost. If you need to maintain separation, consider these workarounds:
472
+
473
+ 1. **Use a horizontal rule:** Add `---` after the abstract to create visual separation
474
+ 2. **Use an admonition block:** Wrap the abstract in an info/note block
475
+ 3. **Use front-matter title:** Set `title` in front-matter to keep the heading in the body
476
+
458
477
  ### Labels
459
478
 
460
479
  If a Markdown document has the front-matter attribute `tags`, *md2conf* assigns the specified tags to the Confluence page as labels.
@@ -571,7 +590,7 @@ options:
571
590
  -r ROOT_PAGE Root Confluence page to create new pages. If omitted, will raise exception when creating new pages.
572
591
  --keep-hierarchy Maintain source directory structure when exporting to Confluence.
573
592
  --flatten-hierarchy Flatten directories with no index.md or README.md when exporting to Confluence.
574
- --generated-by GENERATED_BY
593
+ --generated-by MARKDOWN
575
594
  Add prompt to pages (default: 'This page has been generated with a tool.').
576
595
  --no-generated-by Do not add 'generated by a tool' prompt to pages.
577
596
  --render-drawio Render draw.io diagrams as image files. (Installed utility required to covert.)
@@ -582,16 +601,24 @@ options:
582
601
  --no-render-latex Inline LaTeX formulas in Confluence page. (Marketplace app required to display.)
583
602
  --diagram-output-format {png,svg}
584
603
  Format for rendering Mermaid and draw.io diagrams (default: 'png').
604
+ --prefer-raster Prefer PNG over SVG when both exist (default: enabled).
605
+ --no-prefer-raster Use SVG files directly instead of preferring PNG equivalents.
585
606
  --heading-anchors Place an anchor at each section heading with GitHub-style same-page identifiers.
586
607
  --no-heading-anchors Don't place an anchor at each section heading.
587
608
  --ignore-invalid-url Emit a warning but otherwise ignore relative URLs that point to ill-specified locations.
588
- --local Write XHTML-based Confluence Storage Format files locally without invoking Confluence API.
589
- --headers KEY=VALUE [KEY=VALUE ...]
590
- Apply custom headers to all Confluence API requests.
609
+ --skip-title-heading Skip the first heading from document body when it is used as the page title (does not apply if title comes from front-matter).
610
+ --no-skip-title-heading
611
+ Keep the first heading in document body even when used as page title (default).
612
+ --title-prefix TEXT String to prepend to Confluence page title for each published page.
591
613
  --webui-links Enable Confluence Web UI links. (Typically required for on-prem versions of Confluence.)
592
614
  --alignment {center,left,right}
593
615
  Alignment for block-level images and formulas (default: 'center').
616
+ --max-image-width MAX_IMAGE_WIDTH
617
+ Maximum display width for images [px]. Wider images are scaled down for page display. Original size kept for full-size viewing.
594
618
  --use-panel Transform admonitions and alerts into a Confluence custom panel.
619
+ --local Write XHTML-based Confluence Storage Format files locally without invoking Confluence API.
620
+ --headers KEY=VALUE [KEY=VALUE ...]
621
+ Apply custom headers to all Confluence API requests.
595
622
  ```
596
623
 
597
624
  ### Confluence REST API v1 vs. v2
@@ -1,3 +1,49 @@
1
+ Metadata-Version: 2.4
2
+ Name: markdown-to-confluence
3
+ Version: 0.5.2
4
+ Summary: Publish Markdown files to Confluence wiki
5
+ Author-email: Levente Hunyadi <hunyadi@gmail.com>
6
+ Maintainer-email: Levente Hunyadi <hunyadi@gmail.com>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/hunyadi/md2conf
9
+ Project-URL: Source, https://github.com/hunyadi/md2conf
10
+ Keywords: markdown,converter,confluence
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: End Users/Desktop
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: cattrs>=25.3
27
+ Requires-Dist: lxml>=6.0
28
+ Requires-Dist: markdown>=3.10
29
+ Requires-Dist: orjson>=3.11
30
+ Requires-Dist: pymdown-extensions>=10.19
31
+ Requires-Dist: PyYAML>=6.0
32
+ Requires-Dist: requests>=2.32
33
+ Requires-Dist: truststore>=0.10
34
+ Requires-Dist: typing-extensions>=4.15; python_version < "3.12"
35
+ Provides-Extra: dev
36
+ Requires-Dist: markdown_doc>=0.1.6; extra == "dev"
37
+ Requires-Dist: types-lxml>=2025.11.25; extra == "dev"
38
+ Requires-Dist: types-markdown>=3.10; extra == "dev"
39
+ Requires-Dist: types-PyYAML>=6.0; extra == "dev"
40
+ Requires-Dist: types-requests>=2.32; extra == "dev"
41
+ Requires-Dist: mypy>=1.19; extra == "dev"
42
+ Requires-Dist: ruff>=0.14; extra == "dev"
43
+ Provides-Extra: formulas
44
+ Requires-Dist: matplotlib>=3.9; extra == "formulas"
45
+ Dynamic: license-file
46
+
1
47
  # Publish Markdown files to Confluence wiki
2
48
 
3
49
  Contributors to software projects typically write documentation in Markdown format and host Markdown files in collaborative version control systems (VCS) such as GitHub or GitLab to track changes and facilitate the review process. However, not everyone at a company has access to VCS, and documents are often circulated in Confluence wiki instead.
@@ -169,6 +215,19 @@ Provide generated-by prompt text in the Markdown file with a tag:
169
215
 
170
216
  Alternatively, use the `--generated-by GENERATED_BY` option. The tag takes precedence.
171
217
 
218
+ The generated-by text can also be templated with the following variables:
219
+
220
+ - `%{filename}`: the name of the Markdown file
221
+ - `%{filepath}`: the path of the Markdown file relative to the *source root*
222
+
223
+ When publishing a directory hierarchy, the *source root* is the directory in which *md2conf* is launched. When publishing a single file, this is the directory in which the Markdown file resides.
224
+
225
+ It can be used with the CLI `--generated-by` option or directly in the files:
226
+
227
+ ```markdown
228
+ <!-- generated-by: Do not edit! Check out the file %{filepath} in the repo -->
229
+ ```
230
+
172
231
  ### Publishing a single page
173
232
 
174
233
  *md2conf* has two modes of operation: *single-page mode* and *directory mode*.
@@ -409,6 +468,58 @@ This is useful if you have a page in a hierarchy that participates in parent-chi
409
468
 
410
469
  If the `title` attribute (in the front-matter) or the topmost unique heading (in the document body) changes, the Confluence page title is updated. A warning is raised if the new title conflicts with the title of another page, and thus cannot be updated.
411
470
 
471
+ #### Avoiding duplicate titles
472
+
473
+ By default, when *md2conf* extracts a page title from the first unique heading in a Markdown document, the heading remains in the document body. This means the title appears twice on the Confluence page: once as the page title at the top, and once as the first heading in the content.
474
+
475
+ To avoid this duplication, use the `--skip-title-heading` option. When enabled, *md2conf* removes the first heading from the document body if it was used as the page title. This option only takes effect when:
476
+
477
+ 1. The title was extracted from the document's first unique heading (not from front-matter), AND
478
+ 2. There is exactly one top-level heading in the document.
479
+
480
+ If the title comes from the `title` attribute in front-matter, the heading is preserved in the document body regardless of this setting, as the heading and title are considered separate.
481
+
482
+ **Example without `--skip-title-heading` (default):**
483
+
484
+ Markdown:
485
+ ```markdown
486
+ # Installation Guide
487
+
488
+ Follow these steps...
489
+ ```
490
+
491
+ Confluence displays:
492
+ - Page title: "Installation Guide"
493
+ - Content: Starts with heading "Installation Guide", followed by "Follow these steps..."
494
+
495
+ **Example with `--skip-title-heading`:**
496
+
497
+ Same Markdown source, but Confluence displays:
498
+ - Page title: "Installation Guide"
499
+ - Content: Starts directly with "Follow these steps..." (heading removed)
500
+
501
+ **Edge case: Abstract or introductory text before the title:**
502
+
503
+ When a document has content before the first heading (like an abstract), removing the heading eliminates the visual separator between the introductory text and the main content:
504
+
505
+ ```markdown
506
+ This is an abstract paragraph providing context.
507
+
508
+ # Document Title
509
+
510
+ This is the main document content.
511
+ ```
512
+
513
+ With `--skip-title-heading`, the output becomes:
514
+ - Page title: "Document Title"
515
+ - Content: "This is an abstract paragraph..." flows directly into "This is the main document content..." (no heading separator)
516
+
517
+ While the structure remains semantically correct, the visual separation is lost. If you need to maintain separation, consider these workarounds:
518
+
519
+ 1. **Use a horizontal rule:** Add `---` after the abstract to create visual separation
520
+ 2. **Use an admonition block:** Wrap the abstract in an info/note block
521
+ 3. **Use front-matter title:** Set `title` in front-matter to keep the heading in the body
522
+
412
523
  ### Labels
413
524
 
414
525
  If a Markdown document has the front-matter attribute `tags`, *md2conf* assigns the specified tags to the Confluence page as labels.
@@ -525,7 +636,7 @@ options:
525
636
  -r ROOT_PAGE Root Confluence page to create new pages. If omitted, will raise exception when creating new pages.
526
637
  --keep-hierarchy Maintain source directory structure when exporting to Confluence.
527
638
  --flatten-hierarchy Flatten directories with no index.md or README.md when exporting to Confluence.
528
- --generated-by GENERATED_BY
639
+ --generated-by MARKDOWN
529
640
  Add prompt to pages (default: 'This page has been generated with a tool.').
530
641
  --no-generated-by Do not add 'generated by a tool' prompt to pages.
531
642
  --render-drawio Render draw.io diagrams as image files. (Installed utility required to covert.)
@@ -536,16 +647,24 @@ options:
536
647
  --no-render-latex Inline LaTeX formulas in Confluence page. (Marketplace app required to display.)
537
648
  --diagram-output-format {png,svg}
538
649
  Format for rendering Mermaid and draw.io diagrams (default: 'png').
650
+ --prefer-raster Prefer PNG over SVG when both exist (default: enabled).
651
+ --no-prefer-raster Use SVG files directly instead of preferring PNG equivalents.
539
652
  --heading-anchors Place an anchor at each section heading with GitHub-style same-page identifiers.
540
653
  --no-heading-anchors Don't place an anchor at each section heading.
541
654
  --ignore-invalid-url Emit a warning but otherwise ignore relative URLs that point to ill-specified locations.
542
- --local Write XHTML-based Confluence Storage Format files locally without invoking Confluence API.
543
- --headers KEY=VALUE [KEY=VALUE ...]
544
- Apply custom headers to all Confluence API requests.
655
+ --skip-title-heading Skip the first heading from document body when it is used as the page title (does not apply if title comes from front-matter).
656
+ --no-skip-title-heading
657
+ Keep the first heading in document body even when used as page title (default).
658
+ --title-prefix TEXT String to prepend to Confluence page title for each published page.
545
659
  --webui-links Enable Confluence Web UI links. (Typically required for on-prem versions of Confluence.)
546
660
  --alignment {center,left,right}
547
661
  Alignment for block-level images and formulas (default: 'center').
662
+ --max-image-width MAX_IMAGE_WIDTH
663
+ Maximum display width for images [px]. Wider images are scaled down for page display. Original size kept for full-size viewing.
548
664
  --use-panel Transform admonitions and alerts into a Confluence custom panel.
665
+ --local Write XHTML-based Confluence Storage Format files locally without invoking Confluence API.
666
+ --headers KEY=VALUE [KEY=VALUE ...]
667
+ Apply custom headers to all Confluence API requests.
549
668
  ```
550
669
 
551
670
  ### Confluence REST API v1 vs. v2
@@ -34,6 +34,7 @@ md2conf/puppeteer-config.json
34
34
  md2conf/py.typed
35
35
  md2conf/scanner.py
36
36
  md2conf/serializer.py
37
+ md2conf/svg.py
37
38
  md2conf/text.py
38
39
  md2conf/toc.py
39
40
  md2conf/uri.py
@@ -46,6 +47,7 @@ tests/test_matcher.py
46
47
  tests/test_mermaid.py
47
48
  tests/test_processor.py
48
49
  tests/test_scanner.py
50
+ tests/test_svg.py
49
51
  tests/test_text.py
50
52
  tests/test_unit.py
51
53
  tests/test_xml.py
@@ -66,6 +68,10 @@ tests/source/math.md
66
68
  tests/source/mermaid.md
67
69
  tests/source/missing.md
68
70
  tests/source/sections.md
71
+ tests/source/skip_title_heading.md
72
+ tests/source/skip_title_heading_abstract.md
73
+ tests/source/skip_title_heading_frontmatter.md
74
+ tests/source/skip_title_heading_multiple.md
69
75
  tests/source/status.md
70
76
  tests/source/table.md
71
77
  tests/source/tags.md
@@ -94,6 +100,13 @@ tests/target/mermaid.xml
94
100
  tests/target/missing.xml
95
101
  tests/target/panel.xml
96
102
  tests/target/sections.xml
103
+ tests/target/skip_title_heading.xml
104
+ tests/target/skip_title_heading_abstract.xml
105
+ tests/target/skip_title_heading_abstract_removed.xml
106
+ tests/target/skip_title_heading_frontmatter.xml
107
+ tests/target/skip_title_heading_multiple.xml
108
+ tests/target/skip_title_heading_preserved.xml
109
+ tests/target/skip_title_heading_removed.xml
97
110
  tests/target/status.xml
98
111
  tests/target/table.xml
99
112
  tests/target/tags.xml
@@ -2,7 +2,7 @@ cattrs>=25.3
2
2
  lxml>=6.0
3
3
  markdown>=3.10
4
4
  orjson>=3.11
5
- pymdown-extensions>=10.17
5
+ pymdown-extensions>=10.19
6
6
  PyYAML>=6.0
7
7
  requests>=2.32
8
8
  truststore>=0.10
@@ -11,12 +11,12 @@ truststore>=0.10
11
11
  typing-extensions>=4.15
12
12
 
13
13
  [dev]
14
- markdown_doc>=0.1.5
15
- types-lxml>=2025.8.25
14
+ markdown_doc>=0.1.6
15
+ types-lxml>=2025.11.25
16
16
  types-markdown>=3.10
17
17
  types-PyYAML>=6.0
18
18
  types-requests>=2.32
19
- mypy>=1.18
19
+ mypy>=1.19
20
20
  ruff>=0.14
21
21
 
22
22
  [formulas]
@@ -5,7 +5,7 @@ Parses Markdown files, converts Markdown content into the Confluence Storage For
5
5
  Confluence API endpoints to upload images and content.
6
6
  """
7
7
 
8
- __version__ = "0.5.1"
8
+ __version__ = "0.5.2"
9
9
  __author__ = "Levente Hunyadi"
10
10
  __copyright__ = "Copyright 2022-2025, Levente Hunyadi"
11
11
  __license__ = "MIT"