Class: Uniword::Images::ImageManager

Inherits:
Object
  • Object
show all
Defined in:
lib/uniword/images/image_manager.rb

Overview

Orchestrator for image operations on a DocumentRoot.

Works with the document’s image_parts hash (populated during DOCX loading) and the package ZIP to enumerate, extract, insert, and remove images.

Examples:

List images

doc = Uniword::DocumentFactory.from_file("report.docx")
manager = ImageManager.new(doc)
manager.list  # => [#<ImageInfo ...>, ...]

Extract images to a directory

count = manager.extract("/tmp/images")

Insert a new image

manager.insert("/path/photo.png", position: 3, width: "6in")

Remove an image by name

manager.remove("image1.png")

Constant Summary collapse

EMU_PER_PX =

Pixels per EMU at 96 DPI.

9525
EMU_PER_INCH =

Inches per EMU.

914_400
EMU_PER_CM =

Centimeters per EMU.

360_000

Instance Method Summary collapse

Constructor Details

#initialize(document) ⇒ ImageManager

Returns a new instance of ImageManager.

Parameters:



38
39
40
# File 'lib/uniword/images/image_manager.rb', line 38

def initialize(document)
  @document = document
end

Instance Method Details

#extract(output_dir) ⇒ Integer

Extract all images to a directory on disk.

Creates output_dir if it does not exist. Each image is written using the filename from the package entry.

Parameters:

  • output_dir (String)

    Target directory

Returns:

  • (Integer)

    Number of images extracted



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/uniword/images/image_manager.rb', line 76

def extract(output_dir)
  FileUtils.mkdir_p(output_dir)

  parts = @document.image_parts
  return 0 unless parts && !parts.empty?

  parts.each_value do |entry|
    data = entry[:data]
    next unless data

    filename = File.basename(entry[:target].to_s)
    File.binwrite(File.join(output_dir, filename), data)
  end

  parts.size
end

#insert(image_path, **options) ⇒ String

Insert an image into the document.

Reads the file at image_path, registers it in the document’s image_parts, and appends a new paragraph containing an inline image Drawing at the specified position (or at the end).

Parameters:

  • image_path (String)

    Path to the image file on disk

  • options (Hash)

    Insertion options

Options Hash (**options):

  • :position (Integer)

    Paragraph index (nil = append)

  • :width (String)

    Width string (e.g., “6in”, “15cm”)

  • :height (String)

    Height string (e.g., “4in”, “10cm”)

  • :description (String)

    Alt text / description

Returns:

  • (String)

    Relationship ID assigned to the new image



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/uniword/images/image_manager.rb', line 106

def insert(image_path, **options)
  unless File.exist?(image_path)
    raise ArgumentError,
          "Image file not found: #{image_path}"
  end

  width_emu = parse_dimension(options[:width])
  height_emu = parse_dimension(options[:height])

  # Register image part via the existing ImageBuilder infrastructure
  r_id = Builder::ImageBuilder.register_image(@document, image_path)

  # Determine pixel dimensions for fallback EMU conversion
  px_w, px_h = Builder::ImageBuilder.read_dimensions(image_path)
  width_emu ||= px_w * EMU_PER_PX
  height_emu ||= px_h * EMU_PER_PX

  # Build the Drawing element
  drawing = Builder::ImageBuilder.create_drawing(
    @document, image_path,
    width: width_emu,
    height: height_emu,
    alt_text: options[:description]
  )

  # Wrap in a Run and Paragraph, then insert
  run = Wordprocessingml::Run.new
  run.drawings << drawing

  paragraph = Wordprocessingml::Paragraph.new
  paragraph.runs << run

  body = @document.body ||= Wordprocessingml::Body.new
  body.paragraphs ||= []

  position = options[:position]
  if position && position < body.paragraphs.size
    body.paragraphs.insert(position, paragraph)
  else
    body.paragraphs << paragraph
  end

  r_id
end

#listArray<ImageInfo>

List all images in the document.

Returns one ImageInfo per entry in document.image_parts. When pixel dimensions cannot be determined from the binary data, width and height will be nil.

Returns:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/uniword/images/image_manager.rb', line 49

def list
  parts = @document.image_parts
  return [] unless parts && !parts.empty?

  parts.map do |_r_id, entry|
    data = entry[:data]
    px_w, px_h = detect_pixel_dimensions(data) if data
    name = File.basename(entry[:target].to_s)

    ImageInfo.new(
      name: name,
      path: "word/#{entry[:target]}",
      content_type: entry[:content_type].to_s,
      size: data ? data.bytesize : 0,
      width: px_w,
      height: px_h,
    )
  end
end

#remove(image_name) ⇒ Boolean

Remove an image by filename from the document.

Removes the matching entry from image_parts. Does NOT remove Drawing references in the document XML; callers should handle that separately if needed.

Parameters:

  • image_name (String)

    Filename (e.g., “image1.png”)

Returns:

  • (Boolean)

    true if an entry was removed



159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/uniword/images/image_manager.rb', line 159

def remove(image_name)
  parts = @document.image_parts
  return false unless parts && !parts.empty?

  key = parts.find do |_k, entry|
    File.basename(entry[:target].to_s) == image_name
  end&.first

  return false unless key

  parts.delete(key)
  true
end