RMagick / Imagemagick слишком медленно обрабатывает текст для Heroku - PullRequest
0 голосов
/ 11 декабря 2018

В Японии огромная культура подарков, и каждый год нам приходится распечатывать тонны этих "ноши".Я сделал просто программу rails для добавления текста в пустое изображение noshi для добавления в нашу систему (уже встроенную в rails).

Для справки, в основном я хотел сделать открытую версию этого, которая не имеетводяной знак: www.noshi.jp

Вот как выглядит контроллер: def create @noshi = Noshi.new (noshi_params)

  # Set up variables
  ntype = @noshi.ntype
  omote = @noshi.omotegaki
  olength = omote.length
  opsize = (168 - (olength * 12))
  namae = @noshi.namae
  namae2 = @noshi.namae2
  # namae3 = @noshi.namae3
  # namae4 = @noshi.namae4
  # namae5 = @noshi.namae5

    replacements = [ ["(株)", "㈱"], ["(有)", "㈲"] ]
    replacements.each {|replacement| namae.gsub!(replacement[0], replacement[1])}
    replacements.each {|replacement| namae2.gsub!(replacement[0], replacement[1])}
    # replacements.each {|replacement| namae3.gsub!(replacement[0], replacement[1])}
    # replacements.each {|replacement| namae4.gsub!(replacement[0], replacement[1])}
    # replacements.each {|replacement| namae5.gsub!(replacement[0], replacement[1])}

    names = []
    names += [namae, namae2] # removed namae3, namae4, namae5 for the time being
    longest = names.max_by(&:length)
    nlength = longest.length
  npsize = (144 - (nlength * 12))
  i = 0
  # Pull Noshi Type
  noshi_img = MiniMagick::Image.open("#{ENV['GBUCKET_PREFIX']}noshi/noshi#{ntype}.jpg")
    # Resize to A4 @ 300dpi
    noshi_img.resize "2480x3508"
    # Iterate through each character
    omote.each_char do |c|
      # Open new blank/transparent noshi
      chars = MiniMagick::Image.open("#{ENV['GBUCKET_PREFIX']}noshi/noshi_blank.png")
      chars.resize "2480x3508"
      # Draw Each Omotegaki Character
      chars.combine_options do |d|
        d.gravity 'North'
        # Placement based on point size
        plcmnt = ((opsize / 12 * 12) + (opsize * i * 1.2))
        d.draw "text 0,#{plcmnt} '#{c}'"
        d.font 'TakaoPMincho'
        d.pointsize opsize
        d.fill("#000000")
        i += 1
      end
      # Composite each letter as iterated
      noshi_img = noshi_img.composite(chars) do |comp|
        comp.compose "Over"    # OverCompositeOp
        comp.geometry "+0+0" # copy second_image onto first_image from (0, 0)
      end
    end
  # Iterator Reset
  i = 0
    # Draw Name Text (Line 1)
    namae.each_char do |c|
      # Iterate through each character
      # Open new blank/transparent noshi
      chars = MiniMagick::Image.open("#{ENV['GBUCKET_PREFIX']}noshi/noshi_blank.png")
      # Resize to a square so it's easy to flip
      chars.resize "2480x3508"
      chars.combine_options do |d|
      # Middle position for first line so set to 0
      xplcmnt =  (npsize / 12) * 0
      yplcmnt = (625 - npsize) - (npsize * i)
        d.gravity 'south'
        # Placement based on point size, fix for katakana dash
        # positive x is 
        if c == 'ー' 
          yplcmnt += 15
          d.draw "text 0,#{yplcmnt} '|'"
          d.pointsize (npsize * 0.85)
        else
          d.draw "text 0,#{yplcmnt} '#{c}'"
          d.pointsize npsize
        end
        d.font 'TakaoPMincho'
        d.fill("#000000")
        i += 1
      end
      # Composite each letter as iterated
      noshi_img = noshi_img.composite(chars) do |comp|
        comp.compose "Over"    # OverCompositeOp
        comp.geometry "+0+0" # copy second_image onto first_image from (0, 0)
      end
    end
  # Iterator Reset
  i = 0
    # Draw Name Text (Line 2)
    namae2.each_char do |c|
      # Iterate through each character
      # Open new blank/transparent noshi
      chars = MiniMagick::Image.open("#{ENV['GBUCKET_PREFIX']}noshi/noshi_blank.png")
      # Resize to a square so it's easy to flip
      chars.resize "2480x3508"
      chars.combine_options do |d|
      # Next position for second line so set by font size
      xplcmnt = (npsize / 6) - npsize * 1.45
      yplcmnt = (625 - (npsize * 2)) - (npsize * i)
        d.gravity 'south'
        # Placement based on point size, fix for katakana dash
        if c == 'ー' 
          yplcmnt += 15
          d.draw "text #{xplcmnt},#{yplcmnt} '|'"
          d.pointsize (npsize * 0.85)
        else
          d.draw "text #{xplcmnt},#{yplcmnt} '#{c}'"
          d.pointsize npsize
        end
        d.font 'TakaoPMincho'
        d.fill("#000000")
        i += 1
      end
      # Composite each letter as iterated
      noshi_img = noshi_img.composite(chars) do |comp|
        comp.compose "Over"    # OverCompositeOp
        comp.geometry "+0+0" # copy second_image onto first_image from (0, 0)
      end
    end

  # Setup and save the file
  noshi_img.format "png"
  fname = "#{@noshi.omotegaki}_#{@noshi.namae}"
  dkey = Time.now.strftime('%Y%m%d%H%M%S')
  ext = '.png'
  finlname = fname + dkey + ext
  noshi_img.write finlname
  @noshi.image = File.open(finlname)
  File.delete(finlname) if File.exist?(finlname)

respond_to do |format|
  if @noshi.save
    format.html { redirect_to @noshi, notice: '熨斗が作成されました。' }
    format.json { render :show, status: :created, location: @noshi }
  else
    format.html { render :new }
    format.json { render json: @noshi.errors, status: :unprocessable_entity }
  end
end end

Как это работает.1. Пользователь рисует фон ноши, выбирает тип заголовка ноши (для お 歳 暮 или お 祝 い и т. Д.) И вводит имя 2. Затем приложение берет соответствующий файл из gCloud для фона ноши.3. Приложение берет каждую букву и вычисляет размер и расположение шрифта на основе количества букв и строк.4. Он берет пустой файл изображения и помещает каждую букву в свое собственное изображение, а затем объединяет их все в окончательное изображение.

ДА, необходимо создать новое изображение для каждой буквы, потому что, насколько яЯ могу сказать, что для ImageMagick нет вертикального текстового формата (в правой части вверх) (довольно важной функции для большой части планеты [Китай, Япония, Корея], поэтому я нахожу довольно удивительным, что ему этого не хватает).

Это отлично работает в разработке, и для наших целей я не против, чтобы это было медленно.Однако в Heroku это возвращает ошибку, если обработка занимает более 30 секунд, даже если noshi создается правильно каждый раз.

ВОПРОС:

Я прочитал, что «масштаб» вместо «изменение размера» может помочь, но, глядя на мой код, я чувствую, что должен быть более эффективный способ сделать то, что я »мы сделали здесьЯ пытался использовать базовые изображения с меньшими размерами файлов, это не очень помогло.

Есть ли более эффективный способ сделать это?

Если нет, есть ли способ отправить пользователя куда-нибудь, чтобы дождаться завершения ноши, чтобы он не возвращал ошибку каждый раз?

ОБНОВЛЕНИЕ:

Просто возвращаюсь, чтобы показать работающий контроллер Ruby on Rails, с которым я закончил:

def create
        @noshi = Noshi.new(noshi_params)

        # Set up variables
        ntype = @noshi.ntype
        omote = @noshi.omotegaki
        omote_length = omote.length
        omote_point_size = (168 - (omote_length * 12))

        #make an array with each of the name lines entered
        name_array = Array.new
        name_array << @noshi.namae
        name_array << @noshi.namae2
        name_array << @noshi.namae3
        name_array << @noshi.namae4
        name_array << @noshi.namae5

        #replace multi-character prefixes with their single charcter versions
        #replace katakana dash with capital I
        #insert line breaks after each letter for Japanese vertical type
        name_array.each do |namae|
            replacements = [ ["(株)", "㈱"], ["(有)", "㈲"], ["ー", "|"] ]
            replacements.each {|replacement| namae.gsub!(replacement[0], replacement[1])}
        end
        def add_line_breaks(string)
            string.scan(/.{1}/).join("\n")
        end
        name_array.map!{ |namae| add_line_breaks(namae)}
        #add line breaks after each character for the omote as well
        omote = add_line_breaks(omote)
        #find the longest string (important: after the character concatenation) in the name array to calculate the point size for the names section
        name_array_max_length = (name_array.map { |namae| namae.length }).max
        name_point_size = (204 - (name_array_max_length * 10))
        #max omote size is 156, and the name needs to be an order smaller than that by default.
        if name_point_size > 108
            name_point_size = 108
        end

        # Pull Noshi Type
        noshi_img = MiniMagick::Image.open("#{ENV['GBUCKET_PREFIX']}noshi/noshi#{ntype}.jpg")
        # Resize to A4 @ 300dpi
        noshi_img.resize "2480x3508"

        #create the overlay image
        name_overlay = MiniMagick::Image.open("#{ENV['GBUCKET_PREFIX']}noshi/noshi_blank.png")
        name_overlay.resize "2480x3508"
        #first time for omote
        name_overlay.combine_options do |image|
            image.gravity 'North'
            # Placement based on point size
            omote_placement_y = (348 - (omote_length * (omote_point_size / 2)))
            image.font 'TakaoPMincho'
            image.pointsize omote_point_size
            image.fill("#000000")
            image.draw "text 0,#{omote_placement_y} '#{omote}'"
        end
        #count number of names in array, add a name for each time
        name_array.count.times do |i|
            name_overlay.combine_options do |image|
                image.gravity 'North'
                # Placement based on point size and iteration
                name_placement_x = (0 - i * name_point_size)
                name_placement_y = 1150 + ((i * name_point_size) - (name_point_size / 2))
                image.font 'TakaoPMincho'
                image.pointsize name_point_size
                image.fill("#000000")
                image.draw "text #{name_placement_x},#{name_placement_y} '#{name_array[i]}'"
            end
        end

        noshi_img = noshi_img.composite(name_overlay) do |comp|
            comp.compose "Over"    #OverCompositeOp
            comp.geometry "+0+0"   #copy second_image onto first_image from (0, 0)
        end


        # Setup and save the file
        noshi_img.format "png"
        #name the file
        fname = "#{@noshi.omotegaki}_#{@noshi.namae}"
        dkey = Time.now.strftime('%Y%m%d%H%M%S')
        ext = '.png'
        final_name = fname + dkey + ext
        #write a temporary version
        noshi_img.write final_name
        #write/stream the file to the uploader
        @noshi.image = File.open(final_name)
        #delete the original temporary
        File.delete(final_name) if File.exist?(final_name)

    respond_to do |format|
        if @noshi.save
        format.html { redirect_to @noshi, notice: '熨斗が作成されました。' }
        format.json { render :show, status: :created, location: @noshi }
        else
        format.html { render :new }
        format.json { render json: @noshi.errors, status: :unprocessable_entity }
        end
    end
    end

1 Ответ

0 голосов
/ 11 декабря 2018

ДА, необходимо создать новое изображение для каждой буквы, потому что, насколько я могу судить, вертикальный текстовый формат для ImageMagick

отсутствует.В командной строке ImageMagick вы можете создать вертикально выровненное текстовое строковое изображение, поместив перевод строки после каждого символа.

convert -background white -fill black -pointsize 18 -font arial -gravity center label:"t\ne\ns\nt\ni\nn\ng" result.png


enter image description here

Вам это помогает?Или это не практично?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...