Class: Pdfrb::Document::Fonts
- Inherits:
-
Object
- Object
- Pdfrb::Document::Fonts
- Defined in:
- lib/pdfrb/document/fonts.rb
Constant Summary collapse
- STANDARDS =
%w[ Helvetica Helvetica-Bold Helvetica-Oblique Helvetica-BoldOblique Times-Roman Times-Bold Times-Italic Times-BoldItalic Courier Courier-Bold Courier-Oblique Courier-BoldOblique Symbol ZapfDingbats ].freeze
- NO_ENCODING_FONTS =
%w[Symbol ZapfDingbats].freeze
- DEFAULT_WIDTH =
500
Instance Attribute Summary collapse
-
#document ⇒ Object
readonly
Returns the value of attribute document.
Class Method Summary collapse
- .build_tounicode(doc) ⇒ Object
- .build_tounicode_cmap ⇒ Object
- .loaders ⇒ Object
- .register_loader(loader) ⇒ Object
Instance Method Summary collapse
- #[](name) ⇒ Object
- #add(name_or_io) ⇒ Object
- #each ⇒ Object
- #embedded?(resource) ⇒ Boolean
- #encodable?(text, resource) ⇒ Boolean
- #encode_text(text, resource) ⇒ Object
- #encoding_for(resource) ⇒ Object
- #glyph_width(resource, codepoint) ⇒ Object
- #glyph_widths(resource, codepoints) ⇒ Object
-
#initialize(document) ⇒ Fonts
constructor
A new instance of Fonts.
- #measure_text(text, font:, size:) ⇒ Object
- #metrics_for(resource) ⇒ Object
-
#register_usage(font_dict, text) ⇒ Object
Record that
textwas drawn withfont_dictso the write-time subsetter knows which codepoints each embedded font must retain. - #subset_fonts! ⇒ Object
- #text_width(text, _resource, size:) ⇒ Object
- #used_codepoints(resource) ⇒ Object
- #valid_font_data?(data) ⇒ Boolean
Constructor Details
#initialize(document) ⇒ Fonts
Returns a new instance of Fonts.
20 21 22 23 24 25 26 27 28 29 |
# File 'lib/pdfrb/document/fonts.rb', line 20 def initialize(document) @document = document @next_id = 1 @registry = {} @encodings = {} @font_dicts = {} @afm_metrics = {} @used_codepoints = Hash.new { |h, k| h[k] = Set.new } @font_streams = {} end |
Instance Attribute Details
#document ⇒ Object (readonly)
Returns the value of attribute document.
18 19 20 |
# File 'lib/pdfrb/document/fonts.rb', line 18 def document @document end |
Class Method Details
.build_tounicode(doc) ⇒ Object
190 191 192 193 194 195 |
# File 'lib/pdfrb/document/fonts.rb', line 190 def self.build_tounicode(doc) cmap_body = build_tounicode_cmap stream = doc.add({ Length: cmap_body.bytesize }, type: Pdfrb::Model::Cos::Stream) stream.stream = cmap_body stream end |
.build_tounicode_cmap ⇒ Object
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/pdfrb/document/fonts.rb', line 197 def self.build_tounicode_cmap lines = [] lines << "/CIDInit /ProcSet findresource begin" lines << "12 dict begin" lines << "begincmap" lines << "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def" lines << "/CMapName /Adobe-Identity-UCS def" lines << "/CMapType 2 def" lines << "1 begincodespacerange" lines << "<00> <FF>" lines << "endcodespacerange" table = Pdfrb::Font::Encoding::WinAnsiEncoding::TABLE pairs = [] table.each_with_index { |cp, byte| pairs << format("<%02X> <%04X>", byte, cp) if cp } pairs.each_slice(100) do |chunk| lines << "#{chunk.length} beginbfchar" lines.concat(chunk) lines << "endbfchar" end lines << "endcmap" lines << "CMapName currentdict /CMap defineresource pop" lines << "end" lines << "end" "#{lines.join("\n")}\n" end |
.loaders ⇒ Object
164 |
# File 'lib/pdfrb/document/fonts.rb', line 164 def loaders; @loaders ||= []; end |
.register_loader(loader) ⇒ Object
165 |
# File 'lib/pdfrb/document/fonts.rb', line 165 def register_loader(loader); loaders.unshift(loader); end |
Instance Method Details
#[](name) ⇒ Object
54 |
# File 'lib/pdfrb/document/fonts.rb', line 54 def [](name); @registry[name]; end |
#add(name_or_io) ⇒ Object
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/pdfrb/document/fonts.rb', line 31 def add(name_or_io, **) name = font_name_for(name_or_io) cached = @registry[name] return cached if cached resource = next_resource_name font_dict = register_font(resource, name, **) @encodings[resource] = font_dict&.value&.[](:Encoding) @font_dicts[resource] = font_dict load_afm_metrics(resource, name) if @pending_io_data load_ttf_metrics(resource, @pending_io_data) unless @afm_metrics[resource] @font_streams[resource] = @pending_io_data @pending_io_data = nil end @pending_subtype = nil if @afm_metrics[resource] && font_dict&.value&.[](:Widths) font_dict.value[:Widths] = @afm_metrics[resource][:widths] end @registry[name] = resource resource end |
#each ⇒ Object
56 57 58 59 60 61 |
# File 'lib/pdfrb/document/fonts.rb', line 56 def each(&) return enum_for(:each) unless block_given? @registry.each(&) self end |
#embedded?(resource) ⇒ Boolean
137 138 139 140 141 142 143 144 145 146 |
# File 'lib/pdfrb/document/fonts.rb', line 137 def (resource) dict = @font_dicts[resource] return false unless dict desc = dict.value[:FontDescriptor] return false unless desc desc = document.object(desc) if desc.is_a?(Pdfrb::Model::Reference) desc&.value&.key?(:FontFile2) end |
#encodable?(text, resource) ⇒ Boolean
83 |
# File 'lib/pdfrb/document/fonts.rb', line 83 def encodable?(text, resource); !encode_text(text, resource).include?("?"); end |
#encode_text(text, resource) ⇒ Object
75 76 77 78 79 80 81 |
# File 'lib/pdfrb/document/fonts.rb', line 75 def encode_text(text, resource) text.to_s.each_codepoint { |cp| @used_codepoints[resource] << cp } enc = @encodings[resource] return text.to_s.b unless enc Pdfrb::Font::Encoding.encode(enc, text.to_s) end |
#encoding_for(resource) ⇒ Object
73 |
# File 'lib/pdfrb/document/fonts.rb', line 73 def encoding_for(resource); @encodings[resource]; end |
#glyph_width(resource, codepoint) ⇒ Object
113 114 115 116 117 118 119 120 121 |
# File 'lib/pdfrb/document/fonts.rb', line 113 def glyph_width(resource, codepoint) metrics = @afm_metrics[resource] return DEFAULT_WIDTH unless metrics cp = codepoint.is_a?(String) ? (codepoint.each_codepoint.first || 0) : codepoint.to_i return 0 if cp > 255 metrics[:widths][cp] || 0 end |
#glyph_widths(resource, codepoints) ⇒ Object
123 124 125 126 |
# File 'lib/pdfrb/document/fonts.rb', line 123 def glyph_widths(resource, codepoints) cps = codepoints.is_a?(String) ? codepoints.each_codepoint.to_a : codepoints cps.map { |cp| glyph_width(resource, cp) } end |
#measure_text(text, font:, size:) ⇒ Object
85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/pdfrb/document/fonts.rb', line 85 def measure_text(text, font:, size:) return 0 unless text && size metrics = @afm_metrics[font] return text.to_s.length * size.to_f * 0.5 unless metrics total = text.to_s.each_char.sum do |ch| byte = ch.bytes.first || 0 metrics[:widths][byte] || DEFAULT_WIDTH end total * size.to_f / 1000.0 end |
#metrics_for(resource) ⇒ Object
128 |
# File 'lib/pdfrb/document/fonts.rb', line 128 def metrics_for(resource); @afm_metrics[resource]; end |
#register_usage(font_dict, text) ⇒ Object
Record that text was drawn with font_dict so the
write-time subsetter knows which codepoints each embedded
font must retain.
68 69 70 71 |
# File 'lib/pdfrb/document/fonts.rb', line 68 def register_usage(font_dict, text) resource = @font_dicts.key(font_dict) track(resource, text) if resource end |
#subset_fonts! ⇒ Object
148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/pdfrb/document/fonts.rb', line 148 def subset_fonts! @font_streams.each do |resource, data| next unless valid_font_data?(data) codepoints = @used_codepoints[resource] next if codepoints.empty? begin subset_font(resource, data, codepoints) rescue StandardError next end end end |
#text_width(text, _resource, size:) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/pdfrb/document/fonts.rb', line 98 def text_width(text, _resource, size:) return 0 unless text && size res = @afm_metrics[_resource] ? _resource : (@registry[_resource.to_s] || _resource) metrics = @afm_metrics[res] return text.to_s.length * size.to_f * 0.5 unless metrics upem = metrics[:units_per_em] || 1000 total = text.to_s.each_char.sum do |ch| byte = ch.bytes.first || 0 metrics[:widths][byte] || DEFAULT_WIDTH end total * size.to_f / upem.to_f end |
#used_codepoints(resource) ⇒ Object
63 |
# File 'lib/pdfrb/document/fonts.rb', line 63 def used_codepoints(resource); @used_codepoints[resource]; end |
#valid_font_data?(data) ⇒ Boolean
130 131 132 133 134 135 |
# File 'lib/pdfrb/document/fonts.rb', line 130 def valid_font_data?(data) return false unless data.is_a?(String) && data.bytesize >= 4 magic = data.byteslice(0, 4) ["ttcf".b, "\x00\x01\x00\x00".b, "OTTO".b, "true".b, "typ1".b].include?(magic) end |