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 <Martin_Hosken@sil.org>
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 42     42   99886 use base 'PDF::Builder::Basic::PDF::Objind';
  42         92  
  42         6816  
19              
20 42     42   354 use strict;
  42         85  
  42         1449  
21 42     42   218 use warnings;
  42         179  
  42         66321  
22              
23             our $VERSION = '3.028'; # VERSION
24             our $LAST_UPDATE = '3.027'; # manually update whenever code is changed
25              
26             =head1 NAME
27              
28             PDF::Builder::Basic::PDF::String - PDF String type objects
29              
30             Superclass for simple objects that are basically stringlike (Number, Name, etc.)
31              
32             Inherits from L<PDF::Builder::Basic::PDF::Objind>
33              
34             =head1 METHODS
35              
36             =cut
37              
38             our %trans = (
39             'n' => "\n",
40             'r' => "\r",
41             't' => "\t",
42             'b' => "\b",
43             'f' => "\f",
44             "\\" => "\\",
45             '(' => '(',
46             ')' => ')',
47             );
48              
49             our %out_trans = (
50             "\n" => 'n',
51             "\r" => 'r',
52             "\t" => 't',
53             "\b" => 'b',
54             "\f" => 'f',
55             "\\" => "\\",
56             '(' => '(',
57             ')' => ')',
58             );
59              
60             =head2 from_pdf
61              
62             PDF::Builder::Basic::PDF::String->from_pdf($string)
63              
64             =over
65              
66             Creates a new string object (not a full object yet) from a given
67             string. The string is parsed according to input criteria with
68             escaping working.
69              
70             =back
71              
72             =cut
73              
74             sub from_pdf {
75 973     973 1 244391 my ($class, $str) = @_;
76 973         1801 my $self = {};
77              
78 973         1795 bless $self, $class;
79 973         2374 $self->{'val'} = $self->convert($str);
80 973         2231 $self->{' realised'} = 1;
81 973         2579 return $self;
82             }
83              
84             =head2 new
85              
86             PDF::Builder::Basic::PDF::String->new($string)
87              
88             =over
89              
90             Creates a new string object (not a full object yet) from a given
91             string. The string is parsed according to input criteria with
92             escaping working.
93              
94             =back
95              
96             =cut
97              
98             sub new {
99 24787     24787 1 47917 my ($class, $str) = @_;
100 24787         48319 my $self = {};
101              
102 24787         40210 bless $self, $class;
103 24787         52370 $self->{'val'} = $str;
104 24787         40588 $self->{' realised'} = 1;
105 24787         65659 return $self;
106             }
107              
108             =head2 convert
109              
110             $s->convert($str)
111              
112             =over
113              
114             Returns $str converted as per criteria for input from PDF file
115              
116             =back
117              
118             =cut
119              
120             sub convert {
121 30     30 1 57 my ($self, $input) = @_;
122 30         42 my $output = '';
123              
124             # Hexadecimal Strings (PDF 1.7 section 7.3.4.3)
125 30 100       138 if ($input =~ m|^\s*\<|o) {
126 3         7 $self->{' ishex'} = 1;
127 3         4 $output = $input;
128              
129             # Remove any extraneous characters to simplify processing
130 3         15 $output =~ s/[^0-9a-f]+//gio;
131 3         5 $output = "<$output>";
132              
133             # Convert each sequence of two hexadecimal characters into a byte
134 3         15 1 while $output =~ s/\<([0-9a-f]{2})/chr(hex($1)) . '<'/oige;
  7         35  
135              
136             # If a single hexadecimal character remains, append 0 and
137             # convert it into a byte.
138 3         5 $output =~ s/\<([0-9a-f])\>/chr(hex($1 . '0'))/oige;
  1         45  
139              
140             # Remove surrounding angle brackets
141 3         10 $output =~ s/\<\>//og;
142             }
143              
144             # Literal Strings (PDF 1.7 section 7.3.4.2)
145             else {
146             # Remove surrounding parentheses
147 27         237 $input =~ s/^\s*\((.*)\)\s*$/$1/os;
148              
149 27         54 my $cr = '(?:\015\012|\015|\012)';
150 27         35 my $prev_input;
151 27         77 while ($input) {
152 52 50 66     149 if (defined $prev_input and $input eq $prev_input) {
153 0         0 die "Infinite loop while parsing literal string";
154             }
155 52         94 $prev_input = $input;
156              
157             # Convert bachslash followed by up to three octal digits
158             # into that binary byte
159 52 100       556 if ($input =~ /^\\([0-7]{1,3})(.*)/os) {
    100          
    100          
    100          
    100          
160 13         44 $output .= chr(oct($1));
161 13         38 $input = $2;
162             }
163             # Convert backslash followed by an escaped character into that
164             # character
165             elsif ($input =~ /^\\([nrtbf\\\(\)])(.*)/osi) {
166 9         22 $output .= $trans{$1};
167 9         27 $input = $2;
168             }
169             # Ignore backslash followed by an end-of-line marker
170             elsif ($input =~ /^\\$cr(.*)/os) {
171 5         12 $input = $1;
172             }
173             # Convert an unescaped end-of-line marker to a line-feed
174             elsif ($input =~ /^\015\012?(.*)/os) {
175 2         4 $output .= "\012";
176 2         35 $input = $1;
177             }
178             # Check to see if there are any other special sequences
179             elsif ($input =~ /^(.*?)((?:\\(?:[nrtbf\\\(\)0-7]|$cr)|\015\012?).*)/os) {
180 10         27 $output .= $1;
181 10         28 $input = $2;
182             }
183             else {
184 13         32 $output .= $input;
185 13         38 $input = undef;
186             }
187             }
188             }
189              
190 30         112 return $output;
191             }
192              
193             =head2 val
194              
195             $s->val()
196              
197             =over
198              
199             Returns the value of this string (the string itself).
200              
201             =back
202              
203             =cut
204              
205             sub val {
206 2574     2574 1 9885 return $_[0]->{'val'};
207             }
208              
209             =head2 as_pdf
210              
211             $s->as_pdf()
212              
213             =over
214              
215             Returns the string formatted for output as PDF for PDF File object $pdf.
216              
217             =back
218              
219             =cut
220              
221             sub as_pdf {
222 198     198 1 548 my ($self) = @_;
223 198         552 my $str = $self->{'val'};
224              
225 198 100 66     1879 if ($self->{' ishex'}) { # imported as hex ?
    100 33        
226 1         5 $str = unpack('H*', $str);
227 1         16 return "<$str>";
228             } elsif ($self->{' isutf'} or
229             (utf8::is_utf8($str) and
230             $str =~ /[^[:ascii:]]/)) {
231 1         6 $str = join('', map { sprintf('%04X' , $_) } unpack('U*', $str) );
  3         15  
232 1         9 return "<FEFF$str>";
233             } else {
234 196 100       1052 if ($str =~ m/[^\n\r\t\b\f\040-\176\200-\377]/) {
235 3         17 $str =~ s/(.)/sprintf('%02X', ord($1))/sge;
  18         67  
236 3         26 return "<$str>";
237             } else {
238 193         635 $str =~ s/([\n\r\t\b\f\\()])/\\$out_trans{$1}/g;
239 193         1234 return "($str)";
240             }
241             }
242             }
243              
244             =head2 outobjdeep
245              
246             $s->outobjdeep($fh, $pdf)
247              
248             =over
249              
250             Outputs the string in PDF format, complete with necessary conversions.
251              
252             =back
253              
254             =cut
255              
256             sub outobjdeep {
257 12766     12766 1 22332 my ($self, $fh, $pdf) = @_;
258              
259 12766         28708 $fh->print($self->as_pdf($pdf));
260 12766         83986 return;
261             }
262              
263             1;