sc: never wrap statistics table
[sheet.git] / Shiar_Sheet / FormatChar.pm
1 package Shiar_Sheet::FormatChar;
2
3 use strict;
4 use warnings;
5
6 use Data::Dump 'pp';
7 use PLP::Functions 'EscapeHTML';
8
9 our $VERSION = '1.06';
10
11 our $uc = do 'unicode-char.inc.pl';
12
13 sub new {
14         my ($class) = @_;
15         bless { anno => ['di', 0], style => 'di' }, $class;
16 }
17
18 sub glyph_info {
19         my ($self, $codepoint) = @_;
20         return $uc->{chr $codepoint} || eval {
21                 require Unicode::UCD;
22                 if (my $fullinfo = Unicode::UCD::charinfo($codepoint)) {
23                         return [@$fullinfo{qw/category name - string/}];
24                 }
25         } || [];
26 }
27
28 sub glyph_html {
29         my ($self, $char) = @_;
30         my $codepoint = ord $char;
31         my $info = $self->glyph_info($codepoint);
32         my ($class, $name, $mnem, $html, $string) = @$info;
33
34         my $cell = EscapeHTML($string || $char);
35         my $title = sprintf 'U+%04X%s', $codepoint, $name && " ($name)";
36
37         $cell = "<span>$cell</span>" if $class =~ /\bZs\b/;
38         $cell = '&nbsp;' if $cell eq '';
39
40         return ($cell, EscapeHTML($title), "X $class", $mnem, $html);
41 }
42
43 sub glyphs_html {
44         my $self = shift;
45
46         return $self->glyph_html(@_) if length $_[0] <= 1;
47
48         my @chars = map { [ $self->glyph_html($_) ] } split //, $_[0];
49         return (
50                 EscapeHTML($_[0]), # cell
51                 join(' | ', map { $_->[1] } @chars), # title
52                 $chars[0][2], # class
53                 join(' ',  grep { defined } map { $_->[3] } @chars), # digraph
54         );
55 }
56
57 sub glyph_cell {
58         my ($self, $char) = @_;
59         return sprintf('<td class="%3$s" title="%2$s">%s', $self->glyph_html($char));
60 }
61
62 sub cell {
63         my ($self, $input, $html) = @_;
64         my (@class, $title, $cell, $mnem, $entity);
65
66         if ($input eq '-') {
67                 $cell = '';
68         }
69         elsif ($input eq '=') {
70                 push @class, 'u-invalid';
71                 $cell = '';
72         }
73         else {
74                 push @class, 'X';
75
76                 if ($input =~ s/^-//) {
77                         push @class, 'ex'; # discouraged
78                 }
79
80                 $input =~ s/^\\//;  # escaped char
81                 ($cell, $title, my $class, $mnem, $entity) = $self->glyphs_html($input);
82
83                 if ($self->{style} eq 'di') {
84                         if ($class =~ /\bu-di\b/) {
85                                 push @class, ('l3', 'u-di'); # standard digraph
86                         }
87                         elsif ($class =~ /\bu-prop\b/) {
88                                 push @class, ('l2', 'u-prop'); # unofficial
89                         }
90                 }
91                 elsif ($self->{style} eq 'html') {
92                         if (defined $entity) {
93                                 push @class, ('l3', 'u-html');
94                         }
95                 }
96                 else {
97                         my $codepoint = ord(substr $input, 0, 1);
98                         if ($codepoint <= 0xFF) {
99                                 push @class, 'l3', 'u-lat1';  # latin1
100                         }
101                         elsif ($codepoint <= 0xD7FF) {
102                                 push @class, 'l2', 'u-bmp';  # bmp
103                         }
104                 }
105
106                 if ($input =~ /[ -~]/) {
107                         push @class, 'l4', 'u-ascii'; # ascii
108                 }
109                 else {
110                         push @class, 'l1'; # basic unicode
111                 }
112         }
113
114         my $anno = '';
115         if ($cell ne '') {
116                 for (@{ $self->{anno} }) {
117                         if (/html$/) {
118                                 if (defined $entity) {
119                                         $entity = "&$entity;" if /^&/;
120                                         $anno = sprintf(' <small class="digraph">%s</small>', EscapeHTML($entity));
121                                         last;
122                                 }
123                         }
124                         elsif ($_ eq 'xml') {
125                                 $anno = sprintf(' <small class="digraph">%s</small>',
126                                         sprintf '#%d', ord($cell)
127                                 );
128                                 last;
129                         }
130                         elsif ($_ eq '&xml') {
131                                 $anno = sprintf(' <small class="digraph">%s</small>',
132                                         sprintf '&amp;#%d;', ord($cell)
133                                 );
134                                 last;
135                         }
136                         elsif ($_ eq 'di') {
137                                 if (defined $mnem and length $mnem) {
138                                         $anno = sprintf(' <small class="digraph">%s</small>', EscapeHTML($mnem));
139                                         last;
140                                 }
141                         }
142                         else {
143                                 if ($_ eq 'hex' or $cell =~ /^[^a-zA-Z]$/) {
144                                         $anno = sprintf(' <small class="%s">%04X</small>', 'value', ord $cell);
145                                         last;
146                                 }
147                         }
148                 }
149         }
150
151         return sprintf('<td%s%s%s>%s%s',
152                 defined $title  ? qq{ title="$title"}  : '',
153                 @class ? sprintf(' class="%s"', join ' ', @class) : '',
154                 $html || '',
155                 $cell eq '' ? '&nbsp;' : $cell,
156                 $anno,
157         );
158 }
159
160 sub table {
161         my ($self, $digraphs) = @_;
162
163         my @rows;
164
165         my @colheads;
166         while ($digraphs->[0] !~ /^\./) {
167                 my $cell = shift @$digraphs or last;
168                 push @colheads, sprintf(
169                         '<%s%s>%s',
170                         $cell =~ s/^-// ? 'td' : 'th',
171                         $cell =~ s/:(.*)// ? qq{ title="$1"} : '',
172                         $cell eq '_' ? '&nbsp;' : $cell
173                 );
174         }
175         push @rows, sprintf '<thead><tr>%s<tbody>', join '', @colheads if @colheads;
176
177         my $colspan = 1;
178         for my $cell (@$digraphs) {
179                 if ($cell =~ s/^\.//) {
180                         # dot indicates start of a new row
181                         push @rows, '<tr>';
182                         if ($cell =~ s/^>//) {
183                                 # header cell text follows
184                                 $cell =~ s/_/ /g;  # underscores may be used instead of whitespace (for qw//ability)
185                                 $rows[-1] .= '<th>'.($cell || '&nbsp;');
186                         }
187                         next;
188                 }
189                 elsif ($cell eq '>') {
190                         # merge this cell to the next column
191                         $colspan++;
192                         next;
193                 }
194
195                 $rows[-1] .= $self->cell($cell,
196                         $colspan > 1 && qq{ colspan="$colspan"},
197                 );
198
199                 $colspan = 1;
200         }
201
202         return sprintf qq{<table class="glyphs%s">\n%s</table>\n},
203                 @{ $self->{anno} } ? ' dilabel' : '',
204                 join '', map {"$_\n"} @rows;
205 }
206
207 sub print {
208         my $self = shift;
209         while (@_) {
210                 print '<div class="section">';
211                 printf '<h2>%s</h2>', shift unless ref $_[0];
212                 print "\n\n";
213                 while (ref $_[0] and $_ = shift) {
214                         print $self->table($_);
215                 }
216                 print "\n</div>";
217         }
218 }
219
220 1;
221