Print out via lpr does not work with embedded Open Sans font
I'm using Open Sans as embedded font in my documents. When I send them to a printer by lpr the text gets corrupted. I think it's an encoding problem. A document using Open Sans created with TCPDF under PHP prints out via lpr without problems.
I can open the prawn created document in and print out from Evince without any problems. But not with lpr. The result is garbage. When I remove the Open Sans fonts and use the default font, the document gets printed out correct through lpr.
Any ideas what I can do to change this behavior?
Hi Michael,
Does TCPDF perform subsetting as well?
Could you please generate a simple PDF that demonstrates the issue? Also could you please generate the same document with TCPDF for comparison?
I have to change the document creation due to private data that I can not publish here on github. This takes some time. In advance I can give you this
Prawn

TCPDF

Here a quick and dirty TCPDF document Rechnung_10927.pdf
And the one created with Prawn Invoice_10002.pdf
Anything new on this? Do you need more informations?
@tekkla How did you manage to get prawn to use OpenSans. I am doing following and unable to use font.
class DocumentPdf < Prawn::Document
def initialize(document, view)
font_families.update(
"OpenSans" => { normal: "#{Rails.root.join("app/assets/fonts/OpenSans-Regular.ttf")}" })
font 'OpenSans'
end
end
I keep getting the following error -
NoMethodError : undefined method `pages' for nil:NilClass
C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/prawn-2.2.2/lib/prawn/font.rb:53:in `font'
Can you please help me to know what am I doing wrong here?
@shah-disha I have no idea. Sorry.
Okay, as far as I can say for now it's not a problem with embedded OpenSans. It's a general problem with each embedded font. I tried different fonts and all documents can't be printed directly via CUPS. And I think I know the reason why.
As far as I understood converts CUPS all Files (incl. PDF) to Postscript and then back to PDF before sending it to the printer. This conversion process seems to have problems with the PDF created from prawn. With pdf2ps (Ghostscript and default converter in CUPS) you can reproduce this, while pdftops converts the PDF correctly into a ps file.
OK, at this point it looks more like an issue with pdf2ps rather than Prawn. However, I'm curious what part of a generated document may result in an incompatibility. Could you please provide a sample code, PDF and PS files produced with each tool in case someone would like to take a stab at this issue?
Here you have a fresh prawn generate pdf file and the postscript files in a zip archive.
This is my code
class InvoicePdf
include Prawn::View
def initialize(invoice, view)
@invoice = invoice
# for debug and development always on
@invoice.heading = 1
@invoice.net_invoice = 1
@view = view
@number_and_date_size = 14
@border_width = 0.1
@price_key = @invoice.net_invoice ? 'net' : 'gross'
@cols = {
'position' => {
'width' => 10,
'align' => :left
},
'title' => {
'align' => :left
},
'amount_with_unit' => {
'width' => 40,
'align' => :center
},
"#{@price_key}" => {
'width' => 60,
'align' => :right,
'currency' => true
},
"#{@price_key}_sum" => {
'width' => 60,
'align' => :right,
'currency' => true
}
}
@font_small = 8
@font_normal = 10
font_name = 'OpenSans'
font_path = Rails.root.join("app/assets/fonts/#{font_name}")
font_families.update(
font_name.to_s => {
normal: "#{font_path}-Regular.ttf",
bold: "#{font_path}-Bold.ttf",
extra_bold: "#{font_path}-ExtraBold.ttf"
}
)
font(font_name) do
repeat :all do
header
footer
fold_marks 570
end
content
page_counter
end
end
def document
@my_prawn_doc ||= Prawn::Document.new(
page_size: 'A4',
info: {
Title: "#{I18n.t('invoices.title.one')} #{@invoice.number}",
CreationDate: Time.now
},
left_margin: 50,
right_margin: 25,
bottom_margin: 5,
compress: true
)
end
private
def header
company
address_label
number_and_date
end
def footer
bounding_box [bounds.left, bounds.bottom + 45], width: bounds.width do
col_width = bounds.width / 5
font_size @font_small do
bounding_box([0, 0], width: col_width) do
text 'Somewhere'
text 'Uhlenkamp 33'
text '24119 Kolsten'
end
bounding_box([col_width, bounds.top], width: col_width / 5) do
text 'Tel'
text 'Fax'
text 'Mail'
text 'Web'
end
bounding_box([col_width + col_width / 5, bounds.top], width: col_width * 1.5) do
text '0431 521578'
text '0431 521579'
text '[email protected]'
text 'www.somewhere.de'
end
bounding_box([col_width * 2.4, bounds.top], width: col_width * 1.25) do
text 'Bankverbindung (IBAN)'
text 'DE27 21 15 11 79 412 5190 24'
text 'Förde Sparkasse'
text 'BIC NOLADE21 KIE'
end
bounding_box([col_width * 3.75, bounds.top], width: col_width) do
text 'USt-Id. Nr. DE174563218'
text 'Handelsregister HR A 4567'
end
end
end
end
def company
# COMPANY AND IMAGE
bounding_box [bounds.left, bounds.top], width: bounds.width do
image "#{Rails.root}/app/assets/images/logo.png",
width: 170,
position: :right
move_up 80
text 'SOMEWHERE', size: 33.5, style: :bold
move_up 3
text 'And Everywhere', size: 16
end
end
def address_label
bounding_box [bounds.left + 20, 710], width: 195 do
text 'Somewhere - Uhlenkamp 33 - 24119 Kolsten',
size: @font_small,
align: :center
my_horizontal_rule
bounding_box [5, cursor - 5], width: 185 do
text @invoice.addresslabel, size: @font_normal
end
end
end
def number_and_date
bounding_box [bounds.left, 555], width: bounds.width do
text "#{I18n.t('invoices.title.one')}: #{@invoice.number}",
size: @number_and_date_size,
style: :bold
move_up @number_and_date_size
text @invoice.date_of.to_s,
size: @number_and_date_size,
align: :right,
style: :bold
end
end
def content
bounding_box([bounds.left, 520], width: bounds.width, height: 460) do
title_and_description
line_items
terms
end
end
def title_and_description
return if @invoice.heading.blank?
font_size @font_normal do
if @invoice.title.present?
text @invoice.title, style: :bold
move_down 5
end
if @invoice.description.present?
text @invoice.description
move_down 10
end
end
end
def line_items
pad 5 do
text I18n.t('invoices.pdf.line_items').upcase, style: :bold
end
table line_item_rows,
width: bounds.width,
cell_style: {
inline_format: true,
borders: [:top],
border_widths: @border_width,
size: @font_small
} do
row(0).font_style = :bold
columns(3..4).align = :right
self.header = true
end
send("summary_#{@price_key}")
end
def summary_gross
string = "#{I18n.t('invoices.pdf.taxes_included')}: "
@invoice.taxes.each do |taxes|
string += "#{percent(taxes.tax_rate)}= #{price(taxes.tax_sum)}"
end
string += "\n#{I18n.t('invoices.pdf.work_and_drivecost_sum', value: price(@invoice.work_and_drivecost_sum))}"
summary = [
[
{
content: string,
size: @font_small
},
{
content: I18n.t("invoices.pdf.total"),
align: :right,
width: 80,
font_style: :bold
},
{
content: price(@invoice.send(@price_key)),
width: 140,
align: :right,
font_style: :bold
}
]
]
table summary,
width: bounds.width,
cell_style: {
inline_format: true,
borders: [:top],
border_widths: @border_width,
padding_top: 0,
valign: :center
} do
end
# double line under total
my_horizontal_rule
move_down 1
my_horizontal_rule
end
def summary_net
my_horizontal_rule
# start summary with net amount
summary = [
[
I18n.t('invoices.pdf.net_amount'),
price(@invoice.net)
]
]
# add taxes and values
@invoice.taxes.each do |taxes|
summary << [
"+ #{I18n.t('invoices.pdf.tax')} #{percent(taxes.tax_rate)}",
price(taxes.tax_sum)
]
end
## and finally the total value
summary << [
I18n.t('invoices.pdf.total'),
price(@invoice.gross)
]
table summary,
width: bounds.width,
cell_style: {
size: @font_small,
align: :right,
borders: [:bottom],
border_widths: @border_width,
font_style: :bold
} do
columns(1).width = 110
rows(-1).size = @font_normal
end
# double line under total
move_down 1
my_horizontal_rule
end
def terms
unless @invoice.terms.blank?
move_down 5
text I18n.t('invoices.pdf.col.terms')
text I18n.t(@invoice.terms, days: @invoice.days), style: :bold
end
end
def line_item_rows
table = []
th = []
@cols.each do |key, col|
th << { content: I18n.t("invoices.pdf.col.#{key}"), width: col['width'] }
end
p th
table << th
@invoice.items.each do |item|
tr = []
@cols.each do |key, col|
value = item.send(key).to_s
value = price(value) unless col['currency'].blank?
value = "<b>#{value}</b>"
if key == 'title'
value += "\n#{item.description}" unless item.description.blank?
end
tr << value
end
p tr
table << tr
end
table
end
def page_counter
number_pages I18n.t('invoices.pdf.pages'),
at: [bounds.left, 540],
width: bounds.width - 1,
start_count_at: 1,
style: :bold,
align: :right,
size: 8
end
def price(num)
@view.number_to_currency num,
unit: '€',
separator: ',',
delimiter: '.',
format: '%n %u'
end
def percent(num)
@view.number_to_percentage num,
precision: 2,
separator: ',',
delimiter: '.'
end
def my_horizontal_rule(line_width = 0.1)
stroke do
line_width line_width
horizontal_rule
end
end
def fold_marks(y_position, line_width = 0.1)
stroke do
line_width line_width
stroke_color "aaaaaa"
horizontal_line bounds.left - 20, bounds.left - 15, at: y_position
horizontal_line bounds.right - 5, bounds.right, at: y_position
end
end
end
@tekkla It's been a while but still… Could you please check if you're having this issue with Prawn/ttfunk/pdf-core from master?