msdoc2docx 0.7.0__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.
- msdoc2docx-0.7.0/LICENSE +21 -0
- msdoc2docx-0.7.0/PKG-INFO +84 -0
- msdoc2docx-0.7.0/README.md +73 -0
- msdoc2docx-0.7.0/pyproject.toml +19 -0
- msdoc2docx-0.7.0/setup.cfg +4 -0
- msdoc2docx-0.7.0/src/doc2docx/__init__.py +6 -0
- msdoc2docx-0.7.0/src/doc2docx/__main__.py +4 -0
- msdoc2docx-0.7.0/src/doc2docx/binary/__init__.py +4 -0
- msdoc2docx-0.7.0/src/doc2docx/binary/reader.py +87 -0
- msdoc2docx-0.7.0/src/doc2docx/cfb/__init__.py +5 -0
- msdoc2docx-0.7.0/src/doc2docx/cfb/constants.py +9 -0
- msdoc2docx-0.7.0/src/doc2docx/cfb/container.py +370 -0
- msdoc2docx-0.7.0/src/doc2docx/cfb/directory.py +135 -0
- msdoc2docx-0.7.0/src/doc2docx/cfb/header.py +108 -0
- msdoc2docx-0.7.0/src/doc2docx/cli.py +100 -0
- msdoc2docx-0.7.0/src/doc2docx/converter.py +321 -0
- msdoc2docx-0.7.0/src/doc2docx/diagnostics.py +105 -0
- msdoc2docx-0.7.0/src/doc2docx/errors.py +37 -0
- msdoc2docx-0.7.0/src/doc2docx/model/__init__.py +57 -0
- msdoc2docx-0.7.0/src/doc2docx/model/document.py +806 -0
- msdoc2docx-0.7.0/src/doc2docx/msdoc/__init__.py +24 -0
- msdoc2docx-0.7.0/src/doc2docx/msdoc/document_properties.py +31 -0
- msdoc2docx-0.7.0/src/doc2docx/msdoc/fib.py +246 -0
- msdoc2docx-0.7.0/src/doc2docx/msdoc/fonts.py +132 -0
- msdoc2docx-0.7.0/src/doc2docx/msdoc/formatting.py +467 -0
- msdoc2docx-0.7.0/src/doc2docx/msdoc/headers.py +184 -0
- msdoc2docx-0.7.0/src/doc2docx/msdoc/pieces.py +407 -0
- msdoc2docx-0.7.0/src/doc2docx/msdoc/sections.py +259 -0
- msdoc2docx-0.7.0/src/doc2docx/msdoc/sprm.py +753 -0
- msdoc2docx-0.7.0/src/doc2docx/msdoc/styles.py +288 -0
- msdoc2docx-0.7.0/src/doc2docx/ooxml/__init__.py +3 -0
- msdoc2docx-0.7.0/src/doc2docx/ooxml/package.py +1163 -0
- msdoc2docx-0.7.0/src/msdoc2docx.egg-info/PKG-INFO +84 -0
- msdoc2docx-0.7.0/src/msdoc2docx.egg-info/SOURCES.txt +46 -0
- msdoc2docx-0.7.0/src/msdoc2docx.egg-info/dependency_links.txt +1 -0
- msdoc2docx-0.7.0/src/msdoc2docx.egg-info/entry_points.txt +2 -0
- msdoc2docx-0.7.0/src/msdoc2docx.egg-info/top_level.txt +1 -0
- msdoc2docx-0.7.0/tests/test_binary.py +18 -0
- msdoc2docx-0.7.0/tests/test_cfb.py +57 -0
- msdoc2docx-0.7.0/tests/test_cli.py +43 -0
- msdoc2docx-0.7.0/tests/test_conversion.py +352 -0
- msdoc2docx-0.7.0/tests/test_fonts_styles.py +146 -0
- msdoc2docx-0.7.0/tests/test_headers.py +34 -0
- msdoc2docx-0.7.0/tests/test_model.py +33 -0
- msdoc2docx-0.7.0/tests/test_pieces.py +94 -0
- msdoc2docx-0.7.0/tests/test_sections.py +70 -0
- msdoc2docx-0.7.0/tests/test_sprm.py +159 -0
- msdoc2docx-0.7.0/tests/test_tables.py +282 -0
msdoc2docx-0.7.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 doc2docx contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: msdoc2docx
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: A specification-driven, pure-Python Word binary .doc to .docx converter
|
|
5
|
+
Author: doc2docx contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# doc2docx
|
|
13
|
+
|
|
14
|
+
`doc2docx` is a specification-driven, pure-Python converter for Microsoft Word
|
|
15
|
+
97–2003 binary `.doc` files. It does not invoke Microsoft Word, LibreOffice,
|
|
16
|
+
COM, Java, or an external conversion executable.
|
|
17
|
+
|
|
18
|
+
The current M0–M5b implementation supports unencrypted CFB/OLE Word documents,
|
|
19
|
+
the `0Table`/`1Table` selection mechanism, CLX piece tables, compressed and
|
|
20
|
+
UTF-16LE text pieces, and the main document story. It reads CHPX/PAPX FKPs and
|
|
21
|
+
preserves a useful first set of direct character and paragraph properties. It
|
|
22
|
+
also reads the SttbfFfn font table, emits DOCX font definitions, converts
|
|
23
|
+
paragraph/character styles with `basedOn` inheritance, resolves style-relative
|
|
24
|
+
toggles, and applies simple and complex piece-level PRMs in specification order.
|
|
25
|
+
It reconstructs tables from DOC cell/row markers, including nested tables, and
|
|
26
|
+
preserves their column grid, preferred cell widths, basic merges, alignment,
|
|
27
|
+
row sizing, Word 97 borders and shading, and default or range-specific cell
|
|
28
|
+
margins. It also follows main-story `PlcfSed` records through `Sed.fcSepx` to
|
|
29
|
+
each `Sepx.grpprl`, reconstructing single or multiple sections. Section-break
|
|
30
|
+
types, paper size, portrait/landscape orientation, page margins, header/footer
|
|
31
|
+
distance, and gutter width are emitted as WordprocessingML section properties.
|
|
32
|
+
It parses `PlcfHdd`, removes each story's guard paragraph, preserves the six
|
|
33
|
+
default/even/first header and footer positions per section, and retains empty-story
|
|
34
|
+
inheritance. Header/footer parts, relationships, first-page rules, and the DOP
|
|
35
|
+
facing-pages setting are written as native DOCX package parts.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
```console
|
|
40
|
+
python -m pip install msdoc2docx
|
|
41
|
+
doc2docx input.doc
|
|
42
|
+
doc2docx input.doc -o output.docx --report report.json
|
|
43
|
+
doc2docx inspect input.doc --json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
To install from a local source checkout instead, run `python -m pip install .`.
|
|
47
|
+
|
|
48
|
+
Python API:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from doc2docx import convert
|
|
52
|
+
|
|
53
|
+
result = convert("input.doc", "output.docx")
|
|
54
|
+
print(result.report.to_dict())
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
M5b currently preserves bold, italic, strike, double strike, capitalization,
|
|
58
|
+
hidden text, underline, text color, highlight, font size, vertical alignment,
|
|
59
|
+
paragraph justification, indents, spacing, line spacing, and keep/page-break
|
|
60
|
+
flags, font names, paragraph/character style references, style inheritance,
|
|
61
|
+
basic section/page layout, and ordinary paragraph/table content in headers and
|
|
62
|
+
footers.
|
|
63
|
+
Conditional table styles, newer color-based table shading, cell spacing,
|
|
64
|
+
numbering styles, multi-column layout, page-number settings, header/footer
|
|
65
|
+
textboxes and shapes, other secondary stories, images, live fields, embedded
|
|
66
|
+
objects, and encrypted documents are intentionally deferred to later iterations.
|
|
67
|
+
Unsupported or lossy content is reported rather than silently treated as fully
|
|
68
|
+
converted.
|
|
69
|
+
|
|
70
|
+
## Tests
|
|
71
|
+
|
|
72
|
+
The test suite uses only the Python standard library:
|
|
73
|
+
|
|
74
|
+
```console
|
|
75
|
+
PYTHONPATH=src python -m unittest discover -v
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
It includes constructed CFB version 3/version 4 files, regular and mini streams,
|
|
79
|
+
both Table stream variants, mixed compressed/UTF-16 text pieces, UTF-16 surrogate
|
|
80
|
+
pair coordinates, CHPX/PAPX and piece-level PRM formatting, font/style table
|
|
81
|
+
parsing, nested table reconstruction, grids, cell margins and shading, PlcfSed and
|
|
82
|
+
Sepx page-layout parsing, multi-section break placement, PlcfHdd guard and story
|
|
83
|
+
mapping, header/footer OPC relationships, malformed input checks, CLI coverage,
|
|
84
|
+
and end-to-end DOCX package validation.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# doc2docx
|
|
2
|
+
|
|
3
|
+
`doc2docx` is a specification-driven, pure-Python converter for Microsoft Word
|
|
4
|
+
97–2003 binary `.doc` files. It does not invoke Microsoft Word, LibreOffice,
|
|
5
|
+
COM, Java, or an external conversion executable.
|
|
6
|
+
|
|
7
|
+
The current M0–M5b implementation supports unencrypted CFB/OLE Word documents,
|
|
8
|
+
the `0Table`/`1Table` selection mechanism, CLX piece tables, compressed and
|
|
9
|
+
UTF-16LE text pieces, and the main document story. It reads CHPX/PAPX FKPs and
|
|
10
|
+
preserves a useful first set of direct character and paragraph properties. It
|
|
11
|
+
also reads the SttbfFfn font table, emits DOCX font definitions, converts
|
|
12
|
+
paragraph/character styles with `basedOn` inheritance, resolves style-relative
|
|
13
|
+
toggles, and applies simple and complex piece-level PRMs in specification order.
|
|
14
|
+
It reconstructs tables from DOC cell/row markers, including nested tables, and
|
|
15
|
+
preserves their column grid, preferred cell widths, basic merges, alignment,
|
|
16
|
+
row sizing, Word 97 borders and shading, and default or range-specific cell
|
|
17
|
+
margins. It also follows main-story `PlcfSed` records through `Sed.fcSepx` to
|
|
18
|
+
each `Sepx.grpprl`, reconstructing single or multiple sections. Section-break
|
|
19
|
+
types, paper size, portrait/landscape orientation, page margins, header/footer
|
|
20
|
+
distance, and gutter width are emitted as WordprocessingML section properties.
|
|
21
|
+
It parses `PlcfHdd`, removes each story's guard paragraph, preserves the six
|
|
22
|
+
default/even/first header and footer positions per section, and retains empty-story
|
|
23
|
+
inheritance. Header/footer parts, relationships, first-page rules, and the DOP
|
|
24
|
+
facing-pages setting are written as native DOCX package parts.
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```console
|
|
29
|
+
python -m pip install msdoc2docx
|
|
30
|
+
doc2docx input.doc
|
|
31
|
+
doc2docx input.doc -o output.docx --report report.json
|
|
32
|
+
doc2docx inspect input.doc --json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To install from a local source checkout instead, run `python -m pip install .`.
|
|
36
|
+
|
|
37
|
+
Python API:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from doc2docx import convert
|
|
41
|
+
|
|
42
|
+
result = convert("input.doc", "output.docx")
|
|
43
|
+
print(result.report.to_dict())
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
M5b currently preserves bold, italic, strike, double strike, capitalization,
|
|
47
|
+
hidden text, underline, text color, highlight, font size, vertical alignment,
|
|
48
|
+
paragraph justification, indents, spacing, line spacing, and keep/page-break
|
|
49
|
+
flags, font names, paragraph/character style references, style inheritance,
|
|
50
|
+
basic section/page layout, and ordinary paragraph/table content in headers and
|
|
51
|
+
footers.
|
|
52
|
+
Conditional table styles, newer color-based table shading, cell spacing,
|
|
53
|
+
numbering styles, multi-column layout, page-number settings, header/footer
|
|
54
|
+
textboxes and shapes, other secondary stories, images, live fields, embedded
|
|
55
|
+
objects, and encrypted documents are intentionally deferred to later iterations.
|
|
56
|
+
Unsupported or lossy content is reported rather than silently treated as fully
|
|
57
|
+
converted.
|
|
58
|
+
|
|
59
|
+
## Tests
|
|
60
|
+
|
|
61
|
+
The test suite uses only the Python standard library:
|
|
62
|
+
|
|
63
|
+
```console
|
|
64
|
+
PYTHONPATH=src python -m unittest discover -v
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
It includes constructed CFB version 3/version 4 files, regular and mini streams,
|
|
68
|
+
both Table stream variants, mixed compressed/UTF-16 text pieces, UTF-16 surrogate
|
|
69
|
+
pair coordinates, CHPX/PAPX and piece-level PRM formatting, font/style table
|
|
70
|
+
parsing, nested table reconstruction, grids, cell margins and shading, PlcfSed and
|
|
71
|
+
Sepx page-layout parsing, multi-section break placement, PlcfHdd guard and story
|
|
72
|
+
mapping, header/footer OPC relationships, malformed input checks, CLI coverage,
|
|
73
|
+
and end-to-end DOCX package validation.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "msdoc2docx"
|
|
7
|
+
version = "0.7.0"
|
|
8
|
+
description = "A specification-driven, pure-Python Word binary .doc to .docx converter"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [{name = "doc2docx contributors"}]
|
|
13
|
+
dependencies = []
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
doc2docx = "doc2docx.cli:main"
|
|
17
|
+
|
|
18
|
+
[tool.setuptools.packages.find]
|
|
19
|
+
where = ["src"]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Small, bounded little-endian binary reader."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import struct
|
|
6
|
+
|
|
7
|
+
from ..errors import BinaryBoundsError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BinaryReader:
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
data: bytes | bytearray | memoryview,
|
|
14
|
+
*,
|
|
15
|
+
label: str = "binary data",
|
|
16
|
+
base_offset: int = 0,
|
|
17
|
+
) -> None:
|
|
18
|
+
self._data = memoryview(data).cast("B")
|
|
19
|
+
self._position = 0
|
|
20
|
+
self.label = label
|
|
21
|
+
self.base_offset = base_offset
|
|
22
|
+
|
|
23
|
+
def __len__(self) -> int:
|
|
24
|
+
return len(self._data)
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def position(self) -> int:
|
|
28
|
+
return self._position
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def remaining(self) -> int:
|
|
32
|
+
return len(self._data) - self._position
|
|
33
|
+
|
|
34
|
+
def _check(self, offset: int, size: int) -> None:
|
|
35
|
+
if offset < 0 or size < 0 or offset > len(self._data) - size:
|
|
36
|
+
absolute = self.base_offset + max(offset, 0)
|
|
37
|
+
raise BinaryBoundsError(
|
|
38
|
+
f"{self.label}: read of {size} bytes at offset 0x{absolute:X} "
|
|
39
|
+
f"exceeds {len(self._data)}-byte structure"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def seek(self, offset: int) -> None:
|
|
43
|
+
self._check(offset, 0)
|
|
44
|
+
self._position = offset
|
|
45
|
+
|
|
46
|
+
def skip(self, size: int) -> None:
|
|
47
|
+
self.seek(self._position + size)
|
|
48
|
+
|
|
49
|
+
def read(self, size: int) -> bytes:
|
|
50
|
+
self._check(self._position, size)
|
|
51
|
+
start = self._position
|
|
52
|
+
self._position += size
|
|
53
|
+
return self._data[start : start + size].tobytes()
|
|
54
|
+
|
|
55
|
+
def read_at(self, offset: int, size: int) -> bytes:
|
|
56
|
+
self._check(offset, size)
|
|
57
|
+
return self._data[offset : offset + size].tobytes()
|
|
58
|
+
|
|
59
|
+
def subreader(self, offset: int, size: int, *, label: str | None = None) -> "BinaryReader":
|
|
60
|
+
self._check(offset, size)
|
|
61
|
+
return BinaryReader(
|
|
62
|
+
self._data[offset : offset + size],
|
|
63
|
+
label=label or self.label,
|
|
64
|
+
base_offset=self.base_offset + offset,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def _unpack(self, fmt: str, size: int) -> int:
|
|
68
|
+
self._check(self._position, size)
|
|
69
|
+
value = struct.unpack_from(fmt, self._data, self._position)[0]
|
|
70
|
+
self._position += size
|
|
71
|
+
return int(value)
|
|
72
|
+
|
|
73
|
+
def u8(self) -> int:
|
|
74
|
+
return self._unpack("<B", 1)
|
|
75
|
+
|
|
76
|
+
def u16(self) -> int:
|
|
77
|
+
return self._unpack("<H", 2)
|
|
78
|
+
|
|
79
|
+
def u32(self) -> int:
|
|
80
|
+
return self._unpack("<I", 4)
|
|
81
|
+
|
|
82
|
+
def i32(self) -> int:
|
|
83
|
+
return self._unpack("<i", 4)
|
|
84
|
+
|
|
85
|
+
def u64(self) -> int:
|
|
86
|
+
return self._unpack("<Q", 8)
|
|
87
|
+
|