hexapdf 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +45 -0
 - data/README.md +1 -1
 - data/lib/hexapdf/cli/inspect.rb +13 -4
 - data/lib/hexapdf/composer.rb +14 -0
 - data/lib/hexapdf/configuration.rb +5 -0
 - data/lib/hexapdf/document/annotations.rb +60 -2
 - data/lib/hexapdf/document/layout.rb +45 -6
 - data/lib/hexapdf/error.rb +11 -3
 - data/lib/hexapdf/font/true_type/subsetter.rb +15 -2
 - data/lib/hexapdf/layout/style.rb +101 -7
 - data/lib/hexapdf/object.rb +2 -2
 - data/lib/hexapdf/pdf_array.rb +25 -3
 - data/lib/hexapdf/tokenizer.rb +4 -1
 - data/lib/hexapdf/type/acro_form/appearance_generator.rb +57 -8
 - data/lib/hexapdf/type/acro_form/field.rb +1 -0
 - data/lib/hexapdf/type/acro_form/form.rb +7 -6
 - data/lib/hexapdf/type/annotation.rb +12 -0
 - data/lib/hexapdf/type/annotations/appearance_generator.rb +75 -0
 - data/lib/hexapdf/type/annotations/border_effect.rb +99 -0
 - data/lib/hexapdf/type/annotations/circle.rb +65 -0
 - data/lib/hexapdf/type/annotations/interior_color.rb +84 -0
 - data/lib/hexapdf/type/annotations/line.rb +4 -35
 - data/lib/hexapdf/type/annotations/square.rb +65 -0
 - data/lib/hexapdf/type/annotations/square_circle.rb +77 -0
 - data/lib/hexapdf/type/annotations/widget.rb +50 -20
 - data/lib/hexapdf/type/annotations.rb +5 -0
 - data/lib/hexapdf/version.rb +1 -1
 - data/test/hexapdf/document/test_annotations.rb +22 -0
 - data/test/hexapdf/document/test_layout.rb +24 -2
 - data/test/hexapdf/font/true_type/test_subsetter.rb +10 -0
 - data/test/hexapdf/layout/test_style.rb +27 -2
 - data/test/hexapdf/test_composer.rb +7 -0
 - data/test/hexapdf/test_object.rb +1 -1
 - data/test/hexapdf/test_pdf_array.rb +36 -3
 - data/test/hexapdf/type/acro_form/test_appearance_generator.rb +78 -3
 - data/test/hexapdf/type/acro_form/test_button_field.rb +7 -6
 - data/test/hexapdf/type/acro_form/test_field.rb +5 -0
 - data/test/hexapdf/type/acro_form/test_form.rb +17 -1
 - data/test/hexapdf/type/annotations/test_appearance_generator.rb +84 -0
 - data/test/hexapdf/type/annotations/test_border_effect.rb +59 -0
 - data/test/hexapdf/type/annotations/test_interior_color.rb +37 -0
 - data/test/hexapdf/type/annotations/test_line.rb +0 -20
 - data/test/hexapdf/type/annotations/test_widget.rb +35 -0
 - metadata +9 -2
 
| 
         @@ -0,0 +1,77 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # -*- encoding: utf-8; frozen_string_literal: true -*-
         
     | 
| 
      
 2 
     | 
    
         
            +
            #
         
     | 
| 
      
 3 
     | 
    
         
            +
            #--
         
     | 
| 
      
 4 
     | 
    
         
            +
            # This file is part of HexaPDF.
         
     | 
| 
      
 5 
     | 
    
         
            +
            #
         
     | 
| 
      
 6 
     | 
    
         
            +
            # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
         
     | 
| 
      
 7 
     | 
    
         
            +
            # Copyright (C) 2014-2025 Thomas Leitner
         
     | 
| 
      
 8 
     | 
    
         
            +
            #
         
     | 
| 
      
 9 
     | 
    
         
            +
            # HexaPDF is free software: you can redistribute it and/or modify it
         
     | 
| 
      
 10 
     | 
    
         
            +
            # under the terms of the GNU Affero General Public License version 3 as
         
     | 
| 
      
 11 
     | 
    
         
            +
            # published by the Free Software Foundation with the addition of the
         
     | 
| 
      
 12 
     | 
    
         
            +
            # following permission added to Section 15 as permitted in Section 7(a):
         
     | 
| 
      
 13 
     | 
    
         
            +
            # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
         
     | 
| 
      
 14 
     | 
    
         
            +
            # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
         
     | 
| 
      
 15 
     | 
    
         
            +
            # INFRINGEMENT OF THIRD PARTY RIGHTS.
         
     | 
| 
      
 16 
     | 
    
         
            +
            #
         
     | 
| 
      
 17 
     | 
    
         
            +
            # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
         
     | 
| 
      
 18 
     | 
    
         
            +
            # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
         
     | 
| 
      
 19 
     | 
    
         
            +
            # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
         
     | 
| 
      
 20 
     | 
    
         
            +
            # License for more details.
         
     | 
| 
      
 21 
     | 
    
         
            +
            #
         
     | 
| 
      
 22 
     | 
    
         
            +
            # You should have received a copy of the GNU Affero General Public License
         
     | 
| 
      
 23 
     | 
    
         
            +
            # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
         
     | 
| 
      
 24 
     | 
    
         
            +
            #
         
     | 
| 
      
 25 
     | 
    
         
            +
            # The interactive user interfaces in modified source and object code
         
     | 
| 
      
 26 
     | 
    
         
            +
            # versions of HexaPDF must display Appropriate Legal Notices, as required
         
     | 
| 
      
 27 
     | 
    
         
            +
            # under Section 5 of the GNU Affero General Public License version 3.
         
     | 
| 
      
 28 
     | 
    
         
            +
            #
         
     | 
| 
      
 29 
     | 
    
         
            +
            # In accordance with Section 7(b) of the GNU Affero General Public
         
     | 
| 
      
 30 
     | 
    
         
            +
            # License, a covered work must retain the producer line in every PDF that
         
     | 
| 
      
 31 
     | 
    
         
            +
            # is created or manipulated using HexaPDF.
         
     | 
| 
      
 32 
     | 
    
         
            +
            #
         
     | 
| 
      
 33 
     | 
    
         
            +
            # If the GNU Affero General Public License doesn't fit your need,
         
     | 
| 
      
 34 
     | 
    
         
            +
            # commercial licenses are available at <https://gettalong.at/hexapdf/>.
         
     | 
| 
      
 35 
     | 
    
         
            +
            #++
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            require 'hexapdf/type/annotations'
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            module HexaPDF
         
     | 
| 
      
 40 
     | 
    
         
            +
              module Type
         
     | 
| 
      
 41 
     | 
    
         
            +
                module Annotations
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  # This is the base class for the square and circle markup annotations which display a
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # rectangle or ellipse inside the annotation rectangle.
         
     | 
| 
      
 45 
     | 
    
         
            +
                  #
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # The styling is done through methods included by various modules:
         
     | 
| 
      
 47 
     | 
    
         
            +
                  #
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # * Changing the line width, line dash pattern and color is done using the method
         
     | 
| 
      
 49 
     | 
    
         
            +
                  #   BorderStyling#border_style.  While that method allows special styling of the line (like
         
     | 
| 
      
 50 
     | 
    
         
            +
                  #   :beveled), only a simple line dash pattern is supported by the square and circle
         
     | 
| 
      
 51 
     | 
    
         
            +
                  #   annotations.
         
     | 
| 
      
 52 
     | 
    
         
            +
                  #
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # * The interior color can be changed through InteriorColor#interior_color.
         
     | 
| 
      
 54 
     | 
    
         
            +
                  #
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # * The border effect can be changed through BorderEffect#border_effect. Note that cloudy
         
     | 
| 
      
 56 
     | 
    
         
            +
                  #   borders are not supported.
         
     | 
| 
      
 57 
     | 
    
         
            +
                  #
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # See: PDF2.0 s12.5.6.8, HexaPDF::Type::Annotations::Square,
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # HexaPDF::Type::Annotations::Circle, HexaPDF::Type::MarkupAnnotation
         
     | 
| 
      
 60 
     | 
    
         
            +
                  class SquareCircle < MarkupAnnotation
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    include BorderStyling
         
     | 
| 
      
 63 
     | 
    
         
            +
                    include BorderEffect
         
     | 
| 
      
 64 
     | 
    
         
            +
                    include InteriorColor
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                    # Field Subtype is defined in the two subclasses
         
     | 
| 
      
 67 
     | 
    
         
            +
                    define_field :BS, type: :Border
         
     | 
| 
      
 68 
     | 
    
         
            +
                    define_field :IC, type: PDFArray, version: '1.4'
         
     | 
| 
      
 69 
     | 
    
         
            +
                    define_field :BE, type: :XXBorderEffect, version: '1.5'
         
     | 
| 
      
 70 
     | 
    
         
            +
                    # Array instead of Rectangle, see https://github.com/pdf-association/pdf-issues/issues/524
         
     | 
| 
      
 71 
     | 
    
         
            +
                    define_field :RD, type: PDFArray, version: '1.5'
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
              end
         
     | 
| 
      
 77 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -124,15 +124,21 @@ module HexaPDF 
     | 
|
| 
       124 
124 
     | 
    
         
             
                      end
         
     | 
| 
       125 
125 
     | 
    
         
             
                    end
         
     | 
| 
       126 
126 
     | 
    
         | 
| 
       127 
     | 
    
         
            -
                    # Describes the marker style of a check box or  
     | 
| 
      
 127 
     | 
    
         
            +
                    # Describes the marker style of a check box, radio button or push button widget.
         
     | 
| 
       128 
128 
     | 
    
         
             
                    class MarkerStyle
         
     | 
| 
       129 
129 
     | 
    
         | 
| 
       130 
     | 
    
         
            -
                      # The kind of marker that is shown inside the widget. 
     | 
| 
       131 
     | 
    
         
            -
                      # +:check+, +:circle+, +:cross+, +:diamond+, +:square+ or +:star+, or a one character
         
     | 
| 
       132 
     | 
    
         
            -
                      # string. The latter is interpreted using the ZapfDingbats font.
         
     | 
| 
      
 130 
     | 
    
         
            +
                      # The kind of marker that is shown inside the widget.
         
     | 
| 
       133 
131 
     | 
    
         
             
                      #
         
     | 
| 
       134 
     | 
    
         
            -
                      #  
     | 
| 
       135 
     | 
    
         
            -
                      #  
     | 
| 
      
 132 
     | 
    
         
            +
                      # Radion buttons and check boxes::
         
     | 
| 
      
 133 
     | 
    
         
            +
                      #     Can either be one of the symbols +:check+, +:circle+, +:cross+, +:diamond+,
         
     | 
| 
      
 134 
     | 
    
         
            +
                      #     +:square+ or +:star+, or a one character string. The latter is interpreted using the
         
     | 
| 
      
 135 
     | 
    
         
            +
                      #     ZapfDingbats font.
         
     | 
| 
      
 136 
     | 
    
         
            +
                      #
         
     | 
| 
      
 137 
     | 
    
         
            +
                      #     If an empty string is set, it is treated as if +nil+ was set, i.e. it shows the
         
     | 
| 
      
 138 
     | 
    
         
            +
                      #     default marker for the field type.
         
     | 
| 
      
 139 
     | 
    
         
            +
                      #
         
     | 
| 
      
 140 
     | 
    
         
            +
                      # Push buttons:
         
     | 
| 
      
 141 
     | 
    
         
            +
                      #     The caption string.
         
     | 
| 
       136 
142 
     | 
    
         
             
                      attr_reader :style
         
     | 
| 
       137 
143 
     | 
    
         | 
| 
       138 
144 
     | 
    
         
             
                      # The size of the marker in PDF points that is shown inside the widget. The special value
         
     | 
| 
         @@ -143,27 +149,40 @@ module HexaPDF 
     | 
|
| 
       143 
149 
     | 
    
         
             
                      # HexaPDF::Content::ColorSpace.
         
     | 
| 
       144 
150 
     | 
    
         
             
                      attr_reader :color
         
     | 
| 
       145 
151 
     | 
    
         | 
| 
      
 152 
     | 
    
         
            +
                      # The resource name of the font that should be used for the caption.
         
     | 
| 
      
 153 
     | 
    
         
            +
                      #
         
     | 
| 
      
 154 
     | 
    
         
            +
                      # This is only used for push button widgets.
         
     | 
| 
      
 155 
     | 
    
         
            +
                      attr_reader :font_name
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
       146 
157 
     | 
    
         
             
                      # Creates a new instance with the given values.
         
     | 
| 
       147 
     | 
    
         
            -
                      def initialize(style, size, color)
         
     | 
| 
      
 158 
     | 
    
         
            +
                      def initialize(style, size, color, font_name)
         
     | 
| 
       148 
159 
     | 
    
         
             
                        @style = style
         
     | 
| 
       149 
160 
     | 
    
         
             
                        @size = size
         
     | 
| 
       150 
161 
     | 
    
         
             
                        @color = color
         
     | 
| 
      
 162 
     | 
    
         
            +
                        @font_name = font_name
         
     | 
| 
       151 
163 
     | 
    
         
             
                      end
         
     | 
| 
       152 
164 
     | 
    
         | 
| 
       153 
165 
     | 
    
         
             
                    end
         
     | 
| 
       154 
166 
     | 
    
         | 
| 
       155 
167 
     | 
    
         
             
                    # :call-seq:
         
     | 
| 
       156 
     | 
    
         
            -
                    #   widget.marker_style 
     | 
| 
       157 
     | 
    
         
            -
                    #   widget.marker_style(style: nil, size: nil, color: nil)   => widget
         
     | 
| 
      
 168 
     | 
    
         
            +
                    #   widget.marker_style                                                      => marker_style
         
     | 
| 
      
 169 
     | 
    
         
            +
                    #   widget.marker_style(style: nil, size: nil, color: nil, font_name: nil)   => widget
         
     | 
| 
       158 
170 
     | 
    
         
             
                    #
         
     | 
| 
       159 
171 
     | 
    
         
             
                    # Returns a MarkerStyle instance representing the marker style of the widget when no
         
     | 
| 
       160 
172 
     | 
    
         
             
                    # argument is given. Otherwise sets the button marker style of the widget and returns self.
         
     | 
| 
       161 
173 
     | 
    
         
             
                    #
         
     | 
| 
       162 
     | 
    
         
            -
                    # This method returns valid information only for check boxes and  
     | 
| 
      
 174 
     | 
    
         
            +
                    # This method returns valid information only for check boxes, radio buttons and push buttons!
         
     | 
| 
       163 
175 
     | 
    
         
             
                    #
         
     | 
| 
       164 
     | 
    
         
            -
                    # When setting a marker style, arguments that are not provided will use the default: 
     | 
| 
       165 
     | 
    
         
            -
                    # 
     | 
| 
       166 
     | 
    
         
            -
                    #  
     | 
| 
      
 176 
     | 
    
         
            +
                    # When setting a marker style, arguments that are not provided will use the default:
         
     | 
| 
      
 177 
     | 
    
         
            +
                    #
         
     | 
| 
      
 178 
     | 
    
         
            +
                    # * For check boxes a black auto-sized checkmark (i.e. :check)
         
     | 
| 
      
 179 
     | 
    
         
            +
                    # * For radio buttons a black auto-sized circle (i.e. :circle)
         
     | 
| 
      
 180 
     | 
    
         
            +
                    # * For push buttons a black 9pt empty text using Helvetica
         
     | 
| 
      
 181 
     | 
    
         
            +
                    #
         
     | 
| 
      
 182 
     | 
    
         
            +
                    # This also means that multiple invocations will reset *all* prior values.
         
     | 
| 
      
 183 
     | 
    
         
            +
                    #
         
     | 
| 
      
 184 
     | 
    
         
            +
                    # Note that the +font_name+ argument must be a valid HexaPDF font name (this is in contrast
         
     | 
| 
      
 185 
     | 
    
         
            +
                    # to MarkerStyle#font_name which returns the resource name of the font).
         
     | 
| 
       167 
186 
     | 
    
         
             
                    #
         
     | 
| 
       168 
187 
     | 
    
         
             
                    # Note: The marker is called "normal caption" in the PDF 2.0 spec and the /CA entry of the
         
     | 
| 
       169 
188 
     | 
    
         
             
                    # associated appearance characteristics dictionary. The marker size and color are set using
         
     | 
| 
         @@ -171,13 +190,18 @@ module HexaPDF 
     | 
|
| 
       171 
190 
     | 
    
         
             
                    # does it).
         
     | 
| 
       172 
191 
     | 
    
         
             
                    #
         
     | 
| 
       173 
192 
     | 
    
         
             
                    # See: PDF2.0 s12.5.6.19 and s12.7.4.3
         
     | 
| 
       174 
     | 
    
         
            -
                    def marker_style(style: nil, size: nil, color: nil)
         
     | 
| 
      
 193 
     | 
    
         
            +
                    def marker_style(style: nil, size: nil, color: nil, font_name: nil)
         
     | 
| 
       175 
194 
     | 
    
         
             
                      field = form_field
         
     | 
| 
       176 
     | 
    
         
            -
                      if style || size || color
         
     | 
| 
       177 
     | 
    
         
            -
                        style ||=  
     | 
| 
       178 
     | 
    
         
            -
             
     | 
| 
      
 195 
     | 
    
         
            +
                      if style || size || color || font_name
         
     | 
| 
      
 196 
     | 
    
         
            +
                        style ||= case field.concrete_field_type
         
     | 
| 
      
 197 
     | 
    
         
            +
                                  when :check_box then :check
         
     | 
| 
      
 198 
     | 
    
         
            +
                                  when :radio_button then :circle
         
     | 
| 
      
 199 
     | 
    
         
            +
                                  when :push_button then ''
         
     | 
| 
      
 200 
     | 
    
         
            +
                                  end
         
     | 
| 
      
 201 
     | 
    
         
            +
                        size ||= (field.push_button? ? 9 : 0)
         
     | 
| 
       179 
202 
     | 
    
         
             
                        color = Content::ColorSpace.device_color_from_specification(color || 0)
         
     | 
| 
       180 
203 
     | 
    
         
             
                        serialized_color = Content::ColorSpace.serialize_device_color(color)
         
     | 
| 
      
 204 
     | 
    
         
            +
                        font_name ||= 'Helvetica'
         
     | 
| 
       181 
205 
     | 
    
         | 
| 
       182 
206 
     | 
    
         
             
                        self[:MK] ||= {}
         
     | 
| 
       183 
207 
     | 
    
         
             
                        self[:MK][:CA] = case style
         
     | 
| 
         @@ -191,7 +215,13 @@ module HexaPDF 
     | 
|
| 
       191 
215 
     | 
    
         
             
                                         else
         
     | 
| 
       192 
216 
     | 
    
         
             
                                           raise ArgumentError, "Unknown value #{style} for argument 'style'"
         
     | 
| 
       193 
217 
     | 
    
         
             
                                         end
         
     | 
| 
       194 
     | 
    
         
            -
                        self[:DA] =  
     | 
| 
      
 218 
     | 
    
         
            +
                        self[:DA] = if field.push_button?
         
     | 
| 
      
 219 
     | 
    
         
            +
                                      name = document.acro_form(create: true).default_resources.
         
     | 
| 
      
 220 
     | 
    
         
            +
                                        add_font(document.fonts.add(font_name).pdf_object)
         
     | 
| 
      
 221 
     | 
    
         
            +
                                      "/#{name} #{size} Tf #{serialized_color}".strip
         
     | 
| 
      
 222 
     | 
    
         
            +
                                    else
         
     | 
| 
      
 223 
     | 
    
         
            +
                                      "/ZaDb #{size} Tf #{serialized_color}".strip
         
     | 
| 
      
 224 
     | 
    
         
            +
                                    end
         
     | 
| 
       195 
225 
     | 
    
         
             
                      else
         
     | 
| 
       196 
226 
     | 
    
         
             
                        style = case self[:MK]&.[](:CA)
         
     | 
| 
       197 
227 
     | 
    
         
             
                                when '4' then :check
         
     | 
| 
         @@ -211,12 +241,12 @@ module HexaPDF 
     | 
|
| 
       211 
241 
     | 
    
         
             
                        size = 0
         
     | 
| 
       212 
242 
     | 
    
         
             
                        color = HexaPDF::Content::ColorSpace.prenormalized_device_color([0])
         
     | 
| 
       213 
243 
     | 
    
         
             
                        if (da = self[:DA] || field[:DA])
         
     | 
| 
       214 
     | 
    
         
            -
                           
     | 
| 
      
 244 
     | 
    
         
            +
                          font_name, da_size, da_color = AcroForm::VariableTextField.parse_appearance_string(da)
         
     | 
| 
       215 
245 
     | 
    
         
             
                          size = da_size || size
         
     | 
| 
       216 
246 
     | 
    
         
             
                          color = da_color || color
         
     | 
| 
       217 
247 
     | 
    
         
             
                        end
         
     | 
| 
       218 
248 
     | 
    
         | 
| 
       219 
     | 
    
         
            -
                        MarkerStyle.new(style, size, color)
         
     | 
| 
      
 249 
     | 
    
         
            +
                        MarkerStyle.new(style, size, color, font_name)
         
     | 
| 
       220 
250 
     | 
    
         
             
                      end
         
     | 
| 
       221 
251 
     | 
    
         
             
                    end
         
     | 
| 
       222 
252 
     | 
    
         | 
| 
         @@ -51,6 +51,11 @@ module HexaPDF 
     | 
|
| 
       51 
51 
     | 
    
         
             
                  autoload(:BorderStyling, 'hexapdf/type/annotations/border_styling')
         
     | 
| 
       52 
52 
     | 
    
         
             
                  autoload(:Line, 'hexapdf/type/annotations/line')
         
     | 
| 
       53 
53 
     | 
    
         
             
                  autoload(:AppearanceGenerator, 'hexapdf/type/annotations/appearance_generator')
         
     | 
| 
      
 54 
     | 
    
         
            +
                  autoload(:BorderEffect, 'hexapdf/type/annotations/border_effect')
         
     | 
| 
      
 55 
     | 
    
         
            +
                  autoload(:InteriorColor, 'hexapdf/type/annotations/interior_color')
         
     | 
| 
      
 56 
     | 
    
         
            +
                  autoload(:SquareCircle, 'hexapdf/type/annotations/square_circle')
         
     | 
| 
      
 57 
     | 
    
         
            +
                  autoload(:Square, 'hexapdf/type/annotations/square')
         
     | 
| 
      
 58 
     | 
    
         
            +
                  autoload(:Circle, 'hexapdf/type/annotations/circle')
         
     | 
| 
       54 
59 
     | 
    
         | 
| 
       55 
60 
     | 
    
         
             
                end
         
     | 
| 
       56 
61 
     | 
    
         | 
    
        data/lib/hexapdf/version.rb
    CHANGED
    
    
| 
         @@ -18,6 +18,8 @@ describe HexaPDF::Document::Annotations do 
     | 
|
| 
       18 
18 
     | 
    
         
             
                it "delegates to the actual create_TYPE implementation" do
         
     | 
| 
       19 
19 
     | 
    
         
             
                  annot = @annots.create(:line, @page, start_point: [0, 0], end_point: [10, 10])
         
     | 
| 
       20 
20 
     | 
    
         
             
                  assert_equal(:Line, annot[:Subtype])
         
     | 
| 
      
 21 
     | 
    
         
            +
                  annot = @annots.create(:rectangle, @page, 10, 20, 30, 40)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  assert_equal(:Square, annot[:Subtype])
         
     | 
| 
       21 
23 
     | 
    
         
             
                end
         
     | 
| 
       22 
24 
     | 
    
         
             
              end
         
     | 
| 
       23 
25 
     | 
    
         | 
| 
         @@ -30,4 +32,24 @@ describe HexaPDF::Document::Annotations do 
     | 
|
| 
       30 
32 
     | 
    
         
             
                  assert_equal(annot, @page[:Annots].first)
         
     | 
| 
       31 
33 
     | 
    
         
             
                end
         
     | 
| 
       32 
34 
     | 
    
         
             
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              describe "create_rectangle" do
         
     | 
| 
      
 37 
     | 
    
         
            +
                it "creates an appropriate square annotation object" do
         
     | 
| 
      
 38 
     | 
    
         
            +
                  annot = @annots.create(:rectangle, @page, 10, 20, 30, 40)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  assert_equal(:Annot, annot[:Type])
         
     | 
| 
      
 40 
     | 
    
         
            +
                  assert_equal(:Square, annot[:Subtype])
         
     | 
| 
      
 41 
     | 
    
         
            +
                  assert_equal([10, 20, 40, 60], annot[:Rect])
         
     | 
| 
      
 42 
     | 
    
         
            +
                  assert_equal(annot, @page[:Annots].first)
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              describe "create_ellipse" do
         
     | 
| 
      
 47 
     | 
    
         
            +
                it "creates an appropriate circle annotation object" do
         
     | 
| 
      
 48 
     | 
    
         
            +
                  annot = @annots.create(:ellipse, @page, 100, 100, a: 30, b: 40)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  assert_equal(:Annot, annot[:Type])
         
     | 
| 
      
 50 
     | 
    
         
            +
                  assert_equal(:Circle, annot[:Subtype])
         
     | 
| 
      
 51 
     | 
    
         
            +
                  assert_equal([70, 60, 130, 140], annot[:Rect])
         
     | 
| 
      
 52 
     | 
    
         
            +
                  assert_equal(annot, @page[:Annots].first)
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
       33 
55 
     | 
    
         
             
            end
         
     | 
| 
         @@ -146,6 +146,16 @@ describe HexaPDF::Document::Layout do 
     | 
|
| 
       146 
146 
     | 
    
         
             
                end
         
     | 
| 
       147 
147 
     | 
    
         
             
              end
         
     | 
| 
       148 
148 
     | 
    
         | 
| 
      
 149 
     | 
    
         
            +
              describe "style?" do
         
     | 
| 
      
 150 
     | 
    
         
            +
                it "returns true if a given style is defined" do
         
     | 
| 
      
 151 
     | 
    
         
            +
                  assert(@layout.style?(:base))
         
     | 
| 
      
 152 
     | 
    
         
            +
                end
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                it "returns false if a given style is not defined" do
         
     | 
| 
      
 155 
     | 
    
         
            +
                  refute(@layout.style?(:unknown))
         
     | 
| 
      
 156 
     | 
    
         
            +
                end
         
     | 
| 
      
 157 
     | 
    
         
            +
              end
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
       149 
159 
     | 
    
         
             
              describe "styles" do
         
     | 
| 
       150 
160 
     | 
    
         
             
                it "returns the existing styles" do
         
     | 
| 
       151 
161 
     | 
    
         
             
                  @layout.style(:test, font_size: 20)
         
     | 
| 
         @@ -168,6 +178,16 @@ describe HexaPDF::Document::Layout do 
     | 
|
| 
       168 
178 
     | 
    
         
             
                  assert_kind_of(HexaPDF::Font::Type1Wrapper, style.font)
         
     | 
| 
       169 
179 
     | 
    
         
             
                end
         
     | 
| 
       170 
180 
     | 
    
         | 
| 
      
 181 
     | 
    
         
            +
                it "uses the font_bold property when resolving a font name to a font wrapper" do
         
     | 
| 
      
 182 
     | 
    
         
            +
                  style = @layout.send(:retrieve_style, {font: 'Helvetica', font_bold: true})
         
     | 
| 
      
 183 
     | 
    
         
            +
                  assert_equal('Helvetica-Bold', style.font.wrapped_font.font_name)
         
     | 
| 
      
 184 
     | 
    
         
            +
                end
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                it "uses the font_italic property when resolving a font name to a font wrapper" do
         
     | 
| 
      
 187 
     | 
    
         
            +
                  style = @layout.send(:retrieve_style, {font: 'Helvetica', font_italic: true})
         
     | 
| 
      
 188 
     | 
    
         
            +
                  assert_equal('Helvetica-Oblique', style.font.wrapped_font.font_name)
         
     | 
| 
      
 189 
     | 
    
         
            +
                end
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
       171 
191 
     | 
    
         
             
                it "sets the :base style's font if no font is set" do
         
     | 
| 
       172 
192 
     | 
    
         
             
                  @layout.style(:base, font: 'Helvetica')
         
     | 
| 
       173 
193 
     | 
    
         
             
                  style = @layout.send(:retrieve_style, {})
         
     | 
| 
         @@ -203,7 +223,8 @@ describe HexaPDF::Document::Layout do 
     | 
|
| 
       203 
223 
     | 
    
         | 
| 
       204 
224 
     | 
    
         
             
              describe "box" do
         
     | 
| 
       205 
225 
     | 
    
         
             
                it "creates the request box" do
         
     | 
| 
       206 
     | 
    
         
            -
                  box = @layout.box(:column, columns: 3,  
     | 
| 
      
 226 
     | 
    
         
            +
                  box = @layout.box(:column, columns: 3, width: 15, height: 30,
         
     | 
| 
      
 227 
     | 
    
         
            +
                                    style: {font_size: 10, box_options: {gaps: 20}},
         
     | 
| 
       207 
228 
     | 
    
         
             
                                    properties: {key: :value})
         
     | 
| 
       208 
229 
     | 
    
         
             
                  assert_equal(15, box.width)
         
     | 
| 
       209 
230 
     | 
    
         
             
                  assert_equal(30, box.height)
         
     | 
| 
         @@ -431,9 +452,10 @@ describe HexaPDF::Document::Layout do 
     | 
|
| 
       431 
452 
     | 
    
         | 
| 
       432 
453 
     | 
    
         
             
              describe "table_box" do
         
     | 
| 
       433 
454 
     | 
    
         
             
                it "creates a table box" do
         
     | 
| 
       434 
     | 
    
         
            -
                  box = @layout.table_box([['m']],  
     | 
| 
      
 455 
     | 
    
         
            +
                  box = @layout.table_box([['m']], header: proc { [['a']] },
         
     | 
| 
       435 
456 
     | 
    
         
             
                                          footer: proc { [['b']] }, cell_style: {background_color: "red"},
         
     | 
| 
       436 
457 
     | 
    
         
             
                                          width: 100, height: 300, style: {background_color: "blue"},
         
     | 
| 
      
 458 
     | 
    
         
            +
                                          box_options: {column_widths: [100]},
         
     | 
| 
       437 
459 
     | 
    
         
             
                                          properties: {key: :value}, border: {width: 1})
         
     | 
| 
       438 
460 
     | 
    
         
             
                  assert_equal(100, box.width)
         
     | 
| 
       439 
461 
     | 
    
         
             
                  assert_equal(300, box.height)
         
     | 
| 
         @@ -27,6 +27,16 @@ describe HexaPDF::Font::TrueType::Subsetter do 
     | 
|
| 
       27 
27 
     | 
    
         
             
                assert_equal(value, @subsetter.subset_glyph_id(5))
         
     | 
| 
       28 
28 
     | 
    
         
             
              end
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
      
 30 
     | 
    
         
            +
              it "doesn't use certain subset glyph IDs for performance reasons" do
         
     | 
| 
      
 31 
     | 
    
         
            +
                1.upto(93) {|i| @subsetter.use_glyph(i) }
         
     | 
| 
      
 32 
     | 
    
         
            +
                # glyph 0, 93 used glyph, 4 special glyphs
         
     | 
| 
      
 33 
     | 
    
         
            +
                assert_equal(1 + 93 + 4, @subsetter.instance_variable_get(:@glyph_map).size)
         
     | 
| 
      
 34 
     | 
    
         
            +
                1.upto(12) {|i| assert_equal(i, @subsetter.subset_glyph_id(i), "id=#{i}") }
         
     | 
| 
      
 35 
     | 
    
         
            +
                13.upto(38) {|i| assert_equal(i + 1, @subsetter.subset_glyph_id(i), "id=#{i}") }
         
     | 
| 
      
 36 
     | 
    
         
            +
                39.upto(88) {|i| assert_equal(i + 3, @subsetter.subset_glyph_id(i), "id=#{i}") }
         
     | 
| 
      
 37 
     | 
    
         
            +
                89.upto(93) {|i| assert_equal(i + 4, @subsetter.subset_glyph_id(i), "id=#{i}") }
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
       30 
40 
     | 
    
         
             
              it "creates the subset font file" do
         
     | 
| 
       31 
41 
     | 
    
         
             
                gid = @font[:cmap].preferred_table[0x41]
         
     | 
| 
       32 
42 
     | 
    
         
             
                @subsetter.use_glyph(gid)
         
     | 
| 
         @@ -738,6 +738,30 @@ describe HexaPDF::Layout::Style do 
     | 
|
| 
       738 
738 
     | 
    
         
             
                end
         
     | 
| 
       739 
739 
     | 
    
         
             
              end
         
     | 
| 
       740 
740 
     | 
    
         | 
| 
      
 741 
     | 
    
         
            +
              describe "each_property" do
         
     | 
| 
      
 742 
     | 
    
         
            +
                it "yields all set properties with their values" do
         
     | 
| 
      
 743 
     | 
    
         
            +
                  @style.font_size = 5
         
     | 
| 
      
 744 
     | 
    
         
            +
                  @style.line_spacing = 1.2
         
     | 
| 
      
 745 
     | 
    
         
            +
                  assert_equal(0.005, @style.scaled_font_size)
         
     | 
| 
      
 746 
     | 
    
         
            +
                  assert_equal([[:font, @style.font], [:font_size, 5], [:horizontal_scaling, 100],
         
     | 
| 
      
 747 
     | 
    
         
            +
                                [:line_spacing, @style.line_spacing], [:subscript, false], [:superscript, false]],
         
     | 
| 
      
 748 
     | 
    
         
            +
                               @style.each_property.to_a.sort)
         
     | 
| 
      
 749 
     | 
    
         
            +
                end
         
     | 
| 
      
 750 
     | 
    
         
            +
              end
         
     | 
| 
      
 751 
     | 
    
         
            +
             
     | 
| 
      
 752 
     | 
    
         
            +
              describe "merge" do
         
     | 
| 
      
 753 
     | 
    
         
            +
                it "merges all set properties" do
         
     | 
| 
      
 754 
     | 
    
         
            +
                  @style.font_size = 5
         
     | 
| 
      
 755 
     | 
    
         
            +
                  @style.line_spacing = 1.2
         
     | 
| 
      
 756 
     | 
    
         
            +
                  new_style = HexaPDF::Layout::Style.new
         
     | 
| 
      
 757 
     | 
    
         
            +
                  new_style.update(font_size: 3, line_spacing: {type: :fixed, value: 2.5})
         
     | 
| 
      
 758 
     | 
    
         
            +
                  new_style.merge(@style)
         
     | 
| 
      
 759 
     | 
    
         
            +
                  assert_equal(5, new_style.font_size)
         
     | 
| 
      
 760 
     | 
    
         
            +
                  assert_equal(:proportional, new_style.line_spacing.type)
         
     | 
| 
      
 761 
     | 
    
         
            +
                  assert_equal(1.2, new_style.line_spacing.value)
         
     | 
| 
      
 762 
     | 
    
         
            +
                end
         
     | 
| 
      
 763 
     | 
    
         
            +
              end
         
     | 
| 
      
 764 
     | 
    
         
            +
             
     | 
| 
       741 
765 
     | 
    
         
             
              it "has several simple and dynamically generated properties with default values" do
         
     | 
| 
       742 
766 
     | 
    
         
             
                @style = HexaPDF::Layout::Style.new
         
     | 
| 
       743 
767 
     | 
    
         
             
                assert_raises(HexaPDF::Error) { @style.font }
         
     | 
| 
         @@ -780,6 +804,7 @@ describe HexaPDF::Layout::Style do 
     | 
|
| 
       780 
804 
     | 
    
         
             
                assert_equal(:left, @style.align)
         
     | 
| 
       781 
805 
     | 
    
         
             
                assert_equal(:top, @style.valign)
         
     | 
| 
       782 
806 
     | 
    
         
             
                assert_equal(:default, @style.mask_mode)
         
     | 
| 
      
 807 
     | 
    
         
            +
                assert_equal({}, @style.box_options)
         
     | 
| 
       783 
808 
     | 
    
         
             
              end
         
     | 
| 
       784 
809 
     | 
    
         | 
| 
       785 
810 
     | 
    
         
             
              it "allows using a non-standard setter for generated properties" do
         
     | 
| 
         @@ -790,8 +815,8 @@ describe HexaPDF::Layout::Style do 
     | 
|
| 
       790 
815 
     | 
    
         
             
                @style.stroke_dash_pattern(5, 2)
         
     | 
| 
       791 
816 
     | 
    
         
             
                assert_equal([[5], 2], @style.stroke_dash_pattern.to_operands)
         
     | 
| 
       792 
817 
     | 
    
         | 
| 
       793 
     | 
    
         
            -
                @style.line_spacing( 
     | 
| 
       794 
     | 
    
         
            -
                assert_equal([:proportional,  
     | 
| 
      
 818 
     | 
    
         
            +
                @style.line_spacing(HexaPDF::Layout::Style::LineSpacing.new(type: :double))
         
     | 
| 
      
 819 
     | 
    
         
            +
                assert_equal([:proportional, 2], [@style.line_spacing.type, @style.line_spacing.value])
         
     | 
| 
       795 
820 
     | 
    
         
             
              end
         
     | 
| 
       796 
821 
     | 
    
         | 
| 
       797 
822 
     | 
    
         
             
              it "allows checking for valid values" do
         
     | 
| 
         @@ -127,6 +127,13 @@ describe HexaPDF::Composer do 
     | 
|
| 
       127 
127 
     | 
    
         
             
                end
         
     | 
| 
       128 
128 
     | 
    
         
             
              end
         
     | 
| 
       129 
129 
     | 
    
         | 
| 
      
 130 
     | 
    
         
            +
              describe "style?" do
         
     | 
| 
      
 131 
     | 
    
         
            +
                it "delegates to layout.style?" do
         
     | 
| 
      
 132 
     | 
    
         
            +
                  @composer.document.layout.style(:header, font_size: 20)
         
     | 
| 
      
 133 
     | 
    
         
            +
                  assert(@composer.style?(:header))
         
     | 
| 
      
 134 
     | 
    
         
            +
                end
         
     | 
| 
      
 135 
     | 
    
         
            +
              end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
       130 
137 
     | 
    
         
             
              describe "styles" do
         
     | 
| 
       131 
138 
     | 
    
         
             
                it "delegates to layout.styles" do
         
     | 
| 
       132 
139 
     | 
    
         
             
                  @composer.styles(base: {font_size: 30}, other: {font_size: 40})
         
     | 
    
        data/test/hexapdf/test_object.rb
    CHANGED
    
    | 
         @@ -197,7 +197,7 @@ describe HexaPDF::Object do 
     | 
|
| 
       197 
197 
     | 
    
         
             
                  @obj.define_singleton_method(:perform_validation) { raise "Unknown" }
         
     | 
| 
       198 
198 
     | 
    
         
             
                  invoked = []
         
     | 
| 
       199 
199 
     | 
    
         
             
                  refute(@obj.validate {|*a| invoked << a })
         
     | 
| 
       200 
     | 
    
         
            -
                  assert_equal([[" 
     | 
| 
      
 200 
     | 
    
         
            +
                  assert_equal([["Unexpected error encountered: Unknown", false, @obj]], invoked)
         
     | 
| 
       201 
201 
     | 
    
         
             
                end
         
     | 
| 
       202 
202 
     | 
    
         
             
              end
         
     | 
| 
       203 
203 
     | 
    
         | 
| 
         @@ -131,9 +131,42 @@ describe HexaPDF::PDFArray do 
     | 
|
| 
       131 
131 
     | 
    
         
             
                end
         
     | 
| 
       132 
132 
     | 
    
         
             
              end
         
     | 
| 
       133 
133 
     | 
    
         | 
| 
       134 
     | 
    
         
            -
               
     | 
| 
       135 
     | 
    
         
            -
                 
     | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
      
 134 
     | 
    
         
            +
              describe "reject!" do
         
     | 
| 
      
 135 
     | 
    
         
            +
                it "allows deleting elements that are selected using a block" do
         
     | 
| 
      
 136 
     | 
    
         
            +
                  assert_same(@array, @array.reject! {|item| item == :data })
         
     | 
| 
      
 137 
     | 
    
         
            +
                  assert_equal([1, "deref", @array[2]], @array.to_a)
         
     | 
| 
      
 138 
     | 
    
         
            +
                end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                it "returns nil if no elements were deleted" do
         
     | 
| 
      
 141 
     | 
    
         
            +
                  assert_nil(@array.reject! {|item| false })
         
     | 
| 
      
 142 
     | 
    
         
            +
                end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                it "returns an enumerator if no block is given" do
         
     | 
| 
      
 145 
     | 
    
         
            +
                  assert_kind_of(Enumerator, @array.reject!)
         
     | 
| 
      
 146 
     | 
    
         
            +
                end
         
     | 
| 
      
 147 
     | 
    
         
            +
              end
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
              describe "map!" do
         
     | 
| 
      
 150 
     | 
    
         
            +
                it "maps elements in-place to the return values of the block" do
         
     | 
| 
      
 151 
     | 
    
         
            +
                  assert_same(@array, @array.map! {|item| 5 })
         
     | 
| 
      
 152 
     | 
    
         
            +
                  assert_equal([5, 5, 5, 5], @array.to_a)
         
     | 
| 
      
 153 
     | 
    
         
            +
                end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                it "returns an enumerator if no block is given" do
         
     | 
| 
      
 156 
     | 
    
         
            +
                  assert_kind_of(Enumerator, @array.reject!)
         
     | 
| 
      
 157 
     | 
    
         
            +
                end
         
     | 
| 
      
 158 
     | 
    
         
            +
              end
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
              describe "compact!" do
         
     | 
| 
      
 161 
     | 
    
         
            +
                it "removes all nil elements and returns self" do
         
     | 
| 
      
 162 
     | 
    
         
            +
                  @array << nil
         
     | 
| 
      
 163 
     | 
    
         
            +
                  assert_same(@array, @array.compact!)
         
     | 
| 
      
 164 
     | 
    
         
            +
                  assert_equal(4, @array.size)
         
     | 
| 
      
 165 
     | 
    
         
            +
                end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                it "returns nil if no elements were removed" do
         
     | 
| 
      
 168 
     | 
    
         
            +
                  assert_nil(@array.compact!)
         
     | 
| 
      
 169 
     | 
    
         
            +
                end
         
     | 
| 
       137 
170 
     | 
    
         
             
              end
         
     | 
| 
       138 
171 
     | 
    
         | 
| 
       139 
172 
     | 
    
         
             
              describe "index" do
         
     | 
| 
         @@ -373,12 +373,87 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do 
     | 
|
| 
       373 
373 
     | 
    
         
             
                describe "push buttons" do
         
     | 
| 
       374 
374 
     | 
    
         
             
                  before do
         
     | 
| 
       375 
375 
     | 
    
         
             
                    @field.initialize_as_push_button
         
     | 
| 
       376 
     | 
    
         
            -
                    @widget = @field.create_widget(@page, Rect: [0, 0,  
     | 
| 
      
 376 
     | 
    
         
            +
                    @widget = @field.create_widget(@page, Rect: [0, 0, 100, 50])
         
     | 
| 
      
 377 
     | 
    
         
            +
                    @widget.marker_style(style: 'Test')
         
     | 
| 
       377 
378 
     | 
    
         
             
                    @generator = HexaPDF::Type::AcroForm::AppearanceGenerator.new(@widget)
         
     | 
| 
       378 
379 
     | 
    
         
             
                  end
         
     | 
| 
       379 
380 
     | 
    
         | 
| 
       380 
     | 
    
         
            -
                  it " 
     | 
| 
       381 
     | 
    
         
            -
                     
     | 
| 
      
 381 
     | 
    
         
            +
                  it "set the print flag on the widgets" do
         
     | 
| 
      
 382 
     | 
    
         
            +
                    @generator.create_appearances
         
     | 
| 
      
 383 
     | 
    
         
            +
                    assert(@widget.flagged?(:print))
         
     | 
| 
      
 384 
     | 
    
         
            +
                  end
         
     | 
| 
      
 385 
     | 
    
         
            +
             
     | 
| 
      
 386 
     | 
    
         
            +
                  it "removes the hidden flag on the widgets" do
         
     | 
| 
      
 387 
     | 
    
         
            +
                    @widget.flag(:hidden)
         
     | 
| 
      
 388 
     | 
    
         
            +
                    @generator.create_appearances
         
     | 
| 
      
 389 
     | 
    
         
            +
                    refute(@widget.flagged?(:hidden))
         
     | 
| 
      
 390 
     | 
    
         
            +
                  end
         
     | 
| 
      
 391 
     | 
    
         
            +
             
     | 
| 
      
 392 
     | 
    
         
            +
                  it "adds an appropriate form XObject" do
         
     | 
| 
      
 393 
     | 
    
         
            +
                    @generator.create_appearances
         
     | 
| 
      
 394 
     | 
    
         
            +
                    form = @widget[:AP][:N]
         
     | 
| 
      
 395 
     | 
    
         
            +
                    assert_equal(:XObject, form.type)
         
     | 
| 
      
 396 
     | 
    
         
            +
                    assert_equal(:Form, form[:Subtype])
         
     | 
| 
      
 397 
     | 
    
         
            +
                    assert_equal([0, 0, 100, 50], form[:BBox])
         
     | 
| 
      
 398 
     | 
    
         
            +
                    assert_equal(@doc.acro_form.default_resources[:Font][:F1], form[:Resources][:Font][:F1])
         
     | 
| 
      
 399 
     | 
    
         
            +
                  end
         
     | 
| 
      
 400 
     | 
    
         
            +
             
     | 
| 
      
 401 
     | 
    
         
            +
                  it "re-uses the existing form XObject" do
         
     | 
| 
      
 402 
     | 
    
         
            +
                    @generator.create_appearances
         
     | 
| 
      
 403 
     | 
    
         
            +
                    form = @widget[:AP][:N]
         
     | 
| 
      
 404 
     | 
    
         
            +
                    form[:key] = :value
         
     | 
| 
      
 405 
     | 
    
         
            +
                    form.delete(:Subtype)
         
     | 
| 
      
 406 
     | 
    
         
            +
                    @widget[:AP][:N] = @doc.wrap(form, type: HexaPDF::Dictionary)
         
     | 
| 
      
 407 
     | 
    
         
            +
             
     | 
| 
      
 408 
     | 
    
         
            +
                    @generator.create_appearances
         
     | 
| 
      
 409 
     | 
    
         
            +
                    assert_equal(form, @widget[:AP][:N])
         
     | 
| 
      
 410 
     | 
    
         
            +
                    refute(form.key?(:key))
         
     | 
| 
      
 411 
     | 
    
         
            +
                  end
         
     | 
| 
      
 412 
     | 
    
         
            +
             
     | 
| 
      
 413 
     | 
    
         
            +
                  describe "takes the rotation into account" do
         
     | 
| 
      
 414 
     | 
    
         
            +
                    def check_rotation(angle, width, height, matrix)
         
     | 
| 
      
 415 
     | 
    
         
            +
                      @widget[:MK][:R] = angle
         
     | 
| 
      
 416 
     | 
    
         
            +
                      @generator.create_appearances
         
     | 
| 
      
 417 
     | 
    
         
            +
                      form = @widget[:AP][:N]
         
     | 
| 
      
 418 
     | 
    
         
            +
                      assert_equal([0, 0, width, height], form[:BBox].value)
         
     | 
| 
      
 419 
     | 
    
         
            +
                      assert_equal(matrix, form[:Matrix].value)
         
     | 
| 
      
 420 
     | 
    
         
            +
                    end
         
     | 
| 
      
 421 
     | 
    
         
            +
             
     | 
| 
      
 422 
     | 
    
         
            +
                    it "works for 0 degrees" do
         
     | 
| 
      
 423 
     | 
    
         
            +
                      check_rotation(-360, @widget[:Rect].width, @widget[:Rect].height, [1, 0, 0, 1, 0, 0])
         
     | 
| 
      
 424 
     | 
    
         
            +
                    end
         
     | 
| 
      
 425 
     | 
    
         
            +
             
     | 
| 
      
 426 
     | 
    
         
            +
                    it "works for 90 degrees" do
         
     | 
| 
      
 427 
     | 
    
         
            +
                      check_rotation(450, @widget[:Rect].height, @widget[:Rect].width, [0, 1, -1, 0, 0, 0])
         
     | 
| 
      
 428 
     | 
    
         
            +
                    end
         
     | 
| 
      
 429 
     | 
    
         
            +
             
     | 
| 
      
 430 
     | 
    
         
            +
                    it "works for 180 degrees" do
         
     | 
| 
      
 431 
     | 
    
         
            +
                      check_rotation(180, @widget[:Rect].width, @widget[:Rect].height, [0, -1, -1, 0, 0, 0])
         
     | 
| 
      
 432 
     | 
    
         
            +
                    end
         
     | 
| 
      
 433 
     | 
    
         
            +
             
     | 
| 
      
 434 
     | 
    
         
            +
                    it "works for 270 degrees" do
         
     | 
| 
      
 435 
     | 
    
         
            +
                      check_rotation(-90, @widget[:Rect].height, @widget[:Rect].width, [0, -1, 1, 0, 0, 0])
         
     | 
| 
      
 436 
     | 
    
         
            +
                    end
         
     | 
| 
      
 437 
     | 
    
         
            +
                  end
         
     | 
| 
      
 438 
     | 
    
         
            +
             
     | 
| 
      
 439 
     | 
    
         
            +
                  it "adds the button title in the center" do
         
     | 
| 
      
 440 
     | 
    
         
            +
                    @generator.create_appearances
         
     | 
| 
      
 441 
     | 
    
         
            +
                    assert_operators(@widget[:AP][:N].stream,
         
     | 
| 
      
 442 
     | 
    
         
            +
                                     [[:save_graphics_state],
         
     | 
| 
      
 443 
     | 
    
         
            +
                                      [:set_device_gray_non_stroking_color, [0.5]],
         
     | 
| 
      
 444 
     | 
    
         
            +
                                      [:append_rectangle, [0, 0, 100, 50]],
         
     | 
| 
      
 445 
     | 
    
         
            +
                                      [:fill_path_non_zero],
         
     | 
| 
      
 446 
     | 
    
         
            +
                                      [:append_rectangle, [0.5, 0.5, 99.0, 49.0]],
         
     | 
| 
      
 447 
     | 
    
         
            +
                                      [:stroke_path],
         
     | 
| 
      
 448 
     | 
    
         
            +
                                      [:restore_graphics_state],
         
     | 
| 
      
 449 
     | 
    
         
            +
                                      [:save_graphics_state],
         
     | 
| 
      
 450 
     | 
    
         
            +
                                      [:set_font_and_size, [:F1, 9]],
         
     | 
| 
      
 451 
     | 
    
         
            +
                                      [:begin_text],
         
     | 
| 
      
 452 
     | 
    
         
            +
                                      [:move_text, [41.2475, 22.7005]],
         
     | 
| 
      
 453 
     | 
    
         
            +
                                      [:show_text, ["Test"]],
         
     | 
| 
      
 454 
     | 
    
         
            +
                                      [:end_text],
         
     | 
| 
      
 455 
     | 
    
         
            +
                                      [:restore_graphics_state]],
         
     | 
| 
      
 456 
     | 
    
         
            +
                                     )
         
     | 
| 
       382 
457 
     | 
    
         
             
                  end
         
     | 
| 
       383 
458 
     | 
    
         
             
                end
         
     | 
| 
       384 
459 
     | 
    
         
             
              end
         
     | 
| 
         @@ -236,6 +236,13 @@ describe HexaPDF::Type::AcroForm::ButtonField do 
     | 
|
| 
       236 
236 
     | 
    
         
             
                  assert(widget[:AP][:N][:test])
         
     | 
| 
       237 
237 
     | 
    
         
             
                end
         
     | 
| 
       238 
238 
     | 
    
         | 
| 
      
 239 
     | 
    
         
            +
                it "works for push buttons" do
         
     | 
| 
      
 240 
     | 
    
         
            +
                  @field.initialize_as_push_button
         
     | 
| 
      
 241 
     | 
    
         
            +
                  @field.create_widget(@doc.pages.add, Rect: [0, 0, 100, 50])
         
     | 
| 
      
 242 
     | 
    
         
            +
                  @field.create_appearances
         
     | 
| 
      
 243 
     | 
    
         
            +
                  assert(@field[:AP][:N])
         
     | 
| 
      
 244 
     | 
    
         
            +
                end
         
     | 
| 
      
 245 
     | 
    
         
            +
             
     | 
| 
       239 
246 
     | 
    
         
             
                it "won't generate appearances if they already exist" do
         
     | 
| 
       240 
247 
     | 
    
         
             
                  widget = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
         
     | 
| 
       241 
248 
     | 
    
         
             
                  @field.create_appearances
         
     | 
| 
         @@ -262,12 +269,6 @@ describe HexaPDF::Type::AcroForm::ButtonField do 
     | 
|
| 
       262 
269 
     | 
    
         
             
                  refute_same(yes, widget.appearance_dict.normal_appearance[:Yes])
         
     | 
| 
       263 
270 
     | 
    
         
             
                end
         
     | 
| 
       264 
271 
     | 
    
         | 
| 
       265 
     | 
    
         
            -
                it "fails for push buttons as they are not implemented yet" do
         
     | 
| 
       266 
     | 
    
         
            -
                  @field.flag(:push_button)
         
     | 
| 
       267 
     | 
    
         
            -
                  @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
         
     | 
| 
       268 
     | 
    
         
            -
                  assert_raises(HexaPDF::Error) { @field.create_appearances }
         
     | 
| 
       269 
     | 
    
         
            -
                end
         
     | 
| 
       270 
     | 
    
         
            -
             
     | 
| 
       271 
272 
     | 
    
         
             
                it "uses the configuration option acro_form.appearance_generator" do
         
     | 
| 
       272 
273 
     | 
    
         
             
                  @doc.config['acro_form.appearance_generator'] = 'NonExistent'
         
     | 
| 
       273 
274 
     | 
    
         
             
                  assert_raises(Exception) { @field.create_appearances }
         
     | 
| 
         @@ -193,9 +193,14 @@ describe HexaPDF::Type::AcroForm::Field do 
     | 
|
| 
       193 
193 
     | 
    
         | 
| 
       194 
194 
     | 
    
         
             
                it "extracts an embedded widget into a standalone object if necessary" do
         
     | 
| 
       195 
195 
     | 
    
         
             
                  widget1 = @field.create_widget(@page, Rect: [1, 2, 3, 4])
         
     | 
| 
      
 196 
     | 
    
         
            +
                  # Make sure that the field/widget looks like as if it has been loaded from a file
         
     | 
| 
      
 197 
     | 
    
         
            +
                  @doc.revisions.current.update(widget1)
         
     | 
| 
      
 198 
     | 
    
         
            +
                  assert_equal(@field, widget1)
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
       196 
200 
     | 
    
         
             
                  widget2 = @field.create_widget(@doc.pages.add, Rect: [2, 1, 4, 3])
         
     | 
| 
       197 
201 
     | 
    
         
             
                  kids = @field[:Kids]
         
     | 
| 
       198 
202 
     | 
    
         | 
| 
      
 203 
     | 
    
         
            +
                  assert_kind_of(HexaPDF::Type::AcroForm::Field, @doc.object(@field.oid))
         
     | 
| 
       199 
204 
     | 
    
         
             
                  assert_equal(2, kids.length)
         
     | 
| 
       200 
205 
     | 
    
         
             
                  refute_same(widget1, kids[0])
         
     | 
| 
       201 
206 
     | 
    
         
             
                  assert_same(widget2, kids[1])
         
     | 
| 
         @@ -558,13 +558,23 @@ describe HexaPDF::Type::AcroForm::Form do 
     | 
|
| 
       558 
558 
     | 
    
         
             
                    assert_equal(:Tx4, @acro_form[:Fields][2][:Kids][0][:T])
         
     | 
| 
       559 
559 
     | 
    
         
             
                    assert_equal(@acro_form[:Fields][2], @acro_form[:Fields][2][:Kids][0][:Parent])
         
     | 
| 
       560 
560 
     | 
    
         
             
                  end
         
     | 
| 
      
 561 
     | 
    
         
            +
             
     | 
| 
      
 562 
     | 
    
         
            +
                  it "ensures that objects loaded as widget are stored as field" do
         
     | 
| 
      
 563 
     | 
    
         
            +
                    @acro_form[:Fields][2] = @doc.add({T: :WidgetField, Type: :Annot, Subtype: :Widget})
         
     | 
| 
      
 564 
     | 
    
         
            +
                    assert_kind_of(HexaPDF::Type::Annotations::Widget, @acro_form[:Fields][2])
         
     | 
| 
      
 565 
     | 
    
         
            +
             
     | 
| 
      
 566 
     | 
    
         
            +
                    assert(@acro_form.validate)
         
     | 
| 
      
 567 
     | 
    
         
            +
                    field = @acro_form[:Fields][0]
         
     | 
| 
      
 568 
     | 
    
         
            +
                    assert_kind_of(HexaPDF::Type::AcroForm::Field, field)
         
     | 
| 
      
 569 
     | 
    
         
            +
                    assert_equal(:WidgetField, field.full_field_name)
         
     | 
| 
      
 570 
     | 
    
         
            +
                  end
         
     | 
| 
       561 
571 
     | 
    
         
             
                end
         
     | 
| 
       562 
572 
     | 
    
         | 
| 
       563 
573 
     | 
    
         
             
                describe "combining fields with the same name" do
         
     | 
| 
       564 
574 
     | 
    
         
             
                  before do
         
     | 
| 
       565 
575 
     | 
    
         
             
                    @acro_form[:Fields] = [
         
     | 
| 
       566 
576 
     | 
    
         
             
                      @doc.add({T: 'e', Subtype: :Widget, Rect: [0, 0, 0, 1]}),
         
     | 
| 
       567 
     | 
    
         
            -
                      @doc.add({T: 'e', Subtype: :Widget, Rect: [0, 0, 0, 2]}),
         
     | 
| 
      
 577 
     | 
    
         
            +
                      @merged_field = @doc.add({T: 'e', Subtype: :Widget, Rect: [0, 0, 0, 2]}),
         
     | 
| 
       568 
578 
     | 
    
         
             
                      @doc.add({T: 'Tx2'}),
         
     | 
| 
       569 
579 
     | 
    
         
             
                      @doc.add({T: 'e', Kids: [{Subtype: :Widget, Rect: [0, 0, 0, 3]}]}),
         
     | 
| 
       570 
580 
     | 
    
         
             
                    ]
         
     | 
| 
         @@ -576,6 +586,12 @@ describe HexaPDF::Type::AcroForm::Form do 
     | 
|
| 
       576 
586 
     | 
    
         
             
                    assert_equal([[0, 0, 0, 1], [0, 0, 0, 2], [0, 0, 0, 3]],
         
     | 
| 
       577 
587 
     | 
    
         
             
                                 @acro_form.field_by_name('e').each_widget.map {|w| w[:Rect] })
         
     | 
| 
       578 
588 
     | 
    
         
             
                  end
         
     | 
| 
      
 589 
     | 
    
         
            +
             
     | 
| 
      
 590 
     | 
    
         
            +
                  it "deletes the combined and now unneeded field objects" do
         
     | 
| 
      
 591 
     | 
    
         
            +
                    assert(@acro_form.validate)
         
     | 
| 
      
 592 
     | 
    
         
            +
                    assert(@merged_field.null?)
         
     | 
| 
      
 593 
     | 
    
         
            +
                    assert(@doc.object(@merged_field.oid).null?)
         
     | 
| 
      
 594 
     | 
    
         
            +
                  end
         
     | 
| 
       579 
595 
     | 
    
         
             
                end
         
     | 
| 
       580 
596 
     | 
    
         | 
| 
       581 
597 
     | 
    
         
             
                describe "automatically creates the terminal fields; appearances" do
         
     |