rutils 0.2.1 → 0.2.2

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.
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ Версия 0.2.2 - 24.09.2007
2
+ * Gilenson - обрабатываем акронимы по принципу Textile (zajats, julik)
3
+ * Gilenson - отформатирован под 2 пробела (julik)
4
+ * В тестах пользуйтесь assert_equal_cp (julik)
5
+ * Welcome to subversion (julik)
6
+
1
7
  Версия 0.2.1 - 02.04.2007
2
8
  * Восстановлен в gemspec gilenson.rb (Сергей Барабаш)
3
9
 
data/bin/rutilize CHANGED
@@ -15,8 +15,8 @@
15
15
  $KCODE = 'u'
16
16
  require File.dirname(__FILE__) + '/../lib/rutils'
17
17
  require 'optparse'
18
- require 'rdoc/usage'
19
- require 'fileutils'
18
+ require 'rdoc/usage'
19
+ require 'fileutils'
20
20
  include FileUtils::Verbose
21
21
 
22
22
  opts = OptionParser.new
@@ -80,6 +80,9 @@ end
80
80
  # "copypaste" - замена непечатных и "специальных" юникодных символов на entities
81
81
  # "(c)" - обрабатывать знак копирайта
82
82
  # "(r)", "(tm)", "(p)", "+-" - спецсимволы, какие - понятно
83
+ # "acronyms" - сворачивание пояснений к аббревиатурам (пояснение - в скобках после аббревиатуры
84
+ # без пробела). В текстовой версии пояснение будет "приклеено" к аббревиатуре
85
+ # полукруглой шпацией
83
86
  # "degrees" - знак градуса
84
87
  # "dashglue", "wordglue" - приклеивание предлогов и дефисов
85
88
  # "spacing" - запятые и пробелы, перестановка
@@ -89,511 +92,533 @@ end
89
92
  # "raw_output" - (по умолчанию false) - при true вместо entities выводятся UTF-символы
90
93
  # "skip_attr" - (по умолчанию false) - при true не отрабатывать типографику в атрибутах тегов (title, alt)
91
94
  # "skip_code" - (по умолчанию true) - при true не отрабатывать типографику внутри <code/>, <tt/>, CDATA
92
-
93
95
  class RuTils::Gilenson::Formatter
94
- attr_accessor :glyph
96
+ attr_accessor :glyph
97
+ attr_accessor :settings
95
98
 
96
- SETTINGS = {
97
- "inches" => true, # преобразовывать дюймы в знак дюйма;
98
- "laquo" => true, # кавычки-ёлочки
99
- "quotes" => true, # кавычки-английские лапки
100
- "dash" => true, # короткое тире (150)
101
- "emdash" => true, # длинное тире двумя минусами (151)
102
- "initials" => true, # тонкие шпации в инициалах
103
- "copypaste" => false, # замена непечатных и "специальных" юникодных символов на entities
104
- "(c)" => true, # обрабатывать знак копирайта
105
- "(r)" => true,
106
- "(tm)" => true,
107
- "(p)" => true,
108
- "+-" => true, # спецсимволы, какие - понятно
109
- "degrees" => true, # знак градуса
110
- "dashglue" => true, "wordglue" => true, # приклеивание предлогов и дефисов
111
- "spacing" => true, # запятые и пробелы, перестановка
112
- "phones" => true, # обработка телефонов
113
- "html" => true, # разрешение использования тагов html
114
- "de_nobr" => false, # при true все <nobr/> заменяются на <span class="nobr"/>
115
- "raw_output" => false, # выводить UTF-8 вместо entities
116
- "skip_attr" => false, # при true не отрабатывать типографику в атрибутах тегов
117
- "skip_code" => true, # при true не отрабатывать типографику внутри <code/>, <tt/>, CDATA
118
- } #:nodoc:
119
-
120
- # Глифы, использующиеся в подстановках по-умолчанию
121
- GLYPHS = {
122
- :quot => "&#34;", # quotation mark
123
- :amp => "&#38;", # ampersand
124
- :apos => "&#39;", # apos
125
- :gt => "&#62;", # greater-than sign
126
- :lt => "&#60;", # less-than sign
127
- :nbsp => "&#160;", # non-breaking space
128
- :sect => "&#167;", # section sign
129
- :copy => "&#169;", # copyright sign
130
- :laquo => "&#171;", # left-pointing double angle quotation mark = left pointing guillemet
131
- :reg => "&#174;", # registered sign = registered trade mark sign
132
- :deg => "&#176;", # degree sign
133
- :plusmn => "&#177;", # plus-minus sign = plus-or-minus sign
134
- :para => "&#182;", # pilcrow sign = paragraph sign
135
- :middot => "&#183;", # middle dot = Georgian comma = Greek middle dot
136
- :raquo => "&#187;", # right-pointing double angle quotation mark = right pointing guillemet
137
- :ndash => "&#8211;", # en dash
138
- :mdash => "&#8212;", # em dash
139
- :lsquo => "&#8216;", # left single quotation mark
140
- :rsquo => "&#8217;", # right single quotation mark
141
- :ldquo => "&#8220;", # left double quotation mark
142
- :rdquo => "&#8221;", # right double quotation mark
143
- :bdquo => "&#8222;", # double low-9 quotation mark
144
- :bull => "&#8226;", # bullet = black small circle
145
- :hellip => "&#8230;", # horizontal ellipsis = three dot leader
146
- :numero => "&#8470;", # numero
147
- :trade => "&#8482;", # trade mark sign
148
- :minus => "&#8722;", # minus sign
149
- :inch => "&#8243;", # inch/second sign (u0x2033) (не путать с кавычками!)
150
- :thinsp => "&#8201;", # полукруглая шпация (тонкий пробел)
151
- :nob_open => '<nobr>', # открывающий блок без переноса слов
152
- :nob_close => '</nobr>', # открывающий блок без переноса слов
153
- } #:nodoc:
99
+ SETTINGS = {
100
+ "inches" => true, # преобразовывать дюймы в знак дюйма;
101
+ "laquo" => true, # кавычки-ёлочки
102
+ "quotes" => true, # кавычки-английские лапки
103
+ "dash" => true, # короткое тире (150)
104
+ "emdash" => true, # длинное тире двумя минусами (151)
105
+ "initials" => true, # тонкие шпации в инициалах
106
+ "copypaste" => false, # замена непечатных и "специальных" юникодных символов на entities
107
+ "(c)" => true, # обрабатывать знак копирайта
108
+ "(r)" => true,
109
+ "(tm)" => true,
110
+ "(p)" => true,
111
+ "acronyms" => true, # Акронимы с пояснениями - ЖЗЛ(Жизнь Замечатльных Людей)
112
+ "+-" => true, # спецсимволы, какие - понятно
113
+ "degrees" => true, # знак градуса
114
+ "dashglue" => true, "wordglue" => true, # приклеивание предлогов и дефисов
115
+ "spacing" => true, # запятые и пробелы, перестановка
116
+ "phones" => true, # обработка телефонов
117
+ "html" => true, # разрешение использования тагов html
118
+ "de_nobr" => false, # при true все <nobr/> заменяются на <span class="nobr"/>
119
+ "raw_output" => false, # выводить UTF-8 вместо entities
120
+ "skip_attr" => false, # при true не отрабатывать типографику в атрибутах тегов
121
+ "skip_code" => true, # при true не отрабатывать типографику внутри <code/>, <tt/>, CDATA
122
+ } #:nodoc:
123
+
124
+ SETTINGS.freeze
154
125
 
155
- # Нормальные "типографские" символы в UTF-виде. Браузерами обрабатываются плохонько, поэтому
156
- # лучше заменять их на entities.
157
- VERBATIM_GLYPHS = {
158
- ' ' => :nbsp,# alt+0160 (NBSP here)
159
- '«' => :laquo,
160
- '»' => :raquo,
161
- '§' => :sect,
162
- '©' => :copy,
163
- '®' => :reg,
164
- '°' => :deg,
165
- '±' => :plusmn,
166
- '¶' => :para,
167
- '·' => :middot,
168
- '–' => :ndash,
169
- '—' => :mdash,
170
- '‘' => :lsquo,
171
- '’' => :rsquo,
172
- '“' => :ldquo,
173
- '”' => :rdquo,
174
- '„' => :bdquo,
175
- '•' => :bull,
176
- '…' => :hellip,
177
- '№' => :numero,
178
- '™' => :trade,
179
- '−' => :minus,
180
- ' ' => :thinsp,
181
- '″' => :inch,
182
- } #:nodoc:
126
+ # Глифы, использующиеся в подстановках по-умолчанию
127
+ GLYPHS = {
128
+ :quot => "&#34;", # quotation mark
129
+ :amp => "&#38;", # ampersand
130
+ :apos => "&#39;", # apos
131
+ :gt => "&#62;", # greater-than sign
132
+ :lt => "&#60;", # less-than sign
133
+ :nbsp => "&#160;", # non-breaking space
134
+ :sect => "&#167;", # section sign
135
+ :copy => "&#169;", # copyright sign
136
+ :laquo => "&#171;", # left-pointing double angle quotation mark = left pointing guillemet
137
+ :reg => "&#174;", # registered sign = registered trade mark sign
138
+ :deg => "&#176;", # degree sign
139
+ :plusmn => "&#177;", # plus-minus sign = plus-or-minus sign
140
+ :para => "&#182;", # pilcrow sign = paragraph sign
141
+ :middot => "&#183;", # middle dot = Georgian comma = Greek middle dot
142
+ :raquo => "&#187;", # right-pointing double angle quotation mark = right pointing guillemet
143
+ :ndash => "&#8211;", # en dash
144
+ :mdash => "&#8212;", # em dash
145
+ :lsquo => "&#8216;", # left single quotation mark
146
+ :rsquo => "&#8217;", # right single quotation mark
147
+ :ldquo => "&#8220;", # left double quotation mark
148
+ :rdquo => "&#8221;", # right double quotation mark
149
+ :bdquo => "&#8222;", # double low-9 quotation mark
150
+ :bull => "&#8226;", # bullet = black small circle
151
+ :hellip => "&#8230;", # horizontal ellipsis = three dot leader
152
+ :numero => "&#8470;", # numero
153
+ :trade => "&#8482;", # trade mark sign
154
+ :minus => "&#8722;", # minus sign
155
+ :inch => "&#8243;", # inch/second sign (u0x2033) (не путать с кавычками!)
156
+ :thinsp => "&#8201;", # полукруглая шпация (тонкий пробел)
157
+ :nob_open => '<nobr>', # открывающий блок без переноса слов
158
+ :nob_close => '</nobr>', # открывающий блок без переноса слов
159
+ } #:nodoc:
160
+
161
+ GLYPHS.freeze
162
+ # Нормальные "типографские" символы в UTF-виде. Браузерами обрабатываются плохонько, поэтому
163
+ # лучше заменять их на entities.
164
+ VERBATIM_GLYPHS = {
165
+ ' ' => :nbsp,# alt+0160 (NBSP here)
166
+ '«' => :laquo,
167
+ '»' => :raquo,
168
+ '§' => :sect,
169
+ '©' => :copy,
170
+ '®' => :reg,
171
+ '°' => :deg,
172
+ '±' => :plusmn,
173
+ '¶' => :para,
174
+ '·' => :middot,
175
+ '–' => :ndash,
176
+ '—' => :mdash,
177
+ '‘' => :lsquo,
178
+ '’' => :rsquo,
179
+ '“' => :ldquo,
180
+ '”' => :rdquo,
181
+ '„' => :bdquo,
182
+ '•' => :bull,
183
+ '…' => :hellip,
184
+ '№' => :numero,
185
+ '™' => :trade,
186
+ '−' => :minus,
187
+ ' ' => :thinsp,
188
+ '″' => :inch,
189
+ }
190
+ VERBATIM_GLYPHS.freeze #:nodoc:
183
191
 
184
- # Для маркера мы применяем invalid UTF-sequence чтобы его НЕЛЬЗЯ было перепутать с частью
185
- # любого другого мультибайтного глифа. Thanks to huNter.
186
- REPLACEMENT_MARKER = RuTils::SUBSTITUTION_MARKER #:nodoc:
187
-
188
- # Кто придумал &#147;? Не учите людей плохому...
189
- # Привет А.Лебедеву http://www.artlebedev.ru/kovodstvo/62/
190
- # Используем символы, потом берем по символам из glyphs форматтера.
191
- # Молодец mash!
192
- FORBIDDEN_NUMERIC_ENTITIES = {
193
- '132' => :bdquo,
194
- '133' => :hellip,
195
- '146' => :apos,
196
- '147' => :ldquo,
197
- '148' => :rdquo,
198
- '149' => :bull,
199
- '150' => :ndash,
200
- '151' => :mdash,
201
- '153' => :trade,
202
- } #:nodoc:
203
-
204
- PROTECTED_SETTINGS = [ :raw_output ] #:nodoc:
192
+ # Для маркера мы применяем UTF-BOM чтобы его НЕЛЬЗЯ было перепутать с частью
193
+ # любого другого мультибайтного глифа. Thanks to huNter.
194
+ REPLACEMENT_MARKER = RuTils::SUBSTITUTION_MARKER.freeze #:nodoc:
195
+
196
+ # Кто придумал &#147;? Не учите людей плохому...
197
+ # Привет А.Лебедеву http://www.artlebedev.ru/kovodstvo/62/
198
+ # Используем символы, потом берем по символам из glyphs форматтера.
199
+ # Молодец mash!
200
+ FORBIDDEN_NUMERIC_ENTITIES = {
201
+ '132' => :bdquo,
202
+ '133' => :hellip,
203
+ '146' => :apos,
204
+ '147' => :ldquo,
205
+ '148' => :rdquo,
206
+ '149' => :bull,
207
+ '150' => :ndash,
208
+ '151' => :mdash,
209
+ '153' => :trade,
210
+ }
211
+ FORBIDDEN_NUMERIC_ENTITIES.freeze #:nodoc:
212
+
213
+ PROTECTED_SETTINGS = [ :raw_output ] #:nodoc:
205
214
 
206
- def initialize(*args)
207
- @_text = args[0].is_a?(String) ? args[0] : ''
208
- setup_default_settings!
209
- accept_configuration_arguments!(args.last) if args.last.is_a?(Hash)
210
- end
211
-
212
- # Настраивает форматтер ассоциированным хешем
213
- # formatter.configure!(:dash=>true, :wordglue=>false)
214
- def configure!(*config)
215
- accept_configuration_arguments!(config.last) if config.last.is_a?(Hash)
216
- end
217
-
218
- alias :configure :configure! #Дружественный API
219
-
220
- # Неизвестные методы - настройки. С = - установка ключа, без - получение значения
221
- def method_missing(meth, *args) #:nodoc:
222
- setting = meth.to_s.gsub(/=$/, '')
223
- super(meth, *args) unless @settings.has_key?(setting) #this will pop the exception if we have no such setting
215
+ def initialize(*args)
216
+ @_text = args[0].is_a?(String) ? args[0] : ''
217
+ setup_default_settings!
218
+ accept_configuration_arguments!(args.last) if args.last.is_a?(Hash)
219
+ end
224
220
 
225
- return (@settings[setting] = args[0])
226
- end
227
-
228
- # Обрабатывает text_to_process с сохранением настроек, присвоенных обьекту-форматтеру
229
- # Дополнительные аргументы передаются как параметры форматтера и не сохраняются после прогона.
230
- def process(text_to_process, *args)
231
- @_text = text_to_process
232
- if args.last.is_a?(Hash)
233
- with_configuration(args.last) { self.to_html }
234
- else
235
- self.to_html
236
- end
237
- end
238
-
239
- # Обрабатывает текст, присвоенный форматтеру при создании и возвращает результат обработки.
240
- def to_html()
241
- return '' unless @_text
242
-
243
- text = @_text.strip
244
-
245
- # -4. запрет тагов html
246
- process_escape_html(text) unless @settings["html"]
247
-
248
- # -3. Никогда (вы слышите?!) не пущать лабуду &#not_correct_number;
249
- FORBIDDEN_NUMERIC_ENTITIES.dup.each_pair do | key, rep |
250
- text.gsub!(/&##{key};/, glyph[rep])
251
- end
252
-
253
- # -2. Чистим copy&paste
254
- process_copy_paste_clearing(text) if @settings['copypaste']
255
-
256
- # -1. Замена &entity_name; на входе ('&nbsp;' => '&#160;' и т.д.)
257
- process_html_entities(text)
258
-
259
- # 0. Вырезаем таги
260
- tags = lift_ignored_elements(text) if @skip_tags
261
-
262
- # 1. Запятые и пробелы
263
- process_spacing(text) if @settings["spacing"]
264
-
265
- # 3. Спецсимволы
266
- # 0. дюймы с цифрами
267
- # заменено на инчи
268
- process_inches(text) if @settings["inches"]
269
-
270
- # 1. лапки
271
- process_quotes(text) if @settings["quotes"]
272
-
273
- # 2. ёлочки
274
- process_laquo(text) if @settings["laquo"]
275
-
276
- # 2b. одновременно ёлочки и лапки
277
- process_compound_quotes(text) if (@settings["quotes"] && @settings["laquo"])
278
-
279
- # 3. тире
280
- process_dash(text) if @settings["dash"]
281
-
282
- # 3a. тире длинное
283
- process_emdash(text) if @settings["emdash"]
284
-
285
- # 5. +/-
286
- process_plusmin(text) if @settings["+-"]
287
-
288
- # 5a. 12^C
289
- process_degrees(text) if @settings["degrees"]
290
-
291
- # 6. телефоны
292
- process_phones(text) if @settings["phones"]
293
-
294
- # 7. Короткие слова и &nbsp;
295
- process_wordglue(text) if @settings["wordglue"]
296
-
297
- # 8. Склейка ласт. Тьфу! дефисов.
298
- process_dashglue(text) if @settings["dashglue"]
299
-
300
- # 8a. Инициалы
301
- process_initials(text) if @settings['initials']
302
-
303
- # 8b. Троеточия
304
- process_ellipsises(text) if @settings["wordglue"]
305
-
306
- # БЕСКОНЕЧНОСТЬ. Вставляем таги обратно.
307
- reinsert_fragments(text, tags) if @skip_tags
308
-
309
- # фуф, закончили.
310
- process_span_instead_of_nobr(text) if @settings["de_nobr"]
311
-
312
- # заменяем entities на истинные символы
313
- process_raw_output(text) if @settings["raw_output"]
314
-
315
- text.strip
316
- end
317
-
221
+ # Настраивает форматтер ассоциированным хешем
222
+ # formatter.configure!(:dash=>true, :wordglue=>false)
223
+ def configure!(*config)
224
+ accept_configuration_arguments!(config.last) if config.last.is_a?(Hash)
225
+ end
226
+
227
+ alias :configure :configure! #Дружественный API
228
+
229
+ # Неизвестные методы - настройки. С = - установка ключа, без - получение значения
230
+ def method_missing(meth, *args) #:nodoc:
231
+ setting = meth.to_s.gsub(/=$/, '')
232
+ super(meth, *args) unless @settings.has_key?(setting) #this will pop the exception if we have no such setting
233
+
234
+ return (@settings[setting] = args[0])
235
+ end
236
+
237
+ # Обрабатывает text_to_process с сохранением настроек, присвоенных обьекту-форматтеру
238
+ # Дополнительные аргументы передаются как параметры форматтера и не сохраняются после прогона.
239
+ def process(text_to_process, *args)
240
+ @_text = text_to_process
241
+ if args.last.is_a?(Hash)
242
+ with_configuration(args.last) { self.to_html }
243
+ else
244
+ self.to_html
245
+ end
246
+ end
247
+
248
+ # Обрабатывает текст, присвоенный форматтеру при создании и возвращает результат обработки.
249
+ def to_html()
250
+ return '' unless @_text
251
+
252
+ text = @_text.strip
253
+
254
+ # -4. запрет тагов html
255
+ process_escape_html(text) unless @settings["html"]
256
+
257
+ # -3. Никогда (вы слышите?!) не пущать лабуду &#not_correct_number;
258
+ FORBIDDEN_NUMERIC_ENTITIES.dup.each_pair do | key, rep |
259
+ text.gsub!(/&##{key};/, glyph[rep])
260
+ end
261
+
262
+ # -2. Чистим copy&paste
263
+ process_copy_paste_clearing(text) if @settings['copypaste']
264
+
265
+ # -1. Замена &entity_name; на входе ('&nbsp;' => '&#160;' и т.д.)
266
+ process_html_entities(text)
267
+
268
+ # 0. Вырезаем таги
269
+ tags = lift_ignored_elements(text) if @skip_tags
270
+
271
+ # 1. Запятые и пробелы
272
+ process_spacing(text) if @settings["spacing"]
273
+
274
+ # 3. Спецсимволы
275
+ # 0. дюймы с цифрами
276
+ # заменено на инчи
277
+ process_inches(text) if @settings["inches"]
278
+
279
+ # 1. лапки
280
+ process_quotes(text) if @settings["quotes"]
281
+
282
+ # 2. ёлочки
283
+ process_laquo(text) if @settings["laquo"]
284
+
285
+ # 2b. одновременно ёлочки и лапки
286
+ process_compound_quotes(text) if (@settings["quotes"] && @settings["laquo"])
287
+
288
+ # 3. тире
289
+ process_dash(text) if @settings["dash"]
290
+
291
+ # 3a. тире длинное
292
+ process_emdash(text) if @settings["emdash"]
293
+
294
+ # 5. +/-
295
+ process_plusmin(text) if @settings["+-"]
296
+
297
+ # 5a. 12^C
298
+ process_degrees(text) if @settings["degrees"]
299
+
300
+ # 6. телефоны
301
+ process_phones(text) if @settings["phones"]
302
+
303
+ # 7. Короткие слова и &nbsp;
304
+ process_wordglue(text) if @settings["wordglue"]
305
+
306
+ # 8. Склейка ласт. Тьфу! дефисов.
307
+ process_dashglue(text) if @settings["dashglue"]
308
+
309
+ # 8a. Инициалы
310
+ process_initials(text) if @settings['initials']
311
+
312
+ # 8b. Троеточия
313
+ process_ellipsises(text) if @settings["wordglue"]
314
+
315
+ # 9. Акронимы от Текстиля
316
+ process_acronyms(text) if @settings["acronyms"]
317
+
318
+ # БЕСКОНЕЧНОСТЬ. Вставляем таги обратно.
319
+ reinsert_fragments(text, tags) if @skip_tags
320
+
321
+ # фуф, закончили.
322
+ process_span_instead_of_nobr(text) if @settings["de_nobr"]
323
+
324
+ # заменяем entities на истинные символы
325
+ process_raw_output(text) if @settings["raw_output"]
326
+
327
+ text.strip
328
+ end
329
+
318
330
 
319
- # Применяет отдельный фильтр к text и возвращает результат. Например:
320
- # formatter.apply(:wordglue, "Вот так") => "Вот&#160;так"
321
- # Удобно применять когда вам нужно задействовать отдельный фильтр Гиленсона, но не нужна остальная механика
322
- # Последний аргумент определяет, нужно ли при применении фильтра сохранить в неприкосновенности таги и другие
323
- # игнорируемые фрагменты текста (по умолчанию они сохраняются).
324
- def apply(filter, text, lift_ignored_elements = true)
325
- copy = text.dup
326
- unless lift_ignored_elements
327
- self.send("process_#{filter}".to_sym, copy)
328
- else
329
- lifting_fragments(copy) { self.send("process_#{filter}".to_sym, copy) }
330
- end
331
- copy
332
- end
331
+ # Применяет отдельный фильтр к text и возвращает результат. Например:
332
+ # formatter.apply(:wordglue, "Вот так") => "Вот&#160;так"
333
+ # Удобно применять когда вам нужно задействовать отдельный фильтр Гиленсона, но не нужна остальная механика
334
+ # Последний аргумент определяет, нужно ли при применении фильтра сохранить в неприкосновенности таги и другие
335
+ # игнорируемые фрагменты текста (по умолчанию они сохраняются).
336
+ def apply(filter, text, lift_ignored_elements = true)
337
+ copy = text.dup
338
+ unless lift_ignored_elements
339
+ self.send("process_#{filter}".to_sym, copy)
340
+ else
341
+ lifting_fragments(copy) { self.send("process_#{filter}".to_sym, copy) }
342
+ end
343
+ copy
344
+ end
333
345
 
334
- private
335
-
336
- def setup_default_settings!
337
- @skip_tags = true;
338
- @ignore = /notypo/ # regex, который игнорируется. Этим надо воспользоваться для обработки pre и code
339
-
340
- @glueleft = ['рис.', 'табл.', 'см.', 'им.', 'ул.', 'пер.', 'кв.', 'офис', 'оф.', 'г.']
341
- @glueright = ['руб.', 'коп.', 'у.е.', 'мин.']
342
-
343
- # Установки можно менять в каждом экземпляре
344
- @settings = SETTINGS.dup
345
-
346
- @mark_tag = REPLACEMENT_MARKER
347
- # Глифы можено подменять в экземпляре форматтера поэтому копируем их из константы
348
- @glyph = GLYPHS.dup
349
-
350
- @phonemasks = [[ /([0-9]{4})\-([0-9]{2})\-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/,
351
- /([0-9]{4})\-([0-9]{2})\-([0-9]{2})/,
352
- /(\([0-9\+\-]+\)) ?([0-9]{3})\-([0-9]{2})\-([0-9]{2})/,
353
- /(\([0-9\+\-]+\)) ?([0-9]{2})\-([0-9]{2})\-([0-9]{2})/,
354
- /(\([0-9\+\-]+\)) ?([0-9]{3})\-([0-9]{2})/,
355
- /(\([0-9\+\-]+\)) ?([0-9]{2})\-([0-9]{3})/,
356
- /([0-9]{3})\-([0-9]{2})\-([0-9]{2})/,
357
- /([0-9]{2})\-([0-9]{2})\-([0-9]{2})/,
358
- /([0-9]{1})\-([0-9]{2})\-([0-9]{2})/,
359
- /([0-9]{2})\-([0-9]{3})/,
360
- /([0-9]+)\-([0-9]+)/,
361
- ],[
362
- ':nob_open\1:ndash\2:ndash\3:nbsp\4:\5:\6:nob_close',
363
- ':nob_open\1:ndash\2:ndash\3:nob_close',
364
- ':nob_open\1:nbsp\2:ndash\3:ndash\4:nob_close',
365
- ':nob_open\1:nbsp\2:ndash\3:ndash\4:nob_close',
366
- ':nob_open\1:nbsp\2:ndash\3:nob_close',
367
- ':nob_open\1:nbsp\2:ndash\3:nob_close',
368
- ':nob_open\1:ndash\2:ndash\3:nob_close',
369
- ':nob_open\1:ndash\2:ndash\3:nob_close',
370
- ':nob_open\1:ndash\2:ndash\3:nob_close',
371
- ':nob_open\1:ndash\2:nob_close',
372
- ':nob_open\1:ndash\2:nob_close'
373
- ]]
374
- end
346
+ private
375
347
 
376
- # Позволяет получить процедуру, при вызове возвращающую значение глифа
377
- def lookup(glyph_to_lookup)
378
- return Proc.new { self.glyph[glyph_to_lookup] }
379
- end
380
-
381
- # Подставляет "символы" (двоеточие + имя глифа) на нужное значение глифа заданное в данном форматтере
382
- def substitute_glyphs_in_string(str)
383
- re = str.dup
384
- @glyph.each_pair do | key, subst |
385
- re.gsub!(":#{key.to_s}", subst)
386
- end
387
- re
388
- end
389
-
390
- # Выполняет блок, временно включая настройки переданные в +hash+
391
- def with_configuration(hash, &block)
392
- old_settings, old_glyphs = @settings.dup, @glyph.dup
393
- accept_configuration_arguments!(hash)
394
- txt = yield
395
- @settings, @glyph = old_settings, old_glyphs
396
-
397
- return txt
398
- end
348
+ def setup_default_settings!
349
+ @skip_tags = true;
350
+ @ignore = /notypo/ # regex, который игнорируется. Этим надо воспользоваться для обработки pre и code
351
+
352
+ @glueleft = ['рис.', 'табл.', 'см.', 'им.', 'ул.', 'пер.', 'кв.', 'офис', 'оф.', 'г.']
353
+ @glueright = ['руб.', 'коп.', 'у.е.', 'мин.']
354
+
355
+ # Установки можно менять в каждом экземпляре
356
+ @settings = SETTINGS.dup
357
+
358
+ @mark_tag = REPLACEMENT_MARKER
359
+ # Глифы можено подменять в экземпляре форматтера поэтому копируем их из константы
360
+ @glyph = GLYPHS.dup
399
361
 
400
- def accept_configuration_arguments!(args_hash)
401
-
402
- # Специальный случай - :all=>true|false
403
- if args_hash.has_key?(:all)
404
- if args_hash[:all]
405
- @settings.each_pair {|k, v| @settings[k] = true unless PROTECTED_SETTINGS.include?(k.to_sym)}
406
- else
407
- @settings.each_pair {|k, v| @settings[k] = false unless PROTECTED_SETTINGS.include?(k.to_sym)}
408
- end
409
- else
410
-
411
- # Кинуть ошибку если настройка нам неизвестна
412
- unknown_settings = args_hash.keys.collect{|k|k.to_s} - @settings.keys.collect { |k| k.to_s }
413
- raise RuTils::Gilenson::UnknownSetting, unknown_settings if unknown_settings.any?
414
-
415
- args_hash.each_pair do | key, value |
416
- @settings[key.to_s] = (value ? true : false)
417
- end
418
- end
419
- end
420
-
421
- # Вынимает игнорируемые фрагменты и заменяет их маркером, выполняет переданный блок и вставляет вынутое на место
422
- def lifting_fragments(text, &block)
423
- lifted = lift_ignored_elements(text)
424
- yield
425
- reinsert_fragments(text, lifted)
426
- end
427
-
428
- #Вынимает фрагменты из текста и возвращает массив с фрагментами
429
- def lift_ignored_elements(text)
430
- # re = /<\/?[a-z0-9]+("+ # имя тага
431
- # "\s+("+ # повторяющая конструкция: хотя бы один разделитель и тельце
432
- # "[a-z]+("+ # атрибут из букв, за которым может стоять знак равенства и потом
433
- # "=((\'[^\']*\')|(\"[^\"]*\")|([0-9@\-_a-z:\/?&=\.]+))"+ #
434
- # ")?"+
435
- # ")?"+
436
- # ")*\/?>|\xA2\xA2[^\n]*?==/i;
437
-
438
- re_skipcode = '((<(code|tt)[ >](.*?)<\/(code|tt)>)|(<!\[CDATA\[(.*?)\]\]>))|' if @settings['skip_code']
439
- re = /(#{re_skipcode}<\/?[a-z0-9]+(\s+([a-z]+(=((\'[^\']*\')|(\"[^\"]*\")|([0-9@\-_a-z:\/?&=\.]+)))?)?)*\/?>)/uim
440
- tags = text.scan(re).map{ |tag| tag[0] } # первая группа!
441
- text.gsub!(re, @mark_tag) #маркер тега, мы используем Invalid UTF-sequence для него
442
- return tags
443
- end
444
-
445
- def reinsert_fragments(text, fragments)
446
- fragments.each do |fragment|
447
- fragment.gsub!(/ (href|src|data)=((?:(\')([^\']*)(\'))|(?:(\")([^\"]*)(\")))/uim) do
448
- " #{$1}=" + $2.gsub(/&(?!(#0*38)|(amp);)/, self.glyph[:amp])
449
- end # unless @settings['raw_output'] -- делать это надо всегда (mash)
450
-
451
- unless @settings['skip_attr']
452
- fragment.gsub!(/ (title|alt)=((?:(\')([^\']*)(\'))|(?:(\")([^\"]*)(\")))/uim) do
453
- " #{$1}=#{$3}" + self.process($4.to_s) + "#{$5}#{$6}" + self.process($7.to_s) + "#{$8}"
454
- end
455
- end
456
- text.sub!(@mark_tag, fragment)
457
- end
458
- end
459
-
460
- ### Имплементации фильтров
461
- def process_html_entities(text)
462
- self.glyph.each { |key, value| text.gsub!(/&#{key};/, value)}
463
- end
464
-
465
- def process_initials(text)
466
- initials = /([А-Я])[\.]{1,2}[\s]*?([А-Я])[\.]*[\s]*?([А-Я])([а-я])/u
467
- replacement = substitute_glyphs_in_string('\1.\2.:thinsp\3\4')
468
- text.gsub!(initials, replacement)
469
- end
470
-
471
- def process_copy_paste_clearing(text)
472
- VERBATIM_GLYPHS.each {|key,value| text.gsub!(/#{key}/, glyph[value]) }
473
- end
474
-
475
- def process_spacing(text)
476
- text.gsub!( /(\s*)([,]*)/sui, '\2\1');
477
- text.gsub!( /(\s*)([\.?!]*)(\s*[ЁА-ЯA-Z])/su, '\2\1\3');
478
- end
479
-
480
- def process_dashglue(text)
481
- text.gsub!( /([a-zа-яА-Я0-9]+(\-[a-zа-яА-Я0-9]+)+)/ui, glyph[:nob_open]+'\1'+glyph[:nob_close])
482
- end
483
-
484
- def process_escape_html(text)
485
- text.gsub!(/&/, self.glyph[:amp])
486
- text.gsub!(/</, self.glyph[:lt])
487
- text.gsub!(/>/, self.glyph[:gt])
488
- end
489
-
490
- def process_span_instead_of_nobr(text)
491
- text.gsub!(/<nobr>/, '<span class="nobr">')
492
- text.gsub!(/<\/nobr>/, '</span>')
493
- end
494
-
495
- def process_dash(text)
496
- text.gsub!( /(\s|;)\-(\s)/ui, '\1'+self.glyph[:ndash]+'\2')
497
- end
498
-
499
- def process_emdash(text)
500
- text.gsub!( /(\s|;)\-\-(\s)/ui, '\1'+self.glyph[:mdash]+'\2')
501
- # 4. (с)
502
- text.gsub!(/\([сСcC]\)((?=\w)|(?=\s[0-9]+))/u, self.glyph[:copy]) if @settings["(c)"]
503
- # 4a. (r)
504
- text.gsub!( /\(r\)/ui, '<sup>'+self.glyph[:reg]+'</sup>') if @settings["(r)"]
505
-
506
- # 4b. (tm)
507
- text.gsub!( /\(tm\)|\(тм\)/ui, self.glyph[:trade]) if @settings["(tm)"]
508
- # 4c. (p)
509
- text.gsub!( /\(p\)/ui, self.glyph[:sect]) if @settings["(p)"]
510
- end
511
-
512
- def process_ellipsises(text)
513
- text.gsub!( '...', self.glyph[:hellip])
514
- end
515
-
516
- def process_laquo(text)
517
- text.gsub!( /\"\"/ui, self.glyph[:quot]*2);
518
- text.gsub!( /(^|\s|#{@mark_tag}|>|\()\"((#{@mark_tag})*[~0-9ёЁA-Za-zА-Яа-я\-:\/\.])/ui, '\1'+self.glyph[:laquo]+'\2');
519
- _text = '""';
520
- until _text == text do
521
- _text = text;
522
- text.gsub!( /(#{self.glyph[:laquo]}([^\"]*)[ёЁA-Za-zА-Яа-я0-9\.\-:\/\?\!](#{@mark_tag})*)\"/sui,
523
- '\1'+self.glyph[:raquo])
524
- end
525
- end
526
-
527
- def process_quotes(text)
528
- text.gsub!( /\"\"/ui, self.glyph[:quot]*2)
529
- text.gsub!( /\"\.\"/ui, self.glyph[:quot]+"."+self.glyph[:quot])
530
- _text = '""';
531
- until _text == text do
532
- _text = text.dup
533
- text.gsub!( /(^|\s|#{@mark_tag}|>)\"([0-9A-Za-z\'\!\s\.\?\,\-\&\;\:\_\#{@mark_tag}]+(\"|#{self.glyph[:rdquo]}))/ui, '\1'+self.glyph[:ldquo]+'\2')
534
- #this doesnt work in-place. somehow.
535
- text.gsub!( /(#{self.glyph[:ldquo]}([A-Za-z0-9\'\!\s\.\?\,\-\&\;\:\#{@mark_tag}\_]*).*[A-Za-z0-9][\#{@mark_tag}\?\.\!\,]*)\"/ui, '\1'+self.glyph[:rdquo])
536
- end
537
- end
538
-
539
- def process_compound_quotes(text)
540
- text.gsub!(/(#{self.glyph[:ldquo]}(([A-Za-z0-9'!\.?,\-&;:]|\s|#{@mark_tag})*)#{self.glyph[:laquo]}(.*)#{self.glyph[:raquo]})#{self.glyph[:raquo]}/ui,'\1'+self.glyph[:rdquo]);
541
- end
542
-
543
- def process_degrees(text)
544
- text.gsub!( /-([0-9])+\^([FCС])/, self.glyph[:ndash]+'\1'+self.glyph[:deg]+'\2') #deg
545
- text.gsub!( /\+([0-9])+\^([FCС])/, '+\1'+self.glyph[:deg]+'\2')
546
- text.gsub!( /\^([FCС])/, self.glyph[:deg]+'\1')
547
- end
548
-
549
- def process_wordglue(text)
550
- text.replace(" " + text + " ")
551
- _text = " " + text + " "
552
-
553
- until _text == text
554
- _text = text
555
- text.gsub!( /(\s+)([a-zа-яА-Я]{1,2})(\s+)([^\\s$])/ui, '\1\2'+self.glyph[:nbsp]+'\4')
556
- text.gsub!( /(\s+)([a-zа-яА-Я]{3})(\s+)([^\\s$])/ui, '\1\2'+self.glyph[:nbsp]+'\4')
557
- end
558
-
559
- text.gsub!(/(\s+)([a-zа-яА-Я]{1,2}[\)\]\!\?,\.;]{0,3}\s$)/ui, self.glyph[:nbsp]+'\2')
560
-
561
- @glueleft.each { | i | text.gsub!( /(\s)(#{i})(\s+)/sui, '\1\2' + self.glyph[:nbsp]) }
562
-
563
- @glueright.each { | i | text.gsub!( /(\s)(#{i})(\s+)/sui, self.glyph[:nbsp]+'\2\3') }
564
-
565
- text.strip!
566
- end
567
-
568
- def process_phones(text)
569
- @phonemasks[0].each_with_index do |pattern, i|
570
- replacement = substitute_glyphs_in_string(@phonemasks[1][i])
571
- text.gsub!(pattern, replacement)
572
- end
573
- end
574
-
575
- def process_inches(text)
576
- text.gsub!(/\s([0-9]{1,2}([\.,][0-9]{1,2})?)\"/ui, ' \1'+self.glyph[:inch])
577
- end
578
-
579
- def process_plusmin(text)
580
- text.gsub!(/[^+]\+\-/ui, self.glyph[:plusmn])
581
- end
582
-
583
- # Подменяет все юникодные entities в тексте на истинные UTF-8-символы
584
- def process_raw_output(text)
585
- # Все глифы
586
- @glyph.values.each do | entity |
587
- next unless entity =~ /^&#(\d+);/
588
- text.gsub!(/#{entity}/, entity_to_raw_utf8(entity))
589
- end
590
- end
591
-
592
- # Конвертирует юникодные entities в UTF-8-codepoints
593
- def entity_to_raw_utf8(entity)
594
- entity =~ /^&#(\d+);/
595
- $1 ? [$1.to_i].pack("U") : entity
362
+ @phonemasks = [[ /([0-9]{4})\-([0-9]{2})\-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/,
363
+ /([0-9]{4})\-([0-9]{2})\-([0-9]{2})/,
364
+ /(\([0-9\+\-]+\)) ?([0-9]{3})\-([0-9]{2})\-([0-9]{2})/,
365
+ /(\([0-9\+\-]+\)) ?([0-9]{2})\-([0-9]{2})\-([0-9]{2})/,
366
+ /(\([0-9\+\-]+\)) ?([0-9]{3})\-([0-9]{2})/,
367
+ /(\([0-9\+\-]+\)) ?([0-9]{2})\-([0-9]{3})/,
368
+ /([0-9]{3})\-([0-9]{2})\-([0-9]{2})/,
369
+ /([0-9]{2})\-([0-9]{2})\-([0-9]{2})/,
370
+ /([0-9]{1})\-([0-9]{2})\-([0-9]{2})/,
371
+ /([0-9]{2})\-([0-9]{3})/,
372
+ /([0-9]+)\-([0-9]+)/,
373
+ ],[
374
+ ':nob_open\1:ndash\2:ndash\3:nbsp\4:\5:\6:nob_close',
375
+ ':nob_open\1:ndash\2:ndash\3:nob_close',
376
+ ':nob_open\1:nbsp\2:ndash\3:ndash\4:nob_close',
377
+ ':nob_open\1:nbsp\2:ndash\3:ndash\4:nob_close',
378
+ ':nob_open\1:nbsp\2:ndash\3:nob_close',
379
+ ':nob_open\1:nbsp\2:ndash\3:nob_close',
380
+ ':nob_open\1:ndash\2:ndash\3:nob_close',
381
+ ':nob_open\1:ndash\2:ndash\3:nob_close',
382
+ ':nob_open\1:ndash\2:ndash\3:nob_close',
383
+ ':nob_open\1:ndash\2:nob_close',
384
+ ':nob_open\1:ndash\2:nob_close'
385
+ ]]
386
+ end
387
+
388
+ # Позволяет получить процедуру, при вызове возвращающую значение глифа
389
+ def lookup(glyph_to_lookup)
390
+ return Proc.new { self.glyph[glyph_to_lookup] }
391
+ end
392
+
393
+ # Подставляет "символы" (двоеточие + имя глифа) на нужное значение глифа заданное в данном форматтере
394
+ def substitute_glyphs_in_string(str)
395
+ re = str.dup
396
+ @glyph.each_pair do | key, subst |
397
+ re.gsub!(":#{key.to_s}", subst)
398
+ end
399
+ re
400
+ end
401
+
402
+ # Выполняет блок, временно включая настройки переданные в +hash+
403
+ def with_configuration(hash, &block)
404
+ old_settings, old_glyphs = @settings.dup, @glyph.dup
405
+ accept_configuration_arguments!(hash)
406
+ txt = yield
407
+ @settings, @glyph = old_settings, old_glyphs
408
+
409
+ return txt
410
+ end
411
+
412
+ def accept_configuration_arguments!(args_hash)
413
+
414
+ # Специальный случай - :all=>true|false
415
+ if args_hash.has_key?(:all)
416
+ if args_hash[:all]
417
+ @settings.each_pair {|k, v| @settings[k] = true unless PROTECTED_SETTINGS.include?(k.to_sym)}
418
+ else
419
+ @settings.each_pair {|k, v| @settings[k] = false unless PROTECTED_SETTINGS.include?(k.to_sym)}
420
+ end
421
+ else
422
+
423
+ # Кинуть ошибку если настройка нам неизвестна
424
+ unknown_settings = args_hash.keys.collect{|k|k.to_s} - @settings.keys.collect { |k| k.to_s }
425
+ raise RuTils::Gilenson::UnknownSetting, unknown_settings if unknown_settings.any?
426
+
427
+ args_hash.each_pair do | key, value |
428
+ @settings[key.to_s] = (value ? true : false)
429
+ end
430
+ end
431
+ end
432
+
433
+ # Вынимает игнорируемые фрагменты и заменяет их маркером, выполняет переданный блок и вставляет вынутое на место
434
+ def lifting_fragments(text, &block)
435
+ lifted = lift_ignored_elements(text)
436
+ yield
437
+ reinsert_fragments(text, lifted)
438
+ end
439
+
440
+ #Вынимает фрагменты из текста и возвращает массив с фрагментами
441
+ def lift_ignored_elements(text)
442
+ # re = /<\/?[a-z0-9]+("+ # имя тага
443
+ # "\s+("+ # повторяющая конструкция: хотя бы один разделитель и тельце
444
+ # "[a-z]+("+ # атрибут из букв, за которым может стоять знак равенства и потом
445
+ # "=((\'[^\']*\')|(\"[^\"]*\")|([0-9@\-_a-z:\/?&=\.]+))"+ #
446
+ # ")?"+
447
+ # ")?"+
448
+ # ")*\/?>|\xA2\xA2[^\n]*?==/i;
449
+
450
+ re_skipcode = '((<(code|tt)[ >](.*?)<\/(code|tt)>)|(<!\[CDATA\[(.*?)\]\]>))|' if @settings['skip_code']
451
+ re = /(#{re_skipcode}<\/?[a-z0-9]+(\s+([a-z]+(=((\'[^\']*\')|(\"[^\"]*\")|([0-9@\-_a-z:\/?&=\.]+)))?)?)*\/?>)/uim
452
+ tags = text.scan(re).map{ |tag| tag[0] } # первая группа!
453
+ text.gsub!(re, @mark_tag) #маркер тега, мы используем Invalid UTF-sequence для него
454
+ return tags
455
+ end
456
+
457
+ def reinsert_fragments(text, fragments)
458
+ fragments.each do |fragment|
459
+ fragment.gsub!(/ (href|src|data)=((?:(\')([^\']*)(\'))|(?:(\")([^\"]*)(\")))/uim) do
460
+ " #{$1}=" + $2.gsub(/&(?!(#0*38)|(amp);)/, self.glyph[:amp])
461
+ end # unless @settings['raw_output'] -- делать это надо всегда (mash)
462
+
463
+ unless @settings['skip_attr']
464
+ fragment.gsub!(/ (title|alt)=((?:(\')([^\']*)(\'))|(?:(\")([^\"]*)(\")))/uim) do
465
+ " #{$1}=#{$3}" + self.process($4.to_s) + "#{$5}#{$6}" + self.process($7.to_s) + "#{$8}"
466
+ end
467
+ end
468
+ text.sub!(@mark_tag, fragment)
469
+ end
470
+ end
471
+
472
+ ### Имплементации фильтров
473
+ def process_html_entities(text)
474
+ self.glyph.each { |key, value| text.gsub!(/&#{key};/, value)}
475
+ end
476
+
477
+ def process_initials(text)
478
+ initials = /([А-Я])[\.]{1,2}[\s]*?([А-Я])[\.]*[\s]*?([А-Я])([а-я])/u
479
+ replacement = substitute_glyphs_in_string('\1.\2.:thinsp\3\4')
480
+ text.gsub!(initials, replacement)
481
+ end
482
+
483
+ def process_copy_paste_clearing(text)
484
+ VERBATIM_GLYPHS.each {|key,value| text.gsub!(/#{key}/, glyph[value]) }
485
+ end
486
+
487
+ def process_spacing(text)
488
+ text.gsub!( /(\s*)([,]*)/sui, '\2\1');
489
+ text.gsub!( /(\s*)([\.?!]*)(\s*[ЁА-ЯA-Z])/su, '\2\1\3');
490
+ end
491
+
492
+ def process_dashglue(text)
493
+ text.gsub!( /([a-zа-яА-Я0-9]+(\-[a-zа-яА-Я0-9]+)+)/ui, glyph[:nob_open]+'\1'+glyph[:nob_close])
494
+ end
495
+
496
+ def process_escape_html(text)
497
+ text.gsub!(/&/, glyph[:amp])
498
+ text.gsub!(/</, glyph[:lt])
499
+ text.gsub!(/>/, glyph[:gt])
500
+ end
501
+
502
+ def process_span_instead_of_nobr(text)
503
+ text.gsub!(/<nobr>/, '<span class="nobr">')
504
+ text.gsub!(/<\/nobr>/, '</span>')
505
+ end
506
+
507
+ def process_dash(text)
508
+ text.gsub!( /(\s|;)\-(\s)/ui, '\1'+self.glyph[:ndash]+'\2')
509
+ end
510
+
511
+ def process_emdash(text)
512
+ text.gsub!( /(\s|;)\-\-(\s)/ui, '\1'+self.glyph[:mdash]+'\2')
513
+ # 4. (с)
514
+ text.gsub!(/\([сСcC]\)((?=\w)|(?=\s[0-9]+))/u, self.glyph[:copy]) if @settings["(c)"]
515
+ # 4a. (r)
516
+ text.gsub!( /\(r\)/ui, '<sup>'+self.glyph[:reg]+'</sup>') if @settings["(r)"]
517
+
518
+ # 4b. (tm)
519
+ text.gsub!( /\(tm\)|\(тм\)/ui, self.glyph[:trade]) if @settings["(tm)"]
520
+ # 4c. (p)
521
+ text.gsub!( /\(p\)/ui, self.glyph[:sect]) if @settings["(p)"]
522
+ end
523
+
524
+ def process_ellipsises(text)
525
+ text.gsub!( '...', self.glyph[:hellip])
526
+ end
527
+
528
+ def process_laquo(text)
529
+ text.gsub!( /\"\"/ui, self.glyph[:quot]*2);
530
+ text.gsub!( /(^|\s|#{@mark_tag}|>|\()\"((#{@mark_tag})*[~0-9ёЁA-Za-zА-Яа-я\-:\/\.])/ui, '\1'+self.glyph[:laquo]+'\2');
531
+ _text = '""';
532
+ until _text == text do
533
+ _text = text;
534
+ text.gsub!( /(#{self.glyph[:laquo]}([^\"]*)[ёЁA-Za-zА-Яа-я0-9\.\-:\/\?\!](#{@mark_tag})*)\"/sui,
535
+ '\1'+self.glyph[:raquo])
536
+ end
537
+ end
538
+
539
+ def process_quotes(text)
540
+ text.gsub!( /\"\"/ui, self.glyph[:quot]*2)
541
+ text.gsub!( /\"\.\"/ui, self.glyph[:quot]+"."+self.glyph[:quot])
542
+ _text = '""';
543
+ until _text == text do
544
+ _text = text.dup
545
+ text.gsub!( /(^|\s|#{@mark_tag}|>)\"([0-9A-Za-z\'\!\s\.\?\,\-\&\;\:\_\#{@mark_tag}]+(\"|#{self.glyph[:rdquo]}))/ui, '\1'+self.glyph[:ldquo]+'\2')
546
+ #this doesnt work in-place. somehow.
547
+ text.gsub!( /(#{self.glyph[:ldquo]}([A-Za-z0-9\'\!\s\.\?\,\-\&\;\:\#{@mark_tag}\_]*).*[A-Za-z0-9][\#{@mark_tag}\?\.\!\,]*)\"/ui, '\1'+self.glyph[:rdquo])
548
+ end
549
+ end
550
+
551
+ def process_compound_quotes(text)
552
+ text.gsub!(/(#{self.glyph[:ldquo]}(([A-Za-z0-9'!\.?,\-&;:]|\s|#{@mark_tag})*)#{self.glyph[:laquo]}(.*)#{self.glyph[:raquo]})#{self.glyph[:raquo]}/ui,'\1'+self.glyph[:rdquo]);
553
+ end
554
+
555
+ def process_degrees(text)
556
+ text.gsub!( /-([0-9])+\^([FCС])/, self.glyph[:ndash]+'\1'+self.glyph[:deg]+'\2') #deg
557
+ text.gsub!( /\+([0-9])+\^([FCС])/, '+\1'+self.glyph[:deg]+'\2')
558
+ text.gsub!( /\^([FCС])/, self.glyph[:deg]+'\1')
559
+ end
560
+
561
+ def process_wordglue(text)
562
+ text.replace(" " + text + " ")
563
+ _text = " " + text + " "
564
+
565
+ until _text == text
566
+ _text = text
567
+ text.gsub!( /(\s+)([a-zа-яА-Я]{1,2})(\s+)([^\\s$])/ui, '\1\2' + glyph[:nbsp]+'\4')
568
+ text.gsub!( /(\s+)([a-zа-яА-Я]{3})(\s+)([^\\s$])/ui, '\1\2' + glyph[:nbsp]+'\4')
569
+ end
570
+
571
+ text.gsub!(/(\s+)([a-zа-яА-Я]{1,2}[\)\]\!\?,\.;]{0,3}\s$)/ui, glyph[:nbsp]+'\2')
572
+
573
+ @glueleft.each { | i | text.gsub!( /(\s)(#{i})(\s+)/sui, '\1\2' + glyph[:nbsp]) }
574
+
575
+ @glueright.each { | i | text.gsub!( /(\s)(#{i})(\s+)/sui, glyph[:nbsp]+'\2\3') }
576
+
577
+ text.strip!
578
+ end
579
+
580
+ def process_phones(text)
581
+ @phonemasks[0].each_with_index do |pattern, i|
582
+ replacement = substitute_glyphs_in_string(@phonemasks[1][i])
583
+ text.gsub!(pattern, replacement)
596
584
  end
585
+ end
586
+
587
+ def process_acronyms(text)
588
+ acronym = /\b([A-ZА-Я][A-ZА-Я0-9]{2,})\b(?:[(]([^)]*)[)])/u
589
+ if @settings["raw_output"]
590
+ text.gsub!(acronym, '\1%s(\2)' % glyph[:thinsp])
591
+ else
592
+ text.gsub!(acronym) do
593
+ expl = $2.to_s; process_escape_html(expl)
594
+ "<acronym title=\"#{expl}\">#{$1}</acronym>"
595
+ end
596
+ end
597
+ end
598
+
599
+ def process_inches(text)
600
+ text.gsub!(/\s([0-9]{1,2}([\.,][0-9]{1,2})?)\"/ui, ' \1'+self.glyph[:inch])
601
+ end
602
+
603
+ def process_plusmin(text)
604
+ text.gsub!(/[^+]\+\-/ui, self.glyph[:plusmn])
605
+ end
606
+
607
+ # Подменяет все юникодные entities в тексте на истинные UTF-8-символы
608
+ def process_raw_output(text)
609
+ # Все глифы
610
+ @glyph.values.each do | entity |
611
+ next unless entity =~ /^&#(\d+);/
612
+ text.gsub!(/#{entity}/, entity_to_raw_utf8(entity))
613
+ end
614
+ end
615
+
616
+ # Конвертирует юникодные entities в UTF-8-codepoints
617
+ def entity_to_raw_utf8(entity)
618
+ entity =~ /^&#(\d+);/
619
+ $1 ? [$1.to_i].pack("U") : entity
620
+ end
621
+
597
622
  end #end Gilenson
598
623
 
599
624
  # Выбрасывается если форматтеру задается неизвестная настройка
@@ -614,4 +639,4 @@ module RuTils::Gilenson::StringFormatting
614
639
  end
615
640
  end
616
641
 
617
- Object::String.send(:include, RuTils::Gilenson::StringFormatting)
642
+ Object::String.send(:include, RuTils::Gilenson::StringFormatting)
data/lib/rutils.rb CHANGED
@@ -6,7 +6,7 @@ module RuTils
6
6
  INSTALLATION_DIRECTORY = File.expand_path(File.dirname(__FILE__) + '/../') #:nodoc:
7
7
  MAJOR = 0
8
8
  MINOR = 2
9
- TINY = 1
9
+ TINY = 2
10
10
 
11
11
  # Версия RuTils
12
12
  VERSION = [MAJOR, MINOR ,TINY].join('.') #:nodoc:
data/test/t_datetime.rb CHANGED
@@ -25,7 +25,8 @@ class StrftimeRuTest < Test::Unit::TestCase
25
25
 
26
26
  date = Date.new(2005, 12, 31)
27
27
  assert_equal "дек декабрь сб суббота", "#{Date::RU_ABBR_MONTHNAMES[date.mon]} #{Date::RU_MONTHNAMES[date.mon]} #{Date::RU_ABBR_DAYNAMES[date.wday]} #{Date::RU_DAYNAMES[date.wday]}"
28
- assert_equal "сб, суббота, дек, декабрь", date.strftime("%a, %A, %b, %B")
28
+ # We do not support strftime on date at this point
29
+ # assert_equal "сб, суббота, дек, декабрь", date.strftime("%a, %A, %b, %B")
29
30
 
30
31
  RuTils::overrides = false
31
32
  assert_equal "Sat, Saturday, Dec, December", Time.local(2005,"dec",31).strftime("%a, %A, %b, %B")
data/test/t_gilenson.rb CHANGED
@@ -34,6 +34,26 @@ class GilensonOwnTest < Test::Unit::TestCase
34
34
  assert_equal '<nobr>725&#8211;01&#8211;10</nobr>', '725-01-10'.gilensize
35
35
  end
36
36
 
37
+ def test_acronyms_with_html_output
38
+ assert_equal '<acronym title="Большая советская энциклопедия">БСЭ</acronym>', 'БСЭ(Большая советская энциклопедия)'.gilensize
39
+ assert_equal '<acronym title="Advanced Micro Devices">AMD</acronym>',
40
+ 'AMD(Advanced Micro Devices)'.gilensize
41
+ assert_equal '<acronym title="Расширяемый язык разметки">XML</acronym>',
42
+ 'XML(Расширяемый язык разметки)'.gilensize
43
+ end
44
+
45
+ def test_acronyms_should_escape_entities
46
+ @gilenson.configure(:raw_output => false)
47
+ assert_equal '<acronym title="Знак &#60;">БВГ</acronym>', 'БВГ(Знак <)'.gilensize
48
+ end
49
+
50
+ def test_acronyms_with_text_output
51
+ @gilenson.configure(:raw_output => true)
52
+ thin_space = [8201].pack("U")
53
+ assert_equal_cp "так утверждает БСЭ#{thin_space}(Большая советская энциклопедия)",
54
+ @gilenson.process('так утверждает БСЭ(Большая советская энциклопедия)')
55
+ end
56
+
37
57
  def test_address
38
58
  assert_equal 'табл.&#160;2, рис.&#160;2.10', 'табл. 2, рис. 2.10'.gilensize
39
59
  assert_equal 'офис&#160;415, оф.340, д.5, ул.&#160;Народной Воли, пл. Малышева', 'офис 415, оф.340, д.5, ул. Народной Воли, пл. Малышева'.gilensize
@@ -243,6 +263,28 @@ class GilensonOwnTest < Test::Unit::TestCase
243
263
  @gilenson.process("<a href='test?test15=15&amppp;test16=16'>test&</a>")
244
264
 
245
265
  end
266
+
267
+ private
268
+ # Проверить равны ли строки, и если нет то обьяснить какой кодпойнт отличается.
269
+ # Совершенно необходимо для работы с различными пробелами.
270
+ def assert_equal_cp(reference, actual, msg = nil)
271
+ (assert(true, msg); return) if (reference == actual)
272
+
273
+ reference_cp, actual_cp = [reference, actual].map{|t| t.unpack("U*") }
274
+ reference_cp.each_with_index do | ref_codepoint, idx |
275
+ next unless actual_cp[idx] != ref_codepoint
276
+ beg, fin = idx - 2, idx + 2
277
+ beg = 0 if (beg < 0)
278
+ conflicting_piece = actual_cp[beg..fin].pack("U*")
279
+ msg = []
280
+ msg << "Expected #{actual.inspect} to be equal to #{reference.inspect}, but they were not, " +
281
+ "in fragment '#{beg > 0 ? '...' : ''}#{conflicting_piece}...'"
282
+ msg << "Non-matching codepoint #{actual[idx]} at offset #{idx}, " +
283
+ "expected codepoint #{ref_codepoint} instead"
284
+ flunk msg.join("\n"); return
285
+ end
286
+ raise "We should never get here"
287
+ end
246
288
  end
247
289
 
248
290
 
@@ -306,7 +348,6 @@ class GilensonConfigurationTest < Test::Unit::TestCase
306
348
  assert @gilenson.configure(:raw_output=>true)
307
349
  assert @gilenson.configure!(:raw_output=>true)
308
350
  end
309
-
310
351
  end
311
352
 
312
353
  # class TypograficaTrakoEntries < Test::Unit::TestCase
@@ -321,4 +362,4 @@ end
321
362
  # def test_paths # http://pixel-apes.com/typografica/trako/13
322
363
  # assert_equal '&#171;c:\www\sites\&#187;', '"c:\www\sites\"'.gilensize
323
364
  # end
324
- # end
365
+ # end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: rutils
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.1
7
- date: 2007-04-03 00:00:00 +02:00
6
+ version: 0.2.2
7
+ date: 2007-09-24 00:00:00 +02:00
8
8
  summary: Simple processing of russian strings
9
9
  require_paths:
10
10
  - lib
@@ -40,21 +40,21 @@ files:
40
40
  - test/t_transliteration.rb
41
41
  - test/t_typografica.rb
42
42
  - lib/countries
43
- - lib/datetime
44
- - lib/gilenson
45
- - lib/integration
46
- - lib/pluralizer
47
- - lib/rutils.rb
48
- - lib/transliteration
49
43
  - lib/countries/countries.rb
44
+ - lib/datetime
50
45
  - lib/datetime/datetime.rb
46
+ - lib/gilenson
51
47
  - lib/gilenson/gilenson.rb
52
48
  - lib/gilenson/gilenson_port.rb
49
+ - lib/integration
53
50
  - lib/integration/blue_cloth_override.rb
54
51
  - lib/integration/integration.rb
55
52
  - lib/integration/rails_date_helper_override.rb
56
53
  - lib/integration/red_cloth_override.rb
54
+ - lib/pluralizer
57
55
  - lib/pluralizer/pluralizer.rb
56
+ - lib/rutils.rb
57
+ - lib/transliteration
58
58
  - lib/transliteration/bidi.rb
59
59
  - lib/transliteration/simple.rb
60
60
  - lib/transliteration/transliteration.rb