Class: LiquidXlsx::Image

Inherits:
Object
  • Object
show all
Defined in:
lib/liquid_xlsx/image.rb

Overview

Polymorphic image source with format detection and deduplication.

Accepts:

  • Raw binary String (auto-detects format from magic bytes)
  • Image (idempotent)
  • IO / object with #read
  • Hash: { data:, io:, path:, url:, content_type:, width:, height: }
  • String that looks like a path/URL (requires images.loader)

Constant Summary collapse

SIGNATURES =

Magic byte signatures for format detection.

{
  "PNG" => ["\x89PNG\r\n\x1a\n".b, "image/png", "png"],
  "JPEG" => ["\xFF\xD8\xFF".b, "image/jpeg", "jpeg"],
  "GIF87a" => ["GIF87a".b, "image/gif", "gif"],
  "GIF89a" => ["GIF89a".b, "image/gif", "gif"]
}.freeze
MAX_COERCE_DEPTH =

Polymorphic coercion. Maximum depth of nested coercion (loader indirections etc.). Prevents infinite recursion when a loader returns a string that itself looks like a path/URL (or the same value it was given).

Returns:

4

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(binary:, content_type: nil, extension: nil) ⇒ Image

Returns a new instance of Image.



26
27
28
29
30
31
32
# File 'lib/liquid_xlsx/image.rb', line 26

def initialize(binary:, content_type: nil, extension: nil)
  @binary = binary.b
  @content_type = content_type || detect_content_type(@binary)
  raise_unknown_format(@content_type) unless @content_type

  @extension = extension || detect_extension(@content_type)
end

Instance Attribute Details

#binaryObject (readonly)

Returns the value of attribute binary.



16
17
18
# File 'lib/liquid_xlsx/image.rb', line 16

def binary
  @binary
end

#content_typeObject (readonly)

Returns the value of attribute content_type.



16
17
18
# File 'lib/liquid_xlsx/image.rb', line 16

def content_type
  @content_type
end

#extensionObject (readonly)

Returns the value of attribute extension.



16
17
18
# File 'lib/liquid_xlsx/image.rb', line 16

def extension
  @extension
end

Class Method Details

.coerce(value, loader: nil, depth: 0) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/liquid_xlsx/image.rb', line 43

def self.coerce(value, loader: nil, depth: 0)
  if depth > MAX_COERCE_DEPTH
    raise RenderError,
          "Image source could not be resolved to binary data " \
          "(loader kept returning unresolvable values)"
  end

  case value
  when Image
    value
  when String
    if value.b.start_with?(*signature_bytes)
      # Raw binary — wrap directly
      new(binary: value)
    else
      # Path or URL string — requires loader
      raise_loader_required(loader, value) unless loader

      raw = loader.call(value)
      if raw.is_a?(String) && !raw.b.start_with?(*signature_bytes)
        raise RenderError,
              "images.loader returned a string that is not recognized " \
              "image data for source '#{value}'"
      end
      coerce(raw, loader: loader, depth: depth + 1)
    end
  when Hash
    coerce_hash(value, loader: loader)
  else
    if value.respond_to?(:read)
      coerce(value.read, loader: loader, depth: depth + 1)
    else
      raise RenderError, "Unsupported image source type: #{value.class}"
    end
  end
end

Instance Method Details

#native_dimensionsObject

Native pixel dimensions [width, height] parsed from the binary header. Used to preserve aspect ratio when only width OR height is given. Returns nil when the format/size cannot be determined.



89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/liquid_xlsx/image.rb', line 89

def native_dimensions
  case @content_type
  when "image/png"
    png_dimensions
  when "image/gif"
    gif_dimensions
  when "image/jpeg"
    jpeg_dimensions
  end
rescue StandardError
  nil
end

#sha256String

SHA-256 hash for deduplication.

Returns:

  • (String)


82
83
84
# File 'lib/liquid_xlsx/image.rb', line 82

def sha256
  @sha256 ||= Digest::SHA256.hexdigest(@binary)
end