package GridFiller::Result::Pango; use Moose; use namespace::autoclean; use GridFiller::Constants ':directions'; use MooseX::Types::Moose qw(Int); use GridFiller::Types qw(FontDescrT FontOptT); use Cairo; use Pango; use Carp; use POSIX 'ceil'; extends 'GridFiller::Result'; with 'MooseX::Log::Log4perl'; has cell_size => ( isa => Int, is => 'rw', default => 20, ); has border => ( isa => Int, is => 'rw', default => 2, ); has _width => ( isa => Int, is => 'ro', lazy_build => 1, clearer => '_reset_w', ); sub _build__width { my ($self) = @_; return $self->cell_size * (2*$self->border + @{$self->source_grid->[0]}); } has _height => ( isa => Int, is => 'ro', lazy_build => 1, clearer => '_reset_h', ); sub _build__height { my ($self) = @_; return $self->cell_size * (2*$self->border + @{$self->source_grid}); } has _cairo_s => ( is => 'ro', lazy_build => 1, clearer => '_reset_s', ); sub _build__cairo_s { my ($self) = @_; my $cs = Cairo::ImageSurface->create( 'argb32', $self->_width, $self->_height, ); return $cs; }; has _cairo_c => ( is => 'ro', lazy_build => 1, clearer => '_reset_c', ); sub _build__cairo_c { my ($self) = @_; my $cr = Cairo::Context->create($self->_cairo_s); $self->_put_squares($cr); return $cr; } has font_description => ( is => 'ro', isa => FontDescrT, coerce => 1, lazy_build => 1, trigger => \&_adjust_font_description, ); sub _build_font_description { my ($self)=@_; my $fd = Pango::FontDescription->from_string('Sans 10'); return $self->_adjust_font_description($fd); } sub _adjust_font_description { my ($self,$fd) = @_; $fd->set_absolute_size($self->cell_size * Pango->scale * 0.8); return $fd; } has font_options => ( is => 'ro', isa => FontOptT, lazy_build => 1, ); sub _build_font_options { my ($self) = @_; my $fo = Cairo::FontOptions->create(); $fo->set_hint_style('none'); return $fo; } sub _put_squares { my ($self,$cr) = @_; $self->log->debug(sprintf('putting squares (%dx%d)',$self->_width,$self->_height)); my $size = $self->cell_size; $cr->save; $cr->rectangle(0,0,$self->_width,$self->_height); $cr->set_source_rgb(1,1,1); $cr->fill; my $y=$self->border*$size; for my $row (@{$self->source_grid}) { my $x=$self->border*$size; for my $cell (@$row) { if ($cell eq '*') { $cr->rectangle($x,$y,$size,$size); $cr->set_source_rgb(0,0,0); $cr->fill; } $x+=$size; } $y+=$size; } $cr->restore; return; } sub layout_for_string { my ($self,$string,$dir) = @_; my $p = Pango::Cairo::create_layout($self->_cairo_c); Pango::Cairo::Context::set_font_options($p->get_context,$self->font_options); Pango::Cairo::update_context($self->_cairo_c,,$p->get_context); if ($dir == $VERTICAL) { $p->get_context->set_base_gravity('east'); } $p->context_changed; $p->set_font_description($self->font_description); $p->set_text($string); return $p; } sub length_closure { my ($self) = @_; return sub { $self->string_width(@_) } } sub string_extents { my ($self,$string,$dir) = @_; my $p = $self->layout_for_string($string,$dir); my ($w,$h) = $p->get_pixel_size; return ceil($w / $self->cell_size), ceil($h / $self->cell_size); } sub string_width { my ($self,$string,$dir) = @_; return +($self->string_extents($string,$dir))[0]; } sub string_height { my ($self,$string,$dir) = @_; return +($self->string_extents($string,$dir))[1]; } sub _center_adj { my ($self,$p,$space) = @_; my ($w,$h) = $p->get_pixel_size; my $size = $self->cell_size; my $w_rounded = ceil($w/$size)*$size; my $w_adj = ceil(($space*$size+$w_rounded-$w)/2); my $h_adj = ceil(($size - $h)/2); return $w_adj,$h_adj; } sub colour_for_string { my ($self, $word, $x, $y, $dir) = @_; if ($self->source_grid->[$y][$x] eq '*') { return (1,1,1); } else { return (0,0,0); } } my $PI=3.1415926; sub place_word_at { my ($self, $word, $space, $x, $y, $dir) = @_; $self->log->debug("Placing $word at ${x}:${y} ($dir)"); my $p = $self->layout_for_string($word,$dir); my @colour = $self->colour_for_string($word,$x,$y,$dir); my $size = $self->cell_size; my $cr = $self->_cairo_c; $x+=$self->border;$y+=$self->border; $x*=$size;$y*=$size; my ($w_adjustment,$h_adjustment) = $self->_center_adj($p,$space); $cr->save; if ($dir == $HORIZONTAL) { $x+=$w_adjustment; $y+=$h_adjustment; $cr->move_to($x,$y); } else { $y+=$w_adjustment; $x-=$h_adjustment; $cr->move_to($x+$size,$y); $cr->rotate($PI/2); } Pango::Cairo::update_layout($cr,$p); $cr->set_source_rgb(@colour); Pango::Cairo::show_layout($cr,$p); $cr->restore; return; } sub as_png { my ($self) = @_; $self->_cairo_c->show_page; my $buffer; $self->_cairo_s->write_to_png_stream(sub{$buffer.=$_[1]}); return $buffer; } sub save_png { my ($self,$filename) = @_; $self->_cairo_c->show_page; $self->_cairo_s->write_to_png($filename); return; } sub to_string {} after reset => sub { my ($self) = @_; $self->_reset_w; $self->_reset_h; $self->_reset_c; $self->_reset_s; }; 1;