Class: HexaPDF::Layout::Style

Inherits:
Object
  • Object
show all
Defined in:
lib/hexapdf/layout/style.rb

Overview

A Style is a container for properties that describe the appearance of text or graphics.

Each property except #font has a default value, so only the desired properties need to be changed.

Each property has three associated methods:

property_name

Getter method.

property_name(*args) and property_name=

Setter method.

property_name?

Tester method to see if a value has been set or if the default value has already been used.

Defined Under Namespace

Classes: Border, Layers, LineSpacing, LinkLayer, Quad

Constant Summary collapse

UNSET =

:nodoc:

::Object.new

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**properties) ⇒ Style

Creates a new Style object.

The properties hash may be used to set the initial values of properties by using keys equivalent to the property names.

Example:

Style.new(font_size: 15, align: :center, valign: center)


549
550
551
552
# File 'lib/hexapdf/layout/style.rb', line 549

def initialize(**properties)
  update(**properties)
  @scaled_item_widths = {}.compare_by_identity
end

Class Method Details

.create(style) ⇒ Object

:call-seq:

Style.create(style)     -> style
Style.create(properties_hash)   -> style

Creates a Style object based on the style argument and returns it:

  • If style is already a Style object, it is just returned.

  • If style is a hash, a new Style object with the style properties specified by the hash

  • is created.

  • If style is nil, a new Style object with only default values is created.



533
534
535
536
537
538
539
540
# File 'lib/hexapdf/layout/style.rb', line 533

def self.create(style)
  case style
  when self then style
  when Hash then new(**style)
  when nil then new
  else raise ArgumentError, "Invalid argument class #{style.class}"
  end
end

Instance Method Details

#calculated_font_sizeObject

The calculated font size, taking superscript and subscript into account.



1064
1065
1066
# File 'lib/hexapdf/layout/style.rb', line 1064

def calculated_font_size
  (superscript || subscript ? 0.583 : 1) * font_size
end

#calculated_strikeout_positionObject

Returns the correct offset from the baseline for the strikeout line.



1081
1082
1083
1084
1085
# File 'lib/hexapdf/layout/style.rb', line 1081

def calculated_strikeout_position
  calculated_text_rise +
    calculated_font_size / 1000.0 * font.wrapped_font.strikeout_position *
    font.scaling_factor - calculated_strikeout_thickness / 2.0
end

#calculated_strikeout_thicknessObject

Returns the correct thickness for the strikeout line.



1088
1089
1090
# File 'lib/hexapdf/layout/style.rb', line 1088

def calculated_strikeout_thickness
  calculated_font_size / 1000.0 * font.wrapped_font.strikeout_thickness * font.scaling_factor
end

#calculated_text_riseObject

The calculated text rise, taking superscript and subscript into account.



1053
1054
1055
1056
1057
1058
1059
1060
1061
# File 'lib/hexapdf/layout/style.rb', line 1053

def calculated_text_rise
  if superscript
    text_rise + font_size * 0.33
  elsif subscript
    text_rise - font_size * 0.20
  else
    text_rise
  end
end

#calculated_underline_positionObject

Returns the correct offset from the baseline for the underline.



1069
1070
1071
1072
1073
# File 'lib/hexapdf/layout/style.rb', line 1069

def calculated_underline_position
  calculated_text_rise +
    calculated_font_size / 1000.0 * font.wrapped_font.underline_position *
    font.scaling_factor - calculated_underline_thickness / 2.0
end

#calculated_underline_thicknessObject

Returns the correct thickness for the underline.



1076
1077
1078
# File 'lib/hexapdf/layout/style.rb', line 1076

def calculated_underline_thickness
  calculated_font_size / 1000.0 * font.wrapped_font.underline_thickness * font.scaling_factor
end

#clear_cacheObject

Clears all cached values.

This method needs to be called if the following style properties are changed and values were already cached: font, font_size, character_spacing, word_spacing, horizontal_scaling, ascender, descender.



1153
1154
1155
1156
1157
1158
# File 'lib/hexapdf/layout/style.rb', line 1153

def clear_cache
  @scaled_font_size = @scaled_character_spacing = @scaled_word_spacing = nil
  @scaled_horizontal_scaling = @scaled_font_ascender = @scaled_font_descender = nil
  @scaled_y_min = @scaled_y_max = nil
  @scaled_item_widths.clear
end

#initialize_copy(other) ⇒ Object

Duplicates the complex properties that can be modified, as well as the cache.



555
556
557
558
559
560
561
562
563
564
565
566
# File 'lib/hexapdf/layout/style.rb', line 555

def initialize_copy(other)
  super
  @scaled_item_widths = {}
  clear_cache

  @font_features = @font_features.dup if defined?(@font_features)
  @padding = @padding.dup if defined?(@padding)
  @margin = @margin.dup if defined?(@margin)
  @border = @border.dup if defined?(@border)
  @overlays = @overlays.dup if defined?(@overlays)
  @underlays = @underlays.dup if defined?(@underlays)
end

#nameObject

:method: text_line_wrapping_algorithm :call-seq:

text_line_wrapping_algorithm(algorithm = nil) {|items, width_block| block }

The line wrapping algorithm that should be used, defaults to TextLayouter::SimpleLineWrapping.

When setting the algorithm, either an object that responds to #call or a block can be used. See TextLayouter::SimpleLineWrapping#call for the needed method signature.



935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
# File 'lib/hexapdf/layout/style.rb', line 935

[
  [:font, "raise HexaPDF::Error, 'No font set'"],
  [:font_size, 10],
  [:character_spacing, 0],
  [:word_spacing, 0],
  [:horizontal_scaling, 100],
  [:text_rise, 0],
  [:font_features, {}],
  [:text_rendering_mode, "Content::TextRenderingMode::FILL",
   {setter: "Content::TextRenderingMode.normalize(value)"}],
  [:subscript, false,
   {setter: "value; superscript(false) if superscript",
    valid_values: [true, false]}],
  [:superscript, false,
   {setter: "value; subscript(false) if subscript",
    valid_values: [true, false]}],
  [:underline, false, {valid_values: [true, false]}],
  [:strikeout, false, {valid_values: [true, false]}],
  [:fill_color, "default_color"],
  [:fill_alpha, 1],
  [:stroke_color, "default_color"],
  [:stroke_alpha, 1],
  [:stroke_width, 1],
  [:stroke_cap_style, "Content::LineCapStyle::BUTT_CAP",
   {setter: "Content::LineCapStyle.normalize(value)"}],
  [:stroke_join_style, "Content::LineJoinStyle::MITER_JOIN",
   {setter: "Content::LineJoinStyle.normalize(value)"}],
  [:stroke_miter_limit, 10.0],
  [:stroke_dash_pattern, "Content::LineDashPattern.new",
   {setter: "Content::LineDashPattern.normalize(value, phase)", extra_args: ", phase = 0"}],
  [:align, :left, {valid_values: [:left, :center, :right, :justify]}],
  [:valign, :top, {valid_values: [:top, :center, :bottom]}],
  [:text_indent, 0],
  [:line_spacing, "LineSpacing.new(type: :single)",
   {setter: "LineSpacing.new(**(value.kind_of?(Symbol) || value.kind_of?(Numeric) ? " \
     "{type: value, value: extra_arg} : value))",
    extra_args: ", extra_arg = nil"}],
  [:last_line_gap, false, {valid_values: [true, false]}],
  [:background_color, nil],
  [:background_alpha, 1],
  [:padding, "Quad.new(0)", {setter: "Quad.new(value)"}],
  [:margin, "Quad.new(0)", {setter: "Quad.new(value)"}],
  [:border, "Border.new", {setter: "Border.new(**value)"}],
  [:overlays, "Layers.new", {setter: "Layers.new(value)"}],
  [:underlays, "Layers.new", {setter: "Layers.new(value)"}],
  [:position, :default, {valid_values: [:default, :float, :flow, :absolute]}],
  [:position_hint, nil],
].each do |name, default, options = {}|
  default = default.inspect unless default.kind_of?(String)
  setter = options.delete(:setter) || "value"
  extra_args = options.delete(:extra_args) || ""
  valid_values = options.delete(:valid_values)
  raise ArgumentError, "Invalid keywords: #{options.keys.join(', ')}" unless options.empty?
  valid_values_const = "#{name}_valid_values".upcase
  const_set(valid_values_const, valid_values)
  module_eval(<<-EOF, __FILE__, __LINE__ + 1)
    def #{name}(value = UNSET#{extra_args})
      if value == UNSET
        @#{name} ||= #{default}
      elsif #{valid_values_const} && !#{valid_values_const}.include?(value)
        raise ArgumentError, "\#{value.inspect} is not a valid #{name} value " \\
          "(\#{#{valid_values_const}.map(&:inspect).join(', ')})"
      else
        @#{name} = #{setter}
        self
      end
    end
    def #{name}?
      defined?(@#{name})
    end
  EOF
  alias_method("#{name}=", name)
end

#scaled_character_spacingObject

The character spacing scaled appropriately.



1099
1100
1101
# File 'lib/hexapdf/layout/style.rb', line 1099

def scaled_character_spacing
  @scaled_character_spacing ||= character_spacing * scaled_horizontal_scaling
end

#scaled_font_ascenderObject

The ascender of the font scaled appropriately.



1114
1115
1116
# File 'lib/hexapdf/layout/style.rb', line 1114

def scaled_font_ascender
  @scaled_font_ascender ||= font.wrapped_font.ascender * font.scaling_factor * font_size / 1000
end

#scaled_font_descenderObject

The descender of the font scaled appropriately.



1119
1120
1121
# File 'lib/hexapdf/layout/style.rb', line 1119

def scaled_font_descender
  @scaled_font_descender ||= font.wrapped_font.descender * font.scaling_factor * font_size / 1000
end

#scaled_font_sizeObject

The font size scaled appropriately.



1093
1094
1095
1096
# File 'lib/hexapdf/layout/style.rb', line 1093

def scaled_font_size
  @scaled_font_size ||= calculated_font_size * font.pdf_object.glyph_scaling_factor *
    scaled_horizontal_scaling
end

#scaled_horizontal_scalingObject

The horizontal scaling scaled appropriately.



1109
1110
1111
# File 'lib/hexapdf/layout/style.rb', line 1109

def scaled_horizontal_scaling
  @scaled_horizontal_scaling ||= horizontal_scaling / 100.0
end

#scaled_item_width(item) ⇒ Object

Returns the width of the item scaled appropriately (by taking font size, characters spacing, word spacing and horizontal scaling into account).

The item may be a (singleton) glyph object or an integer/float, i.e. items that can appear inside a TextFragment.



1138
1139
1140
1141
1142
1143
1144
1145
1146
# File 'lib/hexapdf/layout/style.rb', line 1138

def scaled_item_width(item)
  @scaled_item_widths[item] ||=
    if item.kind_of?(Numeric)
      -item * scaled_font_size
    else
      item.width * scaled_font_size + scaled_character_spacing +
        (item.apply_word_spacing? ? scaled_word_spacing : 0)
    end
end

#scaled_word_spacingObject

The word spacing scaled appropriately.



1104
1105
1106
# File 'lib/hexapdf/layout/style.rb', line 1104

def scaled_word_spacing
  @scaled_word_spacing ||= word_spacing * scaled_horizontal_scaling
end

#scaled_y_maxObject

The maximum y-coordinate, calculated using the scaled descender of the font.



1129
1130
1131
# File 'lib/hexapdf/layout/style.rb', line 1129

def scaled_y_max
  @scaled_y_max ||= scaled_font_ascender + calculated_text_rise
end

#scaled_y_minObject

The minimum y-coordinate, calculated using the scaled descender of the font.



1124
1125
1126
# File 'lib/hexapdf/layout/style.rb', line 1124

def scaled_y_min
  @scaled_y_min ||= scaled_font_descender + calculated_text_rise
end

#update(**properties) ⇒ Object

:call-seq:

style.update(**properties)    -> style

Updates the style's properties using the key-value pairs specified by the properties hash.



572
573
574
575
# File 'lib/hexapdf/layout/style.rb', line 572

def update(**properties)
  properties.each {|key, value| send(key, value) }
  self
end