File Coverage

blib/lib/ICC/Profile/pseq.pm
Criterion Covered Total %
statement 12 129 9.3
branch 0 60 0.0
condition 0 3 0.0
subroutine 4 13 30.7
pod 1 6 16.6
total 17 211 8.0


line stmt bran cond sub pod time code
1             package ICC::Profile::pseq;
2              
3 2     2   97772 use strict;
  2         14  
  2         53  
4 2     2   9 use Carp;
  2         3  
  2         133  
5              
6             our $VERSION = 0.22;
7              
8             # revised 2018-08-07
9             #
10             # Copyright © 2004-2019 by William B. Birkett
11              
12             # add development directory
13 2     2   481 use lib 'lib';
  2         649  
  2         10  
14              
15             # inherit from Shared
16 2     2   641 use parent qw(ICC::Shared);
  2         290  
  2         9  
17              
18             # create new pseq tag object
19             # parameters: ([array_of_profile_objects])
20             # returns: (ref_to_object)
21             sub new {
22              
23             # get object class
24 0     0 0   my $class = shift();
25            
26             # create empty pseq object
27 0           my $self = [
28             {}, # object header
29             [], # array of profile description structures
30             ];
31              
32             # if parameter(s) supplied
33 0 0         if (@_) {
34            
35             # make new pseq tag
36 0           _newICCpseq($self, @_);
37            
38             }
39              
40             # bless object
41 0           bless($self, $class);
42            
43             # return object reference
44 0           return($self);
45              
46             }
47              
48             # create pseq tag object from ICC profile
49             # parameters: (ref_to_parent_object, file_handle, ref_to_tag_table_entry)
50             # returns: (ref_to_object)
51             sub new_fh {
52              
53             # get object class
54 0     0 0   my $class = shift();
55              
56             # create empty pseq object
57 0           my $self = [
58             {}, # object header
59             [] # array of profile description structures
60             ];
61              
62             # verify 3 parameters
63 0 0         (@_ == 3) or croak('wrong number of parameters');
64              
65             # read pseq data from profile
66 0           _readICCpseq($self, @_);
67              
68             # bless object
69 0           bless($self, $class);
70              
71             # return object reference
72 0           return($self);
73              
74             }
75              
76             # writes pseq tag object to ICC profile
77             # parameters: (ref_to_parent_object, file_handle, ref_to_tag_table_entry)
78             sub write_fh {
79              
80             # verify 4 parameters
81 0 0   0 0   (@_ == 4) or croak('wrong number of parameters');
82              
83             # write pseq data to profile
84 0           goto &_writeICCpseq;
85              
86             }
87              
88             # get tag size (for writing to profile)
89             # returns: (tag_size)
90             sub size {
91            
92             # get parameters
93 0     0 0   my ($self) = @_;
94            
95             # local variables
96 0           my ($size);
97            
98             # set base size
99 0           $size = 12;
100            
101             # for each profile description structure
102 0           for my $pds (@{$self->[1]}) {
  0            
103            
104             # add size of profile description structure
105 0           $size += 20 + $pds->[5]->size + $pds->[6]->size;
106            
107             # add padding if mluc tag (version 4)
108 0 0         $size += (-$pds->[5]->size % 4) if (UNIVERSAL::isa($pds->[5], 'ICC::Profile::mluc'));
109            
110             # add padding if mluc tag (version 4)
111 0 0         $size += (-$pds->[6]->size % 4) if (UNIVERSAL::isa($pds->[6], 'ICC::Profile::mluc'));
112            
113             }
114            
115             # return size
116 0           return($size);
117            
118             }
119              
120             # get pds (profile description structure) reference(s)
121             # parameters: (index)
122             # returns: (ref_to_pds)
123             # parameters: (list_of_indices)
124             # returns: (list_of_refs_to_pds)
125             sub pds {
126              
127             # get object reference
128 0     0 0   my $self = shift();
129            
130             # if parameters
131 0 0         if (@_) {
132            
133             # if list is wanted
134 0 0         if (wantarray) {
135            
136             # return list of pds references
137 0           return(map {$self->[1][$_]} @_);
  0            
138            
139             # single value wanted
140             } else {
141            
142             # return single pds reference
143 0           return($self->[1][$_[0]]);
144            
145             }
146            
147             }
148            
149             }
150              
151             # print object contents to string
152             # format is an array structure
153             # parameter: ([format])
154             # returns: (string)
155             sub sdump {
156              
157             # get parameters
158 0     0 1   my ($self, $p) = @_;
159              
160             # local variables
161 0           my ($s, $fmt);
162              
163             # resolve parameter to an array reference
164 0 0         $p = defined($p) ? ref($p) eq 'ARRAY' ? $p : [$p] : [];
    0          
165              
166             # get format string
167 0 0 0       $fmt = defined($p->[0]) && ! ref($p->[0]) ? $p->[0] : 'undef';
168              
169             # set string to object ID
170 0           $s = sprintf("'%s' object, (0x%x)\n", ref($self), $self);
171              
172             # return
173 0           return($s);
174              
175             }
176              
177             # make new pseq tag from array of profile objects
178             # parameters: (ref_to_object, array_of_profile_objects)
179             sub _newICCpseq {
180            
181             # get object reference
182 0     0     my $self = shift();
183            
184             # local variables
185 0           my ($vmaj, @pds);
186            
187             # verify array of profile objects
188 0 0         (! grep {ref() ne 'ICC::Profile'} @_) or croak('not a profile object');
  0            
189            
190             # for each profile
191 0           for my $profile (@_) {
192            
193             # get profile major version
194 0           $vmaj = substr($profile->profile_header->[2], 0, 2);
195            
196             # copy profile header info
197 0           @pds[0 .. 3] = @{$profile->profile_header}[15 .. 18];
  0            
198            
199             # if profile technology tag defined
200 0 0         if (defined($profile->tag('tech'))) {
201            
202             # copy technology signature
203 0           $pds[4] = $profile->tag('tech')->text;
204            
205             } else {
206            
207             # set to nulls
208 0           $pds[4] = "\x00" x 4;
209            
210             }
211            
212             # if profile device manufacturer tag defined
213 0 0         if (defined($profile->tag('dmnd'))) {
    0          
214            
215             # copy profile device manufacturer tag
216 0           $pds[5] = $profile->tag('dmnd');
217            
218             } elsif ($vmaj == 2) {
219            
220             # make empty 'desc' tag
221 0           $pds[5] = ICC::Profile::desc->new();
222            
223             } else {
224            
225             # make empty 'mluc' tag
226 0           $pds[5] = ICC::Profile::mluc->new();
227            
228             }
229            
230             # if profile device model tag defined
231 0 0         if (defined($profile->tag('dmdd'))) {
    0          
232            
233             # copy profile device manufacturer tag
234 0           $pds[6] = $profile->tag('dmdd');
235            
236             } elsif ($vmaj == 2) {
237            
238             # make empty 'desc' tag
239 0           $pds[6] = ICC::Profile::desc->new();
240            
241             } else {
242            
243             # make empty 'mluc' tag
244 0           $pds[6] = ICC::Profile::mluc->new();
245            
246             }
247            
248             # add structure to tag
249 0           push(@{$self->[1]}, [@pds]);
  0            
250            
251             }
252            
253             }
254              
255             # read pseq tag from ICC profile
256             # note: mluc tag sizes and padding are ambiguous, see "PSD_TechNote.pdf"
257             # parameters: (ref_to_object, ref_to_parent_object, file_handle, ref_to_tag_table_entry)
258             sub _readICCpseq {
259            
260             # get parameters
261 0     0     my ($self, $parent, $fh, $tag) = @_;
262            
263             # local variables
264 0           my ($buf, $cnt, $sig, $tab);
265 0           my ($mark, $eot, $eos);
266            
267             # save tag signature
268 0           $self->[0]{'signature'} = $tag->[0];
269            
270             # seek start of tag
271 0           seek($fh, $tag->[1], 0);
272            
273             # read count
274 0           read($fh, $buf, 12);
275            
276             # unpack count
277 0           $cnt = unpack('x8 N', $buf);
278            
279             # for each profile description structure
280 0           for my $i (0 .. $cnt - 1) {
281            
282             # if index > 0
283 0 0         if ($i > 0) {
284            
285             # set file position to end of pervious tag
286 0           seek($fh, $eot, 0);
287              
288             # read ahead 100 bytes
289 0           read($fh, $buf, 100);
290            
291             # match allowed tag type signatures
292 0 0         ($buf =~ m/(desc|mluc|\x3f\x00)/g) or croak('invalid profile description structure');
293            
294             # seek start of next profile description structure
295 0           seek($fh, $eot + pos($buf) - 20 - length($1), 0);
296            
297             }
298            
299             # read structure signatures and attributes
300 0           read($fh, $buf, 20);
301            
302             # unpack structure signatures and attributes
303 0           @{$self->[1][$i]}[0 .. 4] = unpack('a4 a4 N2 a4', $buf);
  0            
304            
305             # mark file position
306 0           $mark = tell($fh);
307            
308             # get tag type signature ('desc' or 'mluc')
309 0           read($fh, $sig, 4);
310            
311             # if 'desc' type
312 0 0         if ($sig eq 'desc') {
    0          
    0          
313            
314             # parse manufacturer description object
315 0           $self->[1][$i][5] = ICC::Profile::desc->new_fh($self, $fh, ['pseq', $mark, 0, 0]);
316            
317             # set end of tag
318 0           $eot = $mark + $self->[1][$i][5]->size;
319            
320             # if 'mluc' type
321             } elsif ($sig eq 'mluc') {
322            
323             # parse manufacturer description object
324 0           $self->[1][$i][5] = ICC::Profile::mluc->new_fh($self, $fh, ['pseq', $mark, 0, 0]);
325            
326             # set end of tag
327 0           $eot = $mark + 12;
328            
329             # if name record count > 0
330 0 0         if (@{$self->[1][$i][5][2]}) {
  0            
331            
332             # for each name record
333 0           for my $rec (@{$self->[1][$i][5][2]}) {
  0            
334            
335             # compute end of string (eos)
336 0           $eos = $mark + $rec->[2] + $rec->[3];
337            
338             # set eot to greater value
339 0 0         $eot = $eot > $eos ? $eot : $eos;
340            
341             }
342            
343             }
344            
345             # if Monaco non-standard notation
346             } elsif (substr($sig, 0, 2) eq "\x3f\x00") {
347            
348             # create an empty 'desc' tag object
349 0           $self->[1][$i][5] = ICC::Profile::desc->new();
350            
351             # set end of tag
352 0           $eot = $mark + 2;
353            
354             } else {
355            
356             # error
357 0           croak('invalid profile description structure');
358            
359             }
360            
361             # set file position to end of tag
362 0           seek($fh, $eot, 0);
363            
364             # read ahead 100 bytes
365 0           read($fh, $buf, 100);
366            
367             # match allowed tag type signatures
368 0 0         ($buf =~ m/(desc|mluc|\x3f\x00)/g) or croak('invalid profile description structure');
369            
370             # mark start of next tag
371 0           $mark = $eot + pos($buf) - length($1);
372            
373             # if 'desc' type
374 0 0         if ($1 eq 'desc') {
    0          
375            
376             # parse model description object
377 0           $self->[1][$i][6] = ICC::Profile::desc->new_fh($self, $fh, ['pseq', $mark, 0, 0]);
378            
379             # set end of tag
380 0           $eot = $mark + $self->[1][$i][5]->size;
381            
382             # if 'mluc' type
383             } elsif ($1 eq 'mluc') {
384            
385             # parse model description object
386 0           $self->[1][$i][6] = ICC::Profile::mluc->new_fh($self, $fh, ['pseq', $mark, 0, 0]);
387            
388             # set end of tag
389 0           $eot = $mark + 12;
390            
391             # if name record count > 0
392 0 0         if (@{$self->[1][$i][5][2]}) {
  0            
393            
394             # for each name record
395 0           for my $rec (@{$self->[1][$i][5][2]}) {
  0            
396            
397             # compute end of string (eos)
398 0           $eos = $mark + $rec->[2] + $rec->[3];
399            
400             # set eot to greater value
401 0 0         $eot = $eot > $eos ? $eot : $eos;
402            
403             }
404            
405             }
406            
407             # if Monaco non-standard notation
408             } else {
409            
410             # create an empty 'desc' tag object
411 0           $self->[1][$i][6] = ICC::Profile::desc->new();
412            
413             # set end of tag
414 0           $eot = $mark + 2;
415            
416             }
417            
418             }
419            
420             }
421              
422             # write pseq tag to ICC profile
423             # parameters: (ref_to_object, ref_to_parent_object, file_handle, ref_to_tag_table_entry)
424             sub _writeICCpseq {
425              
426             # get parameters
427 0     0     my ($self, $parent, $fh, $tag) = @_;
428              
429             # seek start of tag
430 0           seek($fh, $tag->[1], 0);
431              
432             # write tag type and pds count
433 0           print $fh pack('a4 x4 N', 'pseq', scalar(@{$self->[1]}));
  0            
434              
435             # for each profile description structure
436 0           for my $pds (@{$self->[1]}) {
  0            
437            
438             # write structure signatures and attributes
439 0           print $fh pack('a4 a4 N2 a4', @{$pds}[0 .. 4]);
  0            
440            
441             # write manufacturer description object
442 0           $pds->[5]->write_fh($parent, $fh, ['pseq', tell($fh), 0, 0]);
443            
444             # add padding if mluc tag (version 4)
445 0 0         seek($fh, (-tell($fh) % 4), 1) if (UNIVERSAL::isa($pds->[5], 'ICC::Profile::mluc'));
446            
447             # write model description object
448 0           $pds->[6]->write_fh($parent, $fh, ['pseq', tell($fh), 0, 0]);
449            
450             # add padding if mluc tag (version 4)
451 0 0         seek($fh, (-tell($fh) % 4), 1) if (UNIVERSAL::isa($pds->[6], 'ICC::Profile::mluc'));
452            
453             }
454            
455             }
456              
457             1;