133 lines
6.1 KiB
Python
133 lines
6.1 KiB
Python
import fitz # PyMuPDF
|
||
import sys
|
||
import json
|
||
import os
|
||
|
||
def analyze_pdf_to_json(pdf_path):
|
||
"""
|
||
Анализирует PDF-файл, извлекает всю текстовую разметку, шрифты,
|
||
координаты графика и создает готовый map.json файл.
|
||
"""
|
||
try:
|
||
doc = fitz.open(pdf_path)
|
||
print(f"--- Analyzing PDF: {pdf_path} ({len(doc)} pages) ---")
|
||
except Exception as e:
|
||
print(f"Error opening file: {e}")
|
||
return
|
||
|
||
# --- 1. АВТОМАТИЧЕСКИ СОБИРАЕМ ВСЕ УНИКАЛЬНЫЕ ШРИФТЫ ---
|
||
unique_fonts = set()
|
||
for page in doc:
|
||
fonts_on_page = page.get_fonts(full=False)
|
||
for font_info in fonts_on_page:
|
||
unique_fonts.add(font_info[3]) # font_info[3] - это имя шрифта
|
||
|
||
# Сортируем для стабильного порядка и создаем карту "имя -> ifont"
|
||
# Начинаем ifont с 1, так как ifont: 0 мы зарезервируем для Helvetica
|
||
sorted_fonts = sorted(list(unique_fonts))
|
||
font_map = {font_name: index + 1 for index, font_name in enumerate(sorted_fonts)}
|
||
|
||
print("\n--- Found Unique Fonts ---")
|
||
print("This is your 'font_map'. Use it to configure font loading in routes.js:")
|
||
print(json.dumps(font_map, indent=2))
|
||
print("--------------------------\n")
|
||
|
||
# --- 2. ИЩЕМ ПРЯМОУГОЛЬНИК ГРАФИКА НА ПЕРВОЙ СТРАНИЦЕ ---
|
||
chart_area_rect = None
|
||
if len(doc) > 0:
|
||
target_page = doc[0]
|
||
paths = target_page.get_drawings()
|
||
|
||
max_area = 0
|
||
# Ищем самый большой прямоугольник, который не занимает всю страницу
|
||
page_area = target_page.rect.width * target_page.rect.height
|
||
for path in paths:
|
||
# Ищем прямоугольники (type 's' с одним элементом 're')
|
||
if path["type"] == "s" and len(path["items"]) == 1 and path["items"][0][0] == "re":
|
||
rect = fitz.Rect(path["rect"])
|
||
area = rect.width * rect.height
|
||
if area > max_area and area < (page_area * 0.9): # Игнорируем прямоугольники, занимающие почти всю страницу
|
||
max_area = area
|
||
chart_area_rect = rect
|
||
|
||
# --- 3. Создаем базовую структуру для JSON-файла ---
|
||
output_data = {
|
||
"font_map": font_map,
|
||
"constants": {},
|
||
"replacements_template": {},
|
||
"map": []
|
||
}
|
||
|
||
if chart_area_rect:
|
||
print(f"--- Found Chart Area Rectangle ---")
|
||
print(chart_area_rect)
|
||
print("--------------------------------\n")
|
||
output_data["constants"]["bar_chart_x_ill"] = round(chart_area_rect.x0, 2)
|
||
output_data["constants"]["bar_chart_y_ill"] = round(chart_area_rect.y0, 2)
|
||
output_data["constants"]["bar_chart_width"] = round(chart_area_rect.width, 2)
|
||
output_data["constants"]["bar_chart_height"] = round(chart_area_rect.height, 2)
|
||
output_data["constants"]["bar_chart_max_cost"] = 200 # Пример, это значение лучше задавать вручную
|
||
|
||
# --- 4. ИЗВЛЕКАЕМ ВЕСЬ ТЕКСТ И ЕГО СВОЙСТВА ---
|
||
all_found_texts = set()
|
||
|
||
for page_num in range(len(doc)):
|
||
page = doc[page_num]
|
||
# Используем get_text("dict") для получения детальной информации
|
||
blocks = page.get_text("dict", flags=fitz.TEXTFLAGS_SEARCH)["blocks"]
|
||
|
||
for block in blocks:
|
||
if "lines" in block:
|
||
for line in block["lines"]:
|
||
# Собираем текст из спанов, чтобы обрабатывать сгруппированные строки
|
||
full_line_text = "".join(span["text"] for span in line["spans"]).strip()
|
||
if full_line_text:
|
||
all_found_texts.add(full_line_text)
|
||
|
||
# Итерируем по отдельным спанам для точной разметки
|
||
for span in line["spans"]:
|
||
text = span["text"].strip()
|
||
if not text:
|
||
continue
|
||
|
||
font_name = span["font"]
|
||
font_size = span["size"]
|
||
bbox = span["bbox"] # (x0, y0, x1, y1) от левого верхнего угла
|
||
|
||
ifont = font_map.get(font_name, 0)
|
||
|
||
map_item = {
|
||
"ipage": page_num,
|
||
"field": text,
|
||
"x_ill": round(bbox[0], 2),
|
||
"y_ill": round(bbox[1], 2),
|
||
"size": round(font_size, 2),
|
||
"ifont": ifont
|
||
}
|
||
output_data["map"].append(map_item)
|
||
|
||
doc.close()
|
||
|
||
# Заполняем replacements_template, чтобы пользователь мог указать имена переменных
|
||
output_data["replacements_template"] = {text: "" for text in sorted(list(all_found_texts))}
|
||
|
||
# --- 5. СОХРАНЯЕМ РЕЗУЛЬТАТ В JSON ---
|
||
base_name = os.path.splitext(os.path.basename(pdf_path))[0]
|
||
output_filename = f"{base_name.replace('_original', '')}.map.json"
|
||
|
||
try:
|
||
with open(output_filename, 'w', encoding='utf-8') as f:
|
||
json.dump(output_data, f, indent=2, ensure_ascii=False)
|
||
print(f"--- SUCCESS ---")
|
||
print(f"Successfully created JSON map file: {output_filename}")
|
||
print(f"Please review this file and fill in the variable names in 'replacements_template'.")
|
||
except Exception as e:
|
||
print(f"Error writing JSON file: {e}")
|
||
|
||
if __name__ == "__main__":
|
||
if len(sys.argv) != 2:
|
||
print("Usage: python analyze_pdf.py <path_to_pdf_template>")
|
||
sys.exit(1)
|
||
|
||
pdf_path = sys.argv[1]
|
||
analyze_pdf_to_json(pdf_path) |