Конвертація у Markdown
PDF Oxide конвертує сторінки PDF у чистий, читабельний Markdown. Конвеєр витягає текстові span’и, кластеризує їх у рядки, звіряє /StructTreeRoot для заголовків і ролей списків у тегованих PDF, виявляє міжколонкові проміжки та «зворотні» переноси порядку читання по x, групує абзаци і видає Markdown-синтаксис.
Починаючи з v0.3.36, для тегованих PDF конвертер зчитує StructRole(Heading(1..6) | ListItem | ListItemLabel | ListItemBody) прямо зі /StructTreeRoot, а не обчислює рівні заголовків за розміром шрифта. Інформація про роль поширюється через вкладені MCR (H1 → Span → MCR, LI → LBody → Span → MCR). Для нетегованих документів досі працює геометричний відкат: жирність + 5 % збільшення розміру просуває до H4, а is_ordered_list_marker розпізнає 1. / 12. / a) / iv. / A. і відкидає підписи рисунків та роки.
Багатоколонковий режим: span’и з однаковою базовою лінією, розділені > max(3 × font_size, 30 pt), вважаються міжколонковими. Зворотні по x переноси порядку читання (останній span col 1 → перший span col 2) розривають абзаци, а не зшивають їх у безглузді токени.
RTL: bidi-переупорядковування за замовчуванням вимкнене — попереднє безумовне перетворення visual→logical ламало PDF, що вже були в логічному порядку (івритське בנימін інвертувалося). Хибні маркери **bold** навколо контекстних арабських гліфів прибираються. Якщо ваш вхід у візуальному порядку — викликайте text::bidi::reorder_visual_to_logical вручну (Rust).
Вбудовані зображення обмежені payload у 200 КБ base64 (додано в v0.3.36). Для зображень понад ліміт виводиться HTML-коментар із зазначенням оригінального розміру; зберігайте їх на диск через image_output_dir.
Швидкий приклад
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("paper.pdf")
md = doc.to_markdown(0, detect_headings=True)
print(md)
Node.js
const { PdfDocument } = require("pdf-oxide");
const doc = new PdfDocument("paper.pdf");
const md = doc.toMarkdown(0, { detectHeadings: true });
console.log(md);
doc.close();
Go
import pdfoxide "github.com/yfedoseev/pdf_oxide/go"
doc, _ := pdfoxide.Open("paper.pdf")
defer doc.Close()
md, _ := doc.ToMarkdown(0)
fmt.Println(md)
C#
using PdfOxide.Core;
using var doc = PdfDocument.Open("paper.pdf");
var md = doc.ToMarkdown(0);
Console.WriteLine(md);
WASM
const doc = new WasmPdfDocument(bytes);
const md = doc.toMarkdown(0, true);
console.log(md);
Rust
use pdf_oxide::PdfDocument;
use pdf_oxide::converters::ConversionOptions;
let mut doc = PdfDocument::open("paper.pdf")?;
let options = ConversionOptions { detect_headings: true, ..Default::default() };
let md = doc.to_markdown(0, &options)?;
println!("{}", md);
Довідник API
to_markdown(page_index, ...) -> str
Конвертувати одну сторінку у Markdown.
Сигнатура Python
doc.to_markdown(
page: int,
preserve_layout: bool = False,
detect_headings: bool = True,
include_images: bool = True,
image_output_dir: str | None = None,
embed_images: bool = True,
) -> str
Сигнатура JavaScript
doc.toMarkdown(pageIndex, detectHeadings?, includeImages?, includeFormFields?) -> string
Сигнатура Rust
pub fn to_markdown(
&mut self,
page_index: usize,
options: &ConversionOptions,
) -> Result<String>
| Параметр | Тип | За замовчуванням | Опис |
|---|---|---|---|
page_index |
int / usize / number |
– | Індекс сторінки з нуля |
preserve_layout |
bool |
false |
Зберігати позиціонування візуального макета |
detect_headings |
bool |
true |
Виявляти заголовки на основі розміру та насиченості шрифту |
include_images |
bool |
true |
Включати зображення у вивід |
image_output_dir |
str / None |
None |
Каталог для збереження видобутих зображень (лише Python/Rust). Не залежить від обмеження 200 КБ на inline-вкладення. |
embed_images |
bool |
true |
Вбудовувати зображення як base64 data URI (лише Python/Rust). Payload понад 200 КБ замінюється HTML-коментарем із зазначенням оригінального розміру (v0.3.36). |
include_form_fields |
bool |
true |
Включати значення полів форм (Python/JS) |
Повертає: рядок Markdown для сторінки.
to_markdown_all(...) -> str
Конвертувати всі сторінки у Markdown, розділені горизонтальними лініями (---).
Сигнатура Python
doc.to_markdown_all(
preserve_layout: bool = False,
detect_headings: bool = True,
include_images: bool = True,
image_output_dir: str | None = None,
embed_images: bool = True,
) -> str
Сигнатура JavaScript
doc.toMarkdownAll(detectHeadings?, includeImages?, includeFormFields?) -> string
Сигнатура Rust
pub fn to_markdown_all(
&mut self,
options: &ConversionOptions,
) -> Result<String>
| Параметр | Тип | За замовчуванням | Опис |
|---|---|---|---|
preserve_layout |
bool |
false |
Зберігати візуальний макет |
detect_headings |
bool |
true |
Виявляти заголовки |
include_images |
bool |
true |
Включати зображення |
image_output_dir |
str / None |
None |
Каталог виводу зображень |
embed_images |
bool |
true |
Вбудовувати зображення як base64 |
Повертає: рядок Markdown для всіх сторінок, об’єднаних роздільниками ---.
to_markdown_with_ocr(page_index, model_path, options) -> str
Конвертувати сторінку у Markdown із резервним OCR для сканованих сторінок. Коли на сторінці мало або зовсім немає тексту, придатного до видобування, OCR використовується для розпізнавання тексту з відмальованого зображення сторінки. Потребує функції ocr.
| Параметр | Тип | Опис |
|---|---|---|
page_index |
usize |
Індекс сторінки з нуля |
model_path |
&str |
Шлях до файлів моделі OCR |
options |
&ConversionOptions |
Параметри конвертації |
Rust
let mut doc = PdfDocument::open("scanned.pdf")?;
let options = ConversionOptions { detect_headings: true, ..Default::default() };
let md = doc.to_markdown_with_ocr(0, "/path/to/models", &options)?;
println!("{}", md);
ConversionOptions
Структура ConversionOptions керує всією поведінкою конвертації.
| Поле | Тип | За замовчуванням | Опис |
|---|---|---|---|
preserve_layout |
bool |
false |
Зберігати візуальний макет із позиціонуванням |
detect_headings |
bool |
true |
Автоматично виявляти заголовки за кластерами розмірів шрифту |
extract_tables |
bool |
false |
Видобувати таблиці (експериментально) |
include_images |
bool |
true |
Включати зображення у вивід |
image_output_dir |
Option<String> |
None |
Зберігати зображення в цей каталог |
embed_images |
bool |
true |
Вбудовувати зображення як base64 data URI |
reading_order_mode |
ReadingOrderMode |
Auto |
Як визначати порядок читання |
bold_marker_behavior |
BoldMarkerBehavior |
Conservative |
Стратегія застосування маркерів жирності |
Як це працює
Конвеєр конвертації у Markdown працює в кілька етапів:
-
Видобування тексту – Видобуває об’єкти
TextSpanіз потоку вмісту сторінки, фіксуючи текст, позицію, шрифт, розмір, насиченість і колір. -
Кластеризація символів – Групує символи у слова на основі проміжків між символами, а потім слова у рядки на основі вертикальної близькості.
-
Порядок читання – Визначає порядок читання, використовуючи або дерево структури тегованого PDF (бажано), або графовий просторовий аналіз позицій текстових блоків.
-
Виявлення заголовків – Коли увімкнено
detect_headings, кластеризує розміри шрифтів по сторінці для визначення рівнів заголовків. Більший і жирніший текст відображається на заголовки#,##,###. -
Форматування – Застосовує маркери жирності (
**text**) та курсиву (*text*) на основі метаданих насиченості та стилю шрифту. -
Виявлення таблиць – Визначає табличні макети за допомогою просторового аналізу вирівняних текстових блоків і видає Markdown-таблиці у стилі GFM.
-
Очищення пробілів – Нормалізує інтервали, видаляє зайві порожні рядки та забезпечує узгоджені розриви абзаців.
Розширені приклади
Конвертація всього PDF у файл Markdown
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("book.pdf")
md = doc.to_markdown_all(detect_headings=True)
with open("book.md", "w", encoding="utf-8") as f:
f.write(md)
Node.js
const fs = require("node:fs");
const doc = new PdfDocument("book.pdf");
const md = doc.toMarkdownAll();
fs.writeFileSync("book.md", md);
doc.close();
Go
doc, _ := pdfoxide.Open("book.pdf")
defer doc.Close()
md, _ := doc.ToMarkdownAll()
os.WriteFile("book.md", []byte(md), 0644)
C#
using var doc = PdfDocument.Open("book.pdf");
var md = doc.ToMarkdownAll();
File.WriteAllText("book.md", md);
WASM
const doc = new WasmPdfDocument(bytes);
const md = doc.toMarkdownAll(true);
writeFileSync("book.md", md);
doc.free();
Конвертація зі збереженням зображень у каталог
use pdf_oxide::PdfDocument;
use pdf_oxide::converters::ConversionOptions;
let mut doc = PdfDocument::open("report.pdf")?;
let options = ConversionOptions {
detect_headings: true,
include_images: true,
embed_images: false,
image_output_dir: Some("output/images".to_string()),
..Default::default()
};
let md = doc.to_markdown_all(&options)?;
std::fs::write("output/report.md", &md)?;
Посторінкова конвертація з відображенням прогресу
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
pages = doc.page_count()
parts = []
for i in range(pages):
md = doc.to_markdown(i, detect_headings=True)
parts.append(md)
print(f"Converted page {i + 1}/{pages}")
full_md = "\n\n---\n\n".join(parts)
with open("report.md", "w") as f:
f.write(full_md)
Вимкнення виявлення заголовків для плаского тексту
doc = PdfDocument("form.pdf")
md = doc.to_markdown(0, detect_headings=False)
# All text rendered as paragraphs, no # headings
Пов’язані сторінки
- Видобування тексту – видобування сирого тексту та span’ів
- Конвертація в HTML – конвертація в HTML замість Markdown
- Видобування зображень – видобування зображень окремо