Mojolicious-Plugin-Badge

 view release on metacpan or  search on metacpan

lib/Mojolicious/Plugin/Badge.pm  view on Meta::CPAN

package Mojolicious::Plugin::Badge;
use Mojo::Base 'Mojolicious::Plugin';

use Carp ();
use Image::Magick;
use Mojo::ByteStream;
use Mojo::File qw(path);
use Mojo::URL;
use Mojo::UserAgent;
use Mojo::Util qw(b64_encode);
use Mojolicious::Types;

our $VERSION = '1.12';

use constant DEBUG => $ENV{BADGE_PLUGIN_DEBUG} || 0;

# Shields colors
my %SHIELDS_COLORS = (
    brightgreen => '#4c1',
    green       => '#97ca00',
    yellow      => '#dfb317',
    yellowgreen => '#a4a61d',
    orange      => '#fe7d37',
    red         => '#e05d44',
    blue        => '#007ec6',
    grey        => '#555',
    lightgrey   => '#9f9f9f',

    # Alias
    gray          => '#555',
    lightgray     => '#9f9f9f',
    critical      => '#e05d44',
    important     => '#fe7d37',
    success       => '#4c1',
    informational => '#007ec6',
    inactive      => '#9f9f9f',
);

my %NAMED_COLORS = (
    aliceblue            => '#f0f8ff',
    antiquewhite         => '#faebd7',
    aqua                 => '#00ffff',
    aquamarine           => '#7fffd4',
    azure                => '#f0ffff',
    beige                => '#f5f5dc',
    bisque               => '#ffe4c4',
    black                => '#000000',
    blanchedalmond       => '#ffebcd',
    blue                 => '#0000ff',
    blueviolet           => '#8a2be2',
    brown                => '#a52a2a',
    burlywood            => '#deb887',
    cadetblue            => '#5f9ea0',
    chartreuse           => '#7fff00',
    chocolate            => '#d2691e',
    coral                => '#ff7f50',
    cornflowerblue       => '#6495ed',
    cornsilk             => '#fff8dc',
    crimson              => '#dc143c',
    cyan                 => '#00ffff',
    darkblue             => '#00008b',
    darkcyan             => '#008b8b',
    darkgoldenrod        => '#b886b',
    darkgray             => '#a9a9a9',
    darkgreen            => '#006400',

lib/Mojolicious/Plugin/Badge.pm  view on Meta::CPAN

    sandybrown           => '#f4a460',
    seagreen             => '#2e8b57',
    seashell             => '#fff5ee',
    sienna               => '#a0522d',
    silver               => '#c0c0c0',
    skyblue              => '#87ceeb',
    slateblue            => '#6a5acd',
    slategray            => '#708090',
    slategrey            => '#708090',
    snow                 => '#fffafa',
    springgreen          => '#00ff7f',
    steelblue            => '#4682b4',
    tan                  => '#d2b48c',
    teal                 => '#008080',
    thistle              => '#d8bfd8',
    tomato               => '#ff6347',
    turquoise            => '#40e0d0',
    violet               => '#ee82ee',
    wheat                => '#f5deb3',
    white                => '#ffffff',
    whitesmoke           => '#f5f5f5',
    yellow               => '#ffff00',
    yellowgreen          => '#9acd32',
);

sub register {

    my ($self, $app, $config) = @_;

    # Config
    my $prefix = $config->{route} // $app->routes->any('/badge');
    $prefix->to(return_to => $config->{return_to} // '/');

    # Templates
    my $resources = path(__FILE__)->sibling('Badge', 'resources');
    push @{$app->renderer->paths}, $resources->child('templates')->to_string;

    # Routes
    if (!$config->{disable_api}) {
        $prefix->get('/#content' => \&_badge_api)->to('content' => undef)->name('badge');
    }

    # Helpers
    $app->helper('badge', sub { Mojo::ByteStream->new(_badge(@_)) });

}

sub _badge {

    my ($c, %options) = @_;

    my %badge_options = _build_options(%options);

    DEBUG and $c->log->debug('[Badge] User Config',  $c->app->dumper(\%options));
    DEBUG and $c->log->debug('[Badge] Badge Config', $c->app->dumper(\%badge_options));

    my $svg = $c->render_to_string('badge', format => 'svg', %badge_options);

    if ($badge_options{badge_format} eq 'png') {

        my $image = Image::Magick->new(magick => 'svg');
        $image->BlobToImage($svg);

        return $image->ImageToBlob(magick => 'png');

    }

    # Minify SVG
    $svg =~ s/^(\s+)//mg;
    $svg =~ s/\n//mg;

    return $svg;

}

sub _build_options {

    my (%options) = @_;

    my $embed_logo = $options{embed_logo};

    my $id_suffix = $options{id_suffix};

    my $logo         = $options{logo};
    my $color        = _decode_color($options{color} || 'informational');
    my $link         = $options{link};
    my $title        = $options{title};
    my $style        = $options{style}        || 'flat';
    my $badge_format = $options{badge_format} || 'svg';

    if ($style !~ /^(flat|flat-square|plastic|for-the-badge)$/) {
        Carp::croak 'Unknown badge style';
    }

    if ($badge_format !~ /^(png|svg)$/) {
        Carp::croak 'Unknown badge format';
    }

    my $label            = $options{label} || Carp::croak 'Missing label';
    my $label_color      = _decode_color($options{label_color} || 'gray');
    my $label_link       = $options{label_link};
    my $label_text_color = _decode_color($options{label_text_color} || 'white');
    my $label_title      = $options{label_title};

    my $message            = $options{message};
    my $message_link       = $options{message_link};
    my $message_text_color = _decode_color($options{message_text_color} || 'white');
    my $message_title      = $options{message_title};

    if (($label_link || $message_link) && $link) {
        Carp::croak '"link" may not bet set with "label_link" or "message_link"';
    }

    if ($style eq 'for-the-badge') {
        $label   = uc($label);
        $message = uc($message) if ($message);
    }

    my $label_text_width   = _get_text_width($label);
    my $message_text_width = 0;

lib/Mojolicious/Plugin/Badge.pm  view on Meta::CPAN


sub _embed_image {

    my $image = shift;
    my $ua    = Mojo::UserAgent->new;
    my $log   = Mojo::Log->new->context('[Badge]');

    my $content      = undef;
    my $content_type = undef;

    if ($image =~ /^http/) {

        my $url = (ref($image) ne 'Mojo::URL') ? Mojo::URL->new($image) : $image;

        $log->info("Embedding image from URL: $url");

        my $tx = $ua->get($url);

        if (my $err = $tx->error) {
            $log->error('Embed image error:', $err->{message});
            return;
        }

        my $res = $tx->result;

        $content_type = $res->headers->content_type;

        if ($content_type !~ /^image/) {
            $log->error(sprintf(q{URL doesn't contain an image (%s)}, $content_type));
            return;
        }

        $content = $res->body;

    }

    if (-e $image) {

        my $file  = (ref($image) ne 'Mojo::File') ? Mojo::File->new($image) : $image;
        my $types = Mojolicious::Types->new;

        $content_type = $types->file_type($file);

        if ($content_type !~ /^image/) {
            $log->error(sprintf('File is not an image (%s)', $content_type));
            return;
        }

        $content = $file->slurp;

    }

    return sprintf('data:%s;base64,%s', $content_type, b64_encode($content));

}

sub _get_text_width {

    my ($text, %properties) = @_;

    my $image = Image::Magick->new;

    $properties{pointsize} ||= 110;
    $properties{text}      ||= $text;
    $properties{gravity}   ||= 'northwest';
    $properties{font}      ||= 'DejaVu-Sans';

    $image->Set(size => '1x1');
    $image->ReadImage('xc:none');

    my ($x_ppem, $y_ppem, $ascender, $descender, $width, $height, $max_advance)
        = $image->QueryMultilineFontMetrics(%properties);

    return sprintf('%.0f', ($width / 10.0));

}

1;

=encoding utf8

=head1 NAME

Mojolicious::Plugin::Badge - Badge Plugin for Mojolicious

=head1 SYNOPSIS

  # Mojolicious
  $self->plugin('Badge');

  # Mojolicious::Lite
  plugin 'Badge';

  get '/my-cool-badge' => sub ($c) {

    my $badge = $c->app->badge(
      label        => 'Hello',
      message      => 'Mojo!',
      color        => 'orange'
      logo         => 'https://docs.mojolicious.org/mojo/logo.png'
      badge_format => 'png',
    );

    $c->render(data => $badge, format => 'png');

  };


=head1 DESCRIPTION

L<Mojolicious::Plugin::Badge> is a L<Mojolicious> plugin that generate "Shields.io"
like badge from L</badge> helper or via API URL (e.g. C</badge/Hello-Mojo!-orange>).

=begin html

<p>
  <img alt="Hello Mojo!" src="https://raw.github.com/giterlizzi/perl-Mojolicious-Plugin-Badge/main/examples/hello-mojo.png?raw=true">
</p>

=end html



( run in 2.556 seconds using v1.01-cache-2.11-cpan-5a3173703d6 )