File Coverage

blib/lib/Text/UnicodeBox/Utility.pm
Criterion Covered Total %
statement 57 70 81.4
branch 19 32 59.3
condition 10 21 47.6
subroutine 6 7 85.7
pod 3 3 100.0
total 95 133 71.4


line stmt bran cond sub pod time code
1             package Text::UnicodeBox::Utility;
2              
3             =head1 NAME
4              
5             Text::UnicodeBox::Utility
6              
7             =head1 DESCRIPTION
8              
9             This module is part of the low level interface to L<Text::UnicodeBox>; you probably don't need to use it directly.
10              
11             =cut
12              
13 1     1   31535 use strict;
  1         2  
  1         41  
14 1     1   6 use warnings;
  1         1  
  1         30  
15 1     1   8822 use charnames ();
  1         41556  
  1         25  
16 1     1   10 use Exporter 'import';
  1         1  
  1         777  
17              
18             our @EXPORT_OK = qw(find_box_unicode_name fetch_box_character normalize_box_character_parameters);
19             our $report_on_failure = 0;
20              
21             =head1 EXPORTED METHODS
22              
23             The following methods are exportable by name.
24              
25             =head2 fetch_box_character
26              
27             my $character = fetch_box_character( vertical => 'heavy' );
28              
29             Same as C<find_box_unicode_name> but returns the actual symbol.
30              
31             =cut
32              
33             sub fetch_box_character {
34 0     0 1 0 my $name = find_box_unicode_name(@_);
35 0 0       0 return undef unless $name;
36              
37 0         0 return chr charnames::vianame($name);
38             }
39              
40             =head2 find_box_unicode_name (%spec)
41              
42             Given a list of directions and styles, find a matching unicode name that can represent the symbol. Returns undefined if no such symbol exists.
43              
44             The spec may contain keys like so:
45              
46             =over 4
47              
48             =item up
49              
50             =item down
51              
52             =item left
53              
54             =item right
55              
56             Provide a style for the named direction
57              
58             =item horizontal
59              
60             =item vertical
61              
62             These are the same as having provided 'top' & 'bottom' or 'left' and 'right'
63              
64             =back
65              
66             For each key, the value may be and empty string or the string '1' to default to the style 'light'. Otherwise, the value is the style you want the line segment to be ('light', 'heavy', 'double', 'single').
67              
68             =cut
69              
70             sub find_box_unicode_name {
71 10     10 1 39 my %directions = normalize_box_character_parameters(@_);
72 10 50       24 return undef unless %directions;
73              
74             # Group together styles
75 10         12 my %styles;
76 10         35 while (my ($direction, $style) = each %directions) {
77 16         14 push @{ $styles{$style} }, $direction;
  16         70  
78             }
79 10         23 my @styles = keys %styles;
80              
81 10         14 my $base_name = 'box drawings ';
82 10         9 my @variations;
83              
84 10 100       28 if (int @styles == 1) {
    50          
85             # Only one style; should be at most only two directions
86 7         7 my @directions = @{ $styles{ $styles[0] } };
  7         20  
87 7 50       17 if (int @directions > 2) {
88 0         0 die "Unexpected scenario; one style but more than 2 directions";
89             }
90 7         20 foreach my $variation (\@directions, [ reverse @directions ]) {
91 14         45 push @variations, uc $base_name . $styles[0] . ' ' . join (' and ', @$variation);
92             }
93             }
94             elsif (int @styles == 2) {
95 3         4 my @parts;
96 3         6 foreach my $style (@styles) {
97 6         7 my @directions = @{ $styles{$style} };
  6         13  
98 6 50       14 if (int @directions > 1) {
99             # right/left down/up/vertical, never down/up/vertical left/right
100             # up/down horizontal, never horizontal up/down
101 0 0 0     0 if (
      0        
      0        
102             ($directions[0] =~ m/^(down|up|vertical)$/ && $directions[1] =~ m{^(left|right)$})
103             || ($directions[0] =~ m/^(horizontal)$/ && $directions[1] =~ m{^(up|down)$})
104             ) {
105 0         0 @directions = reverse @directions;
106             }
107             }
108 6         17 push @parts, join ' ', @directions, $style;
109             }
110 3         11 foreach my $variation (\@parts, [ reverse @parts ]) {
111 6         20 push @variations, uc $base_name . join(' and ', @$variation);
112             }
113             }
114              
115 10 50       32 if (! @variations) {
116 0         0 return undef;
117             }
118              
119 10         14 foreach my $variation (@variations) {
120 14 100       1456 next unless charnames::vianame($variation);
121 9         14952 return $variation;
122             }
123              
124 1 50       333 if ($report_on_failure) {
125 0         0 print "Unable to find any character like (" .
126 0         0 join (', ', map { "$_: $directions{$_}" } sort keys %directions) .
127             "), tried the following: " .
128             join (', ', @variations) . "\n";
129             }
130              
131 1         7 return undef;
132             }
133              
134             =head2 normalize_box_character_parameters (%spec)
135              
136             Takes the passed argument list to fetch_box_character() and normalizes the arguments in an idempotent fashion, returning the new spec list.
137              
138             =cut
139              
140             sub normalize_box_character_parameters {
141 10     10 1 41 my %directions = @_;
142              
143 10 50       23 if (grep { ! defined $_ } values %directions) {
  18         55  
144 0 0       0 print "No way to handle undefined values: " .
145 0         0 join (', ', map { "$_: ".(defined $directions{$_} ? $directions{$_} : 'undef') } sort keys %directions) . "\n";
146 0         0 return ();
147             }
148              
149             # Expand shorthand
150 10         19 foreach my $direction (keys %directions) {
151 18 100       65 $directions{$direction} = 'light' if $directions{$direction} . '' eq '1';
152             }
153              
154             # Convert left & right to horizontal, up & down to vertical
155 10 100 100     66 if ($directions{down} && $directions{up} && $directions{down} eq $directions{up}) {
      66        
156 1         3 $directions{vertical} = delete $directions{down};
157 1         3 delete $directions{up};
158             }
159 10 100 100     47 if ($directions{left} && $directions{right} && $directions{left} eq $directions{right}) {
      66        
160 1         3 $directions{horizontal} = delete $directions{left};
161 1         2 delete $directions{right};
162             }
163              
164             # If any of the styles is a double, make sure all 'light' are 'single'
165 10 100       18 if (grep { $directions{$_} eq 'double' } keys %directions) {
  16         44  
166 2         5 foreach my $direction (grep { $directions{$_} eq 'light' } keys %directions) {
  4         10  
167 0         0 $directions{$direction} = 'single';
168             }
169             }
170              
171 10         40 return %directions;
172             }
173              
174             =head1 COPYRIGHT
175              
176             Copyright (c) 2012 Eric Waters and Shutterstock Images (http://shutterstock.com). All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
177              
178             The full text of the license can be found in the LICENSE file included with this module.
179              
180             =head1 AUTHOR
181              
182             Eric Waters <ewaters@gmail.com>
183              
184             =cut
185              
186             1;