Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Base64 encode fonts and images for PDF rendering #1363

Merged
merged 4 commits into from
Jun 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/assets/stylesheets/pdf/_fontawesome.scss.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Encoded FontAwesome ttf
*/

@font-face {
font-family: 'FontAwesome';
src: url(<%= asset_data_uri("font-awesome/fontawesome-webfont.ttf") %>);
font-weight: normal;
font-style: normal;
}
10 changes: 10 additions & 0 deletions app/assets/stylesheets/pdf/_patternfly.scss.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Encoded PatternFly ttf
*/

@font-face {
font-family: "PatternFlyIcons-webfont";
src: url(<%= asset_data_uri("patternfly/PatternFlyIcons-webfont.ttf") %>);
font-weight: normal;
font-style: normal;
}
25 changes: 25 additions & 0 deletions app/assets/stylesheets/pdf/_report.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
body {font: 13px/1.231 Arial,Helvetica,sans-serif;}

table { border-spacing:0px;border-left: 1px solid #d1d1d1;border-collapse:separate; color: #4d5258;}
table thead tr th {height: 19px;margin: 0;padding: 4px;padding-left:10px;border-right: 1px solid #d1d1d1;border-left: 0px !important;border-top: 1px solid #d1d1d1;border-bottom: 1px #d1d1d1 solid;font: normal 12px Arial,Helvetica,sans-serif !important;line-height: 19px;}
table thead tr th {background: #f5f5f5}
table tbody tr.row1-nocursor {background: #f5f5f5}
table tbody tr td {border: 1px solid transparent;border-right: 1px solid #d1d1d1;border-bottom-color: #e7e7e7;padding: 2px 2px 2px 10px;margin: 0;vertical-align: middle;cursor: default;color: #4d5258;}
table tbody tr td.group { border-top: 1px solid #fff; background: #ececec; font: normal 12px Arial,Helvetica,sans-serif;}

table tbody tr td.miq_rpt_red_bg, table.adminlist tbody tr td.miq_rpt_red_bg, .miq_rpt_red_bg {background: #fbdbdb !important;}
table tbody tr td.miq_rpt_yellow_bg, table.adminlist tbody tr td.miq_rpt_yellow_bg, .miq_rpt_yellow_bg {background-color: #ffdc99 !important;}
table tbody tr td.miq_rpt_green_bg, table.adminlist tbody tr td.miq_rpt_green_bg, .miq_rpt_green_bg {background-color: #cef7c9 !important;}
table tbody tr td.miq_rpt_blue_bg, table.adminlist tbody tr td.miq_rpt_blue_bg, .miq_rpt_blue_bg {background-color: #76badf !important;}
table tbody tr td.miq_rpt_purple_bg, table.adminlist tbody tr td.miq_rpt_purple_bg, .miq_rpt_purple_bg {background-color: #c0acdc !important;}
table tbody tr td.miq_rpt_maroon_bg, table.adminlist tbody tr td.miq_rpt_maroon_bg, .miq_rpt_maroon_bg {background-color: #d4edfa !important;}
table tbody tr td.miq_rpt_gray_bg, table.adminlist tbody tr td.miq_rpt_gray_bg, .miq_rpt_gray_bg {background-color: #d1d1d1 !important;}

.miq_rpt_red_text {color: #cc0000; font-weight: bold}
.miq_rpt_yellow_text {color:#cccc00; font-weight: bold}
.miq_rpt_green_text {color: #3f9c35; font-weight: bold}
.miq_rpt_blue_text {color: #006e9c; font-weight: bold}
.miq_rpt_purple_text {color: #925bad; font-weight: bold}
.miq_rpt_maroon_text {color: #76badf; font-weight: bold}
.miq_rpt_gray_text {color: gray; font-weight: bold}
@page { prince-shrink-to-fit: auto }
48 changes: 48 additions & 0 deletions app/assets/stylesheets/pdf/_summary.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
body {font: 12px/1.231 Arial,Helvetica,sans-serif; padding-top: 100px;}

#xyz_main {
position: absolute;
top: 0px;
left: 50%;
height: 200px !important;
width: 100px;
margin-left: -55px;
text-align: left;
}

#quadicon {color: #e3e4e4;float:left; height: 72px; padding: 0px 1px 1px 0px;position: relative;text-transform: none;width: 72px;}
#quadicon img {border:0px;}

.flobj { position: absolute;z-index: auto;width: 72px;}
.flobj p { margin: 0 0 0 0;padding: 0;color: #e3e4e4;vertical-align: middle;text-align:center;text-transform: none;text-shadow: 0 0 3px #000;font: normal 16px OpenSansSemibold,Arial,Helvetica,sans-serif !important;}
.a72 { padding: 3px 0 0 5px; width: 28px; height: 28px;font-size: 1.3em;line-height: 1.8em;}
.b72 { padding: 3px 0 0 37px; width: 28px; height: 28px;font-size: 1.3em;line-height: 1.8em;}
.c72 { padding: 38px 0 0 5px; width: 31px; height: 31px;font-size: 1.3em;line-height: 1.7em;}
.d72 { padding: 38px 0 0 39px; width: 31px; height: 31px;font-size: 1.3em;line-height: 1.7em;}
.e72 { padding: 6px 0 0 6px; width: 35px; height: 35px;font-size: 1.3em;line-height: 2em;}
.f72 { padding: 22px 0 0 24px; width: 24px; height: 24px;}
.g72 { padding: 16px 0 0 19px; width: 24px; height: 24px;}
.b72 img {margin-top: -3px; width: 34px; height: 34px;}
.a72 img,.c72 img,.d72 img{ width: 28px; height: 28px;}
.f72 img,.f72 img { width: 24px; height: 24px;}
.g72 img,.g72 img { width: 36px; height: 36px;}
.e72 img { width: 60px; height: 60px;}

dl{float:left;width:100%;}
dl.col2 dd {float:left;width:47%;margin:0;margin-right:15px;padding:0;}

table {background-color: #ededed;border-spacing: 1px;margin-bottom: 15px;width: 100%;color: #4b4b4b;}
table tr th {height: 19px;padding: 4px;padding-left:10px;font: normal 12px Arial,Helvetica,sans-serif !important;line-height: 19px;}
table tr td.label{font: 12px Arial,Helvetica,sans-serif;width: 33%; text-align: right;cursor: default;}
table tr td {background: #fff; vertical-align: top; padding: 3px 6px 3px 6px}
table tr td img {height: 16px;width: 16px;float: left; padding-right: 3px; }
table tr.row0 td {background: #fff}
table tr.row1 td {background: #f5f5f5}

.b72 .stretch {
background-repeat: no-repeat;
width: 35px;
height: 32px;
margin: -3px 0 -2px 1px;
border-top-right-radius: 15px;
}
31 changes: 6 additions & 25 deletions app/assets/stylesheets/pdf_report.css
Original file line number Diff line number Diff line change
@@ -1,25 +1,6 @@
body {font: 13px/1.231 Arial,Helvetica,sans-serif;}

table { border-spacing:0px;border-left: 1px solid #d1d1d1;border-collapse:separate; color: #4d5258;}
table thead tr th {height: 19px;margin: 0;padding: 4px;padding-left:10px;border-right: 1px solid #d1d1d1;border-left: 0px !important;border-top: 1px solid #d1d1d1;border-bottom: 1px #d1d1d1 solid;font: normal 12px Arial,Helvetica,sans-serif !important;line-height: 19px;}
table thead tr th {background: #f5f5f5}
table tbody tr.row1-nocursor {background: #f5f5f5}
table tbody tr td {border: 1px solid transparent;border-right: 1px solid #d1d1d1;border-bottom-color: #e7e7e7;padding: 2px 2px 2px 10px;margin: 0;vertical-align: middle;cursor: default;color: #4d5258;}
table tbody tr td.group { border-top: 1px solid #fff; background: #ececec; font: normal 12px Arial,Helvetica,sans-serif;}

table tbody tr td.miq_rpt_red_bg, table.adminlist tbody tr td.miq_rpt_red_bg, .miq_rpt_red_bg {background: #fbdbdb !important;}
table tbody tr td.miq_rpt_yellow_bg, table.adminlist tbody tr td.miq_rpt_yellow_bg, .miq_rpt_yellow_bg {background-color: #ffdc99 !important;}
table tbody tr td.miq_rpt_green_bg, table.adminlist tbody tr td.miq_rpt_green_bg, .miq_rpt_green_bg {background-color: #cef7c9 !important;}
table tbody tr td.miq_rpt_blue_bg, table.adminlist tbody tr td.miq_rpt_blue_bg, .miq_rpt_blue_bg {background-color: #76badf !important;}
table tbody tr td.miq_rpt_purple_bg, table.adminlist tbody tr td.miq_rpt_purple_bg, .miq_rpt_purple_bg {background-color: #c0acdc !important;}
table tbody tr td.miq_rpt_maroon_bg, table.adminlist tbody tr td.miq_rpt_maroon_bg, .miq_rpt_maroon_bg {background-color: #d4edfa !important;}
table tbody tr td.miq_rpt_gray_bg, table.adminlist tbody tr td.miq_rpt_gray_bg, .miq_rpt_gray_bg {background-color: #d1d1d1 !important;}

.miq_rpt_red_text {color: #cc0000; font-weight: bold}
.miq_rpt_yellow_text {color:#cccc00; font-weight: bold}
.miq_rpt_green_text {color: #3f9c35; font-weight: bold}
.miq_rpt_blue_text {color: #006e9c; font-weight: bold}
.miq_rpt_purple_text {color: #925bad; font-weight: bold}
.miq_rpt_maroon_text {color: #76badf; font-weight: bold}
.miq_rpt_gray_text {color: gray; font-weight: bold}
@page { prince-shrink-to-fit: auto }
/*
* This sheet is used by PDFs generated with MiqReportResult
*= require pdf/_report
*= require pdf/_fontawesome
*= require pdf/_patternfly
*/
46 changes: 6 additions & 40 deletions app/assets/stylesheets/pdf_summary.css
Original file line number Diff line number Diff line change
@@ -1,40 +1,6 @@
body {font: 12px/1.231 Arial,Helvetica,sans-serif; padding-top: 100px;}

#xyz_main {
position: absolute;
top: 0px;
left: 50%;
height: 200px !important;
width: 100px;
margin-left: -55px;
text-align: left;
}

#quadicon {color: #e3e4e4;float:left; height: 72px; padding: 0px 1px 1px 0px;position: relative;text-transform: none;width: 72px;}
#quadicon img {border:0px;}

.flobj { position: absolute;z-index: auto;width: 72px;}
.flobj p { margin: 0 0 0 0;padding: 0;color: #e3e4e4;vertical-align: middle;text-align:center;text-transform: none;text-shadow: 0 0 3px #000;font: normal 16px OpenSansSemibold,Arial,Helvetica,sans-serif !important;}
.a72 { padding: 3px 0 0 5px; width: 28px; height: 28px;font-size: 1.3em;line-height: 1.8em;}
.b72 { padding: 3px 0 0 37px; width: 28px; height: 28px;font-size: 1.3em;line-height: 1.8em;}
.c72 { padding: 38px 0 0 5px; width: 31px; height: 31px;font-size: 1.3em;line-height: 1.7em;}
.d72 { padding: 38px 0 0 39px; width: 31px; height: 31px;font-size: 1.3em;line-height: 1.7em;}
.e72 { padding: 6px 0 0 6px; width: 35px; height: 35px;font-size: 1.3em;line-height: 2em;}
.f72 { padding: 22px 0 0 24px; width: 24px; height: 24px;}
.g72 { padding: 16px 0 0 19px; width: 24px; height: 24px;}
.b72 img {margin-top: -3px; width: 34px; height: 34px;}
.a72 img,.c72 img,.d72 img{ width: 28px; height: 28px;}
.f72 img,.f72 img { width: 24px; height: 24px;}
.g72 img,.g72 img { width: 36px; height: 36px;}
.e72 img { width: 60px; height: 60px;}

dl{float:left;width:100%;}
dl.col2 dd {float:left;width:47%;margin:0;margin-right:15px;padding:0;}

table {background-color: #ededed;border-spacing: 1px;margin-bottom: 15px;width: 100%;color: #4b4b4b;}
table tr th {height: 19px;padding: 4px;padding-left:10px;font: normal 12px Arial,Helvetica,sans-serif !important;line-height: 19px;}
table tr td.label{font: 12px Arial,Helvetica,sans-serif;width: 33%; text-align: right;cursor: default;}
table tr td {background: #fff; vertical-align: top; padding: 3px 6px 3px 6px}
table tr td img {height: 16px;width: 16px;float: left; padding-right: 3px; }
table tr.row0 td {background: #fff}
table tr.row1 td {background: #f5f5f5}
/*
* This sheet is used by PDFs generated from ApplicationController::ReportDownloads#set_summary_pdf_data
*= require pdf/_summary
*= require pdf/_fontawesome
*= require pdf/_patternfly
*/
4 changes: 4 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class ApplicationController < ActionController::Base
helper ToolbarHelper
helper JsHelper
helper QuadiconHelper
helper ImageEncodeHelper

helper CloudResourceQuotaHelper

Expand Down Expand Up @@ -157,6 +158,9 @@ def download_summary_pdf
@embedded = true
@showlinks = false

# encode images and embed in HTML that is sent to Prince
@base64_encode_images = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upstream code does not have Prince by default, though I think the only other choice is the NullPdf plugin. Even so, if/when we ever create a new PDF plugin, will this base64 encoding of images still be needed there? If not, should this be a property of the PDF plugin system?

Copy link
Member

@Fryguy Fryguy Jun 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, I don't really like this instance variable hanging out here. I think it might be better if the ImageEncodeHelper has an attr_accessor for this value (it already has the predicate getter), and then either the default is set in that getter on first access, or the value is set here through the setter self.base64_encode_images = true

This might allow for better testing as well, so you can just call a method instead of doing some nasty instance_variable_set in the test.


@record = identify_record(params[:id])
yield if block_given?
return if record_no_longer_exists?(@record)
Expand Down
31 changes: 31 additions & 0 deletions app/helpers/image_encode_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module ImageEncodeHelper
def encodable_image_tag(source, options = {})
image_tag(encodable_image_source(source), options)
end

def encodable_image_source(source)
if base64_encode_images? && source.present?
base64_encoded_uri(source)
else
path_to_image(source)
end
end

def base64_encode_images?
@base64_encode_images
end

def base64_encoded_uri(source)
asset = Rails.application.assets[source]

if asset.content_type == 'image/svg+xml'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually need a special SVG branch here?

Guess what, base64 encoded images are smaller :) - for our SVGs, the size sum is 1215 kB URL-encoded and 1189 kB base64 encoded.

So, simpler code and smaller images...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, Prince prefers the straight SVG sometimes.

Not encoded:
screen shot 2017-06-01 at 4 59 44 pm

Encoded
screen shot 2017-06-01 at 5 03 48 pm

Plus, it's not always better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prince prefers straight SVG sometimes..

Are you sure that's not a bug in the base64 branch of code? Unless prince's uri parser is signifcantly broken, this should not happen, so it feels like this may be exposing a bug there.

Are you sure there's not a working non-home-cooked way of converting files to data uris?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guess what, base64 encoded images are smaller :) - for our SVGs, the size sum is 1215 kB URL-encoded and 1189 kB base64 encoded.

Sounds like we need to run all of our images through an optimizer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more about base64 reliably taking 8/6ths of space, while URL-encoding a SVG tends to preserve most bytes, but for about a fifth, it makes them 3 times larger.

But really, my concern here is mostly about having a hand-rolled solution to encode URLs in 2 different ways. If we used something ..established, I don't care what the resulting format is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure there's not a working non-home-cooked way of converting files to data uris?

I googled for a while but did not find one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even URI::Data supports just parsing them. Oh well.

encoding = 'charset=utf-8'
data = ERB::Util.url_encode(asset.source).gsub('+', '%20')
else
encoding = 'base64'
data = Base64.strict_encode64(asset.source)
end

"data:#{asset.content_type};#{encoding},#{data}"
end
end
16 changes: 5 additions & 11 deletions app/helpers/quadicon_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -366,14 +366,8 @@ def quadicon_link_to(url, sparkle: false, remote: false, options: {}, &block)
#
def quadicon_reflection_img(options = {})
path = options.delete(:path) || "layout/reflection.png"

options = {
:border => 0,
:width => 72,
:height => 72,
}.merge(options)

image_tag(image_path(path), options)
options = { :border => 0, :size => 72 }.merge(options)
encodable_image_tag(path, options)
end

CLASSLY_NAMED_ITEMS = %w(
Expand Down Expand Up @@ -443,7 +437,7 @@ def flobj_img_simple(image = nil, cls = '', size = 72)
image ||= "layout/base-single.png"

content_tag(:div, :class => "flobj #{cls}") do
image_tag(image, :size => size)
encodable_image_tag(image, :size => size)
end
end

Expand Down Expand Up @@ -473,7 +467,7 @@ def render_service_quadicon(item, options)

output << content_tag(:div, :class => "flobj e72") do
quadicon_link_to(url, **link_opts) do
quadicon_reflection_img(:path => item.decorate.fileicon)
quadicon_reflection_img(options.merge!(:path => item.decorate.fileicon))
end
end

Expand Down Expand Up @@ -506,7 +500,7 @@ def render_resource_pool_quadicon(item, options)
def currentstate_icon(state)
path = "svg/currentstate-#{h(state)}.svg"
content_tag(:div, :class => "flobj b72") do
content_tag(:div, '', :class => "stretch", :style => "background-image: url('#{image_path(path)}')")
content_tag(:div, '', :class => "stretch", :style => "background-image: url('#{encodable_image_source(path)}')")
end
end

Expand Down
2 changes: 1 addition & 1 deletion app/views/layouts/show_pdf.html.haml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= render :partial => "layouts/pdf_styles"
- controller = %w(miq_template vm_infra vm_or_template vm).include?(controller_name) ? "vm_common" : controller_name
#xyz_main
#xyz_main.encode_images
= render_quadicon(@record, :mode => :icon, :size => 72)
- if @record.kind_of?(ConfigurationProfile)
= render :partial => "#{controller}/main_configuration_profile"
Expand Down
2 changes: 1 addition & 1 deletion app/views/shared/summary/_icon_or_image.html.haml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- if item[:icon]
%i{:class => item[:icon], :title => item[:title]}
- elsif item[:image]
= image_tag(image_path(item[:image]), :alt => item[:title], :title => item[:title])
= encodable_image_tag(item[:image], :alt => item[:title], :title => item[:title])
2 changes: 2 additions & 0 deletions spec/helpers/quadicon_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@
# FIXME: complex describe blocks mirror the existing complex control flow

describe QuadiconHelper do
helper ImageEncodeHelper

describe "#render_quadicon" do
context "when vm_or_template" do
subject { helper.render_quadicon(item) }
Expand Down
35 changes: 0 additions & 35 deletions spec/lib/pdf_generator_spec.rb

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
describe "layouts/listnav/_availability_zone.html.haml" do
helper(QuadiconHelper)
helper ImageEncodeHelper
helper QuadiconHelper

before :each do
set_controller_for_view("availability_zone")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
describe "layouts/listnav/_cloud_network.html.haml" do
helper(QuadiconHelper)
helper ImageEncodeHelper
helper QuadiconHelper

before :each do
set_controller_for_view("cloud_network")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
describe "layouts/listnav/_cloud_subnet.html.haml" do
helper(QuadiconHelper)
helper ImageEncodeHelper
helper QuadiconHelper

before :each do
set_controller_for_view("cloud_subnet")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
describe "layouts/listnav/_cloud_tenant.html.haml" do
helper(QuadiconHelper)
helper ImageEncodeHelper
helper QuadiconHelper

before :each do
set_controller_for_view("cloud_tenant")
Expand Down
3 changes: 2 additions & 1 deletion spec/views/layouts/listnav/_ems_cloud.html.haml_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
describe "layouts/listnav/_ems_cloud.html.haml" do
helper(QuadiconHelper)
helper ImageEncodeHelper
helper QuadiconHelper

before :each do
set_controller_for_view("ems_cloud")
Expand Down
3 changes: 2 additions & 1 deletion spec/views/layouts/listnav/_ems_cluster.html.haml_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
describe "layouts/listnav/_ems_cluster.html.haml" do
helper(QuadiconHelper)
helper ImageEncodeHelper
helper QuadiconHelper

before :each do
set_controller_for_view("ems_cluster")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
describe "layouts/listnav/_ems_container.html.haml" do
helper(QuadiconHelper)
helper ImageEncodeHelper
helper QuadiconHelper

before :each do
set_controller_for_view("ems_container")
Expand Down
3 changes: 2 additions & 1 deletion spec/views/layouts/listnav/_ems_network.html.haml_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
describe "layouts/listnav/_ems_network.html.haml" do
helper(QuadiconHelper)
helper ImageEncodeHelper
helper QuadiconHelper

before :each do
set_controller_for_view("ems_network")
Expand Down
Loading