carrierwaveuploader / carrierwave

Classier solution for file uploads for Rails, Sinatra and other Ruby web frameworks
https://github.com/carrierwaveuploader/carrierwave
8.78k stars 1.66k forks source link

What are the read and write options for the `manipulate!` call? #2694

Closed TB-Development closed 10 months ago

TB-Development commented 1 year ago

Task

I'm tasked with using the manipulate! function to only process the first page of a file. I've successfully done this by copying the manipulate! source code and changing the current_path to be current_path[0].

Though this isn't a good idea because then I'd either have to submit a Pull Request with code dynamic and ordered enough for you guys to OK it or manage this code on my end which the former is a whole task itself and the latter is not a good long-term strategy.

I see that there is an options hash being passed into the manipulate! function, that is used to create a read_block, and then from which I think is being used to transform the image data given to ::Magick::Image.read. The idea is that I might be able to use this options hash to transform the information about the file given in order to only ONLY process the first page. The problem comes with utilizing this &read_block, it's making very little sense to me.

Issue 1

First of all, how do I even call the manipulate! function? The documentation for manipulate! says that the read options are meant to be called like this:

    # ==== :read
    # A hash of assignments to be given to the RMagick read call.
    #
    # The options available are identical to those for write, but are passed in directly, like this:
    #
    #     manipulate! :read => { :density => 300 }

but upon using manipulate in this way inside of my carrierwave uploader, like:

  version :thumb, :if => :thumbable? do
    process :front
    def full_filename(filename = model.file.filename) = "#{filename.split(".").first}.jpg"
    def style = :thumb
  end

  def front
    manipulate! :read => { :density => 300 }
  end

I am presented with the error:

wrong number of arguments (given 1, expected 0)

What gives?

Issue 2

The manipulate! documentation doesn't declare a full list of read options, neither links to a full list of read options. So, where is this information?

Issue 3

During the definition of the manipulate! function, there is a line of code that goes like:

 read_block = create_info_block(options[:read])
 image = ::Magick::Image.read(current_path, &read_block)

After looking up the documentation for ::Magick::Image.read/1. There is no second parameter that accepts a block. So, how does that call make sense? Truly, how am I meant to be using the &read_block to modify the image data that is RMagick will read?

TB-Development commented 1 year ago

I eventually decided to piece out code from the CarrierWave source for Rails and came up with an uploader like:

# @doc Returns the first page of a file
version :thumb, :if => :thumbable? do
  process :cover
  process convert: "jpg"
  def full_filename(filename = model.file.filename) = "#{filename.split(".").first}.jpg"
  def style = :thumb

  # @doc This sources code directly from the `manipulate!` function defined in CarrierWave's library
  # @doc I removed the ability to pass in options, or a block to the function to use in processing the image.
  def cover
    cache_file
    push_frames(get_frames)
  rescue ::Magick::ImageMagickError
    raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.processing_error")
  end

  private

  # @doc This will store the file available on the uploader in the cache, unless it is already cached.
  def cache_file
    cache_stored_file! unless cached?
  end

  # @doc This will utilize RMagick to create an image from an altered current_path variable
  # @doc Create a new ImageList, and store the image created as the first frame
  def get_frames
    path = "#{current_path}[0]"
    image = ::Magick::Image.read(path)
    frames = ::Magick::ImageList.new
    frames << image.first
  end

  # @doc This will persist the frames created as an ImageList into the file on the uploader, essentially replacing the original pdf with the modified pdf.
  def push_frames(frames)
    frames.write(current_path)
  end

  # @doc This will destroy the ImageList that is allocating memory.
  def destroy_image(frames)
    frames.try(:destroy!)
  end
end
mshibuya commented 10 months ago

You should refer to the official RMagick documentation. The rubydoc one you put seems to be incomplete, though I don't know why. https://rmagick.github.io/image1.html#read https://rmagick.github.io/ilist.html#write https://rmagick.github.io/info.html