App-Music-ChordPro

 view release on metacpan or  search on metacpan

lib/ChordPro/lib/SVGPDF.pm  view on Meta::CPAN

#! perl

use v5.26;
use Object::Pad;
use Carp;
use utf8;

class  SVGPDF;

our $VERSION = '0.092';

=head1 NAME

SVGPDF - Create PDF XObject from SVG data

=head1 SYNOPSIS

    my $pdf = PDF::API2->new;
    my $svg = SVGPDF->new($pdf);
    my $xof = $svg->process("demo.svg");

    # If all goes well, $xof is an array of hashes, each representing an
    # XObject corresponding to the <svg> elements in the file.
    # Get a page and graphics context.
    my $page = $pdf->page;
    $page->bbox( 0, 0, 595, 842 );
    my $gfx = $pdf->gfx;

    # Place the objects.
    my $y = 832;
    foreach my $xo ( @$xof ) {
	my @bb = @{$xo->{vbox}};
        my $h = $bb[3];
	$gfx->object( $xo->{xo}, 10, $y-$h, 1 );
	$y -= $h;
    }

    $pdf->save("demo.pdf");

=head1 DESCRIPTION

This module processes SVG data and produces one or more PDF XObjects
to be placed in a PDF document. This module is intended to be used
with L<PDF::Builder>, L<PDF::API2> and compatible PDF packages.

The main method is process(). It takes the SVG from an input source, see
L</INPUT>.

=head1 COORDINATES & UNITS

SVG coordinates run from top-left to bottom-right.

Dimensions without units are B<pixels>, at 96 pixels / inch. E.g.,
C<width="96"> means 96px (pixels) and is equal to 72pt (points) or 1in (inch).

For font sizes, CSS defines C<em> to be equal to the font size, and
C<ex> is half of the font size.

=head1 CONSTRUCTORS

=head2 SVGPDF->new($pdf)

In its most simple form, a new SVGPDF object can be created with a
single argument, the PDF document.

There are a few optional arguments, these can be specified as
key/value pairs.

=over 8

=item C<fc>

A reference to a callback routine to handle fonts.
See L</FONT HANDLER CALLBACK>.

It may also be an array of routines which will be called in
sequence until one of them succeeds (returns a 'true' result).

=item C<fontsize>

The font size to be used for dimensions in 'ex' and 'em' units.

Note that CSS defines 'em' to be the font size, and 'ex' half of the
font size.

=item C<pagesize>

An array reference containing the maximum width and height of the
resultant image.

There is no widely accepted default for this, so we use C<[595,842]>
which corresponds to an ISO A4 page.

=item C<grid>

If not zero, a grid will be added to the image. This is mostly for
developing and debugging.

The value determines the grid spacing.

=item C<verbose>

Verbosity of informational messages. Set to zero to silence all but
fatal errors.

=item C<debug>

lib/ChordPro/lib/SVGPDF.pm  view on Meta::CPAN

use SVGPDF::Rect;
use SVGPDF::Style;
use SVGPDF::Svg;
use SVGPDF::Text;
use SVGPDF::Tspan;
use SVGPDF::Use;

################ General methods ################


=head1 METHODS

=cut

# $pdf [ , fc => $callback ] [, atts => { ... } ] [, foo => bar ]
# pdf => $pdf [ , fc => $callback ] [, atts => { ... } ] [, foo => bar ]

sub BUILDARGS ( @args ) {
    my $cls = shift(@args);

    # Assume first is pdf if uneven.
    unshift( @args, "pdf" ) if @args % 2;

    my %args = @args;
    @args = ();
    push( @args, $_, delete $args{$_} ) for qw( pdf fc tc );

    # Flatten everything else into %atts.
    my %x = %{ delete($args{atts}) // {} };
    $x{$_} = $args{$_} for keys(%args);

    # And store as ref.
    push( @args, "atts", \%x );

    # Return new argument list.
    @args;
}

BUILD {
    $debug        = $atts->{debug}        || 0;
    $verbose      = $atts->{verbose}      // $debug;
    $grid         = $atts->{grid}         || 0;
    $prog         = $atts->{prog}         || 0;
    $debug_styles = $atts->{debug_styles} || $debug > 1;
    $trace        = $atts->{trace}        || 0;
    $pagesize     = $atts->{pagesize}     || [ 595, 842 ];
    $fontsize     = $atts->{fontsize}     || 12;
    $wstokens     = $atts->{wstokens}     || 0;
    $indent       = "";
    $xoforms      = [];
    $defs         = {};
    $fontmanager  = SVGPDF::FontManager->new( svg => $self );
    $self;
}

=head2 process

    $xof = $svg->process( $data, %options )

This methods gets SVG data from C<$data> and returns an array reference
with rendered images. See L</OUTPUT> for details.

The input is read using File::LoadLines. See L</INPUT> for details.

Recognized attributes in C<%options> are:

=over 4

=item fontsize

The font size to be used for dimensions in 'ex' and 'em' units.

This value overrides the value set in the constructor.

=item combine

An SVG can produce multiple XObjects, but sometimes these should be
kept as a single image.

There are two ways to combine the image objects. This can be selected
by setting $opts{combine} to either C<"stacked"> or C<"bbox">.

Type C<"stacked"> (default) stacks the images on top of each other,
left sides aligned. The bounding box of each object is only used to
obtain the width and height.

Type C<"bbox"> stacks the images using the bounding box details. The
origins of the images are vertically aligned and images may protrude
other images when the image extends below the origin.

=item sep

When combining images, add additional vertical space between the
individual images.

=back

=cut

method process ( $data, %options ) {

    if ( $options{reset} ) {	# for testing, mostly
	$xoforms = [];
    }

    my $save_fontsize = $fontsize;
    $fontsize = $options{fontsize} if $options{fontsize};
    # TODO: Page size

    # Load the SVG data.
    my $svg = SVGPDF::Parser->new;
    my $tree = $svg->parse_file
      ( $data,
	whitespace_tokens => $wstokens||$options{whitespace_tokens} );
    return unless $tree;

    # CSS persists over svgs, but not over files.
    $css = SVGPDF::CSS->new;

    # Search for svg elements and process them.
    $self->search($tree);

    # Restore.

lib/ChordPro/lib/SVGPDF.pm  view on Meta::CPAN

    $xo->fill;
    $xo->rectangle( $bb[2]-$dd, $bb[3]-$dd, $bb[2]+$dd, $bb[3]+$dd);
    $xo->fill_color("magenta");
    $xo->fill;
    # Show origin. This will cover the bb corner unless it is offset.
    $xo->rectangle( -$dd, $dd, $dd, -$dd );
    $xo->fill_color("red");
    $xo->fill;

    $xo->stroke_color("#bbbbbb");

    # Draw the grid (thick lines).
    $xo->line_width($thick);
    for ( my $x = 0; $x <= $bb[2]; $x += 5*$d ) {
	$xo->move( $x, $bb[1] );
	$xo->vline($bb[3]);
	$xo->stroke;
    }
    for ( my $x = -5*$d; $x > $bb[0]; $x -= 5*$d ) {
	next;
	$xo->move( $x, $bb[1] );
	$xo->vline($bb[3]);
	$xo->stroke;
    }
    for ( my $y = 0; $y <= $bb[3]; $y += 5*$d ) {
	$xo->move( $bb[0], $y );
	$xo->hline($bb[2]);
	$xo->stroke;
    }
    for ( my $y = -5*$d; $y > $bb[0]; $y -= 5*$d ) {
	next;
	$xo->move( $bb[0], $y );
	$xo->hline($bb[2]);
	$xo->stroke;
    }
    # Draw the grid (thin lines).
    $xo->line_width($thin);
    for ( my $x = 0; $x <= $w; $x += $d ) {
	$xo->move( $x, $bb[1] );
	$xo->vline($bb[3]);
	$xo->stroke;
    }
    for ( my $x = -$d; $x > $bb[0]; $x -= $d ) {
	$xo->move( $x, $bb[1] );
	$xo->vline($bb[3]);
	$xo->stroke;
    }
    for ( my $y = 0; $y <= $h; $y += $d ) {
	$xo->move( $bb[0], $y );
	$xo->hline($bb[2]);
	$xo->stroke;
    }
    for ( my $y = -$d; $y > $bb[1]; $y -= $d ) {
	$xo->move( $bb[0], $y );
	$xo->hline($bb[2]);
	$xo->stroke;
    }
    $xo->restore;
}

=head1 INPUT

The input SVG data B<must> be correct XML data.
The data can be a single C<< <svg> >> element, or a container
element (e.g. C<< <html> >> or C<< <xml> >>) with one or more
C<< <svg> >> elements among its children.

The SVG data can come from several sources:

=over 4

=item *

An SVG document on disk, specified as the name of the document.

=item *

A file handle, opened on a SVG document, specified as a glob
reference. You can use C<\*DATA> to append the SVG data after a
C<__DATA__> separator at the end of the program.

=item *

A string containing SVG data, specified as a reference to a scalar.

=back

The input is read using L<File::LoadLines>. See its documentation for
details.

=head1 OUTPUT

The result from calling process() is a reference to an array
containing hashes that describe the XObjects. Each hash has the
following keys:

=over 8

=item C<vbox>

The viewBox as specified in the SVG element.

If no viewBox is specified it is set to C<0 0> I<W H>, where I<W> and
I<H> are the width and the height.

=item C<bbox>

Same as the C<vbox>, but using bottom-left and top-right coordinates.

=item C<width>

The width of the XObject, as specified in the SVG element or derived
from its viewBox.

=item C<height>

The height of the XObject, as specified in the SVG element or derived
from its viewBox.

=item C<vwidth>

The desired width, as specified in the SVG element or derived
from its viewBox.

=item C<vheight>

The desired height, as specified in the SVG element or derived
from its viewBox.

=item C<xo>

The XObject itself.

=back

=head1 FONT HANDLER CALLBACK

In SVG fonts are designated by style attributes C<font-family>,
C<font-style>, C<font-weight>, and C<font-size>. How these translate
to a PDF font is system dependent. SVGPDF provides a callback
mechanism to handle this. As described at L<CONSTRUCTOR>, constructor
argument C<fc> can be set to designate a user routine.

When a font is required at the PDF level, SVGPDF first checks if a
C<@font-face> CSS rule has been set up with matching properties. If a
match is found, it is resolved and the font is set. If there is no
appropriate CSS rule for this font, the callback is called with the
following arguments:

    ( $self, %args )



( run in 1.628 second using v1.01-cache-2.11-cpan-98e64b0badf )