any2md 1.0.3__tar.gz → 1.0.4__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.
- {any2md-1.0.3 → any2md-1.0.4}/CHANGELOG.md +46 -0
- {any2md-1.0.3 → any2md-1.0.4}/PKG-INFO +1 -1
- {any2md-1.0.3 → any2md-1.0.4}/any2md/__init__.py +1 -1
- {any2md-1.0.3 → any2md-1.0.4}/any2md/pipeline/text.py +7 -2
- any2md-1.0.4/docs/superpowers/plans/2026-04-26-issue-17-toc-followups.md +981 -0
- any2md-1.0.4/docs/superpowers/specs/2026-04-26-issue-17-toc-followups-design.md +239 -0
- any2md-1.0.4/scripts/audit-outputs.py +202 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_text_dedupe_toc_table.py +41 -0
- any2md-1.0.4/tests/unit/scripts/__init__.py +0 -0
- any2md-1.0.4/tests/unit/scripts/test_audit_outputs.py +81 -0
- {any2md-1.0.3 → any2md-1.0.4}/.gitignore +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/CODE_OF_CONDUCT.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/CONTRIBUTING.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/LICENSE +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/README.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/SECURITY.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/__main__.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/_docling.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/cli.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/config.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/converters/__init__.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/converters/docx.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/converters/html.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/converters/pdf.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/converters/txt.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/frontmatter.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/heuristics.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/pipeline/__init__.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/pipeline/cleanup.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/pipeline/structured.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/utils.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/any2md/validators.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/assets/logo-original.jpeg +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/assets/logo.png +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/assets/social-preview.jpg +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/architecture.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/cli-reference.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/output-format.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/superpowers/plans/2026-04-26-any2md-v1-phase1-foundation.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/superpowers/plans/2026-04-26-any2md-v1-phase2-docling-backend.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/superpowers/plans/2026-04-26-any2md-v1-phase3-figures-ocr-text-lane.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/superpowers/plans/2026-04-26-any2md-v1-phase4-polish-config-docs.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/superpowers/plans/2026-04-26-any2md-v1.0.2-implementation.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/superpowers/specs/2026-04-26-any2md-v1-design.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/superpowers/specs/2026-04-26-any2md-v1.0.2-design.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/superpowers/specs/2026-04-26-v1.0.3-empty-title-orphan-punct-design.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/troubleshooting.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/docs/upgrading-from-0.7.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/pyproject.toml +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/requirements.txt +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/scripts/validate-phase1.sh +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/__init__.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/cli/__init__.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/cli/test_cli_backend.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/cli/test_cli_existing_flags.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/cli/test_cli_exit_codes.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/cli/test_cli_high_fidelity.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/cli/test_cli_meta.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/cli/test_cli_no_arxiv_lookup.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/cli/test_cli_ocr_save_images.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/cli/test_cli_quiet_verbose.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/cli/test_cli_strict.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/conftest.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/fixtures/__init__.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/fixtures/docs/ligatures_and_softhyphens.txt +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/fixtures/docs/multi_column.pdf +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/fixtures/docs/table_heavy.docx +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/fixtures/docs/web_page.html +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/fixtures/make_fixtures.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/fixtures/snapshots/ligatures_and_softhyphens.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/fixtures/snapshots/multi_column.docling.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/fixtures/snapshots/multi_column.fallback.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/fixtures/snapshots/table_heavy.docling.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/fixtures/snapshots/table_heavy.fallback.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/fixtures/snapshots/web_page.md +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/integration/__init__.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/integration/test_docx_backend_override.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/integration/test_docx_docling.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/integration/test_docx_mammoth_fallback.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/integration/test_html_trafilatura.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/integration/test_pdf_backend_override.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/integration/test_pdf_docling.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/integration/test_pdf_pymupdf_fallback.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/integration/test_save_images.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/integration/test_snapshots.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/integration/test_txt.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/__init__.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/__init__.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_cleanup_footnote_markers.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_cleanup_html_entities.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_cleanup_html_entities_double_encoded.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_cleanup_ligatures.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_cleanup_nfc.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_cleanup_quotes_dashes.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_cleanup_soft_hyphens.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_cleanup_validate.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_cleanup_whitespace.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_runner.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_strip_orphan_punctuation.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_structured_body_cleanup_stages.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_structured_compact_tables.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_structured_heading_hierarchy.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_structured_lift_figures.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_structured_normalize_cites.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_text_dedupe_paragraphs.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_text_dedupe_toc.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_text_dehyphenate.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_text_repair_line_wraps.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_text_restore_lists_code.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_text_strip_cover_artifacts.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_text_strip_headers_footers.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_text_strip_repeated_byline.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/pipeline/test_text_strip_web_fragments.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/test_auto_id.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/test_config.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/test_content_hash.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/test_docling_helper.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/test_frontmatter_compose.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/test_frontmatter_helpers.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/test_heuristics.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/test_meta_overrides.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/test_pdf_complexity.py +0 -0
- {any2md-1.0.3 → any2md-1.0.4}/tests/unit/test_validators.py +0 -0
|
@@ -4,6 +4,52 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## [1.0.4] — 2026-04-26
|
|
8
|
+
|
|
9
|
+
Patch release. Two follow-ups deferred from v1.0.3 (#17): the T7
|
|
10
|
+
`dedupe_toc_table` now normalizes leader-dot padding from TOC
|
|
11
|
+
cells before matching against body headings, so Docling-rendered
|
|
12
|
+
TOC tables (e.g., `Purpose..........Page`) get properly stripped;
|
|
13
|
+
and the audit script's `leading-toc-table` check no longer
|
|
14
|
+
false-flags documents whose only GFM table sits at the end (skip
|
|
15
|
+
when the body contains no H2).
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- `pipeline/text.py::dedupe_toc_table` strips leader-dot padding
|
|
19
|
+
(`re.sub(r'\.{3,}.*$', '', cell)`) from each TOC cell before
|
|
20
|
+
lower-casing and matching against body H2/H3 titles. (The
|
|
21
|
+
text-block TOC variant already handles this via `_TOC_LINE_RE`'s
|
|
22
|
+
capture group — only the table-variant site needed the fix.)
|
|
23
|
+
- `scripts/audit-outputs.py` `leading-toc-table` check is now
|
|
24
|
+
gated on `"\n## "` being present in the body — when no H2
|
|
25
|
+
exists, no "leading-TOC region" is defined, so the check is
|
|
26
|
+
skipped instead of matching the entire document.
|
|
27
|
+
|
|
28
|
+
### Fixes
|
|
29
|
+
- TOC tables with dotted-page-number padding (typical Docling
|
|
30
|
+
output for SafeBreach-style PDFs — e.g., `Backup.pdf`,
|
|
31
|
+
`Password.pdf`) now get stripped by T7 in aggressive/maximum
|
|
32
|
+
profiles. Previously they survived because the cell content
|
|
33
|
+
`"1.1. Purpose............."` failed string-equality with the
|
|
34
|
+
body heading `"1.1. Purpose"`, dropping overlap below the 70%
|
|
35
|
+
threshold.
|
|
36
|
+
- Audit script no longer reports false-positive
|
|
37
|
+
`leading-toc-table` flags on documents whose only GFM table is
|
|
38
|
+
at the end and the body contains no `## ` headings.
|
|
39
|
+
|
|
40
|
+
### Tests
|
|
41
|
+
- New `test_strips_toc_table_with_leader_dot_padding` in
|
|
42
|
+
`tests/unit/pipeline/test_text_dedupe_toc_table.py` — TOC
|
|
43
|
+
table whose cells carry leader-dot padding mirroring later H2
|
|
44
|
+
headings still triggers the strip.
|
|
45
|
+
- New `tests/unit/scripts/test_audit_outputs.py` covering the
|
|
46
|
+
audit-script `leading-toc-table` check: no-H2-with-trailing-
|
|
47
|
+
table is NOT flagged; leading-table-before-H2 IS still flagged.
|
|
48
|
+
|
|
49
|
+
### Other
|
|
50
|
+
- `scripts/audit-outputs.py` is now tracked in version control
|
|
51
|
+
(was previously local-only).
|
|
52
|
+
|
|
7
53
|
## [1.0.3] — 2026-04-26
|
|
8
54
|
|
|
9
55
|
Patch release. Audit on the v1.0.2-regenerated corpus surfaced two
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: any2md
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: Convert PDF, DOCX, HTML, and TXT files — or web pages by URL — to clean, LLM-optimized Markdown with YAML frontmatter.
|
|
5
5
|
Project-URL: Homepage, https://github.com/rocklambros/any2md
|
|
6
6
|
Project-URL: Issues, https://github.com/rocklambros/any2md/issues
|
|
@@ -199,6 +199,7 @@ def dedupe_toc_block(text: str, options: "PipelineOptions") -> str:
|
|
|
199
199
|
return "\n".join(lines[end:]).lstrip("\n")
|
|
200
200
|
|
|
201
201
|
|
|
202
|
+
_LEADER_DOT_RE = re.compile(r"\.{3,}.*$")
|
|
202
203
|
_TABLE_ROW_RE = re.compile(r"^\s*\|.*\|\s*$")
|
|
203
204
|
_TABLE_SEP_RE = re.compile(r"^\s*\|[\s\-:|]+\|\s*$")
|
|
204
205
|
|
|
@@ -260,12 +261,16 @@ def dedupe_toc_table(text: str, options: "PipelineOptions") -> str:
|
|
|
260
261
|
continue
|
|
261
262
|
|
|
262
263
|
# Extract title text from non-numeric cells.
|
|
264
|
+
# Strip leader-dot padding ('Purpose............') before matching —
|
|
265
|
+
# Docling renders TOC entries with dotted page-number padding and
|
|
266
|
+
# the body-heading equivalent does not contain the dots.
|
|
263
267
|
toc_titles: set[str] = set()
|
|
264
268
|
for row in entry_rows:
|
|
265
269
|
cells = _split_table_cells(row)
|
|
266
270
|
for cell in cells:
|
|
267
|
-
|
|
268
|
-
|
|
271
|
+
normalized = _LEADER_DOT_RE.sub("", cell).strip()
|
|
272
|
+
if normalized and not normalized.replace(".", "").isdigit():
|
|
273
|
+
toc_titles.add(normalized.lower())
|
|
269
274
|
if not toc_titles:
|
|
270
275
|
i = tbl_end if tbl_end > i else i + 1
|
|
271
276
|
continue
|