Skip to content

Commit eaf03e3

Browse files
authored
Merge branch 'main' into fix/charset-none-fallback
2 parents ca0a3f5 + af56495 commit eaf03e3

21 files changed

Lines changed: 911 additions & 45 deletions

.github/dependabot.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ updates:
44
directory: "/"
55
schedule:
66
interval: "weekly"
7+
cooldown:
8+
default-days: 7

.github/workflows/pre-commit.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ jobs:
55
pre-commit:
66
runs-on: ubuntu-latest
77
steps:
8-
- uses: actions/checkout@v5
8+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
99
- name: Set up Python
10-
uses: actions/setup-python@v5
10+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
1111
with:
1212
python-version: "3.x"
1313

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ jobs:
55
tests:
66
runs-on: ubuntu-latest
77
steps:
8-
- uses: actions/checkout@v5
9-
- uses: actions/setup-python@v5
8+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
9+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
1010
with:
1111
python-version: |
1212
3.10

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,13 @@ Content Understanding is ideal when you need capabilities beyond what built-in o
188188
markitdown path-to-file.pdf --use-cu --cu-endpoint "<content_understanding_endpoint>"
189189
```
190190

191+
The endpoint can also be set once in the environment, so callers only need `--use-cu`:
192+
193+
```bash
194+
export MARKITDOWN_CU_ENDPOINT="<content_understanding_endpoint>"
195+
markitdown path-to-file.pdf --use-cu
196+
```
197+
191198
**Python API:**
192199

193200
```python
@@ -244,6 +251,13 @@ To use Microsoft Document Intelligence for conversion:
244251
markitdown path-to-file.pdf -o document.md -d -e "<document_intelligence_endpoint>"
245252
```
246253

254+
The endpoint can also be set once in the environment, so callers only need `-d`:
255+
256+
```bash
257+
export MARKITDOWN_DOCINTEL_ENDPOINT="<document_intelligence_endpoint>"
258+
markitdown path-to-file.pdf -o document.md -d
259+
```
260+
247261
More information about how to set up an Azure Document Intelligence Resource can be found [here](https://learn.microsoft.com/en-us/azure/ai-services/document-intelligence/how-to-guides/create-document-intelligence-resource?view=doc-intel-4.0.0)
248262

249263
### Python API
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# SPDX-FileCopyrightText: 2024-present Adam Fourney <adamfo@microsoft.com>
22
#
33
# SPDX-License-Identifier: MIT
4-
__version__ = "0.1.6"
4+
__version__ = "0.1.7"

packages/markitdown/src/markitdown/__main__.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#
33
# SPDX-License-Identifier: MIT
44
import argparse
5+
import os
56
import sys
67
import codecs
78
from typing import Any, Dict
@@ -98,13 +99,15 @@ def main():
9899
"-e",
99100
"--endpoint",
100101
type=str,
101-
help="Document Intelligence Endpoint. Required if using Document Intelligence.",
102+
default=os.environ.get("MARKITDOWN_DOCINTEL_ENDPOINT") or None,
103+
help="Document Intelligence Endpoint. Required if using Document Intelligence. Defaults to the MARKITDOWN_DOCINTEL_ENDPOINT environment variable.",
102104
)
103105

104106
parser.add_argument(
105107
"--cu-endpoint",
106108
type=str,
107-
help="Content Understanding Endpoint. Required if using --use-cu.",
109+
default=os.environ.get("MARKITDOWN_CU_ENDPOINT") or None,
110+
help="Content Understanding Endpoint. Required if using --use-cu. Defaults to the MARKITDOWN_CU_ENDPOINT environment variable.",
108111
)
109112

110113
parser.add_argument(
@@ -203,7 +206,8 @@ def main():
203206
if args.use_docintel:
204207
if args.endpoint is None:
205208
_exit_with_error(
206-
"Document Intelligence Endpoint is required when using Document Intelligence."
209+
"Document Intelligence Endpoint is required when using Document Intelligence. "
210+
"Pass -e/--endpoint or set MARKITDOWN_DOCINTEL_ENDPOINT."
207211
)
208212
elif args.filename is None:
209213
_exit_with_error("Filename is required when using Document Intelligence.")
@@ -215,9 +219,8 @@ def main():
215219
if args.cu_endpoint is None:
216220
_exit_with_error(
217221
"Content Understanding Endpoint (--cu-endpoint) is required when using --use-cu."
222+
"Pass --cu-endpoint or set MARKITDOWN_CU_ENDPOINT."
218223
)
219-
elif args.filename is None:
220-
_exit_with_error("Filename is required when using Content Understanding.")
221224

222225
cu_kwargs: Dict[str, Any] = {
223226
"cu_endpoint": args.cu_endpoint,

packages/markitdown/src/markitdown/_exceptions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,10 @@ def __init__(
7373
else:
7474
message += f" - {type(attempt.converter).__name__} threw {attempt.exc_info[0].__name__} with message: {attempt.exc_info[1]}\n"
7575

76+
if isinstance(attempt.exc_info[1], UnicodeDecodeError):
77+
message += (
78+
"The charset may have been automatically guessed incorrectly. "
79+
"If you didn't specify the charset, please retry with an explicitly defined one.\n"
80+
)
81+
7682
super().__init__(message)

packages/markitdown/src/markitdown/_markitdown.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import traceback
77
import io
88
from dataclasses import dataclass
9+
from email.message import Message
10+
from email.utils import collapse_rfc2231_value
911
from importlib.metadata import entry_points
1012
from typing import Any, List, Dict, Optional, Union, BinaryIO
1113
from pathlib import Path
@@ -51,6 +53,23 @@
5153
)
5254

5355

56+
def _get_content_disposition_filename(content_disposition: str) -> Optional[str]:
57+
message = Message()
58+
message["content-disposition"] = content_disposition
59+
60+
fallback_filename: Optional[str] = None
61+
extended_filename: Optional[str] = None
62+
for key, value in message.get_params(header="content-disposition", unquote=True):
63+
if key != "filename":
64+
continue
65+
if isinstance(value, tuple):
66+
extended_filename = collapse_rfc2231_value(value)
67+
elif fallback_filename is None:
68+
fallback_filename = value
69+
70+
return extended_filename or fallback_filename
71+
72+
5473
# Lower priority values are tried first.
5574
PRIORITY_SPECIFIC_FILE_FORMAT = (
5675
0.0 # e.g., .docx, .pdf, .xlsx, Or specific pages, e.g., wikipedia
@@ -512,9 +531,10 @@ def convert_response(
512531
filename: Optional[str] = None
513532
extension: Optional[str] = None
514533
if "content-disposition" in response.headers:
515-
m = re.search(r"filename=([^;]+)", response.headers["content-disposition"])
516-
if m:
517-
filename = m.group(1).strip("\"'")
534+
filename = _get_content_disposition_filename(
535+
response.headers["content-disposition"]
536+
)
537+
if filename is not None:
518538
_, _extension = os.path.splitext(filename)
519539
if len(_extension) > 0:
520540
extension = _extension
@@ -726,9 +746,9 @@ def _get_stream_info_guesses(
726746
# If it's text, also guess the charset
727747
charset = None
728748
if result.prediction.output.is_text:
729-
# Read the first 4k to guess the charset
749+
# Read the first 64k to guess the charset
730750
file_stream.seek(cur_pos)
731-
stream_page = file_stream.read(4096)
751+
stream_page = file_stream.read(65536)
732752
charset_result = charset_normalizer.from_bytes(stream_page).best()
733753

734754
if charset_result is not None:

packages/markitdown/src/markitdown/_uri_utils.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ def file_uri_to_path(file_uri: str) -> Tuple[str | None, str]:
1212
raise ValueError(f"Not a file URL: {file_uri}")
1313

1414
netloc = parsed.netloc if parsed.netloc else None
15-
path = os.path.abspath(url2pathname(parsed.path))
15+
path = url2pathname(parsed.path)
16+
if os.name == "nt" and path[:1] in "/\\" and path[2:3] == ":":
17+
path = path[1:]
18+
path = os.path.abspath(path)
1619
return netloc, path
1720

1821

packages/markitdown/src/markitdown/converter_utils/docx/math/latex_dict.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"\u20e8": "\\threeunderdot{{{0}}}",
5151
"\u20ec": "\\underrightharpoondown{{{0}}}",
5252
"\u20ed": "\\underleftharpoondown{{{0}}}",
53-
"\u20ee": "\\underledtarrow{{{0}}}",
53+
"\u20ee": "\\underleftarrow{{{0}}}",
5454
"\u20ef": "\\underrightarrow{{{0}}}",
5555
# Over | group
5656
"\u23b4": "\\overbracket{{{0}}}",
@@ -180,6 +180,8 @@
180180
"\U0001d452": "e",
181181
"\U0001d453": "f",
182182
"\U0001d454": "g",
183+
# U+1D455 is reserved: the math italic small h is unified with U+210E
184+
"\u210e": "h",
183185
"\U0001d456": "i",
184186
"\U0001d457": "j",
185187
"\U0001d458": "k",
@@ -222,6 +224,7 @@
222224

223225
CHR_DEFAULT = {
224226
"ACC_VAL": "\\hat{{{0}}}",
227+
"GROUP_CHR_VAL": "\\underbrace{{{0}}}",
225228
}
226229

227230
POS = {

0 commit comments

Comments
 (0)