class EmbedXMP::PNG

PNG images

Constants

PNG_HEADER
PNG_IMAGE_END
PNG_SIGNATURE
XMP_CHUNK_SIG

Public Instance Methods

check_file_signatures() click to toggle source

Quick and dirty test to see if png_data is a PNG image file

# File lib/embed_xmp/png.rb, line 31
def check_file_signatures
  raise 'NoPNGSignature' if PNG_SIGNATURE != @image_data[0..7]
  raise 'NoPNGEndOfFile' if PNG_IMAGE_END != @image_data[-8..-5]
end
chunk(offset) click to toggle source
# File lib/embed_xmp/png.rb, line 36
def chunk(offset)
  chunk_length = @image_data[offset, 4].b.unpack1('N') + 12

  raise 'ChunkLongerThanFile' if offset + chunk_length > @image_data.length

  chunk_id = @image_data[offset + 4, 4]

  data = @image_data[offset + 8, chunk_length]

  [chunk_id, chunk_length, data]
end
join_sidecar(sidecar_file, xpacked: false) click to toggle source

Join an XMP sidecar file into a PNG image file.

# File lib/embed_xmp/png.rb, line 20
def join_sidecar(sidecar_file, xpacked: false)
  check_file_signatures
  remove_xmp

  sidecar = read_io_or_string(sidecar_file)
  xmp_chunk = create_xmp_itxt(sidecar, xpacked)

  insert_into_file(find_xmp_insertion_offset, xmp_chunk)
end
remove_chunk(offset) click to toggle source

Return chunk at offset from the beginning of the file.

# File lib/embed_xmp/png.rb, line 49
def remove_chunk(offset)
  _, chunk_length, = chunk(offset)

  @image_data.slice!(offset, chunk_length)
end
remove_xmp() click to toggle source

rubocop: disable Metrics/MethodLength

# File lib/embed_xmp/png.rb, line 56
def remove_xmp
  offset = PNG_SIGNATURE.length
  while offset < @image_data.length
    chunk_id, chunk_length, = chunk(offset)

    break if [PNG_IMAGE_END, nil].include?(chunk_id)

    if chunk_contains_xmp(offset)
      remove_chunk(offset)
      next
    end

    offset += chunk_length
  end
end

Private Instance Methods

chunk_contains_xmp(offset) click to toggle source
# File lib/embed_xmp/png.rb, line 93
def chunk_contains_xmp(offset)
  XMP_CHUNK_SIG == @image_data[offset + 4, XMP_CHUNK_SIG.length + 3].b
end
create_xmp_itxt(xmp_data, xpacked) click to toggle source
# File lib/embed_xmp/png.rb, line 105
def create_xmp_itxt(xmp_data, xpacked)
  chunk_id = 'iTXt'
  chunk_data = ("XML:com.adobe.xmp\0\0\0\0\0" +
               EmbedXMP::XMP
               .new(xmp_data, writable: false, xpacked: xpacked)
               .to_s.gsub("\0", ' ')).b

  new_chunk(chunk_id, chunk_data)
end
find_xmp_insertion_offset() click to toggle source

rubocop: enable Metrics/MethodLength

# File lib/embed_xmp/png.rb, line 75
def find_xmp_insertion_offset
  offset = PNG_SIGNATURE.length
  cursor = offset
  while offset < @image_data.length
    chunk_id, chunk_length, = chunk(offset)

    break if [PNG_IMAGE_END, nil].include?(chunk_id)

    if PNG_HEADER == chunk_id
      cursor = offset + chunk_length
    end

    offset += chunk_length
  end

  cursor
end
new_chunk(chunk_id, chunk_data) click to toggle source
# File lib/embed_xmp/png.rb, line 97
def new_chunk(chunk_id, chunk_data)
  length = [chunk_data.length].pack('N')

  checksum = [Digest::CRC32.checksum(chunk_id + chunk_data)].pack('N')

  length.b + chunk_id + chunk_data + checksum
end