From d5d6fdb063a7d66b8451f40601297462cc0271b7 Mon Sep 17 00:00:00 2001 From: Sergiotarxz Date: Wed, 23 Oct 2024 02:54:12 +0200 Subject: [PATCH] Implementing font importing functionality and copy to clipboard of imported fonts. --- lib/Exd/FileFormat.pm | 6 +- lib/Exd/FileFormat/DB.pm | 2 + lib/Exd/FileFormat/DB/Migrations.pm | 9 +- lib/Exd/FileFormat/Fontconfig.pm | 35 ++++++-- lib/Exd/Fontconfig.pm | 37 ++++++++ lib/Exd/Gui.pm | 51 +++++------ lib/Exd/Gui/FontGallery.pm | 134 ++++++++++++++++++++++++++-- lib/Exd/Gui/Instance.pm | 44 ++++----- 8 files changed, 252 insertions(+), 66 deletions(-) diff --git a/lib/Exd/FileFormat.pm b/lib/Exd/FileFormat.pm index e100a51..cc0f991 100644 --- a/lib/Exd/FileFormat.pm +++ b/lib/Exd/FileFormat.pm @@ -18,10 +18,10 @@ use Exd::FileFormat::DB; use Exd::FileFormat::Fontconfig; has dir => ( is => 'rw' ); -has _fontconfig => (is => 'lazy'); +has fontconfig => (is => 'lazy'); has dbh => (is => 'lazy'); -sub _build__fontconfig($self) { +sub _build_fontconfig($self) { return Exd::FileFormat::Fontconfig->new(exd => $self); } @@ -150,7 +150,7 @@ sub execute( $self, $printer ) { if ($@) { die $@; } - $self->_fontconfig->set_current; + $self->fontconfig->set_current; $sub->( $self, $printer ); } diff --git a/lib/Exd/FileFormat/DB.pm b/lib/Exd/FileFormat/DB.pm index e98809f..b43e5a8 100644 --- a/lib/Exd/FileFormat/DB.pm +++ b/lib/Exd/FileFormat/DB.pm @@ -38,6 +38,7 @@ sub _migrate { my $dbh = shift; local $dbh->{RaiseError} = 0; local $dbh->{PrintError} = 0; + say 'Trying to migrate'; my @migrations = Exd::FileFormat::DB::Migrations::MIGRATIONS(); if ( $class->get_current_migration($dbh) > @migrations ) { warn "Something happened there, wrong migration number."; @@ -47,6 +48,7 @@ sub _migrate { return; } $class->_apply_migrations( $exd, $dbh, \@migrations ); + say 'Database migrated'; } sub _apply_migrations { diff --git a/lib/Exd/FileFormat/DB/Migrations.pm b/lib/Exd/FileFormat/DB/Migrations.pm index 59f9ee9..df28e9d 100644 --- a/lib/Exd/FileFormat/DB/Migrations.pm +++ b/lib/Exd/FileFormat/DB/Migrations.pm @@ -28,8 +28,13 @@ sub MIGRATIONS { name TEXT NOT NULL );', sub ($exd, $dbh) { - my $fontconfig = Exd::FileFormat::Fontconfig->new(exd => $exd); - $fontconfig->migration2; + eval { + my $fontconfig = Exd::FileFormat::Fontconfig->new(exd => $exd); + $fontconfig->migration2; + }; + if ($@) { + warn $@; + } } ); } diff --git a/lib/Exd/FileFormat/Fontconfig.pm b/lib/Exd/FileFormat/Fontconfig.pm index f5e416d..8c487d3 100644 --- a/lib/Exd/FileFormat/Fontconfig.pm +++ b/lib/Exd/FileFormat/Fontconfig.pm @@ -27,13 +27,29 @@ sub restore_system_config($self) { } sub list_fonts($self) { - return $self->_list_fonts_c; + my $fonts = $self->_list_fonts_c($self->_font_dir->child('dir')); + return $fonts; } sub _font_dir($self) { return $self->exd->dir->child('fonts'); } +sub import_font($self, $file) { + my $exd_font_dir = $self->_font_dir->child('dir'); + my $font_file = path($file); + my $sha = sha256_hex $font_file->slurp_raw; + system 'cp', '-v', $font_file, $exd_font_dir->child($sha); + my $name = $font_file->basename; + $name =~ s/\..*?$//; + eval { + $self->_add_font_to_database($name, $sha); + }; + if ($@) { + warn $@; + } +} + sub migration1($self) { my $exd = $self->exd; my $app_font_dir = @@ -106,10 +122,15 @@ _set_current_c(SV *self, char *font_dir) { } AV * -_list_fonts_c(SV *self) { +_list_fonts_c(SV *self, char *font_dir) { AV *return_array = newAV(); - FcConfig *config = FcConfigGetCurrent(); + FcConfig *config = FcConfigCreate(); + FcConfigAppFontAddDir(config, font_dir); + FcConfigBuildFonts(config); FcFontSet *fonts = FcConfigGetFonts(config, FcSetApplication); + if (fonts == NULL) { + return return_array; + } for (int i = 0; i < fonts->nfont; i++) { FcPattern *pattern = fonts->fonts[i]; FcChar8 *family; @@ -120,10 +141,10 @@ _list_fonts_c(SV *self) { FcPatternGetString(pattern, FC_FILE, 0, &file); char *familyKey; HV *hash = newHV(); - SV *hash_ref = newRV_inc((SV *) hash); - hv_stores(hash, "family", newRV_inc(sv_2mortal(newSVpv(family, 0)))); - hv_stores(hash, "style", newRV_inc(sv_2mortal(newSVpv(style, 0)))); - hv_stores(hash, "file", newRV_inc(sv_2mortal(newSVpv(file, 0)))); + SV *hash_ref = newRV_noinc((SV *) hash); + hv_stores(hash, "family", newSVpv(family, 0)); + hv_stores(hash, "style", newSVpv(style, 0)); + hv_stores(hash, "file", newSVpv(file, 0)); av_push(return_array, hash_ref); } return return_array; diff --git a/lib/Exd/Fontconfig.pm b/lib/Exd/Fontconfig.pm index a108179..ed97187 100644 --- a/lib/Exd/Fontconfig.pm +++ b/lib/Exd/Fontconfig.pm @@ -8,4 +8,41 @@ use utf8; use Moo; +use Inline C => DATA => LIBS => '-lfontconfig -lfreetype'; + +sub list_fonts($self) { + my $fonts = $self->_list_fonts_c; + return $fonts; +} 1; +__DATA__ +__C__ +#include +#include + +AV * +_list_fonts_c(SV *self) { + AV *return_array = newAV(); + FcConfig *config = FcConfigGetCurrent(); + FcFontSet *fonts = FcConfigGetFonts(config, FcSetSystem); + if (fonts == NULL) { + return return_array; + } + for (int i = 0; i < fonts->nfont; i++) { + FcPattern *pattern = fonts->fonts[i]; + FcChar8 *family; + FcChar8 *style; + FcChar8 *file; + FcPatternGetString(pattern, FC_FAMILY, 0, &family); + FcPatternGetString(pattern, FC_STYLE, 0, &style); + FcPatternGetString(pattern, FC_FILE, 0, &file); + char *familyKey; + HV *hash = newHV(); + SV *hash_ref = newRV_noinc((SV *) hash); + hv_stores(hash, "family", newSVpv(family, 0)); + hv_stores(hash, "style", newSVpv(style, 0)); + hv_stores(hash, "file", newSVpv(file, 0)); + av_push(return_array, hash_ref); + } + return return_array; +} diff --git a/lib/Exd/Gui.pm b/lib/Exd/Gui.pm index 90fd758..3be0a4b 100644 --- a/lib/Exd/Gui.pm +++ b/lib/Exd/Gui.pm @@ -222,24 +222,32 @@ sub _on_run_script_packet( $self, $instance_id, $json ) { $json->@{ 'exd_dir', 'device', 'verbose' }; $device = $self->device_hash_to_object($device); - # if ( $self->_last_script_run_pid - # && $self->_last_script_run_device->isa('Exd::DeviceToImage') - # && !$verbose ) - # { - # kill 'KILL', $self->_last_script_run_pid; - # } - # $self->_last_script_run_device($device); + my ($read, $write); + pipe $read, $write; + my $new_pid = fork; if ( !$new_pid ) { - eval { - $self->_on_run_script( $instance_id, path($exd_dir), $device, - $verbose ); - }; - if ($@) { - warn $@; - } + open STDOUT, '>&', $write; + open STDERR, '>&', $write; + $self->_on_run_script( $instance_id, path($exd_dir), $device, + $verbose ); + STDOUT->flush; + $write->flush; + close $read; exit; } + close $write; + if ($verbose) { + my $log_getter_pid = fork; + if (!$log_getter_pid) { + $self->_log($instance_id, '### STARTED EXECUTION ###'); + while (my $line = <$read>) { + $self->_log($instance_id, $line); + } + $self->_log($instance_id, '### TERMINATED EXECUTION ###'); + exit; + } + } $self->_last_script_run_pid($new_pid); } @@ -268,23 +276,10 @@ sub device_hash_to_object( $self, $device_hash ) { sub _on_run_script( $self, $instance_id, $exd_dir, $device, $verbose = 1 ) { my $printer = Exd::Printer->new( device => $device ); - local $| = 1; my $exd = Exd::FileFormat->new( dir => $exd_dir ); - my ( $stdout, $stderr, $exit ) = capture { - eval { $exd->execute($printer); }; - if ($@) { - if ($verbose) { - $self->_log( $instance_id, $@ . "\n" ); - } - } - }; - - if ($verbose) { - $self->_log( $instance_id, $stdout . "\n" ); - $self->_log( $instance_id, $stderr . "\n" ); - } + $exd->execute($printer); } sub _monitor_run($self) { diff --git a/lib/Exd/Gui/FontGallery.pm b/lib/Exd/Gui/FontGallery.pm index 4a0085b..3330a56 100644 --- a/lib/Exd/Gui/FontGallery.pm +++ b/lib/Exd/Gui/FontGallery.pm @@ -8,15 +8,139 @@ use utf8; use Moo; -has app => (is => 'ro', required => 1); -has _window => ( is => 'rw', ); +use Pango; +use Exd::Fontconfig; + +has instance => ( is => 'ro', required => 1 ); +has _window => ( is => 'rw', ); +has _fontconfig => ( is => 'lazy' ); + +sub _build__fontconfig($self) { + return Exd::Fontconfig->new; +} sub start($self) { my $window = Gtk4::Window->new; $self->_window($window); - $window->set_default_size( 650, 600 ); - $window->set_title('Font gallery'); - $window->set_transient_for( $self->app->window ); + $self->_update_window; $window->present; } + +sub _update_window($self) { + my $window = $self->_window; + $window->set_default_size( 650, 600 ); + $window->set_title('Font gallery'); + $window->set_transient_for( $self->instance->window ); + my $box_window = Gtk4::Box->new( 'horizontal', 10 ); + my $box_system_fonts = Gtk4::Box->new( 'vertical', 10 ); + my $box_exd_fonts = Gtk4::Box->new( 'vertical', 10 ); + $self->_generate_system_fonts($box_system_fonts); + $self->_generate_exd_fonts($box_exd_fonts); + $box_window->append($box_system_fonts); + $box_window->append($box_exd_fonts); + $box_window->set_hexpand(1); + $box_window->set_vexpand(1); + $window->set_child($box_window); +} + +sub _generate_system_fonts( $self, $box ) { + my $label = Gtk4::Label->new('System fonts'); + my $font_window = Gtk4::ScrolledWindow->new; + $font_window->set_vexpand(1); + $font_window->set_hexpand(1); + $box->append($label); + my $box_fonts = Gtk4::Box->new( 'vertical', 10 ); + $box_fonts->set_vexpand(1); + $box_fonts->set_hexpand(1); + my %seen_fonts; + + for my $font ( $self->_fontconfig->list_fonts->@* ) { + if ( $seen_fonts{ $font->{family} . '#' . $font->{style} } ) { + next; + } + $seen_fonts{ $font->{family} . '#' . $font->{style} } = 1; + $self->_create_box_for_system_font( $font, $box_fonts ); + } + $font_window->set_child($box_fonts); + $box->append($font_window); + $box->set_hexpand(1); + $box->set_vexpand(1); +} + +sub _create_box_for_system_font( $self, $font, $box ) { + my $family = $font->{family}; + my $style = $font->{style}; + my $file = $font->{file}; + my $box_font = Gtk4::Box->new( 'vertical', 10 ); + my $font_label = Gtk4::Label->new("$family $style"); + my $dest = Pango::FontDescription->from_string("$family $style 10"); + my $attributes = Pango::AttrList->new; + my $attr = Pango::AttrFontDesc->new($dest); + $attributes->insert($attr); + $font_label->set_attributes($attributes); + $font_label->set_halign('start'); + $font_label->set_hexpand(1); + $box_font->append($font_label); + $box_font->set_hexpand(1); + my $box_buttons = Gtk4::Box->new( 'horizontal', 10 ); + my $button_copy = Gtk4::Button->new_with_label('Copy to file'); + $button_copy->signal_connect(clicked => sub { + $self->instance->file_format->fontconfig->import_font($file); + $self->_update_window; + }); + $box_buttons->set_margin_start(10); + $box_buttons->append($button_copy); + $box_font->append($box_buttons); + $box->append($box_font); +} + +sub _create_box_for_exd_font($self, $font, $box) { + my $family = $font->{family}; + my $style = $font->{style}; + my $box_font = Gtk4::Box->new( 'vertical', 10 ); + my $font_label = Gtk4::Label->new("$family $style"); + my $dest = Pango::FontDescription->from_string("$family $style 10"); + my $attributes = Pango::AttrList->new; + my $attr = Pango::AttrFontDesc->new($dest); + $attributes->insert($attr); + $font_label->set_attributes($attributes); + $font_label->set_halign('start'); + $font_label->set_hexpand(1); + $box_font->append($font_label); + $box_font->set_hexpand(1); + my $box_buttons = Gtk4::Box->new( 'horizontal', 10 ); + my $button_copy = Gtk4::Button->new_with_label('Copy to clipboard'); + $button_copy->signal_connect( clicked => sub { + $self->instance->set_clipboard("'$family $style'"); + $self->_window->close; + }); + $box_buttons->set_margin_start(10); + $box_buttons->append($button_copy); + $box_font->append($box_buttons); + $box->append($box_font); +} + +sub _generate_exd_fonts( $self, $box ) { + my $label = Gtk4::Label->new('File fonts'); + my $font_window = Gtk4::ScrolledWindow->new; + $font_window->set_vexpand(1); + $font_window->set_hexpand(1); + $box->append($label); + my $box_fonts = Gtk4::Box->new( 'vertical', 10 ); + $box_fonts->set_vexpand(1); + $box_fonts->set_hexpand(1); + my %seen_fonts; + + for my $font ( $self->instance->file_format->fontconfig->list_fonts->@* ) { + if ( $seen_fonts{ $font->{family} . '#' . $font->{style} } ) { + next; + } + $seen_fonts{ $font->{family} . '#' . $font->{style} } = 1; + $self->_create_box_for_exd_font( $font, $box_fonts ); + } + $font_window->set_child($box_fonts); + $box->append($font_window); + $box->set_hexpand(1); + $box->set_vexpand(1); +} 1; diff --git a/lib/Exd/Gui/Instance.pm b/lib/Exd/Gui/Instance.pm index 7aa4b40..1fef9bf 100644 --- a/lib/Exd/Gui/Instance.pm +++ b/lib/Exd/Gui/Instance.pm @@ -19,7 +19,7 @@ has window => ( is => 'rw' ); has _editor => ( is => 'rw', ); has _safe_to_exit => ( is => 'rw', default => sub { 1 } ); has device => ( is => 'rw' ); -has _file_format => ( +has file_format => ( is => 'rw', default => sub { my $tmp = Exd::FileFormat->new_tmp; @@ -62,6 +62,9 @@ sub start( $self, $exd_file ) { $win->set_show_menubar(1); $self->window($win); + if ( defined $exd_file ) { + $self->_open_file($exd_file); + } my $box_vertical = Gtk4::Box->new( 'vertical', 10 ); $self->_create_popover_menu($box_vertical); my $execute_log = Gtk4::TextView->new; @@ -148,9 +151,6 @@ sub start( $self, $exd_file ) { $execute_log_window->set_child($execute_log); $box_vertical->append($execute_log_window); $win->set_child($box_vertical); - if ( defined $exd_file ) { - $self->_open_file($exd_file); - } $win->present; } @@ -217,7 +217,7 @@ sub _populate_editor( $self, $box_editor_preview ) { my $end_iter = $buffer->get_end_iter; my $script = $buffer->get_text( $begin_iter, $end_iter, 0 ); - $self->_file_format->set_script($script); + $self->file_format->set_script($script); $self->_generate_preview_file(0); } ); @@ -243,8 +243,10 @@ sub _populate_editor_and_preview( $self, $box_vertical ) { sub _update_editor_buffer($self) { my $editor = $self->_editor; - my $buffer = $editor->get_buffer(); - $buffer->set_text( $self->_file_format->get_script, -1 ); + if (defined $editor) { + my $buffer = $editor->get_buffer(); + $buffer->set_text( $self->file_format->get_script, -1 ); + } } sub _populate_preview( $self, $box_editor_preview ) { @@ -294,7 +296,7 @@ sub _run_script( $self, $device = undef, $verbose = 1 ) { { type => 'run_script', data => { - exd_dir => '' . $self->_file_format->dir, + exd_dir => '' . $self->file_format->dir, device => $device->serialize, verbose => $verbose, } @@ -345,13 +347,13 @@ sub _on_preview($self) { } sub _open_fonts_gallery($self) { - my $font_gallery = Exd::Gui::FontGallery->new( app => $self ); + my $font_gallery = Exd::Gui::FontGallery->new( instance => $self ); $font_gallery->start; } sub _create_gallery_image( $self, $hash ) { my $picture = Gtk4::Picture->new; - my $image_file = $self->_file_format->get_image($hash); + my $image_file = $self->file_format->get_image($hash); $picture->set_property( 'width-request', 180 ); $picture->set_property( 'height-request', 180 ); $picture->set_content_fit('fill'); @@ -391,7 +393,7 @@ sub _open_gallery($self) { "$file is not png." ); return; } - $self->_file_format->add_png_image( + $self->file_format->add_png_image( path($file)->slurp_raw, $label ); $buffer->set_text( '', -1 ); $self->_update_gallery_images( $window, @@ -416,7 +418,7 @@ sub _open_gallery($self) { } sub _update_gallery_images( $self, $window, $images_scroll ) { - my %image_hashes_to_label = $self->_file_format->image_hashes_to_label->%*; + my %image_hashes_to_label = $self->file_format->image_hashes_to_label->%*; my $gallery_box = Gtk4::Box->new( 'vertical', 10 ); my $i = 0; my $row_image; @@ -432,14 +434,14 @@ sub _update_gallery_images( $self, $window, $images_scroll ) { $copy->signal_connect( 'clicked', sub { - $self->_set_clipboard("\$exd->get_image('$hash')"); + $self->set_clipboard("\$exd->get_image('$hash')"); $window->close; } ); $copy_gd->signal_connect( 'clicked', sub { - $self->_set_clipboard("\$exd->get_image_gd('$hash')"); + $self->set_clipboard("\$exd->get_image_gd('$hash')"); $window->close; } ); @@ -463,7 +465,7 @@ sub _update_gallery_images( $self, $window, $images_scroll ) { $copy->signal_connect( 'clicked', sub { - $self->_set_clipboard( + $self->set_clipboard( "\$exd->get_image_from_label('$label')"); $window->close; } @@ -471,7 +473,7 @@ sub _update_gallery_images( $self, $window, $images_scroll ) { $copy_gd->signal_connect( 'clicked', sub { - $self->_set_clipboard( + $self->set_clipboard( "\$exd->get_image_gd_from_label('$label')"); $window->close; } @@ -497,7 +499,7 @@ sub _get_row_image( $self, $i, $gallery_box, $row_image ) { return $row_image; } -sub _set_clipboard( $self, $text ) { +sub set_clipboard( $self, $text ) { my $display = Gdk::Display::get_default(); my $clipboard = $display->get_clipboard; my $wrapper = @@ -506,7 +508,7 @@ sub _set_clipboard( $self, $text ) { } sub _save_as_action($self) { - my $file_format = $self->_file_format; + my $file_format = $self->file_format; my $dialog = Gtk4::FileDialog->new; $dialog->set_initial_name('project.exd'); $dialog->save( @@ -523,7 +525,7 @@ sub _save_as_action($self) { } sub _open_action($self) { - my $file_format = $self->_file_format; + my $file_format = $self->file_format; my $dialog = Gtk4::FileDialog->new; $dialog->set_initial_name('project.exd'); my $window = $self->window; @@ -561,13 +563,13 @@ sub _open_file( $self, $file ) { my $window = $self->window; $window->set_title( "Exd (Thermal Printer) " . path( $self->_save_path )->basename ); - $self->_file_format( Exd::FileFormat->from_zip_file($file) ); + $self->file_format( Exd::FileFormat->from_zip_file($file) ); $self->_update_editor_buffer; } sub _save_action($self) { my $instance_id = $self->instance_id; - my $file_format = $self->_file_format; + my $file_format = $self->file_format; my $dialog = Gtk4::FileDialog->new; if ( defined $self->_save_path ) { $self->_send_save_request( $file_format->dir . '', $self->_save_path );