From 0b717c20458d12191f479fc693dd1ca1eb11c050 Mon Sep 17 00:00:00 2001 From: Terence Lee Date: Mon, 5 Feb 2018 19:33:35 -0600 Subject: Provide an alternative PDF previewer based on Poppler mutool is licensed under the Affero GPL, which has strict distribution requirements. Poppler is licensed under the more liberal GPL, making it a good alternative for those who can't use mutool. --- .travis.yml | 1 + activestorage/app/models/active_storage/preview.rb | 7 ++--- activestorage/lib/active_storage/engine.rb | 5 +-- .../active_storage/previewer/mupdf_previewer.rb | 36 ++++++++++++++++++++++ .../lib/active_storage/previewer/pdf_previewer.rb | 26 ---------------- .../previewer/poppler_pdf_previewer.rb | 35 +++++++++++++++++++++ .../test/previewer/mupdf_previewer_test.rb | 23 ++++++++++++++ activestorage/test/previewer/pdf_previewer_test.rb | 23 -------------- .../test/previewer/poppler_pdf_previewer_test.rb | 23 ++++++++++++++ 9 files changed, 124 insertions(+), 55 deletions(-) create mode 100644 activestorage/lib/active_storage/previewer/mupdf_previewer.rb delete mode 100644 activestorage/lib/active_storage/previewer/pdf_previewer.rb create mode 100644 activestorage/lib/active_storage/previewer/poppler_pdf_previewer.rb create mode 100644 activestorage/test/previewer/mupdf_previewer_test.rb delete mode 100644 activestorage/test/previewer/pdf_previewer_test.rb create mode 100644 activestorage/test/previewer/poppler_pdf_previewer_test.rb diff --git a/.travis.yml b/.travis.yml index 2513e87114..09e16698e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,7 @@ addons: - ffmpeg - mupdf - mupdf-tools + - poppler-utils bundler_args: --without test --jobs 3 --retry 3 before_install: diff --git a/activestorage/app/models/active_storage/preview.rb b/activestorage/app/models/active_storage/preview.rb index 45efd26214..2b87897183 100644 --- a/activestorage/app/models/active_storage/preview.rb +++ b/activestorage/app/models/active_storage/preview.rb @@ -21,10 +21,9 @@ # # Outside of a Rails application, modify +ActiveStorage.previewers+ instead. # -# The built-in previewers rely on third-party system libraries: -# -# * {ffmpeg}[https://www.ffmpeg.org] -# * {mupdf}[https://mupdf.com] (version 1.8 or newer) +# The built-in previewers rely on third-party system libraries. Specifically, the built-in video previewer requires +# {ffmpeg}[https://www.ffmpeg.org]. Two PDF previewers are provided: one requires {Poppler}[https://poppler.freedesktop.org], +# and the other requires {mupdf}[https://mupdf.com] (version 1.8 or newer). To preview PDFs, install either Poppler or mupdf. # # These libraries are not provided by Rails. You must install them yourself to use the built-in previewers. Before you # install and use third-party software, make sure you understand the licensing implications of doing so. diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb index 1e223f9f17..1385e2aa84 100644 --- a/activestorage/lib/active_storage/engine.rb +++ b/activestorage/lib/active_storage/engine.rb @@ -3,7 +3,8 @@ require "rails" require "active_storage" -require "active_storage/previewer/pdf_previewer" +require "active_storage/previewer/poppler_pdf_previewer" +require "active_storage/previewer/mupdf_previewer" require "active_storage/previewer/video_previewer" require "active_storage/analyzer/image_analyzer" @@ -14,7 +15,7 @@ module ActiveStorage isolate_namespace ActiveStorage config.active_storage = ActiveSupport::OrderedOptions.new - config.active_storage.previewers = [ ActiveStorage::Previewer::PDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ] + config.active_storage.previewers = [ ActiveStorage::Previewer::PopplerPDFPreviewer, ActiveStorage::Previewer::MuPDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ] config.active_storage.analyzers = [ ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer ] config.active_storage.paths = ActiveSupport::OrderedOptions.new diff --git a/activestorage/lib/active_storage/previewer/mupdf_previewer.rb b/activestorage/lib/active_storage/previewer/mupdf_previewer.rb new file mode 100644 index 0000000000..ae02a4889d --- /dev/null +++ b/activestorage/lib/active_storage/previewer/mupdf_previewer.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module ActiveStorage + class Previewer::MuPDFPreviewer < Previewer + class << self + def accept?(blob) + blob.content_type == "application/pdf" && mutool_exists? + end + + def mutool_path + ActiveStorage.paths[:mutool] || "mutool" + end + + def mutool_exists? + return @mutool_exists unless @mutool_exists.nil? + + system mutool_path, out: File::NULL, err: File::NULL + + @mutool_exists = $?.exitstatus == 1 + end + end + + def preview + download_blob_to_tempfile do |input| + draw_first_page_from input do |output| + yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png" + end + end + end + + private + def draw_first_page_from(file, &block) + draw self.class.mutool_path, "draw", "-F", "png", "-o", "-", file.path, "1", &block + end + end +end diff --git a/activestorage/lib/active_storage/previewer/pdf_previewer.rb b/activestorage/lib/active_storage/previewer/pdf_previewer.rb deleted file mode 100644 index 426ff51eb1..0000000000 --- a/activestorage/lib/active_storage/previewer/pdf_previewer.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module ActiveStorage - class Previewer::PDFPreviewer < Previewer - def self.accept?(blob) - blob.content_type == "application/pdf" - end - - def preview - download_blob_to_tempfile do |input| - draw_first_page_from input do |output| - yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png" - end - end - end - - private - def draw_first_page_from(file, &block) - draw mutool_path, "draw", "-F", "png", "-o", "-", file.path, "1", &block - end - - def mutool_path - ActiveStorage.paths[:mutool] || "mutool" - end - end -end diff --git a/activestorage/lib/active_storage/previewer/poppler_pdf_previewer.rb b/activestorage/lib/active_storage/previewer/poppler_pdf_previewer.rb new file mode 100644 index 0000000000..2a787362cf --- /dev/null +++ b/activestorage/lib/active_storage/previewer/poppler_pdf_previewer.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module ActiveStorage + class Previewer::PopplerPDFPreviewer < Previewer + class << self + def accept?(blob) + blob.content_type == "application/pdf" && pdftoppm_exists? + end + + def pdftoppm_path + ActiveStorage.paths[:pdftoppm] || "pdftoppm" + end + + def pdftoppm_exists? + return @pdftoppm_exists unless @pdftoppm_exists.nil? + + @pdftoppm_exists = system(pdftoppm_path, "-v", out: File::NULL, err: File::NULL) + end + end + + def preview + download_blob_to_tempfile do |input| + draw_first_page_from input do |output| + yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png" + end + end + end + + private + def draw_first_page_from(file, &block) + # use 72 dpi to match thumbnail dimesions of the PDF + draw self.class.pdftoppm_path, "-singlefile", "-r", "72", "-png", file.path, &block + end + end +end diff --git a/activestorage/test/previewer/mupdf_previewer_test.rb b/activestorage/test/previewer/mupdf_previewer_test.rb new file mode 100644 index 0000000000..6c2db6fcbf --- /dev/null +++ b/activestorage/test/previewer/mupdf_previewer_test.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "test_helper" +require "database/setup" + +require "active_storage/previewer/mupdf_previewer" + +class ActiveStorage::Previewer::MuPDFPreviewerTest < ActiveSupport::TestCase + setup do + @blob = create_file_blob(filename: "report.pdf", content_type: "application/pdf") + end + + test "previewing a PDF document" do + ActiveStorage::Previewer::MuPDFPreviewer.new(@blob).preview do |attachable| + assert_equal "image/png", attachable[:content_type] + assert_equal "report.png", attachable[:filename] + + image = MiniMagick::Image.read(attachable[:io]) + assert_equal 612, image.width + assert_equal 792, image.height + end + end +end diff --git a/activestorage/test/previewer/pdf_previewer_test.rb b/activestorage/test/previewer/pdf_previewer_test.rb deleted file mode 100644 index fe32f39be4..0000000000 --- a/activestorage/test/previewer/pdf_previewer_test.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require "test_helper" -require "database/setup" - -require "active_storage/previewer/pdf_previewer" - -class ActiveStorage::Previewer::PDFPreviewerTest < ActiveSupport::TestCase - setup do - @blob = create_file_blob(filename: "report.pdf", content_type: "application/pdf") - end - - test "previewing a PDF document" do - ActiveStorage::Previewer::PDFPreviewer.new(@blob).preview do |attachable| - assert_equal "image/png", attachable[:content_type] - assert_equal "report.png", attachable[:filename] - - image = MiniMagick::Image.read(attachable[:io]) - assert_equal 612, image.width - assert_equal 792, image.height - end - end -end diff --git a/activestorage/test/previewer/poppler_pdf_previewer_test.rb b/activestorage/test/previewer/poppler_pdf_previewer_test.rb new file mode 100644 index 0000000000..2b41c8b642 --- /dev/null +++ b/activestorage/test/previewer/poppler_pdf_previewer_test.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "test_helper" +require "database/setup" + +require "active_storage/previewer/poppler_pdf_previewer" + +class ActiveStorage::Previewer::PopplerPDFPreviewerTest < ActiveSupport::TestCase + setup do + @blob = create_file_blob(filename: "report.pdf", content_type: "application/pdf") + end + + test "previewing a PDF document" do + ActiveStorage::Previewer::PopplerPDFPreviewer.new(@blob).preview do |attachable| + assert_equal "image/png", attachable[:content_type] + assert_equal "report.png", attachable[:filename] + + image = MiniMagick::Image.read(attachable[:io]) + assert_equal 612, image.width + assert_equal 792, image.height + end + end +end -- cgit v1.2.3