Edit existing pdf file metadata with ruby (Apply Password protection) - ruby-on-rails

I am uploading a pdf file using paperclip to s3. I want to apply password protection on the fly to the uploaded pdf file.
I tried to use the code given in question How to edit or write on existing PDF with Ruby?! to edit existing pdf file (the tmp file used by the paperclip) and try to apply password protection using
Prawn::Document.generate("tmp/abc.pdf",:template => params[:ebook].path) do encrypt_document(:user_password => 'foo', :owner_password => 'bar',
:permissions => { :print_document => false,
:modify_contents => false,
:copy_contents => false,
:modify_annotations => false } end
Is the template support still exist in prawn or it had been deprecated as i didn't find anything regarding template in the prawn manual! ?
Is there any other way or any other gem to do so ?
Thanks.

template was removed in version 0.13.0 because it was too buggy :
Support for templates was dropped in Prawn 0.13.0, disabled by default in 0.14.0, and extracted in 0.15.0.
This gem includes the extracted templates code, which is completely unsupported, but provides the old functionality that was in Prawn 0.12.0 for years.
source : https://github.com/prawnpdf/prawn-templates
As he said, you can try to add the library to your current Prawn installation.
Otherwise you can use pdftk with Open3 module (http://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/) :
require 'open3'
file_name = 'hello_world_1.pdf' # input
file_name_output = 'hello_world_2.pdf' # output
usr = 'foo'
pwd = 'bar'
pdftek = './pdftk.exe' # tested on windows
Open3.popen3("#{pdftek} #{file_name} output #{file_name_output} owner_pw #{pwd} user_pw #{usr}") do |stdin,stdout,stderr|
# ...
end
There is also a wrapper for ruby but I haven't test it yet : https://github.com/tcocca/active_pdftk

Related

Rails PDFKit - Errno::ENOENT (No such file or directory) when using to_file

Whenever I try to generate a pdf using to_file, the process will just hang, and when I stop the development server I get Errno::ENOENT (No such file or directory - path/to/pdf). However, I am able to render a pdf inline using to_pdf. I'm also able to generate PDFs from the command line in the same folder that I'm trying to generate them in with to_file.
I'm using Rails 3.2.12 and pdfkit 0.8.2. I've tried using wkhtmltopdf versions 0.9.6 through 0.12.4. I'm on Ubuntu 14.04.
Example from controller:
html = render_to_string(:action => "show.html.erb", :formats => :html)
kit.stylesheets << "{Rails.root}/app/assets/stylesheets/stylesheet1.css"
kit.stylesheets << "#{Rails.root}/vendor/assets/stylesheets/stylesheet2.css"
kit.to_file("#{Rails.root}/folder_to_write_to/generated_pdf.pdf")
Turns out the issue was the asset pipeline conflicting with wkhtmltopdf. Added config.threadsafe! to development.rb and it started working.
Another issue can be the default options passed. For example, when I left the default print_media_type option in place, found this message in the log:
The switch --print-media-type, is not support using unpatched qt, and will be ignored."
Only when I override that does it work for me, either in the initializer or like so:
PDFKit.new(html, {print_media_type: false})
The message says it'll be ignored, but it wasn't. It was causing the file to not get generated.

Dynamic CSS using Sprockets

I'm working on a site builder in rails and I would like to render the sites css using Sprockets SCSS processors. Since the user can change colors and logos, I can't use Sprockets precompilation so I've started working on a Rails SCSS template handler to handle dynamic views.
The goal is to compile 'app/views/sites/show.css.scss' any time /sites/43543.css is requested. Here's what I have so far. You'll notice I first run the template through the ERB processor and then attempt to run it through Sprockets.
https://gist.github.com/3870095
Manuel Meurer came up with an alternative solution that writes the ERB output to a path and then triggers the Asset Pipeline to compile it. I was able to get his solution to work locally but it wont work on heroku because the asset path is not writable. Files can only be written to the tmp directory and those files are only guaranteed for a single request.
http://www.krautcomputing.com/blog/2012/03/27/how-to-compile-custom-sass-stylesheets-dynamically-during-runtime/
After a long day I was able to solve my problem thanks to John Feminella and his post on google. The challenging part for me was figuring out how to create a new Sprockets::Context. Luckily John's solution doesn't require a Context.
Updated gist here
Attempt #1
This code does not allow importing css files from the asset pipeline.
#import "foundation"; works because foundation is loaded as a compass module
#import "custom_css"; results in an error message saying the file could not be found
def call(template)
erb = ActionView::Template.registered_template_handler(:erb).call(template)
%{
options = Compass.configuration.to_sass_engine_options.merge(
:syntax => :scss,
:custom => {:resolver => ::Sass::Rails::Resolver.new(CompassRails.context)},
)
Sass::Engine.new((begin;#{erb};end), options).render
}
end
Attempt #2
This code fails to embed base64 urls using asset-data-url
def call(template)
erb = ActionView::Template.registered_template_handler(:erb).call(template)
%{
compiler = Compass::Compiler.new *Compass.configuration.to_compiler_arguments
options = compiler.options.merge({
:syntax => :scss,
:custom => {:resolver => ::Sass::Rails::Resolver.new(CompassRails.context)},
})
Sass::Engine.new((begin;#{erb};end), options).render
}
end
Attempt 3
Turns out you can use empty values while creating the context. Code below works in development but throws an error in production.
ActionView::Template::Error (can't modify immutable index)
It appears the error occurs in Sprockets::Index which is used instead of Sprockets::Environment in production. Switching to Sprockets::Environment doesn't solve the problem either.
def call(template)
erb = ActionView::Template.registered_template_handler(:erb).call(template)
%{
context = CompassRails.context.new(::Rails.application.assets, '', Pathname.new(''))
resolver = ::Sass::Rails::Resolver.new(context)
compiler = Compass::Compiler.new *Compass.configuration.to_compiler_arguments
options = compiler.options.merge({
:syntax => :scss,
:custom => {:resolver => resolver}
})
Sass::Engine.new((begin;#{erb};end), options).render
}
end

Cannot send xlsx as attachment in Rails mailer

I am trying to send an email containing a file attachment created by the user.
attachments[document.display_name] = File.read(document.public_filename)
This works in most conditions (including .docx, but fails for .xlsx files with the error:
invalid byte sequence in UTF-8
I am using attachment_fu to upload the attachments, and delayed_job to defer sending the emails, however the file I am trying to attach looks ok, and I can open it successfully outside the application.
I also saw a suggestion to change the code as follows, but it does not seem to help:
include an extra.
attachments[document.display_name] = { :content => File.read(document.public_filename), :transfer_encoding => :binary }
How can I make the code work for all attachment types?
You need to specify the mode or encoding to let it read the file as binary:
attachments[document.display_name] = File.read(document.public_filename, :mode => 'rb')
or
attachments[document.display_name] = File.read(document.public_filename, :encoding => 'BINARY')

Detect MIME type of uploaded file in Ruby

Is there a bullet proof way to detect MIME type of uploaded file in Ruby or Ruby on Rails? I'm uploading JPEGs and PNGs using SWFupload and content_type is always "application/octet-stream"
The ruby-filemagic gem will do it:
require 'filemagic'
puts FileMagic.new(FileMagic::MAGIC_MIME).file(__FILE__)
# => text/x-ruby; charset=us-ascii
This gem does not look at the file extension at all. It reads a bit of the file contents and uses that to guess the file's type.
In Ruby on Rails you can do:
MIME::Types.type_for("filename.gif").first.content_type # => "image/gif"
You can use this reliable method base on the magic header of the file :
def get_image_extension(local_file_path)
png = Regexp.new("\x89PNG".force_encoding("binary"))
jpg = Regexp.new("\xff\xd8\xff\xe0\x00\x10JFIF".force_encoding("binary"))
jpg2 = Regexp.new("\xff\xd8\xff\xe1(.*){2}Exif".force_encoding("binary"))
case IO.read(local_file_path, 10)
when /^GIF8/
'gif'
when /^#{png}/
'png'
when /^#{jpg}/
'jpg'
when /^#{jpg2}/
'jpg'
else
mime_type = `file #{local_file_path} --mime-type`.gsub("\n", '') # Works on linux and mac
raise UnprocessableEntity, "unknown file type" if !mime_type
mime_type.split(':')[1].split('/')[1].gsub('x-', '').gsub(/jpeg/, 'jpg').gsub(/text/, 'txt').gsub(/x-/, '')
end
end
The ruby-filemagic gem is good solution, but requires additional dependencies on libmagic (recently removed from CarrierWave as part of CarrierWave::MagicMimeTypes removal).
If you're interested in a pure ruby implementation, consider the MimeMagic gem! It works well for file types listed in the freedesktop.org mime database:
require 'mimemagic'
MimeMagic.by_magic(File.open('Table-Flip-Guy.jpg')).type # => "image/jpeg"
For Microsoft Office 2007+ formats (xlsx, docx, and pptx), require the overlay (unless you're okay with the generic "application/zip" MIME type for these files)
require 'mimemagic'
require 'mimemagic/overlay'
MimeMagic.by_magic(File.open('big_spreadsheet.xlsx')).type # => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
filemagic gem is good solution but depends lots of unnecessary gems. (rails, aws-sdk-core, ...)
If your app is small and only runs in Linux or OSX, consider to use file program:
require 'shellwords'
mimetype = `file --brief --mime-type - < #{Shellwords.shellescape(__FILE__)}`.strip
Note: Replace __FILE__ with any expr contains the filepath.
As of 2021, I would claim that the best tool to compute mime types based on all the available hints (magic number, file name when the magic number does not suffice, user hints) is Marcel.
To shamelessly quote the documentation itself:
Marcel::MimeType.for Pathname.new("example.gif")
# => "image/gif"
File.open "example.gif" do |file|
Marcel::MimeType.for file
end
# => "image/gif"
Marcel::MimeType.for Pathname.new("unrecognisable-data"), name: "example.pdf"
# => "application/pdf"
Marcel::MimeType.for extension: ".pdf"
# => "application/pdf"
Marcel::MimeType.for Pathname.new("unrecognisable-data"), name: "example", declared_type: "image/png"
# => "image/png"
Marcel::MimeType.for StringIO.new(File.read "unrecognisable-data")
# => "application/octet-stream"
mimemagic gem will also do it
https://github.com/minad/mimemagic
from the oficial documentation
MimeMagic is a library to detect the mime type of a file by extension
or by content. It uses the mime database provided by freedesktop.org
(see http://freedesktop.org/wiki/Software/shared-mime-info/).
require 'mimemagic'
MimeMagic.by_extension('html').text?
MimeMagic.by_extension('.html').child_of? 'text/plain'
MimeMagic.by_path('filename.txt')
MimeMagic.by_magic(File.open('test.html'))
# etc...
in case you are doing this from scratch, install mimemagic gem
gem 'mimemagic'
open stream(bytes of target image)
url="https://i.ebayimg.com/images/g/rbIAAOSwojpgyQz1/s-l500.jpg"
result = URI.parse(url).open
then check data-stream's file type for example:
MimeMagic.by_magic(result).type == "image/jpeg"
even though as mentioned above
%w(JPEG GIF TIFF PNG).include?(MimeMagic.by_magic(result).type)
this might be more elegant
You can use
Mime::Type.lookup_by_extension(extention_name)
Thanks

FileTest.exists? issue with ruby on rails

I am trying to check if a file exists in my rails application.
I am running ruby 1.8.6 and rails 2.1.2 with windows XP.
So, the problem is that the FileTest.exists? method doesn't seem to be working. I have simplified the code to this point :
if FileTest.exists?("/images/header.jpg")
render :text => "yes"
else
render :text => "no <img src='/images/header.jpg' />"
end
If I do that the system displays "no" and then includes the image that displays correctly because /images/header.jpg exists.
I tried FileTest.exists?, FileTest.exist?, File.exists?, File.exist? and nothing seems to be working.
What am I doing wrong ?
Thanks
I'm guessing it's because you're asking whether a file "header.jpg" exists in a directory "images" off of the root directory for your system (which on Windows I'd assume is "c:\"). Try putting the full path (from the filesystem root) to the "/images" directory rather than the URL path.
In particular, as pointed out by #Brian, you should use:
FileTest.exists?(RAILS_ROOT + "/images/header.jpg") # < rails 3.0
FileTest.exists?(Rails.root + "/images/header.jpg") # >= rails 3.0
Add RAILS_ROOT to the filename that you're checking before calling exists?

Resources