File Coverage

blib/lib/SVG/Barcode/UPCA.pm
Criterion Covered Total %
statement 107 107 100.0
branch 12 14 85.7
condition 1 3 33.3
subroutine 17 17 100.0
pod 1 1 100.0
total 138 142 97.1


line stmt bran cond sub pod time code
1             package SVG::Barcode::UPCA;
2             $SVG::Barcode::UPCA::VERSION = '0.9';
3 2     2   204673 use parent 'SVG::Barcode';
  2         556  
  2         10  
4 2     2   27437 use strict;
  2         3  
  2         44  
5 2     2   9 use warnings;
  2         3  
  2         104  
6 2     2   8 use utf8;
  2         3  
  2         16  
7 2     2   54 use v5.24;
  2         4  
8 2     2   8 use feature 'signatures';
  2         2  
  2         284  
9 2     2   7 no warnings 'experimental::signatures';
  2         2  
  2         99  
10              
11 2     2   8 use POSIX 'fmax';
  2         3  
  2         15  
12 2     2   155 use Exporter 'import';
  2         3  
  2         90  
13             our @EXPORT_OK = qw|plot_upca|;
14              
15 2     2   1132 use GD::Barcode::UPCA;
  2         4174  
  2         166  
16              
17 2         2537 use constant DEFAULTS => {
18             lineheight => 50,
19             linewidth => 1,
20             quietzone => 9, # UPCA needs an explicit quiet zone left and right
21             textsize => 10,
22 2     2   17 };
  2         3  
23              
24             SVG::Barcode::_param(__PACKAGE__, $_, DEFAULTS->{$_}) for keys DEFAULTS->%*;
25              
26             # functions
27              
28 4     4 1 181954 sub plot_upca ($text, %params) {
  3         7  
  3         12  
  3         4  
29 3         33 return __PACKAGE__->new(%params)->plot($text);
30             }
31              
32             # internal methods
33              
34             # Add support for taller lines (typically at the sides and middle)
35 5     5   9 sub _plot_1d ($self, $code, $sign, $signlong) {
  5         26  
  5         28  
  5         10  
  5         9  
  5         8  
36 5         7 my @line;
37 5         9 my $width = $self->{linewidth};
38 5         12 my $height = $self->{lineheight};
39             my $add_line = sub {
40 260 100   260   347 if (@line) {
41 150         248 $self->_rect(@line);
42 150         210 @line = ();
43             }
44 5         24 };
45 5         18 for my $x (0 .. $#$code) {
46 475 100       651 if ($code->[$x] eq $sign) {
    100          
47 155 100       178 if (@line) {
48 55         62 $line[2] += $width;
49             } else {
50 100         160 @line = ($x * $width, 0, $width, $height);
51             }
52             } elsif ($code->[$x] eq $signlong) {
53             # Make a slightly taller line
54 65 100       90 if (@line) {
55 15         19 $line[2] += $width;
56             } else {
57 50         91 @line = ($x * $width, 0, $width, $height * 1.1);
58             }
59             } else {
60 255         275 $add_line->();
61             }
62             }
63 5         33 $add_line->();
64             }
65              
66 5     5   188469 sub _plot ($self, $text) {
  5         11  
  5         16  
  5         10  
67 5 50 33     65 $self->{plotter} ||= GD::Barcode::UPCA->new($text)
68             or die "Cannot create GD::Barcode::UPCA plotter: " . $GD::Barcode::UPCA::errStr;
69              
70 5         222 my @code = split //, $self->{plotter}->barcode();
71 5         812 $self->_plot_1d(\@code, '1', 'G');
72 5         39 $self->_plot_text($self->{plotter}->{text});
73             }
74              
75             # We have to add the quiet zones on the sides
76 150     150   137 sub _rect ($self, $x, $y, $width, $height, $color = $self->{foreground}) {
  150         145  
  150         140  
  150         146  
  150         164  
  150         181  
  150         195  
  150         187  
77 150         199 my $x1 = $x + $self->{margin} + $self->{quietzone};
78 150         160 my $y1 = $y + $self->{margin};
79 150         289 $self->{vbwidth} = fmax($self->{vbwidth}, $x1 + $width + $self->{quietzone});
80 150         234 $self->{vbheight} = fmax($self->{vbheight}, $y1 + $height);
81             push $self->{elements}->@*,
82 150         394 qq| |;
83 150         179 return $self;
84             }
85              
86             # Handle aligning the text below the barcode
87             # TODO: find a better way to calculate the positions relative to the bars
88 3     3   36 sub _text ($self, $text, $x_offset, $y_offset, $size, $color = $self->{foreground}) {
  3         4  
  3         6  
  3         4  
  3         5  
  3         3  
  3         9  
  3         16  
89 3 50       10 return $self if $size == 0;
90              
91 3         14 my $escaped = $self->_xml_escape($text);
92 3         34 my $margin = $self->{margin};
93 3         4 my $qz = $self->{quietzone};
94 3         5 my $width = $self->{linewidth};
95              
96             # The full barcode string is 95 modules wide.
97             # The text is split into 3 parts:
98             # - 1st digit (system character)
99             # - next 5 digits
100             # - last 5 digits + check digit
101              
102 3         21 my ($sys_char, $left_digits, $right_digits, $check_digit) =
103             $escaped =~ m/^(\d)(\d{5})(\d{5})(\d)$/;
104              
105 3         6 my $y1 = $y_offset; # Position below the shortest bars
106 3         10 $self->{vbheight} = fmax $self->{vbheight}, $y1 + $size; # Ensure height accounts for text
107              
108             # System character (1st digit)
109 3         6 my $x_sys = $margin; # Just inside the margin
110 3         13 push $self->{elements}->@*, qq| $sys_char|;
111              
112             # Left 5 digits (modules 3-47, center at 25)
113 3         12 my $x_left = $margin + $qz + (40 * $width) - (length($left_digits) * $size / 2);
114 3         23 push $self->{elements}->@*, qq| $left_digits|;
115              
116             # Right 5 digits + check digit (modules 48-92, center at 70)
117 3         8 my $x_right = $margin + $qz + (80 * $width) - (length($right_digits) * $size / 2);
118 3         10 push $self->{elements}->@*, qq| $right_digits|;
119              
120             # Check digit (last digit)
121 3         5 my $x_check = $margin + $qz *2 + 90; # Approximately just inside the margin
122 3         13 push $self->{elements}->@*, qq| $check_digit|;
123 3         23 return $self;
124             }
125              
126             1;
127              
128             __END__