ruby_typograph 0.0.1.pre.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +65 -0
- data/Rakefile +21 -0
- data/lib/typograph.rb +12 -0
- data/lib/typograph/adapter.rb +56 -0
- data/lib/typograph/processor.rb +56 -0
- data/lib/typograph/processors/quotes.rb +83 -0
- data/lib/typograph/processors/russian_grammar.rb +274 -0
- data/lib/typograph/version.rb +3 -0
- data/spec/helper.rb +5 -0
- data/spec/parse_error_spec.rb +61 -0
- data/spec/typograph_spec.rb +431 -0
- data/typographer.gemspec +25 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: df22ecd8c810f3eca7f45f19e0bff20ee3a254f1
|
4
|
+
data.tar.gz: bed8ba9ec1431e3f11d6c2115130ad90372f5473
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ea04e9d94b1602ea9bc0687c2d86badb6f26409187c6b4f0237cf3fa4293d0fbdc608f3b2e278d0a95d7a45bb2799f00c4914ba0b6263b7324ef04f471be945a
|
7
|
+
data.tar.gz: 4afcd23b08cb1ad3018c8e700a94910f560e35afae1edb6172769cf3acd9a89947d85f009d37747c8d6b88a016371e0471cbe15392ae4df2580bbfb984840212
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 sterebooster
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Typograph
|
2
|
+
|
3
|
+
Gem for typographing russian and english texts.
|
4
|
+
|
5
|
+
##### Привязка союзов, предлогов
|
6
|
+
``` ruby
|
7
|
+
Typograph.process 'Я бы в лётчики б пошёл, пусть меня научат.'
|
8
|
+
> 'Я бы в летчики б пошел, пусть меня научат.'
|
9
|
+
```
|
10
|
+
|
11
|
+
##### Расстановка кавычек
|
12
|
+
``` ruby
|
13
|
+
Typograph.process '"Кавычки "второго уровня"" and "Quotes "second level""'
|
14
|
+
> '«Кавычки “второго уровня”» and “Quotes ‘second level’”'
|
15
|
+
```
|
16
|
+
|
17
|
+
##### Расстановка запятых перед а, но
|
18
|
+
``` ruby
|
19
|
+
Typograph.process 'Мало написать а запятые кто за тебя расставит.'
|
20
|
+
> '"Мало написать, а запятые кто за тебя расставит.'
|
21
|
+
```
|
22
|
+
|
23
|
+
##### Отсутствие запятых у "а"" и "но" после тире
|
24
|
+
``` ruby
|
25
|
+
Typograph.process 'Текст до тире – а теперь после'
|
26
|
+
> 'Текст до тире — а теперь после'
|
27
|
+
```
|
28
|
+
|
29
|
+
##### Расстановка правильного апострофа в английских текстах
|
30
|
+
``` ruby
|
31
|
+
Typograph.process "don't"
|
32
|
+
> 'don’t'
|
33
|
+
```
|
34
|
+
|
35
|
+
|
36
|
+
- Замена ё на е
|
37
|
+
- Удаление лишних пробелов
|
38
|
+
- Расстановка дефиса в предлогах из-за, из-под
|
39
|
+
- Расстановка дефиса перед -таки
|
40
|
+
- Расстановка дефиса после кое-, кой-
|
41
|
+
- Удаление пробела перед символом процент
|
42
|
+
- Удаление пробелов внутри скобок
|
43
|
+
- Выделение прямой речи
|
44
|
+
- Замена (R) на символ зарегистрированной торговой марки
|
45
|
+
- Замена (c) на символ копирайт
|
46
|
+
- Замена (tm) на символ торговой марки
|
47
|
+
- Замена дробей 1/2, 1/4, 3/4 на соответствующие символы
|
48
|
+
- Расстановка пробелов перед сокращениями см., им.
|
49
|
+
- Расстановка пробелов перед сокращениями гл., стр., рис., илл.
|
50
|
+
- Объединение сокращений и др.
|
51
|
+
- Расстановка пробелов в сокращениях г., ул., пер., д.
|
52
|
+
- Расстановка пробелов перед сокращениями dpi, lpi
|
53
|
+
|
54
|
+
### rspec
|
55
|
+
|
56
|
+
``` ruby
|
57
|
+
Finished in 0.07754 seconds
|
58
|
+
47 examples, 3 failures
|
59
|
+
|
60
|
+
Failed examples:
|
61
|
+
|
62
|
+
rspec ./spec/typograph_spec.rb:73 # .process Удаление пробелов перед знаками препинания
|
63
|
+
rspec ./spec/typograph_spec.rb:79 # .process Расстановка пробелов после знака препинания
|
64
|
+
rspec ./spec/typograph_spec.rb:103 # .process Выделение прямой речи
|
65
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
desc "Run all specs"
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
LANG = "ru_RU.UTF-8"
|
10
|
+
|
11
|
+
SET_CMD = case RUBY_PLATFORM
|
12
|
+
when /mingw32/ then
|
13
|
+
"set"
|
14
|
+
else
|
15
|
+
"export"
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "run tests"
|
19
|
+
task :test do
|
20
|
+
sh "#{SET_CMD} LANG=#{LANG} && chcp 65001 && bundle exec rake"
|
21
|
+
end
|
data/lib/typograph.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "typograph/version"
|
2
|
+
require "typograph/adapter"
|
3
|
+
require "typograph/processors/quotes"
|
4
|
+
require "typograph/processors/russian_grammar"
|
5
|
+
require "typograph/processor"
|
6
|
+
|
7
|
+
module Typograph
|
8
|
+
def self.process(text="", options={})
|
9
|
+
return "" if text.nil?
|
10
|
+
Processor.new(options).process(text)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'htmlentities'
|
4
|
+
|
5
|
+
module Typograph
|
6
|
+
class Adapter
|
7
|
+
|
8
|
+
SPECIAL = {
|
9
|
+
:lsquo => '‘',
|
10
|
+
:rsquo => '’',
|
11
|
+
:sbquo => '‚',
|
12
|
+
:ldquo => '“',
|
13
|
+
:rdquo => '”',
|
14
|
+
:bdquo => '„',
|
15
|
+
:lsaquo => '‹',
|
16
|
+
:rsaquo => '›',
|
17
|
+
:quot => '"',
|
18
|
+
:grave => '`',
|
19
|
+
:laquo => '«',
|
20
|
+
:raquo => '»',
|
21
|
+
:acute => '´',
|
22
|
+
:hellip => '…',
|
23
|
+
:ndash => '–',
|
24
|
+
:mdash => '—',
|
25
|
+
:nbsp => "\xA0",
|
26
|
+
:shy => "\xAD",
|
27
|
+
:cent => '¢',
|
28
|
+
:pound => '£',
|
29
|
+
:curren => '¤',
|
30
|
+
:yen => '¥',
|
31
|
+
:sect => '§',
|
32
|
+
:sup2 => '²',
|
33
|
+
:sup3 => '³'
|
34
|
+
}
|
35
|
+
|
36
|
+
def initialize(options={})
|
37
|
+
@options = options
|
38
|
+
end
|
39
|
+
|
40
|
+
def process(text)
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def sym_to_char(sym)
|
45
|
+
res = SPECIAL[sym]
|
46
|
+
raise ArgumentError, "Unknown sym #{sym}" unless res
|
47
|
+
res
|
48
|
+
end
|
49
|
+
|
50
|
+
def normalize(str)
|
51
|
+
str.gsub!(/ | /, ' ')
|
52
|
+
str.chomp(" \r\n\t")
|
53
|
+
HTMLEntities.new.decode(str)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Typograph
|
4
|
+
class Processor
|
5
|
+
|
6
|
+
DEFAULTS = {}
|
7
|
+
|
8
|
+
SAFE_BLOCKS = [
|
9
|
+
['<pre[^>]*>','</pre>'],
|
10
|
+
['<style[^>]*>','</style>'],
|
11
|
+
['<script[^>]*>','</script>'],
|
12
|
+
['<!--','-->'],
|
13
|
+
['<code[^>]*>','</code>']
|
14
|
+
]
|
15
|
+
|
16
|
+
def initialize(options={})
|
17
|
+
@adapter = Adapter.new options
|
18
|
+
@russian_grammar = Processors::RussianGrammar.new options
|
19
|
+
@quotes = Processors::Quotes.new options
|
20
|
+
end
|
21
|
+
|
22
|
+
def safe_blocks
|
23
|
+
@pattern ||= begin
|
24
|
+
pattern = SAFE_BLOCKS.map do |val|
|
25
|
+
val.join('.*?')
|
26
|
+
end.join('|')
|
27
|
+
Regexp.new("(#{pattern}|<[^>]*[\\s][^>]*>)", Regexp::IGNORECASE | Regexp::MULTILINE)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def process(str)
|
32
|
+
str = @adapter.normalize(str)
|
33
|
+
|
34
|
+
@safe_blocks = {}
|
35
|
+
str.gsub!(safe_blocks) do |match|
|
36
|
+
key = "<#{@safe_blocks.length}>"
|
37
|
+
@safe_blocks[key] = match
|
38
|
+
key
|
39
|
+
end
|
40
|
+
|
41
|
+
str = @quotes.process str
|
42
|
+
str = @russian_grammar.process str
|
43
|
+
|
44
|
+
|
45
|
+
if @safe_blocks
|
46
|
+
str.gsub! /(<\d>)/ do |match|
|
47
|
+
@safe_blocks[match]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
@safe_blocks = {}
|
51
|
+
|
52
|
+
str
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Typograph
|
2
|
+
module Processors
|
3
|
+
class Quotes
|
4
|
+
|
5
|
+
SPECIAL = {
|
6
|
+
:lsquo => '‘',
|
7
|
+
:rsquo => '’',
|
8
|
+
:sbquo => '‚',
|
9
|
+
:ldquo => '“',
|
10
|
+
:rdquo => '”',
|
11
|
+
:bdquo => '„',
|
12
|
+
:quot => '"',
|
13
|
+
:grave => '`',
|
14
|
+
:laquo => '«',
|
15
|
+
:raquo => '»',
|
16
|
+
:acute => '´',
|
17
|
+
}
|
18
|
+
RU = 'А-я'
|
19
|
+
EN = 'A-z'
|
20
|
+
|
21
|
+
def initialize(options)
|
22
|
+
@options = options
|
23
|
+
end
|
24
|
+
|
25
|
+
def process(str)
|
26
|
+
if @options.nil? || @options[:only].nil?
|
27
|
+
str = replace_russian_quotes str
|
28
|
+
str = replace_english_quotes str
|
29
|
+
else
|
30
|
+
if @options[:only].include? :ru
|
31
|
+
str = replace_russian_quotes str, RU+EN
|
32
|
+
elsif @options[:only].include? :en
|
33
|
+
str = replace_english_quotes str, RU+EN
|
34
|
+
end
|
35
|
+
end
|
36
|
+
str
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def replace_russian_quotes(str,lang=RU)
|
42
|
+
left1 = SPECIAL[:laquo]
|
43
|
+
right1 = SPECIAL[:raquo]
|
44
|
+
left2 = SPECIAL[:ldquo]
|
45
|
+
right2 = SPECIAL[:rdquo]
|
46
|
+
str = replace_quotes str, left1, right1, left2, right2, lang
|
47
|
+
end
|
48
|
+
|
49
|
+
def replace_english_quotes(str,lang=EN)
|
50
|
+
left1 = SPECIAL[:ldquo]
|
51
|
+
right1 = SPECIAL[:rdquo]
|
52
|
+
left2 = SPECIAL[:lsquo]
|
53
|
+
right2 = SPECIAL[:rsquo]
|
54
|
+
str = replace_quotes str, left1, right1, left2, right2, lang
|
55
|
+
end
|
56
|
+
|
57
|
+
def replace_quotes(str,left1,right1,left2,right2,letters)
|
58
|
+
replace_quotes = lambda do
|
59
|
+
old_str = String.new(str)
|
60
|
+
str.gsub!(Regexp.new("(\"|\')([#{letters}].*?[^\\s])\\1", Regexp::MULTILINE | Regexp::IGNORECASE)) do |match|
|
61
|
+
inside, before, after = $2, $`, $'
|
62
|
+
if after.match(/^([^<]+>|>)/) || before.match(/<[^>]+$/)
|
63
|
+
match
|
64
|
+
else
|
65
|
+
"#{left1}#{inside}#{right1}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
old_str != str
|
69
|
+
end
|
70
|
+
while replace_quotes.call do end
|
71
|
+
replace_second_level_quotes = lambda do
|
72
|
+
regexp = "(#{left1}([^#{right1},.])*)#{left1}(.*)#{right1}(.*#{right1})"
|
73
|
+
str.gsub! Regexp.new(regexp, Regexp::MULTILINE | Regexp::IGNORECASE) do |text|
|
74
|
+
"#{$1}#{left2}#{$3}#{right2}#{$4}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
while replace_second_level_quotes.call do end
|
78
|
+
str
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Typograph
|
4
|
+
module Processors
|
5
|
+
class RussianGrammar
|
6
|
+
|
7
|
+
OPTIONS = {
|
8
|
+
:ndash => '–',
|
9
|
+
:mdash => '—',
|
10
|
+
:minus => '-',
|
11
|
+
:orphan => false
|
12
|
+
}
|
13
|
+
|
14
|
+
def initialize(options={})
|
15
|
+
@options = OPTIONS.dup.merge(options)
|
16
|
+
|
17
|
+
@ndash = @options[:ndash]
|
18
|
+
@mdash = @options[:mdash]
|
19
|
+
@minus = @options[:minus]
|
20
|
+
|
21
|
+
@abbr = 'ООО|ОАО|ЗАО|ЧП|ИП|НПФ|НИИ|ООО\p{Zs}ТПК'
|
22
|
+
@prepos = 'а|в|во|вне|и|к|о|с|у|со|об|обо|от|ото|то|на|не|ни|но|из|изо|за|уж|на|по|подо|пред|предо|про|над|надо|как|без|безо|да|до|там|ещё|их|ко|меж|между|перед|передо|около|через|сквозь|при|я'
|
23
|
+
# что|для|или|под
|
24
|
+
@metrics = 'мм|см|м|км'
|
25
|
+
@measure = "#{@metrics}|г|кг|б|кб|мб|гб|dpi|px"
|
26
|
+
@shortages = 'г|гр|тов|пос|c|ул|д|пер|м|зам|им|бул'
|
27
|
+
@money = 'руб\.|долл\.|евро|у\.е\.'
|
28
|
+
@countables = 'млн|тыс'
|
29
|
+
|
30
|
+
@rules_strict = rules_strict
|
31
|
+
@rules_symbols = rules_symbols
|
32
|
+
@rules_braces = rules_braces
|
33
|
+
@rules_main = rules_main
|
34
|
+
@rules_main[/(\s)(\S+)\Z/] = ' \2' if options[:orphan]
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def process(str)
|
39
|
+
str = apply_rules @rules_strict, str # Сначала применим строгие правила: пробелы, запятые
|
40
|
+
str = apply_rules @rules_symbols, str
|
41
|
+
str = apply_rules @rules_main, str
|
42
|
+
str = apply_rules @rules_braces, str
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def apply_rules(rules, str)
|
48
|
+
res = str.dup
|
49
|
+
rules.each do |rul, rep|
|
50
|
+
str.gsub!(rul, rep)
|
51
|
+
end
|
52
|
+
str
|
53
|
+
end
|
54
|
+
|
55
|
+
def rules_strict
|
56
|
+
{
|
57
|
+
# Много пробелов или табуляций -> один пробел
|
58
|
+
/( |\t)+/ => ' ',
|
59
|
+
# Запятые после «а» и «но». Если уже есть — не ставим.
|
60
|
+
/([а-яA-я0-9])\s(а|но)\s/ => '\1, \2 ',
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def rules_symbols
|
65
|
+
{
|
66
|
+
# Лишние знаки.
|
67
|
+
# TODO: сделать красиво
|
68
|
+
/([^!])!!([^!])/ => '\1!\2',
|
69
|
+
/([^?])\?\?([^?])/ => '\1?\2',
|
70
|
+
/(\p{L})\s*\.\.(\p{Zs})/ => '\1.\2', # new
|
71
|
+
/(\p{L});[;\s]*(;)/ => '\1;', # new
|
72
|
+
/(\p{L}),[,\s]*(,)/ => '\1,', # new
|
73
|
+
/(\p{L}):[:\s]*(:)/ => '\1:', # new
|
74
|
+
|
75
|
+
/([!\?])\s*(!\s*)[!\s]*(!)/ => '\1!!', # new
|
76
|
+
/(\?\s*)(\?\s*)[\?\s]*(\?)/ => '???', # new
|
77
|
+
/(\.\s*)(\.\s*)[\.\s]*(\.)/ => '...', # new
|
78
|
+
/(,\s*)[,\s]*(,)/ => ',', # new
|
79
|
+
/;{2,}/ => ';', # new
|
80
|
+
/,{2,}/ => ',', # new
|
81
|
+
/:{2,}/ => ':', # new
|
82
|
+
|
83
|
+
# /([;,:])(\S)/ => '\1 \2', # new
|
84
|
+
|
85
|
+
# Занятная комбинация
|
86
|
+
/!\?/ => '?!',
|
87
|
+
|
88
|
+
# Знаки (c), (r), (tm)
|
89
|
+
/\((c|с)\)/i => '©',
|
90
|
+
/\(r\)/i => '<sup><small>®</small></sup>',
|
91
|
+
/\(tm\)/i => '™',
|
92
|
+
|
93
|
+
# От 2 до 5 знака точки подряд - на знак многоточия (больше - мб авторской задумкой).
|
94
|
+
/\s*\.{2,5}/ => '…',
|
95
|
+
|
96
|
+
# Дроби
|
97
|
+
# TODO: найти замену \b
|
98
|
+
/\b1\/2\b/ => '½',
|
99
|
+
/\b1\/3\b/ => '⅓',
|
100
|
+
/\b2\/3\b/ => '⅔',
|
101
|
+
/\b1\/4\b/ => '¼',
|
102
|
+
/\b3\/4\b/ => '¾',
|
103
|
+
/\b1\/5\b/ => '⅕',
|
104
|
+
/\b2\/5\b/ => '⅖',
|
105
|
+
/\b3\/5\b/ => '⅗',
|
106
|
+
/\b4\/5\b/ => '⅘',
|
107
|
+
/\b1\/6\b/ => '⅙',
|
108
|
+
/\b5\/6\b/ => '⅚',
|
109
|
+
/\b1\/8\b/ => '⅛',
|
110
|
+
/\b3\/8\b/ => '⅜',
|
111
|
+
/\b5\/8\b/ => '⅝',
|
112
|
+
/\b7\/8\b/ => '⅞',
|
113
|
+
|
114
|
+
# LО'Лайт, O'Reilly
|
115
|
+
/([A-Z])''?([а-яА-Яa-zA-Z])/i => '\1’\2',
|
116
|
+
|
117
|
+
/'/ => ''',
|
118
|
+
|
119
|
+
# Размеры 10x10, правильный знак + убираем лишние пробелы
|
120
|
+
/(\p{Nd}+)\p{Zs}{0,}?[x|X|х|Х|*]\p{Zs}{0,}(\p{Nd}+)/ => '\1×\2',
|
121
|
+
|
122
|
+
# +-
|
123
|
+
/([^\+]|^)\+\/?-/ => '\1±',
|
124
|
+
|
125
|
+
# Стрелки
|
126
|
+
/([^-]|^)->/ => '\1→',
|
127
|
+
/<-([^-]|$)/ => '←\1'
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
def rules_braces
|
132
|
+
{
|
133
|
+
# Оторвать скобку от слова
|
134
|
+
/(\p{L})\(/ => '\1 (',
|
135
|
+
# Слепляем скобки со словами
|
136
|
+
/\( /m => '(',
|
137
|
+
/ \)/m => ')'
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
def rules_main
|
142
|
+
{
|
143
|
+
# Конфликт с «газо- и электросварка»
|
144
|
+
# Оторвать тире от слова
|
145
|
+
# /(\p{L})- / => '\1 - ',
|
146
|
+
|
147
|
+
# P.S., P.P.S - конфликтует с правилом инициалов.
|
148
|
+
'P.P.S.' => '<nobr>P. P. S.</nobr>',
|
149
|
+
/([^.]|^)P\.S\./ => '\1<nobr>P. S.</nobr>',
|
150
|
+
# /и( |\s)?т( |\s)?\.?д( |\s)?\.{0,2}/ => "<nobr>и т. д.</nobr>",
|
151
|
+
# /и( |\s)?т( |\s)?\.?п( |\s)?\.{0,2}/ => "<nobr>и т. п.</nobr>",
|
152
|
+
# /в( |\s)?т( |\s)?\.?ч( |\s)?\.{0,2}/ => "<nobr>в т. ч.</nobr>",
|
153
|
+
|
154
|
+
# Знаки с предшествующим пробелом… нехорошо!
|
155
|
+
# -/(\p{L}|>|\p{Nd}) +([?!:,;…])/ => '\1\2',
|
156
|
+
# /([?!:,;])(\p{L}|<)/ => '\1 \2',
|
157
|
+
# Для точки отдельно
|
158
|
+
/(\p{L})\p{Zs}(?:\.)(\p{Zs}|$)/ => '\1.\2',
|
159
|
+
# Но перед кавычками пробелов не ставим
|
160
|
+
/([?!:,;\.])\p{Zs}(»)/ => '\1\2',
|
161
|
+
|
162
|
+
# Неразрывные названия организаций и абревиатуры форм собственности
|
163
|
+
# ~ почему не один ?
|
164
|
+
# ! названия организаций тоже могут содержать пробел !
|
165
|
+
/(#{@abbr})\p{Zs}+(«[^»]*»)/ => '<nobr>\1 \2</nobr>',
|
166
|
+
|
167
|
+
# Нельзя отрывать сокращение от относящегося к нему слова.
|
168
|
+
# Например: тов. Сталин, г. Воронеж
|
169
|
+
# Ставит пробел, если его нет.
|
170
|
+
/(^|[^a-zA-Zа-яА-Я])(#{@shortages})\.\s?([А-Я0-9]+)/m => '\1\2. \3',
|
171
|
+
/(^|[^a-zA-Zа-яА-Я])(см)\.\s?([А-Яа-я]+)/m => '\1\2. \3',
|
172
|
+
|
173
|
+
# Не отделять стр., с. и т.д. от номера.
|
174
|
+
/(стр|с|табл|рис|илл|гл)\.\p{Zs}*(\p{Nd}+)/mi => '\1. \2',
|
175
|
+
|
176
|
+
# Не разделять 2007 г., ставить пробел, если его нет. Ставит точку, если её нет.
|
177
|
+
/(\p{Nd}+)\p{Zs}*([гГ])\.\s/m => '\1 \2. ',
|
178
|
+
/(\p{Nd}+)\p{Zs}*-\p{Zs}*(\p{Nd}+)г\.?г\./ => "<nobr>\\1—\\2 гг.</nobr>",
|
179
|
+
/(\d\d\.\d\d\.\d\d\d?\d?)(\s| )*г\.?(\s| )/ => "<nobr>\\1 г.</nobr> ",
|
180
|
+
|
181
|
+
# Неразрывный пробел между цифрой и единицей измерения
|
182
|
+
/(\p{Nd}+)\s*(#{@measure})/m => '\1 \2',
|
183
|
+
|
184
|
+
# Сантиметр и другие ед. измерения в квадрате, кубе и т.д.
|
185
|
+
/(\p{Zs}#{@metrics})2/ => '\1²',
|
186
|
+
/(\p{Zs}#{@metrics})3/ => '\1³',
|
187
|
+
/(\p{Zs}#{@metrics})(\p{Nd}+)/ => '\1<sup>\2</sup>',
|
188
|
+
|
189
|
+
# Знак дефиса или два знака дефиса подряд — на знак длинного тире.
|
190
|
+
# + Нельзя разрывать строку перед тире, например: Знание — сила, Курить — здоровью вредить.
|
191
|
+
/\p{Zs}+(?:--?|—)(?=\p{Zs})/ => " #{@mdash}",
|
192
|
+
/^(?:--?|—)(?=\p{Zs})/ => @mdash,
|
193
|
+
|
194
|
+
# Прямая речь
|
195
|
+
/(?:^|\s+)(?:--?|—)(?=\p{Zs})/ => '\0',
|
196
|
+
|
197
|
+
# Знак дефиса, ограниченный с обоих сторон цифрами — на знак короткого тире.
|
198
|
+
/(?<=\p{Nd})-(?=\p{Nd})/ => @minus,
|
199
|
+
|
200
|
+
# Знак дефиса, ограниченный с обоих сторон пробелами — на знак длинного тире.
|
201
|
+
/(\s)(–|–)(\s)/ => " #{@mdash} ",
|
202
|
+
|
203
|
+
# Знак дефиса, идущий после тэга и справа пробел — на знак длинного тире.
|
204
|
+
/(?<=>)(–|–|-)(\s)/ => "#{@mdash} ",
|
205
|
+
|
206
|
+
# Расстановка дефиса перед -ка, -де, -кась
|
207
|
+
# /\b(\S+)[\s-](ка|де|кась)\b/ => "<nobr>\\1#{ndash}\\2</nobr>",
|
208
|
+
|
209
|
+
# Расстановка дефиса после кое-, кой-
|
210
|
+
/\b(кое|кой)[\s-](как|кого|какой)\b/i => "<nobr>\\1#{@ndash}\\2</nobr>",
|
211
|
+
|
212
|
+
# Расстановка дефиса перед -то, -либо, -нибудь
|
213
|
+
### to od /(кто|что|где|когда|почему|зачем|кем|чем|как|чего)(\s|-|–|—| )+(либо|нибудь|то)([^:]|$)/i => "<nobr>\\1#{ndash}\\3</nobr>\\4",
|
214
|
+
|
215
|
+
# Расстановка дефиса перед -таки
|
216
|
+
/(все)(\s|-|–|—| )+(таки)/i => "<nobr>\\1#{@ndash}\\3</nobr>",
|
217
|
+
|
218
|
+
# Расстановка дефисов в предлогах «из-за», «из-под», «по-над», «по-под».
|
219
|
+
/\b(из)[\s-]?(за|под)\b/i => "<nobr>\\1#{@ndash}\\2</nobr>",
|
220
|
+
/\b(по)[\s-]?(над|под)\b/i => "<nobr>\\1#{@ndash}\\2</nobr>",
|
221
|
+
|
222
|
+
# Нельзя оставлять в конце строки предлоги и союзы
|
223
|
+
/(?<=\p{Zs}|^|\p{^L})(#{@prepos})(\s+)/i => '\1 ',
|
224
|
+
|
225
|
+
# Нельзя отрывать частицы бы, ли, же от предшествующего слова, например: как бы, вряд ли, так же.
|
226
|
+
/(?<=\p{^Zs})(\p{Zs}+)(ж|бы|б|же|ли|ль|либо)(?=(<.*?>)*[\p{Zs})!?.…])/i => ' \2',
|
227
|
+
# |или
|
228
|
+
|
229
|
+
# Неразрывный пробел после инициалов.
|
230
|
+
/([А-ЯA-Z]\.)\s?([А-ЯA-Z]\.)\p{Zs}?([А-ЯA-Z][а-яa-z]+)/m => '\1\2 \3',
|
231
|
+
|
232
|
+
# Сокращения сумм не отделяются от чисел.
|
233
|
+
/(\p{Nd}+)\p{Zs}?(#{@countables})/m => '\1 \2',
|
234
|
+
|
235
|
+
# «уе» в денежных суммах
|
236
|
+
/(\p{Nd}+|#{@countables})\p{Zs}?уе/m => '\1 у.е.',
|
237
|
+
|
238
|
+
# Денежные суммы, расставляя пробелы в нужных местах.
|
239
|
+
/(\p{Nd}+|#{@countables})\p{Zs}?(#{@money})/m => '\1 \2',
|
240
|
+
|
241
|
+
# Неразрывные пробелы в кавычках
|
242
|
+
# "/($sym[lquote]\S*)(\s+)(\S*$sym[rquote])/U" => '\1'.\sym["nbsp"].'\3',
|
243
|
+
|
244
|
+
# Телефоны
|
245
|
+
/(?:тел\.?\/?факс:?\s?\((\d+)\))/i => 'тел./факс: (\1)',
|
246
|
+
|
247
|
+
/тел[:\.] ?(\p{Nd}+)/m => '<nobr>тел: \1</nobr>',
|
248
|
+
|
249
|
+
# Номер версии программы пишем неразрывно с буковкой v.
|
250
|
+
/([vв]\.) ?(\p{Nd})/i => '\1 \2',
|
251
|
+
/(\p{L}) ([vв]\.)/i => '\1 \2',
|
252
|
+
|
253
|
+
# % не отделяется от числа
|
254
|
+
/(\p{Nd}+)\p{Zs}+%/ => '\1%',
|
255
|
+
|
256
|
+
# IP-адреса рвать нехорошо
|
257
|
+
/(1\p{Nd}{0,2}|2(\p{Nd}|[0-5]\p{Nd}+)?)\.(0|1\p{Nd}{0,2}|2(\p{Nd}|[0-5]\p{Nd})?)\.(0|1\p{Nd}{0,2}|2(\p{Nd}|[0-5]\p{Nd})?)\.(0|1\p{Nd}{0,2}|2(\p{Nd}|[0-5]\p{Nd})?)/ =>
|
258
|
+
'<nobr>\0</nobr>',
|
259
|
+
|
260
|
+
# Делаем неразрывными слова с дефисом.
|
261
|
+
# Пример: "слово-слово", "слово-слово-слово".
|
262
|
+
/(\^|;)((\p{L}+((–|–|-)\p{L}+){1,2}))/ => '\1<nobr>\2</nobr>',
|
263
|
+
|
264
|
+
# Меняем ё на е и Ё на Е
|
265
|
+
/ё/ => 'е',
|
266
|
+
/Ё/ => 'Е',
|
267
|
+
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# $KCODE = "utf-8" if defined?($KCODE)
|
3
|
+
|
4
|
+
require "typograph"
|
5
|
+
require "rspec"
|
6
|
+
|
7
|
+
OPT = {
|
8
|
+
:ndash => '-'
|
9
|
+
}
|
10
|
+
|
11
|
+
describe 'errors' do
|
12
|
+
|
13
|
+
describe 'quotes' do
|
14
|
+
|
15
|
+
it '#1' do
|
16
|
+
text = "БМК «ПРОСТ»,Библиотека Тургенева приглашают на 2-е заседание дискуссионного клуба 'Дисккуб21'."
|
17
|
+
text_processed = "БМК «ПРОСТ»,Библиотека Тургенева приглашают на 2-е заседание дискуссионного клуба «Дисккуб21»."
|
18
|
+
Typograph.process(text, OPT).should eq text_processed
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
it '#2' do
|
23
|
+
text = "Аля Самохина, создатель блога wholesomeway.net, и Аня Сидорова, организатор фитнес-туров Fitness travel, научат готовить необычные блюда из фруктов, зелени, орехов и других полезных продуктов:\n\n"+
|
24
|
+
"— смузи \"Бодрость зелени\";\n\n" +
|
25
|
+
"— мороженое \"Банановый рассвет\";\n\n" +
|
26
|
+
"— пирожное \"Баунти\".\n\n" +
|
27
|
+
"Мастер-класс Али Самохиной и Ани Сидоровой состоится в рамках Делай феста — в секции 'Ланчи-бранчи'. Полная программа «Делай феста»\n"
|
28
|
+
|
29
|
+
text_processed = "Аля Самохина, создатель блога wholesomeway.net, и Аня Сидорова, организатор фитнес-туров Fitness travel, научат готовить необычные блюда из фруктов, зелени, орехов и других полезных продуктов:\n\n"+
|
30
|
+
"— смузи «Бодрость зелени»;\n\n" +
|
31
|
+
"— мороженое «Банановый рассвет»;\n\n" +
|
32
|
+
"— пирожное «Баунти».\n\n" +
|
33
|
+
"Мастер-класс Али Самохиной и Ани Сидоровой состоится в рамках Делай феста — в секции «Ланчи-бранчи». Полная программа «Делай феста»\n"
|
34
|
+
|
35
|
+
Typograph.process(text, OPT).should eq text_processed
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'ampersand' do
|
41
|
+
it '#1' do
|
42
|
+
text = '<p>Воркшоп состоится в рамках «Делай феста» — в секции «Арт&Дизайн». Полная программа http://makefest.exchang.es/schedule</p>'
|
43
|
+
text_processed = "<p>Воркшоп состоится в рамках «Делай феста» — в секции «Арт&Дизайн». Полная программа http://makefest.exchang.es/schedule</p>"
|
44
|
+
Typograph.process(text, OPT).should eq text_processed
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
describe 'options' do
|
51
|
+
it 'only russian quotes' do
|
52
|
+
text = 'Тумбочка "staple x"'
|
53
|
+
text_processed = "Тумбочка «staple x»"
|
54
|
+
Typograph.process(text, only: [:ru]).should eq text_processed
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,431 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# $KCODE = "utf-8" if defined?($KCODE)
|
3
|
+
|
4
|
+
require "typograph"
|
5
|
+
require "rspec"
|
6
|
+
|
7
|
+
OPT = {
|
8
|
+
:ndash => '-'
|
9
|
+
}
|
10
|
+
|
11
|
+
describe '.process' do
|
12
|
+
it 'Удаление лишних пробельных символов и табуляций' do
|
13
|
+
text = "В этом тексте \t много пробелов."
|
14
|
+
text_processed = 'В этом тексте много пробелов.'
|
15
|
+
Typograph.process(text, OPT).should eq text_processed
|
16
|
+
end
|
17
|
+
|
18
|
+
# it 'Удаление повторяющихся слов' do
|
19
|
+
# text = 'При при проверке текста обнаружились обнаружились повторяющиеся слова слова. Слова убраны.'
|
20
|
+
# text_processed = 'При проверке текста обнаружились повторяющиеся слова. Слова убраны.'
|
21
|
+
# Typograph.process(text, OPT).should eq text_processed
|
22
|
+
# end
|
23
|
+
|
24
|
+
it 'Расстановка запятых перед а, но' do
|
25
|
+
text = 'Мало написать а запятые кто за тебя расставит.'
|
26
|
+
text_processed = 'Мало написать, а запятые кто за тебя расставит.'
|
27
|
+
Typograph.process(text, OPT).should eq text_processed
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'Отсутствие запятых у "а"" и "но" после тире' do
|
31
|
+
text = 'Текст до тире – а теперь после'
|
32
|
+
text_processed = 'Текст до тире — а теперь после'
|
33
|
+
Typograph.process(text, OPT).should eq text_processed
|
34
|
+
end
|
35
|
+
|
36
|
+
# it 'Расстановка дефиса перед -то, -либо, -нибудь' do
|
37
|
+
# text = 'Кто то где то когда то как то что то чем то стукнул. И возможно чего нибудь бы получилось если б кто либо пришел.'
|
38
|
+
# text_processed = '<nobr>Кто-то</nobr> <nobr>где-то</nobr> <nobr>когда-то</nobr> <nobr>как-то</nobr> <nobr>что-то</nobr> <nobr>чем-то</nobr> стукнул. И возможно <nobr>чего-нибудь</nobr> бы получилось если б <nobr>кто-либо</nobr> пришел.'
|
39
|
+
# # Опять же что - то получилось, но как- то не так.
|
40
|
+
# Typograph.process(text, OPT).should eq text_processed
|
41
|
+
# end
|
42
|
+
|
43
|
+
# it 'Расстановка дефиса перед -ка, -де, -кась' do
|
44
|
+
# text = 'Возьми ка детка молока. А коль увижу де, что казнь ему мала, повешу тут же всех судей вокруг стола. Поди кась так. Планета Ка-Пэкс'
|
45
|
+
# text_processed = '<nobr>Возьми-ка</nobr> детка молока. А коль <nobr>увижу-де</nobr>, что казнь ему мала, повешу тут же всех судей вокруг стола. <nobr>Поди-кась</nobr> так. Планета Ка-Пэкс'
|
46
|
+
# Typograph.process(text, OPT).should eq text_processed
|
47
|
+
# end
|
48
|
+
|
49
|
+
it 'Расстановка дефиса после кое-, кой-' do
|
50
|
+
text = 'Кое как дошли. Кой кого встретили. Кое от кого, кое на чем, кой у кого, кое с чьим.'
|
51
|
+
text_processed = '<nobr>Кое-как</nobr> дошли. <nobr>Кой-кого</nobr> встретили. Кое от кого, кое на чем, кой у кого, кое с чьим.'
|
52
|
+
Typograph.process(text, OPT).should eq text_processed
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'Расстановка дефиса перед -таки' do
|
56
|
+
text = 'Секретарь, хотя и чувствовал свое слабое недовольство, все таки радовался наличию таких старушек в активе района. Но хоть и велик был соблазн, я таки успел себя побороть.'
|
57
|
+
text_processed = 'Секретарь, хотя и чувствовал свое слабое недовольство, <nobr>все-таки</nobr> радовался наличию таких старушек в активе района. Но хоть и велик был соблазн, я таки успел себя побороть.'
|
58
|
+
Typograph.process(text, OPT).should eq text_processed
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'Расстановка дефиса в предлогах из-за, из-под' do
|
62
|
+
text = 'Из за леса величаво выплывало солнце. Из под развесистой сирени вдруг с лаем выскочила собака.'
|
63
|
+
text_processed = '<nobr>Из-за</nobr> леса величаво выплывало солнце. <nobr>Из-под</nobr> развесистой сирени вдруг с лаем выскочила собака.'
|
64
|
+
Typograph.process(text, OPT).should eq text_processed
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'Удаление парных знаков препинания' do
|
68
|
+
text = 'Правда!? Правда!!! Неправда??? Честно?? Честно!! Проехали.. Задумчиво...'
|
69
|
+
text_processed = 'Правда?! Правда!!! Неправда??? Честно? Честно! Проехали. Задумчиво…'
|
70
|
+
Typograph.process(text, OPT).should eq text_processed
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'Удаление пробелов перед знаками препинания' do
|
74
|
+
text = 'Некоторые виды деревьев : ель ,сосна , берёза, дуб ; растут в наших лесах .'
|
75
|
+
text_processed = 'Некоторые виды деревьев: ель, сосна, береза, дуб; растут в наших лесах.'
|
76
|
+
Typograph.process(text, OPT).should eq text_processed
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'Расстановка пробелов после знака препинания' do
|
80
|
+
text = 'Так бывает.И вот так...И ещё вот так!..Бывает же???Что поделать. Вывод:верен.'
|
81
|
+
text_processed = 'Так бывает. И вот так… И ещё вот так!.. Бывает же??? Что поделать. Вывод: верен.'
|
82
|
+
Typograph.process(text, OPT).should eq text_processed
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'Удаление пробела перед символом процент' do
|
86
|
+
text = '100 %'
|
87
|
+
text_processed = '100%'
|
88
|
+
Typograph.process(text, OPT).should eq text_processed
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'Удаление пробелов внутри скобок' do
|
92
|
+
text = 'Текст( ( Внутри ) скобок ).'
|
93
|
+
text_processed = 'Текст ((Внутри) скобок).'
|
94
|
+
Typograph.process(text, OPT).should eq text_processed
|
95
|
+
end
|
96
|
+
|
97
|
+
# it 'Расстановка пробела перед скобками' do
|
98
|
+
# text = 'Текст(Внутри скобок).'
|
99
|
+
# text_processed = 'Текст (Внутри скобок).'
|
100
|
+
# Typograph.process(text, OPT).should eq text_processed
|
101
|
+
# end
|
102
|
+
|
103
|
+
it 'Выделение прямой речи' do
|
104
|
+
text = '- Я пошёл домой... - Может останешься? - Нет, ухожу.'
|
105
|
+
text_processed = '— Я пошёл домой… — Может останешься? — Нет, ухожу.'
|
106
|
+
Typograph.process(text, OPT).should eq text_processed
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'Привязка союзов, предлогов' do
|
110
|
+
text = 'Я бы в лётчики б пошёл, пусть меня научат.'
|
111
|
+
text_processed = 'Я бы в летчики б пошел, пусть меня научат.'
|
112
|
+
Typograph.process(text, OPT).should eq text_processed
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'Замена x на символ × в размерных единицах' do
|
116
|
+
text = '3х4, 3 х 6'
|
117
|
+
text_processed = '3×4, 3×6'
|
118
|
+
Typograph.process(text, OPT).should eq text_processed
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'Замена дробей 1/2, 1/4, 3/4 на соответствующие символы' do
|
122
|
+
text = '1/2, 1/4, 3/4, 123/432'
|
123
|
+
text_processed = '½, ¼, ¾, 123/432'
|
124
|
+
Typograph.process(text, OPT).should eq text_processed
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'Замена (R) на символ зарегистрированной торговой марки' do
|
128
|
+
text = '(r)'
|
129
|
+
text_processed = '<sup><small>®</small></sup>'
|
130
|
+
Typograph.process(text, OPT).should eq text_processed
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'Замена (c) на символ копирайт' do
|
134
|
+
text = '(c) Eugene Spearance'
|
135
|
+
text_processed = '© Eugene Spearance'
|
136
|
+
Typograph.process(text, OPT).should eq text_processed
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'Замена (tm) на символ торговой марки' do
|
140
|
+
text = 'ВасяПупкин(tm)'
|
141
|
+
text_processed = 'ВасяПупкин™'
|
142
|
+
Typograph.process(text, OPT).should eq text_processed
|
143
|
+
end
|
144
|
+
|
145
|
+
# it 'Тире в конце строки (стихотворная форма)' do
|
146
|
+
# text = %q{Раздобудь к утру ковёр -
|
147
|
+
# Шитый золотом узор!..
|
148
|
+
# Государственное дело, -
|
149
|
+
# Расшибись, а будь добёр!}
|
150
|
+
# text_processed = %q{Раздобудь к утру ковёр —<br />\nШитый золотом узор!..<br />\nГосударственное дело, —<br />\nРасшибись, а будь добёр!}
|
151
|
+
# Typograph.process(text, OPT).should eq text_processed
|
152
|
+
# end
|
153
|
+
|
154
|
+
it 'Расстановка знака минус между числами' do
|
155
|
+
text = '123-32'
|
156
|
+
text_processed = '123-32'
|
157
|
+
Typograph.process(text, OPT).should eq text_processed
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'Расстановка правильного апострофа в английских текстах' do
|
161
|
+
text = "don't"
|
162
|
+
text_processed = 'don’t'
|
163
|
+
Typograph.process(text, OPT).should eq text_processed
|
164
|
+
end
|
165
|
+
|
166
|
+
# it 'Замена символа параграф с привязкой к числу' do
|
167
|
+
# text = '§32, §IV'
|
168
|
+
# text_processed = '§ 32, § IV'
|
169
|
+
# Typograph.process(text, OPT).should eq text_processed
|
170
|
+
# end
|
171
|
+
|
172
|
+
# it 'Замена символа номер с привязкой к числу' do
|
173
|
+
# text = '№15Ф, №34/25'
|
174
|
+
# text_processed = '№ 15Ф, № 34/25'
|
175
|
+
# Typograph.process(text, OPT).should eq text_processed
|
176
|
+
# end
|
177
|
+
|
178
|
+
it 'Расстановка правильных «тройных» кавычек' do
|
179
|
+
text = 'Она добавила: "И цвет мой самый любимый - "эсмеральда"".'
|
180
|
+
text_processed = 'Она добавила: «И цвет мой самый любимый — “эсмеральда”».'
|
181
|
+
Typograph.process(text, OPT).should eq text_processed
|
182
|
+
end
|
183
|
+
|
184
|
+
# it 'Расстановка пробелов и привязка в денежных сокращениях' do
|
185
|
+
# text = '10,34руб., 23тыс.долл., 64 млн.евро, 34.3€, 56$, 3,65уе'
|
186
|
+
# text_processed = '10,34 руб., 23 тыс. долл., 64 млн. евро, 34.3 €, 56 $, 3,65 у.е.'
|
187
|
+
# Typograph.process(text, OPT).should eq text_processed
|
188
|
+
# end
|
189
|
+
|
190
|
+
# it 'Объединение сокращений и т.д., и т.п., в т.ч.' do
|
191
|
+
# text = 'Лес, газ, нефть и тд., и т.п.. Перины, подушки в тч. подушки-думки.'
|
192
|
+
# text_processed = 'Лес, газ, нефть <nobr>и т. д.</nobr>, <nobr>и т. п.</nobr> Перины, подушки <nobr>в т. ч.</nobr> <nobr>подушки-думки</nobr>.'
|
193
|
+
# Typograph.process(text, OPT).should eq text_processed
|
194
|
+
# end
|
195
|
+
|
196
|
+
it 'Расстановка пробелов перед сокращениями см., им.' do
|
197
|
+
text = 'Данные изложены в таблице см.цветной вкладыш. Дом им.Пушкина.'
|
198
|
+
text_processed = 'Данные изложены в таблице см. цветной вкладыш. Дом им. Пушкина.'
|
199
|
+
Typograph.process(text, OPT).should eq text_processed
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'Расстановка пробелов перед сокращениями гл., стр., рис., илл.' do
|
203
|
+
text = 'Инструкцию см. гл. 8, стр.34, рис.3 или илл.3.'
|
204
|
+
text_processed = 'Инструкцию см. гл. 8, стр. 34, рис. 3 или илл. 3.'
|
205
|
+
Typograph.process(text, OPT).should eq text_processed
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'Объединение сокращений и др.' do
|
209
|
+
text = 'Оля, Иван, Олег и др. ребята.'
|
210
|
+
text_processed = 'Оля, Иван, Олег и др. ребята.'
|
211
|
+
Typograph.process(text, OPT).should eq text_processed
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'Расстановка пробелов в сокращениях г., ул., пер., д.' do
|
215
|
+
text = 'г.Тюмень, ул.Ленина, пер. Ленина, бул. Ленина, д. 4'
|
216
|
+
text_processed = 'г. Тюмень, ул. Ленина, пер. Ленина, бул. Ленина, д. 4'
|
217
|
+
Typograph.process(text, OPT).should eq text_processed
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'Расстановка пробелов перед сокращениями dpi, lpi' do
|
221
|
+
text = 'Разрешение 300dpi (для офсета).'
|
222
|
+
text_processed = 'Разрешение 300 dpi (для офсета).'
|
223
|
+
Typograph.process(text, OPT).should eq text_processed
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'Объединение сокращений P.S., P.P.S.' do
|
227
|
+
text = 'P.S. привет всем. P.P.S. и мне тоже.'
|
228
|
+
text_processed = '<nobr>P. S.</nobr> привет всем. <nobr>P. P. S.</nobr> и мне тоже.'
|
229
|
+
Typograph.process(text, OPT).should eq text_processed
|
230
|
+
end
|
231
|
+
|
232
|
+
# it 'Объединение в неразрывные конструкции слов с дефисом.' do
|
233
|
+
# text = 'Жёлто-оранжевый цвет. Ростов-на-Дону красивый город.'
|
234
|
+
# text_processed = '<nobr>Жёлто-оранжевый</nobr> цвет. <nobr>Ростов-на-Дону</nobr> красивый город.'
|
235
|
+
# Typograph.process(text, OPT).should eq text_processed
|
236
|
+
# end
|
237
|
+
|
238
|
+
# it 'Привязка сокращений форм собственности к названиям организаций' do
|
239
|
+
# text = 'ООО "Фирма Терминал", НИИ "ОблСнабВротКомпот"'
|
240
|
+
# text_processed = '<nobr>ООО «Фирма Терминал»</nobr>, <nobr>НИИ «ОблСнабВротКомпот»</nobr>'
|
241
|
+
# Typograph.process(text, OPT).should eq text_processed
|
242
|
+
# end
|
243
|
+
|
244
|
+
# it 'Объединение в неразрывные конструкции номеров телефонов' do
|
245
|
+
# text = '+7 (3452) 55-66-77, 8 905 555-55-55'
|
246
|
+
# text_processed = '<nobr>+7 (3452) 55-66-77</nobr>, <nobr>8 905 555-55-55</nobr>'
|
247
|
+
# Typograph.process(text, OPT).should eq text_processed
|
248
|
+
# end
|
249
|
+
|
250
|
+
# it 'Привязка сокращения ГОСТ к номеру' do
|
251
|
+
# text = 'Гост 5773-90 - российские стандартные форматы изданий'
|
252
|
+
# text_processed = '<nobr>ГОСТ 5773–90 —</nobr> российские стандартные форматы изданий'
|
253
|
+
# Typograph.process(text, OPT).should eq text_processed
|
254
|
+
# end
|
255
|
+
|
256
|
+
# it 'Установка пробельных символов в сокращении вольт' do
|
257
|
+
# text = '~23,5в'
|
258
|
+
# text_processed = '<nobr>~23,5 В</nobr>'
|
259
|
+
# Typograph.process(text, OPT).should eq text_processed
|
260
|
+
# end
|
261
|
+
|
262
|
+
# it 'Объединение триад чисел' do
|
263
|
+
# text = '123 456 789 руб. В стычке участвовало 3 200 человек.'
|
264
|
+
# text_processed = '123 456 789 руб. В стычке участвовало 3 200 человек.'
|
265
|
+
# Typograph.process(text, OPT).should eq text_processed
|
266
|
+
# end
|
267
|
+
|
268
|
+
it 'Объединение IP-адресов' do
|
269
|
+
text = 'Адрес localhost - 127.0.0.1'
|
270
|
+
text_processed = 'Адрес localhost — <nobr>127.0.0.1</nobr>'
|
271
|
+
Typograph.process(text, OPT).should eq text_processed
|
272
|
+
end
|
273
|
+
|
274
|
+
# it 'Установка тире и пробельных символов в периодах дат' do
|
275
|
+
# text = 'Это событие произошло между 1999-2001г.г., на стыке XX-XXIв.'
|
276
|
+
# text_processed = 'Это событие произошло между <nobr>1999—2001 гг.</nobr>, на стыке <nobr>XX—XXI вв.</nobr>'
|
277
|
+
# Typograph.process(text, OPT).should eq text_processed
|
278
|
+
# end
|
279
|
+
|
280
|
+
# it 'Привязка года к дате' do
|
281
|
+
# text = 'Документ был подписан 17.02.1983г. И утратил свою силу 07.03.93 года.'
|
282
|
+
# text_processed = 'Документ был подписан <nobr>17.02.1983 г.</nobr> И утратил свою силу 07.03.93 года.'
|
283
|
+
# Typograph.process(text, OPT).should eq text_processed
|
284
|
+
# end
|
285
|
+
|
286
|
+
# it 'Расстановка тире и объединение в неразрывные периоды дней' do
|
287
|
+
# text = 'Собеседования состоятся 14-24 сентября, в актовом зале с 11:30-13:00.'
|
288
|
+
# text_processed = 'Собеседования состоятся <nobr>14—24 сентября</nobr>, в актовом зале с <nobr>11:30—13:00</nobr>.'
|
289
|
+
# Typograph.process(text, OPT).should eq text_processed
|
290
|
+
# end
|
291
|
+
|
292
|
+
it 'Расстановка тире и объединение в неразрывные периоды месяцев' do
|
293
|
+
text = 'Выставка пройдёт в апреле-мае этого года.'
|
294
|
+
text_processed = 'Выставка пройдет в <nobr>апреле-мае</nobr> этого года.'
|
295
|
+
Typograph.process(text, OPT).should eq text_processed
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'Привязка сокращений до н.э., н.э.' do
|
299
|
+
text = 'IV в до н.э, в V-VIвв до нэ., третий в. н.э.'
|
300
|
+
text_processed = 'IV в до н.э, в <nobr>V-VIвв</nobr> до нэ., третий в. н.э.'
|
301
|
+
Typograph.process(text, OPT).should eq text_processed
|
302
|
+
end
|
303
|
+
|
304
|
+
it 'Привязка инициалов к фамилиям' do
|
305
|
+
text = 'А.С.Пушкин, Пушкин А.С.'
|
306
|
+
text_processed = 'А.С. Пушкин, Пушкин А.С.'
|
307
|
+
Typograph.process(text, OPT).should eq text_processed
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'Замена символа градус, плюс-минус' do
|
311
|
+
text = '+- 10, +/- 25'
|
312
|
+
text_processed = '± 10, ± 25'
|
313
|
+
Typograph.process(text, OPT).should eq text_processed
|
314
|
+
end
|
315
|
+
|
316
|
+
it 'Замена символов и привязка сокращений в размерных величинах: м, см, м2…' do
|
317
|
+
text = 'На лесопилку завезли 32 м3 леса, из которых 4м3 пустили под распил на 25мм доски, длинной по 6м.'
|
318
|
+
text_processed = 'На лесопилку завезли 32 м³ леса, из которых 4 м³ пустили под распил на 25 мм доски, длинной по 6 м.'
|
319
|
+
Typograph.process(text, OPT).should eq text_processed
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'Повторное типографирование текста' do
|
323
|
+
text = '<p><nobr>Coca-Cola</nobr><sup><small>®</small></sup> — зарегистрированный товарный знак.</p>'
|
324
|
+
text_processed = '<p><nobr>Coca-Cola</nobr><sup><small>®</small></sup> — зарегистрированный товарный знак.</p>'
|
325
|
+
Typograph.process(text, OPT).should eq text_processed
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'Обработка HTML' do
|
329
|
+
text = '<!-- В тексте попался комментарий -->'
|
330
|
+
text_processed = text.dup
|
331
|
+
Typograph.process(text, OPT).should eq text_processed
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'Обработка CSS' do
|
335
|
+
text = %q{<style type="text/css">
|
336
|
+
SPAN.nobr { white-space: nowrap }
|
337
|
+
</style>}
|
338
|
+
text_processed = text.dup
|
339
|
+
Typograph.process(text, OPT).should eq text_processed
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'Обработка JavaScript' do
|
343
|
+
text = "<script type='text/javascript'>document.write('Этот текст не должен быть типографирован.')</script>"
|
344
|
+
text_processed = text.dup
|
345
|
+
Typograph.process(text, OPT).should eq text_processed
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'Обработка тегов <pre>' do
|
349
|
+
text = %q{<pre> Я не хотел бы
|
350
|
+
чтобы этот текст
|
351
|
+
был форматирован.</pre>}
|
352
|
+
text_processed = text.dup
|
353
|
+
Typograph.process(text, OPT).should eq text_processed
|
354
|
+
end
|
355
|
+
|
356
|
+
# it 'Выделение ссылок из текста' do
|
357
|
+
# text = 'В тексте встретилась ссылка: http://www.typograf.ru'
|
358
|
+
# text_processed = 'В тексте встретилась ссылка: <a href="http://www.typograf.ru">http://www.typograf.ru</a>'
|
359
|
+
# Typograph.process(text, OPT).should eq text_processed
|
360
|
+
# end
|
361
|
+
|
362
|
+
# it 'Выделение e-mail из текста' do
|
363
|
+
# text = 'В тексте встретился e-mail: mail@typograf.ru'
|
364
|
+
# text_processed = 'В тексте встретился <nobr>e-mail</nobr>: <a href="mailto:mail@typograf.ru">mail@typograf.ru</a>'
|
365
|
+
# Typograph.process(text, OPT).should eq text_processed
|
366
|
+
# end
|
367
|
+
|
368
|
+
# it 'Обработка списков' do
|
369
|
+
# text = ''
|
370
|
+
# # * Программное обеспечение
|
371
|
+
# # ** Операционные системы
|
372
|
+
# # +++ Windows XP
|
373
|
+
# # +++ Linux RedHat
|
374
|
+
# # +++ MacOS X
|
375
|
+
# # ** Текстовые радакторы
|
376
|
+
# # * Компьютеры
|
377
|
+
# text_processed = ''
|
378
|
+
# Typograph.process(text, OPT).should eq text_processed
|
379
|
+
# end
|
380
|
+
|
381
|
+
# it 'Выделение акронимов' do
|
382
|
+
# text = '<p>Все что вы хотели узнать о HTML.</p>'
|
383
|
+
# text_processed = '<p>Все что вы хотели узнать о <acronym title="HyperText Markup Language" lang="en">HTML</acronym>.</p>'
|
384
|
+
# Typograph.process(text, OPT).should eq text_processed
|
385
|
+
# end
|
386
|
+
|
387
|
+
it 'Меняем ё на е и Ё на Е' do
|
388
|
+
text = 'Ёж обыкновенный, или европейский ёж.'
|
389
|
+
text_processed = 'Еж обыкновенный, или европейский еж.'
|
390
|
+
Typograph.process(text, OPT).should eq text_processed
|
391
|
+
end
|
392
|
+
|
393
|
+
it 'Не трогаем ссылки в маркдауне' do
|
394
|
+
text = "Глава из книги Бориса Гройса «Поэтика политики»](http://theoryandpractice.ru/posts/6762-wikileaks-vosstanie-klerkov-glava-iz-knigi-borisa-groysa-poetika-politiki)"
|
395
|
+
text_processed = "Глава из книги Бориса Гройса «Поэтика политики»](http://theoryandpractice.ru/posts/6762-wikileaks-vosstanie-klerkov-glava-iz-knigi-borisa-groysa-poetika-politiki)"
|
396
|
+
Typograph.process(text, OPT).should eq text_processed
|
397
|
+
end
|
398
|
+
|
399
|
+
it 'Не трогам отсупы у тире, если с тире начинается строка' do
|
400
|
+
text = "Правах человека в России, и о ее месте в Совете Европы, и о коллективной памяти о преступлениях Советской власти.
|
401
|
+
Татьяна Добровольская
|
402
|
+
-------
|
403
|
+
**— Где и чему ты учишься, как давно?**
|
404
|
+
— Сейчас я учусь по обмену в Болонском университете на факультете политических наук. В Москве — в Высшей школе экономики"
|
405
|
+
text_processed = "Правах человека в России, и о ее месте в Совете Европы, и о коллективной памяти о преступлениях Советской власти.
|
406
|
+
Татьяна Добровольская
|
407
|
+
-------
|
408
|
+
**— Где и чему ты учишься, как давно?**
|
409
|
+
— Сейчас я учусь по обмену в Болонском университете на факультете политических наук. В Москве — в Высшей школе экономики"
|
410
|
+
Typograph.process(text, OPT).should eq text_processed
|
411
|
+
end
|
412
|
+
|
413
|
+
it "Лишнее тере с 'как' 'то'" do
|
414
|
+
text = "Я постараюсь ответить на твой вопрос так просто, как только смогу. Вот мой ответ:"
|
415
|
+
text_processed = "Я постараюсь ответить на твой вопрос так просто, как только смогу. Вот мой ответ:"
|
416
|
+
Typograph.process(text, OPT).should eq text_processed
|
417
|
+
end
|
418
|
+
|
419
|
+
it "should make english and russian quotes in the same string" do
|
420
|
+
text = '"Quotes" и "Кавычки"'
|
421
|
+
text_processed = '“Quotes” и «Кавычки»'
|
422
|
+
Typograph.process(text, OPT).should eq text_processed
|
423
|
+
end
|
424
|
+
|
425
|
+
it 'Quotes second level' do
|
426
|
+
text = '"Кавычки "второго уровня"" and "Quotes "second level""'
|
427
|
+
text_processed = "«Кавычки “второго уровня”» and “Quotes ‘second level’”"
|
428
|
+
Typograph.process(text, OPT).should eq text_processed
|
429
|
+
end
|
430
|
+
|
431
|
+
end
|
data/typographer.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'typograph/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'ruby_typograph'
|
8
|
+
gem.version = Typograph::VERSION
|
9
|
+
gem.authors = ['Tolia']
|
10
|
+
gem.email = ['toliademidov@gmail.com']
|
11
|
+
gem.description = %q{Gem for typographing russian and english texts.}
|
12
|
+
gem.summary = %q{Gem for typographing russian and english texts.}
|
13
|
+
gem.homepage = "https://github.com/Tolia/typograph"
|
14
|
+
gem.license = "MIT"
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(spec)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
|
21
|
+
gem.add_dependency 'htmlentities'
|
22
|
+
|
23
|
+
gem.add_development_dependency 'rake'
|
24
|
+
gem.add_development_dependency 'rspec'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby_typograph
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.pre.7
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tolia
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-12-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: htmlentities
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Gem for typographing russian and english texts.
|
56
|
+
email:
|
57
|
+
- toliademidov@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".rspec"
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- lib/typograph.rb
|
69
|
+
- lib/typograph/adapter.rb
|
70
|
+
- lib/typograph/processor.rb
|
71
|
+
- lib/typograph/processors/quotes.rb
|
72
|
+
- lib/typograph/processors/russian_grammar.rb
|
73
|
+
- lib/typograph/version.rb
|
74
|
+
- spec/helper.rb
|
75
|
+
- spec/parse_error_spec.rb
|
76
|
+
- spec/typograph_spec.rb
|
77
|
+
- typographer.gemspec
|
78
|
+
homepage: https://github.com/Tolia/typograph
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 1.3.1
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 2.2.2
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: Gem for typographing russian and english texts.
|
102
|
+
test_files:
|
103
|
+
- spec/helper.rb
|
104
|
+
- spec/parse_error_spec.rb
|
105
|
+
- spec/typograph_spec.rb
|