Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 66 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ jobs:
test "$parent_count" -eq 1
candidate_commit="$(git rev-parse "$GITHUB_SHA^")"
evidence="docs/releases/v${package_version}-evidence.md"
test "$evidence" = "docs/releases/v0.1.0-evidence.md"
test -f "$evidence"
mapfile -t changed_paths < <(git diff-tree --no-commit-id --name-only -r "$GITHUB_SHA")
test "${#changed_paths[@]}" -eq 1
Expand All @@ -55,9 +54,12 @@ jobs:
uv run --frozen ty check src tests benchmarks scripts examples
git diff --check
- name: Build and inspect once
shell: bash
run: |
package_version="${GITHUB_REF_NAME#v}"
uv build
uv run --frozen python scripts/check_release_artifacts.py dist
uv run --frozen python scripts/check_release_artifacts.py dist \
--version "$package_version"
- name: Upload immutable release distributions
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
Expand All @@ -69,9 +71,39 @@ jobs:
if-no-files-found: error
retention-days: 7

testpypi:
wheel-smoke:
runs-on: ubuntu-latest
needs: build
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Python
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
python-version: ${{ matrix.python-version }}
- name: Download checked distributions
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-dists
path: dist
- name: Install the built wheel and run the native smoke
shell: bash
run: |
package_version="${GITHUB_REF_NAME#v}"
wheel="$(find dist -name '*.whl' -print -quit)"
python scripts/check_release_artifacts.py dist \
--version "$package_version" --verify-checksums
python -m venv "$RUNNER_TEMP/wheel-env"
"$RUNNER_TEMP/wheel-env/bin/pip" install "$wheel"
"$RUNNER_TEMP/wheel-env/bin/python" scripts/release_smoke.py \
"$RUNNER_TEMP/wheel-smoke" --version "$package_version"

testpypi:
runs-on: ubuntu-latest
needs: [build, wheel-smoke]
environment: testpypi
permissions:
contents: read
Expand All @@ -88,7 +120,11 @@ jobs:
name: release-dists
path: dist
- name: Verify downloaded distributions
run: python scripts/check_release_artifacts.py dist --verify-checksums
shell: bash
run: |
package_version="${GITHUB_REF_NAME#v}"
python scripts/check_release_artifacts.py dist \
--version "$package_version" --verify-checksums
- name: Stage only publishable files
run: |
mkdir publish
Expand All @@ -101,13 +137,14 @@ jobs:
- name: Smoke exact TestPyPI version
shell: bash
run: |
package_version="${GITHUB_REF_NAME#v}"
python -m venv "$RUNNER_TEMP/testpypi-env"
for attempt in {1..12}; do
if "$RUNNER_TEMP/testpypi-env/bin/pip" download \
--no-deps --only-binary=:all: \
--index-url https://test.pypi.org/simple/ \
--dest "$RUNNER_TEMP/testpypi-dist" \
opendocs-sdk==0.1.0; then
opendocs-sdk=="$package_version"; then
break
fi
test "$attempt" -lt 12
Expand All @@ -128,7 +165,7 @@ jobs:
PY
"$RUNNER_TEMP/testpypi-env/bin/pip" install "$testpypi_wheel"
"$RUNNER_TEMP/testpypi-env/bin/python" scripts/release_smoke.py \
"$RUNNER_TEMP/testpypi-smoke" --version 0.1.0
"$RUNNER_TEMP/testpypi-smoke" --version "$package_version"

pypi:
runs-on: ubuntu-latest
Expand All @@ -145,7 +182,11 @@ jobs:
name: release-dists
path: dist
- name: Verify downloaded distributions
run: python scripts/check_release_artifacts.py dist --verify-checksums
shell: bash
run: |
package_version="${GITHUB_REF_NAME#v}"
python scripts/check_release_artifacts.py dist \
--version "$package_version" --verify-checksums
- name: Stage only publishable files
run: |
mkdir publish
Expand Down Expand Up @@ -180,17 +221,22 @@ jobs:
name: release-dists
path: dist
- name: Verify downloaded distributions
run: python scripts/check_release_artifacts.py dist --verify-checksums
shell: bash
run: |
package_version="${GITHUB_REF_NAME#v}"
python scripts/check_release_artifacts.py dist \
--version "$package_version" --verify-checksums
- name: Download exact public wheel and verify identity
shell: bash
run: |
package_version="${GITHUB_REF_NAME#v}"
python -m venv "$RUNNER_TEMP/public-env"
for attempt in {1..12}; do
if "$RUNNER_TEMP/public-env/bin/pip" download \
--no-deps --only-binary=:all: \
--index-url https://pypi.org/simple \
--dest "$RUNNER_TEMP/public-dist" \
opendocs-sdk==0.1.0; then
opendocs-sdk=="$package_version"; then
break
fi
test "$attempt" -lt 12
Expand All @@ -211,7 +257,7 @@ jobs:
PY
"$RUNNER_TEMP/public-env/bin/pip" install "$public_wheel"
"$RUNNER_TEMP/public-env/bin/python" scripts/release_smoke.py \
"$RUNNER_TEMP/public-smoke" --version 0.1.0
"$RUNNER_TEMP/public-smoke" --version "$package_version"

github-release:
runs-on: ubuntu-latest
Expand All @@ -226,13 +272,21 @@ jobs:
name: release-dists
path: dist
- name: Verify downloaded distributions
run: python scripts/check_release_artifacts.py dist --verify-checksums
shell: bash
run: |
package_version="${GITHUB_REF_NAME#v}"
python scripts/check_release_artifacts.py dist \
--version "$package_version" --verify-checksums
- name: Create GitHub Release for the verified public package
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
package_version="${GITHUB_REF_NAME#v}"
notes="docs/releases/v${package_version}-notes.md"
test -f "$notes"
gh release create "$GITHUB_REF_NAME" \
dist/*.whl dist/*.tar.gz dist/SHA256SUMS \
--verify-tag \
--title "OpenDocs $GITHUB_REF_NAME" \
--notes-file docs/releases/v0.1.0-notes.md
--notes-file "$notes"
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@

## 未发布

### 新增

- 新增标准 `.xlsx` 工作簿解析:按源顺序保留全部 worksheet/chartsheet(含 hidden、
very hidden 与空 sheet)、常见保存值和货币/日期格式、Excel 表格、相离区域、合并跨度、
公式缓存缺失回退、标准文本对象以及原生图表事实。
- XLSX 内嵌图片与图表支持可选视觉语义补充;视觉只解释趋势、标注、关系与含义,任一模型
不可用、超时或失败均保留原生结果并产生可定位 warning。

### 兼容性与限制

- XLSX 不使用 `max_pages` 限制工作表数量,而由私有结构预算在昂贵加载前约束资源;公共
`ParseOptions` 与 Markdown 返回契约保持不变。
- 仅支持 `.xlsx`,不支持 `.xls`、`.xlsm` 或 `.xlsb`;不重算公式,不访问外部 URL、链接
工作簿或数据连接,也不承诺字体、颜色、边框、尺寸或像素级 Excel 外观保真。

### 修复

- 限制单页 PDF 视觉候选数量,避免异常重叠对象触发高复杂度区域合并。
Expand All @@ -13,7 +28,7 @@

## 0.1.0 - Alpha

OpenDocs 的首个公开 Alpha 将本地文档转换为 Markdown。
OpenDocs 的首个公开 Alpha `opendocs-sdk==0.1.0` 将本地文档转换为 Markdown。

### 支持范围

Expand Down
28 changes: 24 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![Downloads](https://img.shields.io/pypi/dm/opendocs-sdk.svg)](https://pypistats.org/packages/opendocs-sdk)

OpenDocs is a Python SDK that converts local documents into clean Markdown —
TXT, Markdown, images, PDF (native / hybrid / vision), DOCX, and PPTX — through a unified
TXT, Markdown, images, PDF (native / hybrid / vision), DOCX, PPTX, and XLSX — through a unified
sync/async API.

> **Package name**: `opendocs-sdk` &nbsp;|&nbsp; **Import name**: `opendocs` &nbsp;|&nbsp; **Python**: 3.11+
Expand Down Expand Up @@ -69,6 +69,10 @@ downloaded before calling OpenDocs.
| PNG / JPEG / WebP | ✅ | Static images only; sanitized before the configured vision model sees them |
| DOCX | ✅ | Continuous authored body flow with structured text, lists, links, tables, explicit breaks, and inline images |
| PPTX | ✅ | Slide and shape-tree order with text, tables, accessible charts, groups, and inline images |
| XLSX (`.xlsx`) | ✅ | All sheet-like entries in source order, saved values, tables/regions, merges, standard text objects, native chart facts, and optional visual interpretation |

Only standard `.xlsx` workbooks are supported. Legacy `.xls`, macro-enabled `.xlsm`, binary
`.xlsb`, and other spreadsheet formats are not accepted.

## Vision parsing

Expand All @@ -87,8 +91,9 @@ apt-get install poppler-utils
```

Standalone images require `VisionConfig`. PDFs and Office documents without vision configuration
preserve usable native content and emit deterministic warnings for visual regions; a document with
no usable native content raises `VisionRequiredError`.
preserve usable native content and emit deterministic warnings for visual regions; XLSX always
keeps its native sheet and chart facts when visual enrichment is unavailable. A document with no
usable native content raises `VisionRequiredError` where that format requires vision.

```python
from opendocs import ParseOptions, VisionConfig, parse
Expand All @@ -111,7 +116,7 @@ response — use distinct typed exceptions for precise error handling.
cross-document concurrency themselves (e.g. with an `asyncio.Semaphore`); see the
[independent consumer example](examples/basic_consumer/README.md).

### DOCX & PPTX details
### DOCX, PPTX & XLSX details

DOCX extraction preserves authored body paragraphs, headings, lists, safe links, tables, merged
cells, explicit page breaks, and inline raster-image positions. A DOCX remains one continuous
Expand All @@ -121,13 +126,28 @@ PPTX extraction emits every slide boundary and traverses each slide's shape tree
including recursive groups, text, tables, accessible chart data, and raster pictures. Exact
duplicate embedded images are analyzed once per parse and replayed at every authored slot.

XLSX extraction emits every worksheet and chartsheet in workbook order, including visible, hidden,
very hidden, and empty sheets. It preserves non-empty regions, Excel tables, merged-cell spans,
standard comments/text boxes/links/header-footer text, and common saved display semantics such as
`$`, `€`, `£`, and `¥` currency, grouping, decimals, percentages, dates, and times. Saved formula
caches are preferred; when a cache is missing, the formula text is returned with a warning. OpenDocs
does not recalculate formulas or fetch linked workbooks, data connections, or URLs—the reference is
preserved as text only.

Chart titles, labels, series, categories, and accessible values come from native workbook data.
When vision is configured, normalized chart fact cards and embedded images may add trend, label,
relationship, or meaning interpretation. This enrichment is fail-open and never replaces native
facts. XLSX output does not promise Excel pixel appearance, fonts, colors, borders, dimensions, or
other visual styling fidelity.

### How OpenDocs compares

| Feature | OpenDocs | marker | docling | unstructured | pypdf |
| --- | :---: | :---: | :---: | :---: | :---: |
| PDF → Markdown | ✅ | ✅ | ✅ | ✅ | ❌ |
| DOCX → Markdown | ✅ | ❌ | ✅ | ✅ | N/A |
| PPTX → Markdown | ✅ | ❌ | ✅ | ✅ | N/A |
| XLSX → Markdown | ✅ | ❌ | ✅ | ✅ | N/A |
| LLM vision integration | ✅ | ❌ | ❌ | ❌ | ❌ |
| Sync + Async API | ✅ | ❌ | ❌ | ❌ | ❌ |
| No external service required | ✅ | ✅ | ✅ | ⚠️ | ✅ |
Expand Down
Loading