acro_that 1.0.3 → 1.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 31a0652c9e6d834244b0a296b017d82a1a77ef07666c3e4e1b482b622e1c80cd
4
- data.tar.gz: 3af1db0522ab1a9a754afb6c1b297184c49ed05547093611d6f3dce838b1e782
3
+ metadata.gz: 8983cb8f5bef4f8085baf807e7fea9b8efc11d0d25efc3cbcd25b54a8e938af3
4
+ data.tar.gz: 646674f34985529556d9b36ee91351aa7fd25a3e0ca561814fa9ae2274da7653
5
5
  SHA512:
6
- metadata.gz: f2265b5ee6e1ae3ffd9421b0c3944bbe59c52f4e4d1c437a0a555764a36aa7ed23f8c7b0189eb4b7c4aa3703889f7eb58e8cd44232826060fc4bc7599b84504a
7
- data.tar.gz: 9c90530b5041bd419f28a7c4f9f82829897bbb0a83bb2527ab93e0c90af862ddeed7f4a070f133daf1ab0a149faabf1b7b517c8bf5e11f5719f36050804d9ec3
6
+ metadata.gz: f1827af100fbb441cd52015c146abc2522852c3c57f4cdcdec32b382c91db0896fef918a101bcd65ced3167ad201eed136c279baa4c59139318f41cfc6db5116
7
+ data.tar.gz: 877931eced47139862b7966fd9b73307532e075e218538e7fda02bd768a6a13354ed0e5fefe9481d2b1af3d756a7a81a1f129f34f42eb80b4c19031d8906e913
data/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ 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
+ ## [1.0.4] - 2025-11-12
9
+
10
+ ### Fixed
11
+ - Fixed `Encoding::CompatibilityError` when processing field values with special characters (e.g., "María", "José"). Special characters are now automatically transliterated to ASCII equivalents (e.g., "María" → "Maria") before encoding to PDF format, ensuring compatibility with PDF string encoding requirements.
12
+
13
+ ### Added
14
+ - Added I18n gem as a runtime dependency for transliteration support
15
+ - Added `DictScan.transliterate_to_ascii` method to convert special characters to ASCII equivalents
16
+ - Automatic transliteration for text field values and radio button field export values
17
+
8
18
  ## [0.1.8] - 2025-11-04
9
19
 
10
20
  ### Fixed
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- acro_that (1.0.2)
4
+ acro_that (1.0.3)
5
5
  chunky_png (~> 1.4)
6
+ i18n (~> 1.14)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
@@ -10,7 +11,10 @@ GEM
10
11
  ast (2.4.3)
11
12
  chunky_png (1.4.0)
12
13
  coderay (1.1.3)
14
+ concurrent-ruby (1.3.5)
13
15
  diff-lcs (1.6.2)
16
+ i18n (1.14.7)
17
+ concurrent-ruby (~> 1.0)
14
18
  json (2.15.2)
15
19
  language_server-protocol (3.17.0.5)
16
20
  lint_roller (1.1.0)
data/acro_that.gemspec CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = ["lib"]
29
29
 
30
30
  spec.add_runtime_dependency "chunky_png", "~> 1.4"
31
+ spec.add_runtime_dependency "i18n", "~> 1.14"
31
32
  spec.add_development_dependency "rspec", "~> 3.0"
32
33
  spec.add_development_dependency "pry", "~> 0.14"
33
34
  spec.add_development_dependency "rubocop", "~> 1.50"
@@ -591,23 +591,23 @@ module AcroThat
591
591
 
592
592
  # Calculate scale to maximize size while maintaining aspect ratio
593
593
  # Use the smaller dimension to ensure it fits
594
- scale = [width, height].min * 0.85 # Use 85% of the smaller dimension
595
-
594
+ scale = [width, height].min * 0.85 # Use 85% of the smaller dimension
595
+
596
596
  # Calculate checkmark dimensions
597
597
  check_width = scale
598
598
  check_height = scale
599
-
599
+
600
600
  # Center the checkmark in the box
601
601
  offset_x = (width - check_width) / 2
602
602
  offset_y = (height - check_height) / 2
603
-
603
+
604
604
  # Calculate actual coordinates
605
- check_x1 = offset_x + norm_x1 * check_width
606
- check_y1 = offset_y + norm_y1 * check_height
607
- check_x2 = offset_x + norm_x2 * check_width
608
- check_y2 = offset_y + norm_y2 * check_height
609
- check_x3 = offset_x + norm_x3 * check_width
610
- check_y3 = offset_y + norm_y3 * check_height
605
+ check_x1 = offset_x + (norm_x1 * check_width)
606
+ check_y1 = offset_y + (norm_y1 * check_height)
607
+ check_x2 = offset_x + (norm_x2 * check_width)
608
+ check_y2 = offset_y + (norm_y2 * check_height)
609
+ check_x3 = offset_x + (norm_x3 * check_width)
610
+ check_y3 = offset_y + (norm_y3 * check_height)
611
611
 
612
612
  content_stream = "q\n"
613
613
  # Draw square border around field bounds
@@ -4,6 +4,26 @@ module AcroThat
4
4
  module DictScan
5
5
  module_function
6
6
 
7
+ # Configure I18n for transliteration (disable locale enforcement)
8
+ I18n.config.enforce_available_locales = false
9
+
10
+ # Transliterate a string to ASCII, converting special characters to their ASCII equivalents
11
+ # Example: "María Valentina" -> "Maria Valentina"
12
+ def transliterate_to_ascii(str)
13
+ return str unless str.is_a?(String)
14
+
15
+ # Ensure the string is in UTF-8 encoding
16
+ utf8_str = str.encode("UTF-8", invalid: :replace, undef: :replace)
17
+
18
+ # Use I18n transliteration to convert to ASCII
19
+ begin
20
+ I18n.transliterate(utf8_str, locale: :en, replacement: "")
21
+ rescue StandardError
22
+ # Fallback: if transliteration fails, try to encode to ASCII with replacements
23
+ utf8_str.encode("ASCII", invalid: :replace, undef: :replace)
24
+ end
25
+ end
26
+
7
27
  # --- low-level string helpers -------------------------------------------------
8
28
 
9
29
  def strip_stream_bodies(pdf)
@@ -118,10 +138,15 @@ module AcroThat
118
138
  when Symbol
119
139
  "/#{val}"
120
140
  when String
121
- if val.ascii_only?
122
- "(#{val.gsub(/([\\()])/, '\\\\\\1').gsub("\n", '\\n')})"
141
+ # Transliterate special characters to ASCII to avoid encoding issues
142
+ ascii_val = transliterate_to_ascii(val)
143
+
144
+ if ascii_val.ascii_only?
145
+ "(#{ascii_val.gsub(/([\\()])/, '\\\\\\1').gsub("\n", '\\n')})"
123
146
  else
124
- utf16 = val.encode("UTF-16BE")
147
+ # Ensure string is in UTF-8 before encoding to UTF-16BE
148
+ utf8_str = ascii_val.encode("UTF-8", invalid: :replace, undef: :replace)
149
+ utf16 = utf8_str.encode("UTF-16BE")
125
150
  bytes = "\xFE\xFF#{utf16}"
126
151
  "<#{bytes.unpack1('H*')}>"
127
152
  end
@@ -138,8 +163,11 @@ module AcroThat
138
163
  # Remove leading / if present (we'll add it back)
139
164
  name_str = name_str[1..] if name_str.start_with?("/")
140
165
 
166
+ # Transliterate special characters to ASCII to avoid encoding issues
167
+ ascii_name = transliterate_to_ascii(name_str)
168
+
141
169
  # Encode special characters as hex
142
- encoded = name_str.each_byte.map do |byte|
170
+ encoded = ascii_name.each_byte.map do |byte|
143
171
  char = byte.chr
144
172
  # PDF name special characters that need hex encoding: # ( ) < > [ ] { } / %
145
173
  # Also encode control characters (0x00-0x1F, 0x7F) and non-ASCII (0x80-0xFF)
@@ -99,23 +99,23 @@ module AcroThat
99
99
 
100
100
  # Calculate scale to maximize size while maintaining aspect ratio
101
101
  # Use the smaller dimension to ensure it fits
102
- scale = [width, height].min * 0.85 # Use 85% of the smaller dimension
103
-
102
+ scale = [width, height].min * 0.85 # Use 85% of the smaller dimension
103
+
104
104
  # Calculate checkmark dimensions
105
105
  check_width = scale
106
106
  check_height = scale
107
-
107
+
108
108
  # Center the checkmark in the box
109
109
  offset_x = (width - check_width) / 2
110
110
  offset_y = (height - check_height) / 2
111
-
111
+
112
112
  # Calculate actual coordinates
113
- check_x1 = offset_x + norm_x1 * check_width
114
- check_y1 = offset_y + norm_y1 * check_height
115
- check_x2 = offset_x + norm_x2 * check_width
116
- check_y2 = offset_y + norm_y2 * check_height
117
- check_x3 = offset_x + norm_x3 * check_width
118
- check_y3 = offset_y + norm_y3 * check_height
113
+ check_x1 = offset_x + (norm_x1 * check_width)
114
+ check_y1 = offset_y + (norm_y1 * check_height)
115
+ check_x2 = offset_x + (norm_x2 * check_width)
116
+ check_y2 = offset_y + (norm_y2 * check_height)
117
+ check_x3 = offset_x + (norm_x3 * check_width)
118
+ check_y3 = offset_y + (norm_y3 * check_height)
119
119
 
120
120
  content_stream = "q\n"
121
121
  # Draw square border around field bounds
@@ -176,23 +176,23 @@ module AcroThat
176
176
 
177
177
  # Calculate scale to maximize size while maintaining aspect ratio
178
178
  # Use the smaller dimension to ensure it fits
179
- scale = [width, height].min * 0.85 # Use 85% of the smaller dimension
180
-
179
+ scale = [width, height].min * 0.85 # Use 85% of the smaller dimension
180
+
181
181
  # Calculate checkmark dimensions
182
182
  check_width = scale
183
183
  check_height = scale
184
-
184
+
185
185
  # Center the checkmark in the box
186
186
  offset_x = (width - check_width) / 2
187
187
  offset_y = (height - check_height) / 2
188
-
188
+
189
189
  # Calculate actual coordinates
190
- check_x1 = offset_x + norm_x1 * check_width
191
- check_y1 = offset_y + norm_y1 * check_height
192
- check_x2 = offset_x + norm_x2 * check_width
193
- check_y2 = offset_y + norm_y2 * check_height
194
- check_x3 = offset_x + norm_x3 * check_width
195
- check_y3 = offset_y + norm_y3 * check_height
190
+ check_x1 = offset_x + (norm_x1 * check_width)
191
+ check_y1 = offset_y + (norm_y1 * check_height)
192
+ check_x2 = offset_x + (norm_x2 * check_width)
193
+ check_y2 = offset_y + (norm_y2 * check_height)
194
+ check_x3 = offset_x + (norm_x3 * check_width)
195
+ check_y3 = offset_y + (norm_y3 * check_height)
196
196
 
197
197
  content_stream = "q\n"
198
198
  # Draw checkmark only (no border)
@@ -58,12 +58,12 @@ module AcroThat
58
58
  while current_obj <= max_obj_num
59
59
  # Find next existing object
60
60
  next_existing = sorted.find { |num, _gen, _offset| num >= current_obj }
61
-
61
+
62
62
  if next_existing && next_existing[0] == current_obj
63
63
  # Object exists - find consecutive run of existing objects
64
64
  first_num = current_obj
65
65
  run_length = 1
66
-
66
+
67
67
  while (i + run_length) < sorted.length &&
68
68
  sorted[i + run_length][0] == first_num + run_length &&
69
69
  sorted[i + run_length][1] == sorted[i][1]
@@ -86,12 +86,13 @@ module AcroThat
86
86
  # Object doesn't exist - find consecutive run of missing objects
87
87
  first_missing = current_obj
88
88
  missing_count = 1
89
-
89
+
90
90
  while current_obj + missing_count <= max_obj_num
91
91
  check_obj = current_obj + missing_count
92
92
  if sorted.any? { |num, _gen, _offset| num == check_obj }
93
93
  break
94
94
  end
95
+
95
96
  missing_count += 1
96
97
  end
97
98
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AcroThat
4
- VERSION = "1.0.3"
4
+ VERSION = "1.0.4"
5
5
  end
data/lib/acro_that.rb CHANGED
@@ -5,6 +5,7 @@ require "stringio"
5
5
  require "zlib"
6
6
  require "base64"
7
7
  require "set"
8
+ require "i18n"
8
9
 
9
10
  require_relative "acro_that/dict_scan"
10
11
  require_relative "acro_that/object_resolver"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acro_that
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Wynkoop
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-11-07 00:00:00.000000000 Z
11
+ date: 2025-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chunky_png
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: i18n
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.14'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.14'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rspec
29
43
  requirement: !ruby/object:Gem::Requirement