File Coverage

blib/lib/PDF/Builder/Basic/PDF/String.pm
Criterion Covered Total %
statement 70 71 98.5
branch 19 20 95.0
condition 5 9 55.5
subroutine 9 9 100.0
pod 6 6 100.0
total 109 115 94.7


line stmt bran cond sub pod time code
1             #=======================================================================
2             #
3             # THIS IS A REUSED PERL MODULE, FOR PROPER LICENCING TERMS SEE BELOW:
4             #
5             # Copyright Martin Hosken
6             #
7             # No warranty or expression of effectiveness, least of all regarding
8             # anyone's safety, is implied in this software or documentation.
9             #
10             # This specific module is licensed under the Perl Artistic License.
11             # Effective 28 January 2021, the original author and copyright holder,
12             # Martin Hosken, has given permission to use and redistribute this module
13             # under the MIT license.
14             #
15             #=======================================================================
16             package PDF::Builder::Basic::PDF::String;
17              
18 41     41   61774 use base 'PDF::Builder::Basic::PDF::Objind';
  41         83  
  41         5832  
19              
20 41     41   262 use strict;
  41         81  
  41         885  
21 41     41   216 use warnings;
  41         77  
  41         42172  
22              
23             our $VERSION = '3.024'; # VERSION
24             our $LAST_UPDATE = '3.024'; # manually update whenever code is changed
25              
26             =head1 NAME
27              
28             PDF::Builder::Basic::PDF::String - PDF String type objects and superclass
29             for simple objects that are basically stringlike (Number, Name, etc.)
30              
31             =head1 METHODS
32              
33             =over
34              
35             =cut
36              
37             our %trans = (
38             'n' => "\n",
39             'r' => "\r",
40             't' => "\t",
41             'b' => "\b",
42             'f' => "\f",
43             "\\" => "\\",
44             '(' => '(',
45             ')' => ')',
46             );
47              
48             our %out_trans = (
49             "\n" => 'n',
50             "\r" => 'r',
51             "\t" => 't',
52             "\b" => 'b',
53             "\f" => 'f',
54             "\\" => "\\",
55             '(' => '(',
56             ')' => ')',
57             );
58              
59             =item PDF::Builder::Basic::PDF::String->from_pdf($string)
60              
61             Creates a new string object (not a full object yet) from a given
62             string. The string is parsed according to input criteria with
63             escaping working.
64              
65             =cut
66              
67             sub from_pdf {
68 969     969 1 1646 my ($class, $str) = @_;
69 969         1388 my $self = {};
70              
71 969         1398 bless $self, $class;
72 969         1754 $self->{'val'} = $self->convert($str);
73 969         1513 $self->{' realised'} = 1;
74 969         1928 return $self;
75             }
76              
77             =item PDF::Builder::Basic::PDF::String->new($string)
78              
79             Creates a new string object (not a full object yet) from a given
80             string. The string is parsed according to input criteria with
81             escaping working.
82              
83             =cut
84              
85             sub new {
86 32098     32098 1 44036 my ($class, $str) = @_;
87 32098         40716 my $self = {};
88              
89 32098         39928 bless $self, $class;
90 32098         49728 $self->{'val'} = $str;
91 32098         38953 $self->{' realised'} = 1;
92 32098         62420 return $self;
93             }
94              
95             =item $s->convert($str)
96              
97             Returns $str converted as per criteria for input from PDF file
98              
99             =cut
100              
101             sub convert {
102 30     30 1 58 my ($self, $input) = @_;
103 30         44 my $output = '';
104              
105             # Hexadecimal Strings (PDF 1.7 section 7.3.4.3)
106 30 100       94 if ($input =~ m|^\s*\<|o) {
107 3         7 $self->{' ishex'} = 1;
108 3         7 $output = $input;
109              
110             # Remove any extraneous characters to simplify processing
111 3         14 $output =~ s/[^0-9a-f]+//gio;
112 3         9 $output = "<$output>";
113              
114             # Convert each sequence of two hexadecimal characters into a byte
115 3         15 1 while $output =~ s/\<([0-9a-f]{2})/chr(hex($1)) . '<'/oige;
  7         39  
116              
117             # If a single hexadecimal character remains, append 0 and
118             # convert it into a byte.
119 3         9 $output =~ s/\<([0-9a-f])\>/chr(hex($1 . '0'))/oige;
  1         4  
120              
121             # Remove surrounding angle brackets
122 3         7 $output =~ s/\<\>//og;
123             }
124              
125             # Literal Strings (PDF 1.7 section 7.3.4.2)
126             else {
127             # Remove surrounding parentheses
128 27         165 $input =~ s/^\s*\((.*)\)\s*$/$1/os;
129              
130 27         43 my $cr = '(?:\015\012|\015|\012)';
131 27         32 my $prev_input;
132 27         62 while ($input) {
133 52 50 66     129 if (defined $prev_input and $input eq $prev_input) {
134 0         0 die "Infinite loop while parsing literal string";
135             }
136 52         62 $prev_input = $input;
137              
138             # Convert bachslash followed by up to three octal digits
139             # into that binary byte
140 52 100       422 if ($input =~ /^\\([0-7]{1,3})(.*)/os) {
    100          
    100          
    100          
    100          
141 13         31 $output .= chr(oct($1));
142 13         29 $input = $2;
143             }
144             # Convert backslash followed by an escaped character into that
145             # character
146             elsif ($input =~ /^\\([nrtbf\\\(\)])(.*)/osi) {
147 9         23 $output .= $trans{$1};
148 9         21 $input = $2;
149             }
150             # Ignore backslash followed by an end-of-line marker
151             elsif ($input =~ /^\\$cr(.*)/os) {
152 5         12 $input = $1;
153             }
154             # Convert an unescaped end-of-line marker to a line-feed
155             elsif ($input =~ /^\015\012?(.*)/os) {
156 2         4 $output .= "\012";
157 2         5 $input = $1;
158             }
159             # Check to see if there are any other special sequences
160             elsif ($input =~ /^(.*?)((?:\\(?:[nrtbf\\\(\)0-7]|$cr)|\015\012?).*)/os) {
161 10         21 $output .= $1;
162 10         23 $input = $2;
163             }
164             else {
165 13         146 $output .= $input;
166 13         141 $input = undef;
167             }
168             }
169             }
170              
171 30         88 return $output;
172             }
173              
174             =item $s->val()
175              
176             Returns the value of this string (the string itself).
177              
178             =cut
179              
180             sub val {
181 2577     2577 1 6944 return $_[0]->{'val'};
182             }
183              
184             =item $s->as_pdf()
185              
186             Returns the string formatted for output as PDF for PDF File object $pdf.
187              
188             =cut
189              
190             sub as_pdf {
191 195     195 1 454 my ($self) = @_;
192 195         432 my $str = $self->{'val'};
193              
194 195 100 66     1508 if ($self->{' ishex'}) { # imported as hex ?
    100 33        
195 1         6 $str = unpack('H*', $str);
196 1         5 return "<$str>";
197             } elsif ($self->{' isutf'} or
198             (utf8::is_utf8($str) and
199             $str =~ /[^[:ascii:]]/)) {
200 1         8 $str = join('', map { sprintf('%04X' , $_) } unpack('U*', $str) );
  3         12  
201 1         8 return "";
202             } else {
203 193 100       784 if ($str =~ m/[^\n\r\t\b\f\040-\176\200-\377]/) {
204 3         12 $str =~ s/(.)/sprintf('%02X', ord($1))/sge;
  18         48  
205 3         16 return "<$str>";
206             } else {
207 190         500 $str =~ s/([\n\r\t\b\f\\()])/\\$out_trans{$1}/g;
208 190         834 return "($str)";
209             }
210             }
211             }
212              
213             =item $s->outobjdeep($fh, $pdf)
214              
215             Outputs the string in PDF format, complete with necessary conversions.
216              
217             =cut
218              
219             sub outobjdeep {
220 12778     12778 1 16459 my ($self, $fh, $pdf) = @_;
221              
222 12778         20442 $fh->print($self->as_pdf($pdf));
223 12778         61560 return;
224             }
225              
226             =back
227              
228             =cut
229              
230             1;