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

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)


528
529
530
531
# File 'lib/hexapdf/layout/style.rb', line 528

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

Instance Method Details

#calculated_font_sizeObject

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



1042
1043
1044
# File 'lib/hexapdf/layout/style.rb', line 1042

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.



1059
1060
1061
1062
1063
# File 'lib/hexapdf/layout/style.rb', line 1059

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.



1066
1067
1068
# File 'lib/hexapdf/layout/style.rb', line 1066

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.



1031
1032
1033
1034
1035
1036
1037
1038
1039
# File 'lib/hexapdf/layout/style.rb', line 1031

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.



1047
1048
1049
1050
1051
# File 'lib/hexapdf/layout/style.rb', line 1047

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.



1054
1055
1056
# File 'lib/hexapdf/layout/style.rb', line 1054

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.



1133
1134
1135
1136
1137
1138
# File 'lib/hexapdf/layout/style.rb', line 1133

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.



534
535
536
537
538
539
540
541
542
543
544
545
# File 'lib/hexapdf/layout/style.rb', line 534

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.



914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
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
# File 'lib/hexapdf/layout/style.rb', line 914

[
  [: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) ? {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.



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

def scaled_character_spacing
  @scaled_character_spacing ||= character_spacing * scaled_horizontal_scaling
end

#scaled_font_ascenderObject

The ascender of the font scaled appropriately.



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

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.



1097
1098
1099
# File 'lib/hexapdf/layout/style.rb', line 1097

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.



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

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.



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

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.



1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
# File 'lib/hexapdf/layout/style.rb', line 1116

def scaled_item_width(item)
  @scaled_item_widths[item] ||=
    begin
      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
end

#scaled_word_spacingObject

The word spacing scaled appropriately.



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

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.



1107
1108
1109
# File 'lib/hexapdf/layout/style.rb', line 1107

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.



1102
1103
1104
# File 'lib/hexapdf/layout/style.rb', line 1102

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.



551
552
553
554
# File 'lib/hexapdf/layout/style.rb', line 551

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