Class: Pdf::ImageExtractor

Inherits:
Object
  • Object
show all
Defined in:
app/services/pdf/image_extractor.rb

Overview

Extracts raster image placements from a PDF: every image XObject painted on
a page, saved to a temp file, with its bounding box in PDF user-space points
(lower-left origin) — the same coordinate system Toolkit.stamp
consumes, so a regenerated image can be stamped back over the exact original
position.

Placement is tracked by walking each page's content stream with a HexaPDF
processor: an image is painted into the unit square (0,0)-(1,1) by the Do
operator, so the current transformation matrix at paint time maps that
square to the on-page bounding box. Images nested inside Form XObjects are
found too (the default processor recurses into forms with the form's matrix
premultiplied).

extraction = Pdf::ImageExtractor.extract('/tmp/manual.pdf')
extraction.placements # => [#<Placement page=2 x=72.0 y=300.0 …>, …]

FileUtils.rm_rf(extraction.workdir) # caller cleans up

Defined Under Namespace

Classes: Placement, Processor, Result

Constant Summary collapse

MIN_DIMENSION_PT =

Skip tiny placements (icons, bullets, rules, logos in hairline boxes) —
they never carry translatable prose and would burn vision/image-gen calls.

24

Class Method Summary collapse

Class Method Details

.extract(path) ⇒ Result

Returns workdir + placements (caller deletes workdir).

Parameters:

  • path (String)

    local PDF file path

Returns:

  • (Result)

    workdir + placements (caller deletes workdir)



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'app/services/pdf/image_extractor.rb', line 94

def extract(path)
  Pdf::Loader.load!
  doc     = HexaPDF::Document.open(path)
  workdir = Dir.mktmpdir('pdf_images')

  placements = doc.pages.each_with_index.flat_map do |page, idx|
    processor = Processor.new(page.resources, page_number: idx + 1, workdir:)
    page.process_contents(processor)
    processor.placements
  end

  Result.new(workdir:, placements:)
rescue HexaPDF::Error => e
  FileUtils.rm_rf(workdir) if workdir
  raise Pdf::Toolkit::Error, "Could not extract images from PDF: #{e.message}"
end