prawn_hebrew 0.1.7 → 0.2.1

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/prawn_hebrew.rb +198 -11
  3. data/lib/version.rb +1 -1
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88d569ba2a0dd8db253f15e73d08a43eb1fdd8a98e6bd8aeb0976b0f90a55dfe
4
- data.tar.gz: 41df57c6a0c2beccff2c7229fcc8330628e70f3cd18297701103f87e7636681f
3
+ metadata.gz: 07fed241f2c5f202aad8246a171d2c25d47912b0b1c99e5617e1d97228f057cc
4
+ data.tar.gz: 6780ab88428065941b1a9b07e9396bc26e186fb7f2d06a9c907a0402e7061162
5
5
  SHA512:
6
- metadata.gz: e16f0bd9e4f962ba9e9ae9553e9ed0fcbd8fcc5bffd038490b44e059123c670a29dbd24f13a18ee105d3f743c4a2e1fc424b85dddcd0570fdad8d090c760ecd7
7
- data.tar.gz: 7d47bdab8d6be871bf98932464ad82b776ff5d4cd43594ab21e07ad810dcf92e9e9d13199bb4a1f0892650c1da3279b72e7994dd908bf6c92acd21cdd5639a48
6
+ metadata.gz: 31a99ee31175fa68e986105b6324157e9457b9ed6bf8dad99ba3a65f9177c888b4c9284a70c7b7fe3052df041dc0c122552786ed8ea328ef44f79e2d85e243c1
7
+ data.tar.gz: 1d89b7a87a803d949209e0b058c49152d4b3f62eb82220a5191b08729a9206bee5b4303d886b484ce78b424aed76180563e411e35d9764d3633a721425556cdb
data/lib/prawn_hebrew.rb CHANGED
@@ -2,26 +2,92 @@ require 'prawn'
2
2
  require_relative 'version'
3
3
 
4
4
  module PrawnHebrew
5
+ # Simple wrapper class for Hebrew text in table cells
6
+ class HebrewTableCell
7
+ attr_reader :text
8
+
9
+ def initialize(text, hebrew_text: false)
10
+ @text = text
11
+ @hebrew_text = hebrew_text
12
+ end
13
+
14
+ def to_s
15
+ @text.to_s
16
+ end
17
+ end
18
+
5
19
  module Text
6
20
  DEFAULT_HEBREW_FONT = 'GveretLevinHebrew'.freeze
7
21
  DEFAULT_ENGLISH_FONT = 'Helvetica'.freeze
8
22
 
9
23
  # Set to true for debugging which text rendering path is used
10
24
  DEBUG_MODE = false
11
- INVISIBLE_CHARS = /[\u2011\u2010\u2012\u2013\u2014\u2018\u2019\u201C\u201D\u2026\u200B\u200C\u200D\u200E\u200F\uFEFF\u00AD\u202A\u202B\u202C\u202D\u202E]/.freeze
12
- NBSP_CHARS = /[\u00A0\u202F]/.freeze
25
+
26
+ # Characters that should be removed or replaced to prevent Prawn errors
27
+ # Zero-width and invisible characters
28
+ INVISIBLE_CHARS = /[\u200B\u200C\u200D\u200E\u200F\uFEFF\u00AD\u202A\u202B\u202C\u202D\u202E\u2060\u2061\u2062\u2063\u2064\u206A\u206B\u206C\u206D\u206E\u206F]/.freeze
29
+
30
+ # Non-breaking spaces (replace with regular space)
31
+ NBSP_CHARS = /[\u00A0\u202F\u2007\u2008\u2009\u200A\u205F\u3000]/.freeze
32
+
33
+ # Dashes - em dash, en dash, figure dash, horizontal bar, etc. (replace with regular hyphen)
34
+ DASH_CHARS = /[\u2010\u2011\u2012\u2013\u2014\u2015\u2212\uFE58\uFE63\uFF0D]/.freeze
35
+
36
+ # Quotation marks (replace with standard quotes)
37
+ SMART_QUOTES_DOUBLE = /[\u201C\u201D\u201E\u201F\u00AB\u00BB\u301D\u301E\u301F]/.freeze
38
+ SMART_QUOTES_SINGLE = /[\u2018\u2019\u201A\u201B\u2039\u203A]/.freeze
39
+
40
+ # Ellipsis (replace with three dots)
41
+ ELLIPSIS_CHAR = /\u2026/.freeze
42
+
43
+ # Arrows and special symbols (replace with text equivalents)
44
+ ARROW_CHAR = /\u2192/.freeze # → (rightward arrow)
45
+ LEFT_ARROW = /\u2190/.freeze # ←
46
+ UP_ARROW = /\u2191/.freeze # ↑
47
+ DOWN_ARROW = /\u2193/.freeze # ↓
48
+
49
+ # Bullet points and list markers
50
+ BULLET_CHARS = /[\u2022\u2023\u2043\u204C\u204D\u2219\u25E6\u25AA\u25AB\u25CF\u25CB]/.freeze
51
+
52
+ # Other problematic characters
53
+ MISC_PROBLEM_CHARS = /[\u2028\u2029\uFFFC\uFFFD\uFFFF]/.freeze # line/paragraph separators, object replacement, replacement char
54
+
55
+ # Punctuation that should stay at the end of Hebrew text (rendered after in RTL)
56
+ TRAILING_PUNCTUATION = /([.,:;!?\-\u05BE\u05C3]+)$/.freeze # includes Hebrew punctuation
57
+ LEADING_PUNCTUATION = /^([.,:;!?\-\u05BE\u05C3()\[\]{}]+)/.freeze
13
58
 
14
59
 
15
60
 
16
61
 
17
62
  def sanitize_text(text)
18
63
  return text if text.nil?
19
- text.to_s.gsub(INVISIBLE_CHARS, ' ').gsub(NBSP_CHARS, ' ')
64
+ text.to_s
65
+ .gsub(INVISIBLE_CHARS, '') # Remove invisible characters completely
66
+ .gsub(NBSP_CHARS, ' ') # Replace non-breaking spaces with regular space
67
+ .gsub(DASH_CHARS, '-') # Replace all dash types with regular hyphen
68
+ .gsub(SMART_QUOTES_DOUBLE, '"') # Replace smart double quotes
69
+ .gsub(SMART_QUOTES_SINGLE, "'") # Replace smart single quotes
70
+ .gsub(ELLIPSIS_CHAR, '...') # Replace ellipsis with three dots
71
+ .gsub(ARROW_CHAR, '->') # Replace right arrow
72
+ .gsub(LEFT_ARROW, '<-') # Replace left arrow
73
+ .gsub(UP_ARROW, '^') # Replace up arrow
74
+ .gsub(DOWN_ARROW, 'v') # Replace down arrow
75
+ .gsub(BULLET_CHARS, '*') # Replace bullets with asterisk
76
+ .gsub(MISC_PROBLEM_CHARS, '') # Remove other problematic chars
20
77
  end
21
78
 
22
79
  def hebrew_formatted_text(text, size: 12, style: :normal, hebrew_font: DEFAULT_HEBREW_FONT, english_font: DEFAULT_ENGLISH_FONT)
23
80
  text = sanitize_text(text)
24
81
 
82
+ # Check if text is pure Hebrew (no English characters)
83
+ is_pure_hebrew = text.to_s =~ /\p{Hebrew}/ && text.to_s !~ /[a-zA-Z]/
84
+
85
+ # If pure Hebrew, render it directly without word reversal
86
+ if is_pure_hebrew
87
+ return render_pure_hebrew(text, size, style, hebrew_font)
88
+ end
89
+
90
+ # Otherwise, use the mixed text logic with word reversal
25
91
  # Split by newlines first to process each line independently
26
92
  lines = text.to_s.split("\n")
27
93
  all_fragments = []
@@ -42,10 +108,8 @@ module PrawnHebrew
42
108
  hebrew_run << word
43
109
  else
44
110
  unless hebrew_run.empty?
45
- hebrew_run.reverse.each_with_index do |hw, idx|
46
- all_fragments << { text: hw, font: hebrew_font, size: size, direction: :rtl, styles: styles }
47
- all_fragments << { text: ' ', font: hebrew_font, size: size, direction: :rtl, styles: styles } if idx < hebrew_run.length - 1
48
- end
111
+ # Process Hebrew run and handle trailing punctuation
112
+ process_hebrew_run(hebrew_run, all_fragments, hebrew_font, english_font, size, styles)
49
113
  all_fragments << { text: ' ' }
50
114
  hebrew_run.clear
51
115
  end
@@ -54,10 +118,8 @@ module PrawnHebrew
54
118
  end
55
119
 
56
120
  unless hebrew_run.empty?
57
- hebrew_run.reverse.each_with_index do |hw, idx|
58
- all_fragments << { text: hw, font: hebrew_font, size: size, direction: :rtl, styles: styles }
59
- all_fragments << { text: ' ', font: hebrew_font, size: size, direction: :rtl, styles: styles } if idx < hebrew_run.length - 1
60
- end
121
+ # Process remaining Hebrew run
122
+ process_hebrew_run(hebrew_run, all_fragments, hebrew_font, english_font, size, styles)
61
123
  end
62
124
 
63
125
  # Add newline between lines (except after the last line)
@@ -68,12 +130,115 @@ module PrawnHebrew
68
130
 
69
131
  all_fragments
70
132
  end
133
+
134
+ # Render pure Hebrew text as RTL without word reversal
135
+ def render_pure_hebrew(text, size, style, hebrew_font)
136
+ lines = text.to_s.split("\n")
137
+ all_fragments = []
138
+
139
+ styles = style.is_a?(Array) ? style : [style].compact
140
+
141
+ lines.each_with_index do |line, line_idx|
142
+ # For pure Hebrew, just add the line as-is with RTL direction
143
+ all_fragments << { text: line, font: hebrew_font, size: size, direction: :rtl, styles: styles }
144
+
145
+ # Add newline between lines (except after the last line)
146
+ if line_idx < lines.length - 1
147
+ all_fragments << { text: "\n", font: hebrew_font, size: size, direction: :rtl, styles: styles }
148
+ end
149
+ end
150
+
151
+ all_fragments
152
+ end
153
+
154
+ # Process a run of Hebrew words, handling punctuation correctly
155
+ def process_hebrew_run(hebrew_run, all_fragments, hebrew_font, english_font, size, styles)
156
+ # Check if the last word in the run has trailing punctuation
157
+ last_word = hebrew_run.last
158
+ trailing_punct = nil
159
+
160
+ if last_word =~ TRAILING_PUNCTUATION
161
+ trailing_punct = $1
162
+ # Remove trailing punctuation from the last word
163
+ hebrew_run[-1] = last_word.sub(TRAILING_PUNCTUATION, '')
164
+ end
165
+
166
+ # Check if the first word has leading punctuation (for RTL, this appears at the end)
167
+ first_word = hebrew_run.first
168
+ leading_punct = nil
169
+
170
+ if first_word =~ LEADING_PUNCTUATION
171
+ leading_punct = $1
172
+ hebrew_run[0] = first_word.sub(LEADING_PUNCTUATION, '')
173
+ end
174
+
175
+ # Add leading punctuation first (it will appear at the visual end due to RTL)
176
+ if leading_punct
177
+ all_fragments << { text: leading_punct, font: english_font, size: size, styles: styles }
178
+ end
179
+
180
+ # Reverse Hebrew words for RTL display
181
+ hebrew_run.reverse.each_with_index do |hw, idx|
182
+ next if hw.empty?
183
+ all_fragments << { text: hw, font: hebrew_font, size: size, direction: :rtl, styles: styles }
184
+ all_fragments << { text: ' ', font: hebrew_font, size: size, direction: :rtl, styles: styles } if idx < hebrew_run.length - 1
185
+ end
186
+
187
+ # Add trailing punctuation at the end (it will appear at the visual end of Hebrew text)
188
+ if trailing_punct
189
+ all_fragments << { text: trailing_punct, font: english_font, size: size, styles: styles }
190
+ end
191
+ end
192
+
193
+ def hebrew_table(data, size: 12, style: :normal,
194
+ hebrew_font: DEFAULT_HEBREW_FONT,
195
+ english_font: DEFAULT_ENGLISH_FONT,
196
+ **table_opts)
197
+ # Process each row: sanitize and render Hebrew cells using formatted text
198
+ processed_data = data.map do |row|
199
+ row.map do |cell_content|
200
+ # Handle hash cells (e.g., {content: "text", font_style: :bold})
201
+ if cell_content.is_a?(Hash)
202
+ cell_hash = cell_content.dup
203
+ # Sanitize the content if it exists
204
+ if cell_hash[:content]
205
+ cell_hash[:content] = sanitize_text(cell_hash[:content].to_s)
206
+ end
207
+ cell_hash
208
+ else
209
+ # Handle simple string cells
210
+ sanitize_text(cell_content.to_s)
211
+ end
212
+ end
213
+ end
214
+
215
+ # Create table with a block to apply Hebrew formatting to cells
216
+ table(processed_data, table_opts) do |table|
217
+ # Apply Hebrew formatting to cells that contain Hebrew text
218
+ table.cells.each do |cell|
219
+ cell_text = cell.content
220
+ if cell_text =~ /\p{Hebrew}/
221
+ # Use text_color and font to support Hebrew
222
+ cell.font = hebrew_font
223
+ cell.size = size unless cell.size # Don't override if already set
224
+ cell.text_color = table_opts[:text_color] || "000000"
225
+ else
226
+ # English cells
227
+ cell.font = english_font
228
+ cell.size = size unless cell.size # Don't override if already set
229
+ end
230
+ end
231
+ end
232
+ end
71
233
 
72
234
  def hebrew_text_box(text, size: 12, style: :normal,
73
235
  hebrew_font: DEFAULT_HEBREW_FONT,
74
236
  english_font: DEFAULT_ENGLISH_FONT,
75
237
  direction: :auto, **box_opts)
76
238
 
239
+ # Sanitize text first to remove problematic characters
240
+ text = sanitize_text(text)
241
+
77
242
  # Handle font specification in box_opts or use defaults
78
243
  final_hebrew_font = box_opts.delete(:hebrew_font) || hebrew_font
79
244
  final_english_font = box_opts.delete(:english_font) || english_font
@@ -223,6 +388,28 @@ module PrawnHebrew
223
388
  end
224
389
  end
225
390
  end
391
+
392
+ # Helper method to create table cells with Hebrew support
393
+ def hebrew_table_cell(text, size: 12, style: :normal,
394
+ hebrew_font: DEFAULT_HEBREW_FONT,
395
+ english_font: DEFAULT_ENGLISH_FONT,
396
+ **cell_opts)
397
+ # Create a hash that can be used as table cell content
398
+ # with Hebrew text handling
399
+ if text.to_s =~ /\p{Hebrew}/
400
+ {
401
+ content: text,
402
+ hebrew_text: true,
403
+ size: size,
404
+ style: style,
405
+ hebrew_font: hebrew_font,
406
+ english_font: english_font,
407
+ cell_opts: cell_opts
408
+ }
409
+ else
410
+ { content: text }
411
+ end
412
+ end
226
413
  end
227
414
  end
228
415
 
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module PrawnHebrew
2
- VERSION = "0.1.7"
2
+ VERSION = "0.2.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prawn_hebrew
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Lite