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
- #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
195 196 197 198 199 200 |
# File 'lib/pdfrb/document/fonts.rb', line 195 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
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/pdfrb/document/fonts.rb', line 202 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
169 |
# File 'lib/pdfrb/document/fonts.rb', line 169 def loaders; @loaders ||= []; end |
.register_loader(loader) ⇒ Object
170 |
# File 'lib/pdfrb/document/fonts.rb', line 170 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
128 129 130 131 132 133 134 135 136 137 |
# File 'lib/pdfrb/document/fonts.rb', line 128 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
74 |
# File 'lib/pdfrb/document/fonts.rb', line 74 def encodable?(text, resource); !encode_text(text, resource).include?("?"); end |
#encode_text(text, resource) ⇒ Object
66 67 68 69 70 71 72 |
# File 'lib/pdfrb/document/fonts.rb', line 66 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
64 |
# File 'lib/pdfrb/document/fonts.rb', line 64 def encoding_for(resource); @encodings[resource]; end |
#glyph_width(resource, codepoint) ⇒ Object
104 105 106 107 108 109 110 111 112 |
# File 'lib/pdfrb/document/fonts.rb', line 104 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
114 115 116 117 |
# File 'lib/pdfrb/document/fonts.rb', line 114 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
76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/pdfrb/document/fonts.rb', line 76 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
119 |
# File 'lib/pdfrb/document/fonts.rb', line 119 def metrics_for(resource); @afm_metrics[resource]; end |
#subset_fonts! ⇒ Object
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/pdfrb/document/fonts.rb', line 139 def subset_fonts! @font_streams.each do |resource, data| next unless valid_font_data?(data) codepoints = @used_codepoints[resource] next if codepoints.empty? begin ttf = Pdfrb::Font::TrueType::File.new(data) subsetter = Pdfrb::Font::TrueType::Subsetter.new(ttf) subset = subsetter.subset(codepoints.to_a) dict = @font_dicts[resource] next unless dict desc_ref = dict.value[:FontDescriptor] next unless desc_ref desc = desc_ref.is_a?(Pdfrb::Model::Reference) ? document.object(desc_ref) : desc_ref next unless desc fd_stream = document.add({ Length: subset.bytesize }, type: Pdfrb::Model::Cos::Stream) fd_stream.stream = subset desc.value[:FontFile2] = Pdfrb::Model::Reference.new(fd_stream.oid, fd_stream.gen) rescue StandardError next end end end |
#text_width(text, _resource, size:) ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/pdfrb/document/fonts.rb', line 89 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
121 122 123 124 125 126 |
# File 'lib/pdfrb/document/fonts.rb', line 121 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 |