App-Music-ChordPro

 view release on metacpan or  search on metacpan

lib/ChordPro/Output/PDF/KeyboardDiagram.pm  view on Meta::CPAN


my $dcache;			# cache core grids
my $pdf = "";			# for cache flush

class ChordPro::Output::PDF::KeyboardDiagram;

field $pr       :param;

field $config;
field $ps;

field $kw;
field $kh;
field $lw;
field $fg;
field $keys;
field $base;
field $base_k;
field $show;
field $pressed;

ADJUST {
    $config	  = $::config;
    $ps		  = $pr->{ps};
    my $ctl	  = $ps->{kbdiagrams};
    $kw		  = $ctl->{width} || 6;
    $kh		  = $ctl->{height} || 6;
    $lw		  = ($ctl->{linewidth} || 0.10) * $kw;
    $keys	  = $ctl->{keys};
    $base	  = $ctl->{base};
    $show	  = $ctl->{show};
    $pressed	  = $ctl->{pressed};
    $dcache = {} if $pr->{pdf} ne $pdf;
    $pdf          = $pr->{pdf};

    unless ( $keys =~ /^(?:7|10|14|17|21)$/ ) {
	die("pdf.kbdiagrams.keys is $keys, must be one of 7, 10, 14, 17, or 21\n");
    }

    unless ( $base =~ /^(?:C|F)$/i ) {
	die("pdf.kbdiagrams.base is \"$base\", must be \"C\" or \"F\"\n");
    }
    if ( uc($base) eq 'C' ) {
	$base = $base_k = 0;
    }
    else {			# must be 'F'
	$base_k = 3;
	$base = 5;
    }

    unless ( $show =~ /^(?:top|bottom|right|below)$/i ) {
	die("pdf.kbdiagrams.show is \"$show\", must be one of ".
	    "\"top\", \"bottom\", \"right\", or \"below\"\n");
    }
}

use constant DIAG_DEBUG => 0;

# The vertical space the diagram requires.
method vsp0 ( $elt, $dummy = 0 ) {
    $ps->{fonts}->{diagram}->{size} * 1.2 + $kh + $lw;
}

# The advance height.
method vsp1 ( $elt, $dummy = 0 ) {
    $ps->{kbdiagrams}->{vspace} * $kh;
}

# The vertical space the diagram requires, including advance height.
method vsp ( $elt, $dummy = 0 ) {
    $self->vsp0($elt) + $self->vsp1($elt);
}

# The horizontal space the diagram requires.
method hsp0 ( $elt, $dummy = 0 ) {
    $lw + $keys * $kw;
}

# The advance width.
method hsp1 ( $elt, $dummy = 0 ) {
    $ps->{kbdiagrams}->{hspace} * $kw;
}

# The horizontal space the diagram requires, including advance width.
method hsp ( $elt, $dummy = 0 ) {
    $self->hsp0($elt) + $self->hsp1($ps);
}

my %keytypes =
  (  0 => [0,"L"],		# Left
     1 => [0,"B"],		# Black
     2 => [1,"M"],		# Middle
     3 => [1,"B"],
     4 => [2,"R"],		# Right
     5 => [3,"L"],
     6 => [3,"B"],
     7 => [4,"M"],
     8 => [4,"B"],
     9 => [5,"M"],
    10 => [5,"B"],
    11 => [6,"R"] );


# The actual draw method.
method draw ( $info, $x, $y, $dummy = 0 ) {
    return unless $info;
    my $w = $lw + $kw * $keys;
    $fg = $info->{diagram} // $ps->{theme}->{foreground};

    # Get (or infer) keys.
    my @keys = @{ChordPro::Chords::get_keys($info)};
    unless ( @keys ) {
	warn("PDF: No diagram for chord \"", $info->name, "\"\n");
    }

    my $font = $ps->{fonts}->{diagram};

    my $xo = $self->diagram_xo($info);
    my @bb = $xo->bbox;
    warn("BB [ @bb ] $x $y\n") if DIAG_DEBUG;
    $pr->{pdfgfx}->object( $xo, $x,
			   $y - ($font->{size} * 1.2 + $lw) );

    # Draw font name.
    $pr->setfont($font);
    my $name = $info->chord_display;
    $name .= "?" unless @keys;
    $name = "<span color='$fg'>$name</span>"
      if $info->{diagram};
    $pr->text( $name, $x + ($w - $pr->strwidth($name))/2, $y - $pr->font_bl($font) );
}

# Returns the complete diagram as an xo. This includes the core grid
# and pressed keys.
# Bounding box origin is top left of the grid.
# Note that the chord name is not part of the diagram.

method diagram_xo ($info) {
    return unless $info;

    my $col = $pressed // "red";
    $fg = $info->{diagram} // $fg // $ps->{theme}->{foreground};
    my $w = $lw + $kw * $keys;
    my $v = $kh;

    # Get (or infer) keys.
    my @keys = @{ChordPro::Chords::get_keys($info)};
    unless ( @keys ) {
	warn("PDF: No diagram for chord \"", $info->name, "\"\n");
    }

    my $xo = $pdf->xo_form;

    # Draw the core grid.
    $xo->line_width($lw);
    my $xg = $self->grid_xo;
    $xo->bbox( $xg->bbox );
    $xo->object( $xg, 0, 0, 1 );

    my $kk = ( $keys % 7 == 0 )
      ? 12 * int( $keys / 7 )
      : $keys == 10 ? 17 : 29;

    # Vertical offsets in the key image.
    my $t  = 0;
    my $m  = $t - $kh / 2;
    my $b  = $t - $kh;

    # Horizontal offsets in the key image.
    my $l  = 0;
    my $ml = $l + 1 * $kw / 3;
    my $mr = $l + 2 * $kw / 3;
    my $r  = $l + $kw; # 3 * $kw / 3;
    my $xr = $l + 4 * $kw / 3;

    # Don't use theme colour, use black & white.
    $xo->stroke_color($fg);
    $xo->fill_color($col);

    # Shift down if would start in 2nd octave.
    my $kd = -int(($keys[0] + $info->{root_ord}) / 12) * 12;
    # Adjust for diagram start.
    $kd+=12 if ($keys[0] + $info->{root_ord}) < $base;

    for my $key ( @keys ) {
	$key += $kd + $info->{root_ord};
	$key += 12 if $key < 0;
	$key -= 12 while $key >= $kk;
	# Get octave and reduce.
	my $o = int( $key / 12 ); # octave



( run in 0.484 second using v1.01-cache-2.11-cpan-5735350b133 )