File Coverage

blib/lib/PDF/Make/Builder/Layout/Cell.pm
Criterion Covered Total %
statement 83 87 95.4
branch 21 28 75.0
condition 23 49 46.9
subroutine 12 12 100.0
pod 0 4 0.0
total 139 180 77.2


line stmt bran cond sub pod time code
1             package PDF::Make::Builder::Layout::Cell;
2 42     42   192 use strict;
  42         61  
  42         1074  
3 42     42   119 use warnings;
  42         55  
  42         1601  
4 42     42   6735 use overload '%{}' => '_as_hashref', fallback => 1;
  42         18420  
  42         283  
5 42     42   3021 use Object::Proto;
  42         84  
  42         2132  
6 42     42   167 use PDF::Make::Builder::Font;
  42         65  
  42         2329  
7              
8             BEGIN {
9 42     42   2919 Object::Proto::define('PDF::Make::Builder::Layout::Cell',
10             'row',
11             'weight:Num:default(1)',
12             'align:Str:default(left)',
13             'valign:Str:default(top)',
14             'pad:Num:default(5)',
15             'bg:Str',
16             'border:Str',
17             'text_border:Str',
18             'text_border_width:Num:default(0.5)',
19             'wrap_slack:Num:default(0)',
20             'content:ArrayRef:default([])',
21             );
22 42         39071 Object::Proto::import_accessors('PDF::Make::Builder::Layout::Cell');
23             }
24              
25             sub text {
26 26     26 0 2217 my ($self, $str, %args) = @_;
27 26         31 my $content = content $self;
28 26         61 push @$content, { type => 'text', text => $str, %args };
29 26         49 return $self;
30             }
31              
32             sub image {
33 2     2 0 637 my ($self, $path, %args) = @_;
34 2         3 my $content = content $self;
35 2         7 push @$content, { type => 'image', path => $path, %args };
36 2         4 return $self;
37             }
38              
39             sub _as_hashref {
40 2     2   545 my ($self) = @_;
41             return {
42 2         16 row => row($self),
43             weight => weight($self),
44             align => align($self),
45             valign => valign($self),
46             pad => pad($self),
47             bg => bg($self),
48             border => border($self),
49             text_border => text_border($self),
50             text_border_width => text_border_width($self),
51             wrap_slack => wrap_slack($self),
52             content => content($self),
53             };
54             }
55              
56             sub _resolve_item_font {
57 34     34   44 my ($self, $base_font, $item) = @_;
58              
59             return $base_font
60             if !defined($item->{size})
61             && !defined($item->{family})
62             && !defined($item->{bold})
63 34 50 66     166 && !defined($item->{italic});
      33        
      33        
64              
65             return PDF::Make::Builder::Font->new(
66             colour => $item->{colour} // $base_font->colour,
67             size => $item->{size} // $base_font->size,
68             family => $item->{family} // $base_font->family,
69             bold => $item->{bold} // $base_font->bold,
70             italic => $item->{italic} // $base_font->italic,
71 16   66     169 line_height => $item->{line_height} // $base_font->effective_line_height,
      33        
      33        
      33        
      33        
      33        
72             );
73             }
74              
75             sub measure_height {
76 10     10 0 19 my ($self, $font, $cell_w) = @_;
77 10         12 my $h = 0;
78 10         20 my $inner_w = $cell_w - 2 * (pad $self);
79 10         11 for my $item (@{content $self}) {
  10         13  
80 11 100       27 if ($item->{type} eq 'text') {
    50          
81 10         39 my $item_font = $self->_resolve_item_font($font, $item);
82 10   66     35 my $sz = $item->{size} // $item_font->size;
83 10   33     28 my $lh = $item->{line_height} // $sz;
84 10         26 my $text_w = $item_font->measure_text($item->{text});
85 10   50     30 my $lines = int($text_w / ($inner_w || 1)) + 1;
86 10         27 $h += $lines * $lh;
87             } elsif ($item->{type} eq 'image') {
88 1   50     2 $h += $item->{h} // 50;
89             }
90             }
91 10         53 return $h;
92             }
93              
94             sub render_content {
95 24     24 0 45 my ($self, $canvas, $font, $page, $cx, $cy, $cw, $ch) = @_;
96              
97 24 50       52 if (my $tb = text_border $self) {
98 0         0 my ($r, $g, $b) = $font->hex_to_rgb($tb);
99 0         0 my $bw = text_border_width $self;
100 0         0 $canvas->q->w($bw)->RG($r, $g, $b)->re($cx, $cy, $cw, $ch)->S->Q;
101             }
102              
103 24         53 my $text_y = $cy + $ch - (pad $self);
104 24         33 my $align = align $self;
105              
106 24         25 for my $item (@{content $self}) {
  24         32  
107 25 100       54 next unless $item->{type} eq 'text';
108              
109 24         34 my $item_font = $self->_resolve_item_font($font, $item);
110 24   66     59 my $sz = $item->{size} // $item_font->size;
111 24   33     53 my $lh = $item->{line_height} // $sz;
112 24   66     70 my $colour = $item->{colour} // $item_font->colour;
113 24         43 my ($r, $g, $b) = $item_font->hex_to_rgb($colour);
114 24         69 my $res = $item_font->ensure_loaded($page->xs_page);
115              
116 24         97 my @words = split /\s+/, $item->{text};
117 24         33 my $line = '';
118 24         27 my $line_w = 0;
119              
120 24         27 for my $word (@words) {
121 176 100       243 my $candidate = $line eq '' ? $word : ($line . ' ' . $word);
122 176         231 my $test = $item_font->measure_text($candidate);
123 176         178 my $slack = wrap_slack $self;
124              
125 176 100 66     302 if ($test > $cw + $slack && $line ne '') {
126 12         14 $text_y -= $lh;
127 12 50       16 last if $text_y < $cy;
128 12         11 my $tx = $cx;
129 12 100       19 if ($align eq 'center') {
    50          
130 9         13 $tx = $cx + ($cw - $line_w) / 2;
131             } elsif ($align eq 'right') {
132 0         0 $tx = $cx + $cw - $line_w;
133             }
134 12         121 $canvas->BT->Tf($res, $sz)->rg($r, $g, $b)
135             ->Td($tx, $text_y)->Tj($line)->ET;
136 12         15 $line = $word;
137 12         19 $line_w = $item_font->measure_text($line);
138             } else {
139 164         168 $line = $candidate;
140 164         179 $line_w = $test;
141             }
142             }
143              
144 24 50       42 if ($line ne '') {
145 24         25 $text_y -= $lh;
146 24 50       33 if ($text_y >= $cy) {
147 24         29 my $tx = $cx;
148 24 100       46 if ($align eq 'center') {
    100          
149 4         8 $tx = $cx + ($cw - $line_w) / 2;
150             } elsif ($align eq 'right') {
151 3         5 $tx = $cx + $cw - $line_w;
152             }
153 24         355 $canvas->BT->Tf($res, $sz)->rg($r, $g, $b)
154             ->Td($tx, $text_y)->Tj($line)->ET;
155             }
156             }
157             }
158             }
159              
160             1;