App-Music-ChordPro
view release on metacpan or search on metacpan
lib/ChordPro/Delegate/TextBlock.pm view on Meta::CPAN
#! perl
use v5.26;
use strict;
use warnings;
use feature qw( signatures );
no warnings "experimental::signatures";
use utf8;
package ChordPro::Delegate::TextBlock;
# Combine one or more text lines into a single xforms object.
#
# Attributes:
#
# width: Width of the resultant object.
# Defaults to the actual width (tight fit) of the texts.
# height: Height of the resultant object.
# Defaults to the actual height of the text, including
# the advance of the last line (non-tight fit).
# When height or padding is set, a tight fit is used.
# padding: Provide padding between the object and the inner text.
# When height or padding is set, a tight fit is used.
# flush: Horizontal text flush (left, center, right).
# vflush: Vertical text flush (top, middle, bottom).
# textstyle: Style (font) to be used. Must be one of "text", "chords",
# "comment" etc.
# textsize: Initial value for the text size.
# textspacing: Text spacing. A factor (e.g. 1.2) or "flex".
# textcolor: Initial color for the text.
# background: Background color of the object.
#
# Common attributes:
#
# id: Make asset instead of image.
# align: Image alignment (left, center, right)
# border: Draw border around the image.
use ChordPro::Utils;
sub DEBUG() { $::config->{debug}->{txtblk} }
sub txt2xform( $self, %args ) {
my $elt = $args{elt};
my $ps = $self->{_ps};
my $pr = $ps->{pr};
my $opts = { %{$elt->{opts}} };
# Text style must be one of the known styles (text, chord, comment, ...).
my $style = delete($opts->{textstyle}) // "text";
unless ( defined($ps->{fonts}->{$style} ) ) {
warn("TextBlock: Unknown style \"$style\", using \"text\"\n");
$style = "text";
}
my $font = $ps->{fonts}->{$style};
my $bgcol = $pr->_bgcolor($font->{background});
$bgcol = "" if $bgcol eq "none";
my $vsp = delete($opts->{textspacing}) // "flex";
my $sp = $vsp eq "flex"
? ($font->{leading} || $ps->{spacing}->{$style} || 1) : $vsp;
my $size = fontsize( delete($opts->{textsize}), $font->{size} );
my $color = delete($opts->{textcolor}) // $font->{color};
my $flush = delete($opts->{flush}) // "left";
my $vflush = delete($opts->{vflush}) // "top";
$color = $ps->{pr}->_fgcolor($color) if $color;
my $data = $elt->{data};
if ( $color || $bgcol ) {
my $span = "";
$span .= " color='$color'" if $color;
$span .= " background='$bgcol'" if $bgcol;
$data = [ map { "<span$span>$_</span>" } @$data ];
}
my $padding = delete($opts->{padding});
# New xo.
my $xo = $pr->{pdf}->xo_form;
# Pre-pass to establish the actual width/height.
my ( $awidth, $aheight ) = ( 0, undef );
my ( $w, $h );
for ( @$data ) {
( $w, $h ) = $pr->strwidth( $_, $font, $size );
$awidth = $w if $w > $awidth;
if ( defined($aheight) ) {
$aheight += $vsp eq "flex" ? ($h||$size)*$sp : $size*$vsp;
}
else {
$aheight = ($h||$size);
}
}
# Desired width (includes padding).
my ( $width, $height );
if ( $width = delete($opts->{width}) ) {
# Note that using dimension is not yet operational.
$width = dimension( $width, width => $size ) - 2*($padding||0);
}
else {
$width = $awidth;
}
# Correction for tight y-fit.
my $ycorr = 0;#($vsp eq "flex" ? $h||$size : $size) * ($sp - 1);
# Desired height (includes padding).
if ( $height = delete($opts->{height}) ) {
# Note that using dimension is not yet operational.
$height = dimension( $height, width => $size ) - 2*($padding||0);
}
else {
$height = $aheight - $ycorr;
$ycorr = 0 unless defined($padding);
}
# Width and height are now the 'inner' box (w/o padding).
# With padding, we cancel the leading after the last line.
if ( defined $padding ) {
$ycorr = 0;
}
else {
$padding = 0;
}
if ( $::config->{debug}->{txtblk} ) {
warn("tb: w = $width, h = $height, p = $padding, sp = $sp, c = $ycorr\n");
}
# Note that the padding will be dealt with in the bbox.
# Draw background.
$xo->bbox( -$padding, -$padding, $width+$padding, $height+$padding );
if ( my $bg = delete($opts->{background}) ) {
$xo->rectangle( $xo->bbox );
$xo->fill_color($bg);
$xo->fill;
}
# Put it in text mode.
$xo->textstart;
my $y = $height - $ycorr;
if ( $flush eq "right" || $flush eq "center"
|| $vflush eq "middle" || $vflush eq "bottom" ) {
if ( $vflush eq "middle" ) {
$y += ($aheight-$height)/2;
}
elsif ( $vflush eq "bottom" ) {
$y += $aheight - $height;
}
for ( @$data ) {
my $h = $pr->strheight( $_, $font, $size ) || $size;
$pr->{tmplayout}->set_width($width);
$pr->{tmplayout}->set_alignment($flush);
$pr->{tmplayout}->show( 0, $y, $xo );
$y -= ($vsp eq "flex" ? $h : $size) * $sp;
}
}
else { # assume top/left
for ( @$data ) {
my $h = $pr->strheight( $_, $font, $size ) || $size;
$pr->{tmplayout}->set_alignment($flush);
$pr->{tmplayout}->show( 0, $y, $xo );
$y -= ($vsp eq "flex" ? $h : $size) * $sp;
}
}
# Finish.
$xo->textend;
return
{ type => "image",
subtype => "xform",
line => $elt->{line},
data => $xo,
width => $width + 2*$padding,
height => $height + 2*$padding,
opts => { align => "left", %$opts },
};
}
sub txt2html( $self, %args ) {
my $elt = $args{elt};
require ChordPro::Output::Common;
my @lines = map { ChordPro::Output::Common::encode_html($_) } @{ $elt->{data} // [] };
return { type => "html",
data => [ join( "<br/>\n", @lines ) ]
};
}
( run in 0.624 second using v1.01-cache-2.11-cpan-f56aa216473 )