The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

# Rotated Text
#
# This demo shows how to use GDK and Pango to draw rotated and transformed
# text. The use of GdkPangoRenderer in this example is a somewhat advanced
# technique; most applications can simply use gdk_draw_layout(). We use
# it here mostly because that allows us to work in user coordinates - that is,
# coordinates prior to the application of the transformation matrix, rather
# than device coordinates.
#
# As of GTK+-2.6, the ability to draw transformed and anti-aliased graphics
# as shown in this example is only present for text. With GTK+-2.8, a new
# graphics system called "Cairo" will be introduced that provides these
# capabilities and many more for all types of graphics.
#

package rotated_text;

use strict;
use Glib qw(TRUE FALSE);
use Gtk2;
use Gtk2::Pango; # constants

my $window = undef;

sub min { $_[0] < $_[1] ? $_[0] : $_[1] }

sub rotated_text_expose_event {
  my ($widget, $event) = @_;

  use constant RADIUS => 150;
  use constant N_WORDS => 10;
  use constant FONT => "Sans Bold 27";
  use constant M_PI => 3.141526;

  my $matrix = Gtk2::Pango::Matrix->new; # all defaults

  my $width = $widget->allocation->width;
  my $height = $widget->allocation->height;

  # Get the default renderer for the screen, and set it up for drawing
  my $renderer = Gtk2::Gdk::PangoRenderer->get_default ($widget->get_screen);
  $renderer->set_drawable ($widget->window);
  $renderer->set_gc ($widget->style->black_gc);

  # Set up a transformation matrix so that the user space coordinates for
  # the centered square where we draw are [-RADIUS, RADIUS], [-RADIUS, RADIUS]
  # We first center, then change the scale
  my $device_radius = min ($width, $height) / 2.;
  $matrix->translate ($device_radius + ($width - 2 * $device_radius) / 2,
		      $device_radius + ($height - 2 * $device_radius) / 2);
  $matrix->scale ($device_radius / RADIUS, $device_radius / RADIUS);

  # Create a PangoLayout, set the font and text
  my $context = $widget->create_pango_context;
  my $layout = Gtk2::Pango::Layout->new ($context);
  $layout->set_text ("Text");
  my $desc = Gtk2::Pango::FontDescription->from_string (FONT);
  $layout->set_font_description ($desc);

  # Draw the layout N_WORDS times in a circle
  foreach my $i (0..N_WORDS-1) {
      my $rotated_matrix = $matrix->copy;
      my $angle = (360. * $i) / N_WORDS;

      # Gradient from red at angle == 60 to blue at angle == 300
      my $red   = 65535 * (1 + cos (($angle - 60) * M_PI / 180.)) / 2;
      my $green = 0;
      my $blue  = 65535  - $red;
      my $color = Gtk2::Gdk::Color->new ($red, $green, $blue);

      $renderer->set_override_color ('foreground', $color);

      $rotated_matrix->rotate ($angle);

      $context->set_matrix ($rotated_matrix);

      # Inform Pango to re-layout the text with the new transformation matrix
      $layout->context_changed;

      my ($width, $height) = $layout->get_size;
      $renderer->draw_layout ($layout, - $width / 2, - RADIUS * PANGO_SCALE);
  }

  # Clean up default renderer, since it is shared
  $renderer->set_override_color ('foreground', undef);
  $renderer->set_drawable (undef);
  $renderer->set_gc (undef);

  return FALSE;
}

sub do {
  my $do_widget = shift;

  if (!$window) {
      my $white = Gtk2::Gdk::Color->new (0xffff, 0xffff, 0xffff);

      $window = Gtk2::Window->new;
      $window->set_screen ($do_widget->get_screen);
      $window->set_title ("Rotated Text");

      $window->signal_connect (destroy => sub { $window = undef; });

      my $drawing_area = Gtk2::DrawingArea->new;
      $window->add ($drawing_area);

      # This overrides the background color from the theme
      $drawing_area->modify_bg ('normal', $white);

      $drawing_area->signal_connect
	      	(expose_event => \&rotated_text_expose_event);

      $window->set_default_size (2 * RADIUS, 2 * RADIUS);

  }

  if (! $window->visible) {
      $window->show_all;
  } else {
      $window->destroy;
      $window = undef;
  }

  return $window;
}

1;