Skip to content

Початок роботи з PDF Oxide (Ruby)

PDF Oxide — це найшвидша бібліотека PDF для Ruby: видобування тексту за 0,8 мс у середньому й 100% успішних результатів на 3830 PDF. Один gem для видобування, пошуку, конвертації, створення та редагування PDF, побудований на тому самому ядрі на Rust, що живить прив’язки для Python, Java, Node, Go, C# і PHP.

Встановлення

gem install pdf_oxide

Або додайте його до свого Gemfile:

gem 'pdf_oxide', '~> 0.3'

Готова нативна бібліотека libpdf_oxide постачається всередині gem із тегом платформи — компілятор чи системне встановлення не потрібні. Готові gem-и охоплюють Ruby 3.1–3.4 на x86_64-linux, aarch64-linux, macOS на Intel та Apple Silicon, а також Windows (x64-mingw-ucrt).

Відкриття PDF

Використовуйте PdfDocument.open, щоб завантажити файл. Блокова форма автоматично закриває документ після завершення блоку; #close також доступний і є ідемпотентним.

require 'pdf_oxide'

PdfOxide::PdfDocument.open('research-paper.pdf') do |doc|
  puts "Pages: #{doc.page_count}"
  puts "PDF version: #{doc.pdf_version}"
  puts "Encrypted: #{doc.encrypted?}"
end

Для зашифрованих документів передайте password::

PdfOxide::PdfDocument.open('confidential.pdf', password: 'secret') do |doc|
  puts doc.extract_text(0)
end

Можна також відкривати документ із байтів у пам’яті — зручно під час потокового читання з S3, HTTP чи бази даних. PdfDocument.open автоматично розпізнає сирі байти PDF за магічним заголовком %PDF-:

bytes = File.binread('report.pdf')
PdfOxide::PdfDocument.open(bytes) do |doc|
  puts doc.extract_text(0)
end

Видобування тексту

Одна сторінка

Видобувайте простий текст із будь-якої сторінки за її індексом, що починається з нуля.

PdfOxide::PdfDocument.open('report.pdf') do |doc|
  text = doc.extract_text(0)
  puts text
end

Усі сторінки

PdfOxide::PdfDocument.open('book.pdf') do |doc|
  doc.page_count.times do |i|
    puts "--- Page #{i + 1} ---"
    puts doc.extract_text(i)
  end
end

Помічник для одного виклику

Коли вам потрібен текст лише однієї сторінки, PdfDocument.extract_text відкриває, видобуває та закриває документ одним викликом:

text = PdfOxide::PdfDocument.extract_text('report.pdf', page: 0)
puts text

Видобування з автомаршрутизацією

extract_text_auto використовує автомаршрутизатор із v0.3.51, щоб для кожної сторінки обрати нативний текст або OCR. У збірці без можливості ocr він плавно повертається до нативного текстового шару — він ніколи не викидає помилку «OCR unavailable».

PdfOxide::PdfDocument.open('scan.pdf') do |doc|
  puts doc.extract_text_auto(0)
end

Щоб отримати типізовану причину, яка описує якість видобування, використовуйте AutoExtractor:

PdfOxide::PdfDocument.open('scan.pdf') do |doc|
  ax     = doc.auto_extractor
  result = ax.extract_page(0)
  puts result[:text]
  warn "degraded: #{result[:reason]}" unless ax.ok?(result[:reason])
end

Робота зі сторінками

PdfDocument#page повертає легке подання PdfPage, яке запозичує дані з документа. #pages повертає по одному для кожної сторінки.

PdfOxide::PdfDocument.open('paper.pdf') do |doc|
  page = doc.page(0)
  puts "Index: #{page.index}"
  puts page.text   # same as doc.extract_text(0)

  doc.pages.each do |p|
    puts "Page #{p.index}: #{p.text.length} chars"
  end
end

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

Конвертуйте окрему сторінку (передавши її індекс) або весь документ (пропустивши індекс) у Markdown чи HTML.

PdfOxide::PdfDocument.open('paper.pdf') do |doc|
  puts doc.to_markdown(0)   # first page to Markdown
  puts doc.to_html(0)       # first page to HTML
  puts doc.to_markdown      # entire document to Markdown
end

Структуроване видобування

extract_structured повертає розібрану розкладку сторінки у вигляді Hash — розміри сторінки плюс типізовані області з текстом, обмежувальними прямокутниками й індексами колонок.

PdfOxide::PdfDocument.open('paper.pdf') do |doc|
  page = doc.extract_structured(0)
  puts "Size: #{page['page_width']} x #{page['page_height']}"
  page['regions'].each do |region|
    puts "#{region['kind']}: #{region['text']}"
  end
end

Пошук

search сканує весь документ і повертає масив хешів збігів, кожен із :page, :text і хешем :bbox із :x, :y, :width, :height.

PdfOxide::PdfDocument.open('manual.pdf') do |doc|
  matches = doc.search('configuration', case_sensitive: false)
  matches.each do |m|
    bbox = m[:bbox]
    puts "Page #{m[:page]}: '#{m[:text]}' at (#{bbox[:x].round}, #{bbox[:y].round})"
  end
end

Рендеринг

Відрендерте сторінку в байти PNG із заданою роздільною здатністю (DPI):

PdfOxide::PdfDocument.open('poster.pdf') do |doc|
  png = doc.render(0, dpi: 150)
  File.binwrite('page-0.png', png)
end

Створення PDF

Клас Pdf створює PDF із Markdown, HTML або простого тексту. Екземпляри володіють нативним дескриптором; використовуйте блокову форму (автоматичне закриття) або викликайте #close самостійно.

PdfOxide::Pdf.from_markdown("# Hello World\n\nThis is a PDF.") do |pdf|
  pdf.save('output.pdf')
end

PdfOxide::Pdf.from_html('<h1>Invoice</h1><p>Amount due: $42.00</p>') do |pdf|
  pdf.save('invoice.pdf')
end

PdfOxide::Pdf.from_text("Plain text document.\n\nSecond paragraph.") do |pdf|
  pdf.save('notes.pdf')
end

Замість збереження на диск отримайте байти за допомогою #to_bytes:

pdf_bytes = PdfOxide::Pdf.from_markdown('# Report').to_bytes
# upload pdf_bytes, attach to an email, etc.

Редагування (видалення вмісту)

DocumentEditor відкриває наявний PDF для незворотного видалення вмісту. apply_redactions! остаточно прибирає закритий вміст і може в тому самому проході очистити метадані документа.

PdfOxide::DocumentEditor.open('source.pdf') do |ed|
  ed.add_redaction(page: 0, rect: [100, 200, 300, 250])
  ed.apply_redactions!(scrub_metadata: true)
  ed.save_to('redacted.pdf')
end

Обробка помилок

PDF Oxide викидає типізовані підкласи PdfOxide::Error для специфічних для PDF збоїв.

begin
  PdfOxide::PdfDocument.open('document.pdf') do |doc|
    puts doc.extract_text(0)
  end
rescue PdfOxide::FileNotFoundError
  warn 'File not found'
rescue PdfOxide::EncryptedError
  warn 'Wrong or missing password'
rescue PdfOxide::ParseError => e
  warn "Malformed PDF: #{e.message}"
rescue PdfOxide::Error => e
  warn "PDF error: #{e.message}"
end

Наступні кроки