combine_pdf 0.1.13 → 0.1.15
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 +8 -0
- data/lib/combine_pdf.rb +2 -0
- data/lib/combine_pdf/combine_pdf_basic_writer.rb +391 -386
- data/lib/combine_pdf/combine_pdf_methods.rb +30 -0
- data/lib/combine_pdf/combine_pdf_page.rb +545 -0
- data/lib/combine_pdf/combine_pdf_pdf.rb +19 -16
- data/lib/combine_pdf/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fcabdcfbb7f58ec22dbc23d3edc0182f3a8dc2ca
|
4
|
+
data.tar.gz: 9ff09d74f401086ac834c21a0059266c08b3c1ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f103e64dc370eb7742add0f21a1263adda67d53a2fd9e72848c29a9f81dac9db9b392a8a6b51dcc61aa2c78995fc2de8fb00aa561f376959498e357938bf9a08
|
7
|
+
data.tar.gz: 8c6fc6df3c6f046d1eb7a1da2f50088cb4e246cfd2ffd69edcbf74711f1a04d339bf782979e28cd8458beb1b28c61431938c1b3b8f413cddf5afd310e172415c
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
|
3
3
|
***
|
4
4
|
|
5
|
+
Change log v.0.1.14
|
6
|
+
|
7
|
+
**changes**: changed the way the PDF Page objects are 'injected' with their methods, so that the PDF#pages method is faster and more methods can be injected into the Hash object. For instance, textbox can now be called on an existing page without creating a PDFWriter object and 'stumping' the new data.
|
8
|
+
|
9
|
+
(the number_pages method hasn't been update to use this new feature as of yet)
|
10
|
+
|
11
|
+
***
|
12
|
+
|
5
13
|
Change log v.0.1.13
|
6
14
|
|
7
15
|
**fix**: fix for Acrobat Reader compatablity (by removing color-space declarations). Should solve issue #13 , reported originaly by Imanol and Diyei Gomi.
|
data/lib/combine_pdf.rb
CHANGED
@@ -3,12 +3,14 @@
|
|
3
3
|
require 'zlib'
|
4
4
|
require 'securerandom'
|
5
5
|
require 'strscan'
|
6
|
+
require 'matrix'
|
6
7
|
|
7
8
|
#require the RC4 Gem
|
8
9
|
require 'rc4'
|
9
10
|
|
10
11
|
|
11
12
|
load "combine_pdf/combine_pdf_operations.rb"
|
13
|
+
load "combine_pdf/combine_pdf_page.rb"
|
12
14
|
load "combine_pdf/combine_pdf_basic_writer.rb"
|
13
15
|
load "combine_pdf/combine_pdf_decrypt.rb"
|
14
16
|
load "combine_pdf/combine_pdf_fonts.rb"
|
@@ -48,394 +48,399 @@ module CombinePDF
|
|
48
48
|
self[:Contents] = { is_reference_only: true , referenced_object: {indirect_reference_id: 0, raw_stream_content: @contents} }
|
49
49
|
self[:MediaBox] = mediabox
|
50
50
|
end
|
51
|
-
# accessor (getter) for the :MediaBox element of the page
|
52
|
-
def mediabox
|
53
|
-
self[:MediaBox]
|
54
|
-
end
|
55
|
-
# accessor (setter) for the :MediaBox element of the page
|
56
|
-
# dimensions:: an Array consisting of four numbers (can be floats) setting the size of the media box.
|
57
|
-
def mediabox=(dimensions = [0.0, 0.0, 612.0, 792.0])
|
58
|
-
self[:MediaBox] = dimensions
|
59
|
-
end
|
60
|
-
|
61
|
-
# This method adds a simple text box to the Page represented by the PDFWriter class.
|
62
|
-
# This function takes two values:
|
63
|
-
# text:: the text to potin the box.
|
64
|
-
# properties:: a Hash of box properties.
|
65
|
-
# the symbols and values in the properties Hash could be any or all of the following:
|
66
|
-
# x:: the left position of the box.
|
67
|
-
# y:: the BUTTOM position of the box.
|
68
|
-
# width:: the width/length of the box. negative values will be computed from edge of page. defaults to 0 (end of page).
|
69
|
-
# height:: the height of the box. negative values will be computed from edge of page. defaults to 0 (end of page).
|
70
|
-
# text_align:: symbol for horizontal text alignment, can be ":center" (default), ":right", ":left"
|
71
|
-
# text_valign:: symbol for vertical text alignment, can be ":center" (default), ":top", ":buttom"
|
72
|
-
# text_padding:: a Float between 0 and 1, setting the padding for the text. defaults to 0.05 (5%).
|
73
|
-
# font:: a registered font name or an Array of names. defaults to ":Helvetica". The 14 standard fonts names are:
|
74
|
-
# - :"Times-Roman"
|
75
|
-
# - :"Times-Bold"
|
76
|
-
# - :"Times-Italic"
|
77
|
-
# - :"Times-BoldItalic"
|
78
|
-
# - :Helvetica
|
79
|
-
# - :"Helvetica-Bold"
|
80
|
-
# - :"Helvetica-BoldOblique"
|
81
|
-
# - :"Helvetica- Oblique"
|
82
|
-
# - :Courier
|
83
|
-
# - :"Courier-Bold"
|
84
|
-
# - :"Courier-Oblique"
|
85
|
-
# - :"Courier-BoldOblique"
|
86
|
-
# - :Symbol
|
87
|
-
# - :ZapfDingbats
|
88
|
-
# font_size:: a Fixnum for the font size, or :fit_text to fit the text in the box. defaults to ":fit_text"
|
89
|
-
# max_font_size:: if font_size is set to :fit_text, this will be the maximum font size. defaults to nil (no maximum)
|
90
|
-
# font_color:: text color in [R, G, B], an array with three floats, each in a value between 0 to 1 (gray will be "[0.5, 0.5, 0.5]"). defaults to black.
|
91
|
-
# stroke_color:: text stroke color in [R, G, B], an array with three floats, each in a value between 0 to 1 (gray will be "[0.5, 0.5, 0.5]"). defounlts to nil (no stroke).
|
92
|
-
# stroke_width:: text stroke width in PDF units. defaults to 0 (none).
|
93
|
-
# box_color:: box fill color in [R, G, B], an array with three floats, each in a value between 0 to 1 (gray will be "[0.5, 0.5, 0.5]"). defaults to nil (none).
|
94
|
-
# border_color:: box border color in [R, G, B], an array with three floats, each in a value between 0 to 1 (gray will be "[0.5, 0.5, 0.5]"). defaults to nil (none).
|
95
|
-
# border_width:: border width in PDF units. defaults to nil (none).
|
96
|
-
# box_radius:: border radius in PDF units. defaults to 0 (no corner rounding).
|
97
|
-
# opacity:: textbox opacity, a float between 0 (transparent) and 1 (opaque)
|
98
|
-
def textbox(text, properties = {})
|
99
|
-
options = {
|
100
|
-
x: 0,
|
101
|
-
y: 0,
|
102
|
-
width: 0,
|
103
|
-
height: -1,
|
104
|
-
text_align: :center,
|
105
|
-
text_valign: :center,
|
106
|
-
text_padding: 0.1,
|
107
|
-
font: nil,
|
108
|
-
font_size: :fit_text,
|
109
|
-
max_font_size: nil,
|
110
|
-
font_color: [0,0,0],
|
111
|
-
stroke_color: nil,
|
112
|
-
stroke_width: 0,
|
113
|
-
box_color: nil,
|
114
|
-
border_color: nil,
|
115
|
-
border_width: 0,
|
116
|
-
box_radius: 0,
|
117
|
-
opacity: 1,
|
118
|
-
ctm: [1,0,0,1,0,0]
|
119
|
-
}
|
120
|
-
options.update properties
|
121
|
-
# reset the length and height to meaningful values, if negative
|
122
|
-
options[:width] = mediabox[2] - options[:x] + options[:width] if options[:width] <= 0
|
123
|
-
options[:height] = mediabox[3] - options[:y] + options[:height] if options[:height] <= 0
|
124
|
-
|
125
|
-
# reset the padding value
|
126
|
-
options[:text_padding] = 0 if options[:text_padding].to_f >= 1
|
127
|
-
|
128
|
-
# create box stream
|
129
|
-
box_stream = ""
|
130
|
-
# set graphic state for box
|
131
|
-
if options[:box_color] || (options[:border_width].to_i > 0 && options[:border_color])
|
132
|
-
# compute x and y position for text
|
133
|
-
x = options[:x]
|
134
|
-
y = options[:y]
|
135
|
-
|
136
|
-
# set graphic state for the box
|
137
|
-
box_stream << "q\n"
|
138
|
-
box_graphic_state = { ca: options[:opacity], CA: options[:opacity], LW: options[:border_width], LC: 0, LJ: 0, LD: 0, CTM: options[:ctm]}
|
139
|
-
if options[:box_radius] != 0 # if the text box has rounded corners
|
140
|
-
box_graphic_state[:LC], box_graphic_state[:LJ] = 2, 1
|
141
|
-
end
|
142
|
-
box_graphic_state = graphic_state box_graphic_state # adds the graphic state to Resources and gets the reference
|
143
|
-
box_stream << "#{PDFOperations._object_to_pdf box_graphic_state} gs\n"
|
144
|
-
|
145
|
-
# the following line was removed for Acrobat Reader compatability
|
146
|
-
# box_stream << "DeviceRGB CS\nDeviceRGB cs\n"
|
147
|
-
|
148
|
-
if options[:box_color]
|
149
|
-
box_stream << "#{options[:box_color].join(' ')} rg\n"
|
150
|
-
end
|
151
|
-
if options[:border_width].to_i > 0 && options[:border_color]
|
152
|
-
box_stream << "#{options[:border_color].join(' ')} RG\n"
|
153
|
-
end
|
154
|
-
# create the path
|
155
|
-
radius = options[:box_radius]
|
156
|
-
half_radius = (radius.to_f / 2).round 4
|
157
|
-
## set starting point
|
158
|
-
box_stream << "#{options[:x] + radius} #{options[:y]} m\n"
|
159
|
-
## buttom and right corner - first line and first corner
|
160
|
-
box_stream << "#{options[:x] + options[:width] - radius} #{options[:y]} l\n" #buttom
|
161
|
-
if options[:box_radius] != 0 # make first corner, if not straight.
|
162
|
-
box_stream << "#{options[:x] + options[:width] - half_radius} #{options[:y]} "
|
163
|
-
box_stream << "#{options[:x] + options[:width]} #{options[:y] + half_radius} "
|
164
|
-
box_stream << "#{options[:x] + options[:width]} #{options[:y] + radius} c\n"
|
165
|
-
end
|
166
|
-
## right and top-right corner
|
167
|
-
box_stream << "#{options[:x] + options[:width]} #{options[:y] + options[:height] - radius} l\n"
|
168
|
-
if options[:box_radius] != 0
|
169
|
-
box_stream << "#{options[:x] + options[:width]} #{options[:y] + options[:height] - half_radius} "
|
170
|
-
box_stream << "#{options[:x] + options[:width] - half_radius} #{options[:y] + options[:height]} "
|
171
|
-
box_stream << "#{options[:x] + options[:width] - radius} #{options[:y] + options[:height]} c\n"
|
172
|
-
end
|
173
|
-
## top and top-left corner
|
174
|
-
box_stream << "#{options[:x] + radius} #{options[:y] + options[:height]} l\n"
|
175
|
-
if options[:box_radius] != 0
|
176
|
-
box_stream << "#{options[:x] + half_radius} #{options[:y] + options[:height]} "
|
177
|
-
box_stream << "#{options[:x]} #{options[:y] + options[:height] - half_radius} "
|
178
|
-
box_stream << "#{options[:x]} #{options[:y] + options[:height] - radius} c\n"
|
179
|
-
end
|
180
|
-
## left and buttom-left corner
|
181
|
-
box_stream << "#{options[:x]} #{options[:y] + radius} l\n"
|
182
|
-
if options[:box_radius] != 0
|
183
|
-
box_stream << "#{options[:x]} #{options[:y] + half_radius} "
|
184
|
-
box_stream << "#{options[:x] + half_radius} #{options[:y]} "
|
185
|
-
box_stream << "#{options[:x] + radius} #{options[:y]} c\n"
|
186
|
-
end
|
187
|
-
# fill / stroke path
|
188
|
-
box_stream << "h\n"
|
189
|
-
if options[:box_color] && options[:border_width].to_i > 0 && options[:border_color]
|
190
|
-
box_stream << "B\n"
|
191
|
-
elsif options[:box_color] # fill if fill color is set
|
192
|
-
box_stream << "f\n"
|
193
|
-
elsif options[:border_width].to_i > 0 && options[:border_color] # stroke if border is set
|
194
|
-
box_stream << "S\n"
|
195
|
-
end
|
196
|
-
|
197
|
-
# exit graphic state for the box
|
198
|
-
box_stream << "Q\n"
|
199
|
-
end
|
200
|
-
contents << box_stream
|
201
|
-
|
202
|
-
# reset x,y by text alignment - x,y are calculated from the buttom left
|
203
|
-
# each unit (1) is 1/72 Inch
|
204
|
-
# create text stream
|
205
|
-
text_stream = ""
|
206
|
-
if text.to_s != "" && options[:font_size] != 0 && (options[:font_color] || options[:stroke_color])
|
207
|
-
# compute x and y position for text
|
208
|
-
x = options[:x] + (options[:width]*options[:text_padding])
|
209
|
-
y = options[:y] + (options[:height]*options[:text_padding])
|
210
|
-
|
211
|
-
# set the fonts (fonts array, with :Helvetica as fallback).
|
212
|
-
fonts = [*options[:font], :Helvetica]
|
213
|
-
# fit text in box, if requested
|
214
|
-
font_size = options[:font_size]
|
215
|
-
if options[:font_size] == :fit_text
|
216
|
-
font_size = self.fit_text text, fonts, (options[:width]*(1-options[:text_padding])), (options[:height]*(1-options[:text_padding]))
|
217
|
-
font_size = options[:max_font_size] if options[:max_font_size] && font_size > options[:max_font_size]
|
218
|
-
end
|
219
|
-
|
220
|
-
text_size = dimensions_of text, fonts, font_size
|
221
|
-
|
222
|
-
if options[:text_align] == :center
|
223
|
-
x = ( ( options[:width]*(1-(2*options[:text_padding])) ) - text_size[0] )/2 + x
|
224
|
-
elsif options[:text_align] == :right
|
225
|
-
x = ( ( options[:width]*(1-(1.5*options[:text_padding])) ) - text_size[0] ) + x
|
226
|
-
end
|
227
|
-
if options[:text_valign] == :center
|
228
|
-
y = ( ( options[:height]*(1-(2*options[:text_padding])) ) - text_size[1] )/2 + y
|
229
|
-
elsif options[:text_valign] == :top
|
230
|
-
y = ( options[:height]*(1-(1.5*options[:text_padding])) ) - text_size[1] + y
|
231
|
-
end
|
232
|
-
|
233
|
-
# set graphic state for text
|
234
|
-
text_stream << "q\n"
|
235
|
-
text_graphic_state = graphic_state({ca: options[:opacity], CA: options[:opacity], LW: options[:stroke_width].to_f, LC: 2, LJ: 1, LD: 0, CTM: options[:ctm]})
|
236
|
-
text_stream << "#{PDFOperations._object_to_pdf text_graphic_state} gs\n"
|
237
|
-
|
238
|
-
# the following line was removed for Acrobat Reader compatability
|
239
|
-
# text_stream << "DeviceRGB CS\nDeviceRGB cs\n"
|
240
|
-
|
241
|
-
# set text render mode
|
242
|
-
if options[:font_color]
|
243
|
-
text_stream << "#{options[:font_color].join(' ')} rg\n"
|
244
|
-
end
|
245
|
-
if options[:stroke_width].to_i > 0 && options[:stroke_color]
|
246
|
-
text_stream << "#{options[:stroke_color].join(' ')} RG\n"
|
247
|
-
if options[:font_color]
|
248
|
-
text_stream << "2 Tr\n"
|
249
|
-
else
|
250
|
-
final_stream << "1 Tr\n"
|
251
|
-
end
|
252
|
-
elsif options[:font_color]
|
253
|
-
text_stream << "0 Tr\n"
|
254
|
-
else
|
255
|
-
text_stream << "3 Tr\n"
|
256
|
-
end
|
257
|
-
# format text object(s)
|
258
|
-
# text_stream << "#{options[:font_color].join(' ')} rg\n" # sets the color state
|
259
|
-
encode(text, fonts).each do |encoded|
|
260
|
-
text_stream << "BT\n" # the Begine Text marker
|
261
|
-
text_stream << PDFOperations._format_name_to_pdf(set_font encoded[0]) # Set font name
|
262
|
-
text_stream << " #{font_size.round 3} Tf\n" # set font size and add font operator
|
263
|
-
text_stream << "#{x.round 4} #{y.round 4} Td\n" # set location for text object
|
264
|
-
text_stream << ( encoded[1] ) # insert the encoded string to the stream
|
265
|
-
text_stream << " Tj\n" # the Text object operator and the End Text marker
|
266
|
-
text_stream << "ET\n" # the Text object operator and the End Text marker
|
267
|
-
x += encoded[2]/1000*font_size #update text starting point
|
268
|
-
y -= encoded[3]/1000*font_size #update text starting point
|
269
|
-
end
|
270
|
-
# exit graphic state for text
|
271
|
-
text_stream << "Q\n"
|
272
|
-
end
|
273
|
-
contents << text_stream
|
274
|
-
|
275
|
-
self
|
276
|
-
end
|
277
|
-
# gets the dimentions (width and height) of the text, as it will be printed in the PDF.
|
278
|
-
#
|
279
|
-
# text:: the text to measure
|
280
|
-
# font:: a font name or an Array of font names. Font names should be registered fonts. The 14 standard fonts are pre regitered with the font library.
|
281
|
-
# size:: the size of the font (defaults to 1000 points).
|
282
|
-
def dimensions_of(text, fonts, size = 1000)
|
283
|
-
Fonts.dimensions_of text, fonts, size
|
284
|
-
end
|
285
|
-
# this method returns the size for which the text fits the requested metrices
|
286
|
-
# the size is type Float and is rather exact
|
287
|
-
# if the text cannot fit such a small place, returns zero (0).
|
288
|
-
# maximum font size possible is set to 100,000 - which should be big enough for anything
|
289
|
-
# text:: the text to fit
|
290
|
-
# font:: the font name. @see font
|
291
|
-
# length:: the length to fit
|
292
|
-
# height:: the height to fit (optional - normally length is the issue)
|
293
|
-
def fit_text(text, font, length, height = 10000000)
|
294
|
-
size = 100000
|
295
|
-
size_array = [size]
|
296
|
-
metrics = Fonts.dimensions_of text, font, size
|
297
|
-
if metrics[0] > length
|
298
|
-
size_array << size * length/metrics[0]
|
299
|
-
end
|
300
|
-
if metrics[1] > height
|
301
|
-
size_array << size * height/metrics[1]
|
302
|
-
end
|
303
|
-
size_array.min
|
304
|
-
end
|
305
|
-
|
306
51
|
|
307
|
-
|
52
|
+
# includes the PDF Page_Methods module, including all page methods (textbox etc').
|
53
|
+
include Page_Methods
|
54
|
+
|
55
|
+
# # accessor (getter) for the :MediaBox element of the page
|
56
|
+
# def mediabox
|
57
|
+
# self[:MediaBox]
|
58
|
+
# end
|
59
|
+
# # accessor (setter) for the :MediaBox element of the page
|
60
|
+
# # dimensions:: an Array consisting of four numbers (can be floats) setting the size of the media box.
|
61
|
+
# def mediabox=(dimensions = [0.0, 0.0, 612.0, 792.0])
|
62
|
+
# self[:MediaBox] = dimensions
|
63
|
+
# end
|
64
|
+
|
65
|
+
# # This method adds a simple text box to the Page represented by the PDFWriter class.
|
66
|
+
# # This function takes two values:
|
67
|
+
# # text:: the text to potin the box.
|
68
|
+
# # properties:: a Hash of box properties.
|
69
|
+
# # the symbols and values in the properties Hash could be any or all of the following:
|
70
|
+
# # x:: the left position of the box.
|
71
|
+
# # y:: the BUTTOM position of the box.
|
72
|
+
# # width:: the width/length of the box. negative values will be computed from edge of page. defaults to 0 (end of page).
|
73
|
+
# # height:: the height of the box. negative values will be computed from edge of page. defaults to 0 (end of page).
|
74
|
+
# # text_align:: symbol for horizontal text alignment, can be ":center" (default), ":right", ":left"
|
75
|
+
# # text_valign:: symbol for vertical text alignment, can be ":center" (default), ":top", ":buttom"
|
76
|
+
# # text_padding:: a Float between 0 and 1, setting the padding for the text. defaults to 0.05 (5%).
|
77
|
+
# # font:: a registered font name or an Array of names. defaults to ":Helvetica". The 14 standard fonts names are:
|
78
|
+
# # - :"Times-Roman"
|
79
|
+
# # - :"Times-Bold"
|
80
|
+
# # - :"Times-Italic"
|
81
|
+
# # - :"Times-BoldItalic"
|
82
|
+
# # - :Helvetica
|
83
|
+
# # - :"Helvetica-Bold"
|
84
|
+
# # - :"Helvetica-BoldOblique"
|
85
|
+
# # - :"Helvetica- Oblique"
|
86
|
+
# # - :Courier
|
87
|
+
# # - :"Courier-Bold"
|
88
|
+
# # - :"Courier-Oblique"
|
89
|
+
# # - :"Courier-BoldOblique"
|
90
|
+
# # - :Symbol
|
91
|
+
# # - :ZapfDingbats
|
92
|
+
# # font_size:: a Fixnum for the font size, or :fit_text to fit the text in the box. defaults to ":fit_text"
|
93
|
+
# # max_font_size:: if font_size is set to :fit_text, this will be the maximum font size. defaults to nil (no maximum)
|
94
|
+
# # font_color:: text color in [R, G, B], an array with three floats, each in a value between 0 to 1 (gray will be "[0.5, 0.5, 0.5]"). defaults to black.
|
95
|
+
# # stroke_color:: text stroke color in [R, G, B], an array with three floats, each in a value between 0 to 1 (gray will be "[0.5, 0.5, 0.5]"). defounlts to nil (no stroke).
|
96
|
+
# # stroke_width:: text stroke width in PDF units. defaults to 0 (none).
|
97
|
+
# # box_color:: box fill color in [R, G, B], an array with three floats, each in a value between 0 to 1 (gray will be "[0.5, 0.5, 0.5]"). defaults to nil (none).
|
98
|
+
# # border_color:: box border color in [R, G, B], an array with three floats, each in a value between 0 to 1 (gray will be "[0.5, 0.5, 0.5]"). defaults to nil (none).
|
99
|
+
# # border_width:: border width in PDF units. defaults to nil (none).
|
100
|
+
# # box_radius:: border radius in PDF units. defaults to 0 (no corner rounding).
|
101
|
+
# # opacity:: textbox opacity, a float between 0 (transparent) and 1 (opaque)
|
102
|
+
# def textbox(text, properties = {})
|
103
|
+
# options = {
|
104
|
+
# x: 0,
|
105
|
+
# y: 0,
|
106
|
+
# width: 0,
|
107
|
+
# height: -1,
|
108
|
+
# text_align: :center,
|
109
|
+
# text_valign: :center,
|
110
|
+
# text_padding: 0.1,
|
111
|
+
# font: nil,
|
112
|
+
# font_size: :fit_text,
|
113
|
+
# max_font_size: nil,
|
114
|
+
# font_color: [0,0,0],
|
115
|
+
# stroke_color: nil,
|
116
|
+
# stroke_width: 0,
|
117
|
+
# box_color: nil,
|
118
|
+
# border_color: nil,
|
119
|
+
# border_width: 0,
|
120
|
+
# box_radius: 0,
|
121
|
+
# opacity: 1,
|
122
|
+
# ctm: [1,0,0,1,0,0]
|
123
|
+
# }
|
124
|
+
# options.update properties
|
125
|
+
# # reset the length and height to meaningful values, if negative
|
126
|
+
# options[:width] = mediabox[2] - options[:x] + options[:width] if options[:width] <= 0
|
127
|
+
# options[:height] = mediabox[3] - options[:y] + options[:height] if options[:height] <= 0
|
128
|
+
|
129
|
+
# # reset the padding value
|
130
|
+
# options[:text_padding] = 0 if options[:text_padding].to_f >= 1
|
131
|
+
|
132
|
+
# # create box stream
|
133
|
+
# box_stream = ""
|
134
|
+
# # set graphic state for box
|
135
|
+
# if options[:box_color] || (options[:border_width].to_i > 0 && options[:border_color])
|
136
|
+
# # compute x and y position for text
|
137
|
+
# x = options[:x]
|
138
|
+
# y = options[:y]
|
139
|
+
|
140
|
+
# # set graphic state for the box
|
141
|
+
# box_stream << "q\n"
|
142
|
+
# box_graphic_state = { ca: options[:opacity], CA: options[:opacity], LW: options[:border_width], LC: 0, LJ: 0, LD: 0, CTM: options[:ctm]}
|
143
|
+
# if options[:box_radius] != 0 # if the text box has rounded corners
|
144
|
+
# box_graphic_state[:LC], box_graphic_state[:LJ] = 2, 1
|
145
|
+
# end
|
146
|
+
# box_graphic_state = graphic_state box_graphic_state # adds the graphic state to Resources and gets the reference
|
147
|
+
# box_stream << "#{PDFOperations._object_to_pdf box_graphic_state} gs\n"
|
148
|
+
|
149
|
+
# # the following line was removed for Acrobat Reader compatability
|
150
|
+
# # box_stream << "DeviceRGB CS\nDeviceRGB cs\n"
|
151
|
+
|
152
|
+
# if options[:box_color]
|
153
|
+
# box_stream << "#{options[:box_color].join(' ')} rg\n"
|
154
|
+
# end
|
155
|
+
# if options[:border_width].to_i > 0 && options[:border_color]
|
156
|
+
# box_stream << "#{options[:border_color].join(' ')} RG\n"
|
157
|
+
# end
|
158
|
+
# # create the path
|
159
|
+
# radius = options[:box_radius]
|
160
|
+
# half_radius = (radius.to_f / 2).round 4
|
161
|
+
# ## set starting point
|
162
|
+
# box_stream << "#{options[:x] + radius} #{options[:y]} m\n"
|
163
|
+
# ## buttom and right corner - first line and first corner
|
164
|
+
# box_stream << "#{options[:x] + options[:width] - radius} #{options[:y]} l\n" #buttom
|
165
|
+
# if options[:box_radius] != 0 # make first corner, if not straight.
|
166
|
+
# box_stream << "#{options[:x] + options[:width] - half_radius} #{options[:y]} "
|
167
|
+
# box_stream << "#{options[:x] + options[:width]} #{options[:y] + half_radius} "
|
168
|
+
# box_stream << "#{options[:x] + options[:width]} #{options[:y] + radius} c\n"
|
169
|
+
# end
|
170
|
+
# ## right and top-right corner
|
171
|
+
# box_stream << "#{options[:x] + options[:width]} #{options[:y] + options[:height] - radius} l\n"
|
172
|
+
# if options[:box_radius] != 0
|
173
|
+
# box_stream << "#{options[:x] + options[:width]} #{options[:y] + options[:height] - half_radius} "
|
174
|
+
# box_stream << "#{options[:x] + options[:width] - half_radius} #{options[:y] + options[:height]} "
|
175
|
+
# box_stream << "#{options[:x] + options[:width] - radius} #{options[:y] + options[:height]} c\n"
|
176
|
+
# end
|
177
|
+
# ## top and top-left corner
|
178
|
+
# box_stream << "#{options[:x] + radius} #{options[:y] + options[:height]} l\n"
|
179
|
+
# if options[:box_radius] != 0
|
180
|
+
# box_stream << "#{options[:x] + half_radius} #{options[:y] + options[:height]} "
|
181
|
+
# box_stream << "#{options[:x]} #{options[:y] + options[:height] - half_radius} "
|
182
|
+
# box_stream << "#{options[:x]} #{options[:y] + options[:height] - radius} c\n"
|
183
|
+
# end
|
184
|
+
# ## left and buttom-left corner
|
185
|
+
# box_stream << "#{options[:x]} #{options[:y] + radius} l\n"
|
186
|
+
# if options[:box_radius] != 0
|
187
|
+
# box_stream << "#{options[:x]} #{options[:y] + half_radius} "
|
188
|
+
# box_stream << "#{options[:x] + half_radius} #{options[:y]} "
|
189
|
+
# box_stream << "#{options[:x] + radius} #{options[:y]} c\n"
|
190
|
+
# end
|
191
|
+
# # fill / stroke path
|
192
|
+
# box_stream << "h\n"
|
193
|
+
# if options[:box_color] && options[:border_width].to_i > 0 && options[:border_color]
|
194
|
+
# box_stream << "B\n"
|
195
|
+
# elsif options[:box_color] # fill if fill color is set
|
196
|
+
# box_stream << "f\n"
|
197
|
+
# elsif options[:border_width].to_i > 0 && options[:border_color] # stroke if border is set
|
198
|
+
# box_stream << "S\n"
|
199
|
+
# end
|
200
|
+
|
201
|
+
# # exit graphic state for the box
|
202
|
+
# box_stream << "Q\n"
|
203
|
+
# end
|
204
|
+
# contents << box_stream
|
205
|
+
|
206
|
+
# # reset x,y by text alignment - x,y are calculated from the buttom left
|
207
|
+
# # each unit (1) is 1/72 Inch
|
208
|
+
# # create text stream
|
209
|
+
# text_stream = ""
|
210
|
+
# if text.to_s != "" && options[:font_size] != 0 && (options[:font_color] || options[:stroke_color])
|
211
|
+
# # compute x and y position for text
|
212
|
+
# x = options[:x] + (options[:width]*options[:text_padding])
|
213
|
+
# y = options[:y] + (options[:height]*options[:text_padding])
|
214
|
+
|
215
|
+
# # set the fonts (fonts array, with :Helvetica as fallback).
|
216
|
+
# fonts = [*options[:font], :Helvetica]
|
217
|
+
# # fit text in box, if requested
|
218
|
+
# font_size = options[:font_size]
|
219
|
+
# if options[:font_size] == :fit_text
|
220
|
+
# font_size = self.fit_text text, fonts, (options[:width]*(1-options[:text_padding])), (options[:height]*(1-options[:text_padding]))
|
221
|
+
# font_size = options[:max_font_size] if options[:max_font_size] && font_size > options[:max_font_size]
|
222
|
+
# end
|
223
|
+
|
224
|
+
# text_size = dimensions_of text, fonts, font_size
|
225
|
+
|
226
|
+
# if options[:text_align] == :center
|
227
|
+
# x = ( ( options[:width]*(1-(2*options[:text_padding])) ) - text_size[0] )/2 + x
|
228
|
+
# elsif options[:text_align] == :right
|
229
|
+
# x = ( ( options[:width]*(1-(1.5*options[:text_padding])) ) - text_size[0] ) + x
|
230
|
+
# end
|
231
|
+
# if options[:text_valign] == :center
|
232
|
+
# y = ( ( options[:height]*(1-(2*options[:text_padding])) ) - text_size[1] )/2 + y
|
233
|
+
# elsif options[:text_valign] == :top
|
234
|
+
# y = ( options[:height]*(1-(1.5*options[:text_padding])) ) - text_size[1] + y
|
235
|
+
# end
|
236
|
+
|
237
|
+
# # set graphic state for text
|
238
|
+
# text_stream << "q\n"
|
239
|
+
# text_graphic_state = graphic_state({ca: options[:opacity], CA: options[:opacity], LW: options[:stroke_width].to_f, LC: 2, LJ: 1, LD: 0, CTM: options[:ctm]})
|
240
|
+
# text_stream << "#{PDFOperations._object_to_pdf text_graphic_state} gs\n"
|
241
|
+
|
242
|
+
# # the following line was removed for Acrobat Reader compatability
|
243
|
+
# # text_stream << "DeviceRGB CS\nDeviceRGB cs\n"
|
244
|
+
|
245
|
+
# # set text render mode
|
246
|
+
# if options[:font_color]
|
247
|
+
# text_stream << "#{options[:font_color].join(' ')} rg\n"
|
248
|
+
# end
|
249
|
+
# if options[:stroke_width].to_i > 0 && options[:stroke_color]
|
250
|
+
# text_stream << "#{options[:stroke_color].join(' ')} RG\n"
|
251
|
+
# if options[:font_color]
|
252
|
+
# text_stream << "2 Tr\n"
|
253
|
+
# else
|
254
|
+
# final_stream << "1 Tr\n"
|
255
|
+
# end
|
256
|
+
# elsif options[:font_color]
|
257
|
+
# text_stream << "0 Tr\n"
|
258
|
+
# else
|
259
|
+
# text_stream << "3 Tr\n"
|
260
|
+
# end
|
261
|
+
# # format text object(s)
|
262
|
+
# # text_stream << "#{options[:font_color].join(' ')} rg\n" # sets the color state
|
263
|
+
# encode(text, fonts).each do |encoded|
|
264
|
+
# text_stream << "BT\n" # the Begine Text marker
|
265
|
+
# text_stream << PDFOperations._format_name_to_pdf(set_font encoded[0]) # Set font name
|
266
|
+
# text_stream << " #{font_size.round 3} Tf\n" # set font size and add font operator
|
267
|
+
# text_stream << "#{x.round 4} #{y.round 4} Td\n" # set location for text object
|
268
|
+
# text_stream << ( encoded[1] ) # insert the encoded string to the stream
|
269
|
+
# text_stream << " Tj\n" # the Text object operator and the End Text marker
|
270
|
+
# text_stream << "ET\n" # the Text object operator and the End Text marker
|
271
|
+
# x += encoded[2]/1000*font_size #update text starting point
|
272
|
+
# y -= encoded[3]/1000*font_size #update text starting point
|
273
|
+
# end
|
274
|
+
# # exit graphic state for text
|
275
|
+
# text_stream << "Q\n"
|
276
|
+
# end
|
277
|
+
# contents << text_stream
|
278
|
+
|
279
|
+
# self
|
280
|
+
# end
|
281
|
+
# # gets the dimentions (width and height) of the text, as it will be printed in the PDF.
|
282
|
+
# #
|
283
|
+
# # text:: the text to measure
|
284
|
+
# # font:: a font name or an Array of font names. Font names should be registered fonts. The 14 standard fonts are pre regitered with the font library.
|
285
|
+
# # size:: the size of the font (defaults to 1000 points).
|
286
|
+
# def dimensions_of(text, fonts, size = 1000)
|
287
|
+
# Fonts.dimensions_of text, fonts, size
|
288
|
+
# end
|
289
|
+
# # this method returns the size for which the text fits the requested metrices
|
290
|
+
# # the size is type Float and is rather exact
|
291
|
+
# # if the text cannot fit such a small place, returns zero (0).
|
292
|
+
# # maximum font size possible is set to 100,000 - which should be big enough for anything
|
293
|
+
# # text:: the text to fit
|
294
|
+
# # font:: the font name. @see font
|
295
|
+
# # length:: the length to fit
|
296
|
+
# # height:: the height to fit (optional - normally length is the issue)
|
297
|
+
# def fit_text(text, font, length, height = 10000000)
|
298
|
+
# size = 100000
|
299
|
+
# size_array = [size]
|
300
|
+
# metrics = Fonts.dimensions_of text, font, size
|
301
|
+
# if metrics[0] > length
|
302
|
+
# size_array << size * length/metrics[0]
|
303
|
+
# end
|
304
|
+
# if metrics[1] > height
|
305
|
+
# size_array << size * height/metrics[1]
|
306
|
+
# end
|
307
|
+
# size_array.min
|
308
|
+
# end
|
309
|
+
|
310
|
+
|
311
|
+
# protected
|
312
|
+
|
313
|
+
# # accessor (getter) for the :Resources element of the page
|
314
|
+
# def resources
|
315
|
+
# self[:Resources]
|
316
|
+
# end
|
317
|
+
# # accessor (getter) for the stream in the :Contents element of the page
|
318
|
+
# # after getting the string object, you can operate on it but not replace it (use << or other String methods).
|
319
|
+
# def contents
|
320
|
+
# @contents
|
321
|
+
# end
|
322
|
+
# # creates a font object and adds the font to the resources dictionary
|
323
|
+
# # returns the name of the font for the content stream.
|
324
|
+
# # font:: a Symbol of one of the fonts registered in the library, or:
|
325
|
+
# # - :"Times-Roman"
|
326
|
+
# # - :"Times-Bold"
|
327
|
+
# # - :"Times-Italic"
|
328
|
+
# # - :"Times-BoldItalic"
|
329
|
+
# # - :Helvetica
|
330
|
+
# # - :"Helvetica-Bold"
|
331
|
+
# # - :"Helvetica-BoldOblique"
|
332
|
+
# # - :"Helvetica- Oblique"
|
333
|
+
# # - :Courier
|
334
|
+
# # - :"Courier-Bold"
|
335
|
+
# # - :"Courier-Oblique"
|
336
|
+
# # - :"Courier-BoldOblique"
|
337
|
+
# # - :Symbol
|
338
|
+
# # - :ZapfDingbats
|
339
|
+
# def set_font(font = :Helvetica)
|
340
|
+
# # if the font exists, return it's name
|
341
|
+
# resources[:Font] ||= {}
|
342
|
+
# resources[:Font].each do |k,v|
|
343
|
+
# if v.is_a?(Fonts::Font) && v.name && v.name == font
|
344
|
+
# return k
|
345
|
+
# end
|
346
|
+
# end
|
347
|
+
# # set a secure name for the font
|
348
|
+
# name = (@base_font_name + (resources[:Font].length + 1).to_s).to_sym
|
349
|
+
# # get font object
|
350
|
+
# font_object = Fonts.get_font(font)
|
351
|
+
# # return false if the font wan't found in the library.
|
352
|
+
# return false unless font_object
|
353
|
+
# # add object to reasource
|
354
|
+
# resources[:Font][name] = font_object
|
355
|
+
# #return name
|
356
|
+
# name
|
357
|
+
# end
|
358
|
+
# # register or get a registered graphic state dictionary.
|
359
|
+
# # the method returns the name of the graphos state, for use in a content stream.
|
360
|
+
# def graphic_state(graphic_state_dictionary = {})
|
361
|
+
# # if the graphic state exists, return it's name
|
362
|
+
# resources[:ExtGState] ||= {}
|
363
|
+
# resources[:ExtGState].each do |k,v|
|
364
|
+
# if v.is_a?(Hash) && v == graphic_state_dictionary
|
365
|
+
# return k
|
366
|
+
# end
|
367
|
+
# end
|
368
|
+
# # set graphic state type
|
369
|
+
# graphic_state_dictionary[:Type] = :ExtGState
|
370
|
+
# # set a secure name for the graphic state
|
371
|
+
# name = (SecureRandom.hex(9)).to_sym
|
372
|
+
# # add object to reasource
|
373
|
+
# resources[:ExtGState][name] = graphic_state_dictionary
|
374
|
+
# #return name
|
375
|
+
# name
|
376
|
+
# end
|
377
|
+
|
378
|
+
# # encodes the text in an array of [:font_name, <PDFHexString>] for use in textbox
|
379
|
+
# def encode text, fonts
|
380
|
+
# # text must be a unicode string and fonts must be an array.
|
381
|
+
# # this is an internal method, don't perform tests.
|
382
|
+
# fonts_array = []
|
383
|
+
# fonts.each do |name|
|
384
|
+
# f = Fonts.get_font name
|
385
|
+
# fonts_array << f if f
|
386
|
+
# end
|
387
|
+
|
388
|
+
# # before starting, we should reorder any RTL content in the string
|
389
|
+
# text = reorder_rtl_content text
|
390
|
+
|
391
|
+
# out = []
|
392
|
+
# text.chars.each do |c|
|
393
|
+
# fonts_array.each_index do |i|
|
394
|
+
# if fonts_array[i].cmap.nil? || (fonts_array[i].cmap && fonts_array[i].cmap[c])
|
395
|
+
# #add to array
|
396
|
+
# if out.last.nil? || out.last[0] != fonts[i]
|
397
|
+
# out.last[1] << ">" unless out.last.nil?
|
398
|
+
# out << [fonts[i], "<" , 0, 0]
|
399
|
+
# end
|
400
|
+
# out.last[1] << ( fonts_array[i].cmap.nil? ? ( c.unpack("H*")[0] ) : (fonts_array[i].cmap[c]) )
|
401
|
+
# if fonts_array[i].metrics[c]
|
402
|
+
# out.last[2] += fonts_array[i].metrics[c][:wx].to_f
|
403
|
+
# out.last[3] += fonts_array[i].metrics[c][:wy].to_f
|
404
|
+
# end
|
405
|
+
# break
|
406
|
+
# end
|
407
|
+
# end
|
408
|
+
# end
|
409
|
+
# out.last[1] << ">" if out.last
|
410
|
+
# out
|
411
|
+
# end
|
412
|
+
|
413
|
+
# # a very primitive text reordering algorithm... I was lazy...
|
414
|
+
# # ...still, it works (I think).
|
415
|
+
# def reorder_rtl_content text
|
416
|
+
# rtl_characters = "\u05d0-\u05ea\u05f0-\u05f4\u0600-\u06ff\u0750-\u077f"
|
417
|
+
# rtl_replaces = { '(' => ')', ')' => '(',
|
418
|
+
# '[' => ']', ']'=>'[',
|
419
|
+
# '{' => '}', '}'=>'{',
|
420
|
+
# '<' => '>', '>'=>'<',
|
421
|
+
# }
|
422
|
+
# return text unless text =~ /[#{rtl_characters}]/
|
423
|
+
|
424
|
+
# out = []
|
425
|
+
# scanner = StringScanner.new text
|
426
|
+
# until scanner.eos? do
|
427
|
+
# if scanner.scan /[#{rtl_characters} ]/
|
428
|
+
# out.unshift scanner.matched
|
429
|
+
# elsif scanner.scan /[^#{rtl_characters}]+/
|
430
|
+
# if out.empty? && scanner.matched.match(/[\s]$/) && !scanner.eos?
|
431
|
+
# white_space_to_move = scanner.matched.match(/[\s]+$/).to_s
|
432
|
+
# out.unshift scanner.matched[0..-1-white_space_to_move.length]
|
433
|
+
# out.unshift white_space_to_move
|
434
|
+
# elsif scanner.matched.match /^[\(\)\[\]\{\}\<\>]$/
|
435
|
+
# out.unshift rtl_replaces[scanner.matched]
|
436
|
+
# else
|
437
|
+
# out.unshift scanner.matched
|
438
|
+
# end
|
439
|
+
# end
|
440
|
+
# end
|
441
|
+
# out.join.strip
|
442
|
+
# end
|
308
443
|
|
309
|
-
# accessor (getter) for the :Resources element of the page
|
310
|
-
def resources
|
311
|
-
self[:Resources]
|
312
|
-
end
|
313
|
-
# accessor (getter) for the stream in the :Contents element of the page
|
314
|
-
# after getting the string object, you can operate on it but not replace it (use << or other String methods).
|
315
|
-
def contents
|
316
|
-
@contents
|
317
|
-
end
|
318
|
-
# creates a font object and adds the font to the resources dictionary
|
319
|
-
# returns the name of the font for the content stream.
|
320
|
-
# font:: a Symbol of one of the fonts registered in the library, or:
|
321
|
-
# - :"Times-Roman"
|
322
|
-
# - :"Times-Bold"
|
323
|
-
# - :"Times-Italic"
|
324
|
-
# - :"Times-BoldItalic"
|
325
|
-
# - :Helvetica
|
326
|
-
# - :"Helvetica-Bold"
|
327
|
-
# - :"Helvetica-BoldOblique"
|
328
|
-
# - :"Helvetica- Oblique"
|
329
|
-
# - :Courier
|
330
|
-
# - :"Courier-Bold"
|
331
|
-
# - :"Courier-Oblique"
|
332
|
-
# - :"Courier-BoldOblique"
|
333
|
-
# - :Symbol
|
334
|
-
# - :ZapfDingbats
|
335
|
-
def set_font(font = :Helvetica)
|
336
|
-
# if the font exists, return it's name
|
337
|
-
resources[:Font] ||= {}
|
338
|
-
resources[:Font].each do |k,v|
|
339
|
-
if v.is_a?(Fonts::Font) && v.name && v.name == font
|
340
|
-
return k
|
341
|
-
end
|
342
|
-
end
|
343
|
-
# set a secure name for the font
|
344
|
-
name = (@base_font_name + (resources[:Font].length + 1).to_s).to_sym
|
345
|
-
# get font object
|
346
|
-
font_object = Fonts.get_font(font)
|
347
|
-
# return false if the font wan't found in the library.
|
348
|
-
return false unless font_object
|
349
|
-
# add object to reasource
|
350
|
-
resources[:Font][name] = font_object
|
351
|
-
#return name
|
352
|
-
name
|
353
|
-
end
|
354
|
-
# register or get a registered graphic state dictionary.
|
355
|
-
# the method returns the name of the graphos state, for use in a content stream.
|
356
|
-
def graphic_state(graphic_state_dictionary = {})
|
357
|
-
# if the graphic state exists, return it's name
|
358
|
-
resources[:ExtGState] ||= {}
|
359
|
-
resources[:ExtGState].each do |k,v|
|
360
|
-
if v.is_a?(Hash) && v == graphic_state_dictionary
|
361
|
-
return k
|
362
|
-
end
|
363
|
-
end
|
364
|
-
# set graphic state type
|
365
|
-
graphic_state_dictionary[:Type] = :ExtGState
|
366
|
-
# set a secure name for the graphic state
|
367
|
-
name = (SecureRandom.hex(9)).to_sym
|
368
|
-
# add object to reasource
|
369
|
-
resources[:ExtGState][name] = graphic_state_dictionary
|
370
|
-
#return name
|
371
|
-
name
|
372
|
-
end
|
373
|
-
|
374
|
-
# encodes the text in an array of [:font_name, <PDFHexString>] for use in textbox
|
375
|
-
def encode text, fonts
|
376
|
-
# text must be a unicode string and fonts must be an array.
|
377
|
-
# this is an internal method, don't perform tests.
|
378
|
-
fonts_array = []
|
379
|
-
fonts.each do |name|
|
380
|
-
f = Fonts.get_font name
|
381
|
-
fonts_array << f if f
|
382
|
-
end
|
383
|
-
|
384
|
-
# before starting, we should reorder any RTL content in the string
|
385
|
-
text = reorder_rtl_content text
|
386
|
-
|
387
|
-
out = []
|
388
|
-
text.chars.each do |c|
|
389
|
-
fonts_array.each_index do |i|
|
390
|
-
if fonts_array[i].cmap.nil? || (fonts_array[i].cmap && fonts_array[i].cmap[c])
|
391
|
-
#add to array
|
392
|
-
if out.last.nil? || out.last[0] != fonts[i]
|
393
|
-
out.last[1] << ">" unless out.last.nil?
|
394
|
-
out << [fonts[i], "<" , 0, 0]
|
395
|
-
end
|
396
|
-
out.last[1] << ( fonts_array[i].cmap.nil? ? ( c.unpack("H*")[0] ) : (fonts_array[i].cmap[c]) )
|
397
|
-
if fonts_array[i].metrics[c]
|
398
|
-
out.last[2] += fonts_array[i].metrics[c][:wx].to_f
|
399
|
-
out.last[3] += fonts_array[i].metrics[c][:wy].to_f
|
400
|
-
end
|
401
|
-
break
|
402
|
-
end
|
403
|
-
end
|
404
|
-
end
|
405
|
-
out.last[1] << ">" if out.last
|
406
|
-
out
|
407
|
-
end
|
408
|
-
|
409
|
-
# a very primitive text reordering algorithm... I was lazy...
|
410
|
-
# ...still, it works (I think).
|
411
|
-
def reorder_rtl_content text
|
412
|
-
rtl_characters = "\u05d0-\u05ea\u05f0-\u05f4\u0600-\u06ff\u0750-\u077f"
|
413
|
-
rtl_replaces = { '(' => ')', ')' => '(',
|
414
|
-
'[' => ']', ']'=>'[',
|
415
|
-
'{' => '}', '}'=>'{',
|
416
|
-
'<' => '>', '>'=>'<',
|
417
|
-
}
|
418
|
-
return text unless text =~ /[#{rtl_characters}]/
|
419
|
-
|
420
|
-
out = []
|
421
|
-
scanner = StringScanner.new text
|
422
|
-
until scanner.eos? do
|
423
|
-
if scanner.scan /[#{rtl_characters} ]/
|
424
|
-
out.unshift scanner.matched
|
425
|
-
elsif scanner.scan /[^#{rtl_characters}]+/
|
426
|
-
if out.empty? && scanner.matched.match(/[\s]$/) && !scanner.eos?
|
427
|
-
white_space_to_move = scanner.matched.match(/[\s]+$/).to_s
|
428
|
-
out.unshift scanner.matched[0..-1-white_space_to_move.length]
|
429
|
-
out.unshift white_space_to_move
|
430
|
-
elsif scanner.matched.match /^[\(\)\[\]\{\}\<\>]$/
|
431
|
-
out.unshift rtl_replaces[scanner.matched]
|
432
|
-
else
|
433
|
-
out.unshift scanner.matched
|
434
|
-
end
|
435
|
-
end
|
436
|
-
end
|
437
|
-
out.join.strip
|
438
|
-
end
|
439
444
|
end
|
440
445
|
|
441
446
|
end
|