Class: SwarmSDK::Tools::ImageExtractors::PdfImageExtractor

Inherits:
Object
  • Object
show all
Defined in:
lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb

Overview

Extracts images from PDF documents Only extracts JPEG images (DCTDecode format) which are LLM API compatible Non-JPEG images (FlateDecode, LZWDecode) are skipped because they would require TIFF format which is not supported by LLM APIs Supported LLM image formats: ['png', 'jpeg', 'gif', 'webp']

Class Method Summary collapse

Class Method Details

.extract_from_page(page, page_number, temp_dir) ⇒ Array<String>

Extract images from a single PDF page

Parameters:

  • page (PDF::Reader::Page)

    The PDF page

  • page_number (Integer)

    Page number (1-indexed)

  • temp_dir (String)

    Directory to save extracted images

Returns:

  • (Array<String>)

    Array of file paths for extracted images



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb', line 37

def extract_from_page(page, page_number, temp_dir)
  extracted_files = []

  # Get XObjects (external objects) from the page
  xobjects = page.xobjects
  return extracted_files if xobjects.empty?

  xobjects.each do |name, stream|
    # Only process Image XObjects (not Form XObjects)
    next unless stream.hash[:Subtype] == :Image

    file_path = save_image(stream, page_number, name, temp_dir)
    extracted_files << file_path if file_path
  end

  extracted_files
rescue StandardError
  # If extraction fails for this page, continue with others
  []
end

.extract_images(reader, pdf_path) ⇒ Array<String>

Extract all images from a PDF document

Parameters:

  • reader (PDF::Reader)

    The PDF reader instance

  • pdf_path (String)

    Path to the PDF file

Returns:

  • (Array<String>)

    Array of temporary file paths containing extracted images



17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb', line 17

def extract_images(reader, pdf_path)
  image_paths = []
  temp_dir = Dir.mktmpdir("pdf_images_#{File.basename(pdf_path, ".*")}")

  reader.pages.each_with_index do |page, page_index|
    page_images = extract_from_page(page, page_index + 1, temp_dir)
    image_paths.concat(page_images)
  end

  image_paths
rescue StandardError
  # If image extraction fails, log it but don't fail the entire PDF read
  []
end

.save_as_tiff(stream, page_number, name, temp_dir) ⇒ String?

Save raw image data as TIFF

Parameters:

  • stream (PDF::Reader::Stream)

    The image stream

  • page_number (Integer)

    Page number

  • name (Symbol)

    Image name

  • temp_dir (String)

    Directory to save the image

Returns:

  • (String, nil)

    File path if successful, nil for unsupported color spaces



107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb', line 107

def save_as_tiff(stream, page_number, name, temp_dir)
  color_space = stream.hash[:ColorSpace]

  case color_space
  when :DeviceRGB
    save_rgb_tiff(stream, page_number, name, temp_dir)
  when :DeviceGray
    save_gray_tiff(stream, page_number, name, temp_dir)
  end
  # Unsupported color spaces return nil
rescue StandardError
  # If conversion fails, skip this image
  nil
end

.save_gray_tiff(stream, page_number, name, temp_dir) ⇒ String

Save grayscale image as TIFF

Parameters:

  • stream (PDF::Reader::Stream)

    The image stream

  • page_number (Integer)

    Page number

  • name (Symbol)

    Image name

  • temp_dir (String)

    Directory to save the image

Returns:

  • (String)

    File path



149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb', line 149

def save_gray_tiff(stream, page_number, name, temp_dir)
  filename = File.join(temp_dir, "page-#{page_number}-#{name}.tif")

  width = stream.hash[:Width]
  height = stream.hash[:Height]
  bpc = stream.hash[:BitsPerComponent] || 8

  # Build TIFF header for grayscale
  tiff = ImageFormats::TiffBuilder.build_gray_header(width, height, bpc)
  tiff << stream.unfiltered_data

  File.open(filename, "wb") { |file| file.write(tiff) }
  filename
end

.save_image(stream, page_number, name, temp_dir) ⇒ String?

Save a PDF image stream to disk Supports JPEG (DCTDecode) and raw formats

Parameters:

  • stream (PDF::Reader::Stream)

    The image stream

  • page_number (Integer)

    Page number

  • name (Symbol)

    Image name from XObject

  • temp_dir (String)

    Directory to save the image

Returns:

  • (String, nil)

    File path if successful, nil otherwise



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb', line 65

def save_image(stream, page_number, name, temp_dir)
  filter = stream.hash[:Filter]

  case filter
  when :DCTDecode
    # JPEG images can be saved directly - LLM API compatible
    save_jpeg(stream, page_number, name, temp_dir)
  when :FlateDecode, :LZWDecode, nil
    # Skip non-JPEG images to avoid TIFF format (not supported by LLM APIs)
    # LLM APIs only support: ['png', 'jpeg', 'gif', 'webp']
    # These images would require TIFF conversion which causes API errors
    nil
  end
  # Unsupported formats return nil
rescue StandardError
  # If saving fails, skip this image
  nil
end

.save_jpeg(stream, page_number, name, temp_dir) ⇒ String

Save JPEG image directly from PDF stream

Parameters:

  • stream (PDF::Reader::Stream)

    The image stream

  • page_number (Integer)

    Page number

  • name (Symbol)

    Image name

  • temp_dir (String)

    Directory to save the image

Returns:

  • (String)

    File path



90
91
92
93
94
95
96
97
98
99
# File 'lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb', line 90

def save_jpeg(stream, page_number, name, temp_dir)
  filename = File.join(temp_dir, "page-#{page_number}-#{name}.jpg")

  # JPEG images can be written directly - the stream.data contains a complete JPEG file
  File.open(filename, "wb") do |file|
    file.write(stream.data)
  end

  filename
end

.save_rgb_tiff(stream, page_number, name, temp_dir) ⇒ String

Save RGB image as TIFF

Parameters:

  • stream (PDF::Reader::Stream)

    The image stream

  • page_number (Integer)

    Page number

  • name (Symbol)

    Image name

  • temp_dir (String)

    Directory to save the image

Returns:

  • (String)

    File path



128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb', line 128

def save_rgb_tiff(stream, page_number, name, temp_dir)
  filename = File.join(temp_dir, "page-#{page_number}-#{name}.tif")

  width = stream.hash[:Width]
  height = stream.hash[:Height]
  bpc = stream.hash[:BitsPerComponent] || 8

  # Build TIFF header
  tiff = ImageFormats::TiffBuilder.build_rgb_header(width, height, bpc)
  tiff << stream.unfiltered_data # Get decompressed raw pixel data

  File.open(filename, "wb") { |file| file.write(tiff) }
  filename
end