summaryrefslogtreecommitdiff
path: root/lib/booru/image_processing/svg_processor.rb
blob: 9e3e2ffca0075a035715ff3b92856238f062a000 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
# frozen_string_literal: true
require_relative 'shell_helper'
require 'nokogiri'

module Booru
  module ImageProcessing
    class SvgProcessor < Processor
      DATA_URI_REGEX = /\Adata:(image\/png|image\/gif|image\/jpeg|image\/svg+xml);base64,/

      include ShellHelper

      def initialize(original_path, directory, metadata)
        super(original_path, directory, metadata)

        # We keep around a reference to the Tempfile object so that it doesn't get GC'd (and deleted) too early.
        @rasterized_tempfile = Tempfile.new(%w[rendered .png]).tap(&:close)
        @rasterized = @rasterized_tempfile.path
      end

      def pre_process
        write_log 'pre_process'

        # clean up SVG file to remove embedded scripts and remote references
        svg = Nokogiri::XML(IO.read(@original_path))

        # The magic.
        svg.css('script').each(&:remove)
        svg.css('image').reject { |x| x.attributes['href'].to_s =~ DATA_URI_REGEX }.each(&:remove)

        File.open(@original_path, 'w') do |file|
          file.write svg.to_xml
        end

        generate_rasterized
      end

      def generate_rasterized
        # Is it OK to do this without scrubbing? - Yeah, that's the whole point of safe-rsvg-convert.
        exec_new 'RSVG-CONVERT', ['safe-rsvg-convert', @original_path, @rasterized]
      end

      def generate_version(dimensions, destination_path)
        write_log "generate_version #{dimensions} #{destination_path}"

        width, height = dimensions

        # resize rasterized version to desired size and optipng the resized version.
        # (converting the original file is slower than resizing a rasterized version)
        png = ::MiniMagick::Image.open(@rasterized)
        png.resize("#{width}x#{height}")
        png.write(destination_path)

        # is this needed? they didn't used to do that, but I do now. (because I want to)
        optipng(destination_path)
      end

      def generate_full_version(destination_path)
        write_log 'generate_full_version'
        # full.png -> rasterized
        # full.svg -> svg
        FileUtils.copy(@rasterized, destination_path)
        platform_link(@original_path, File.join(File.dirname(destination_path), 'full.svg'))
      end

      def post_process
        write_log 'post_process'
        optipng(@rasterized)
      end
    end
  end
end