| Prima documentation | Contained in the Prima distribution. |
Prima::PS::Drawable - PostScript interface to Prima::Drawable
use Prima;
use Prima::PS::Drawable;
my $x = Prima::PS::Drawable-> create( onSpool => sub {
open F, ">> ./test.ps";
print F $_[1];
close F;
});
die "error:$@" unless $x-> begin_doc;
$x-> font-> size( 30);
$x-> text_out( "hello!", 100, 100);
$x-> end_doc;
Realizes the Prima library interface to PostScript level 2 document language. The module is designed to be compliant with Prima::Drawable interface. All properties' behavior is as same as Prima::Drawable's, except those described below.
Can be set while object is in normal stage - cannot be changed if document is opened. Applies to fillPattern realization and general pixel-to-point and vice versa calculations
- ::region is not realized ( yet?)
amount of copies that PS interpreter should print
could be 0 or 1
physical page dimension, in points
non-printable page area, an array of 4 integers: left, bottom, right and top margins in points.
if 1, a 90 degrees rotated document layout is assumed
along with Prima::Drawable::translate provide PS-specific transformation matrix manipulations. ::rotate is number, measured in degrees, counter-clockwise. ::scale is array of two numbers, respectively x- and y-scale. 1 is 100%, 2 is 200% etc.
1 by default; optimizes greatly text operations, but takes the risk that a character could be drawn incorrectly or not drawn at all - this behavior depends on a particular PS interpreter.
If 1, the system fonts, available from Prima::Application
interfaces can not be used. It is designed for
developers and the outside-of-Prima applications that wish to
use PS generation module without graphics. If 1, ::useDeviceFonts
is set to 1 automatically.
Default value is 0
Can be called for direct PostScript code injection. Example:
$x-> emit('0.314159 setgray');
$x-> bar( 10, 10, 20, 20);
Helpers for translation from pixel to points and vice versa.
Wrappers for PS outline that is expected to be filled or stroked. Apply colors, line and fill styles if necessary.
Prima::PS::Drawable is not responsible for output of generated document, it just calls ::spool when document is closed through ::end_doc. By default just skips data. Prima::PS::Printer handles spooling logic.
Returns Prima::Application::font plus those that defined into Prima::PS::Fonts module.
| Prima documentation | Contained in the Prima distribution. |
# # Copyright (c) 1997-2002 The Protein Laboratory, University of Copenhagen # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # Created by Dmitry Karasik <dk@plab.ku.dk> # $Id$ # package Prima::PS::Drawable; use vars qw(@ISA); @ISA = qw(Prima::Drawable); use strict; use Prima; use Prima::PS::Fonts; use Prima::PS::Encodings; use Encode; { my %RNT = ( %{Prima::Drawable-> notification_types()}, Spool => nt::Action, ); sub notification_types { return \%RNT; } } sub profile_default { my $def = $_[ 0]-> SUPER::profile_default; my %prf = ( copies => 1, font => { %{$def-> {font}}, name => $Prima::PS::Fonts::defaultFontName, }, grayscale => 0, pageDevice => undef, pageSize => [ 598, 845], pageMargins => [ 12, 12, 12, 12], resolution => [ 300, 300], reversed => 0, rotate => 0, scale => [ 1, 1], textOutBaseline => 1, useDeviceFonts => 1, useDeviceFontsOnly => 0, ); @$def{keys %prf} = values %prf; return $def; } sub profile_check_in { my ( $self, $p, $default) = @_; Prima::Component::profile_check_in( $self, $p, $default); $p-> { font} = {} unless exists $p-> { font}; $p-> { font} = Prima::Drawable-> font_match( $p-> { font}, $default-> { font}, 0); } sub init { my $self = shift; $self-> {clipRect} = [0,0,0,0]; $self-> {pageSize} = [0,0]; $self-> {pageMargins} = [0,0,0,0]; $self-> {resolution} = [72,72]; $self-> {scale} = [ 1, 1]; $self-> {copies} = 1; $self-> {rotate} = 1; $self-> {font} = {}; $self-> {useDeviceFonts} = 1; my %profile = $self-> SUPER::init(@_); $self-> $_( $profile{$_}) for qw( grayscale copies pageDevice useDeviceFonts rotate reversed useDeviceFontsOnly); $self-> $_( @{$profile{$_}}) for qw( pageSize pageMargins resolution scale); $self-> {localeEncoding} = []; return %profile; } # internal routines sub cmd_rgb { my ( $r, $g, $b) = ( int((($_[1] & 0xff0000) >> 16) * 100 / 256 + 0.5) / 100, int((($_[1] & 0xff00) >> 8) * 100 / 256 + 0.5) / 100, int(($_[1] & 0xff)*100/256 + 0.5) / 100); unless ( $_[0]-> {grayscale}) { return "$r $g $b A"; } else { my $i = int( 100 * ( 0.31 * $r + 0.5 * $g + 0.18 * $b) + 0.5) / 100; return "$i G"; } } sub emit { my $self = $_[0]; return 0 unless $self-> {canDraw}; $self-> {psData} .= $_[1] . "\n"; if ( length($self-> {psData}) > 10240) { $self-> abort_doc unless $self-> spool( $self-> {psData}); $self-> {psData} = ''; } return 1; } sub save_state { my $self = $_[0]; $self-> {saveState} = {}; $self-> set_font( $self-> get_font) if $self-> {useDeviceFonts}; $self-> {saveState}-> {$_} = $self-> $_() for qw( color backColor fillPattern lineEnd linePattern lineWidth rop rop2 textOpaque textOutBaseline font lineJoin fillWinding ); $self-> {saveState}-> {$_} = [$self-> $_()] for qw( translate clipRect ); $self-> {saveState}-> {localeEncoding} = $self-> {useDeviceFonts} ? [ @{$self-> {localeEncoding}}] : []; } sub restore_state { my $self = $_[0]; for ( qw( color backColor fillPattern lineEnd linePattern lineWidth rop rop2 textOpaque textOutBaseline font lineJoin fillWinding)) { $self-> $_( $self-> {saveState}-> {$_}); } for ( qw( translate clipRect)) { $self-> $_( @{$self-> {saveState}-> {$_}}); } $self-> {localeEncoding} = $self-> {saveState}-> {localeEncoding}; } sub pixel2point { my $self = shift; my $i; my @res; for ( $i = 0; $i < scalar @_; $i+=2) { my ( $x, $y) = @_[$i,$i+1]; push( @res, int( $x * 7227 / $self-> {resolution}-> [0] + 0.5) / 100 ); push( @res, int( $y * 7227 / $self-> {resolution}-> [1] + 0.5) / 100 ) if defined $y; } return @res; } sub point2pixel { my $self = shift; my $i; my @res; for ( $i = 0; $i < scalar @_; $i+=2) { my ( $x, $y) = @_[$i,$i+1]; push( @res, $x * $self-> {resolution}-> [0] / 72.27); push( @res, $y * $self-> {resolution}-> [1] / 72.27) if defined $y; } return @res; } sub change_transform { return if $_[0]-> {delay}; my @tp = $_[0]-> translate; my @cr = $_[0]-> clipRect; my @sc = $_[0]-> scale; my $ro = $_[0]-> rotate; $cr[2] -= $cr[0]; $cr[3] -= $cr[1]; my $doClip = grep { $_ != 0 } @cr; my $doTR = grep { $_ != 0 } @tp; my $doSC = grep { $_ != 0 } @sc; if ( !$doClip && !$doTR && !$doSC && !$ro) { $_[0]-> emit(':') if $_[1]; return; } @cr = $_[0]-> pixel2point( @cr); @tp = $_[0]-> pixel2point( @tp); my $mcr2 = -$cr[2]; $_[0]-> emit(';') unless $_[1]; $_[0]-> emit(':'); $_[0]-> emit(<<CLIP) if $doClip; N $cr[0] $cr[1] M 0 $cr[2] L $cr[3] 0 L 0 $mcr2 L X C CLIP $_[0]-> emit("@tp T") if $doTR; $_[0]-> emit("@sc Z") if $doSC; $_[0]-> emit("$ro R") if $ro != 0; $_[0]-> {changed}-> {$_} = 1 for qw(fill linePattern lineWidth lineJoin lineEnd font); } sub fill { my ( $self, $start, $code, $end) = @_; my ( $r1, $r2) = ( $self-> rop, $self-> rop2); return if $r1 == rop::NoOper && $r1 == rop::NoOper; $self-> emit( $start) if length $start; if ( $r2 != rop::NoOper && $self-> {fpType} ne 'F') { my $bk = ( $r2 == rop::Blackness) ? 0 : ( $r2 == rop::Whiteness) ? 0xffffff : $self-> backColor; $self-> {changed}-> {fill} = 1; $self-> emit( $self-> cmd_rgb( $bk)); $self-> emit( $code); } if ( $r1 != rop::NoOper && $self-> {fpType} ne 'B') { my $c = ( $r1 == rop::Blackness) ? 0 : ( $r1 == rop::Whiteness) ? 0xffffff : $self-> color; if ($self-> {changed}-> {fill}) { if ( $self-> {fpType} eq 'F') { $self-> emit( $self-> cmd_rgb( $c)); } else { my ( $r, $g, $b) = ( int((($c & 0xff0000) >> 16) * 100 / 256 + 0.5) / 100, int((($c & 0xff00) >> 8) * 100 / 256 + 0.5) / 100, int(($c & 0xff)*100/256 + 0.5) / 100); if ( $self-> {grayscale}) { my $i = int( 100 * ( 0.31 * $r + 0.5 * $g + 0.18 * $b) + 0.5) / 100; $self-> emit(<<GRAYPAT); [\/Pattern \/DeviceGray] SS $i Pat_$self->{fpType} SC GRAYPAT } else { $self-> emit(<<RGBPAT); [\/Pattern \/DeviceRGB] SS $r $g $b Pat_$self->{fpType} SC RGBPAT } } $self-> {changed}-> {fill} = 0; } $self-> emit( $code); } $self-> emit( $end) if length $end; } sub stroke { my ( $self, $start, $code, $end) = @_; my ( $r1, $r2) = ( $self-> rop, $self-> rop2); my $lp = $self-> linePattern; return if $r1 == rop::NoOper && $r2 == rop::NoOper; $self-> emit( $start) if length $start; if ( $r2 != rop::NoOper && $lp ne lp::Solid ) { my $bk = ( $r2 == rop::Blackness) ? 0 : ( $r2 == rop::Whiteness) ? 0xffffff : $self-> backColor; $self-> {changed}-> {linePattern} = 1; $self-> {changed}-> {fill} = 1; $self-> emit( $self-> cmd_rgb( $bk)); $self-> emit( $code); } if ( $r1 != rop::NoOper && length( $lp)) { my $fk = ( $r1 == rop::Blackness) ? 0 : ( $r1 == rop::Whiteness) ? 0xffffff : $self-> color; if ( $self-> {changed}-> {linePattern}) { if ( length( $lp) == 1) { $self-> emit('[] 0 SD'); } else { my @x = split('', $lp); push( @x, 0) if scalar(@x) % 1; @x = map { ord($_) } @x; $self-> emit("[@x] 0 SD"); } $self-> {changed}-> {linePattern} = 0; } if ( $self-> {changed}-> {lineWidth}) { my ($lw) = $self-> pixel2point($self-> lineWidth); $self-> emit( $lw . ' SW'); $self-> {changed}-> {lineWidth} = 0; } if ( $self-> {changed}-> {lineEnd}) { my $le = $self-> lineEnd; my $id = ( $le == le::Round) ? 1 : (( $le == le::Square) ? 2 : 0); $self-> emit( "$id SL"); } if ( $self-> {changed}-> {lineJoin}) { my $lj = $self-> lineJoin; my $id = ( $lj == lj::Round) ? 1 : (( $lj == lj::Bevel) ? 2 : 0); $self-> emit( "$id SJ"); } if ( $self-> {changed}-> {fill}) { $self-> emit( $self-> cmd_rgb( $fk)); } $self-> emit( $code); } $self-> emit( $end) if length $end; } # Prima::Printer interface sub begin_doc { my ( $self, $docName) = @_; return 0 if $self-> get_paint_state; $self-> {psData} = ''; $self-> {canDraw} = 1; $docName = $::application ? $::application-> name : "Prima::PS::Drawable" unless defined $docName; my $data = scalar localtime; my @b2 = ( $self-> {pageSize}-> [0] - $self-> {pageMargins}-> [2], $self-> {pageSize}-> [1] - $self-> {pageMargins}-> [3] ); $self-> {fpHash} = {}; $self-> {pages} = 1; my ($x,$y) = ( $self-> {pageSize}-> [0] - $self-> {pageMargins}-> [0] - $self-> {pageMargins}-> [2], $self-> {pageSize}-> [1] - $self-> {pageMargins}-> [1] - $self-> {pageMargins}-> [3] ); my $extras = ''; my $setup = ''; my %pd = defined( $self-> {pageDevice}) ? %{$self-> {pageDevice}} : (); if ( $self-> {copies} > 1) { $pd{NumCopies} = $self-> {copies}; $extras .= "\%\%Requirements: numcopies($self->{copies})\n"; } if ( scalar keys %pd) { my $jd = join( "\n", map { "/$_ $pd{$_}"} keys %pd); $setup .= <<NUMPAGES; %%BeginFeature << $jd >> SPD %%EndFeature NUMPAGES } $self-> {localeData} = {}; $self-> {fontLocaleData} = {}; $self-> emit( <<PSHEADER); %!PS-Adobe-2.0 %%Title: $docName %%Creator: Prima::PS::Drawable %%CreationDate: $data %%Pages: (atend) %%BoundingBox: @{$self->{pageMargins}}[0,1] @b2 $extras %%LanguageLevel: 2 %%DocumentNeededFonts: (atend) %%DocumentSuppliedFonts: (atend) %%EndComments /d/def load def/,/load load d/~/exch , d/S/show , d/:/gsave , d/;/grestore , d/N/newpath , d/M/moveto , d/L/rlineto , d/X/closepath , d/C/clip , d/T/translate , d/R/rotate , d/P/showpage , d/Z/scale , d/I/imagemask , d/@/dup , d/G/setgray , d/A/setrgbcolor , d/l/lineto , d/F/fill , d/FF/findfont , d/XF/scalefont , d/SF/setfont , d/O/stroke , d/SD/setdash , d/SL/setlinecap , d/SW/setlinewidth , d/SJ/setlinejoin , d/E/eofill , d/SS/setcolorspace , d/SC/setcolor , d/SM/setmatrix , d/SPD/setpagedevice , d/SP/setpattern , d/CP/currentpoint , d/MX/matrix , d/MP/makepattern , d/b/begin , d/e/end , d/t/true , d/f/false , d/?/ifelse , d/a/arc , d/dummy/_dummy %%BeginSetup $setup %%EndSetup %%Page: 1 1 PSHEADER $self-> {pagePrefix} = <<PREFIX; @{$self->{pageMargins}}[0,1] T N 0 0 M 0 $y L $x 0 L 0 -$y L X C PREFIX $self-> {pagePrefix} .= "0 0 M 90 R 0 -$x T\n" if $self-> {reversed}; $self-> {changed} = { map { $_ => 0 } qw( fill lineEnd linePattern lineWidth lineJoin font)}; $self-> {docFontMap} = {}; $self-> SUPER::begin_paint; $self-> save_state; $self-> {delay} = 1; $self-> restore_state; $self-> {delay} = 0; $self-> emit( $self-> {pagePrefix}); $self-> change_transform( 1); $self-> {changed}-> {linePattern} = 0; return 1; } sub abort_doc { my $self = $_[0]; return unless $self-> {canDraw}; $self-> {canDraw} = 0; $self-> SUPER::end_paint; $self-> restore_state; delete $self-> {$_} for qw (saveState localeData psData changed fontLocaleData pagePrefix); $self-> {plate}-> destroy, $self-> {plate} = undef if $self-> {plate}; } sub end_doc { my $self = $_[0]; return 0 unless $self-> {canDraw}; $self-> emit(<<PSFOOTER); ; P %%Trailer %%DocumentNeededFonts: %%DocumentSuppliedFonts: %%Pages: $_[0]->{pages} %%EOF PSFOOTER # if ( $self-> {locale}) { # my @z = map { '/' . $_ } keys %{$self-> {docFontMap}}; # my $xcl = "/FontList [@z] d\n"; # } my $ret = $self-> spool( $self-> {psData}); $self-> {canDraw} = 0; $self-> SUPER::end_paint; $self-> restore_state; delete $self-> {$_} for qw (saveState localeData changed fontLocaleData psData pagePrefix); $self-> {plate}-> destroy, $self-> {plate} = undef if $self-> {plate}; return $ret; } # Prima::Drawable interface sub begin_paint { return $_[0]-> begin_doc; } sub end_paint { $_[0]-> abort_doc; } sub begin_paint_info { my $self = $_[0]; return 0 if $self-> get_paint_state; my $ok = $self-> SUPER::begin_paint_info; return 0 unless $ok; $self-> save_state; } sub end_paint_info { my $self = $_[0]; return if $self-> get_paint_state != 2; $self-> SUPER::end_paint_info; $self-> restore_state; } sub new_page { return 0 unless $_[0]-> {canDraw}; my $self = $_[0]; $self-> {pages}++; $self-> emit("; P\n%%Page: $self->{pages} $self->{pages}\n"); $self-> $_( @{$self-> {saveState}-> {$_}}) for qw( translate clipRect); $self-> change_transform(1); $self-> emit( $self-> {pagePrefix}); return 1; } sub pages { $_[0]-> {pages} } sub spool { shift-> notify( 'Spool', @_); return 1; # my $p = $_[1]; # open F, ">> ./test.ps"; # print F $p; # close F; } # properties sub color { return $_[0]-> SUPER::color unless $#_; $_[0]-> SUPER::color( $_[1]); return unless $_[0]-> {canDraw}; $_[0]-> {changed}-> {fill} = 1; } sub fillPattern { return $_[0]-> SUPER::fillPattern unless $#_; $_[0]-> SUPER::fillPattern( $_[1]); return unless $_[0]-> {canDraw}; my $self = $_[0]; my @fp = @{$self-> SUPER::fillPattern}; my $solidBack = ! grep { $_ != 0 } @fp; my $solidFore = ! grep { $_ != 0xff } @fp; my $fpid; my @scaleto = $self-> pixel2point( 8, 8); if ( !$solidBack && !$solidFore) { $fpid = join( '', map { sprintf("%02x", $_)} @fp); unless ( exists $self-> {fpHash}-> {$fpid}) { $self-> emit( <<PATTERNDEF); << \/PatternType 1 \% Tiling pattern \/PaintType 2 \% Uncolored \/TilingType 1 \/BBox [ 0 0 @scaleto] \/XStep $scaleto[0] \/YStep $scaleto[1] \/PaintProc { b : @scaleto Z 8 8 t [8 0 0 8 0 0] < $fpid > I ; e } bind >> MX MP \/Pat_$fpid ~ d PATTERNDEF $self-> {fpHash}-> {$fpid} = 1; } } $self-> {fpType} = $solidBack ? 'B' : ( $solidFore ? 'F' : $fpid); $self-> {changed}-> {fill} = 1; } sub lineEnd { return $_[0]-> SUPER::lineEnd unless $#_; $_[0]-> SUPER::lineEnd($_[1]); return unless $_[0]-> {canDraw}; $_[0]-> {changed}-> {lineEnd} = 1; } sub lineJoin { return $_[0]-> SUPER::lineJoin unless $#_; $_[0]-> SUPER::lineJoin($_[1]); return unless $_[0]-> {canDraw}; $_[0]-> {changed}-> {lineJoin} = 1; } sub fillWinding { return $_[0]-> SUPER::fillWinding unless $#_; $_[0]-> SUPER::fillWinding($_[1]); } sub linePattern { return $_[0]-> SUPER::linePattern unless $#_; $_[0]-> SUPER::linePattern($_[1]); return unless $_[0]-> {canDraw}; $_[0]-> {changed}-> {linePattern} = 1; } sub lineWidth { return $_[0]-> SUPER::lineWidth unless $#_; $_[0]-> SUPER::lineWidth($_[1]); return unless $_[0]-> {canDraw}; $_[0]-> {changed}-> {lineWidth} = 1; } sub rop { return $_[0]-> SUPER::rop unless $#_; my ( $self, $rop) = @_; $rop = rop::CopyPut if $rop != rop::Blackness || $rop != rop::Whiteness || $rop != rop::NoOper; $self-> SUPER::rop( $rop); } sub rop2 { return $_[0]-> SUPER::rop2 unless $#_; my ( $self, $rop) = @_; $rop = rop::CopyPut if $rop != rop::Blackness || $rop != rop::Whiteness || $rop != rop::NoOper; $self-> SUPER::rop2( $rop); } sub translate { return $_[0]-> SUPER::translate unless $#_; my $self = shift; $self-> SUPER::translate(@_); $self-> change_transform; } sub clipRect { return @{$_[0]-> {clipRect}} unless $#_; $_[0]-> {clipRect} = [@_[1..4]]; $_[0]-> change_transform; } sub region { return undef; } sub scale { return @{$_[0]-> {scale}} unless $#_; my $self = shift; $self-> {scale} = [@_[0,1]]; $self-> change_transform; } sub reversed { return $_[0]-> {reversed} unless $#_; my $self = $_[0]; $self-> {reversed} = $_[1] unless $self-> get_paint_state; $self-> calc_page; } sub rotate { return $_[0]-> {rotate} unless $#_; my $self = $_[0]; $self-> {rotate} = $_[1]; $self-> change_transform; } sub resolution { return @{$_[0]-> {resolution}} unless $#_; return if $_[0]-> get_paint_state; my ( $x, $y) = @_[1..2]; return if $x <= 0 || $y <= 0; $_[0]-> {resolution} = [$x, $y]; $_[0]-> calc_page; } sub copies { return $_[0]-> {copies} unless $#_; $_[0]-> {copies} = $_[1] unless $_[0]-> get_paint_state; } sub pageDevice { return $_[0]-> {pageDevice} unless $#_; $_[0]-> {pageDevice} = $_[1] unless $_[0]-> get_paint_state; } sub useDeviceFonts { return $_[0]-> {useDeviceFonts} unless $#_; if ( $_[1]) { delete $_[0]-> {font}-> {width}; $_[0]-> set_font( $_[0]-> get_font); } $_[0]-> {useDeviceFonts} = $_[1] unless $_[0]-> get_paint_state; $_[0]-> {useDeviceFonts} = 1 if $_[0]-> {useDeviceFontsOnly}; } sub useDeviceFontsOnly { return $_[0]-> {useDeviceFontsOnly} unless $#_; $_[0]-> useDeviceFonts(1) if $_[0]-> {useDeviceFontsOnly} = $_[1] && !$_[0]-> get_paint_state; } sub grayscale { return $_[0]-> {grayscale} unless $#_; $_[0]-> {grayscale} = $_[1] unless $_[0]-> get_paint_state; } sub set_locale { my ( $self, $loc) = @_; return if !$self-> {useDeviceFonts} || !$self-> {canDraw}; $self-> {locale} = $loc; my $le = $self-> {localeEncoding} = Prima::PS::Encodings::load( $loc); unless ( scalar keys %{$self-> {localeData}}) { return if ! defined($loc); $self-> emit( <<ENCODER); \/reencode_font { ~ \/enco ~ d @ @ FF @ length dict b { 1 index \/FID ne{d}{pop pop}?} forall \/Encoding enco d currentdict e definefont } bind d ENCODER } unless ( exists $self-> {localeData}-> {$loc}) { $self-> {localeData}-> {$loc} = 1; $self-> emit( "/Encoding_$loc ["); my $i = 0; for ( $i = 0; $i < 16; $i++) { $self-> emit( join('', map {'/' . $_ } @$le[$i * 16 .. $i * 16 + 15])); } $self-> emit("] d\n"); } } sub calc_page { my $self = $_[0]; my @s = @{$self-> {pageSize}}; my @m = @{$self-> {pageMargins}}; if ( $self-> {reversed}) { @s = @s[1,0]; @m = @m[1,0,3,2]; } $self-> {size} = [ int(( $s[0] - $m[0] - $m[2]) * $self-> {resolution}-> [0] / 72.27 + 0.5), int(( $s[1] - $m[1] - $m[3]) * $self-> {resolution}-> [1] / 72.27 + 0.5), ]; } sub pageSize { return @{$_[0]-> {pageSize}} unless $#_; my ( $self, $px, $py) = @_; return if $self-> get_paint_state; $px = 1 if $px < 1; $py = 1 if $py < 1; $self-> {pageSize} = [$px, $py]; $self-> calc_page; } sub pageMargins { return @{$_[0]-> {pageMargins}} unless $#_; my ( $self, $px, $py, $px2, $py2) = @_; return if $self-> get_paint_state; $px = 0 if $px < 0; $py = 0 if $py < 0; $px2 = 0 if $px2 < 0; $py2 = 0 if $py2 < 0; $self-> {pageMargins} = [$px, $py, $px2, $py2]; $self-> calc_page; } sub size { return @{$_[0]-> {size}} unless $#_; $_[0]-> raise_ro("size"); } # primitives sub arc { my ( $self, $x, $y, $dx, $dy, $start, $end) = @_; my $try = $dy / $dx; ( $x, $y, $dx, $dy) = $self-> pixel2point( $x, $y, $dx, $dy); my $rx = $dx / 2; $end -= $start; $self-> stroke( <<ARC, $x $y M : $x $y T 1 $try Z $start R ARC "N $rx 0 M 0 0 $rx 0 $end a O", ";"); } sub chord { my ( $self, $x, $y, $dx, $dy, $start, $end) = @_; my $try = $dy / $dx; ( $x, $y, $dx, $dy) = $self-> pixel2point( $x, $y, $dx, $dy); my $rx = $dx / 2; $end -= $start; $self-> stroke(<<ARC, $x $y M : $x $y T 1 $try Z $start R ARC "N $rx 0 M 0 0 $rx 0 $end a X O", ";"); } sub ellipse { my ( $self, $x, $y, $dx, $dy) = @_; my $try = $dy / $dx; ( $x, $y, $dx, $dy) = $self-> pixel2point( $x, $y, $dx, $dy); my $rx = $dx / 2; $self-> stroke(<<ARC, $x $y M : $x $y T 1 $try Z ARC "N $rx 0 M 0 0 $rx 0 360 a O", ";"); } sub fill_chord { my ( $self, $x, $y, $dx, $dy, $start, $end) = @_; my $try = $dy / $dx; ( $x, $y, $dx, $dy) = $self-> pixel2point( $x, $y, $dx, $dy); my $rx = $dx / 2; $end -= $start; my $F = $self-> fillWinding ? 'F' : 'E'; $self-> fill( <<START, $x $y M : $x $y T 1 $try Z START "N $rx 0 M 0 0 $rx 0 $end a X $F", ";"); } sub fill_ellipse { my ( $self, $x, $y, $dx, $dy) = @_; my $try = $dy / $dx; ( $x, $y, $dx, $dy) = $self-> pixel2point( $x, $y, $dx, $dy); my $rx = $dx / 2; $self-> fill(<<ARC, $x $y M : $x $y T 1 $try Z ARC "N $rx 0 M 0 0 $rx 0 360 a F", ";"); } sub sector { my ( $self, $x, $y, $dx, $dy, $start, $end) = @_; my $try = $dy / $dx; ( $x, $y, $dx, $dy) = $self-> pixel2point( $x, $y, $dx, $dy); my $rx = $dx / 2; $end -= $start; $self-> stroke(<<ARC, $x $y M : $x $y T 1 $try Z $start R ARC "N 0 0 M 0 0 $rx 0 $end a 0 0 l O", ";"); } sub fill_sector { my ( $self, $x, $y, $dx, $dy, $start, $end) = @_; my $try = $dy / $dx; ( $x, $y, $dx, $dy) = $self-> pixel2point( $x, $y, $dx, $dy); my $rx = $dx / 2; $end -= $start; my $F = $self-> fillWinding ? 'F' : 'E'; $self-> fill(<<ARC, $x $y M : $x $y T 1 $try Z $start R ARC "N 0 0 M 0 0 $rx 0 $end a 0 0 l $F", ";"); } sub text_out { my ( $self, $text, $x, $y) = @_; return 0 unless $self-> {canDraw} and length $text; $y += $self-> {font}-> {descent} if !$self-> textOutBaseline; ( $x, $y) = $self-> pixel2point( $x, $y); my $n = $self-> {typeFontMap}-> {$self-> {font}-> {name}}; my $spec = exists ( $self-> {font}-> {encoding}) ? exists ( $Prima::PS::Encodings::fontspecific{ $self-> {font}-> {encoding}}) : 0; if ( $n == 1) { my $fn = $self-> {font}-> {docname}; unless ( $spec || ( !defined( $self-> {locale}) && !defined($self-> {fontLocaleData}-> {$fn})) || ( defined( $self-> {locale}) && defined($self-> {fontLocaleData}-> {$fn}) && ($self-> {fontLocaleData}-> {$fn} eq $self-> {locale}))) { $self-> {fontLocaleData}-> {$fn} = $self-> {locale}; $self-> emit( "Encoding_$self->{locale} /$fn reencode_font"); $self-> {changed}-> {font} = 1; } if ( $self-> {changed}-> {font}) { $self-> emit( "/$fn FF $self->{font}->{size} XF SF"); $self-> {changed}-> {font} = 0; } } my $wmul = $self-> {font}-> {width} / $self-> {fontWidthDivisor}; $self-> emit(": $x $y T"); $self-> emit("$wmul 1 Z") if $wmul != 1; $self-> emit("0 0 M"); if ( $self-> {font}-> {direction} != 0) { my $r = $self-> {font}-> {direction}; $self-> emit("$r R"); } my @rb; if ( $self-> textOpaque || $self-> {font}-> {style} & (fs::Underlined|fs::StruckOut)) { my ( $ds, $bs) = ( $self-> {font}-> {direction}, $self-> textOutBaseline); $self-> {font}-> {direction} = 0; $self-> textOutBaseline(1) unless $bs; @rb = $self-> pixel2point( @{$self-> get_text_box( $text)}); $self-> {font}-> {direction} = $ds; $self-> textOutBaseline($bs) unless $bs; } if ( $self-> textOpaque) { $self-> emit( $self-> cmd_rgb( $self-> backColor)); $self-> emit( ": N @rb[0,1] M @rb[2,3] l @rb[6,7] l @rb[4,5] l X F ;"); } $self-> emit( $self-> cmd_rgb( $self-> color)); my ( $rm, $nd) = $self-> get_rmap; my ( $xp, $yp) = ( $x, $y); my $c = $self-> {font}-> {chardata}; my $le = $self-> {localeEncoding}; my $adv = 0; my ( @t, @umap); my $unicode = Encode::is_utf8( $text); if ( defined($self-> {font}-> {encoding}) && $unicode) { # known encoding? eval { Encode::encode( $self-> {font}-> {encoding}, ''); }; unless ( $@) { # convert as much of unicode text as possible into the current encoding while ( 1) { my $conv = Encode::encode( $self-> {font}-> {encoding}, $text, Encode::FB_QUIET ); push @t, split( '', $conv); push @umap, (undef) x length $conv; last unless length $text; push @t, substr( $text, 0, 1, ''); push @umap, 1; } } else { @t = split '', $text; @umap = map { undef } @t; } } else { @t = split '', $text; @umap = map { undef } @t; } my $i = -1; for my $j ( @t) { $i++; my $advance; my $u = $umap[$i]||0; if ( !$umap[$i] && # not unicode $n == 1 && # postscript font ( $le-> [ ord $j] ne '.notdef') && ( # $spec || # fontspecific exists ( $c-> {$le-> [ ord $j]} # have predefined font metrics ) )) { $j =~ s/([\\()])/\\$1/g; my $adv2 = int( $adv * 100 + 0.5) / 100; $self-> emit( "$adv2 0 M") if $adv2 != 0; $self-> emit("($j) S"); my $xr = $rm-> [ ord $j]; $advance = $$xr[1] + $$xr[2] + $$xr[3]; } else { my ( $pg, $a, $b, $c) = $self-> place_glyph( $j); if ( length $pg) { my $adv2 = $adv + $a * 72.27 / $self-> {resolution}-> [0]; $adv2 = int( $adv * 100 + 0.5) / 100; $self-> emit( "$adv2 $self->{plate}->{yd} M : CP T"); $self-> emit( $pg); $self-> emit(";"); $advance = $a + $b + $c; } else { $advance = $$nd[1] + $$nd[2] + $$nd[3]; } } $adv += $advance * 72.27 / $self-> {resolution}-> [0]; } #$text =~ s/([\\()])/\\$1/g; #$self-> emit("($text) S"); if ( $self-> {font}-> {style} & (fs::Underlined|fs::StruckOut)) { my $lw = $self-> {font}-> {size}/30; # XXX empiric $self-> emit("[] 0 SD 0 SL $lw SW"); if ( $self-> {font}-> {style} & fs::Underlined) { $self-> emit("N @rb[0,3] M $rb[4] 0 L O"); } if ( $self-> {font}-> {style} & fs::StruckOut) { $rb[3] += $rb[1]/2; $self-> emit("N @rb[0,3] M $rb[4] 0 L O"); } } $self-> emit(";"); return 1; } sub bar { my ( $self, $x1, $y1, $x2, $y2) = @_; ( $x1, $y1, $x2, $y2) = $self-> pixel2point( $x1, $y1, $x2, $y2); $self-> fill('', "N $x1 $y1 M $x1 $y2 l $x2 $y2 l $x2 $y1 l X F", ''); } sub rectangle { my ( $self, $x1, $y1, $x2, $y2) = @_; ( $x1, $y1, $x2, $y2) = $self-> pixel2point( $x1, $y1, $x2, $y2); $self-> stroke( '', "N $x1 $y1 M $x1 $y2 l $x2 $y2 l $x2 $y1 l X O", ''); } sub clear { my ( $self, $x1, $y1, $x2, $y2) = @_; if ( grep { ! defined } $x1, $y1, $x2, $y2) { ($x1, $y1, $x2, $y2) = $self-> clipRect; unless ( grep { $_ != 0 } $x1, $y1, $x2, $y2) { ($x1, $y1, $x2, $y2) = (0,0,@{$self-> {size}}); } } ( $x1, $y1, $x2, $y2) = $self-> pixel2point( $x1, $y1, $x2, $y2); my $c = $self-> cmd_rgb( $self-> backColor); $self-> emit(<<CLEAR); $c N $x1 $y1 M $x1 $y2 l $x2 $y2 l $x2 $y1 l X F CLEAR $self-> {changed}-> {fill} = 1; } sub line { my ( $self, $x1, $y1, $x2, $y2) = @_; ( $x1, $y1, $x2, $y2) = $self-> pixel2point( $x1, $y1, $x2, $y2); $self-> stroke('', "N $x1 $y1 M $x2 $y2 l O", ''); } sub lines { my ( $self, $array) = @_; my $i; my $c = scalar @$array; my @a = $self-> pixel2point( @$array); $c = int( $c / 4) * 4; my $z = ''; for ( $i = 0; $i < $c; $i += 4) { $z .= "N @a[$i,$i+1] M @a[$i+2,$i+3] l O"; } $self-> stroke( '', $z, ''); } sub polyline { my ( $self, $array) = @_; my $i; my $c = scalar @$array; my @a = $self-> pixel2point( @$array); $c = int( $c / 2) * 2; return if $c < 2; my $z = "N @a[0,1] M "; for ( $i = 2; $i < $c; $i += 2) { $z .= "@a[$i,$i+1] l "; } $z .= "O"; $self-> stroke( '', $z, ''); } sub fillpoly { my ( $self, $array) = @_; my $i; my $c = scalar @$array; $c = int( $c / 2) * 2; return if $c < 2; my @a = $self-> pixel2point( @$array); my $x = "N @a[0,1] M "; for ( $i = 2; $i < $c; $i += 2) { $x .= "@a[$i,$i+1] l "; } $x .= 'X ' . ($self-> fillWinding ? 'F' : 'E'); $self-> fill( '', $x, ''); } sub flood_fill { return 0; } sub pixel { my ( $self, $x, $y, $pix) = @_; return cl::Invalid unless defined $pix; my $c = $self-> cmd_rgb( $pix); ($x, $y) = $self-> pixel2point( $x, $y); $self-> emit(<<PIXEL); : $c N $x $y M 0 0 L F ; PIXEL $self-> {changed}-> {fill} = 1; } # methods sub put_image_indirect { return 0 unless $_[0]-> {canDraw}; my ( $self, $image, $x, $y, $xFrom, $yFrom, $xDestLen, $yDestLen, $xLen, $yLen) = @_; my $touch; $touch = 1, $image = $image-> image if $image-> isa('Prima::DeviceBitmap'); unless ( $xFrom == 0 && $yFrom == 0 && $xLen == $image-> width && $yLen == $image-> height) { $image = $image-> extract( $xFrom, $yFrom, $xLen, $yLen); $touch = 1; } my $ib = $image-> get_bpp; if ( $ib != $self-> get_bpp) { $image = $image-> dup unless $touch; if ( $self-> {grayscale} || $image-> type & im::GrayScale) { $image-> type( im::Byte); } else { $image-> type( im::RGB); } } elsif ( $self-> {grayscale} || $image-> type & im::GrayScale) { $image = $image-> dup unless $touch; $image-> type( im::Byte); } $ib = $image-> get_bpp; $image-> type( im::RGB) if $ib != 8 && $ib != 24; my @is = $image-> size; ($x, $y, $xDestLen, $yDestLen) = $self-> pixel2point( $x, $y, $xDestLen, $yDestLen); my @fullScale = ( $is[0] / $xLen * $xDestLen, $is[1] / $yLen * $yDestLen, ); my $g = $image-> data; my $bt = ( $image-> type & im::BPP) * $is[0] / 8; my $ls = int(( $is[0] * ( $image-> type & im::BPP) + 31) / 32) * 4; my ( $i, $j); $self-> emit(": $x $y T @fullScale Z"); $self-> emit("/scanline $bt string d"); $self-> emit("@is 8 [$is[0] 0 0 $is[1] 0 0]"); $self-> emit('{currentfile scanline readhexstring pop}'); $self-> emit(( $image-> type & im::GrayScale) ? "image" : "false 3 colorimage"); for ( $i = 0; $i < $is[1]; $i++) { my $w = substr( $g, $ls * $i, $bt); $w =~ s/(.)(.)(.)/$3$2$1/g if $ib == 24; $w =~ s/(.)/sprintf("%02x",ord($1))/eg; $self-> emit( $w); } $self-> emit(';'); return 1; } sub get_bpp { return $_[0]-> {grayscale} ? 8 : 24 } sub get_nearest_color { return $_[1] } sub get_physical_palette { return $_[0]-> {grayscale} ? [map { $_, $_, $_ } 0..255] : 0 } sub get_handle { return 0 } # fonts sub fonts { my ( $self, $family, $encoding) = @_; $family = undef if defined $family && !length $family; $encoding = undef if defined $encoding && !length $encoding; my $f1 = $self-> {useDeviceFonts} ? Prima::PS::Fonts::enum_fonts( $family, $encoding) : []; return $f1 if !$::application || $self-> {useDeviceFontsOnly}; my $f2 = $::application-> fonts( $family, $encoding); if ( !defined($family) && !defined($encoding)) { my %f = map { $_-> {name} => $_ } @$f1; my @add; for ( @$f2) { if ( $f{$_}) { push @{$f{$_}-> {encodings}}, @{$_-> {encodings}}; } else { push @add, $_; } } push @$f1, @add; } else { push @$f1, @$f2; } return $f1; } sub font_encodings { my @r; if ( $_[0]-> {useDeviceFonts}) { @r = Prima::PS::Encodings::unique, keys %Prima::PS::Encodings::fontspecific; } if ( $::application && !$_[0]-> {useDeviceFontsOnly}) { my %h = map { $_ => 1 } @r; for ( @{$::application-> font_encodings}) { next if $h{$_}; push @r, $_; } } return \@r; } sub get_font { my $z = {%{$_[0]-> {font}}}; delete $z-> {charmap}; delete $z-> {docname}; return $z; } sub set_font { my ( $self, $font) = @_; $font = { %$font }; my $n = exists($font-> {name}) ? $font-> {name} : $self-> {font}-> {name}; my $gui_font; $n = $self-> {useDeviceFonts} ? $Prima::PS::Fonts::defaultFontName : 'Default' unless defined $n; $font-> {height} = int(( $font-> {size} * $self-> {resolution}-> [1]) / 72.27 + 0.5) if exists $font-> {size}; AGAIN: if ( $self-> {useDeviceFontsOnly} || !$::application || ( $self-> {useDeviceFonts} && ( # enter, if there's a device font exists $Prima::PS::Fonts::enum_families{ $n} || exists $Prima::PS::Fonts::files{ $n} || ( # or the font encoding is PS::Encodings-specific, # not present in the GUI space exists $font-> {encoding} && ( exists $Prima::PS::Encodings::fontspecific{$font-> {encoding}} || exists $Prima::PS::Encodings::files{$font-> {encoding}} ) && ( !grep { $_ eq $font-> {encoding} } @{$::application-> font_encodings} ) ) ) && # and, the encoding is supported ( !exists $font-> {encoding} || !length ($font-> {encoding}) || ( exists $Prima::PS::Encodings::fontspecific{$font-> {encoding}} || exists $Prima::PS::Encodings::files{$font-> {encoding}} ) ) ) ) { $self-> {font} = Prima::PS::Fonts::font_pick( $font, $self-> {font}, resolution => $self-> {resolution}-> [1]); $self-> {fontCharHeight} = $self-> {font}-> {charheight}; $self-> {docFontMap}-> {$self-> {font}-> {docname}} = 1; $self-> {typeFontMap}-> {$self-> {font}-> {name}} = 1; $self-> {fontWidthDivisor} = $self-> {font}-> {maximalWidth}; $self-> set_locale( $self-> {font}-> {encoding}); } else { my $wscale = $font-> {width}; my $wsize = $font-> {size}; my $wfsize = $self-> {font}-> {size}; delete $font-> {width}; delete $font-> {size}; delete $self-> {font}-> {size}; unless ( $gui_font) { $gui_font = Prima::Drawable-> font_match( $font, $self-> {font}); if ( $gui_font-> {name} ne $n && $self-> {useDeviceFonts}) { # back up my $pitch = (exists ( $font-> {pitch} ) ? $font-> {pitch} : $self-> {font}-> {pitch}) || fp::Variable; $n = $font-> {name} = ( $pitch == fp::Variable) ? $Prima::PS::Fonts::variablePitchName : $Prima::PS::Fonts::fixedPitchName; $font-> {width} = $wscale if defined $wscale; $font-> {wsize} = $wsize if defined $wsize; $self-> {font}-> {size} = $wfsize if defined $wfsize; goto AGAIN; } } $self-> {font} = $gui_font; $self-> {font}-> {size} = int( $self-> {font}-> {height} * 72.27 / $self-> {resolution}-> [1] + 0.5); $self-> {typeFontMap}-> {$self-> {font}-> {name}} = 2; $self-> {fontWidthDivisor} = $self-> {font}-> {width}; $self-> {font}-> {width} = $wscale if $wscale; $self-> {fontCharHeight} = $self-> {font}-> {height}; } $self-> {changed}-> {font} = 1; $self-> {plate}-> destroy, $self-> {plate} = undef if $self-> {plate}; } my %fontmap = (Prima::Application-> get_system_info-> {apc} == apc::Win32) ? ( 'Helvetica' => 'Arial', 'Times' => 'Times New Roman', 'Courier' => 'Courier New', ) : (); sub plate { my $self = $_[0]; return $self-> {plate} if $self-> {plate}; return {ABC => []} if $self-> {useDeviceFontsOnly}; my ( $dimx, $dimy) = ( $self-> {font}-> {maximalWidth}, $self-> {font}-> {height}); my %f = %{$self-> {font}}; $f{style} &= ~(fs::Underlined|fs::StruckOut); if ( $self-> {useDeviceFonts} && exists $Prima::PS::Fonts::files{$f{name}}) { $f{name} =~ s/^([^-]+)\-.*$/$1/; $f{pitch} = fp::Default unless $f{pitch} == fp::Fixed; $f{name} = $fontmap{$f{name}} if exists $fontmap{$f{name}}; } delete $f{size}; delete $f{width}; delete $f{direction}; $self-> {plate} = Prima::Image-> create( type => im::BW, width => $dimx, height => $dimy, font => \%f, backColor => cl::Black, color => cl::White, textOutBaseline => 1, preserveType => 1, conversion => ict::None, ); my ( $f, $l) = ( $self-> {plate}-> font-> {firstChar}, $self-> {plate}-> font-> {lastChar}); my $x = $self-> {plate}-> {ABC} = $self-> {plate}-> get_font_abc( $f, $l); my $j = (230 - $f) * 3; return $self-> {plate}; } sub place_glyph { return '' if $_[0]-> {useDeviceFontsOnly}; my ( $self, $char) = @_; my $z = $_[0]-> plate; my $x = ord $char; my $d = $z-> font-> descent; my ( $dimx, $dimy) = $z-> size; my ( $f, $l) = ( $z-> font-> firstChar, $z-> font-> lastChar); my $ls = int(( $dimx + 31) / 32) * 4; my $la = int ($dimx / 8) + (( $dimx & 7) ? 1 : 0); my $ax = ( $dimx & 7) ? (( 0xff << (7-( $dimx & 7))) & 0xff) : 0xff; my $xsf = 0; my ( $a, $b, $c); if ( Encode::is_utf8( $char)) { ( $a, $b, $c) = @{ $z-> get_font_abc( $x, $x, 1)}; } else { my $abc = $z-> {ABC}; ( $a, $b, $c) = ( $abc-> [ ( $x - $f) * 3], $abc-> [ ( $x - $f) * 3 + 1], $abc-> [ ( $x - $f) * 3 + 2], ); } return '' if $b <= 0; $z-> begin_paint; $z-> clear; $z-> text_out( chr( $x), ($a < 0) ? -$a : 0, $d); $z-> end_paint; my $dd = $z-> data; my ($j, $k); my @emmap = (0) x $dimy; my @bbox = ( $a, 0, $b - $a, $dimy - 1); for ( $j = $dimy - 1; $j >= 0; $j--) { #my @ss = map { my $x = ord $_; map { ($x & (0x80>>$_))?'X':'.'} 0..7 } split( '', substr( $dd, $ls * $j, $la)); my @xdd = map { ord $_ } split( '', substr( $dd, $ls * $j, $la)); #print "@ss @xdd\n"; $xdd[-1] &= $ax; $emmap[$j] = 1 unless grep { $_ } @xdd; } for ( $j = 0; $j < $dimy; $j++) { last unless $emmap[$j]; $bbox[1]++; } for ( $j = $dimy - 1; $j >= 0; $j--) { last unless $emmap[$j]; $bbox[3]--; } if ( $bbox[3] >= 0) { $bbox[1] -= $d; $bbox[3] -= $d; my $zd = $z-> extract( ( $a < 0) ? 0 : $a, $bbox[1] + $d, $b, $bbox[3] - $bbox[1] + 1, ); # $z-> save("a.gif"); my $bby = $bbox[3] - $bbox[1] + 1; my $zls = int(( $b + 31) / 32) * 4; my $zla = int ($b / 8) + (( $b & 7) ? 1 : 0); $zd = $zd-> data; my $cd = ''; for ( $j = $bbox[3] - $bbox[1]; $j >= 0; $j--) { $cd .= substr( $zd, $j * $zls, $zla); } my $cdz = ''; for ( $j = 0; $j < length $cd; $j++) { $cdz .= sprintf("%02x", ord substr( $cd, $j, 1)); } $_[0]-> {plate}-> {yd} = $bbox[1] * 72.27 / $_[0]-> {resolution}-> [1]; my $scalex = 72.27 * $b / $_[0]-> {resolution}-> [0]; my $scaley = 72.27 * $bby / $_[0]-> {resolution}-> [1]; return "$scalex $scaley scale $b $bby true [$b 0 0 -$bby 0 $bby] <$cdz> imagemask", $a, $b, $c; } return ''; } sub get_rmap { my @rmap; my $c = $_[0]-> {font}-> {chardata}; my $le = $_[0]-> {localeEncoding}; my $nd = $c-> {'.notdef'}; my $fs = $_[0]-> {font}-> {height} / $_[0]-> {fontCharHeight}; if ( defined $nd) { $nd = [ @$nd ]; $$nd[$_] *= $fs for 1..3; } else { $nd = [0,0,0,0]; } my ( $f, $l) = ( $_[0]-> {font}-> {firstChar}, $_[0]-> {font}-> {lastChar}); my $i; my $abc; if ( $_[0]-> {typeFontMap}-> {$_[0]-> {font}-> {name}} == 1) { for ( $i = 0; $i < 255; $i++) { if (( $le-> [$i] ne '.notdef') && $c-> { $le-> [ $i]}) { $rmap[$i] = [ $i, map { $_ * $fs } @{$c-> { $le-> [ $i]}}[1..3]]; } elsif ( $i >= $f && $i <= $l) { $abc = $_[0]-> plate-> {ABC} unless $abc; my $j = ( $i - $f) * 3; $rmap[$i] = [ $i, @$abc[ $j .. $j + 2]]; } } } else { $abc = $_[0]-> plate-> {ABC}; for ( $i = $f; $i <= $l; $i++) { my $j = ( $i - $f) * 3; $rmap[$i] = [ $i, @$abc[ $j .. $j + 2]]; } } # @rmap = map { $c-> {$_} } @{$_[0]-> {localeEncoding}}; return \@rmap, $nd; } sub get_font_abc { my ( $self, $first, $last) = @_; my $lim = ( defined ($self-> {font}-> {encoding}) && exists($Prima::PS::Encodings::fontspecific{$self-> {font}-> {encoding}})) ? 255 : 127; $first = 0 if !defined $first || $first < 0; $first = $lim if $first > $lim; $last = $lim if !defined $last || $last < 0 || $last > $lim; my $i; my @ret; my ( $rmap, $nd) = $self-> get_rmap; my $wmul = $self-> {font}-> {width} / $self-> {fontWidthDivisor}; for ( $i = $first; $i < $last; $i++) { my $cd = $rmap-> [ $i] || $nd; push( @ret, map { $_ * $wmul } @$cd[1..3]); } return \@ret; } sub get_font_ranges { my $self = $_[0]; return [ $self-> {font}-> {firstChar}, $self-> {font}-> {lastChar}]; } sub get_text_width { my ( $self, $text, $addOverhang) = @_; my $i; my $len = length $text; return 0 unless $len; my ( $rmap, $nd) = $self-> get_rmap; my $cd; my $w = 0; for ( $i = 0; $i < $len; $i++) { my $cd = $rmap-> [ ord( substr( $text, $i, 1))] || $nd; $w += $cd-> [1] + $cd-> [2] + $cd-> [3]; } if ( $addOverhang) { $cd = $rmap-> [ ord( substr( $text, 0, 1))] || $nd; $w += ( $cd-> [1] < 0) ? -$cd-> [1] : 0; $cd = $rmap-> [ ord( substr( $text, $len - 1, 1))] || $nd; $w += ( $cd-> [3] < 0) ? -$cd-> [3] : 0; } return $w * $self-> {font}-> {width} / $self-> {fontWidthDivisor}; } sub get_text_box { my ( $self, $text) = @_; my ( $rmap, $nd) = $self-> get_rmap; my $len = length $text; return [ (0) x 10 ] unless $len; my $cd; my $wmul = $self-> {font}-> {width} / $self-> {fontWidthDivisor}; $cd = $rmap-> [ ord( substr( $text, 0, 1))] || $nd; my $ovxa = $wmul * (( $cd-> [1] < 0) ? -$cd-> [1] : 0); $cd = $rmap-> [ ord( substr( $text, $len - 1, 1))] || $nd; my $ovxb = $wmul * (( $cd-> [3] < 0) ? -$cd-> [3] : 0); my $w = $self-> get_text_width( $text); my @ret = ( -$ovxa, $self-> {font}-> {ascent} - 1, -$ovxa, -$self-> {font}-> {descent}, $w - $ovxb, $self-> {font}-> {ascent} - 1, $w - $ovxb, -$self-> {font}-> {descent}, $w, 0 ); unless ( $self-> textOutBaseline) { $ret[$_] += $self-> {font}-> {descent} for (1,3,5,7,9); } if ( $self-> {font}-> {direction} != 0) { my $s = sin( $self-> {font}-> {direction} / 57.29577951); my $c = cos( $self-> {font}-> {direction} / 57.29577951); my $i; for ( $i = 0; $i < 10; $i+=2) { my ( $x, $y) = @ret[$i,$i+1]; $ret[$i] = $x * $c - $y * $s; $ret[$i+1] = $x * $s + $y * $c; } } return \@ret; } 1; __END__