lib/ttfunk/encoded_string.rb in ttfunk-1.7.0 vs lib/ttfunk/encoded_string.rb in ttfunk-1.8.0

- old
+ new

@@ -1,69 +1,120 @@ # frozen_string_literal: true require 'stringio' +require_relative 'placeholder' module TTFunk + # Risen when the final encoded string was requested but there were some + # unresolved placeholders in it. class UnresolvedPlaceholderError < StandardError end + # Risen when a placeholder is added to an Encoded String but it already + # contains a placeholder with the same name. class DuplicatePlaceholderError < StandardError end + # Encoded string takes care of placeholders in binary strings. Placeholders + # are used when bytes need to be placed in the stream before their value is + # known. + # + # @api private class EncodedString + # @yieldparam [self] def initialize - yield self if block_given? + yield(self) if block_given? end + # Append to string. + # + # @param obj [String, Placeholder, EncodedString] + # @return [self] def <<(obj) case obj when String io << obj when Placeholder add_placeholder(obj) - io << "\0" * obj.length + io << ("\0" * obj.length) when self.class # adjust placeholders to be relative to the entire encoded string obj.placeholders.each_pair do |_, placeholder| add_placeholder(placeholder.dup, placeholder.position + io.length) end - self << obj.unresolved_string + io << obj.unresolved_string end self end + # Append multiple objects. + # + # @param objs [Array<String, Placeholder, EncodedString>] + # @return [self] + def concat(*objs) + objs.each do |obj| + self << obj + end + self + end + + # Append padding to align string to the specified word width. + # + # @param width [Integer] + # @return [self] def align!(width = 4) if (length % width).positive? - self << "\0" * (width - length % width) + self << ("\0" * (width - (length % width))) end self end + # Length of this string. + # + # @return [Integer] def length io.length end + # Raw string. + # + # @return [String] + # @raise [UnresolvedPlaceholderError] if there are any unresolved + # placeholders left. def string unless placeholders.empty? raise UnresolvedPlaceholderError, "string contains #{placeholders.size} unresolved placeholder(s)" end io.string end + # Raw bytes. + # + # @return [Array<Integer>] + # @raise [UnresolvedPlaceholderError] if there are any unresolved + # placeholders left. def bytes string.bytes end + # Unresolved raw string. + # + # @return [String] def unresolved_string io.string end + # Resolve placeholder. + # + # @param name [Symbol] + # @param value [String] + # @return [void] def resolve_placeholder(name, value) last_pos = io.pos if (placeholder = placeholders[name]) io.seek(placeholder.position) @@ -72,9 +123,12 @@ end ensure io.seek(last_pos) end + # Plaholders + # + # @return [Hash{Symbol => Plaholder}] def placeholders @placeholders ||= {} end private