# -*- encoding: utf-8; frozen_string_literal: true -*- # #-- # This file is part of HexaPDF. # # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby # Copyright (C) 2014-2018 Thomas Leitner # # HexaPDF is free software: you can redistribute it and/or modify it # under the terms of the GNU Affero General Public License version 3 as # published by the Free Software Foundation with the addition of the # following permission added to Section 15 as permitted in Section 7(a): # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON # INFRINGEMENT OF THIRD PARTY RIGHTS. # # HexaPDF is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public # License for more details. # # You should have received a copy of the GNU Affero General Public License # along with HexaPDF. If not, see . # # The interactive user interfaces in modified source and object code # versions of HexaPDF must display Appropriate Legal Notices, as required # under Section 5 of the GNU Affero General Public License version 3. # # In accordance with Section 7(b) of the GNU Affero General Public # License, a covered work must retain the producer line in every PDF that # is created or manipulated using HexaPDF. #++ require 'hexapdf/error' require 'hexapdf/content/graphics_state' require 'hexapdf/content/transformation_matrix' module HexaPDF module Content # This module contains the content operator implementations. # # == General Information # # A PDF content streams consists of a series of instructions, operands followed by an operator # name. Each operator has a specific function, for example, the 'G' operator sets the stroke # color to the specified gray value. # # Since HexaPDF doesn't have a content stream rendering facility, it is only interested in the # effects an operator has on the graphics state. By calling the #invoke method with a # Content::Processor as first argument and the operands as the rest of the arguments, the # operator can modify the graphics state as needed. This ensures internal consistency and # correct operation. # # Operator objects are designed to be state-less. This means that the operands have to be # passed as arguments to the methods that need them. # # # == Operator Implementations # # HexaPDF comes with operator implementations for all PDF operations. These operator # implementations are derived from the Operator::BaseOperator class which provides all needed # methods. # # In general, an operator implementation is an object that responds to the following methods: # # #invoke(processor, *operands):: # When an operator is invoked, it performs its job, e.g. changing the graphics state. # # #serialize(serializer, *operands):: # Returns the operator together with its operands in serialized form. # # #name:: # Returns the name of the operator as String. # # See: PDF1.7 s8, s9 module Operator # A base class for operator implementations. # # A default implementation for the #serialize method is provided. However, for performance # reasons each operator should provide a custom #serialize method. class BaseOperator # The name of the operator. attr_reader :name # Initialize the operator called +name+. def initialize(name) @name = name.freeze end # Invokes the operator so that it performs its job. # # This base version does nothing! def invoke(*) end # Returns the string representation of the operator, i.e. # # operand1 operand2 operand3 name def serialize(serializer, *operands) result = ''.b operands.each do |operand| result << serializer.serialize(operand) << " " end result << name << "\n" end end # A specialized operator class for operators that take no arguments. Provides an optimized # #serialize method. class NoArgumentOperator < BaseOperator def initialize(name) #:nodoc: super(name) @serialized = "#{name}\n" end def invoke(_processor) # :nodoc: end # An optimized version of the serialization algorithm. # # See: BaseOperator#serialize def serialize(_serializer) @serialized end end # A specialized operator class for operators that take a single numeric argument. Provides # an optimized #serialize method. class SingleNumericArgumentOperator < BaseOperator # An optimized version of the serialization algorithm. # # See: BaseOperator#serialize def serialize(serializer, arg) "#{serializer.serialize_numeric(arg)} #{name}\n" end end # Implementation of the 'q' operator. # # See: PDF1.7 s8.4.4 class SaveGraphicsState < NoArgumentOperator # Creates the operator. def initialize super('q') end def invoke(processor) #:nodoc: processor.graphics_state.save end end # Implementation of the 'Q' operator. # # See: PDF1.7 s8.4.4 class RestoreGraphicsState < NoArgumentOperator # Creates the operator. def initialize super('Q') end def invoke(processor) #:nodoc: processor.graphics_state.restore end end # Implementation of the 'cm' operator. # # See: PDF1.7 s8.4.4 class ConcatenateMatrix < BaseOperator # Creates the operator. def initialize super('cm') end def invoke(processor, a, b, c, d, e, f) #:nodoc: processor.graphics_state.ctm.premultiply(a, b, c, d, e, f) end def serialize(serializer, a, b, c, d, e, f) #:nodoc: "#{serializer.serialize_numeric(a)} #{serializer.serialize_numeric(b)} " \ "#{serializer.serialize_numeric(c)} #{serializer.serialize_numeric(d)} " \ "#{serializer.serialize_numeric(e)} #{serializer.serialize_numeric(f)} cm\n" end end # Implementation of the 'w' operator. # # See: PDF1.7 s8.4.4 class SetLineWidth < SingleNumericArgumentOperator # Creates the operator. def initialize super('w') end def invoke(processor, width) #:nodoc: processor.graphics_state.line_width = width end end # Implementation of the 'J' operator. # # See: PDF1.7 s8.4.4 class SetLineCapStyle < SingleNumericArgumentOperator # Creates the operator. def initialize super('J') end def invoke(processor, cap_style) #:nodoc: processor.graphics_state.line_cap_style = LineCapStyle.normalize(cap_style) end end # Implementation of the 'j' operator. # # See: PDF1.7 s8.4.4 class SetLineJoinStyle < SingleNumericArgumentOperator # Creates the operator. def initialize super('j') end def invoke(processor, join_style) #:nodoc: processor.graphics_state.line_join_style = LineJoinStyle.normalize(join_style) end end # Implementation of the 'M' operator. # # See: PDF1.7 s8.4.4 class SetMiterLimit < SingleNumericArgumentOperator # Creates the operator. def initialize super('M') end def invoke(processor, miter_limit) #:nodoc: processor.graphics_state.miter_limit = miter_limit end end # Implementation of the 'd' operator. # # See: PDF1.7 s8.4.4 class SetLineDashPattern < BaseOperator # Creates the operator. def initialize super('d') end def invoke(processor, dash_array, dash_phase) #:nodoc: processor.graphics_state.line_dash_pattern = LineDashPattern.new(dash_array, dash_phase) end def serialize(serializer, dash_array, dash_phase) #:nodoc: "#{serializer.serialize_array(dash_array)} " \ "#{serializer.serialize_integer(dash_phase)} d\n" end end # Implementation of the 'ri' operator. # # See: PDF1.7 s8.4.4 class SetRenderingIntent < BaseOperator # Creates the operator. def initialize super('ri') end def invoke(processor, intent) #:nodoc: processor.graphics_state.rendering_intent = intent end def serialize(serializer, intent) #:nodoc: "#{serializer.serialize_symbol(intent)} ri\n" end end # Implementation of the 'gs' operator. # # Note: Only parameters supported by the GraphicsState/TextState classes are assigned, the # rest are ignored! # # See: PDF1.7 s8.4.4 class SetGraphicsStateParameters < BaseOperator # Creates the operator. def initialize super('gs') end def invoke(processor, name) #:nodoc: dict = processor.resources.ext_gstate(name) ops = processor.operators ops[:w].invoke(processor, dict[:LW]) if dict.key?(:LW) ops[:J].invoke(processor, dict[:LC]) if dict.key?(:LC) ops[:j].invoke(processor, dict[:LJ]) if dict.key?(:LJ) ops[:M].invoke(processor, dict[:ML]) if dict.key?(:ML) ops[:d].invoke(processor, *dict[:D]) if dict.key?(:D) ops[:ri].invoke(processor, dict[:RI]) if dict.key?(:RI) # TODO: dict[:SMask] works differently than operator! # No content operator exists for the following parameters gs = processor.graphics_state gs.stroke_adjustment = dict[:SA] if dict.key?(:SA) gs.blend_mode = dict[:BM] if dict.key?(:BM) gs.stroke_alpha = dict[:CA] if dict.key?(:CA) gs.fill_alpha = dict[:ca] if dict.key?(:ca) gs.alpha_source = dict[:AIS] if dict.key?(:AIS) gs.text_knockout = dict[:TK] if dict.key?(:TK) if dict.key?(:Font) gs.font = processor.resources.document.deref(dict[:Font][0]) gs.font_size = processor.resources.document.deref(dict[:Font][1]) end end def serialize(serializer, name) #:nodoc: "#{serializer.serialize_symbol(name)} gs\n" end end # Implementation of the 'CS' operator. # # See: PDF1.7 s8.6.8 class SetStrokingColorSpace < BaseOperator # Creates the operator. def initialize super('CS') end def invoke(processor, name) #:nodoc: processor.graphics_state.stroke_color_space = processor.resources.color_space(name) end def serialize(serializer, name) #:nodoc: "#{serializer.serialize_symbol(name)} CS\n" end end # Implementation of the 'cs' operator. # # See: PDF1.7 s8.6.8 class SetNonStrokingColorSpace < BaseOperator # Creates the operator. def initialize super('cs') end def invoke(processor, name) #:nodoc: processor.graphics_state.fill_color_space = processor.resources.color_space(name) end def serialize(serializer, name) #:nodoc: "#{serializer.serialize_symbol(name)} cs\n" end end # Implementation of the 'SC' and 'SCN' operator. # # See: PDF1.7 s8.6.8 class SetStrokingColor < BaseOperator def invoke(processor, *operands) #:nodoc: processor.graphics_state.stroke_color = processor.graphics_state.stroke_color.color_space.color(*operands) end end # Implementation of the 'sc' and 'scn' operator. # # See: PDF1.7 s8.6.8 class SetNonStrokingColor < BaseOperator def invoke(processor, *operands) #:nodoc: processor.graphics_state.fill_color = processor.graphics_state.fill_color.color_space.color(*operands) end end # Implementation of the 'G' operator. # # See: PDF1.7 s8.6.8 class SetDeviceGrayStrokingColor < SingleNumericArgumentOperator def initialize #:nodoc: super('G') end def invoke(processor, gray) #:nodoc: processor.graphics_state.stroke_color = processor.resources.color_space(:DeviceGray).color(gray) end end # Implementation of the 'g' operator. # # See: PDF1.7 s8.6.8 class SetDeviceGrayNonStrokingColor < SingleNumericArgumentOperator # Creates the operator. def initialize super('g') end def invoke(processor, gray) #:nodoc: processor.graphics_state.fill_color = processor.resources.color_space(:DeviceGray).color(gray) end end # Implementation of the 'RG' operator. # # See: PDF1.7 s8.6.8 class SetDeviceRGBStrokingColor < BaseOperator # Creates the operator. def initialize super('RG') end def invoke(processor, r, g, b) #:nodoc: processor.graphics_state.stroke_color = processor.resources.color_space(:DeviceRGB).color(r, g, b) end def serialize(serializer, r, g, b) #:nodoc: "#{serializer.serialize_numeric(r)} #{serializer.serialize_numeric(g)} " \ "#{serializer.serialize_numeric(b)} RG\n" end end # Implementation of the 'rg' operator. # # See: PDF1.7 s8.6.8 class SetDeviceRGBNonStrokingColor < BaseOperator # Creates the operator. def initialize super('rg') end def invoke(processor, r, g, b) #:nodoc: processor.graphics_state.fill_color = processor.resources.color_space(:DeviceRGB).color(r, g, b) end def serialize(serializer, r, g, b) #:nodoc: "#{serializer.serialize_numeric(r)} #{serializer.serialize_numeric(g)} " \ "#{serializer.serialize_numeric(b)} rg\n" end end # Implementation of the 'K' operator. # # See: PDF1.7 s8.6.8 class SetDeviceCMYKStrokingColor < BaseOperator # Creates the operator. def initialize super('K') end def invoke(processor, c, m, y, k) #:nodoc: processor.graphics_state.stroke_color = processor.resources.color_space(:DeviceCMYK).color(c, m, y, k) end def serialize(serializer, c, m, y, k) #:nodoc: "#{serializer.serialize_numeric(c)} #{serializer.serialize_numeric(m)} " \ "#{serializer.serialize_numeric(y)} #{serializer.serialize_numeric(k)} K\n" end end # Implementation of the 'k' operator. # # See: PDF1.7 s8.6.8 class SetDeviceCMYKNonStrokingColor < BaseOperator # Creates the operator. def initialize super('k') end def invoke(processor, c, m, y, k) #:nodoc: processor.graphics_state.fill_color = processor.resources.color_space(:DeviceCMYK).color(c, m, y, k) end def serialize(serializer, c, m, y, k) #:nodoc: "#{serializer.serialize_numeric(c)} #{serializer.serialize_numeric(m)} " \ "#{serializer.serialize_numeric(y)} #{serializer.serialize_numeric(k)} k\n" end end # Implementation of the 'm' operator. # # See: PDF1.7 s8.5.2.1 class MoveTo < BaseOperator # Creates the operator. def initialize super('m') end def invoke(processor, _x, _y) #:nodoc: processor.graphics_object = :path end def serialize(serializer, x, y) #:nodoc: "#{serializer.serialize_numeric(x)} #{serializer.serialize_numeric(y)} m\n" end end # Implementation of the 're' operator. # # See: PDF1.7 s8.5.2.1 class AppendRectangle < BaseOperator # Creates the operator. def initialize super('re') end def invoke(processor, _x, _y, _w, _h) #:nodoc: processor.graphics_object = :path end def serialize(serializer, x, y, w, h) #:nodoc: "#{serializer.serialize_numeric(x)} #{serializer.serialize_numeric(y)} " \ "#{serializer.serialize_numeric(w)} #{serializer.serialize_numeric(h)} re\n" end end # Implementation of the 'l' operator. # # See: PDF1.7 s8.5.2.1 class LineTo < BaseOperator # Creates the operator. def initialize super('l') end def invoke(_processor, _x, _y) #:nodoc: end def serialize(serializer, x, y) #:nodoc: "#{serializer.serialize_numeric(x)} #{serializer.serialize_numeric(y)} l\n" end end # Implementation of the 'c' operators. # # See: PDF1.7 s8.5.2.1 class CurveTo < BaseOperator # Creates the operator. def initialize super('c') end def serialize(serializer, x1, y1, x2, y2, x3, y3) #:nodoc: "#{serializer.serialize_numeric(x1)} #{serializer.serialize_numeric(y1)} " \ "#{serializer.serialize_numeric(x2)} #{serializer.serialize_numeric(y2)} " \ "#{serializer.serialize_numeric(x3)} #{serializer.serialize_numeric(y3)} c\n" end end # Implementation of the 'v' operators. # # See: PDF1.7 s8.5.2.1 class CurveToNoFirstControlPoint < BaseOperator # Creates the operator. def initialize super('v') end def serialize(serializer, x2, y2, x3, y3) #:nodoc: "#{serializer.serialize_numeric(x2)} #{serializer.serialize_numeric(y2)} " \ "#{serializer.serialize_numeric(x3)} #{serializer.serialize_numeric(y3)} v\n" end end # Implementation of the 'y' operators. # # See: PDF1.7 s8.5.2.1 class CurveToNoSecondControlPoint < BaseOperator # Creates the operator. def initialize super('y') end def serialize(serializer, x1, y1, x3, y3) #:nodoc: "#{serializer.serialize_numeric(x1)} #{serializer.serialize_numeric(y1)} " \ "#{serializer.serialize_numeric(x3)} #{serializer.serialize_numeric(y3)} y\n" end end # Implementation of the 'S', 's', 'f', 'F', 'f*', 'B', 'B*', 'b', 'b*' and 'n' operators. # # See: PDF1.7 s8.5.3.1 class EndPath < NoArgumentOperator def invoke(processor) #:nodoc: processor.graphics_object = :none end end # Implementation of the 'W' and 'W*' operators. # # See: PDF1.7 s8.5.4 class ClipPath < NoArgumentOperator def invoke(processor) #:nodoc: processor.graphics_object = :clipping_path end end # Implementation of the 'BI' operator which handles the *complete* inline image, i.e. the # 'ID' and 'EI' operators are never encountered. # # See: PDF1.7 s8.9.7 class InlineImage < BaseOperator # Creates the operator. def initialize super('BI') end def serialize(serializer, dict, data) #:nodoc: result = +"BI\n" dict.each do |k, v| result << serializer.serialize_symbol(k) << ' ' result << serializer.serialize(v) << ' ' end result << "ID\n" << data << "EI\n" end end # Implementation of the 'Tc' operator. # # See: PDF1.7 s9.3.1 class SetCharacterSpacing < SingleNumericArgumentOperator # Creates the operator. def initialize super('Tc') end def invoke(processor, char_space) #:nodoc: processor.graphics_state.character_spacing = char_space end end # Implementation of the 'Tw' operator. # # See: PDF1.7 s9.3.1 class SetWordSpacing < SingleNumericArgumentOperator # Creates the operator. def initialize super('Tw') end def invoke(processor, word_space) #:nodoc: processor.graphics_state.word_spacing = word_space end end # Implementation of the 'Tz' operator. # # See: PDF1.7 s9.3.1 class SetHorizontalScaling < SingleNumericArgumentOperator # Creates the operator. def initialize super('Tz') end def invoke(processor, scale) #:nodoc: processor.graphics_state.horizontal_scaling = scale end end # Implementation of the 'TL' operator. # # See: PDF1.7 s9.3.1 class SetLeading < SingleNumericArgumentOperator # Creates the operator. def initialize super('TL') end def invoke(processor, leading) #:nodoc: processor.graphics_state.leading = leading end end # Implementation of the 'Tf' operator. # # See: PDF1.7 s9.3.1 class SetFontAndSize < BaseOperator # Creates the operator. def initialize super('Tf') end def invoke(processor, font, size) #:nodoc: processor.graphics_state.font = processor.resources.font(font) processor.graphics_state.font_size = size end def serialize(serializer, font, size) #:nodoc: "#{serializer.serialize_symbol(font)} #{serializer.serialize_numeric(size)} Tf\n" end end # Implementation of the 'Tr' operator. # # See: PDF1.7 s9.3.1 class SetTextRenderingMode < SingleNumericArgumentOperator # Creates the operator. def initialize super('Tr') end def invoke(processor, rendering_mode) #:nodoc: processor.graphics_state.text_rendering_mode = TextRenderingMode.normalize(rendering_mode) end end # Implementation of the 'Ts' operator. # # See: PDF1.7 s9.3.1 class SetTextRise < SingleNumericArgumentOperator # Creates the operator. def initialize super('Ts') end def invoke(processor, rise) #:nodoc: processor.graphics_state.text_rise = rise end end # Implementation of the 'BT' operator. # # See: PDF1.7 s9.4.1 class BeginText < NoArgumentOperator def initialize #:nodoc: super('BT') end def invoke(processor) #:nodoc: processor.graphics_object = :text processor.graphics_state.tm = TransformationMatrix.new processor.graphics_state.tlm = TransformationMatrix.new end end # Implementation of the 'ET' operator. # # See: PDF1.7 s9.4.1 class EndText < NoArgumentOperator # Creates the operator. def initialize super('ET') end def invoke(processor) #:nodoc: processor.graphics_object = :none processor.graphics_state.tm = nil processor.graphics_state.tlm = nil end end # Implementation of the 'Td' operator. # # See: PDF1.7 s9.4.2 class MoveText < BaseOperator # Creates the operator. def initialize super('Td') end def invoke(processor, tx, ty) #:nodoc: processor.graphics_state.tlm.translate(tx, ty) processor.graphics_state.tm = processor.graphics_state.tlm.dup end def serialize(serializer, tx, ty) #:nodoc: "#{serializer.serialize_numeric(tx)} #{serializer.serialize_numeric(ty)} Td\n" end end # Implementation of the 'TD' operator. # # See: PDF1.7 s9.4.2 class MoveTextAndSetLeading < BaseOperator # Creates the operator. def initialize super('TD') end def invoke(processor, tx, ty) #:nodoc: processor.operators[:TL].invoke(processor, -ty) processor.operators[:Td].invoke(processor, tx, ty) end def serialize(serializer, tx, ty) #:nodoc: "#{serializer.serialize_numeric(tx)} #{serializer.serialize_numeric(ty)} TD\n" end end # Implementation of the 'Tm' operator. # # See: PDF1.7 s9.4.2 class SetTextMatrix < BaseOperator # Creates the operator. def initialize super('Tm') end def invoke(processor, a, b, c, d, e, f) #:nodoc: processor.graphics_state.tm = TransformationMatrix.new(a, b, c, d, e, f) processor.graphics_state.tlm = processor.graphics_state.tm.dup end def serialize(serializer, a, b, c, d, e, f) #:nodoc: "#{serializer.serialize_numeric(a)} #{serializer.serialize_numeric(b)} " \ "#{serializer.serialize_numeric(c)} #{serializer.serialize_numeric(d)} " \ "#{serializer.serialize_numeric(e)} #{serializer.serialize_numeric(f)} Tm\n" end end # Implementation of the 'T*' operator. # # See: PDF1.7 s9.4.2 class MoveTextNextLine < NoArgumentOperator # Creates the operator. def initialize super('T*') end def invoke(processor) #:nodoc: leading = processor.graphics_state.leading processor.operators[:Td].invoke(processor, 0, -leading) end end # Implementation of the 'Tj' operator. # # See: PDF1.7 s9.4.3 class ShowText < BaseOperator # Creates the operator. def initialize super('Tj') end def serialize(serializer, text) #:nodoc: "#{serializer.serialize_string(text)}Tj\n" end end # Implementation of the ' operator. # # See: PDF1.7 s9.4.3 class MoveTextNextLineAndShowText < BaseOperator def initialize #:nodoc: super("'") end def invoke(processor, text) #:nodoc: processor.operators[:'T*'].invoke(processor) processor.operators[:Tj].invoke(processor, text) end def serialize(serializer, text) "#{serializer.serialize_string(text)}'\n" end end # Implementation of the " operator. # # See: PDF1.7 s9.4.3 class SetSpacingMoveTextNextLineAndShowText < BaseOperator # Creates the operator. def initialize super('"') end def invoke(processor, word_space, char_space, text) #:nodoc: processor.operators[:Tw].invoke(processor, word_space) processor.operators[:Tc].invoke(processor, char_space) processor.operators[:"'"].invoke(processor, text) end def serialize(serializer, word_space, char_space, text) #:nodoc: "#{serializer.serialize_numeric(word_space)} " \ "#{serializer.serialize_numeric(char_space)} " \ "#{serializer.serialize_string(text)}\"\n" end end # Implementation of the 'TJ' operator. # # See: PDF1.7 s9.4.3 class ShowTextWithPositioning < BaseOperator # Creates the operator. def initialize super('TJ') end def serialize(serializer, array) #:nodoc: "#{serializer.serialize_array(array)}TJ\n" end end # Mapping of operator names to their default operator implementations. DEFAULT_OPERATORS = { q: SaveGraphicsState.new, Q: RestoreGraphicsState.new, cm: ConcatenateMatrix.new, w: SetLineWidth.new, J: SetLineCapStyle.new, j: SetLineJoinStyle.new, M: SetMiterLimit.new, d: SetLineDashPattern.new, ri: SetRenderingIntent.new, gs: SetGraphicsStateParameters.new, CS: SetStrokingColorSpace.new, cs: SetNonStrokingColorSpace.new, SC: SetStrokingColor.new('SC'), SCN: SetStrokingColor.new('SCN'), sc: SetNonStrokingColor.new('sc'), scn: SetNonStrokingColor.new('scn'), G: SetDeviceGrayStrokingColor.new, g: SetDeviceGrayNonStrokingColor.new, RG: SetDeviceRGBStrokingColor.new, rg: SetDeviceRGBNonStrokingColor.new, K: SetDeviceCMYKStrokingColor.new, k: SetDeviceCMYKNonStrokingColor.new, m: MoveTo.new, re: AppendRectangle.new, l: LineTo.new, c: CurveTo.new, v: CurveToNoFirstControlPoint.new, y: CurveToNoSecondControlPoint.new, h: NoArgumentOperator.new('h'), S: EndPath.new('S'), s: EndPath.new('s'), f: EndPath.new('f'), F: EndPath.new('F'), 'f*'.to_sym => EndPath.new('f*'), B: EndPath.new('B'), 'B*'.to_sym => EndPath.new('B*'), b: EndPath.new('b'), 'b*'.to_sym => EndPath.new('b*'), n: EndPath.new('n'), W: ClipPath.new('W'), 'W*'.to_sym => ClipPath.new('W*'), BI: InlineImage.new, BT: BeginText.new, ET: EndText.new, Tc: SetCharacterSpacing.new, Tw: SetWordSpacing.new, Tz: SetHorizontalScaling.new, TL: SetLeading.new, Tf: SetFontAndSize.new, Tr: SetTextRenderingMode.new, Ts: SetTextRise.new, Td: MoveText.new, TD: MoveTextAndSetLeading.new, Tm: SetTextMatrix.new, 'T*'.to_sym => MoveTextNextLine.new, Tj: ShowText.new, '\''.to_sym => MoveTextNextLineAndShowText.new, '"'.to_sym => SetSpacingMoveTextNextLineAndShowText.new, TJ: ShowTextWithPositioning.new, } DEFAULT_OPERATORS.default_proc = proc {|h, k| h[k] = BaseOperator.new(k.to_s) } end end end