Skip to content
Open
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
29 changes: 29 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
# Office files

*.doc
*.docx
*.pdf
*.xls
*.xlsx
*.ppt
*.pptx

# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]

# Session
Session.vim
Sessionx.vim

# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
*.pyc
.venv
dist/
83 changes: 83 additions & 0 deletions README_CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# doc2docx

[](https://pypi.org/project/doc2docx/)https://img.shields.io/pypi/v/doc2docx

直接在 Windows 或 macOS 上使用 Microsoft Word(**必须已安装**)将 `doc` 文件转换为 `docx` 格式。

在 Windows 上,该工具通过 [`win32com`](https://pypi.org/project/pywin32/) 实现;在 macOS 上,则通过 [JXA](https://github.com/JXA-Cookbook/JXA-Cookbook)(JavaScript for Automation,即用 JavaScript 编写的 AppleScript)实现。

## 安装

通过 brew:

```bash
brew install cosmojg/tap/doc2docx
```



通过 [pipx](https://pipxproject.github.io/pipx/):

```bash
pipx install doc2docx
```

通过 pip:

```bash
pip install doc2docx
```



## 命令行界面(CLI)

```context
用法:doc2docx [-h] [--keep-active] [--version] input [output]
示例用法:
原位转换单个 doc 文件,从 myfile.doc 转换为 myfile.docx:
doc2docx myfile.doc
批量转换文件夹内的 doc 文件(原位转换)。输出的 docx 文件将保存在同一文件夹中:
doc2docx myfolder/
转换单个 doc 文件并指定显式输出文件路径:
doc2docx input.doc output.docx
转换单个 doc 文件并输出到不同的指定文件夹:
doc2docx input.doc output_dir/
批量转换 doc 文件夹,输出到不同的指定文件夹:
doc2docx input_dir/ output_dir/
位置参数:
input 输入文件或文件夹。可批量转换整个文件夹或转换单个文件
output 输出文件或文件夹
可选参数:
-h, --help 显示此帮助信息并退出
--keep-active 转换后不关闭 Word
--version 显示版本信息并退出
```



## 库调用

```python
from doc2docx import convert
convert("input.doc")
convert("input.doc", "output.docx")
convert("my_doc_folder/")
```

所有不同的调用方式请参阅上面的 CLI 文档(或 `doc2docx --help`)。命令行和 Python 库的使用方式相同。

## Jupyter Notebook

如果在 Jupyter Notebook 中使用,需要安装 `ipywidgets` 以便正确渲染 tqdm 进度条。

```text
pip install ipywidgets
jupyter nbextension enable --py widgetsnbextension
```



## 致谢

衷心感谢 [@AlJohri](https://github.com/AlJohri) 提供的出色项目 [docx2pdf](https://github.com/AlJohri/docx2pdf),本工具正是基于它开发!
108 changes: 79 additions & 29 deletions doc2docx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,93 @@
import sys
from importlib.metadata import version
from pathlib import Path
import win32com.client

from tqdm.auto import tqdm

__version__ = version(__package__)

def convert_one(word_app, input_path, output_path, timeout=30):
"""
Convert a single .doc file to .docx using the Word application.

Args:
word_app: COM object of Word.Application (already started).
input_path (Path): Path to the source .doc file (must exist).
output_path (Path): Path to the target .docx file (parent directory must exist).
timeout (int): Optional, timeout for opening the document (seconds), default 30 seconds

Returns:
bool: Returns True if conversion succeeds, False if it fails.
"""
doc = None
try:
# Open the document
doc = word_app.Documents.Open(str(input_path))
# Save as .docx (wdFormatDocumentDefault = 16)
doc.SaveAs(str(output_path), FileFormat=16)
return True
except Exception as e:
# Can log here, e.g. logging.error(f"Conversion failed {input_path} -> {output_path}: {e}")
return False
finally:
# Ensure the document is closed (if opened)
if doc is not None:
try:
doc.Close(0) # wdDoNotSaveChanges
except Exception:
pass # Ignore close failure to avoid masking the original exception

def windows(paths, keep_active):
import win32com.client

"""
Args:
paths (dict): Must contain keys:
- 'batch' (bool): Whether batch mode
- 'input' (str): Input directory for batch mode, full file path for single file mode
- 'output' (str): Output directory for batch mode, output file path for single file mode (if directory, filename is auto-appended)
keep_active (bool): If False, quit the Word process after conversion.
"""
# Start the Word application
word = win32com.client.Dispatch("Word.Application")
wdFormatDocumentDefault = 16

if paths["batch"]:
for doc_filepath in tqdm(sorted(Path(paths["input"]).glob("[!~]*.doc*"))):
docx_filepath = Path(paths["output"]) / f"{str(doc_filepath.stem)}.docx"
doc = word.Documents.Open(str(doc_filepath))
try:
doc.SaveAs(str(docx_filepath), FileFormat=wdFormatDocumentDefault)
except:
raise
finally:
doc.Close(0)
else:
pbar = tqdm(total=1)
doc_filepath = Path(paths["input"]).resolve()
docx_filepath = Path(paths["output"]).resolve()
doc = word.Documents.Open(str(doc_filepath))
try:
doc.SaveAs(str(docx_filepath), FileFormat=wdFormatDocumentDefault)
except:
raise
finally:
doc.Close(0)
pbar.update(1)

if not keep_active:
word.Quit()
word.Visible = False # Recommended to run in background

try:
if paths["batch"]:
# ---- Batch mode ----
input_dir = Path(paths["input"])
output_dir = Path(paths["output"])
output_dir.mkdir(parents=True, exist_ok=True) # Ensure the output directory exists

# Collect all eligible .doc files (excluding temporary files)
doc_files = list(input_dir.glob("[!~]*.doc"))
# Progress bar
for doc_path in tqdm(doc_files, desc="Batch conversion"):
out_path = output_dir / f"{doc_path.stem}.docx"
success = convert_one(word, doc_path, out_path)
if not success:
# Can log failure info here, or print a warning
print(f"Warning: conversion failed {doc_path.name}")
else:
# ---- Single file mode ----
in_path = Path(paths["input"]).resolve()
out_path = Path(paths["output"]).resolve()

# If the output path is a directory, auto-complete the filename
if out_path.is_dir():
out_path = out_path / f"{in_path.stem}.docx"

# Single file mode progress bar (displayed only once)
with tqdm(total=1, desc="Converting single file") as pbar:
success = convert_one(word, in_path, out_path)
if success:
pbar.update(1)
else:
print("Conversion failed, please check the log or file validity.")

finally:
# Regardless of whether an exception occurs, quit Word if not needed to keep active
if not keep_active:
word.Quit()


def macos(paths, keep_active):
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[tool.poetry]
name = "doc2docx"
version = "0.2.4"
version = "0.2.5"
description = "Convert doc to docx on Windows or macOS directly using Microsoft Word (must be installed)."
authors = ["Cosmo <cosmo@cosmo.red>"]
readme = "README.md"
packages = [{include = "doc2docx"}]
license = "MIT"
homepage = "https://github.com/cosmojg/doc2docx"
repository = "https://github.com/cosmojg/doc2docx"
repository = "https://github.com/Harvey-Walker/doc2docx"
classifiers = [
"Operating System :: MacOS",
"Environment :: MacOS X",
Expand Down