File Coverage

blib/lib/CSS/Yamaantaka.pm
Criterion Covered Total %
statement 276 293 94.2
branch 81 106 76.4
condition 12 23 52.1
subroutine 36 36 100.0
pod 5 23 21.7
total 410 481 85.2


line stmt bran cond sub pod time code
1             #-*- perl -*-
2             #-*- coding: us-ascii -*-
3              
4             =head1 NAME
5              
6             CSS::Yamaantaka - Converts direction of Cascading Style Sheet (CSS)
7              
8             =head1 SYNOPSIS
9              
10             use CSS::Yamaantaka;
11            
12             $ya = CSS::Yamaantaka->new('lr_tb' => 'tb_rl');
13             $css_source_vertical_rl = $ya->transform($css_source);
14              
15             =head1 DESCRIPTION
16              
17             As YamE<257>ntaka has many legs, texts can run in various directions:
18             left to right and right to left horizontally; vertically with lines
19             extending right to left and left to right.
20              
21             CSS::Yamaantaka replaces things directed to "left" or "horizontal-tb" in a
22             Cascading Style Sheet (CSS) file such as float, padding, margin with
23             values directed to "right" or "vertical-rl", and so on.
24              
25             =cut
26              
27 65     65   19474 use 5.005; # qr{} and $10 are required.
  65         191  
  65         2209  
28              
29             package CSS::Yamaantaka;
30              
31 65     65   12161 use strict;
  65         239  
  65         1658  
32             #use warnings;
33 65     65   11116 use Carp qw(carp croak);
  65         137  
  65         3107  
34 65     65   29269 use CSS::Yamaantaka::Consts;
  65         183  
  65         21422  
35              
36             # To be compatible with Perl 5.5.
37 65     65   11390 use vars qw($VERSION $BASE_REVISION);
  65         138  
  65         153127  
38             $VERSION = '0.04_02';
39             $BASE_REVISION = 'http://cssjanus.googlecode.com/svn/trunk@31';
40              
41             =head2 Constructor
42              
43             =over 4
44              
45             =item new ( SRC =E DEST, [ options... ] )
46              
47             =item new ( C<'adaptor'> =E ADAPTOR, [ options... ] )
48              
49             Creates new CSS::Yamaantaka object.
50              
51             In first form, SRC and DEST are the original and resulting directions.
52             Available directions are
53             C<'lr_tb'>, C<'rl_tb'>, C<'tb_lr'> and C<'tb_rl'>.
54             Their synonyms are C<'ltr'>, C<'rtl'>, C<'vertical-lr'> and C<'vertical-rl'>,
55             respectively.
56              
57             Following options are available.
58              
59             =over 4
60              
61             =item flip_bg =E 0|1
62              
63             Fixes background positions properties.
64             Default is C<1>, will fix.
65              
66             =item flip_cursor =E 0|1
67              
68             Fixes positions "n"/"e"/"s"/"w" and so on within cursor properties.
69             Default is C<1>, will fix.
70              
71             =item flip_url =E 0|1
72              
73             Fixes "top"/"right"/"bottom"/"left" string within URLs.
74             Default is C<0>, won't fix.
75              
76             =item ignore_bad_bgp =E 0|1
77              
78             Ignores unmirrorable background-position values.
79             Default is C<1>, WILL ignore and won't croak it.
80              
81             =item swap_ltr_rtl_in_url =E 0|1
82              
83             Fixes "ltr"/"rtl" string within URLs, if needed.
84             Default is C<0>, won't fix.
85              
86             =back
87              
88             In second form, ADAPTOR is a name of package or an object.
89             package will be automatically loaded.
90             See L about standard adaptors.
91              
92             =back
93              
94             =cut
95              
96             my %defaults = (
97             'flip_bg' => 1,
98             'flip_cursor' => 1,
99             'flip_url' => 0,
100             'ignore_bad_bgp' => 1,
101             'swap_ltr_rtl_in_url' => 0,
102             );
103              
104             my %dir_synonym = (
105             'ltr' => 'lr_tb',
106             'rtl' => 'rl_tb',
107             'vertical-lr' => 'tb_lr',
108             'vertical-rl' => 'tb_rl',
109             );
110              
111             my %adaptor = (
112             "lr_tb$;rl_tb" => 'CSS::Yamaantaka::MirrorH',
113             "rl_tb$;lr_tb" => 'CSS::Yamaantaka::MirrorH',
114             "lr_tb$;tb_lr" => 'CSS::Yamaantaka::MirrorTL_BR',
115             "tb_lr$;lr_tb" => 'CSS::Yamaantaka::MirrorTL_BR',
116             "lr_tb$;tb_rl" => 'CSS::Yamaantaka::RotateR',
117             "tb_rl$;lr_tb" => 'CSS::Yamaantaka::RotateL',
118             "rl_tb$;tb_lr" => 'CSS::Yamaantaka::RotateL',
119             "tb_lr$;rl_tb" => 'CSS::Yamaantaka::RotateR',
120             "rl_tb$;tb_rl" => 'CSS::Yamaantaka::MirrorTR_BL',
121             "tb_rl$;rl_tb" => 'CSS::Yamaantaka::MirrorTR_BL',
122             "tb_lr$;tb_rl" => 'CSS::Yamaantaka::MirrorV',
123             "tb_rl$;tb_lr" => 'CSS::Yamaantaka::MirrorV',
124             );
125              
126             my %body_direction = (
127             'lr_tb' => 'ltr',
128             'rl_tb' => 'rtl',
129             'tb_lr' => 'ltr',
130             'tb_rl' => 'rtl',
131             );
132              
133             my %writing_mode = (
134             'lr_tb' => 'horizontal-tb',
135             'rl_tb' => 'horizontal-tb',
136             'tb_lr' => 'vertical-lr',
137             'tb_rl' => 'vertical-rl',
138             );
139              
140             my %text_orientation = (
141             "rl_tb$;tb_lr" => 'sideways-left',
142             "rl_tb$;tb_rl" => 'sideways-left',
143             );
144              
145             sub new {
146 602     602 1 273952 my $pkg = shift;
147 602         1796 my $self = {@_};
148              
149 602         1983 my ($src) = grep {/^((lr|rl)_tb|tb_(lr|rl)|ltr|rtl|vertical-(lr|rl))$/}
  855         4514  
150             keys %$self;
151 602         813 my $dest;
152 602 100       2425 if ($src) {
153 497         892 $dest = $self->{$src};
154 497 50       1170 if ($dest) {
155 497   33     2315 $src = $dir_synonym{$src} || $src;
156 497   33     1724 $dest = $dir_synonym{$dest} || $dest;
157 497         1241 $self->{'body_direction'} = $body_direction{$dest};
158 497         1278 $self->{'writing_mode'} = $writing_mode{$dest};
159 497         10374 $self->{'text_orientation'} = $text_orientation{$src, $dest};
160 497         1332 $self->{'adaptor'} = $adaptor{$src, $dest};
161             }
162             }
163 602 50 66     4785 unless ($src and $dest and $src eq $dest) {
      66        
164 590 50       1419 croak 'available adaptor not found'
165             unless $self->{'adaptor'};
166             }
167 590 50 33     2863 if ($self->{'adaptor'} and !ref $self->{'adaptor'}) {
168 60     60   24525 eval "use $self->{'adaptor'}";
  60     7   177  
  60     7   2093  
  590     7   44806  
        7      
        7      
        6      
169 590 50       2009 croak $@ if $@;
170             }
171              
172             # apply default
173 590         2422 foreach my $o (keys %defaults) {
174 2898 100       9771 $self->{$o} = $defaults{$o} unless defined $self->{$o};
175             }
176              
177 590         3209 bless $self => $pkg;
178             }
179              
180             # Substituttion of CSS gradients which cannot be performed only by regexp
181             # because they can contain nested parentheses.
182              
183             my $GRADIENT_RE = qr<$IDENT[\.-]gradient\s*\(>i;
184              
185             sub substituteGradient {
186 631     643 0 1050 my $self = shift;
187 631         923 my $match_function = shift;
188 631         857 my $input_string = shift;
189              
190 631         1681 pos($input_string) = 0;
191 631         1299 my $output = '';
192 631         774 my ($other, $match, $paren_count);
193              
194 631         21089 while ($input_string =~ m{\G(.*?)($GRADIENT_RE)}cg) {
195 15         240 ($other, $match) = ($1, $2);
196              
197 15         83 $paren_count = 1;
198 15   66     46 while ($paren_count and $input_string =~ m{\G(\(|\)|[^()]+)}cg) {
199 24 100       219 if ($1 eq '(') {
    100          
200 15         91 $paren_count++;
201             } elsif ($1 eq ')') {
202 17         33 $paren_count--;
203             }
204 24         235 $match .= $1;
205             }
206              
207             # pos() is at last closing parenthesis (or end of text).
208 13         77 $output .= $other . &$match_function($match);
209             }
210 629         3776 return $output . substr($input_string, pos($input_string));
211             }
212              
213             # fixBodyDirectionLtrAndRtl ($line)
214             #
215             # Replaces ltr with rtl and vice versa ONLY in the body direction:
216             # 'body { direction:ltr }' => 'body { direction:rtl }'
217              
218             sub fixBodyDirectionLtrAndRtl {
219 629     643 0 987 my $self = shift;
220 629         986 my $line = shift;
221 629         977 my $adaptor = $self->{'adaptor'};
222              
223 629 100       2791 return $line
224             unless $adaptor->willReverseGlobalDirection;
225              
226 445         1630 $line =~ s{$BODY_DIRECTION_LTR_RE}{$1$2$3~TMP~}g;
227 445         1277 $line =~ s{$BODY_DIRECTION_RTL_RE}{$1$2$3ltr}g;
228 445         907 $line =~ s{~TMP~}{rtl}g;
229              
230 445         985 return $line;
231             }
232              
233             # fixSingleBorderRadius ($line)
234              
235             sub fixSingleBorderRadiusName {
236 35     49 0 57 my $adaptor = shift;
237 35         256 my @m = @_;
238              
239 35 100       135 if (defined $m[0]) {
240 23 100       95 unless ($adaptor->willSwapHorizontalVertical) {
241 15         263 return 'border-' . $adaptor->fixBoxDirectionPart($m[0]) . '-' .
242             $adaptor->fixBoxDirectionPart($m[1]) . '-radius';
243             } else {
244 16         97 return 'border-' . $adaptor->fixBoxDirectionPart($m[1]) . '-' .
245             $adaptor->fixBoxDirectionPart($m[0]) . '-radius';
246             }
247             } else {
248 20 100       74 unless ($adaptor->willSwapHorizontalVertical) {
249 12         120 return 'border-radius-' . $adaptor->fixBoxDirectionPart($m[2]) .
250             $adaptor->fixBoxDirectionPart($m[3]);
251             } else {
252 15         83 return 'border-radius-' . $adaptor->fixBoxDirectionPart($m[3]) .
253             $adaptor->fixBoxDirectionPart($m[2]);
254             }
255             }
256             }
257              
258             sub fixSingleBorderRadius {
259 625     643 0 863 my $self = shift;
260 625         756 my $line = shift;
261 625         951 my $adaptor = $self->{'adaptor'};
262              
263 625         10899 $line =~ s{$SINGLE_BORDER_RADIUS_RE}{
264 31 100       148 if (defined $7) {
265 19 100       82 unless ($adaptor->willSwapHorizontalVertical) {
266 11         26 $1 . fixSingleBorderRadiusName($adaptor, $2, $3, $4, $5) .
267             "$6$7 $8";
268             } else {
269 15         103 $1 . fixSingleBorderRadiusName($adaptor, $2, $3, $4, $5) .
270             "$6$8 $7";
271             }
272             } else {
273 19         74 $1 . fixSingleBorderRadiusName($adaptor, $2, $3, $4, $5) .
274             "$6$8";
275             }
276             }eg;
277              
278 625         1327 return $line;
279             }
280              
281             # fixBoxDirection ($line)
282             #
283             # Replaces left with right and vice versa in line, e,g,:
284             # 'padding-left: 2px; margin-right: 1px;' =>
285             # 'padding-right: 2px; margin-left: 1px;'
286             #
287             # Note: Old name is fixLeftAndRight().
288              
289             sub fixBoxDirection {
290 625     631 0 874 my $self = shift;
291 625         754 my $line = shift;
292 625         1059 my $adaptor = $self->{'adaptor'};
293              
294 625         9341 $line =~ s{$BOX_DIRECTIONS_RE}{
295 214 100       923 if (defined $4) {
    100          
296 166         809 $adaptor->fixBoxDirectionPart($4);
297             } elsif ($adaptor->willSwapHorizontalVertical) {
298 39         225 $adaptor->fixBoxDirectionPart($3) . $2 .
299             $adaptor->fixBoxDirectionPart($1);
300             } else {
301 23         130 $adaptor->fixBoxDirectionPart($1) . $2 .
302             $adaptor->fixBoxDirectionPart($3);
303             }
304             }eg;
305              
306 625         1690 return $line;
307             }
308              
309             # fixBoxDirectionInUrl ($line)
310             #
311             # Replaces left with right and vice versa within background URLs, e.g.:
312             # 'background:url(right.png)' => 'background:url(left.png)'
313             #
314             # Note: Old name is fixLeftAndRightInUrl().
315              
316             sub fixBoxDirectionInUrl {
317 73     79 0 165 my $self = shift;
318 73         121 my $line = shift;
319 73         102 my $adaptor = $self->{'adaptor'};
320              
321 73         727 $line =~ s{$BOX_DIRECTION_IN_URL_RE}{
322 37         158 $adaptor->fixBoxDirectionPart($1);
323             }eg;
324              
325 73         194 return $line;
326             }
327              
328             # fixLtrAndRtlInUrl ($line)
329             #
330             # Replaces ltr with rtl and vice versa within background URLs, e.g.:
331             # 'background:url(rtl.png)' => 'background:url(ltr.png)'
332              
333             sub fixLtrAndRtlInUrl {
334 73     79 0 155 my $self = shift;
335 73         154 my $line = shift;
336 73         107 my $adaptor = $self->{'adaptor'};
337              
338 73 100       258 return $line
339             unless $adaptor->willReverseGlobalDirection;
340              
341 51         292 $line =~ s{$LTR_IN_URL_RE}{~TMP~}g;
342 51         223 $line =~ s{$RTL_IN_URL_RE}{ltr}g;
343 51         184 $line =~ s{~TMP~}{rtl}g;
344              
345 50         126 return $line;
346             }
347              
348             sub fixCursorDirection {
349 54     61 0 61 my $adaptor = shift;
350 54         217 my $direction = shift;
351              
352 48         166 $direction = $adaptor->fixCursorPositions($direction);
353 48         156 $direction =~ s/^([ew])([ns])/$2$1/;
354 48         95 $direction =~ s/([ew])([ns])$/$2$1/;
355 48         67 $direction =~ s/^(s[ew])(n[ew])$/$2$1/;
356              
357 48         161 $direction;
358             }
359              
360             # fixCursorProperties ($line)
361             #
362             # Changes directional CSS cursor properties:
363             # 'cursor: ne-resize' => 'cursor: nw-resize'
364              
365             sub fixCursorProperties {
366 618     631 0 813 my $self = shift;
367 618         715 my $line = shift;
368 618         921 my $adaptor = $self->{'adaptor'};
369              
370 618         2392 $line =~ s{$CURSOR_DIRECTION_RE}{
371 48         107 fixCursorDirection($adaptor, $1) . '-resize';
372             }eg;
373              
374 618         1246 return $line;
375             }
376              
377             # fixBorderRadius ($line)
378             #
379             # Changes border-radius and its browser-specific variants, e.g.:
380             # 'border-radius: 1px 2px 3px 4px / 5px 6px 7px' =>
381             # 'border-radius: 2px 1px 4px 3px / 6px 5px 6px 7px'
382              
383             sub fixBorderRadius {
384 618     631 0 728 my $self = shift;
385 618         743 my $line = shift;
386 618         954 my $adaptor = $self->{'adaptor'};
387              
388 618         17175 $line =~ s{$BORDER_RADIUS_RE}{
389 28         104 $self->reorderBorderRadius($&, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
390             }eg;
391              
392 618         1282 return $line;
393             }
394              
395             # fixFourPartNotation ($line)
396             #
397             # Fixes the second and fourth positions in four-part CSS notation, e.g.:
398             # 'padding: 1px 2px 3px 4px' => 'padding: 1px 4px 3px 2px'
399              
400             sub fixFourPartNotation {
401 618     631 0 726 my $self = shift;
402 618         728 my $line = shift;
403 618         882 my $adaptor = $self->{'adaptor'};
404              
405 618         25147 $line =~ s{$FOUR_NOTATION_QUANTITY_RE}{
406 36         526 join(' ', $adaptor->reorderFourPartNotation($1, $2, $3, $4))
407             }eg;
408 618         11103 $line =~ s{$FOUR_NOTATION_COLOR_RE}{
409 0         0 $1 . join(' ', $adaptor->reorderFourPartNotation($2, $3, $4, $5))
410             }eg;
411              
412 618         1376 return $line;
413             }
414              
415             # fixBackgroundPosition ($line)
416             #
417             # METHOD. Changes horizontal background values in line.
418             #
419             # If value is not replaceable, croak it (by default) or carp it (if
420             # 'ignore_bad_bgp' option is set).
421              
422             sub fixBackgroundPosition {
423 618     629 0 750 my $self = shift;
424 618         688 my $line = shift;
425              
426 618   50     1485 my $adaptor = $self->{'adaptor'} || return $line;
427             # return $line
428             # unless $adaptor->willReverseGlobalDirection;
429              
430 618         4672 $line =~ s{$BG_QUANTITY_RE}{
431 21         70 $self->calculateNewBackgroundQuantityPosition(
432             $&, $1, $2, $3, $4, $5, $6
433             )
434             }eg;
435 617         1916 $line =~ s{$BG_HORIZONTAL_PERCENTAGE_X_RE}{
436 1         10 $self->calculateNewBackgroundPositionX($&, $1, $2)
437             }eg;
438             # $line =~ s{$BG_HORIZONTAL_LENGTH_RE}{
439             # $self->calculateNewBackgroundLengthPosition($&, $1, $2, $3, $4, $5)
440             # }eg;
441 617         1449 $line =~ s{$BG_HORIZONTAL_LENGTH_X_RE}{
442 2         14 $self->calculateNewBackgroundLengthPositionX($&, $1, $2)
443             }eg;
444              
445 617         1257 return $line;
446             }
447              
448             # Takes a list of zero to four border radius parts and returns a string of
449             # them reordered for bidi mirroring.
450              
451             sub reorderBorderRadiusPart {
452 62     73 0 91 my $adaptor = shift;
453 62         140 my @part = @_;
454              
455             # Remove any piece which may be 'None'
456 62 100       96 @part = grep { defined $_ and length $_ } @part;
  242         906  
457              
458 62 50       137 return join ' ', @part
459             unless $adaptor;
460              
461 62 100       133 if (scalar @part == 0) {
462 4         8 return '';
463             }
464 58 100       116 if (scalar @part == 1) {
465 8         67 $part[1] = $part[0];
466             }
467 58 100       114 if (scalar @part == 2) {
468 16         29 $part[2] = $part[0];
469             }
470 58 100       113 if (scalar @part == 3) {
471 23         41 $part[3] = $part[1];
472             }
473              
474 58         239 @part = $adaptor->reorderBorderRadiusSubparts(@part);
475              
476 58 100       187 if ($part[3] eq $part[1]) {
477 18         26 pop @part;
478 18 100       48 if ($part[2] eq $part[0]) {
479 16         21 pop @part;
480 16 100       44 if ($part[1] eq $part[0]) {
481 8         13 pop @part;
482             }
483             }
484             }
485 58         216 return join ' ', @part;
486             }
487              
488             # Receives a match object for a border-radius element and reorders it pieces.
489             sub reorderBorderRadius {
490 29     40 0 40 my $self = shift;
491 29         217 my @m = @_;
492 29         62 my $adaptor = $self->{'adaptor'};
493              
494 29         87 my $first_group = reorderBorderRadiusPart($adaptor, @m[3 .. 6]);
495 29         89 my $second_group = reorderBorderRadiusPart($adaptor, @m[7 .. $#m]);
496 29 100       176 if ($second_group eq '') {
    100          
497 4         29 return sprintf '%sborder-radius%s%s', $_[1], $_[2], $first_group;
498             } elsif ($adaptor->willSwapHorizontalVertical) {
499 16         251 return sprintf '%sborder-radius%s%s / %s', $_[1], $_[2],
500             $second_group, $first_group;
501             } else {
502 9         80 return sprintf '%sborder-radius%s%s / %s', $_[1], $_[2],
503             $first_group, $second_group;
504             }
505             }
506              
507             ## calculateNewBackgroundPosition ($&, $1, $2, $3, $4, $5)
508             ##
509             ## Changes horizontal background-position percentages, e.g.:
510             ## 'background-position: 75% 50%' => 'background-position: 25% 50%'
511             #
512             #sub calculateNewBackgroundPosition {
513             # my $self = shift;
514             # my @m = @_;
515             # my $new_x;
516             # my $position_string;
517             #
518             # # The flipped value is the offset from 100%
519             # $new_x = 100 - int($m[4]);
520             #
521             # # Since m.group(1) may very well be None type and we need a string..
522             # if ($m[1]) {
523             # $position_string = $m[1];
524             # } else {
525             # $position_string = '';
526             # }
527             #
528             # return sprintf 'background%s%s%s%s%%%s',
529             # $position_string, $m[2], $m[3], $new_x, $m[5];
530             #}
531              
532             # calculateNewBackgroundPositionX ($&, $1, $2)
533             #
534             # Fixes percent based background-position-x, e.g.:
535             # 'background-position-x: 75%' => 'background-position-x: 25%'
536              
537             sub calculateNewBackgroundPositionX {
538 1     12 0 2 my $self = shift;
539 1         3 my @m = @_;
540 1         2 my $new_x;
541              
542             # The flipped value is the offset from 100%
543 1         3 $new_x = 100 - int($m[2]);
544              
545 1         6 return sprintf 'background-position-x%s%s%%', $m[1], $new_x;
546             }
547              
548             my $BACKGROUND_POSITION_ERROR_MESSAGE =
549             "Unmirrorable position value \"%s\": %s\n";
550              
551             sub warnForBackgroundPosition {
552 1     12 0 2 my $self = shift;
553 1         3 my $bad_length = shift;
554 1         1 my $whole_value = shift;
555              
556 1         6 my $msg = sprintf $BACKGROUND_POSITION_ERROR_MESSAGE, $bad_length,
557             $whole_value;
558 1 50       5 if ($self->{'ignore_bad_bgp'}) {
559 0         0 $@ = $msg;
560 0         0 carp $msg;
561             } else {
562 1         229 croak $msg;
563             }
564             }
565              
566             ## calculateNewBackgroundLengthPosition ($&, $1, $2, $3, $4, $5)
567             ##
568             ## Changes horizontal background-position lengths, e.g.:
569             ## 'background-position: 0px 10px' => 'background-position: 100% 10px'
570             ##
571             ## If value is not replaceable, croak it (by default) or carp it (if
572             ## 'ignore_bad_bgp' option is set).
573             #
574             #sub calculateNewBackgroundLengthPosition {
575             # my $self = shift;
576             # my @m = @_;
577             # my $position_string;
578             #
579             # # croak if the length is not zero-valued
580             # unless ($m[4] =~ m{^$ZERO_LENGTH}) {
581             # $self->warnForBackgroundPosition($m[4], $m[0]);
582             # return $m[0];
583             # }
584             #
585             # if (defined $m[1] and length $m[1]) {
586             # $position_string = $m[1];
587             # } else {
588             # $position_string = '';
589             # }
590             #
591             # return sprintf 'background%s%s%s100%%%s',
592             # $position_string, $m[2], $m[3], $m[5];
593             #}
594              
595             # calculateNewBackgroundLengthPositionX ($&, $1, $2)
596             #
597             # Fixes background-position-x lengths, e.g.:
598             # 'background-position-x: 0' => 'background-position-x: 100%'
599             #
600             # If value is not replaceable, croak it (by default) or carp it (if
601             # 'ignore_bad_bgp' option is set).
602              
603             sub calculateNewBackgroundLengthPositionX {
604 2     10 0 3 my $self = shift;
605 2         10 my @m = @_;
606              
607             # croak if the length is not zero-valued
608 2 50       60 unless ($m[2] =~ m{^$ZERO_LENGTH}) {
609 0         0 $self->warnForBackgroundPosition($m[2], $m[0]);
610 0         0 return $m[0];
611             }
612              
613 2         17 return sprintf 'background-position-x%s100%%', $m[1];
614             }
615              
616             # calculateNewBackgroundQuantityPosition ($&, $1, $2, $3, $4, $5, $6)
617             #
618             # Changes background-position percentages, e.g.:
619             # 'background-position: 75% 50%' => 'background-position: 25% 50%'
620              
621             sub calculateNewBackgroundQuantityPosition {
622 21     28 0 25 my $self = shift;
623 21         150 my @m = @_;
624 21         33 my $adaptor = $self->{'adaptor'};
625 21         25 my $position_string;
626              
627 21         53 my @pos = ($m[6], undef, undef, $m[4]);
628             # The flipped value is the offset from 100%
629 21 100       709 if ($pos[3] =~ m{^($NUM)\%$}) {
    100          
    50          
630 9         38 $pos[1] = (100 - int($1)) . '%';
631             } elsif ($pos[3] =~ m{^$ZERO_LENGTH}) {
632 11         19 $pos[1] = '100%';
633             } elsif ($pos[3] =~ m{auto|inherit}) {
634 0         0 $pos[1] = $pos[3];
635             }
636 21 100       305 if ($pos[0] =~ m{^($NUM)\%$}) {
    100          
    50          
637 15         50 $pos[2] = (100 - int($1)) . '%';
638             } elsif ($pos[0] =~ m{^$ZERO_LENGTH}) {
639 4         7 $pos[2] = '100%';
640             } elsif ($pos[0] =~ m{auto|inherit}) {
641 2         5 $pos[2] = $pos[0];
642             }
643              
644 21         95 @pos = $adaptor->reorderFourPartNotation(@pos);
645              
646 21 100 66     124 unless (defined $pos[0] and defined $pos[3]) {
647 1         12 $self->warnForBackgroundPosition("$m[4]$m[5]$m[6]", $m[0]);
648 0         0 return $m[0];
649             }
650              
651 20         186 return sprintf 'background%s%s%s%s%s%s',
652             $m[1], $m[2], $m[3], $pos[3], $m[5], $pos[0];
653             }
654              
655             =head2 Methods
656              
657             =over 4
658              
659             =item body_direction
660              
661             Get direction property or dir attribute of body element thought to be
662             appropriate.
663             Returns C<'ltr'>, C<'rtl'> or undef (unknown).
664              
665             =back
666              
667             =cut
668              
669             sub body_direction {
670 0 0   7 1 0 shift->{'body_direction'} || undef;
671             }
672              
673             =over 4
674              
675             =item text_orientation
676              
677             Get text-orientation property of texts assumed.
678             Returns C<'sideways-left'> or undef (upright or sideways-right is assumed).
679              
680             =back
681              
682             =cut
683              
684             sub text_orientation {
685 0 0   7 1 0 shift->{'text_orientation'} || undef;
686             }
687              
688             =over 4
689              
690             =item transform ( $lines, [ options... ] )
691              
692             Runs the fixing functions against CSS source.
693              
694             $lines is a string.
695             Following options are available.
696              
697             =over 4
698              
699             =item flip_bg =E 0|1
700              
701             =item flip_cursor =E 0|1
702              
703             =item flip_url =E 0|1
704              
705             =item swap_ltr_rtl_in_url =E 0|1
706              
707             Overrides these flags if params are set.
708              
709             =back
710              
711             Returns same lines directions are changed.
712              
713             =back
714              
715             =cut
716              
717             sub transform {
718 618     625 1 7058 my $self = shift;
719 618         1009 my $line = shift;
720 618         1029 my %opts = @_;
721              
722 618 50       1479 return undef unless defined $line;
723 618 50       3172 return $line unless $self->{'adaptor'};
724              
725             # Possibly override flags with params.
726 618         909 my $swap_ltr_rtl_in_url = $opts{'swap_ltr_rtl_in_url'};
727 618         982 my $flip_url = $opts{'flip_url'};
728 618         908 my $flip_cursor = $opts{'flip_cursor'};
729 618         849 my $flip_bg = $opts{'flip_bg'};
730              
731             # compat.
732 618 50       1259 if (defined $opts{'swap_left_right_in_url'}) {
733 0         0 $flip_url = $opts{'swap_left_right_in_url'};
734             }
735              
736 618 50       1414 unless (defined $swap_ltr_rtl_in_url) {
737 618         943 $swap_ltr_rtl_in_url = $self->{'swap_ltr_rtl_in_url'};
738             }
739 618 50       1553 unless (defined $flip_url) {
740 618         938 $flip_url = $self->{'flip_url'};
741             }
742 618 50       1126 unless (defined $flip_cursor) {
743 618         793 $flip_cursor = $self->{'flip_cursor'};
744             }
745 618 50       1342 unless (defined $flip_bg) {
746 618         1033 $flip_bg = $self->{'flip_bg'};
747             }
748              
749 618         943 my @originals = ();
750              
751             # Tokenize tokens tokenizer can be confused.
752 618         1174 $line =~ s{(~[A-Z_\d]+~)}{
753 0         0 push @originals, $1;
754 0         0 '~X_' . (scalar @originals) . '~'
755             }eg;
756              
757             # Tokenize any single line rules with the /* noflip */ annotation.
758 618         2302 $line =~ s{$NOFLIP_SINGLE_RE}{
759 4         11 push @originals, $1;
760 4         16 '~NOFLIP_SINGLE_' . (scalar @originals) . '~'
761             }eg;
762              
763             # Tokenize any class rules with the /* noflip */ annotation.
764 618         1554 $line =~ s{$NOFLIP_CLASS_RE}{
765 5         12 push @originals, $1;
766 5         24 '~NOFLIP_CLASS_' . (scalar @originals) . '~'
767             }eg;
768              
769             # Tokenize the comments so we can preserve them through the changes.
770 618         1697 $line =~ s{$COMMENT_RE}{
771 8         18 push @originals, $1;
772 8         32 '~C_' . (scalar @originals) . '~'
773             }eg;
774              
775             # Tokenize gradients since we don't want to mirror the values inside
776             $line = $self->substituteGradient(
777             sub {
778 2     9   3 push @originals, shift;
779 2         17 '~GRADIENT_' . (scalar @originals) . '~';
780             },
781 618         3747 $line
782             );
783              
784             # Tokenize line-relative properties if any, because
785             # direction of line-relative properties should not be modified
786             # except true ltr-rtl swapping.
787 618 100       4727 unless ($self->{'adaptor'}->willReverseLineRelativeDirection) {
788 472         18509 $line =~ s{$LINE_RELATIVE_DIRECTION_RE}{
789 72         182 push @originals, $1;
790 72         271 '~LINE_RELATIVE_' . (scalar @originals) . '~'
791             }eg;
792             }
793              
794             # Tokenize properties including "right"/"left" not to be changed.
795 618         3100 $line =~ s{$PROHIBITED_DIRECTION_RE}{
796 0         0 push @originals, $1;
797 0         0 '~PROHIBITED_DIRECTION_' . (scalar @originals) . '~'
798             }eg;
799              
800             # Here start the various direction fixes.
801              
802 618         1746 $line = $self->fixBodyDirectionLtrAndRtl($line);
803              
804 618 100       1890 if ($flip_url) {
805 66         151 $line = $self->fixBoxDirectionInUrl($line);
806             }
807              
808 618 100       1103 if ($swap_ltr_rtl_in_url) {
809 66         141 $line = $self->fixLtrAndRtlInUrl($line);
810             }
811              
812 618         1731 $line = $self->fixSingleBorderRadius($line);
813              
814             # Since BoxDirection conflicts with SingleBorderRadius, we tokenize
815             # border--radius properties here.
816 618         6784 $line =~ s{$SINGLE_BORDER_RADIUS_TOKENIZER_RE}{
817 24         49 push @originals, $1;
818 24         77 '~SINGLE_BORDER_RADIUS_' . (scalar @originals) . '~'
819             }eg;
820 618         1895 $line = $self->fixBoxDirection($line);
821 618         1157 $line =~ s{~SINGLE_BORDER_RADIUS_(\d+)~}{$originals[$1 - 1]}eg;
  24         89  
822              
823 618 50       1323 if ($flip_cursor) {
824 618         1595 $line = $self->fixCursorProperties($line);
825             }
826              
827 618         1862 $line = $self->fixBorderRadius($line);
828              
829             # Since FourPartNotation conflicts with BorderRadius, we tokenize
830             # border-radius properties here.
831 618         17046 $line =~ s{$BORDER_RADIUS_TOKENIZER_RE}{
832 28         64 push @originals, $1;
833 28         96 '~BORDER_RADIUS_' . (scalar @originals) . '~'
834             }eg;
835 618         1615 $line = $self->fixFourPartNotation($line);
836 618         1104 $line =~ s{~BORDER_RADIUS_(\d+)~}{$originals[$1 - 1]}eg;
  28         134  
837              
838 618 50       1250 if ($flip_bg) {
839 618         1477 $line = $self->fixBackgroundPosition($line);
840             }
841              
842             # DeTokenize properties including "right"/"left" not to be fixed
843 617         1933 $line =~ s{~PROHIBITED_DIRECTION_(\d+)~}{$originals[$1 - 1]}eg;
  0         0  
844              
845             # DeTokenize line-relative properties, if any
846 617         1036 $line =~ s{~LINE_RELATIVE_(\d+)~}{$originals[$1 - 1]}eg;
  72         310  
847              
848             # DeTokenize gradients
849 617         903 $line =~ s{~GRADIENT_(\d+)~}{$originals[$1 - 1]}eg;
  2         10  
850              
851             # DeTokenize the single line noflips.
852 617         807 $line =~ s{~NOFLIP_SINGLE_(\d+)~}{$originals[$1 - 1]}eg;
  4         20  
853              
854             # DeTokenize the class-level noflips.
855 617         1232 $line =~ s{~NOFLIP_CLASS_(\d+)~}{$originals[$1 - 1]}eg;
  5         27  
856              
857             # DeTokenize the comments.
858 617         794 $line =~ s{~C_(\d+)~}{$originals[$1 - 1]}eg;
  8         31  
859              
860             # Detokenize tokens tokenizer can be confused.
861 617         765 $line =~ s{~X_(\d+)~}{$originals[$1 - 1]}eg;
  0         0  
862              
863 617         4590 return $line;
864             }
865              
866             =over 4
867              
868             =item writing_mode
869              
870             Get writing-mode property of texts thought to be appropriate.
871             Returns C<'horizontal-tb'>, C<'vertical-lr'>, C<'vertical-rl'> or undef
872             (unknown).
873              
874             =back
875              
876             =cut
877              
878             sub writing_mode {
879 0 0   7 1   shift->{'writing_mode'} || undef;
880             }
881              
882             =head2 Adaptors
883              
884             This module supports four directions of documents:
885              
886             =over 4
887              
888             =item lr-tb
889              
890             The direction specified by
891             C<{ direction: ltr; writing-mode: horizontal-tb; }>.
892             For example, most Western writing systems employ it.
893              
894             =item rl-tb
895              
896             The direction specified by
897             C<{ direction: rtl; writing-mode: horizontal-tb; }>.
898             For example, some Middle Eastern writing systems employ it.
899              
900             =item tb-lr
901              
902             The direction specified by
903             C<{ writing-mode: vertical-lr; }>.
904             For example, several North Asian writing systems employ it.
905              
906             =item lr-tb
907              
908             The direction specified by
909             C<{ writing-mode: vertical-rl; }>.
910             East Asian writing systems with vertical layout employ it.
911              
912             =back
913              
914             This module chooses adaptors by source & resulting directions:
915              
916             table 1. Choosing adaptors
917             +-----------+-------------+-------------+-------------+--------------+
918             | from \ to | lr-tb : rl-tb : tb-lr : tb-rl |
919             +-----------+-------------+-------------+-------------+--------------+
920             | lr-tb | - : MirrorH : MirrorTL_BR : RotateR |
921             | rl-tb | MirrorH : - : RotateL* : MirrorTR_BL* |
922             | tb-lr | MirrorTL_BR : RotateR : - : MirrorV |
923             | tb-rl | RotateL : MirrorTR_BL : MirrorV : - |
924             +-----------+-------------+-------------+-------------+--------------+
925             * Assumed text-orientation: sideways-left.
926            
927             n.b.: Prefixing "CSS::Yamaantaka::" are omitted.
928              
929             Each adaptor will or won't change following "directions" of CSS properties.
930              
931             =over 4
932              
933             =item line-relative box directions
934              
935             "right" / "left" of text-align, float and clear.
936             "top" / "bottom" of vertical-align.
937              
938             =item physical box directions
939              
940             "top" / "right" / "bottom" / "left".
941              
942             =item global directions
943              
944             Directions specified by body element, "ltr" / "rtl".
945              
946             =item direction swapping
947              
948             Horizontal and vertical orientation.
949              
950             =back
951              
952             table 2. Feature of adaptors
953             +-------------+-----------+-------------------------+---------+------+
954             | | line-rel. | box directions : global : h/v |
955             +-------------+-----------+-------------------------+---------+------+
956             | MirrorH | reverse h.: reverse horizontally : reverse : - |
957             | MirrorV | - : reverse horizontally : reverse : - |
958             | RotateR | - : rotate clockwise : reverse : swap |
959             | RotateL | - : rotate counter-clockwise: reverse : swap |
960             | MirrorTL_BR | - : reverse with tl-br axis : - : swap |
961             | MirrorTR_BL | - : reverse with tr-bl axis : - : swap |
962             +-------------+-----------+-------------------------+---------+------+
963              
964             Any adaptors listed above won't fix line-relative text directions
965             ("rtl" / "ltr").
966              
967             =head1 VERSION
968              
969             Consult C<$VERSION> variable.
970              
971             =head1 SEE ALSO
972              
973             L
974              
975             Extended CSSJanus supporting vertical-rl writing-mode:
976             L
977              
978             L
979              
980             =head1 AUTHOR
981              
982             Hatuka*nezumi - IKEDA Soji .
983              
984             =head1 COPYRIGHT
985              
986             Copyright (C) 2013 Hatuka*nezumi - IKEDA Soji.
987              
988             This program is free software; you can redistribute
989             it and/or modify it under the same terms as Perl itself.
990              
991             =cut
992              
993             1;