ascii_pngfy 0.2.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 +7 -0
- data/lib/ascii_pngfy.rb +40 -0
- data/lib/ascii_pngfy/aabb.rb +49 -0
- data/lib/ascii_pngfy/color_rgba.rb +56 -0
- data/lib/ascii_pngfy/exceptions.rb +29 -0
- data/lib/ascii_pngfy/glyphs.rb +118 -0
- data/lib/ascii_pngfy/pngfyer.rb +43 -0
- data/lib/ascii_pngfy/rendering_rules.rb +198 -0
- data/lib/ascii_pngfy/result.rb +25 -0
- data/lib/ascii_pngfy/settings.rb +6 -0
- data/lib/ascii_pngfy/settings/color_setting.rb +44 -0
- data/lib/ascii_pngfy/settings/font_height_setting.rb +74 -0
- data/lib/ascii_pngfy/settings/horizontal_spacing_setting.rb +46 -0
- data/lib/ascii_pngfy/settings/setable_getable.rb +35 -0
- data/lib/ascii_pngfy/settings/setable_getable_settings.rb +85 -0
- data/lib/ascii_pngfy/settings/settings_snapshot.rb +35 -0
- data/lib/ascii_pngfy/settings/text_setting.rb +210 -0
- data/lib/ascii_pngfy/settings/vertical_spacing_setting.rb +46 -0
- data/lib/ascii_pngfy/settings_renderer.rb +41 -0
- data/lib/ascii_pngfy/vec2i.rb +23 -0
- metadata +155 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'chunky_png'
|
4
|
+
|
5
|
+
module AsciiPngfy
|
6
|
+
# Reponsibilities
|
7
|
+
# > Provides acces to Result data points, which are
|
8
|
+
# - generated png as ChunkyPNG::Image instance
|
9
|
+
# - render width and height
|
10
|
+
# - settings snapshot that supports only setting getters
|
11
|
+
class Result
|
12
|
+
attr_reader(:png, :render_width, :render_height, :settings)
|
13
|
+
|
14
|
+
def initialize(png, render_width, render_height, settings_snapshot)
|
15
|
+
self.png = png
|
16
|
+
self.render_width = render_width
|
17
|
+
self.render_height = render_height
|
18
|
+
self.settings = settings_snapshot
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_writer(:png, :render_width, :render_height, :settings)
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AsciiPngfy
|
4
|
+
module Settings
|
5
|
+
# Reponsibilities
|
6
|
+
# - Keeps track of the color setting
|
7
|
+
# - Validated through ColorRGBA implicitly
|
8
|
+
class ColorSetting
|
9
|
+
include SetableGetable
|
10
|
+
|
11
|
+
def initialize(initial_red, initial_green, initial_blue, initial_alpha)
|
12
|
+
self.color = AsciiPngfy::ColorRGBA.new(0, 0, 0, 0)
|
13
|
+
|
14
|
+
set(
|
15
|
+
red: initial_red,
|
16
|
+
green: initial_green,
|
17
|
+
blue: initial_blue,
|
18
|
+
alpha: initial_alpha
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def get
|
23
|
+
color.dup
|
24
|
+
end
|
25
|
+
|
26
|
+
def set(red: nil, green: nil, blue: nil, alpha: nil)
|
27
|
+
color.red = red unless red.nil?
|
28
|
+
color.green = green unless green.nil?
|
29
|
+
color.blue = blue unless blue.nil?
|
30
|
+
color.alpha = alpha unless alpha.nil?
|
31
|
+
|
32
|
+
color.dup
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize_copy(original_color_setting)
|
36
|
+
self.color = original_color_setting.color.dup
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
attr_accessor(:color)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AsciiPngfy
|
4
|
+
module Settings
|
5
|
+
# Reponsibilities
|
6
|
+
# - Keeps track of the font_height setting
|
7
|
+
# - Validates font_height
|
8
|
+
class FontHeightSetting
|
9
|
+
include SetableGetable
|
10
|
+
|
11
|
+
def initialize(initial_font_height)
|
12
|
+
self.font_height = GLYPH_DESIGN_HEIGHT
|
13
|
+
|
14
|
+
set(initial_font_height)
|
15
|
+
end
|
16
|
+
|
17
|
+
def get
|
18
|
+
font_height
|
19
|
+
end
|
20
|
+
|
21
|
+
def set(desired_font_height)
|
22
|
+
validated_font_height = validate_font_height(desired_font_height)
|
23
|
+
|
24
|
+
new_font_height =
|
25
|
+
if multiple_of_glyph_design_height?(validated_font_height)
|
26
|
+
validated_font_height
|
27
|
+
else
|
28
|
+
lower_bound_distance = (validated_font_height % GLYPH_DESIGN_HEIGHT)
|
29
|
+
determine_bound_font_height(validated_font_height, lower_bound_distance)
|
30
|
+
end
|
31
|
+
|
32
|
+
self.font_height = new_font_height
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_accessor(:font_height)
|
38
|
+
|
39
|
+
def font_height_valid?(some_font_height)
|
40
|
+
some_font_height.is_a?(Integer) && (some_font_height >= GLYPH_DESIGN_HEIGHT)
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate_font_height(some_font_height)
|
44
|
+
return some_font_height if font_height_valid?(some_font_height)
|
45
|
+
|
46
|
+
error_message = String.new
|
47
|
+
error_message << "#{some_font_height} is not a valid font size. "
|
48
|
+
error_message << "Must be an Integer in the range (#{GLYPH_DESIGN_HEIGHT}..)."
|
49
|
+
|
50
|
+
raise AsciiPngfy::Exceptions::InvalidFontHeightError, error_message
|
51
|
+
end
|
52
|
+
|
53
|
+
def multiple_of_glyph_design_height?(number)
|
54
|
+
(number % GLYPH_DESIGN_HEIGHT).zero?
|
55
|
+
end
|
56
|
+
|
57
|
+
def lower_bound_distance?(distance)
|
58
|
+
[1, 2, 3, 4].include?(distance)
|
59
|
+
end
|
60
|
+
|
61
|
+
def higher_bound_distance?(distance)
|
62
|
+
[5, 6, 7, 8].include?(distance)
|
63
|
+
end
|
64
|
+
|
65
|
+
def determine_bound_font_height(validated_font_height, lower_bound_distance)
|
66
|
+
if lower_bound_distance?(lower_bound_distance)
|
67
|
+
(validated_font_height / GLYPH_DESIGN_HEIGHT) * GLYPH_DESIGN_HEIGHT
|
68
|
+
elsif higher_bound_distance?(lower_bound_distance)
|
69
|
+
((validated_font_height / GLYPH_DESIGN_HEIGHT) + 1) * GLYPH_DESIGN_HEIGHT
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AsciiPngfy
|
4
|
+
module Settings
|
5
|
+
# Reponsibilities
|
6
|
+
# - Keeps track of the the horizontal_spacing setting
|
7
|
+
# - Validates horizontal_spacing
|
8
|
+
class HorizontalSpacingSetting
|
9
|
+
include SetableGetable
|
10
|
+
|
11
|
+
def initialize(initial_spacing)
|
12
|
+
self.horizontal_spacing = 0
|
13
|
+
|
14
|
+
set(initial_spacing)
|
15
|
+
end
|
16
|
+
|
17
|
+
def get
|
18
|
+
horizontal_spacing
|
19
|
+
end
|
20
|
+
|
21
|
+
def set(desired_spacing)
|
22
|
+
validated_horizontal_spacing = validate_horizontal_spacing(desired_spacing)
|
23
|
+
|
24
|
+
self.horizontal_spacing = validated_horizontal_spacing
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_accessor(:horizontal_spacing)
|
30
|
+
|
31
|
+
def horizontal_spacing_valid?(some_spacing)
|
32
|
+
some_spacing.is_a?(Integer) && (some_spacing >= 0)
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_horizontal_spacing(some_spacing)
|
36
|
+
return some_spacing if horizontal_spacing_valid?(some_spacing)
|
37
|
+
|
38
|
+
error_message = String.new
|
39
|
+
error_message << "#{some_spacing} is not a valid horizontal spacing. "
|
40
|
+
error_message << 'Must be an Integer in the range (0..).'
|
41
|
+
|
42
|
+
raise AsciiPngfy::Exceptions::InvalidHorizontalSpacingError, error_message
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AsciiPngfy
|
4
|
+
module Settings
|
5
|
+
# Provides the interface for all the Setting implementations in a way that
|
6
|
+
# each inclusion of this module forces the including class to override
|
7
|
+
# the behaviour for #set and #get
|
8
|
+
#
|
9
|
+
# If the #get or #set method is called, but it is not overriden by the object
|
10
|
+
# mixing this module in, a NotImplementedError is raised
|
11
|
+
module SetableGetable
|
12
|
+
def get
|
13
|
+
self_class_name = self.class.to_s
|
14
|
+
|
15
|
+
expected_error_message = String.new
|
16
|
+
expected_error_message << "#{self_class_name}#get has not yet been implemented. "
|
17
|
+
expected_error_message << "Must override the #{self_class_name}#get method in order to "
|
18
|
+
expected_error_message << 'function as Setting.'
|
19
|
+
|
20
|
+
raise NotImplementedError, expected_error_message
|
21
|
+
end
|
22
|
+
|
23
|
+
def set
|
24
|
+
self_class_name = self.class.to_s
|
25
|
+
|
26
|
+
expected_error_message = String.new
|
27
|
+
expected_error_message << "#{self_class_name}#set has not yet been implemented. "
|
28
|
+
expected_error_message << "Must override the #{self_class_name}#set method in order to "
|
29
|
+
expected_error_message << 'function as Setting.'
|
30
|
+
|
31
|
+
raise NotImplementedError, expected_error_message
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AsciiPngfy
|
4
|
+
module Settings
|
5
|
+
# Reponsibilities
|
6
|
+
# - Pipes setter and getter calls to a specific Setting instance
|
7
|
+
# - Associates supported settings with the respective Setting name
|
8
|
+
# - Generates a SettingsSnapshot object for the purpose of describing
|
9
|
+
# a specific set of settings at the time of the snapshot generation
|
10
|
+
class SetableGetableSettings
|
11
|
+
GET = :get
|
12
|
+
SET = :set
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
initialize_supported_settings
|
16
|
+
end
|
17
|
+
|
18
|
+
def respond_to_missing?(method_name, _)
|
19
|
+
setting_name = determine_setting_name_as_symbol(method_name)
|
20
|
+
|
21
|
+
setting_exists?(setting_name) || super
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(method_name, *arguments)
|
25
|
+
super unless respond_to?(method_name)
|
26
|
+
|
27
|
+
setting_operation = determine_operation(method_name)
|
28
|
+
setting_name = determine_setting_name_as_symbol(method_name)
|
29
|
+
|
30
|
+
# call the settings #set or #get method depending on the operation
|
31
|
+
setting(setting_name).public_send(setting_operation, *arguments)
|
32
|
+
end
|
33
|
+
|
34
|
+
def snapshot
|
35
|
+
snapshot_settings = settings.dup
|
36
|
+
snapshot_settings.transform_values!(&:get)
|
37
|
+
|
38
|
+
SettingsSnapshot.new(snapshot_settings)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_accessor(:settings)
|
44
|
+
|
45
|
+
def add(setting_name, setting_instance)
|
46
|
+
self.settings ||= {}
|
47
|
+
|
48
|
+
settings[setting_name] = setting_instance
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize_supported_settings
|
52
|
+
add(:font_color, ColorSetting.new(255, 255, 255, 255))
|
53
|
+
add(:background_color, ColorSetting.new(0, 0, 0, 255))
|
54
|
+
add(:font_height, FontHeightSetting.new(9))
|
55
|
+
add(:horizontal_spacing, HorizontalSpacingSetting.new(1))
|
56
|
+
add(:vertical_spacing, VerticalSpacingSetting.new(1))
|
57
|
+
add(:text, TextSetting.new(self))
|
58
|
+
end
|
59
|
+
|
60
|
+
def setting(setting_name)
|
61
|
+
settings[setting_name]
|
62
|
+
end
|
63
|
+
|
64
|
+
def setting_exists?(setting_name)
|
65
|
+
settings.key?(setting_name)
|
66
|
+
end
|
67
|
+
|
68
|
+
def setter?(method_name)
|
69
|
+
method_name.start_with?('set_')
|
70
|
+
end
|
71
|
+
|
72
|
+
def determine_operation(method_name)
|
73
|
+
setter?(method_name) ? SET : GET
|
74
|
+
end
|
75
|
+
|
76
|
+
def determine_setting_name_as_symbol(method_name)
|
77
|
+
if setter?(method_name)
|
78
|
+
method_name[4..]
|
79
|
+
else
|
80
|
+
method_name
|
81
|
+
end.to_sym
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AsciiPngfy
|
4
|
+
module Settings
|
5
|
+
# Reponsibilities
|
6
|
+
# - Keeps track of the setting values at the point of creation
|
7
|
+
# of this snapshot
|
8
|
+
# - Dynamic getter for each setting name which simply returns
|
9
|
+
# the value of the respective setting
|
10
|
+
class SettingsSnapshot
|
11
|
+
def initialize(setting_names_and_values)
|
12
|
+
self.setting_names_and_values = setting_names_and_values
|
13
|
+
end
|
14
|
+
|
15
|
+
def respond_to_missing?(setting_name, _)
|
16
|
+
setting_exists?(setting_name) || super
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(setting_name, *_arguments)
|
20
|
+
super unless respond_to?(setting_name)
|
21
|
+
|
22
|
+
# return that settings value
|
23
|
+
setting_names_and_values[setting_name]
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def setting_exists?(setting_name)
|
29
|
+
setting_names_and_values.key?(setting_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_accessor(:setting_names_and_values)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
module AsciiPngfy
|
4
|
+
module Settings
|
5
|
+
# Reponsibilities
|
6
|
+
# - Keeps track of the text and replacement_text setting
|
7
|
+
# - Replaces unsupported text characters with replacement text
|
8
|
+
# - Validates text and replacement_text
|
9
|
+
# rubocop: disable Metrics/ClassLength
|
10
|
+
class TextSetting
|
11
|
+
include SetableGetable
|
12
|
+
|
13
|
+
def initialize(settings)
|
14
|
+
self.settings = settings
|
15
|
+
self.text = ''
|
16
|
+
|
17
|
+
set('<3 Ascii-Pngfy <3')
|
18
|
+
end
|
19
|
+
|
20
|
+
def get
|
21
|
+
text.dup
|
22
|
+
end
|
23
|
+
|
24
|
+
# The philosophy behind this method is as follows:
|
25
|
+
# - The desired_text cannot be empty pre replacement. If it is an error is raised
|
26
|
+
#
|
27
|
+
# - When no replacement text is passed, the desired_text is considered as is by
|
28
|
+
# skipping the replacement procedure
|
29
|
+
#
|
30
|
+
# - When a replacement text is passed though, the replacement text is validated and
|
31
|
+
# all unsupported text characters are replaced with the replacement text
|
32
|
+
#
|
33
|
+
# - The text is then validated post replacement to make sure it is not empty,
|
34
|
+
# the same as pre replacement
|
35
|
+
#
|
36
|
+
# - At this point the text is validated to only contain supported ASCII characters
|
37
|
+
# and has its dimensions checked in terms of the needed png texture size to fit
|
38
|
+
# the resulting text along with the character spacing previously set.
|
39
|
+
#
|
40
|
+
# - Finally the text setting is updated to the resulting, optionally replaced text
|
41
|
+
def set(desired_text, desired_replacement_text = nil)
|
42
|
+
pre_replacement_text_validation(desired_text, desired_replacement_text)
|
43
|
+
|
44
|
+
if replacement_desired?(desired_replacement_text)
|
45
|
+
desired_replacement_text = validate_replacement_text(desired_replacement_text)
|
46
|
+
|
47
|
+
desired_text = replace_unsupported_characters(from: desired_text, with: desired_replacement_text)
|
48
|
+
end
|
49
|
+
|
50
|
+
post_replacement_text_validation(desired_text)
|
51
|
+
validate_text_contents(desired_text)
|
52
|
+
validate_text_image_dimensions(desired_text)
|
53
|
+
|
54
|
+
# set the text to a duplicate of the original text to avoid string injection
|
55
|
+
self.text = desired_text.dup
|
56
|
+
|
57
|
+
desired_text
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize_copy(original_text_setting)
|
61
|
+
self.text = original_text_setting.text.dup
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
attr_accessor(:text)
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
attr_accessor(:settings)
|
71
|
+
|
72
|
+
def character_supported?(some_character)
|
73
|
+
SUPPORTED_ASCII_CHARACTERS.include?(some_character)
|
74
|
+
end
|
75
|
+
|
76
|
+
def extract_unsupported_characters(some_string)
|
77
|
+
unsupported_characters = []
|
78
|
+
|
79
|
+
some_string.each_char do |some_char|
|
80
|
+
unsupported_characters << some_char unless character_supported?(some_char)
|
81
|
+
end
|
82
|
+
|
83
|
+
unsupported_characters
|
84
|
+
end
|
85
|
+
|
86
|
+
def string_supported?(some_string)
|
87
|
+
# also returns true when the string is empty, so an undesired empty string must
|
88
|
+
# be handled separately
|
89
|
+
extract_unsupported_characters(some_string).empty?
|
90
|
+
end
|
91
|
+
|
92
|
+
def validate_replacement_text(some_text)
|
93
|
+
return some_text if string_supported?(some_text)
|
94
|
+
|
95
|
+
error_message = "#{some_text.inspect} is not a valid replacement string. "\
|
96
|
+
"Must contain only characters with ASCII code #{SUPPORTED_ASCII_CODES.min} "\
|
97
|
+
"or in the range (#{SUPPORTED_ASCII_CODES_WITHOUT_NEWLINE_RANGE})."
|
98
|
+
|
99
|
+
raise AsciiPngfy::Exceptions::InvalidReplacementTextError, error_message
|
100
|
+
end
|
101
|
+
|
102
|
+
def replace_unsupported_characters(from:, with:)
|
103
|
+
text_with_replacements = ''
|
104
|
+
|
105
|
+
from.each_char do |text_character|
|
106
|
+
replacement_text = character_supported?(text_character) ? text_character : with
|
107
|
+
text_with_replacements << replacement_text
|
108
|
+
end
|
109
|
+
|
110
|
+
text_with_replacements
|
111
|
+
end
|
112
|
+
|
113
|
+
def replacement_desired?(replacement_text)
|
114
|
+
!!replacement_text
|
115
|
+
end
|
116
|
+
|
117
|
+
def pre_replacement_text_validation(desired_text, desired_replacement_text)
|
118
|
+
return desired_text unless desired_text.empty?
|
119
|
+
|
120
|
+
error_message = 'Text cannot be empty because that would result in a PNG with a width or height of zero. '\
|
121
|
+
"Must contain at least one character with ASCII code #{SUPPORTED_ASCII_CODES.min} "\
|
122
|
+
"or in the range (#{SUPPORTED_ASCII_CODES_WITHOUT_NEWLINE_RANGE})."
|
123
|
+
|
124
|
+
# hint the user that the desired replacement text is also empty
|
125
|
+
if replacement_desired?(desired_replacement_text) && desired_replacement_text.empty?
|
126
|
+
error_message << ' Hint: Both the text and the replacement text are empty.'
|
127
|
+
end
|
128
|
+
|
129
|
+
raise AsciiPngfy::Exceptions::EmptyTextError, error_message
|
130
|
+
end
|
131
|
+
|
132
|
+
def post_replacement_text_validation(desired_text)
|
133
|
+
return desired_text unless desired_text.empty?
|
134
|
+
|
135
|
+
error_message = 'Text cannot be empty because that would result in a PNG with a width or height of zero. '\
|
136
|
+
"Must contain at least one character with ASCII code #{SUPPORTED_ASCII_CODES.min} "\
|
137
|
+
"or in the range (#{SUPPORTED_ASCII_CODES_WITHOUT_NEWLINE_RANGE}). "\
|
138
|
+
'Hint: An empty replacement text causes text with only unsupported characters to end up as '\
|
139
|
+
'empty string.'
|
140
|
+
|
141
|
+
raise AsciiPngfy::Exceptions::EmptyTextError, error_message
|
142
|
+
end
|
143
|
+
|
144
|
+
def validate_text_contents(some_text)
|
145
|
+
# this method only accounts for non-empty strings that contains unsupported characters
|
146
|
+
# empty strings are handled separately to separate different types of errors more clearly
|
147
|
+
return some_text if string_supported?(some_text)
|
148
|
+
|
149
|
+
un_supported_characters = extract_unsupported_characters(some_text)
|
150
|
+
un_supported_inspected_characters = un_supported_characters.map(&:inspect)
|
151
|
+
un_supported_characters_list = "#{un_supported_inspected_characters[0..-2].join(', ')} and "\
|
152
|
+
"#{un_supported_inspected_characters.last}"
|
153
|
+
|
154
|
+
error_message = "#{un_supported_characters_list} are all invalid text characters. "\
|
155
|
+
"Must contain only characters with ASCII code #{SUPPORTED_ASCII_CODES.min} "\
|
156
|
+
"or in the range (#{SUPPORTED_ASCII_CODES_WITHOUT_NEWLINE_RANGE})."
|
157
|
+
|
158
|
+
raise AsciiPngfy::Exceptions::InvalidCharacterError, error_message
|
159
|
+
end
|
160
|
+
|
161
|
+
def validate_text_image_width(desired_text, image_width)
|
162
|
+
return desired_text unless image_width > AsciiPngfy::MAX_RESULT_PNG_IMAGE_WIDTH
|
163
|
+
|
164
|
+
longest_text_line = RenderingRules.longest_text_line(desired_text)
|
165
|
+
|
166
|
+
capped_text = cap_string(longest_text_line, '..', 60)
|
167
|
+
|
168
|
+
error_message = "The text line #{capped_text.inspect} is too long to be represented in a "\
|
169
|
+
"#{AsciiPngfy::MAX_RESULT_PNG_IMAGE_WIDTH} pixel wide png. Hint: Use shorter "\
|
170
|
+
'text lines and/or reduce the horizontal character spacing.'
|
171
|
+
|
172
|
+
raise AsciiPngfy::Exceptions::TextLineTooLongError, error_message
|
173
|
+
end
|
174
|
+
|
175
|
+
def validate_text_image_height(desired_text, image_height)
|
176
|
+
return desired_text unless image_height > AsciiPngfy::MAX_RESULT_PNG_IMAGE_HEIGHT
|
177
|
+
|
178
|
+
capped_text = cap_string(desired_text, '..', 60)
|
179
|
+
|
180
|
+
error_message = "The text #{capped_text.inspect} contains too many lines to be represented in a "\
|
181
|
+
"#{MAX_RESULT_PNG_IMAGE_HEIGHT} pixel high png. Hint: Use less text lines and/or "\
|
182
|
+
'reduce the vertical character spacing.'
|
183
|
+
|
184
|
+
raise AsciiPngfy::Exceptions::TooManyTextLinesError, error_message
|
185
|
+
end
|
186
|
+
|
187
|
+
def validate_text_image_dimensions(desired_text)
|
188
|
+
image_width = AsciiPngfy::RenderingRules.png_width(settings, desired_text)
|
189
|
+
image_height = AsciiPngfy::RenderingRules.png_height(settings, desired_text)
|
190
|
+
|
191
|
+
validate_text_image_width(desired_text, image_width)
|
192
|
+
validate_text_image_height(desired_text, image_height)
|
193
|
+
end
|
194
|
+
|
195
|
+
def cap_string(some_string, desired_separator, desired_cap_length)
|
196
|
+
if some_string.length <= desired_cap_length
|
197
|
+
some_string
|
198
|
+
else
|
199
|
+
half_cap_length = (desired_cap_length - desired_separator.length) / 2
|
200
|
+
|
201
|
+
string_beginning_portion = some_string[0, half_cap_length]
|
202
|
+
string_end_portion = some_string[-half_cap_length..]
|
203
|
+
|
204
|
+
"#{string_beginning_portion}#{desired_separator}#{string_end_portion}"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
# rubocop: enable Metrics/ClassLength
|
210
|
+
end
|