User:Saric/Roadmap

This is the script I wrote to generate this image. Feel free to update the $data_string, run the program, and replace the image once the unused code points get assigned.

#!/usr/bin/perl

use warnings;
use strict;
use SVG;

# ---------------------------------------------------------------
# Options
# ---------------------------------------------------------------

our $side_length = 500;
  # Height and width of the roadmap square in pixels.
# The following sizes are expressed as fractions of
# $side_length.
our $line_width = 1/250;
  # The width of the divider lines.
our $legend_width = 1/2;
  # Width of the margin used for the legend.
our $legend_box_space = 1/30;
  # Space between the rectangle for each legend and the
  # right edge of the roadmap square.
our $legend_top_margin = 1/30;
  # Space between the first box of the legend and the top of the
  # image.
our $legend_box_width = 1/20;
our $legend_box_height = 1/40;
our $legend_text_space = 1/50;
  # Space between the left edge of each legend box and
  # its descriptive text.
our $legend_line_break = 1/50;

our $line_color = '#DADADA';
our $roadmap_font =
    '"DejaVu Sans Mono", "Andale Mono", monospace';
our $legend_font =
    '"DejaVu Sans", Arial, "sans-serif"';

our %text_colors =
   (map({$_ => 'white'}
        qw(black darkgray blue darkgreen purple)),
    map({$_ => 'black'}
        qw(white lightgray lightblue cyan orange
           lightgreen red yellow salmon magenta)));

our @scripts =
   (['latin', 'Latin scripts and symbols', 'black'],
    ['ling', 'Linguistic scripts', 'lightblue'],
    ['euro', 'Other European scripts', 'blue'],
    ['meswa', "Middle Eastern and\nSouthwest Asian scripts", 'orange'],
    ['africa', 'African scripts', 'lightgreen'],
    ['Sasian', 'South Asian scripts', 'darkgreen'],
    ['SEasian', 'Southeast Asian scripts', 'purple'],
    ['Easian', 'East Asian scripts', 'red'],
    ['han', 'Unified CJK Han', 'salmon'],
    ['canada', 'Canadian Aboriginal scripts', 'yellow'],
    ['symbol', 'Symbols', 'magenta'],
    ['diacritics', 'Diacritics', 'darkgray'],
    ['private', "UTF-16 surrogates and\nprivate use", 'lightgray'],
    ['misc', 'Miscellaneous characters', 'cyan'],
    ['unused', 'Unallocated code points', 'white']);

# http://www.unicode.org/roadmaps/bmp/
# Format of each line:
#   First 2 hex digits, 3rd digit, script name   # Comment
our $data_string =
 qq[00 0 latin
    02 5 ling
    03 0 diacritics
    03 7 euro
    05 9 meswa
    07 8 Sasian
    07 C africa
    08 4 unused
    09 0 Sasian
    0E 0 SEasian
    10 A meswa
    11 0 Easian
    12 0 africa
    13 A canada
    16 8 euro
    17 0 Easian
    17 8 SEasian
    18 0 Easian
    18 B unused
    19 0 Sasian
    19 5 Easian
    19 E SEasian
    1A B unused
    1B 0 Sasian
    1B C unused
    1C 0 Sasian
    1D 0 ling
    1D C diacritics
    1E 0 latin
    1F 0 euro
    20 0 symbol
    20 7 latin
    20 A symbol
    21 0 latin
    21 9 symbol
    24 6 latin
    25 0 symbol
    2C 0 euro
    2C 6 latin
    2C 8 euro
    2D 0 meswa
    2D 3 africa
    2D E euro
    2E 0 SEasian
    2E 8 han
    30 4 Easian
    31 A han  # The third digit is a wild guess, really.
    31 F Easian
    34 0 han
    4D C symbol
    4E 0 han
    A0 0 Easian
    A5 0 africa  # Vai counts as an African script, right?
    A6 4 euro
    A6 A africa
    A7 0 Easian
    A7 2 latin
    A8 0 Sasian
    A9 0 Easian
    A9 3 SEasian
    A9 6 Easian
    A9 8 SEasian
    A9 E unused
    AA 0 SEasian
    AA 6 unused
    AA 8 SEasian
    AA E unused
    AB 0 unused
    AC 0 Easian
    D8 0 private
    F9 0 han
    FB 0 misc];

# ---------------------------------------------------------------
# Other declarations
# ---------------------------------------------------------------

our $grad_defs;
our $grad_id = -1;

sub stripes
# This creates a "gradient" of distinct vertical stripes. Its
# arguments should be the starting x-coordinate of the gradient,
# the ending x-coordinate, an SVG color, and then any number of
# stops. Each stop should be an array reference containing a stop
# location (expressed as a number between 0 and 1) and a color.
# The subroutine returns a string you can set a stroke or fill
# attribute to to use the gradient.
 {my ($x1, $x2, $first_color, @stops) = @_;
  my $grad = $grad_defs->gradient
   (-type => 'linear',
    gradientUnits => "userSpaceOnUse",
    id => 'grad' . ++$grad_id,
    x1 => $x1, x2 => $x2);
  $grad->stop
     (offset => '0%',
      'stop-color' => $first_color);
  my $last_color = $first_color;
  foreach (@stops)
     {my $percent = 100*$_->[0] . '%';
      $grad->stop
         (offset => $percent,
          'stop-color' => $last_color);
      $grad->stop
         (offset => $percent,
          'stop-color' => $_->[1]);
      $last_color = $_->[1];}
  $grad->stop
     (offset => '100%',
      'stop-color' => $last_color);
  return "url(#grad$grad_id)";}

sub tcolor
# Given the same arguments as &stripes, returns a value to use
# for the "fill" of text overlaying the given colors. This may be
# a solid color instead of a gradient.
 {my ($x1, $x2, $first_color, @stops) = @_;
  my $last_tc = $text_colors{$first_color};
  $first_color = $last_tc;
  for (my $n = 0 ; $n < @stops ; ++$n)
     {my $this_tc = $text_colors{$stops[$n][1]};
      if ($this_tc eq $last_tc)
         # This stop is redundant, so we can remove it.
         {splice(@stops, $n, 1);
          $n < @stops ? redo : last;}
      $stops[$n][1] = $this_tc;
      $last_tc = $this_tc;}
  return (@stops
    ? stripes($x1, $x2, $first_color, @stops) 
    : # We can just return a solid color.
      $first_color);}

# ---------------------------------------------------------------
# Process $data_string
# ---------------------------------------------------------------

$data_string =~ s {\#.+} {}gm;
our @d = ();
 {my %script_colors = ();
  $script_colors{$_->[0]} = $_->[2] foreach @scripts;
  foreach (split /\s*\n\s*/, $data_string)
     {/\S/ or next;
      /(.)(.)\s+(.)\s+(.+)/;
      push( @d, [hex($1), hex($2), hex($3),
                 $script_colors{$4}] );}}

# ---------------------------------------------------------------
# Set up the SVG
# ---------------------------------------------------------------

$$_ *= $side_length foreach
   (\$line_width, \$legend_width, \$legend_box_space,
    \$legend_box_height, \$legend_top_margin,
    \$legend_box_width, \$legend_text_space,
    \$legend_line_break);

our $svg = new SVG
   (width => $side_length + $legend_width,
    height => $side_length);
$svg->title->cdata('Roadmap to the Unicode BMP');
$grad_defs = $svg->defs;
  # I declare this here to ensure that the gradient definitions
  # appear in the file before anything else, especially the
  # rectangles that reference them.
$svg->rectangle
   (x => 0, 'y' => 0,
    width => $side_length + $legend_width,
    height => $side_length,
    'stroke-width' => 0,
    'fill' => 'white');
our $rectgrp = $svg->group
   ('stroke-width' => ($line_width . 'px'),
    'stroke' => $line_color);
our $sq_side_length =
    ($side_length - $line_width) / 16;
our $roadmap_tgrp = $svg->group
   ('text-anchor' => 'middle',
    'font-family' => $roadmap_font,
    'font-size' => ($sq_side_length/2 . 'px'),
    'stroke-width' => 0);
our $legend_tgrp = $svg->group
   ('text-anchor' => 'left',
    'font-family' => $legend_font,
    'font-size' => ($legend_box_height . 'px'),
    'stroke-width' => 0,
    'fill' => 'black');

# ---------------------------------------------------------------
# Draw the roadmap square
# ---------------------------------------------------------------

 {my $last_c = shift(@d)->[3];
    # The last color we used.
  my @next = @{shift @d};
    # The next stop (equivalent to one line of the $data_string).
  foreach my $y (0 .. 15)
     {foreach my $x (0 .. 15)
         # $y and $x correspond to the first and second digits,
         # respectively, of each character's code point
         {my $xp = $line_width/2 + $x*$sq_side_length;
          my $yp = $line_width/2 + $y*$sq_side_length;
          my ($sq_fill, $t_fill);
          my @stops_here = ();
            # Stops that occur in this square.
          while (@next and $next[0] == $y and $next[1] == $x)
             {push(@stops_here, [@next]);
              @next = (@d ? @{shift @d} : ());}
          if (@stops_here)
             {$stops_here[0][2]
                or $last_c = shift(@stops_here)->[3];
              my @args =
                 ($xp, 
                  $xp + $sq_side_length,
                  $last_c,
                  map {[ $_->[2]/16, $_->[3] ]} @stops_here);
              $sq_fill = stripes(@args);
              $t_fill = tcolor(@args);
              @stops_here
                and $last_c = $stops_here[-1][3];}
          else
             {$sq_fill = $last_c;
              $t_fill = $text_colors{$sq_fill};}
          $rectgrp->rectangle
             (x => $xp, 'y' => $yp,
              width => $sq_side_length,
              height => $sq_side_length,
              fill => $sq_fill);
          $roadmap_tgrp->text
             (x => ($xp + $sq_side_length/2),
              'y' => ($yp + (2/3)*$sq_side_length),
              fill => $t_fill)
             ->cdata(sprintf('%X%X', $y, $x));}}}

# ---------------------------------------------------------------
# Draw the legend
# ---------------------------------------------------------------

 {my $x = $side_length + $legend_box_space + $line_width / 2;
  my $y = $legend_top_margin + $line_width / 2;
  foreach (@scripts)
     {$rectgrp->rectangle
         (x => $x, 'y' => $y,
          width => $legend_box_width,
          height => $legend_box_height,
          fill => $_->[2]);
      my @txt = split /\n/, $_->[1];
      foreach (@txt) 
         {$legend_tgrp->text
             (x => ($x + $legend_box_width + $legend_text_space),
              'y' => ($y + (4/5)*$legend_box_height))
             ->cdata($_);
          $y += (5/4)*$legend_box_height;}
      $y += $legend_line_break;}}

# ---------------------------------------------------------------
# Output
# ---------------------------------------------------------------

my $txt = $svg->xmlify;
# Remove extra space in <text> elements. Inkscape ignores it, but
# librsvg treats it like a normal character, thus screwing up
# text alignment.
$txt =~ s{\s+</text>\s} {</text>\n}g;
# Do the same for the <title>, for good measure.
$txt =~ s{\s+</title>\s} {</title>\n};
# Change to Unix-style newlines if necessary.
$txt =~ s{\015\012?} {\012}g;
print $txt;

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.