+ }
+
+ $attr .= sprintf ' rowspan=%d', $rows;
+ $len = $cols;
+ }
+
+ $attr .= sprintf ' colspan=%d', $len unless $len == 1;
+ $attr .= $1 if $class and $class =~ s/( \w+="[^"]*")//;
+ $attr .= sprintf ' class="%s"', $class if $class;
+ $attr .= sprintf ' title="%s"', EscapeHTML($title) if $title;
+ return "<td$attr>$name\n";
+}
+
+for my $row (@request) {
+ my $cols = $row->{cols};
+ my $colsize = $row->{cell} && $row->{cell}->{colsize} || 1;
+ my $coldigits = ceil(log($colsize * $cols) / log(16)); # uniform length of hexadecimal header
+ my $rowdiv = 16 ** $coldigits; # row divide for column digits
+ $rowdiv = 1 if $rowdiv != $cols * $colsize; # divide only if all columns are matched
+ my $offset = $row->{startpoint} * $colsize || 0;
+
+ printf '<div class="section"><table class="glyphs%s">', !$row->{cell} && ' charmap';
+ my $title = $row->{set};
+ $title .= " <aside>(over $_)</aside>"
+ for $row->{parent} || ();
+ printf '<caption>%s</caption>', $title;
+ print '<col>' x ($cols + 1);
+ for my $section (qw{thead}) {
+ print "<$section><tr><th>", $rowdiv == 1 ? '+' : '↱';
+ printf '<th>%0*X', $coldigits, $_ * $colsize for 0 .. $cols - 1;
+ print "\n";
+ }
+
+ print '<tbody>';
+ while ($offset <= $row->{endpoint} * $colsize) {
+ if ($row->{skip}->{$offset}) {
+ $offset += $cols * $colsize;
+ next;
+ }
+
+ print '<tr><th>';
+ if (defined $row->{skip}->{$offset}) {
+ print '⋮';
+ }
+ else {
+ if (my $rowmod = $offset % $rowdiv) {
+ # offset in column units
+ printf '<small>+%X</small>', $rowmod;