X-Git-Url: http://git.shiar.net/unifont.git/blobdiff_plain/527ee724d6dd13bf80699e653962a3d082631377..95e8d0be857e4bf0387fc5174276c1e4ab75ce5b:/src/unifont-viewer diff --git a/src/unifont-viewer b/src/unifont-viewer new file mode 100755 index 0000000..7556f4b --- /dev/null +++ b/src/unifont-viewer @@ -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 () { + 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 () { + 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 ();