rutils 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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