unifont-7.0.01.tar.gz
[unifont.git] / src / unifont-viewer
diff --git a/src/unifont-viewer b/src/unifont-viewer
new file mode 100755 (executable)
index 0000000..7556f4b
--- /dev/null
@@ -0,0 +1,444 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Wx;
+
+package Unifont;
+
+use GD qw (:DEFAULT :cmp);
+
+sub LoadHexFile {
+       my ($input) = @_;
+       my %hexlist = ();
+
+       open (HEXFILE, "$input") or die ("Cannot open file\n");
+
+       while (<HEXFILE>) {
+               chomp;
+               my @data = split (':', $_);
+
+               my $codepoint = hex ($data[0]);
+               my $char = $data[1];
+#              my $display_width = $codepoint > 0xFFFF ? 6 : 4;
+               # Use 6-digit codepoint for all glyphs for now
+               my $display_width = 6;
+
+               $hexlist{sprintf ("%0*X", $display_width, $codepoint)} = $char;
+       }
+
+       return %hexlist;
+}
+
+sub Hex2PNG {
+       my ($hexlist_ref, $pagenum, $charheight) = @_;
+       my %hexlist = %$hexlist_ref;
+
+       if ($pagenum > 0x10FF) {
+               die ("Invalid page\n");
+       }
+
+       my $charxoffset = 4;
+       my $gridxoffset = 48;
+       my $gridyoffset = 32;
+       my ($charyoffset, $boxsize, $xmax, $ymax, $charmaxwidth);
+
+       if (not $charheight) {
+               $charheight = 16;
+       }
+
+       if ($charheight == 16) {
+               $charyoffset = 7;
+               $boxsize = 32;
+               $xmax = 2;
+               $ymax = 1;
+               $charmaxwidth = 6;
+       } elsif ($charheight == 24) {
+               $charyoffset = 4;
+               $boxsize = 32;
+               $xmax = 2;
+               $ymax = 2;
+               $charmaxwidth = 6;
+       } elsif ($charheight == 32) {
+               $charyoffset = 4;
+               $boxsize = 40;
+               $xmax = 3;
+               $ymax = 3;
+               $charmaxwidth = 8;
+       } else {
+               die ("Invalid height\n");
+       }
+
+       # Create box and set as tile pattern
+
+       my $box = new GD::Image ($boxsize, $boxsize);
+
+       my $black = $box->colorAllocate (0, 0, 0);
+       my $white = $box->colorAllocate (255, 255, 255);
+
+       $box->filledRectangle (1, 1, $boxsize - 1, $boxsize - 1, $white);
+
+       # Draw dots at 8 pixel boundaries
+        for (my $count = 0; $count <= $xmax; $count++) {
+               $box->setPixel (($count * 8) + $charxoffset + 1, 0, $white);
+               $box->setPixel (($count * 8) + $charxoffset + 8, 0, $white);
+       }
+
+       for (my $count = 0; $count <= $ymax; $count++) {
+               $box->setPixel (0, ($count * 8) + $charyoffset + 1, $white);
+               $box->setPixel (0, ($count * 8) + $charyoffset + 8, $white);
+       }
+
+       # Draw grid
+
+       my $im = new GD::Image ($boxsize * 16 + $gridxoffset, $boxsize * 16 + $gridyoffset);
+
+       $black = $im->colorAllocate (0, 0, 0);
+       $white = $im->colorAllocate (255, 255, 255);
+
+       $im->fill (0, 0, $white);
+
+       for (my $xcount = 0; $xcount <= 16; $xcount++) {
+               for (my $ycount = 0; $ycount <= 16; $ycount++) {
+                       $im->copy ($box, $xcount * $boxsize + $gridxoffset - 1, $ycount * $boxsize + $gridyoffset - 1, 0, 0, $boxsize, $boxsize);
+               }
+       }
+
+       # Print plane
+       $im->string (gdLargeFont, 8, 9, sprintf ('U+%02X', $pagenum >> 8), $black);
+
+       # Print row headers
+       for (my $count = 0; $count <= 15; $count++) {
+               $im->string (gdLargeFont, 32, ($count * $boxsize) + (($boxsize - 16) / 2) + $gridyoffset, sprintf ('%X', $count), $black);
+       }
+
+       # Print column headers
+       for (my $count = 0; $count <= 15; $count++) {
+               $im->string (gdLargeFont, ($count * $boxsize) + (($boxsize - 24) / 2) + $gridxoffset, 9, sprintf ('%03X', (($pagenum & 0xFF) << 4) + $count), $black);
+       }
+
+       while ((my $codepoint, my $char) = each %hexlist) {
+               if ($codepoint and $codepoint =~ m/^[0-9A-F]{4,6}$/) {
+                       my $cp = hex ($codepoint);
+
+                       # Calculate if codepoint is within page
+                       if ($cp >> 8 == $pagenum) {
+                               # Calculate character width, column and row
+                               my $charwidth = length ($char) / $charheight;
+
+                               if ($charwidth <= $charmaxwidth) {
+                                       my $col = ($cp >> 4) & 0xF;
+                                       my $row = $cp & 0xF;
+
+                                       for (my $j = 0; $j < $charheight; $j++) {
+                                               # Get character row
+                                               my $r = hex (substr ($char, $j * $charwidth, $charwidth));
+
+                                               # Draw character
+                                               for (my $i = 0; $i < $charwidth * 4; $i++) {
+                                                       if ($r & 1 << $i) {
+                                                               $im->setPixel (($col * $boxsize) + ($charwidth * 4 - $i) + $charxoffset + $gridxoffset - 1, ($row * $boxsize) + $j + $charyoffset + $gridyoffset, $black);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return $im;
+}
+
+package UnifontViewer;
+
+use base qw (Wx::App);
+use Wx qw (wxMINIMIZE_BOX wxSYSTEM_MENU wxCAPTION wxCLOSE_BOX wxCLIP_CHILDREN);
+
+sub OnInit {
+       my $self  = shift;
+       my $frame = UnifontViewerFrame->new (
+               undef,
+               -1,
+               'Unifont Viewer',
+               [-1, -1],
+               [-1, -1],
+               wxMINIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN
+       );
+
+       $frame->Show (1);
+       $self->SetTopWindow ($frame);
+
+       return 1;
+}
+
+package ImagePanel;
+
+use base qw (Wx::Panel);
+use fields qw (memdc);
+use Wx qw (wxCOPY);
+use Wx::Event qw (EVT_PAINT);
+
+sub new {
+       my $class = shift;
+       my $self  = $class->SUPER::new (@_);
+
+       $self->{memdc} = Wx::MemoryDC->new ();
+
+       EVT_PAINT($self, \&OnPaint);
+
+       return $self;
+}
+
+sub clear {
+       my $self = shift;
+
+       $self->{memdc}->Clear ();
+       $self->Refresh ();
+}
+
+sub load {
+       my ($self, $filename) = @_;
+
+       my $file = IO::File->new ($filename, 'r');
+       binmode $file;
+       my $handler = Wx::PNGHandler->new ();
+       my $image = Wx::Image->new ();
+       my $bitmap;
+       $handler->LoadFile ($image, $file);
+       $bitmap = Wx::Bitmap->new ($image);
+
+       if ($bitmap->Ok ()) {
+               $self->{memdc}->SelectObject ($bitmap);
+               $self->Refresh ();
+       }
+}
+
+sub load_gd {
+       my ($self, $gd) = @_;
+       my $png = $gd->png;
+
+       open my $fh, '<', \$png;
+       my $handler = Wx::PNGHandler->new ();
+       my $image = Wx::Image->new ();
+       my $bitmap;
+       $handler->LoadFile ($image, $fh);
+       close $fh;
+       $bitmap = Wx::Bitmap->new ($image);
+
+       if ($bitmap->Ok ()) {
+               $self->{memdc}->SelectObject ($bitmap);
+               $self->Refresh ();
+       }
+}
+
+sub OnPaint {
+       my ($self, $event) = @_;
+       my $size = $self->GetClientSize ();
+
+       my $dcPaint = Wx::PaintDC->new ($self);
+       $dcPaint->Blit (0, 0, $size->x, $size->y, $self->{memdc}, 0, 0, wxCOPY, 0);
+}
+
+package UnifontViewerFrame;
+
+use base qw (Wx::Frame);
+use fields qw (filename hexlist charheight listbox imagepanel);
+use Wx::Event qw (EVT_MENU EVT_LISTBOX);
+use Wx qw (wxBORDER_SIMPLE wxID_OPEN wxID_SAVE wxID_EXIT wxID_OK wxHORIZONTAL wxEXPAND wxALL wxLB_SORT wxFD_OPEN wxFD_SAVE wxFD_FILE_MUST_EXIST wxFD_CHANGE_DIR);
+
+our @id = (0 .. 100);
+
+sub new {
+       my $class = shift;
+       my $self  = $class->SUPER::new (@_);
+       $self->{filename} = '';
+       $self->{charheight} = 16;
+
+       my $boxsizer = Wx::BoxSizer->new (wxHORIZONTAL);
+
+       $self->{listbox} = Wx::ListBox->new (
+               $self,
+               -1,
+               [-1, -1],
+               [64, -1],
+               [],
+               wxLB_SORT
+       );
+
+       $self->{imagepanel} = ImagePanel->new (
+               $self,
+               -1,
+               [-1, -1],
+               [-1, -1],
+               wxBORDER_SIMPLE
+       );
+
+       SetCharHeight ($self, $self->{charheight});
+
+       $boxsizer->Add (
+               $self->{listbox},
+               0,
+               wxEXPAND | wxALL,
+               0
+       );
+
+       $boxsizer->Add (
+               $self->{imagepanel},
+               0,
+               wxALL,
+               1
+       );
+
+       EVT_LISTBOX ($self->{listbox}, -1, sub {
+               my $selection = $self->{listbox}->GetStringSelection ();
+               SetPage ($self, $selection);
+       });
+
+       my $menubar = Wx::MenuBar->new ();
+       my $menu = Wx::Menu->new ();
+
+       $menu->Append (wxID_OPEN, "O&pen...\tCtrl+O");
+       $menu->Append (wxID_SAVE, "S&ave As...\tCtrl+S");
+       $menu->AppendSeparator ();
+       $menu->AppendRadioItem ($id[0], "Glyph Height 16");
+       $menu->AppendRadioItem ($id[1], "Glyph Height 24");
+       $menu->AppendRadioItem ($id[2], "Glyph Height 32");
+       $menu->AppendSeparator ();
+       $menu->Append (wxID_EXIT, "E&xit\tCtrl+X");
+       $menubar->Append ($menu, 'File');
+
+       $self->SetMenuBar ($menubar);
+
+       EVT_MENU ($self, wxID_OPEN, \&OpenFile);
+       EVT_MENU ($self, wxID_SAVE, \&SaveFile);
+       EVT_MENU ($self, $id[0], sub {SetCharHeight ($self, 16)});
+       EVT_MENU ($self, $id[1], sub {SetCharHeight ($self, 24)});
+       EVT_MENU ($self, $id[2], sub {SetCharHeight ($self, 32)});
+       EVT_MENU ($self, wxID_EXIT, sub {$_[0]->Close (1)});
+
+       $self->SetSizerAndFit ($boxsizer);
+
+       return $self;
+}
+
+sub OpenFile {
+       my ($self, $event) = @_;
+
+       my $dlg = Wx::FileDialog->new (
+               $self,
+               'Open File',
+               '',
+               '',
+               'Hex files (*.hex)|*.hex',
+               wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_CHANGE_DIR
+       );
+
+       if ($dlg->ShowModal == wxID_OK) {
+               $self->{imagepanel}->clear ();
+
+               my %pages = ();
+
+               $self->{filename} = $dlg->GetPath ();
+               $self->SetTitle ("Unifont Viewer - " . $dlg->GetFilename ());
+
+               open HEXFILE, "<$self->{filename}" || die "Cannot open $self->{filename}\n";
+
+               while (<HEXFILE>) {
+                       chomp;
+                       my ($codepoint, $char) = (split (':', $_));
+                       # For now, list page numbers as 4 digits even for Plane 0 for sorting
+                       if (length ($codepoint) == 4) {
+                               $codepoint = "00" . $codepoint;
+                       }
+
+                       if ($codepoint and $codepoint =~ m/^[0-9A-F]{4,6}$/) {
+                               if (!($char eq '00542A542A542A542A542A542A542A00' || $char eq 'FFB9C5EDD5D5D5D5D5D5D5D5EDB991FF')) {
+                                       my $page = substr ($codepoint, 0, -2);
+                                       $pages{$page} = 1;
+                               }
+                       }
+               }
+
+               $self->{listbox}->Clear ();
+               for (keys %pages) {
+                       $self->{listbox}->Append ($_, hex ($_));
+               }
+
+               my %hexlist = Unifont::LoadHexFile ($self->{filename});
+               $self->{hexlist} = \%hexlist;
+
+               $self->{listbox}->Select (0);
+               my $selection = $self->{listbox}->GetStringSelection ();
+               SetPage ($self, $selection);
+       };
+}
+
+sub SaveFile {
+       my ($self, $event) = @_;
+
+       my $selection = $self->{listbox}->GetStringSelection ();
+
+       if ($selection) {
+               my $dlg = Wx::FileDialog->new (
+                       $self,
+                       'Save File',
+                       '',
+                       "$selection.png",
+                       'PNG files (*.png)|*.png',
+                       wxFD_SAVE
+               );
+
+               if ($dlg->ShowModal == wxID_OK) {
+                       my $im = Unifont::Hex2PNG ($self->{hexlist}, hex ($selection), $self->{charheight});
+
+                       my $filename = $dlg->GetPath ();
+
+                       $self->{imagepanel}->load_gd ($im);
+
+                       open (PICTURE, ">$filename") or die ("Cannot save image\n");
+                       binmode PICTURE;
+                       print PICTURE $im->png;
+                       close PICTURE;
+               }
+       }
+}
+
+sub SetPage {
+       my ($self, $selection) = @_;
+
+       if ($selection) {
+               my $im = Unifont::Hex2PNG ($self->{hexlist}, hex ($selection), $self->{charheight});
+               $self->{imagepanel}->load_gd ($im);
+       } else {
+               $self->{imagepanel}->clear ();
+       }
+}
+
+sub SetCharHeight {
+       my ($self, $charheight) = @_;
+       my ($x, $y);
+       my $selection = $self->{listbox}->GetStringSelection ();
+
+       $self->{charheight} = $charheight;
+
+       if ($charheight == 16 || $charheight == 24) {
+               $x = 562;
+               $y = 544;
+       } elsif ($charheight == 32) {
+               $x = 688;
+               $y = 672;
+       }
+
+       $self->{imagepanel}->SetMinSize ([-1, -1]);
+       $self->{imagepanel}->SetClientSize ($x, $y);
+       $self->{imagepanel}->SetMinSize ($self->{imagepanel}->GetSize ());
+       $self->Fit ();
+
+       SetPage ($self, $selection);
+}
+
+package main;
+
+my ($app) = UnifontViewer->new ();
+
+$app->MainLoop ();