ascii_pngfy 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|