# -*- 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-2025 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. # # If the GNU Affero General Public License doesn't fit your need, # commercial licenses are available at . #++ require 'hexapdf/dictionary' require 'hexapdf/stream' require 'hexapdf/error' require 'hexapdf/content' module HexaPDF module Type module AcroForm # An AcroForm variable text field defines how text that it is not known at generation time # should be rendered. For example, AcroForm text fields (normally) don't have an initial # value; the value is entered by the user and needs to be rendered correctly by the PDF # reader. # # See: PDF2.0 s12.7.4.3 class VariableTextField < Field define_field :DA, type: PDFByteString define_field :Q, type: Integer, default: 0, allowed_values: [0, 1, 2] define_field :DS, type: String, version: '1.5' define_field :RV, type: [String, Stream], version: '1.5' # All inheritable dictionary fields for text fields. INHERITABLE_FIELDS = (superclass::INHERITABLE_FIELDS + [:DA, :Q]).freeze UNSET_ARG = ::Object.new # :nodoc: # Creates an AcroForm appearance string for the HexaPDF +document+ from the given arguments # and returns it. # # +font+:: # The name of the font. # # +font_options+:: # Additional font options like :variant used when loading the font. See # HexaPDF::Document::Fonts#add # # +font_size+:: # The font size. If this is set to 0, the font size is calculated using the height/width # of the field. # # +font_color+:: # The font color. See HexaPDF::Content::ColorSpace.device_color_from_specification for # allowed values. def self.create_appearance_string(document, font: 'Helvetica', font_options: {}, font_size: 0, font_color: 0) name = document.acro_form(create: true).default_resources. add_font(document.fonts.add(font, **font_options).pdf_object) font_color = HexaPDF::Content::ColorSpace.device_color_from_specification(font_color) color_string = HexaPDF::Content::ColorSpace.serialize_device_color(font_color) "#{color_string.chomp} /#{name} #{font_size} Tf" end # :call-seq: # VariableTextField.parse_appearance_string(string) -> [font_name, font_size, font_color] # VariableTextField.parse_appearance_string(string) {|obj, params| block } -> nil # # Parses the given appearance string. # # If no block is given, the appearance string is searched for font name, font size and font # color all of which are returned. Otherwise the block is called with each found content # stream operator and has to handle them itself. def self.parse_appearance_string(appearance_string, &block) # :yield: obj, params font_params = [nil, nil, nil] block ||= lambda do |obj, params| case obj when :Tf font_params[0, 2] = params when :rg, :g, :k font_params[2] = HexaPDF::Content::ColorSpace.prenormalized_device_color(params) end end HexaPDF::Content::Parser.parse(appearance_string.to_s.sub(/\/\//, '/'), &block) block_given? ? nil : font_params end # :call-seq: # field.text_alignment -> alignment # field.text_alignment(alignment) -> field # # Sets or returns the text alignment that should be used when displaying text. # # With no argument, the current text alignment is returned. When a value is provided, the # text alignment is set accordingly. # # The alignment value is one of :left, :center or :right. def text_alignment(alignment = UNSET_ARG) if alignment == UNSET_ARG case self[:Q] when 0 then :left when 1 then :center when 2 then :right end else self[:Q] = case alignment when :left then 0 when :center then 1 when :right then 2 else raise ArgumentError, "Invalid variable text field alignment #{alignment}" end end end # Sets the default appearance string using the provided values or the default values which # provide a sane default. # # See ::create_appearance_string for information on the arguments. def set_default_appearance_string(font: 'Helvetica', font_options: {}, font_size: 0, font_color: 0) self[:DA] = self.class.create_appearance_string(document, font: font, font_options: font_options, font_size: font_size, font_color: font_color) end # Parses the default appearance string and returns an array containing [font_name, # font_size, font_color]. # # The default appearance string is taken from the given +widget+ of the field, falls back to # the field itself and then the default appearance string of the form. If it still not # available, a standard default appearance string is set (see # #set_default_appearance_string) and used. # # The reason why a specific widget of the field can be specified is because the widgets of a # field might differ in their visual representation. def parse_default_appearance_string(widget = self) da = widget[:DA] || self[:DA] || (document.acro_form && document.acro_form[:DA]) unless da if (args = document.config['acro_form.fallback_default_appearance']) da = set_default_appearance_string(**args) else raise HexaPDF::Error, "No default appearance string set" end end self.class.parse_appearance_string(da) end end end end end