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.
Files changed (48) hide show
  1. msdoc2docx-0.7.0/LICENSE +21 -0
  2. msdoc2docx-0.7.0/PKG-INFO +84 -0
  3. msdoc2docx-0.7.0/README.md +73 -0
  4. msdoc2docx-0.7.0/pyproject.toml +19 -0
  5. msdoc2docx-0.7.0/setup.cfg +4 -0
  6. msdoc2docx-0.7.0/src/doc2docx/__init__.py +6 -0
  7. msdoc2docx-0.7.0/src/doc2docx/__main__.py +4 -0
  8. msdoc2docx-0.7.0/src/doc2docx/binary/__init__.py +4 -0
  9. msdoc2docx-0.7.0/src/doc2docx/binary/reader.py +87 -0
  10. msdoc2docx-0.7.0/src/doc2docx/cfb/__init__.py +5 -0
  11. msdoc2docx-0.7.0/src/doc2docx/cfb/constants.py +9 -0
  12. msdoc2docx-0.7.0/src/doc2docx/cfb/container.py +370 -0
  13. msdoc2docx-0.7.0/src/doc2docx/cfb/directory.py +135 -0
  14. msdoc2docx-0.7.0/src/doc2docx/cfb/header.py +108 -0
  15. msdoc2docx-0.7.0/src/doc2docx/cli.py +100 -0
  16. msdoc2docx-0.7.0/src/doc2docx/converter.py +321 -0
  17. msdoc2docx-0.7.0/src/doc2docx/diagnostics.py +105 -0
  18. msdoc2docx-0.7.0/src/doc2docx/errors.py +37 -0
  19. msdoc2docx-0.7.0/src/doc2docx/model/__init__.py +57 -0
  20. msdoc2docx-0.7.0/src/doc2docx/model/document.py +806 -0
  21. msdoc2docx-0.7.0/src/doc2docx/msdoc/__init__.py +24 -0
  22. msdoc2docx-0.7.0/src/doc2docx/msdoc/document_properties.py +31 -0
  23. msdoc2docx-0.7.0/src/doc2docx/msdoc/fib.py +246 -0
  24. msdoc2docx-0.7.0/src/doc2docx/msdoc/fonts.py +132 -0
  25. msdoc2docx-0.7.0/src/doc2docx/msdoc/formatting.py +467 -0
  26. msdoc2docx-0.7.0/src/doc2docx/msdoc/headers.py +184 -0
  27. msdoc2docx-0.7.0/src/doc2docx/msdoc/pieces.py +407 -0
  28. msdoc2docx-0.7.0/src/doc2docx/msdoc/sections.py +259 -0
  29. msdoc2docx-0.7.0/src/doc2docx/msdoc/sprm.py +753 -0
  30. msdoc2docx-0.7.0/src/doc2docx/msdoc/styles.py +288 -0
  31. msdoc2docx-0.7.0/src/doc2docx/ooxml/__init__.py +3 -0
  32. msdoc2docx-0.7.0/src/doc2docx/ooxml/package.py +1163 -0
  33. msdoc2docx-0.7.0/src/msdoc2docx.egg-info/PKG-INFO +84 -0
  34. msdoc2docx-0.7.0/src/msdoc2docx.egg-info/SOURCES.txt +46 -0
  35. msdoc2docx-0.7.0/src/msdoc2docx.egg-info/dependency_links.txt +1 -0
  36. msdoc2docx-0.7.0/src/msdoc2docx.egg-info/entry_points.txt +2 -0
  37. msdoc2docx-0.7.0/src/msdoc2docx.egg-info/top_level.txt +1 -0
  38. msdoc2docx-0.7.0/tests/test_binary.py +18 -0
  39. msdoc2docx-0.7.0/tests/test_cfb.py +57 -0
  40. msdoc2docx-0.7.0/tests/test_cli.py +43 -0
  41. msdoc2docx-0.7.0/tests/test_conversion.py +352 -0
  42. msdoc2docx-0.7.0/tests/test_fonts_styles.py +146 -0
  43. msdoc2docx-0.7.0/tests/test_headers.py +34 -0
  44. msdoc2docx-0.7.0/tests/test_model.py +33 -0
  45. msdoc2docx-0.7.0/tests/test_pieces.py +94 -0
  46. msdoc2docx-0.7.0/tests/test_sections.py +70 -0
  47. msdoc2docx-0.7.0/tests/test_sprm.py +159 -0
  48. msdoc2docx-0.7.0/tests/test_tables.py +282 -0
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ """Public API for doc2docx."""
2
+
3
+ from .converter import ConversionResult, convert, inspect_doc
4
+
5
+ __all__ = ["ConversionResult", "convert", "inspect_doc"]
6
+ __version__ = "0.7.0"
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())
4
+
@@ -0,0 +1,4 @@
1
+ from .reader import BinaryReader
2
+
3
+ __all__ = ["BinaryReader"]
4
+
@@ -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
+
@@ -0,0 +1,5 @@
1
+ from .container import CompoundFile, CompoundFileLimits
2
+ from .directory import DirectoryEntry, ObjectType
3
+
4
+ __all__ = ["CompoundFile", "CompoundFileLimits", "DirectoryEntry", "ObjectType"]
5
+
@@ -0,0 +1,9 @@
1
+ CFB_SIGNATURE = bytes.fromhex("D0CF11E0A1B11AE1")
2
+
3
+ MAXREGSECT = 0xFFFFFFFA
4
+ DIFSECT = 0xFFFFFFFC
5
+ FATSECT = 0xFFFFFFFD
6
+ ENDOFCHAIN = 0xFFFFFFFE
7
+ FREESECT = 0xFFFFFFFF
8
+ NOSTREAM = 0xFFFFFFFF
9
+