Skip to content

Method listing supported "foreign" extensions #186

Closed

Description

@aleksandrs-ledovskis

This is a feature request.

Would it be possible to add class method to Vips that would return an array of supported file formats akin to $ vips -l foreign does? Having such introspection would allow to programmatically decide if for example Vips supports any given format for transformation operation.

If there already exists a way how to get list of Vips' supported extensions, I'm all ears.

Activity

  1. jcupitt commented on Oct 25, 2018

    @jcupitt
    Member

    Hello @aleksandrs-ledovskis,

    You can do this now, though it's a little painful. Have a look at:

    https://github.com/libvips/ruby-vips/blob/master/lib/vips/image.rb#L1341

    The generate_yard method that makes the ruby-vips docs.

    You would use GObject::g_type_from_name("VipsForeign") to get the GType for the base class of the file load/save operations (it's just an int), then walk all the subclasses recursively with Vips::vips_type_map looking for non-abstract types. You can instantiate an object of that type and get the text description, a list of the arguments, and the properties of each argument.

    You can get things like supported suffixes too, though you'd need to add a little more ffi for that.

  2. aleksandrs-ledovskis commented on Oct 28, 2018

    @aleksandrs-ledovskis
    Author

    Spent some time digging in ruby-vips/libvips and managed to get some PoC code

    require "vips"
    require "set"
    
    module Vips
      attach_function :vips_class_find, [:string, :string], :pointer
      attach_function :vips_object_summary_class, [:pointer, :pointer], :void
    
      class BufStruct < FFI::Struct
        layout :base, :pointer,
               :mx, :int,
               :i, :int,
               :full, :bool,
               :lasti, :int,
               :dynamic, :bool
      end
    end
    
    nicknames = []
    
    generate_class = lambda do |gtype, _|
      nickname = Vips.nickname_find(gtype)
      nicknames << nickname if nickname
    
      Vips.vips_type_map(gtype, generate_class, nil)
    end
    
    generate_class.call(
      GObject.g_type_from_name("VipsForeign"),
      nil
    )
    
    extensions = Set.new
    
    nicknames.each do |nickname|
      foreign_class = Vips.vips_class_find("VipsForeignLoad", nickname)
      next if foreign_class.null?
    
      buf_struct = Vips::BufStruct.new
      buf_struct_string = FFI::MemoryPointer.new(:char, 2048)
      buf_struct[:base] = buf_struct_string
      buf_struct[:mx] = 2048
    
      Vips.vips_object_summary_class(foreign_class, buf_struct.pointer)
    
      class_summary = buf_struct_string.read_string
      extensions.merge(class_summary.scan(/\.\w+\.?\w+/))
    end
    
    puts extensions

    On my machine/libvips configuration outputs:

    #<Set: {".csv", ".mat", ".vips", ".img", ".hdr", ".ppm", ".pgm", ".pbm", ".pfm", ".pdf", ".svg", ".svgz", ".svg.gz", ".gif", ".png", ".jpg", ".jpeg", ".jpe", ".webp", ".tif", ".tiff"}>
    

    I couldn't make access to foreign_class->suffs work via FFI. There isn't existing ForeignClass mapping in ruby-vips and making a simplistic pointer-to-struct didn't work.

      class ForeignClassStruct < FFI::Struct
        layout :parent, GObject::GObject::Struct,
           :constructed, :int,
           :static_object, :int,
           :argument_table, :pointer,
           :nickname, :string,
           :description, :string,
           :preclose, :int,
           :close, :int,
           :postclose, :int,
           :local_memory, :size_t,
           :priority, :int,
           :chars, :pointer
      end
    
      # Junk
      ForeignClassStruct.new(Vips.vips_class_find("VipsForeignLoad", "pngload"))[:nickname]
      ForeignClassStruct.new(Vips.vips_class_find("VipsForeignLoad", "pngload"))[:priority]
  3. tomasc commented on Feb 10, 2019

    @tomasc

    @aleksandrs-ledovskis I am using this method here through command-line:

    https://github.com/tomasc/dragonfly_libvips/blob/master/lib/dragonfly_libvips.rb#L19

    But indeed, would be great to have a method of ruby-vips that outputs such list.

  4. jcupitt commented on Feb 23, 2019

    @jcupitt
    Member

    I had a quick hack and added get_suffixes:

    $ irb
    irb(main):001:0> require 'vips'
    => true
    irb(main):002:0> Vips::get_suffixes
    => [".csv", ".mat", ".v", ".vips", ".ppm", ".pgm", ".pbm", ".pfm", ".hdr", ".dz", ".png", ".jpg", ".jpeg", ".jpe", ".webp", ".tif", ".tiff", ".fits", ".fit", ".fts", ".nii", ".nii.gz", ".hdr.gz", ".img", ".img.gz", ".nia", ".nia.gz", ".heic", ".gif", ".bmp"]
    irb(main):003:0> 
    

    Does that look reasonable? I'll add some tests as well.

  5. tomasc commented on Feb 23, 2019

    @tomasc

    Fabulous!

  6. added a commit that references this issue on Feb 23, 2019
  7. aleksandrs-ledovskis commented on Feb 24, 2019

    @aleksandrs-ledovskis
    Author

    @jcupitt I see that you are basing the output on VipsForeignSave. If I compare "save" vs. "load" class extensions, there are some differences (e.g. SVG/PDFs are only supported as input and BMP as output).

    As I use Vips for image processing I would be (more) interested in seeing a way to determine what extensions can be processed by Vips at the intake for purpose of populating texts visible to end-user ("Please upload image in ".jpg", ".jpeg" or ".png" formats)/doing pre-emptive validations based on file name.

    @tomasc's case also seems to require both "load" and "save" extensions.

  8. jcupitt commented on Feb 24, 2019

    @jcupitt
    Member

    Hi @aleksandrs-ledovskis, yes, that's true. libvips uses the suffix to pick savers, so there's a simple relationship between filetype and extension. But loaders are picked by sniffing the first few bytes of the file, so a list of extensions won't always be complete.

    I tried swapping it for VipsForeignLoad and I see:

    irb(main):004:0> Vips::get_suffixes
    => [".v", ".vips", ".ppm", ".pgm", ".pbm", ".pfm", ".png", ".exr", ".svs", ".vms", 
    ".vmu", ".ndpi", ".scn", ".mrxs", ".svslide", ".tif", ".bif", ".jpg", ".jpeg", ".jpe", 
    ".tiff", ".csv", ".pdf", ".svg", ".svgz", ".svg.gz", ".gif", ".mat", ".webp", ".img", 
    ".hdr", ".fits", ".fit", ".fts", ".nii", ".nii.gz", ".hdr.gz", ".img.gz", ".nia", ".nia.gz"]
    

    I don't know how useful that is. It's missing .ico and .bmp; .jpg is there three times, etc.

    If you want a list of the available loaders, just walking the types below VipsForeignLoad might be simpler.

    (libvips will load BMP, btw)

  9. added a commit that references this issue on Mar 14, 2019
  10. added a commit that references this issue on Apr 6, 2019
  11. locked and limited conversation to collaborators on Jan 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions