# # Copyright (c) 2004-2007 - Consultas, PKG.fr # # This file is part of A2P. # # A2P is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # A2P is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with A2P; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # $Id: IOB.pm 3 2007-10-18 16:20:19Z guillaume $ # # Class to export Include Object members # # cf ref #1, p. 92 & ref #2, p. 180 # package AFPDS::MODCA::IOB ; use strict ; use GD ; use A2P::Globals ; use A2P::Syslog ; use AFPDS::MODCA::Common ; BEGIN { our $VERSION = sprintf "%s", q$Rev: 1172 $ =~ /([0-9.]+)\s+/ ; } our $VERSION ; our @ISA = ("AFPDS::MODCA"); our $IDENTS ; sub _ID { 0xD3AFC3 } sub _supported_triplets { return [], [ 0x10, # Mandatory object classification only if object type is 0x92 0x4B, # Measurement units mandatory if coordinates are overrided # 0x01, # Coded charset GID # 0x02, # FQN # 0x04, # Mapping option # 0x4C, # object area size # 0x4E, # color specs # 0x70, # presentation space reset mixing # 0x71, # presentation space mixing rules ]; } sub new { my $class = shift ; &Debug("new " . __PACKAGE__ . " v$VERSION object"); my $self = { FLUX => 0, # Associated flux IDENT => _ID, # MO:DCA identity FLAG => 0, # MO:DCA flags RESERVED => 0, # MO:DCA reserved LONG => 0, # MO:DCA buffer length BUFFER => '' # MO:DCA content buffer }; return bless $self , $class ; } sub validate { my $self = shift ; &Debug("Found IOB to include object"); &UPSTAT('GET-RESOURCE-INCLUSION'); # Check minimal buffer size return ( 251, "IOB is malformed as too short (" . $self->{LONG} . "<28)" ) if ( $self->{LONG} < 28 ); # Check FLUX is a Flux object return ( 251, "IOB needs to be used with Flux object" ) unless ( ref($self->{FLUX}) =~ /^AFPDS::Flux$/ ); return () ; } sub create { my $self = shift ; # Set resource name my $resname = substr( $self->{BUFFER}, 0, 8 ); $resname = $self->convert( $resname ); return ( 252, "Invalid resource name found" ) unless $resname ; # Set resource name as member as it can be later overrided by X'02' triplet $self->name( $resname ); # Extract values my $zero = $self->get_from_buffer( 8, 1 ); my $ObjType = $self->get_from_buffer( 9, 1 ); my $XoaOset = $self->get_from_buffer( 10, 3 ); my $YoaOset = $self->get_from_buffer( 13, 3 ); my $XoaOrent = $self->get_from_buffer( 16, 2 ); my $YoaOrent = $self->get_from_buffer( 18, 2 ); my $XocaOset = $self->get_from_buffer( 20, 3 ); my $YocaOset = $self->get_from_buffer( 23, 3 ); my $RefCSys = $self->get_from_buffer( 26, 1 ); # Analyse offsets unless ( $XoaOset == 0xFFFFFF ) { # This is a signed number $XoaOset -= 0x1000000 if ( $XoaOset > 0x7FFFFF ); # Check and set X off-set return ( 252, "Found bad IOB XoaOset ($XoaOset)" ) if ( $XoaOset < -32768 or $XoaOset > 32767 ); } unless ( $YoaOset == 0xFFFFFF ) { # This is a signed number $YoaOset -= 0x1000000 if ( $YoaOset > 0x7FFFFF ); # Check and set X off-set return ( 252, "Found bad IOB YoaOset ($YoaOset)" ) if ( $YoaOset < -32768 or $YoaOset > 32767 ); } unless ( $XocaOset == 0xFFFFFF ) { # This is a signed number $XocaOset -= 0x1000000 if ( $XocaOset > 0x7FFFFF ); # Check and set X off-set return ( 252, "Found bad IOB XocaOset ($XocaOset)" ) if ( $XocaOset < -32768 or $XocaOset > 32767 ); } unless ( $YocaOset == 0xFFFFFF ) { # This is a signed number $YocaOset -= 0x1000000 if ( $YocaOset > 0x7FFFFF ); # Check and set X off-set return ( 252, "Found bad IOB YocaOset ($YocaOset)" ) if ( $YocaOset < -32768 or $YocaOset > 32767 ); } # Check invalid values return ( 252, "Invalid reserved value in IOB ($zero)" ) if ( $zero ); return ( 252, "Invalid object type in IOB ($ObjType)" ) unless ( $ObjType == 0x92 or $ObjType == 0x5F or $ObjType == 0xBB or $ObjType == 0xEB or $ObjType == 0xFB ); # Check supported orientation return ( 252, "Unsupported orientation found in IOB ($XoaOrent;$YoaOrent)" ) unless ( $XoaOrent == 0x0000 and $YoaOrent == 0x2D00 ); # Check supported system reference return ( 252, "Unsupported RefCSys found in IOB ($RefCSys)" ) unless ( $RefCSys == 0x01 ); # Analyse now authorized triplets as it is mandatory my @err = $self->check_triplets(substr( $self->{BUFFER}, 27 )); return @err if @err ; # Set a default resolution if not set $self->{RESOLUTION} = 240 unless (exists($self->{RESOLUTION})); # Get current resolution my $reso = $self->{RESOLUTION} ; # Update resname if update by triplet X'02', and set it with resolution $resname = $self->name ; # Check container is opened in flux my $flux = $self->{FLUX} ; my $container = $flux->get_resource($resname); return ( 252, "Resource '$resname' not available" ) unless ( $container and ref($container) =~ /^AFPDS::MODCA/ ); # Validate the expected structure of BOC my $struct = $container->{STRUCT} || 0 ; return ( 253, "Unexpected '$struct' structure found for BOC object" ) unless ( $struct == 0xDC00 ); # Validate the expected structure of ourself $struct = $self->{STRUCT} || 0 ; return ( 253, "Unexpected '$struct' structure found for IOB object" ) unless ( $struct == 0xA800 ); if ( $ObjType == 0x92 ) { return ( 252, "No OID found in object container for IOB" ) unless ( exists($container->{OID}) and $container->{OID} ); # Check triplet X'10' has been set as mandatory here and match container return ( 252, "No OID found in object inclusion with IOB" ) unless ( exists($self->{OID}) and $self->{OID} ); return ( 253, "OID included is different from container" ) unless ( $container->{OID} eq $self->{OID} ); # Check specified class in triplet X'10' my $class = $self->{CLASS} || 0 ; return ( 253, "Unexpected '$class' class found for IOB object" ) unless ( $class == 0x01 ); # Check origins are not set for the object return ( 253, "Origin offset not allowed for this IOB object" ) if ( $XocaOset or $YocaOset ); # Reset to default offsets for the object if not set $XoaOset = 0 if ( $XoaOset == 0xFFFFFF ); $YoaOset = 0 if ( $YoaOset == 0xFFFFFF ); # OID is validated, now we only need to handle supported format # Get eventually still converted image my $teximage = $container->teximage( $reso ) ; # Cancel convertion if image is still converted unless ( $teximage ) { &Debug("Converting $resname resource from $self->{FORMAT} format"); return ( 254, "Can't process empty $self->{FORMAT} for $resname" ) unless ( defined($container->{RESOURCE_BUFFER}) and length($container->{RESOURCE_BUFFER}) > 0 ); my $image = $self->get_image( \$container->{RESOURCE_BUFFER} ); return ( 254, "Unsupported $self->{FORMAT} format for $resname" ) unless ( defined($image) ); return ( 254, "Can't convert $resname resource" ) unless ( $image ); # Get the TeX name for the inclusion directive my $texname = $flux->resource_texname($resname); return ( 254, "TeX compatible name not set for $resname resource" ) unless ( defined($texname) and $texname ); # Tag texname with resolution my $tag = $reso ; $tag =~ y/0-9/A-J/ ; $texname .= 'at' . $tag ; # Get the resource number for the PCL inclusion macro my $number = $flux->resource_number( $resname, $reso ) ; return ( 254, "PCL number not set for $resname resource" ) unless ( defined($number) ); return ( 254, "Bad $number PCL number for $resname resource" ) unless ( $number > -1 and $number < 32768 ); # Convert image for TeX/LaTeX inclusion $teximage = $self->convert_image( $texname, $image, \$container->{RESOURCE_BUFFER}, $number, $reso ); return ( 254, "Can't convert $resname resource for TeX/LaTeX" ) unless ( defined($teximage) and $teximage and @{$teximage} ); # Insert TeX/LaTeX directive definitions to allow inclusion $flux->addline( '{%', @{$teximage}, '}%' ) ; # Keep converted image inclusion in container $teximage = $container->teximage( $reso, $texname ); } # Prepare array of Tex/LaTeX lines my @texlines = ( "{% $teximage inclusion as $resname at $reso dpi" ) ; # TODO Offset should be absolute in the AFP standard push @texlines, "\\PAGEPUT{\\XX\\kern$XoaOset\\Xunit}{\\YY\\kern$YoaOset\\Yunit}{" . "\\$teximage}%", "}%" ; # Add TeX/LaTeX code to flux for the inclusion $flux->addline(@texlines) ; } elsif ( $ObjType == 0x5F ) { return ( 252, "Unsupported Page Segment ObjType found in IOB" ); } elsif ( $ObjType == 0xBB ) { return ( 252, "Unsupported GOCA Graphics ObjType found in IOB" ); } elsif ( $ObjType == 0xEB ) { return ( 252, "Unsupported BCOCA Bar Code ObjType found in IOB" ); } elsif ( $ObjType == 0xFB ) { return ( 252, "Unsupported IOCA Image ObjType found in IOB" ); } else { return ( 252, "Unsupported ObjType found in IOB ($ObjType)" ); } return () ; } sub get_image { my $self = shift ; return 0 unless ( exists($self->{FORMAT}) and $self->{FORMAT} ); my $dataref = shift ; return 0 unless ( defined($dataref) and $dataref ); if ( $self->{FORMAT} eq 'GIF' ) { return GD::Image->newFromGifData( $$dataref ); } # No supported format return undef ; } ###################### Image convertion to TeX/LaTeX inclusion code sub convert_image { my $self = shift ; # We must be called with 4 defined arguments return undef unless ( map { defined($_) and $_ } @_[0..3] == 4 ); my $name = shift ; my $image = shift ; my $gifref = shift ; my $number = shift ; my $reso = shift ; my ( $width , $height ) = ( $image->width, $image->height ); # Open TeX/LaTeX global directive declaration with given name my @lines = ( "\\global\\def\\" . $name . "{% as " . $self->name . " at $reso dpi" ) ; # In case of PDF, we could prepare the resource as PNG image to include my $flux = $self->{FLUX} ; my $pdf = $flux->getEnv('DO_PDF') =~ /^yes$/i ? 1 : 0 ; if ( $pdf ) { # Get the image as PNG with compression my $png = $image->png($PNGCOMPRESSION); # Create the PNG file my ( $pngname ) = $flux->getoutbase =~ m|^(.*)/[^/]+$| ; $pngname .= '/' . $name . '.png' ; # Add PNG image to resource to clean $flux->toclean( $pngname ); unless ( open PNG, '>', $pngname ) { &Error("Can't open file $pngname for writing: $!"); return 0 ; } binmode(PNG); print PNG $png ; close(PNG); # Insert TeX/LaTeX code for PDF image inclusion unshift @lines, "\\ifpdf%", "\\pdfimageresolution" . $reso . "%", "\\pdfximage{$name.png}%", "\\newcount\\ximage$name%", "\\global\\ximage$name=\\pdflastximage%", "\\fi% End pdf inclusion preparation" ; # Add TeX/LaTeX for PDF inclusion push @lines, "\\if\\pcloutput0%", "\\ifpdf%", "\\ifx\\ximage$name\\undefined\\else%", "\\message{PNG[" . $self->name . "]}%", "\\pdfrefximage\\ximage$name%", "\\fi\\fi%", "\\else% End pdf case" ; } # Handle PCL case my $pcl = $flux->getEnv('DO_PCL') =~ /^yes$/i ? 1 : 0 ; $pcl = $flux->getEnv('DO_VPCL') =~ /^yes$/i ? 1 : 0 unless $pcl ; if ( $pcl and ! $ONLY_DO_PDF ) { # Create the PCL file my ( $pclname ) = $flux->getoutbase =~ m|^(.*)/[^/]+$| ; $pclname .= '/' . $name . '.pcl' ; # Add PCL resource as to be cleaned $flux->toclean( $pclname ); # Check PCL_RESOLUTION environment with a configuration default my $resolution = $flux->getEnv('PCL_RESOLUTION') || $PCL_RESOLUTION ; # Update the width/height resource expected with the defined resolution my $newwidth = int( $width * $resolution / $reso ) ; my $newheight = int( $height * $resolution / $reso ) ; # Prepare the command to start for PCL resource generation &Debug("Scaling resource from $width to $newwidth in PCL conversion"); my $command = sprintf( $GIFTOPCL, $newwidth, $resolution ); &Debug("Scaling resource with '$command' command") if $ADVANCED_DEBUGGING ; # Finally the PCL flux is output in a file to be included later $command .= " > '$pclname'" ; unless ( open PCL, '|' . $command ) { &Error("Can't use '$command' for execution: $!"); return 0 ; } print PCL $$gifref ; close(PCL); # Insert TeX/LaTeX code for RAW image inclusion unshift @lines, "\\if\\pcloutput1%", # Specify the macro ID to use and start the macro '\\special{pcl5="&f' . $number . 'y0X" ' . # Set the raster resource width/height 'pcl5="*r'. $newwidth .'s'. $newheight .'T" ' . "$name.pcl}%", # Include PCL code for the resource '\\special{pcl5="&f1X"}%', # Stop the macro # Set the global TeX/LaTeX definition '\\global\\def\\pclimage' . $name . "{%", '\\special{pcl5="&f0s' . # Store the PCL current position # Specify the macro ID to use, call it and restore the PCL position $number . 'y3x1S"}%', "}\\fi% End pcl inclusion preparation" ; # Add TeX/LaTeX for PCL inclusion push @lines, "\\message{PCL[" . $self->name . "]}%", "\\pclimage" . $name . "%" ; } # Close TeX/LaTeX if condition if pdf required push @lines, "\\fi% End pcl case" if $pdf ; push @lines, "}% End of $name as " . $self->name . " resource" ; return \@lines ; } &Debug("Module " . __PACKAGE__ . " v$VERSION loaded"); ( $IDENTS->{&_ID} ) = __PACKAGE__ =~ /(\w+)$/ ;