Rails 3.1 asset pipeline with PDFKit - ruby-on-rails

I am using PDFkit with rails 3.1. In the past I was able to use the render_to_string function and create a pdf from that string. I then add the stylesheets as follows. My issue is that I have no idea how to access them from within the asset pipeline. (This is how I did it in rails 3.0)
html_string = render_to_string(:template => "/faxes/show.html.erb", :layout => 'trade_request')
kit = PDFKit.new(html_string, :page_size => 'Letter')
kit.stylesheets << "#{Rails.root.to_s}/public/stylesheets/trade_request.css"
So my question in how do i get direct access from my controller to my css file through the asset pipline?
I know I can use the Rack Middleware with PDFkit to render the pdf to the browser, but in this case i need to send the pdf off to a third party fax service.
Thanks for your help.
Ryan

Just ran into this issue as well, and I got past it without using the asset pipeline, but accessing the file directly as I would have previously in /public. Don't know what the possible cons are to using this approach.
I guess LESS and SCSS files won't be processed as they would have if accessed through the asset pipeline.
html = render_to_string(:layout => false , :action => 'documents/invoice.html.haml')
kit = PDFKit.new(html, :encoding=>"UTF-8")
kit.stylesheets << "#{Rails.root.to_s}/app/assets/stylesheets/pdf_styles.css"
send_data(kit.to_pdf, :filename => "test_invoice", :type => 'application/pdf')

A little late, but better late than never, eh.
I'd do it like this:
found_asset = Rails.application.assets.find_asset( "trade_request.css" ).digest_path
kit.stylesheets << File.join( Rails.root, "public", "assets", found_asset )

In Rails 3.1.1 stylesheets are written to /public/assets with and without the digest fingerprint.
This means that you should be able to reference these files just by changing the path in your code.
One gotcha though: if the PDF sheet is not referenced in a CSS manifest you'll have to add it to the precompile config:
config.assets.precompile += ['trade_request.css']
This tells sprockets to compile that file on its own.
As a (better) alternative, see if the asset_path helper works in your code. This will reference the correct file in dev and production.

I ended up copying the css file to my public directory and referring to it in the same way i did before with rails 3. For more information check out this question: Access stylesheet_link_tag from controller

I landed here trying to solve this problem and none of the answers seemed to solve the issue for me. I found the accepted answer of this stack overflow post worked for me:
How does one reference compiled assets from the controller in Rails 3.1?
I was even able to serve .css.erb files through this method.

Try
= stylesheet_link_tag "application", 'data-turbolinks-track': 'reload'

You should be able to access the stylesheet using this method:
ActionController::Base.helpers.asset_path("trade_request.css")
Making your code:
html_string = render_to_string(:template => "/faxes/show.html.erb", :layout => 'trade_request')
kit = PDFKit.new(html_string, :page_size => 'Letter')
kit.stylesheets = ActionController::Base.helpers.asset_path("trade_request.css")

Related

wickedpdf not rendering images?

i am using wickedpdf gem to generate pdf invoice from the html code.
gems:
gem 'wicked_pdf'
gem "wkhtmltopdf-binary"
gemfile.lock
wicked_pdf (1.0.6)
wkhtmltopdf-binary (0.9.9.3)
in controller:
def show_pdf_invoice
respond_to do |format|
format.html { render :layout => "pdf.pdf.erb" }
format.pdf do
render pdf: "show_pdf_invoice", :layout => 'pdf.pdf.erb'
#render :pdf => "pdf"#, :layout => 'pdf.html.erb'
end
end
end
in views/invoices/show_pdf_invoice.pdf.erb
<img id="image" src="https://www.google.co.in/logos/doodles/2016/holidays-2016-day-2-6356741311692800-scta.png" alt="logo" />
<%= wicked_pdf_image_tag 'https://www.google.co.in/logos/doodles/2016/holidays-2016-day-2-6356741311692800-scta.png' %>
pdf is getting generated. But the images are not showing. in the place of images empty boxes are coming. unable to find the issue.
I've had the same problem, mine was fixed by removing https for http. Have you tried this? and for the Amazon S3 part: You could use gsub for that as in: gsub("https", "http")
Using Rails 5.2 with Active Storage in combination with Amazon S3 storage I had the same problem.
In development on my local machine the images rendered perfectly, but on Heroku
they were presented as small empty rectangles.
To get the url from the logo uploaded to Active Storage I used: #my_object.logo.service_url.
Which used the standard url with https. As mentioned before, replacing this with http resolved the issue.
Full code used in my pdf generator view:
<%= wicked_pdf_image_tag #my_object.logo.service_url.gsub("https", "http") %>
Two Options
1. Upgrade to wkhtmltopdf 0.12.5.
-or-
2. Install libssl1.0-dev with apt-get install libssl1.0-dev.
See this issue for more information: https://github.com/wkhtmltopdf/wkhtmltopdf/issues/3001

Rails - How to pass Sprockets::Context in manual sass compiling

I'm using the following code snippet to manually compile a sass manifest with some variable overrides appended.
template = File.read("#{Rails.root}/app/assets/schemes/#{scheme}/css/styles.css.scss")
scheme_variables.each do |key, value|
template << "$#{key}:#{value};\n"
end
engine = Sass::Engine.new(template, {
:syntax => :scss,
:cache => false,
:read_cache => false,
:style => :compressed,
:filesystem_importer => Sass::Rails::SassImporter,
:load_paths => MyApp::Application.assets.paths,
:sprockets => {
:context => ?,
:environment => MyApp::Application.assets
}
})
output = engine.render
The Sass::Engine constructor wants a sprockets context and environment in the options hash. What do I put in for the context? The first thing I tried was...
:context => MyApp::Application.assets.context_class,
...but that gives me the following error "undefined method `font_path' for #" when it hits one of my sass asset helpers.
The second thing I tried was...
:context => ActionController::Base.helpers,
...That fixed the asset helper issue, but throws the following error "undefined method `depend_on' for #" when it tries to work through my glob imports (e.g. #import "mixins/*").
I'm using Rails 4.2 and sass-rails 5.0.3.
Any advice on this would be much appreciated. Thanks!
With using Sass::Rails::ScssTemplate you can render your sass code with this snippet:
template = '...' # Your sass code
logical_path = pathname = ''
environment = Rails.application.assets
context = environment.context_class.new(environment, logical_path, pathname)
template = Sass::Rails::ScssTemplate.new(pathname) { template }
output = template.render(context, {})
If you want to render from a file then just add its path to pathname and its asset path to logical_path.
For me it works with Rails 4.2.5.1 and sass-rails 5.0.4.
I ended up solving this in a slightly different way - using Sass::Rails::ScssTemplate's render method. Basically, I write my altered css string out to a file and pass it into the Sass::Rails::ScssTemplate constructor. I then compile and remove the temp file when it's done. This doesn't feel great, but it's working well for me.
scheme_css_dir = "#{Rails.root}/app/assets/schemes/#{scheme}/css"
css = File.read("#{scheme_css_dir}/styles.css.scss")
variables_str = ''
scheme_variables.each do |key, value|
variables_str << "$#{key}:#{value};\n"
end
css.gsub!('#import "./variables";', variables_str)
file = Tempfile.new(['styles', '.css.scss'], scheme_css_dir)
file.write(css)
file.close
abs_path = file.path
relative_path = abs_path[Rails.root.to_s.size + 1..-1]
template = Sass::Rails::ScssTemplate.new(abs_path)
environment = Evrconnect::Application.assets
context = environment.context_class.new(
:environment => environment,
:name => relative_path,
:filename => abs_path,
:metadata => {}
)
output = template.render(context)
file.unlink
To answer your original question, you need to supply either the current view_context, or create one with ActionView::Base.new.
http://apidock.com/rails/AbstractController/Rendering/view_context
In Rails 5.x, Sprockets v3.7.x
NOTE: Following method requires: Rails.application.config.assets.compile == true, i.e., set config.assets.compile = true. Check Sprockets README
I manually returned compiled source for sass/js, using the following code.
# For Scss Assets
def pdf_stylesheet_link_tag(name)
if Rails.env.development?
asset = Rails.application.assets.find_asset(name + '.scss')
raw ('<style type="text/css">' + asset.source + '</style>')
else
raw ('<style type="text/css">' + pdf_asset_contents(name, '.css') + '</style>')
end
end
# For Javascript Assets
def pdf_javascript_include_tag(name, *type)
if Rails.env.development?
if debug? # Can be used to check `debug == true` in params for current route
javascript_include_tag(name)
else
asset = Rails.application.assets.find_asset(name + '.js')
raw ('<script>' + asset.source + '</script>')
end
else
javascript_tag pdf_asset_contents(name, '.js')
end
end
Above code actually helps to dynamically pass compiled version of sass/js
to Wicked_PDF, which actually helps to load styles and js on Generated PDFs, for Dev Environments.
pdf_stylesheet_link_tag can be used as a helper for templates, in development/stage (where, config.assets.precompile == false), instead of using wicked_pdf_stylesheet_link_tag(which actually requires a path to precompiled source-file).
After overnight trial and errors, I found and want to share todays's way of doing it ("today" meaning: rails 5.2.1 and sprockets 3.7.2).
Work as expected: no need of a temp file, accept #import, allow asset path helpers.
# Compile SASS and return the resulting string
# Pass the file path and name without 'sass' extension, relative to assets/stylesheets
def compile_sass(stylesheet)
# Load file content
path = Rails.root.join 'app', 'assets', 'stylesheets', "#{stylesheet}.sass"
sass = File.read path
# Configure engine
environment = Sprockets::Railtie.build_environment Rails.application
scope = environment.context_class.new environment: environment, \
filename: "/", metadata: {}
scope.sass_config.merge! cache: false, style: :compressed
# Compile
engine = Sass::Rails::SassTemplate.new {sass}
engine.render scope
end
Other sass_config options can be found here : https://github.com/sass/ruby-sass/blob/stable/doc-src/SASS_REFERENCE.md#options
The following works with rails 5.2.0, sprockets 3.7.2, sassc-rails 1.3.0 and sassc 1.12.1:
template = '...' # Your sass code
environment = Sprockets::Railtie.build_environment(Rails.application)
engine = SassC::Rails::SassTemplate.new
engine.call(environment: environment,
filename: '/',
data: template,
metadata: {})[:data]

Rails 4, asset pipeline causes user downloadable files to be downloaded twice

I have a folder in my app directory named "uploads" where users can upload files and download files. I don't want the uploads folder to be in the public directory because I want to control download authorization.
In my controller, I have:
send_file Rails.root.join('app', 'uploads', filename), :type => 'application/zip', :disposition => 'inline', :x_sendfile=>true
This actually works fine. The problem is that when I'm on the production server, when I run the rake assets:precompile, and have an assets directory, the file downloads twice. The first time the file downloads, the browser acts as if nothing is going on (no loading spinning), but I see data being transferred in the Google Chrome web developer Network tab. Then after the file has been downloaded, a prompt comes up asking the user if he/she wants to download the file.
Removing the assets folder in the public directory gets rid of this problem, but I want to use the asset pipeline. I also tried changing the asset pipeline requires from require_tree to require_directory.
Does anyone know how to get send_file working properly with the asset pipeline?
Thanks.
For anyone having this problem, I solved it. Pass
'data-no-turbolink' => true
into the link_to helper to stop Turbolinks from messing with the download.
https://github.com/rails/turbolinks/issues/182
But if you are using a form with turbooboost = true, instead of link_to, or even with a link_to you can do it like this:
Inside your controller, and inside your action put:
def download
respond_to do |format|
format.html do
data = "Hello World!"
filename = "Your_filename.docx"
send_data(data, type: 'application/docx', filename: filename)
end
format.js { render js: "window.location.href = '#{controller_download_path(params)}';" }
end
end
Replace controller_download_path with a path to your download action,
and place in your routes both post and get for the same path:
post '/download' => 'your_controller#download', as: :controller_download
get '/download' => 'your_controller#download', as: :controller_download

How to create pdf file with layout applied to it. Using pdfkit gem

require 'pdfkit'
html = render_to_string(:layout => 'layouts/test_layout' , :action => print_form.html.erb")
kit = PDFKit.new(html)
send_data(kit.to_pdf, :filename => "Form.pdf", :type => 'application/pdf')
above code generates PDF file without layout specified. How do I create PDF file with layout applied.
layouts/test_layout -> test.css, test.js
My test_layout contains some JavaScript and CSS files.
Please suggest me, I want to print form in PDF format with specified layout.
Without seeing your render_to_string method we can't tell what's going wrong. Here is example code for generating a pdf with layout: https://github.com/pdfkit/pdfkit#usage

Why Ajax renders 2 views in rails 3.1 app

When entering log in our rails 3.1 app, ajax is used to render the input screen below. Here is the link for log:
<%= link_to 'Log', new_part_out_log_path(#part, :format => :js), :remote => true, :id => 'new_log_link' %>
And the new.js.erb as this:
$("<%= escape_javascript render(:file => 'out_logs/new.html.erb') %>").insertAfter('#new_log_link');
$('#new_log_link').hide();
$('#close').hide();
the problem is that after clicking 'Log', instead of one view, 2 identical views of out_logs/new.html.erb were rendered. What may be wrong with our code? thank so much.
The problem is related to upgrade to ruby 1.9.3 which causes webrick server (for development) warning message and also rendering the .js.erb file twice on screen. The twice rendering problem disappears on our production server which is running nginx. The following link may help to understand the problem:
http://theresa.multimediatechnology.at/webrick-warnings-in-ruby-1-9-3/
What does "WARN Could not determine content-length of response body." mean and how to I get rid of it?

Resources