Echo9k commited on
Commit
73683aa
·
1 Parent(s): 780929c

reorganized structure

Browse files
Files changed (7) hide show
  1. app.py +65 -239
  2. config.py +9 -0
  3. config.yaml +12 -0
  4. download_models_hf.py +66 -0
  5. model.py +20 -0
  6. pdf_processor.py +79 -0
  7. utils.py +40 -0
app.py CHANGED
@@ -1,252 +1,78 @@
1
- # Copyright (c) Opendatalab. All rights reserved.
2
-
3
- import base64
4
- import json
5
  import os
6
- import time
7
- import zipfile
8
- from pathlib import Path
9
- import re
10
- import uuid
11
- import pymupdf
12
-
13
- # os.system('pip install -U magic-pdf==0.10.5')
14
- os.system('pip uninstall -y magic-pdf')
15
- os.system('pip install git+https://github.com/opendatalab/MinerU.git@dev')
16
- # os.system('pip install git+https://github.com/myhloli/Magic-PDF.git@dev')
17
-
18
- os.system('wget https://github.com/opendatalab/MinerU/raw/dev/scripts/download_models_hf.py -O download_models_hf.py')
19
- os.system('python download_models_hf.py')
20
-
21
- with open('/home/user/magic-pdf.json', 'r') as file:
22
- data = json.load(file)
23
-
24
- data['device-mode'] = "cuda"
25
- if os.getenv('apikey'):
26
- data['llm-aided-config']['title_aided']['api_key'] = os.getenv('apikey')
27
- data['llm-aided-config']['title_aided']['enable'] = True
28
-
29
- with open('/home/user/magic-pdf.json', 'w') as file:
30
- json.dump(data, file, indent=4)
31
-
32
- os.system('cp -r paddleocr /home/user/.paddleocr')
33
- from gradio_pdf import PDF
34
-
35
  import gradio as gr
36
- from loguru import logger
37
-
38
- from magic_pdf.data.data_reader_writer import FileBasedDataReader
39
- from magic_pdf.libs.hash_utils import compute_sha256
40
- from magic_pdf.tools.common import do_parse, prepare_env
41
-
42
-
43
- def read_fn(path):
44
- disk_rw = FileBasedDataReader(os.path.dirname(path))
45
- return disk_rw.read(os.path.basename(path))
46
-
47
-
48
- def parse_pdf(doc_path, output_dir, end_page_id, is_ocr, layout_mode, formula_enable, table_enable, language):
49
- os.makedirs(output_dir, exist_ok=True)
50
-
51
- try:
52
- file_name = f"{str(Path(doc_path).stem)}_{time.time()}"
53
- pdf_data = read_fn(doc_path)
54
- if is_ocr:
55
- parse_method = "ocr"
56
- else:
57
- parse_method = "auto"
58
- local_image_dir, local_md_dir = prepare_env(output_dir, file_name, parse_method)
59
- do_parse(
60
- output_dir,
61
- file_name,
62
- pdf_data,
63
- [],
64
- parse_method,
65
- False,
66
- end_page_id=end_page_id,
67
- layout_model=layout_mode,
68
- formula_enable=formula_enable,
69
- table_enable=table_enable,
70
- lang=language,
71
- f_dump_orig_pdf=False,
72
- )
73
- return local_md_dir, file_name
74
- except Exception as e:
75
- logger.exception(e)
76
-
77
-
78
- def compress_directory_to_zip(directory_path, output_zip_path):
79
- """
80
- 压缩指定目录到一个 ZIP 文件。
81
-
82
- :param directory_path: 要压缩的目录路径
83
- :param output_zip_path: 输出的 ZIP 文件路径
84
- """
85
- try:
86
- with zipfile.ZipFile(output_zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
87
-
88
- # 遍历目录中的所有文件和子目录
89
- for root, dirs, files in os.walk(directory_path):
90
- for file in files:
91
- # 构建完整的文件路径
92
- file_path = os.path.join(root, file)
93
- # 计算相对路径
94
- arcname = os.path.relpath(file_path, directory_path)
95
- # 添加文件到 ZIP 文件
96
- zipf.write(file_path, arcname)
97
- return 0
98
- except Exception as e:
99
- logger.exception(e)
100
- return -1
101
-
102
-
103
- def image_to_base64(image_path):
104
- with open(image_path, "rb") as image_file:
105
- return base64.b64encode(image_file.read()).decode('utf-8')
106
-
107
-
108
- def replace_image_with_base64(markdown_text, image_dir_path):
109
- # 匹配Markdown中的图片标签
110
- pattern = r'\!\[(?:[^\]]*)\]\(([^)]+)\)'
111
-
112
- # 替换图片链接
113
- def replace(match):
114
- relative_path = match.group(1)
115
- full_path = os.path.join(image_dir_path, relative_path)
116
- base64_image = image_to_base64(full_path)
117
- return f"![{relative_path}](data:image/jpeg;base64,{base64_image})"
118
-
119
- # 应用替换
120
- return re.sub(pattern, replace, markdown_text)
121
-
122
-
123
- def to_markdown(file_path, end_pages, is_ocr, layout_mode, formula_enable, table_enable, language):
124
- file_path = to_pdf(file_path)
125
- if end_pages > 20:
126
- end_pages = 20
127
- # 获取识别的md文件以及压缩包文件路径
128
- local_md_dir, file_name = parse_pdf(file_path, './output', end_pages - 1, is_ocr,
129
- layout_mode, formula_enable, table_enable, language)
130
- archive_zip_path = os.path.join("./output", compute_sha256(local_md_dir) + ".zip")
131
- zip_archive_success = compress_directory_to_zip(local_md_dir, archive_zip_path)
132
- if zip_archive_success == 0:
133
- logger.info("压缩成功")
134
- else:
135
- logger.error("压缩失败")
136
- md_path = os.path.join(local_md_dir, file_name + ".md")
137
- with open(md_path, 'r', encoding='utf-8') as f:
138
- txt_content = f.read()
139
- md_content = replace_image_with_base64(txt_content, local_md_dir)
140
- # 返回转换后的PDF路径
141
- new_pdf_path = os.path.join(local_md_dir, file_name + "_layout.pdf")
142
-
143
- return md_content, txt_content, archive_zip_path, new_pdf_path
144
-
145
-
146
- latex_delimiters = [{"left": "$$", "right": "$$", "display": True},
147
- {"left": '$', "right": '$', "display": False}]
148
-
149
-
150
- def init_model():
151
- from magic_pdf.model.doc_analyze_by_custom_model import ModelSingleton
152
- try:
153
- model_manager = ModelSingleton()
154
- txt_model = model_manager.get_model(False, False)
155
- logger.info(f"txt_model init final")
156
- ocr_model = model_manager.get_model(True, False)
157
- logger.info(f"ocr_model init final")
158
- return 0
159
- except Exception as e:
160
- logger.exception(e)
161
- return -1
162
-
163
-
164
- model_init = init_model()
165
- logger.info(f"model_init: {model_init}")
166
 
 
 
167
 
 
168
  with open("header.html", "r") as file:
169
  header = file.read()
170
 
171
-
172
- latin_lang = [
173
- 'af', 'az', 'bs', 'cs', 'cy', 'da', 'de', 'es', 'et', 'fr', 'ga', 'hr',
174
- 'hu', 'id', 'is', 'it', 'ku', 'la', 'lt', 'lv', 'mi', 'ms', 'mt', 'nl',
175
- 'no', 'oc', 'pi', 'pl', 'pt', 'ro', 'rs_latin', 'sk', 'sl', 'sq', 'sv',
176
- 'sw', 'tl', 'tr', 'uz', 'vi', 'french', 'german'
177
- ]
178
  arabic_lang = ['ar', 'fa', 'ug', 'ur']
179
- cyrillic_lang = [
180
- 'ru', 'rs_cyrillic', 'be', 'bg', 'uk', 'mn', 'abq', 'ady', 'kbd', 'ava',
181
- 'dar', 'inh', 'che', 'lbe', 'lez', 'tab'
182
- ]
183
- devanagari_lang = [
184
- 'hi', 'mr', 'ne', 'bh', 'mai', 'ang', 'bho', 'mah', 'sck', 'new', 'gom',
185
- 'sa', 'bgc'
186
- ]
187
  other_lang = ['ch', 'en', 'korean', 'japan', 'chinese_cht', 'ta', 'te', 'ka']
188
 
189
- all_lang = ['', 'auto']
190
- all_lang.extend([*other_lang, *latin_lang, *arabic_lang, *cyrillic_lang, *devanagari_lang])
191
-
192
-
193
- def to_pdf(file_path):
194
- with pymupdf.open(file_path) as f:
195
- if f.is_pdf:
196
- return file_path
197
- else:
198
- pdf_bytes = f.convert_to_pdf()
199
- # 将pdfbytes 写入到uuid.pdf中
200
- # 生成唯一的文件名
201
- unique_filename = f"{uuid.uuid4()}.pdf"
202
-
203
- # 构建完整的文件路径
204
- tmp_file_path = os.path.join(os.path.dirname(file_path), unique_filename)
205
-
206
- # 将字节数据写入文件
207
- with open(tmp_file_path, 'wb') as tmp_pdf_file:
208
- tmp_pdf_file.write(pdf_bytes)
209
-
210
- return tmp_file_path
211
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
 
213
  if __name__ == "__main__":
214
- with gr.Blocks() as demo:
215
- gr.HTML(header)
216
- with gr.Row():
217
- with gr.Column(variant='panel', scale=5):
218
- file = gr.File(label="Please upload a PDF or image", file_types=[".pdf", ".png", ".jpeg", ".jpg"])
219
- max_pages = gr.Slider(1, 20, 10, step=1, label='Max convert pages')
220
- with gr.Row():
221
- layout_mode = gr.Dropdown(["layoutlmv3", "doclayout_yolo"], label="Layout model", value="doclayout_yolo")
222
- language = gr.Dropdown(all_lang, label="Language", value='auto')
223
- with gr.Row():
224
- formula_enable = gr.Checkbox(label="Enable formula recognition", value=True)
225
- is_ocr = gr.Checkbox(label="Force enable OCR", value=False)
226
- table_enable = gr.Checkbox(label="Enable table recognition(test)", value=True)
227
- with gr.Row():
228
- change_bu = gr.Button("Convert")
229
- clear_bu = gr.ClearButton(value="Clear")
230
- pdf_show = PDF(label='PDF preview', interactive=False, visible=True, height=800)
231
- with gr.Accordion("Examples:"):
232
- example_root = os.path.join(os.path.dirname(__file__), "examples")
233
- gr.Examples(
234
- examples=[os.path.join(example_root, _) for _ in os.listdir(example_root) if
235
- _.endswith("pdf")],
236
- inputs=file
237
- )
238
-
239
- with gr.Column(variant='panel', scale=5):
240
- output_file = gr.File(label="convert result", interactive=False)
241
- with gr.Tabs():
242
- with gr.Tab("Markdown rendering"):
243
- md = gr.Markdown(label="Markdown rendering", height=1100, show_copy_button=True,
244
- latex_delimiters=latex_delimiters, line_breaks=True)
245
- with gr.Tab("Markdown text"):
246
- md_text = gr.TextArea(lines=45, show_copy_button=True)
247
- file.change(fn=to_pdf, inputs=file, outputs=pdf_show)
248
- change_bu.click(fn=to_markdown, inputs=[file, max_pages, is_ocr, layout_mode, formula_enable, table_enable, language],
249
- outputs=[md, md_text, output_file, pdf_show], api_name=False)
250
- clear_bu.add([file, md, pdf_show, md_text, output_file, is_ocr])
251
-
252
  demo.launch(ssr_mode=True)
 
1
+ # app.py
 
 
 
2
  import os
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  import gradio as gr
4
+ from gradio_pdf import PDF
5
+ from model import model_initialized
6
+ from pdf_processor import to_pdf, to_markdown
7
+ from config import config
8
+ import logging
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ # Set up logging
11
+ logging.basicConfig(level=logging.INFO)
12
 
13
+ # Load header HTML content
14
  with open("header.html", "r") as file:
15
  header = file.read()
16
 
17
+ # Language options (you may also move these to config.yaml if preferred)
18
+ latin_lang = ['af', 'az', 'bs', 'cs', 'cy', 'da', 'de', 'es', 'et', 'fr', 'ga', 'hr', 'hu', 'id', 'is', 'it', 'ku', 'la', 'lt', 'lv', 'mi', 'ms', 'mt', 'nl', 'no', 'oc', 'pi', 'pl', 'pt', 'ro', 'rs_latin', 'sk', 'sl', 'sq', 'sv', 'sw', 'tl', 'tr', 'uz', 'vi', 'french', 'german']
 
 
 
 
 
19
  arabic_lang = ['ar', 'fa', 'ug', 'ur']
20
+ cyrillic_lang = ['ru', 'rs_cyrillic', 'be', 'bg', 'uk', 'mn', 'abq', 'ady', 'kbd', 'ava', 'dar', 'inh', 'che', 'lbe', 'lez', 'tab']
21
+ devanagari_lang = ['hi', 'mr', 'ne', 'bh', 'mai', 'ang', 'bho', 'mah', 'sck', 'new', 'gom', 'sa', 'bgc']
 
 
 
 
 
 
22
  other_lang = ['ch', 'en', 'korean', 'japan', 'chinese_cht', 'ta', 'te', 'ka']
23
 
24
+ all_lang = ['', 'auto'] + other_lang + latin_lang + arabic_lang + cyrillic_lang + devanagari_lang
25
+
26
+ # Utility function to ensure input file is a PDF
27
+ def file_to_pdf(file_obj):
28
+ if file_obj is not None:
29
+ return to_pdf(file_obj.name)
30
+ return None
31
+
32
+ with gr.Blocks() as demo:
33
+ gr.HTML(header)
34
+ with gr.Row():
35
+ with gr.Column(variant='panel', scale=5):
36
+ file_input = gr.File(label="Please upload a PDF or image", file_types=[".pdf", ".png", ".jpeg", ".jpg"])
37
+ max_pages = gr.Slider(1, 20, config.get("max_pages_default", 10), step=1, label='Max convert pages')
38
+ with gr.Row():
39
+ layout_mode = gr.Dropdown(
40
+ ["layoutlmv3", "doclayout_yolo"],
41
+ label="Layout model",
42
+ value=config.get("layout_model_default", "doclayout_yolo")
43
+ )
44
+ language = gr.Dropdown(
45
+ all_lang,
46
+ label="Language",
47
+ value=config.get("language_default", "auto")
48
+ )
49
+ with gr.Row():
50
+ formula_enable = gr.Checkbox(label="Enable formula recognition", value=True)
51
+ is_ocr = gr.Checkbox(label="Force enable OCR", value=False)
52
+ table_enable = gr.Checkbox(label="Enable table recognition", value=True)
53
+ with gr.Row():
54
+ convert_button = gr.Button("Convert")
55
+ clear_button = gr.ClearButton(value="Clear")
56
+ pdf_display = PDF(label='PDF preview', interactive=False, visible=True, height=800)
57
+ with gr.Accordion("Examples:"):
58
+ example_root = os.path.join(os.path.dirname(__file__), "examples")
59
+ examples = [os.path.join(example_root, f) for f in os.listdir(example_root) if f.endswith("pdf")]
60
+ gr.Examples(examples=examples, inputs=file_input)
61
+ with gr.Column(variant='panel', scale=5):
62
+ output_file = gr.File(label="Convert result", interactive=False)
63
+ with gr.Tabs():
64
+ with gr.Tab("Markdown rendering"):
65
+ md_render = gr.Markdown(label="Markdown rendering", height=1100, show_copy_button=True, line_breaks=True)
66
+ with gr.Tab("Markdown text"):
67
+ md_text = gr.TextArea(lines=45, show_copy_button=True)
68
+
69
+ file_input.change(fn=file_to_pdf, inputs=file_input, outputs=pdf_display)
70
+ convert_button.click(
71
+ fn=to_markdown,
72
+ inputs=[file_input, max_pages, is_ocr, layout_mode, formula_enable, table_enable, language],
73
+ outputs=[md_render, md_text, output_file, pdf_display]
74
+ )
75
+ clear_button.add([file_input, md_render, pdf_display, md_text, output_file, is_ocr])
76
 
77
  if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  demo.launch(ssr_mode=True)
config.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # config.py
2
+ import yaml
3
+
4
+ def load_config(config_path="config.yaml"):
5
+ with open(config_path, "r") as f:
6
+ config = yaml.safe_load(f)
7
+ return config
8
+
9
+ config = load_config()
config.yaml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # config.yaml
2
+
3
+ # Device and processing settings
4
+ device_mode: "cuda"
5
+ magic_pdf_json: "/home/user/magic-pdf.json"
6
+ paddleocr_dir: "/home/user/.paddleocr"
7
+ output_dir: "./output"
8
+
9
+ # Default UI values
10
+ layout_model_default: "doclayout_yolo"
11
+ language_default: "auto"
12
+ max_pages_default: 10
download_models_hf.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ import requests
5
+ from huggingface_hub import snapshot_download
6
+
7
+
8
+ def download_json(url):
9
+ # 下载JSON文件
10
+ response = requests.get(url)
11
+ response.raise_for_status() # 检查请求是否成功
12
+ return response.json()
13
+
14
+
15
+ def download_and_modify_json(url, local_filename, modifications):
16
+ if os.path.exists(local_filename):
17
+ data = json.load(open(local_filename))
18
+ config_version = data.get('config_version', '0.0.0')
19
+ if config_version < '1.1.1':
20
+ data = download_json(url)
21
+ else:
22
+ data = download_json(url)
23
+
24
+ # 修改内容
25
+ for key, value in modifications.items():
26
+ data[key] = value
27
+
28
+ # 保存修改后的内容
29
+ with open(local_filename, 'w', encoding='utf-8') as f:
30
+ json.dump(data, f, ensure_ascii=False, indent=4)
31
+
32
+
33
+ if __name__ == '__main__':
34
+
35
+ mineru_patterns = [
36
+ "models/Layout/LayoutLMv3/*",
37
+ "models/Layout/YOLO/*",
38
+ "models/MFD/YOLO/*",
39
+ "models/MFR/unimernet_small_2501/*",
40
+ "models/TabRec/TableMaster/*",
41
+ "models/TabRec/StructEqTable/*",
42
+ ]
43
+ model_dir = snapshot_download('opendatalab/PDF-Extract-Kit-1.0', allow_patterns=mineru_patterns)
44
+
45
+ layoutreader_pattern = [
46
+ "*.json",
47
+ "*.safetensors",
48
+ ]
49
+ layoutreader_model_dir = snapshot_download('hantian/layoutreader', allow_patterns=layoutreader_pattern)
50
+
51
+ model_dir = model_dir + '/models'
52
+ print(f'model_dir is: {model_dir}')
53
+ print(f'layoutreader_model_dir is: {layoutreader_model_dir}')
54
+
55
+ json_url = 'https://github.com/opendatalab/MinerU/raw/master/magic-pdf.template.json'
56
+ config_file_name = 'magic-pdf.json'
57
+ home_dir = os.path.expanduser('~')
58
+ config_file = os.path.join(home_dir, config_file_name)
59
+
60
+ json_mods = {
61
+ 'models-dir': model_dir,
62
+ 'layoutreader-model-dir': layoutreader_model_dir,
63
+ }
64
+
65
+ download_and_modify_json(json_url, config_file, json_mods)
66
+ print(f'The configuration file has been configured successfully, the path is: {config_file}')
model.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # model.py
2
+ import logging
3
+ from magic_pdf.model.doc_analyze_by_custom_model import ModelSingleton
4
+
5
+ def init_model():
6
+ """
7
+ Initialize the document analysis models.
8
+ """
9
+ try:
10
+ model_manager = ModelSingleton()
11
+ _ = model_manager.get_model(False, False)
12
+ logging.info("Text model initialized successfully")
13
+ _ = model_manager.get_model(True, False)
14
+ logging.info("OCR model initialized successfully")
15
+ return True
16
+ except Exception as e:
17
+ logging.exception("Model initialization failed: %s", e)
18
+ return False
19
+
20
+ model_initialized = init_model()
pdf_processor.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # pdf_processor.py
2
+ import os
3
+ import time
4
+ from pathlib import Path
5
+ import uuid
6
+ import logging
7
+ import pymupdf
8
+
9
+ from magic_pdf.data.data_reader_writer import FileBasedDataReader
10
+ from magic_pdf.libs.hash_utils import compute_sha256
11
+ from magic_pdf.tools.common import do_parse, prepare_env
12
+ from utils import compress_directory_to_zip, replace_image_with_base64
13
+
14
+ def read_fn(path):
15
+ disk_rw = FileBasedDataReader(os.path.dirname(path))
16
+ return disk_rw.read(os.path.basename(path))
17
+
18
+ def parse_pdf(doc_path, output_dir, end_page_id, is_ocr, layout_mode, formula_enable, table_enable, language):
19
+ """
20
+ Parse the PDF using the specified parameters.
21
+ """
22
+ os.makedirs(output_dir, exist_ok=True)
23
+ try:
24
+ file_name = f"{Path(doc_path).stem}_{int(time.time())}"
25
+ pdf_data = read_fn(doc_path)
26
+ parse_method = "ocr" if is_ocr else "auto"
27
+ local_image_dir, local_md_dir = prepare_env(output_dir, file_name, parse_method)
28
+ do_parse(
29
+ output_dir,
30
+ file_name,
31
+ pdf_data,
32
+ [],
33
+ parse_method,
34
+ False,
35
+ end_page_id=end_page_id,
36
+ layout_model=layout_mode,
37
+ formula_enable=formula_enable,
38
+ table_enable=table_enable,
39
+ lang=language,
40
+ f_dump_orig_pdf=False,
41
+ )
42
+ return local_md_dir, file_name
43
+ except Exception as e:
44
+ logging.exception("Error in parse_pdf: %s", e)
45
+ raise
46
+
47
+ def to_pdf(file_path):
48
+ """
49
+ Ensures the file is in PDF format. Converts if necessary.
50
+ """
51
+ with pymupdf.open(file_path) as f:
52
+ if f.is_pdf:
53
+ return file_path
54
+ else:
55
+ pdf_bytes = f.convert_to_pdf()
56
+ unique_filename = f"{uuid.uuid4()}.pdf"
57
+ tmp_file_path = os.path.join(os.path.dirname(file_path), unique_filename)
58
+ with open(tmp_file_path, 'wb') as tmp_pdf_file:
59
+ tmp_pdf_file.write(pdf_bytes)
60
+ return tmp_file_path
61
+
62
+ def to_markdown(file_path, end_pages, is_ocr, layout_mode, formula_enable, table_enable, language, output_dir="./output"):
63
+ """
64
+ Converts the PDF to markdown and compresses the result.
65
+ """
66
+ file_path = to_pdf(file_path)
67
+ end_pages = min(end_pages, 20)
68
+ local_md_dir, file_name = parse_pdf(file_path, output_dir, end_pages - 1, is_ocr, layout_mode, formula_enable, table_enable, language)
69
+ archive_zip_path = os.path.join(output_dir, compute_sha256(local_md_dir) + ".zip")
70
+ if compress_directory_to_zip(local_md_dir, archive_zip_path):
71
+ logging.info("Compression successful")
72
+ else:
73
+ logging.error("Compression failed")
74
+ md_path = os.path.join(local_md_dir, f"{file_name}.md")
75
+ with open(md_path, 'r', encoding='utf-8') as f:
76
+ txt_content = f.read()
77
+ md_content = replace_image_with_base64(txt_content, local_md_dir)
78
+ new_pdf_path = os.path.join(local_md_dir, f"{file_name}_layout.pdf")
79
+ return md_content, txt_content, archive_zip_path, new_pdf_path
utils.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # utils.py
2
+ import os
3
+ import zipfile
4
+ import base64
5
+ import re
6
+ import logging
7
+
8
+ def compress_directory_to_zip(directory_path, output_zip_path):
9
+ """
10
+ Compresses the specified directory into a ZIP file.
11
+ """
12
+ try:
13
+ with zipfile.ZipFile(output_zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
14
+ for root, dirs, files in os.walk(directory_path):
15
+ for file in files:
16
+ file_path = os.path.join(root, file)
17
+ arcname = os.path.relpath(file_path, directory_path)
18
+ zipf.write(file_path, arcname)
19
+ return True
20
+ except Exception as e:
21
+ logging.exception("Error compressing directory: %s", e)
22
+ return False
23
+
24
+ def image_to_base64(image_path):
25
+ with open(image_path, "rb") as image_file:
26
+ return base64.b64encode(image_file.read()).decode('utf-8')
27
+
28
+ def replace_image_with_base64(markdown_text, image_dir_path):
29
+ """
30
+ Finds markdown image tags and replaces file paths with embedded base64 data.
31
+ """
32
+ pattern = r'\!\[(?:[^\]]*)\]\(([^)]+)\)'
33
+
34
+ def replace(match):
35
+ relative_path = match.group(1)
36
+ full_path = os.path.join(image_dir_path, relative_path)
37
+ base64_image = image_to_base64(full_path)
38
+ return f"![{relative_path}](data:image/jpeg;base64,{base64_image})"
39
+
40
+ return re.sub(pattern, replace, markdown_text)