Skip to content

Начало работы с PDF Oxide (Clojure)

PDF Oxide — самый быстрый PDF-инструментарий со встроенным извлечением текста: 0.8 мс в среднем и 100% успешных разборов на 3830 PDF. Биндинг для Clojure — это идиоматичная тонкая обёртка над зрелым Java-биндингом fyi.oxide:pdf-oxide, которому принадлежит единственный нативный мост через JNI. Он не добавляет ни строчки нативного кода: вызывает Java-классы через interop и возвращает удобные для Clojure значения (java.util.List → вектор, java.util.Optional → значение или nil).

Установка

Добавьте Java-биндинг в свой deps.edn. Пространство имён Clojure (pdf_oxide.core) лежит в вашем дереве исходников и оборачивает его:

{:deps {fyi.oxide/pdf-oxide {:mvn/version "0.3.69"}}}

Типы дескрипторов (Pdf, PdfDocument, DocumentEditor) реализуют AutoCloseable, поэтому используйте with-open для детерминированной очистки ресурсов.

Быстрый старт

Соберите PDF из Markdown, откройте его обратно и извлеките текст. Каждый шаг возвращает обычные значения Clojure.

(require '[pdf-oxide.core :as pdf])

(with-open [p (pdf/from-markdown "# Hello\n\nbody\n")
            d (pdf/open (pdf/save p))]
  (println "pages:   " (pdf/page-count d))
  (println "producer:" (or (pdf/producer d) "(none)"))
  (println (pdf/extract-text d 0)))

Открытие PDF

pdf/open принимает либо массив байтов, либо строку с путём к файлу, а также необязательный пароль для зашифрованных документов.

(require '[pdf-oxide.core :as pdf])

;; Из пути
(with-open [d (pdf/open "research-paper.pdf")]
  (println "pages:" (pdf/page-count d)))

;; Из байтов (например, скачанных из S3 или по HTTP)
(with-open [d (pdf/open pdf-bytes)]
  (println (pdf/extract-text d 0)))

;; Зашифрованный документ
(with-open [d (pdf/open "confidential.pdf" "secret")]
  (println (pdf/extract-text d 0)))

Можно также пройти аутентификацию уже после открытия:

(with-open [d (pdf/open "confidential.pdf")]
  (when (pdf/authenticate d "secret")
    (println (pdf/extract-text d 0))))

Извлечение текста

Извлекайте обычный текст с любой страницы по её индексу, отсчитываемому от нуля.

(require '[pdf-oxide.core :as pdf])

(with-open [d (pdf/open "report.pdf")]
  ;; Одна страница
  (println (pdf/extract-text d 0))

  ;; Все страницы
  (doseq [i (range (pdf/page-count d))]
    (println "--- Page" (inc i) "---")
    (println (pdf/extract-text d i))))

Элементы страницы

pdf/page возвращает PdfPage. Из неё можно извлечь слова, строки, символы, таблицы, изображения и аннотации — каждый набор как вектор Clojure. Объекты слов, строк и символов предоставляют .text и .bbox через interop.

(require '[pdf-oxide.core :as pdf])

(with-open [d (pdf/open "paper.pdf")]
  (let [pg (pdf/page d 0)]
    (println "page width:" (.width pg))

    ;; Слова вместе с их ограничивающими рамками
    (doseq [w (take 8 (pdf/words pg))]
      (println "  " (.text w) "@" (.bbox w)))

    ;; Остальные векторы элементов
    (println "lines:      " (count (pdf/lines pg)))
    (println "chars:      " (count (pdf/chars pg)))
    (println "tables:     " (count (pdf/tables pg)))
    (println "images:     " (count (pdf/images pg)))
    (println "annotations:" (count (pdf/annotations pg)))

    ;; Обычный текст всей страницы или вырезанной области (BBox)
    (println (pdf/page-text pg))))

Чтобы ограничить извлечение областью, передайте fyi.oxide.pdf.geometry.BBox:

(import '[fyi.oxide.pdf.geometry BBox])

(with-open [d (pdf/open "paper.pdf")]
  (let [pg (pdf/page d 0)]
    (println (pdf/page-text pg (BBox. 0.0 0.0 1000.0 1000.0)))))

Конвертация в Markdown и HTML

Конвертируйте весь документ или отдельную страницу в Markdown или HTML.

(require '[pdf-oxide.core :as pdf])

(with-open [d (pdf/open "paper.pdf")]
  ;; Весь документ
  (println (pdf/to-markdown d))
  (println (pdf/to-html d))

  ;; Отдельная страница (индекс от нуля)
  (println (pdf/to-markdown d 0))
  (println (pdf/to-html d 0)))

Для более богатой структуры pdf/extract-structured возвращает дерево структурированных элементов страницы:

(with-open [d (pdf/open "paper.pdf")]
  (println (pdf/extract-structured d 0)))

Поиск

pdf/search сканирует весь документ и возвращает вектор объектов-совпадений. Каждое совпадение предоставляет .text через interop.

(require '[pdf-oxide.core :as pdf])

(with-open [d (pdf/open "manual.pdf")]
  (doseq [m (pdf/search d "configuration")]
    (println (.text m))))

Рендеринг

Отрендерите страницу в массив байтов PNG, при желании с заданным DPI.

(require '[pdf-oxide.core :as pdf]
         '[clojure.java.io :as io])

(with-open [d (pdf/open "paper.pdf")]
  ;; DPI по умолчанию
  (io/copy (pdf/render d 0) (io/file "page-0.png"))

  ;; Явный DPI
  (io/copy (pdf/render d 0 150) (io/file "page-0@150.png")))

Создание

Тип Pdf предоставляет фабричные функции. pdf/save сериализует собранный Pdf в массив байтов.

(require '[pdf-oxide.core :as pdf]
         '[clojure.java.io :as io])

;; Из Markdown
(with-open [p (pdf/from-markdown "# Hello World\n\nThis is a PDF.")]
  (io/copy (pdf/save p) (io/file "output.pdf")))

;; Из HTML
(with-open [p (pdf/from-html "<h1>Invoice</h1><p>Amount: $42</p>")]
  (io/copy (pdf/save p) (io/file "invoice.pdf")))

Редактирование и редактирование с затиранием

pdf/editor открывает DocumentEditor (из массива байтов или пути) для структурных правок. Очистите метаданные, отметьте области для затирания и примените их необратимо, затем сериализуйте результат через pdf/editor-save.

(require '[pdf-oxide.core :as pdf]
         '[clojure.java.io :as io])
(import '[fyi.oxide.pdf.geometry BBox])

(with-open [ed (pdf/editor "form.pdf")]
  (pdf/scrub-metadata ed)
  (pdf/add-redaction ed 0 (BBox. 10.0 10.0 50.0 20.0))
  (pdf/apply-redactions ed)
  (io/copy (pdf/editor-save ed) (io/file "redacted.pdf")))

Метаданные и жизненный цикл

pdf/producer и pdf/creator возвращают метаданные документа как значение или nil, если они отсутствуют (java.util.Optional разворачивается за вас). Предпочитайте with-open; pdf/close и pdf/open? — это запасные средства для ручного управления жизненным циклом.

(require '[pdf-oxide.core :as pdf])

(let [d (pdf/open "paper.pdf")]
  (println "open?    " (pdf/open? d))
  (println "producer:" (or (pdf/producer d) "(none)"))
  (println "creator: " (or (pdf/creator d) "(none)"))
  (pdf/close d)
  (println "open?    " (pdf/open? d)))

Дальнейшие шаги