Data-URIID

 view release on metacpan or  search on metacpan

lib/Data/URIID/Barcode.pm  view on Meta::CPAN

# Copyright (c) 2025 Philipp Schafft

# licensed under Artistic License 2.0 (see LICENSE file)

# ABSTRACT: Extractor for identifiers from URIs

package Data::URIID::Barcode;

use v5.16;
use strict;
use warnings;

use Carp;
use Scalar::Util qw(weaken);

our $VERSION = v0.20;

use parent 'Data::URIID::Base';

use constant {map {$_ => []} qw(TYPE_UNKNOWN TYPE_OTHER TYPE_QRCODE TYPE_EAN13 TYPE_EAN8)};

my %_type_info = (
    TYPE_UNKNOWN()  => {
        type    => TYPE_UNKNOWN,
        special => 1,
    },
    TYPE_OTHER()    => {
        type    => TYPE_OTHER,
        special => 1,
    },
    TYPE_QRCODE()   => {
        type    => TYPE_QRCODE,
        aliases => [qw(qrcode qr-code)],
    },
    TYPE_EAN13()    => {
        type    => TYPE_EAN13,
        aliases => [qw(ean13 ean-13)],
    },
    TYPE_EAN8()     => {
        type    => TYPE_EAN8,
        aliases => [qw(ean8 ean-8)],
    },
);



sub sheet {
    my ($pkg, %opts) = @_;
    my $from        = delete $opts{from};
    my $filename    = delete $opts{filename};
    my $template    = delete $opts{template};
    my $values      = delete $opts{values};
    my $filter_type = delete $opts{filter_type};
    my $filter_data = delete $opts{filter_data};
    my %pass_opts;
    my @res;
    my $done;

    foreach my $key (qw(extractor type)) {
        $pass_opts{$key} = delete $opts{$key} // next;;
    }

    if (!defined($from) && defined($values)) {
        @res = map {{barcode => $_, quality => 0.001}}
               map {$pkg->new(%pass_opts, ref($_) ? (from => $_) : (data => sprintf($template // '%s', $_)))}
               @{$values};
               $done = 1;
    } elsif (!defined($from) && defined($filename)) {
        require Image::Magick;
        $from = Image::Magick->new();
        $from->Read($filename) && croak 'Cannot read file';
    }

    croak 'Stray options passed' if scalar keys %opts;

lib/Data/URIID/Barcode.pm  view on Meta::CPAN

        } else {
            @res = grep {$_->{barcode}->{data} =~ $filter_data} @res;
        }
    }

    if (wantarray) {
        return map {$_->{barcode}} @res;
    } else {
        my $max_length;

        croak 'No code found' unless scalar @res;

        foreach my $res (@res) {
            my $barcode = $res->{barcode};
            my $length = length($barcode->data);
            $max_length = $length if !defined($max_length) || $max_length < $length;
        }

        foreach my $res (@res) {
            my $barcode = $res->{barcode};
            $res->{quality} *= $barcode->_quality_by_type * (length($barcode->data) / $max_length);
        }

        return (sort {$b->{quality} <=> $a->{quality}} @res)[0]{barcode};
    }
}


sub new {
    my ($pkg, %opts) = @_;
    my __PACKAGE__ $self;

    if (defined(my $from = delete($opts{from}))) {
        $self = eval {$pkg->sheet(from => $from)};
        return $self if defined $self;

        if (eval {$from->isa('Data::URIID::Base')}) {
            $opts{extractor} //= $from->extractor(default => undef);
        }

        if (eval {$from->isa('Data::URIID::Result')}) {
            $opts{data} //= $from->url->as_string;
            $opts{type} //= TYPE_QRCODE;
        } elsif (eval {$from->isa('Data::URIID::Base')}) {
            $opts{data} //= $from->ise;
            $opts{type} //= TYPE_QRCODE;
        } elsif (eval {$from->isa('Data::Identifier')}) {
            $opts{data} //= $from->ise;
            $opts{type} //= TYPE_QRCODE;
        } elsif (eval {$from->isa('URI')}) {
            $opts{data} //= $from->as_string;
            $opts{type} //= TYPE_QRCODE;
        } else {
            croak 'Unsupported/invalid from type';
        }
    }

    croak 'No type given' unless defined $opts{type};
    croak 'No data given' unless defined $opts{data};

    weaken($opts{extractor});

    $self = bless \%opts, $pkg;

    return $self;
}


sub data {
    my ($self, %opts) = @_;
    delete $opts{default};
    delete $opts{no_defaults};

    croak 'Stray options passed' if scalar keys %opts;

    return $self->{data};
}


sub type {
    my ($self, %opts) = @_;
    delete $opts{default};
    delete $opts{no_defaults};

    croak 'Stray options passed' if scalar keys %opts;

    return $self->{type};
}


sub has_type {
    my ($self, $type, %opts) = @_;
    delete $opts{default};
    delete $opts{no_defaults};

    croak 'Stray options passed' if scalar keys %opts;
    croak 'No type passed' unless defined $type;

    if (ref($type) && !exists $_type_info{$type}) {
        foreach my $t (@{$type}) {
            return 1 if $self->{type} == $t;
        }
    }

    return $self->{type} == $type;
}


sub render {
    my ($self, %opts) = @_;
    my $filename = delete $opts{filename};
    my $success;

    croak 'Stray options passed' if scalar keys %opts;

    eval {
        if ($self->has_type(TYPE_QRCODE)) {
            require Imager::QRCode;

            my $qrcode = Imager::QRCode->new(level => 'H');
            my $img = $qrcode->plot($self->data);



( run in 2.704 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )