#!/usr/bin/perl -w # Generate PNG files that shows the input text being rotated 90 # degrees clockwise. use strict; use File::Temp; use constant PI => atan2(0, -1); use constant FRAMES => 20; use constant PAGE_OFFSET_X => 306; use constant PAGE_OFFSET_Y => 396; use constant CANVAS_WIDTH => 400; use constant FONT_SCALE_FACTOR => 1.2; use constant CROP_X => 275; use constant CROP_Y => 650; use constant CROP_S => 2000; use constant RESIZE_WIDTH => 800; # Load input if( $#ARGV != 1 ) { die "$0 \n"; } my ($input_text, $output_prefix) = @ARGV; open my $infile, "<$input_text" or die $!; my @lines = <$infile>; close $infile; my $width = 0; foreach (@lines) { chomp; $width = length($_) if $width < length($_); } my $height = scalar @lines; # Compute offsets my $cx = $width / 2; my $cy = $height / 2; my $long_edge = $width > $height ? $width : $height; my $spacing = CANVAS_WIDTH / $long_edge; # Generate frames my $ps_file = tmpnam(); for(my $frame = 0; $frame < FRAMES; $frame++) { # Page header my $scale = $spacing * FONT_SCALE_FACTOR; my $ps = '%!PS' . "\n/Courier-Bold findfont $scale scalefont setfont\n"; # Compute angle, using a cubic to make the rotation accelerate # at the beginning and decelerate at the end. # # f(x) = -2x^3 + 3x^2 # f'(x) = -6x^2 + 6x # -> f(0) = 0 # -> f(1) = 1 # -> f'(0) = 0 # -> f'(1) = 0 my $ax = $frame / FRAMES; my $ay = -2 * ($ax * $ax * $ax) + 3 * ($ax * $ax); my $angle = $ay * -PI / 2; # Transpose and draw each character. # # Note that PostScript renders each character at their bottom left # corners, but we really want to render them at the center so that # the rotated characters will be aligned between animation segments. # This is achieved by adjusting the character offsets with # "$spacing/2" before the rotation is done so that we rotate from # the center, minus another "$spacing/2" after rotation so that we # render characters at their centers. for(my $y = 0; $y < $height; $y++) { my $dy = ($cy - $y) * $spacing - $spacing / 2; for(my $x = 0; $x < $width; $x++) { # No need to render spaces next if $x >= length($lines[$y]); my $c = substr($lines[$y], $x, 1); next if $c eq " "; # Escape special characters for PostScript if( $c =~ /[()\\]/ ) { $c = '\\' . $c; } my $dx = ($x - $cx) * $spacing + $spacing / 2; my $px = cos($angle) * $dx - sin($angle) * $dy + PAGE_OFFSET_X - $spacing / 2; my $py = sin($angle) * $dx + cos($angle) * $dy + PAGE_OFFSET_Y + $spacing / 2; $ps .= "$px $py moveto ($c) show\n"; } } # Page footer $ps .= "showpage\nquit\n"; # Write PostScript file open my $outfile, ">$ps_file" or die $!; print $outfile $ps; close $outfile; # Convert PostScript to PPM my $cmd = "gs -q -dBATCH -dNOPAUSE -sDEVICE=ppmraw " . "-r300x300 -dTextAlphaBits=4 -dGraphicsAlphaBits=4 " . "-sOutputFile=${output_prefix}_tmp.ppm $ps_file"; system $cmd; # Crop and resize output $cmd = sprintf 'pamcut -left %d -top %d -width %d -height %d ' . '%s_tmp.ppm ' . ' | pnminvert ' . ' | pamscale -xsize %d ' . ' | pnmtopng -compression 9 > %s%04d.png', CROP_X, CROP_Y, CROP_S, CROP_S, $output_prefix, RESIZE_WIDTH, $output_prefix, $frame; system $cmd; } unlink "${output_prefix}_tmp.ppm"; unlink $ps_file;