prawn-rtl-support 0.1.8 → 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 +4 -4
- data/CHANGELOG.md +24 -0
- data/CLAUDE.md +5 -4
- data/README.md +28 -1
- data/lib/prawn/rtl/bidi.rb +225 -0
- data/lib/prawn/rtl/connector.rb +4 -10
- data/lib/prawn/rtl/support/version.rb +1 -1
- data/prawn-rtl-support.gemspec +8 -7
- metadata +12 -16
- data/Rakefile +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 233930e38a41afb8a807bf1753e171aa3a8112bacd425c57c8b93c2aecd374f2
|
4
|
+
data.tar.gz: b6ea130b2b05f7ea1f002f7023fbd22b5338324e7f0a4695175310c37d6ebb99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 882f584327fb3d2cacddb945f46a37c07fc48f0c93e0b069fd3f567359f6df226b2a7ad1ed6f604907bc912228b2acfaf34a88f06adbcde58569d97c8428298e
|
7
|
+
data.tar.gz: 9602c40c2e4668188f1c8d91b79587a879a50207771b247f79ea7afd9721706a5310e21793e83453b275587b7b5dc5c4e70d2fbec43e8018e277b72a2d4f58cf
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [0.2.0] - 2025-09-04
|
9
|
+
|
10
|
+
### ⚠️ Breaking Changes
|
11
|
+
- **ICU library requirement**: The gem now requires the ICU library to be installed on the system. This may affect macOS and Windows users who previously relied on twitter_cldr's bundled ICU data.
|
12
|
+
- **Linux**: Usually pre-installed
|
13
|
+
- **macOS**: Install via Homebrew: `brew install icu4c`
|
14
|
+
- **Windows**: May require manual ICU installation
|
15
|
+
- **Custom path**: Set `ICU_LIB_PATH` environment variable if ICU is installed in a non-standard location
|
16
|
+
|
17
|
+
### Customer-Impacting Changes
|
18
|
+
|
19
|
+
#### Added
|
20
|
+
- **Extended platform support**: Added ARM64 architecture support in CI/CD pipeline
|
21
|
+
|
22
|
+
#### Changed
|
23
|
+
- **Dependencies**: Replaced twitter_cldr with direct FFI bindings (lighter dependency footprint, but requires system ICU library)
|
24
|
+
- **Performance**: Direct ICU integration provides better performance for text processing
|
25
|
+
|
26
|
+
### Internal Changes
|
27
|
+
|
28
|
+
#### Testing & Quality
|
29
|
+
- Added comprehensive integration tests for BiDi functionality
|
30
|
+
- Extended CI matrix to include Ubuntu 22.04, 24.04 and ARM64 variants
|
31
|
+
|
8
32
|
## [0.1.8] - 2025-09-04
|
9
33
|
|
10
34
|
### Customer-Impacting Changes
|
data/CLAUDE.md
CHANGED
@@ -5,7 +5,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
5
5
|
## Project Overview
|
6
6
|
|
7
7
|
This is prawn-rtl-support, a Ruby gem that provides bidirectional text (RTL/LTR) support for the Prawn PDF generation library. It enables proper rendering of Arabic and other right-to-left languages in PDFs by:
|
8
|
-
- Using Unicode Bidirectional Algorithm via
|
8
|
+
- Using Unicode Bidirectional Algorithm via ICU for text reordering
|
9
9
|
- Connecting Arabic letters properly for visual display
|
10
10
|
- Minimally patching Prawn's text rendering pipeline
|
11
11
|
|
@@ -41,15 +41,16 @@ The gem patches Prawn by prepending a module to `Prawn::Text::Formatted::Box#ori
|
|
41
41
|
2. **lib/prawn/rtl/connector.rb**: Core RTL fixing logic with three main methods:
|
42
42
|
- `fix_rtl(string)`: Main public API that detects RTL text and processes it
|
43
43
|
- `connect(string)`: Applies Arabic letter connection rules
|
44
|
-
- `reorder(string)`: Uses
|
44
|
+
- `reorder(string)`: Uses ICU's Bidi algorithm to reorder text visually
|
45
45
|
3. **lib/prawn/rtl/connector/logic.rb**: Arabic letter connection logic with character mapping tables for different forms (isolated, initial, medial, final)
|
46
46
|
|
47
47
|
The gem automatically activates when required - no configuration needed. It detects RTL text and only processes strings that contain RTL characters.
|
48
48
|
|
49
49
|
## Key Dependencies
|
50
50
|
|
51
|
-
- **prawn ~> 2.2**: The PDF generation library being patched
|
52
|
-
- **
|
51
|
+
- **prawn ~> 2.2**: The PDF generation library being patched
|
52
|
+
- **ffi ~> 1.15**: Foreign Function Interface for Ruby to call ICU C library functions
|
53
|
+
- **ICU library**: System dependency (libicuuc) providing Unicode Bidirectional Algorithm implementation
|
53
54
|
|
54
55
|
## Contributing
|
55
56
|
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://github.com/prawn-rtl-support/prawn-rtl-support/actions/workflows/ci.yml)
|
4
4
|
|
5
|
-
This gem provides bidirectional text support for Prawn PDF generator. It uses the Unicode Bidirectional Algorithm
|
5
|
+
This gem provides bidirectional text support for Prawn PDF generator. It uses the Unicode Bidirectional Algorithm via [ICU](http://site.icu-project.org/) (International Components for Unicode) for text reordering and implements Arabic letter shaping similar to [Arabic Letter Connector](https://github.com/staii/arabic-letter-connector). Prawn patching is minimal - we only patch [`Prawn::Text::Formatted::Box#original_text`](https://github.com/prawnpdf/prawn/blob/master/lib/prawn/text/formatted/box.rb#L367).
|
6
6
|
|
7
7
|
## Supported Languages
|
8
8
|
|
@@ -22,6 +22,33 @@ This gem provides bidirectional text support for Prawn PDF generator. It uses th
|
|
22
22
|
|
23
23
|
Ruby and Rails internally provide Unicode string normalization. However, Prawn doesn't connect Arabic letters into their contextual forms and doesn't support mixed LTR and RTL strings. This gem adds this support.
|
24
24
|
|
25
|
+
## Requirements
|
26
|
+
|
27
|
+
### ICU Library
|
28
|
+
|
29
|
+
Starting from version 0.2.0, this gem requires the ICU library to be installed on your system:
|
30
|
+
|
31
|
+
- **Linux**: Usually pre-installed. If not, install with:
|
32
|
+
```bash
|
33
|
+
# Ubuntu/Debian
|
34
|
+
sudo apt-get install libicu-dev
|
35
|
+
|
36
|
+
# RHEL/CentOS/Fedora
|
37
|
+
sudo yum install libicu-devel
|
38
|
+
```
|
39
|
+
|
40
|
+
- **macOS**: Install via Homebrew:
|
41
|
+
```bash
|
42
|
+
brew install icu4c
|
43
|
+
```
|
44
|
+
|
45
|
+
- **Windows**: Download and install ICU from the [official site](http://site.icu-project.org/download)
|
46
|
+
|
47
|
+
- **Custom installation path**: If ICU is installed in a non-standard location, set the `ICU_LIB_PATH` environment variable:
|
48
|
+
```bash
|
49
|
+
export ICU_LIB_PATH=/custom/path/to/icu/lib
|
50
|
+
```
|
51
|
+
|
25
52
|
## Installation
|
26
53
|
|
27
54
|
Add this line to your application's Gemfile:
|
@@ -0,0 +1,225 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rbconfig'
|
4
|
+
require 'ffi'
|
5
|
+
|
6
|
+
module Prawn
|
7
|
+
module Rtl
|
8
|
+
# FFI BiDi wrapper for Unicode Bidirectional Algorithm support
|
9
|
+
#
|
10
|
+
# This module provides direct FFI bindings to ICU's ubidi functions
|
11
|
+
# for bidirectional text processing.
|
12
|
+
module Bidi
|
13
|
+
extend FFI::Library
|
14
|
+
|
15
|
+
class BiDiError < StandardError; end
|
16
|
+
|
17
|
+
# Detect platform
|
18
|
+
def self.platform
|
19
|
+
os = RbConfig::CONFIG['host_os']
|
20
|
+
case os
|
21
|
+
when /darwin/
|
22
|
+
:osx
|
23
|
+
when /linux/
|
24
|
+
:linux
|
25
|
+
when /bsd/
|
26
|
+
:bsd
|
27
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
28
|
+
:windows
|
29
|
+
else
|
30
|
+
:linux
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Search paths for ICU libraries
|
35
|
+
def self.search_paths
|
36
|
+
@search_paths ||=
|
37
|
+
if ENV['ICU_LIB_PATH']
|
38
|
+
[ENV['ICU_LIB_PATH']]
|
39
|
+
elsif FFI::Platform::IS_WINDOWS
|
40
|
+
ENV['PATH'].split(File::PATH_SEPARATOR)
|
41
|
+
else
|
42
|
+
[
|
43
|
+
'/usr/local/{lib64,lib}',
|
44
|
+
'/opt/local/{lib64,lib}',
|
45
|
+
'/opt/homebrew/{lib64,lib}',
|
46
|
+
'/usr/{lib64,lib}'
|
47
|
+
] + Dir['/usr/lib/*-linux-gnu'] + Dir['/lib/*-linux-gnu']
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Find ICU library files
|
52
|
+
def self.find_icu_lib
|
53
|
+
candidates = []
|
54
|
+
lib_name = 'icuuc'
|
55
|
+
|
56
|
+
search_paths.each do |path|
|
57
|
+
Dir.glob(File.expand_path(path)).each do |dir|
|
58
|
+
# Try versioned libraries first (newer to older)
|
59
|
+
# ICU versions from 4.0 (2009) to potential future versions
|
60
|
+
90.downto(4).each do |version|
|
61
|
+
case platform
|
62
|
+
when :osx
|
63
|
+
candidates << File.join(dir, "lib#{lib_name}.#{version}.dylib")
|
64
|
+
candidates << File.join(dir, "lib#{lib_name}.dylib")
|
65
|
+
when :windows
|
66
|
+
candidates << File.join(dir, "#{lib_name}#{version}.dll")
|
67
|
+
candidates << File.join(dir, "#{lib_name}.dll")
|
68
|
+
else
|
69
|
+
candidates << File.join(dir, "lib#{lib_name}.so.#{version}")
|
70
|
+
candidates << File.join(dir, "lib#{lib_name}.so")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Find the first existing library
|
77
|
+
found = candidates.find { |path| File.exist?(path) }
|
78
|
+
found || ["lib#{lib_name}.so", "lib#{lib_name}.dylib", "#{lib_name}.dll", lib_name]
|
79
|
+
end
|
80
|
+
|
81
|
+
# Load the library
|
82
|
+
ffi_lib find_icu_lib
|
83
|
+
|
84
|
+
# Detect ICU version suffix by checking for a known function
|
85
|
+
def self.detect_icu_suffix
|
86
|
+
@detect_icu_suffix ||= begin
|
87
|
+
# Try common suffixes from newer to older versions
|
88
|
+
# Some versions use _4_2 format, others use _42 format
|
89
|
+
suffixes = [''] + 90.downto(4).flat_map { |v| ["_#{v}", "_#{v / 10}_#{v % 10}"] }
|
90
|
+
|
91
|
+
# Find suffix by checking if ubidi_open function exists
|
92
|
+
suffix = suffixes.find do |s|
|
93
|
+
# Try to find the function
|
94
|
+
func_name = :"ubidi_open#{s}"
|
95
|
+
ffi_libraries.any? do |lib|
|
96
|
+
lib.find_function(func_name.to_s)
|
97
|
+
end
|
98
|
+
rescue StandardError
|
99
|
+
false
|
100
|
+
end
|
101
|
+
|
102
|
+
suffix || ''
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Helper to attach function with detected suffix
|
107
|
+
def self.attach_icu_function(ruby_name, icu_name, args, return_type)
|
108
|
+
suffixed_name = "#{icu_name}#{detect_icu_suffix}"
|
109
|
+
attach_function ruby_name, suffixed_name.to_sym, args, return_type
|
110
|
+
end
|
111
|
+
|
112
|
+
# Constants from ubidi.h
|
113
|
+
UBIDI_DEFAULT_LTR = 0xfe
|
114
|
+
UBIDI_DEFAULT_RTL = 0xff
|
115
|
+
UBIDI_LTR = 0
|
116
|
+
UBIDI_RTL = 1
|
117
|
+
UBIDI_MIXED = 2
|
118
|
+
UBIDI_NEUTRAL = 3
|
119
|
+
|
120
|
+
# Reorder options
|
121
|
+
UBIDI_DO_MIRRORING = 2
|
122
|
+
UBIDI_OUTPUT_REVERSE = 16
|
123
|
+
|
124
|
+
# Attach ICU functions with version detection
|
125
|
+
attach_icu_function :ubidi_open, 'ubidi_open', [], :pointer
|
126
|
+
attach_icu_function :ubidi_close, 'ubidi_close', [:pointer], :void
|
127
|
+
attach_icu_function :ubidi_setPara, 'ubidi_setPara', %i[pointer pointer int32 uint8 pointer pointer], :void
|
128
|
+
attach_icu_function :ubidi_getDirection, 'ubidi_getDirection', [:pointer], :int
|
129
|
+
attach_icu_function :ubidi_getLength, 'ubidi_getLength', [:pointer], :int32
|
130
|
+
attach_icu_function :ubidi_writeReordered, 'ubidi_writeReordered', %i[pointer pointer int32 uint16 pointer],
|
131
|
+
:int32
|
132
|
+
attach_icu_function :ubidi_countRuns, 'ubidi_countRuns', %i[pointer pointer], :int32
|
133
|
+
|
134
|
+
# Reorders text according to the Unicode Bidirectional Algorithm
|
135
|
+
#
|
136
|
+
# @param text [String] the text to reorder
|
137
|
+
# @param direction [Symbol] :ltr, :rtl, or :auto (default)
|
138
|
+
# @return [String] the visually reordered text
|
139
|
+
def self.reorder(text, direction: :auto)
|
140
|
+
return text if text.nil? || text.empty?
|
141
|
+
|
142
|
+
# Convert direction to ubidi constant
|
143
|
+
para_level =
|
144
|
+
case direction
|
145
|
+
when :ltr then UBIDI_LTR
|
146
|
+
when :rtl then UBIDI_RTL
|
147
|
+
else UBIDI_DEFAULT_LTR
|
148
|
+
end
|
149
|
+
|
150
|
+
bidi = nil
|
151
|
+
begin
|
152
|
+
# Open BiDi object
|
153
|
+
bidi = ubidi_open
|
154
|
+
raise BiDiError, 'Failed to create BiDi object' if bidi.null?
|
155
|
+
|
156
|
+
# Convert string to UTF-16 for ICU
|
157
|
+
utf16_text = text.encode('UTF-16LE')
|
158
|
+
text_length = utf16_text.bytesize / 2
|
159
|
+
|
160
|
+
# Create buffer for UTF-16 string
|
161
|
+
text_buffer = FFI::MemoryPointer.new(:uint16, text_length + 1)
|
162
|
+
text_buffer.put_bytes(0, utf16_text)
|
163
|
+
|
164
|
+
# Error status
|
165
|
+
status = FFI::MemoryPointer.new(:int32)
|
166
|
+
status.put_int32(0, 0)
|
167
|
+
|
168
|
+
# Set paragraph
|
169
|
+
ubidi_setPara(bidi, text_buffer, text_length, para_level, nil, status)
|
170
|
+
|
171
|
+
error_code = status.get_int32(0)
|
172
|
+
raise BiDiError, "ubidi_setPara failed with error code: #{error_code}" if error_code.positive?
|
173
|
+
|
174
|
+
# Get required size for output
|
175
|
+
output_length = text_length * 2
|
176
|
+
output_buffer = FFI::MemoryPointer.new(:uint16, output_length)
|
177
|
+
|
178
|
+
# Write reordered text
|
179
|
+
written = ubidi_writeReordered(bidi, output_buffer, output_length, UBIDI_DO_MIRRORING, status)
|
180
|
+
|
181
|
+
error_code = status.get_int32(0)
|
182
|
+
raise BiDiError, "ubidi_writeReordered failed with error code: #{error_code}" if error_code.positive?
|
183
|
+
|
184
|
+
# Convert back from UTF-16 to UTF-8
|
185
|
+
result_bytes = output_buffer.get_bytes(0, written * 2)
|
186
|
+
result_bytes.force_encoding('UTF-16LE').encode('UTF-8')
|
187
|
+
ensure
|
188
|
+
ubidi_close(bidi) if bidi && !bidi.null?
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Checks if a string contains RTL characters
|
193
|
+
#
|
194
|
+
# @param text [String] the text to check
|
195
|
+
# @return [Boolean] true if the text contains RTL characters
|
196
|
+
def self.contains_rtl?(text)
|
197
|
+
return false if text.nil? || text.empty?
|
198
|
+
|
199
|
+
bidi = nil
|
200
|
+
begin
|
201
|
+
bidi = ubidi_open
|
202
|
+
return false if bidi.null?
|
203
|
+
|
204
|
+
utf16_text = text.encode('UTF-16LE')
|
205
|
+
text_length = utf16_text.bytesize / 2
|
206
|
+
|
207
|
+
text_buffer = FFI::MemoryPointer.new(:uint16, text_length + 1)
|
208
|
+
text_buffer.put_bytes(0, utf16_text)
|
209
|
+
|
210
|
+
status = FFI::MemoryPointer.new(:int32)
|
211
|
+
status.put_int32(0, 0)
|
212
|
+
|
213
|
+
ubidi_setPara(bidi, text_buffer, text_length, UBIDI_DEFAULT_LTR, nil, status)
|
214
|
+
|
215
|
+
return false if status.get_int32(0).positive?
|
216
|
+
|
217
|
+
direction = ubidi_getDirection(bidi)
|
218
|
+
[UBIDI_RTL, UBIDI_MIXED].include?(direction)
|
219
|
+
ensure
|
220
|
+
ubidi_close(bidi) if bidi && !bidi.null?
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
data/lib/prawn/rtl/connector.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'connector/logic'
|
4
|
-
|
4
|
+
require_relative 'bidi'
|
5
5
|
|
6
6
|
module Prawn
|
7
7
|
module Rtl
|
@@ -49,16 +49,13 @@ module Prawn
|
|
49
49
|
|
50
50
|
# Reorders text according to the Unicode Bidirectional Algorithm.
|
51
51
|
#
|
52
|
-
# Uses
|
52
|
+
# Uses ICU's BiDi implementation via FFI to visually reorder mixed
|
53
53
|
# LTR/RTL text for correct display.
|
54
54
|
#
|
55
55
|
# @param string [String] the text to reorder
|
56
56
|
# @return [String] the visually reordered text
|
57
57
|
def self.reorder(string)
|
58
|
-
|
59
|
-
.from_string(string, direction: :RTL)
|
60
|
-
.reorder_visually!
|
61
|
-
.to_s
|
58
|
+
Bidi.reorder(string, direction: :rtl)
|
62
59
|
end
|
63
60
|
|
64
61
|
# Checks if a string contains RTL (Right-to-Left) characters.
|
@@ -66,10 +63,7 @@ module Prawn
|
|
66
63
|
# @param string [String] the text to check
|
67
64
|
# @return [Boolean] true if the text contains RTL characters, false otherwise
|
68
65
|
def self.include_rtl?(string)
|
69
|
-
|
70
|
-
.from_string(string)
|
71
|
-
.types
|
72
|
-
.include?(:R)
|
66
|
+
Bidi.contains_rtl?(string)
|
73
67
|
end
|
74
68
|
end
|
75
69
|
end
|
data/prawn-rtl-support.gemspec
CHANGED
@@ -5,8 +5,8 @@ require_relative 'lib/prawn/rtl/support/version'
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'prawn-rtl-support'
|
7
7
|
spec.version = Prawn::Rtl::Support::VERSION
|
8
|
-
spec.authors = ['Oleksandr Lapchenko']
|
9
|
-
spec.email = ['ozeron@me.com']
|
8
|
+
spec.authors = ['Oleksandr Lapchenko', 'Oleksii Leonov']
|
9
|
+
spec.email = ['ozeron@me.com', 'mail@oleksiileonov.com']
|
10
10
|
|
11
11
|
spec.summary = 'Bidirectional text support for Prawn PDF generator'
|
12
12
|
spec.description = 'Adds right-to-left (RTL) text support to Prawn PDF generator. ' \
|
@@ -26,10 +26,11 @@ Gem::Specification.new do |spec|
|
|
26
26
|
}
|
27
27
|
|
28
28
|
# Specify which files should be included in the gem
|
29
|
-
spec.files = (
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
spec.files = (
|
30
|
+
Dir['{lib,exe}/**/*'] +
|
31
|
+
Dir['*.{md,txt,gemspec}'] +
|
32
|
+
%w[Gemfile LICENSE.txt README.md CODE_OF_CONDUCT.md].select { |f| File.exist?(f) }
|
33
|
+
).reject { |f| File.directory?(f) }
|
33
34
|
|
34
35
|
spec.bindir = 'exe'
|
35
36
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
@@ -37,6 +38,6 @@ Gem::Specification.new do |spec|
|
|
37
38
|
spec.extra_rdoc_files = ['README.md', 'LICENSE.txt']
|
38
39
|
|
39
40
|
# Runtime dependencies
|
41
|
+
spec.add_dependency 'ffi', '~> 1.17'
|
40
42
|
spec.add_dependency 'prawn', '~> 2.2'
|
41
|
-
spec.add_dependency 'twitter_cldr', '>= 4.0', '< 7.0'
|
42
43
|
end
|
metadata
CHANGED
@@ -1,54 +1,50 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prawn-rtl-support
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oleksandr Lapchenko
|
8
|
+
- Oleksii Leonov
|
8
9
|
bindir: exe
|
9
10
|
cert_chain: []
|
10
11
|
date: 1980-01-02 00:00:00.000000000 Z
|
11
12
|
dependencies:
|
12
13
|
- !ruby/object:Gem::Dependency
|
13
|
-
name:
|
14
|
+
name: ffi
|
14
15
|
requirement: !ruby/object:Gem::Requirement
|
15
16
|
requirements:
|
16
17
|
- - "~>"
|
17
18
|
- !ruby/object:Gem::Version
|
18
|
-
version: '
|
19
|
+
version: '1.17'
|
19
20
|
type: :runtime
|
20
21
|
prerelease: false
|
21
22
|
version_requirements: !ruby/object:Gem::Requirement
|
22
23
|
requirements:
|
23
24
|
- - "~>"
|
24
25
|
- !ruby/object:Gem::Version
|
25
|
-
version: '
|
26
|
+
version: '1.17'
|
26
27
|
- !ruby/object:Gem::Dependency
|
27
|
-
name:
|
28
|
+
name: prawn
|
28
29
|
requirement: !ruby/object:Gem::Requirement
|
29
30
|
requirements:
|
30
|
-
- - "
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '4.0'
|
33
|
-
- - "<"
|
31
|
+
- - "~>"
|
34
32
|
- !ruby/object:Gem::Version
|
35
|
-
version: '
|
33
|
+
version: '2.2'
|
36
34
|
type: :runtime
|
37
35
|
prerelease: false
|
38
36
|
version_requirements: !ruby/object:Gem::Requirement
|
39
37
|
requirements:
|
40
|
-
- - "
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
version: '4.0'
|
43
|
-
- - "<"
|
38
|
+
- - "~>"
|
44
39
|
- !ruby/object:Gem::Version
|
45
|
-
version: '
|
40
|
+
version: '2.2'
|
46
41
|
description: Adds right-to-left (RTL) text support to Prawn PDF generator. Fully supports
|
47
42
|
Arabic script languages (Arabic, Persian, Urdu) with contextual letter shaping and
|
48
43
|
ligatures. Also supports Hebrew and other RTL languages with bidirectional text
|
49
44
|
reordering. Handles mixed LTR/RTL text properly.
|
50
45
|
email:
|
51
46
|
- ozeron@me.com
|
47
|
+
- mail@oleksiileonov.com
|
52
48
|
executables: []
|
53
49
|
extensions: []
|
54
50
|
extra_rdoc_files:
|
@@ -61,7 +57,7 @@ files:
|
|
61
57
|
- Gemfile
|
62
58
|
- LICENSE.txt
|
63
59
|
- README.md
|
64
|
-
-
|
60
|
+
- lib/prawn/rtl/bidi.rb
|
65
61
|
- lib/prawn/rtl/connector.rb
|
66
62
|
- lib/prawn/rtl/connector/logic.rb
|
67
63
|
- lib/prawn/rtl/support.rb
|