File Coverage

blib/lib/Text/Amuse/Preprocessor/Typography.pm
Criterion Covered Total %
statement 113 113 100.0
branch 5 6 83.3
condition 4 6 66.6
subroutine 16 16 100.0
pod 4 4 100.0
total 142 145 97.9


line stmt bran cond sub pod time code
1             # -*- mode: cperl -*-
2             package Text::Amuse::Preprocessor::Typography;
3              
4 12     12   230642 use strict;
  12         58  
  12         389  
5 12     12   62 use warnings;
  12         27  
  12         337  
6 12     12   644 use utf8;
  12         37  
  12         83  
7              
8             require Exporter;
9              
10             our @ISA = qw(Exporter);
11              
12             our @EXPORT_OK = qw/typography_filter linkify_filter
13             get_typography_filter/;
14              
15             # frozen at 0.09
16             our $VERSION = '0.09';
17              
18 12     12   7289 use Text::Amuse::Preprocessor::TypographyFilters;
  12         45  
  12         26817  
19              
20              
21             sub linkify_filter {
22 10     10 1 38 return Text::Amuse::Preprocessor::TypographyFilters::linkify(shift);
23             }
24              
25             sub _typography_filter_common {
26 98     98   198 my $l = shift;
27 98         281 $l =~ s/fi/fi/g ;
28 98         201 $l =~ s/fl/fl/g ;
29 98         207 $l =~ s/ffi/ffi/g ;
30 98         194 $l =~ s/ffl/ffl/g ;
31 98         205 $l =~ s/ff/ff/g ;
32              
33 98         200 return $l;
34             }
35              
36              
37             sub _typography_filter_en {
38 24     24   44 my $l = shift;
39             # then the quotes
40             # ascii style
41 24         55 $l =~ s/``/“/g ;
42 24         115 $l =~ s/(''|")\b/“/g ;
43 24         139 $l =~ s/(?<=\s)(''|")/“/gs;
44 24         63 $l =~ s/^(''|")/“/gm;
45 24         76 $l =~ s/(''|")/”/g ;
46              
47             # single
48 24         61 $l =~ s/'(?=[0-9])/’/g;
49 24         41 $l =~ s/`/‘/g;
50 24         63 $l =~ s/\b'/’/g;
51 24         53 $l =~ s/'\b/‘/g;
52 24         45 $l =~ s/^'/‘/gm;
53 24         43 $l =~ s/'/’/g;
54              
55             # the dashes
56             # this is the en-dash –
57 24         78 $l =~ s/(?
58              
59             # em-dash —
60 24         69 $l =~ s/(?<=\S) +-{1,3} +(?=\S)/ — /gs;
61              
62             # and the common case ^th
63 24         168 $l =~ s!\b(\d+)(th|rd|st|nd)\b!$1$2!g;
64 24         64 $l =~ s/(\. ){2,3}\./.../g;
65 24         55 return $l;
66             }
67              
68             sub _typography_filter_es {
69 13     13   24 my $l = shift;
70              
71             # em-dash —
72             # look behind and check it's not a \n
73             # not a spece, space, one-three hyphens, space, not a space => space — space
74 13         55 $l =~ s/(?<=\S) +-{1,3} +(?=\S)/ — /gs;
75             # - at beginning of the line (with no space), it's a dialog (em dash)
76 13         31 $l =~ s/^- */— /gm;
77              
78              
79             # I believe the following rules are dangerous. What if someone says:
80             # "the bit- and byte-wise" => "the bit — and byte-wise" !!!!
81             # I believe they should be removed.
82              
83             # # fix "example- "
84             # $l =~ s/ +-(?=\S)/ — /;
85             # # and " -example"
86             # $l =~ s/(?<=\S)- +/ — /;
87              
88             # better idea: check for matching on the same line
89 13         41 $l =~ s/ +-(\w.+?\w)- +/ — $1 — /gm;
90              
91             # if it touches a word on the right, and on the left there is not a
92             # word, it's an opening quote
93 13         69 $l =~ s/(?<=\W)"(?=\w)/«/gs;
94 13         48 $l =~ s/(?<=\W)'(?=\w)/‘/g;
95              
96             # if there is a space at the left, it's opening
97 13         39 $l =~ s/(?<=\s)"/«/gs;
98 13         31 $l =~ s/(?<=\s)'/‘/gs;
99              
100             # beginning of line, opening
101 13         26 $l =~ s/^"/«/gm;
102 13         23 $l =~ s/^'/‘/gm;
103              
104             # word at the left, closing
105 13         41 $l =~ s/(?<=\w)'/’/g;
106 13         46 $l =~ s/(?<=\w)"/»/g;
107              
108             # the others are right quotes, hopefully
109 13         33 $l =~ s/"/»/gs;
110 13         27 $l =~ s/'/’/g;
111              
112             # now the dots at the end of the quotations, but look behind not to
113             # have another dot
114             # $l =~ s/(?
115            
116 13         47 return $l;
117             }
118              
119              
120             sub _typography_filter_fi {
121 4     4   10 my $l = shift;
122 4         15 $l =~ s/"/\x{201d}/g;
123 4         14 $l =~ s/'/\x{2019}/g;
124 4         22 $l =~ s/(?<=\S) +--? +(?=\S)/ \x{2013} /gs;
125 4         10 return $l;
126             }
127              
128             sub _typography_filter_sr {
129 5     5   12 my $l = shift;
130 5         28 $l =~ s/(''|")\b/\x{201e}/g ;
131 5         30 $l =~ s/(?<=\s)(''|")/\x{201e}/gs;
132 5         19 $l =~ s/(''|")/\x{201c}/g ;
133 5         25 $l =~ s/(?<=\W)'(.*?)'(?=\W)/\x{201a}$1\x{2018}/gs;
134 5         16 $l =~ s/'/\x{2019}/g; # remaining apostrophes
135 5         20 $l =~ s/(?<=\S) +--? +(?=\S)/ \x{2013} /gs;
136 5         13 return $l;
137             }
138              
139             sub _typography_filter_hr {
140 5     5   12 my $l = shift;
141 5         25 $l =~ s/(''|")\b/\x{201e}/g ;
142 5         29 $l =~ s/(?<=\s)(''|")/\x{201e}/gs;
143 5         17 $l =~ s/(''|")/\x{201d}/g ; # ”
144 5         28 $l =~ s/(?<=\W)'(.*?)'(?=\W)/\x{201a}$1\x{2019}/gs; # ‚ ’
145 5         16 $l =~ s/'/\x{2019}/g; # remaining apostrophes
146 5         21 $l =~ s/(?<=\S) +--? +(?=\S)/ \x{2014} /gs; # —
147 5         15 return $l;
148             }
149              
150              
151             sub _typography_filter_ru {
152 44     44   73 my $l = shift;
153 44         409 $l =~ s/(?<=\s)(''|")/«/gs;
154 44         109 $l =~ s/^(''|")/«/gm;
155 44         156 $l =~ s/(''|")\b/«/gs;
156 44         123 $l =~ s/(''|")/»/g ;
157 44         114 $l =~ s/'(?=[0-9])/’/g;
158 44         89 $l =~ s/`/‘/g;
159 44         110 $l =~ s/\b'/’/g;
160 44         94 $l =~ s/'\b/‘/g;
161 44         86 $l =~ s/'/’/g;
162             # em-dash —
163 44         103 $l =~ s/(?<=\S) +-{1,3} +(?=\S)/ — /gs;
164 44         93 $l =~ s/(\. ){2,3}\./.../g;
165              
166              
167             # NON-BREAKING SPACE INSERTIONS
168              
169             # before em dash (—) and en dash (−)
170 44         207 $l =~ s/ (\x{2013}|\x{2014}|\x{2212})/\x{a0}$1/g;
171              
172             # space before, but only if there is a number, otherwise doesn't
173             # make sense.
174              
175 44         314 $l =~ s/(?<=\d)
176             [ ]+ # white space
177             (
178             # months
179             января | февраля | марта | апреля | мая | июня |
180             июля | августа | сентября | октября | ноября | декабря |
181              
182             # units
183             г|кг|мм|дм|см|м|км|л|В|А|ВТ|W|°C
184             )
185             \b # word boundary
186             /\x{a0}$1/gsx;
187            
188             # space after:
189 44         1001 $l =~ s/\b # start with a word boundary
190             (
191             # prepositions
192             в|к|о|с|у|
193             В|К|О|С|У|
194             на|от|об|из|за|по|до|во|та|ту|то|те|ко|со|
195             На|От|Об|Из|За|По|До|Во|Со|Ко|Та|Ту|То|Те|
196              
197             # conjunctions
198             А |А,|
199             а |а,|
200             И |И,|
201             и |и,|
202             но|но,|
203             Но|Но,|
204              
205             # obuiquitous "da"
206             да|да,|Да|Да,|
207              
208             # particles with space after
209             не|ни|
210             Не|Ни|
211              
212             # interjections, space after
213             ну|ну,|
214             Ну|Ну,|
215              
216             # abbreviations
217             с\.|ч\.|
218             см\.|См\.|
219             им\.|Им\.|
220             т\.|п\.
221             )
222             [ ]+ # white space
223             (?=\S) # and look ahead for something that is not a white
224             # space or end of line
225             /$1\x{a0}/gsx;
226              
227              
228             # and a space before
229 44         328 $l =~ s/(?<=\S) # look behind for something that is not \n
230             [ ]+ # one or more space
231             (
232             # particles
233             б|ж|ли|же|ль|бы|бы,|же,
234             )
235             (?=[\W]) # white space follows or something that is not a word
236             /\x{a0}$1/gsx;
237              
238              
239 44         127 return $l;
240             }
241              
242              
243             sub filters {
244             return {
245 94     94 1 543 en => \&_typography_filter_en,
246             fi => \&_typography_filter_fi,
247             hr => \&_typography_filter_hr,
248             sr => \&_typography_filter_sr,
249             ru => \&_typography_filter_ru,
250             es => \&_typography_filter_es,
251             };
252             }
253              
254             sub typography_filter {
255 92     92 1 22526 my $lang = $_[0];
256 92         260 my $text = " " . $_[1] . " ";
257 92         233 $text = _typography_filter_common($text);
258              
259 92         250 my $lang_filters = filters();
260 92 100 66     463 if ($lang and exists $lang_filters->{$lang}) {
261 91         209 $text = $lang_filters->{$lang}->($text);
262             }
263 92         251 my $llength = length($text) - 2;
264 92         595 return substr($text, 1, $llength);
265             }
266              
267             sub get_typography_filter {
268 2     2 1 14850 my ($lang, $links) = @_;
269 2         9 my @routines = (\&_typography_filter_common);
270 2         9 my $lang_filters = filters();
271 2 100 66     16 if ($lang && exists $lang_filters->{$lang}) {
272 1         3 push @routines, $lang_filters->{$lang};
273             }
274 2 50       7 if ($links) {
275 2         5 push @routines, \&linkify_filter;
276             }
277             return sub {
278 6     6   4451 my $text = shift;
279 6         17 $text = ' ' . $text . ' ';
280 6         32 foreach my $sub (@routines) {
281 16         39 $text = $sub->($text);
282             }
283 6         13 my $llength = length($text) - 2;
284 6         24 return substr($text, 1, $llength);
285 2         19 };
286             }
287              
288             1;
289              
290             __END__