| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# Copyright (c) 2025 Philipp Schafft <lion@cpan.org> |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# licensed under Artistic License 2.0 (see LICENSE file) |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# ABSTRACT: generic module for extracting information from filesystems |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package File::Information::Deep; |
|
9
|
|
|
|
|
|
|
|
|
10
|
3
|
|
|
3
|
|
63
|
use v5.20; |
|
|
3
|
|
|
|
|
16
|
|
|
11
|
3
|
|
|
3
|
|
22
|
use strict; |
|
|
3
|
|
|
|
|
7
|
|
|
|
3
|
|
|
|
|
102
|
|
|
12
|
3
|
|
|
3
|
|
16
|
use warnings; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
237
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
3
|
|
|
3
|
|
50
|
use parent 'File::Information::Base'; |
|
|
3
|
|
|
|
|
7
|
|
|
|
3
|
|
|
|
|
35
|
|
|
15
|
|
|
|
|
|
|
|
|
16
|
3
|
|
|
3
|
|
305
|
use Carp; |
|
|
3
|
|
|
|
|
7
|
|
|
|
3
|
|
|
|
|
340
|
|
|
17
|
3
|
|
|
3
|
|
40
|
use Scalar::Util qw(weaken); |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
197
|
|
|
18
|
3
|
|
|
3
|
|
21
|
use Fcntl qw(SEEK_SET); |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
34013
|
|
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our $VERSION = v0.16; |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
my %_PNG_colour_types = ( # namespace: 4c11d438-f6f3-417f-85e3-e56e46851dae |
|
23
|
|
|
|
|
|
|
0 => {ise => 'a3934b85-5bec-5cd7-a571-727e4cecfcb1', displayname => 'Greyscale'}, |
|
24
|
|
|
|
|
|
|
2 => {ise => '56262598-1d35-566d-b9a3-0e752d58b8ce', displayname => 'Truecolor'}, |
|
25
|
|
|
|
|
|
|
3 => {ise => '67f61b65-4978-510b-b318-247da7934837', displayname => 'Indexed-color'}, |
|
26
|
|
|
|
|
|
|
4 => {ise => 'cbdafa4e-1cb8-59a9-b6ec-b7a1bef3fcd4', displayname => 'Greyscale with alpha'}, |
|
27
|
|
|
|
|
|
|
6 => {ise => 'c6ef9ba0-3b7f-5248-a4f4-18e39c14d7b3', displayname => 'Truecolor with alpha'}, |
|
28
|
|
|
|
|
|
|
); |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
my %_PNG_filter_method = ( # namespace: 06f15860-8191-41f5-881c-a465be563089 |
|
31
|
|
|
|
|
|
|
0 => {ise => 'b7a197cb-2eee-517f-ae57-8e299d1a92e9', displayname => 'None'}, |
|
32
|
|
|
|
|
|
|
1 => {ise => 'c194fcce-c957-5436-861f-09af8526fed8', displayname => 'Sub'}, |
|
33
|
|
|
|
|
|
|
2 => {ise => 'fe14ef2d-4098-5a7e-81a0-88beae0e1e65', displayname => 'Up'}, |
|
34
|
|
|
|
|
|
|
3 => {ise => 'b0df25b4-b1fb-52cc-a6be-162440bd9628', displayname => 'Average'}, |
|
35
|
|
|
|
|
|
|
4 => {ise => '974cf00a-c2e2-5d08-b1da-08169e09b173', displayname => 'Paeth'}, |
|
36
|
|
|
|
|
|
|
); |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
my %_PNG_compression_method = ( # namespace: b2b8b4bf-3b0f-4037-9bbc-96e6b53ae73d |
|
39
|
|
|
|
|
|
|
0 => {ise => 'f47c8ff3-5218-555d-bf89-ba30706c29e1', displayname => 'deflate'}, |
|
40
|
|
|
|
|
|
|
); |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
my %_vmv0_section_types = ( |
|
43
|
|
|
|
|
|
|
1 => {ise => 'bc0dc85a-8c72-5ab6-a60b-377fdf76f678', displayname => 'init'}, |
|
44
|
|
|
|
|
|
|
2 => {ise => '18b7bfe0-5e3a-5fe4-ad69-a317e6b2445c', displayname => 'header'}, |
|
45
|
|
|
|
|
|
|
3 => {ise => '5460c878-23d6-56b9-8600-9375d76fefc5', displayname => 'rodata'}, |
|
46
|
|
|
|
|
|
|
4 => {ise => '0520d8d6-3a85-56d2-ae2b-77c517cff2ce', displayname => 'text'}, |
|
47
|
|
|
|
|
|
|
5 => {ise => '95f7f330-a72d-5e0b-ab0f-d46f37edbc9a', displayname => 'trailer'}, |
|
48
|
|
|
|
|
|
|
6 => {ise => '9bbc79eb-5a31-5797-8a05-56e58c530289', displayname => 'resources'}, |
|
49
|
|
|
|
|
|
|
); |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# Extra tags that do not belong into one of the other lists. |
|
52
|
|
|
|
|
|
|
my %_wk = ( |
|
53
|
|
|
|
|
|
|
'.section' => {ise => 'dad2de0d-9711-5b57-9a31-562122d756ba', displayname => '.section'}, |
|
54
|
|
|
|
|
|
|
'.chunk' => {ise => 'bff479fa-a818-58dc-b5df-539852fa8b80', displayname => '.chunk'}, |
|
55
|
|
|
|
|
|
|
); |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
my %_properties = ( |
|
58
|
|
|
|
|
|
|
pdf_version => {loader => \&_load_pdf}, |
|
59
|
|
|
|
|
|
|
pdf_pages => {loader => \&_load_pdf}, |
|
60
|
|
|
|
|
|
|
odf_keywords => {loader => \&_load_odf}, |
|
61
|
|
|
|
|
|
|
data_uriid_barcodes => {loader => \&_load_barcodes, rawtype => 'Data::URIID::Barcode'}, |
|
62
|
|
|
|
|
|
|
vmv0_filesize => {loader => \&_load_vmv0}, |
|
63
|
|
|
|
|
|
|
vmv0_section_pointer => {loader => \&_load_vmv0}, |
|
64
|
|
|
|
|
|
|
vmv0_section => {loader => \&_load_vmv0, rawtype => 'File::Information::Chunk'}, |
|
65
|
|
|
|
|
|
|
vmv0_minimum_handles => {loader => \&_load_vmv0}, |
|
66
|
|
|
|
|
|
|
vmv0_minimum_ram => {loader => \&_load_vmv0}, |
|
67
|
|
|
|
|
|
|
vmv0_boundary_text => {loader => \&_load_vmv0}, |
|
68
|
|
|
|
|
|
|
vmv0_boundary_load => {loader => \&_load_vmv0}, |
|
69
|
|
|
|
|
|
|
png_ihdr_width => {loader => \&_load_png}, |
|
70
|
|
|
|
|
|
|
png_ihdr_height => {loader => \&_load_png}, |
|
71
|
|
|
|
|
|
|
png_ihdr_bit_depth => {loader => \&_load_png}, |
|
72
|
|
|
|
|
|
|
png_ihdr_color_type => {loader => \&_load_png}, |
|
73
|
|
|
|
|
|
|
png_ihdr_compression_method => {loader => \&_load_png}, |
|
74
|
|
|
|
|
|
|
png_ihdr_filter_method => {loader => \&_load_png}, |
|
75
|
|
|
|
|
|
|
png_ihdr_interlace_method => {loader => \&_load_png}, |
|
76
|
|
|
|
|
|
|
gif_screen_width => {loader => \&_load_gif}, |
|
77
|
|
|
|
|
|
|
gif_screen_height => {loader => \&_load_gif}, |
|
78
|
|
|
|
|
|
|
gpl_palette_name => {loader => \&_load_gpl}, |
|
79
|
|
|
|
|
|
|
gpl_palette_columns => {loader => \&_load_gpl}, |
|
80
|
|
|
|
|
|
|
gpl_palette_colours => {loader => \&_load_gpl}, |
|
81
|
|
|
|
|
|
|
rgbtxt_palette_colours => {loader => \&_load_rgbtxt}, |
|
82
|
|
|
|
|
|
|
libpng_ihdr_width => {loader => \&_load_libpng}, |
|
83
|
|
|
|
|
|
|
libpng_ihdr_height => {loader => \&_load_libpng}, |
|
84
|
|
|
|
|
|
|
libpng_ihdr_color_type => {loader => \&_load_libpng}, |
|
85
|
|
|
|
|
|
|
libpng_plte_colours => {loader => \&_load_libpng}, |
|
86
|
|
|
|
|
|
|
); |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
my %_vmv0_code_P1_info = ( |
|
89
|
|
|
|
|
|
|
0 => 'vmv0_filesize', |
|
90
|
|
|
|
|
|
|
2 => 'vmv0_minimum_handles', |
|
91
|
|
|
|
|
|
|
3 => 'vmv0_minimum_ram', |
|
92
|
|
|
|
|
|
|
4 => 'vmv0_boundary_text', |
|
93
|
|
|
|
|
|
|
5 => 'vmv0_boundary_load', |
|
94
|
|
|
|
|
|
|
); |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
my @_odf_medadata_keys = qw(title description subject creator language initial_creator editing_cycles editing_duration generator creation_date date); |
|
97
|
|
|
|
|
|
|
my @_image_info_keys = qw(height width file_media_type file_ext color_type resolution SamplesPerPixel BitsPerSample Comment Interlace Compression Gamma LastModificationTime); |
|
98
|
|
|
|
|
|
|
my @_image_extra_keys = qw(Thumb::URI Thumb::Image::Width Thumb::Image::Height Thumb::MTime Software); |
|
99
|
|
|
|
|
|
|
my @_dynamic_loaders = (\&_load_odf, \&_load_audio_scan); |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
my %_audio_scan_tags = ( |
|
102
|
|
|
|
|
|
|
vorbiscomments => { |
|
103
|
|
|
|
|
|
|
title => 'title', |
|
104
|
|
|
|
|
|
|
}, |
|
105
|
|
|
|
|
|
|
riffwave => { |
|
106
|
|
|
|
|
|
|
title => 'inam', |
|
107
|
|
|
|
|
|
|
}, |
|
108
|
|
|
|
|
|
|
id3 => { |
|
109
|
|
|
|
|
|
|
title => 'tit2', |
|
110
|
|
|
|
|
|
|
}, |
|
111
|
|
|
|
|
|
|
); |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
foreach my $keyword (qw(Author CreationDate ModDate Creator Producer Title Subject Keywords)) { |
|
114
|
|
|
|
|
|
|
$_properties{'pdf_info_'.lc($keyword)} = {loader => \&_load_pdf}; |
|
115
|
|
|
|
|
|
|
} |
|
116
|
|
|
|
|
|
|
foreach my $keyword (qw(CreationDate ModDate)) { |
|
117
|
|
|
|
|
|
|
$_properties{'pdf_info_'.lc($keyword)}{parsing} = 'pdf_date'; |
|
118
|
|
|
|
|
|
|
} |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
foreach my $key (@_odf_medadata_keys) { |
|
121
|
|
|
|
|
|
|
$_properties{'odf_info_'.$key} = {loader => \&_load_odf}; |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
foreach my $key (qw(creation_date date)) { |
|
124
|
|
|
|
|
|
|
$_properties{'odf_info_'.$key}{parsing} = 'iso8601'; |
|
125
|
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
foreach my $key (@_image_info_keys) { |
|
128
|
|
|
|
|
|
|
$_properties{'image_info_'.lc($key)} = {loader => \&_load_image_info}; |
|
129
|
|
|
|
|
|
|
} |
|
130
|
|
|
|
|
|
|
foreach my $key (@_image_extra_keys) { |
|
131
|
|
|
|
|
|
|
$_properties{'image_info_extra_'.lc($key =~ s/::/_/r)} = {loader => \&_load_image_info}; |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
$_properties{image_info_extra_thumb_mtime}{rawtype} = 'unixts'; |
|
134
|
|
|
|
|
|
|
$_properties{image_info_extra_thumb_uri}{rawtype} = 'uri'; |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# Register well known: |
|
138
|
|
|
|
|
|
|
foreach my $value ( |
|
139
|
|
|
|
|
|
|
values(%_PNG_colour_types), |
|
140
|
|
|
|
|
|
|
values(%_PNG_filter_method), |
|
141
|
|
|
|
|
|
|
values(%_PNG_compression_method), |
|
142
|
|
|
|
|
|
|
values(%_vmv0_section_types), |
|
143
|
|
|
|
|
|
|
values(%_wk), |
|
144
|
|
|
|
|
|
|
) { |
|
145
|
|
|
|
|
|
|
Data::Identifier->new(ise => $value->{ise}, displayname => $value->{displayname})->register; |
|
146
|
|
|
|
|
|
|
} |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
#@returns File::Information::Base |
|
150
|
|
|
|
|
|
|
sub parent { |
|
151
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
|
152
|
0
|
|
|
|
|
|
return $self->{parent}; |
|
153
|
|
|
|
|
|
|
} |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# ---------------- |
|
156
|
|
|
|
|
|
|
sub property_info { |
|
157
|
0
|
|
|
0
|
1
|
|
my ($self, @args) = @_; |
|
158
|
|
|
|
|
|
|
|
|
159
|
0
|
0
|
|
|
|
|
unless (defined $self->{_dynamic}) { |
|
160
|
0
|
|
|
|
|
|
$self->{_dynamic} = 1; |
|
161
|
0
|
|
|
|
|
|
foreach my $cb (@_dynamic_loaders) { |
|
162
|
0
|
|
|
|
|
|
$self->$cb('__dummy__'); |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
|
return $self->SUPER::property_info(@args); |
|
167
|
|
|
|
|
|
|
} |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
sub _new { |
|
170
|
0
|
|
|
0
|
|
|
my ($pkg, %opts) = @_; |
|
171
|
0
|
|
|
|
|
|
my $self = $pkg->SUPER::_new(%opts, properties => \%_properties); |
|
172
|
0
|
|
0
|
|
|
|
my $pv = ($self->{properties_values} //= {})->{current} //= {}; |
|
|
|
|
0
|
|
|
|
|
|
173
|
0
|
|
|
|
|
|
my $parent = $self->{parent}; |
|
174
|
|
|
|
|
|
|
|
|
175
|
0
|
|
|
|
|
|
weaken($self->{parent}); |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# copy a few critical values: |
|
178
|
0
|
|
|
|
|
|
$pv->{contentise} = {raw => $parent->get('contentise', lifecycle => 'current', as => 'uuid')}; |
|
179
|
0
|
|
|
|
|
|
eval { $pv->{mediatype} = {raw => $parent->get('mediatype', lifecycle => 'current', as => 'mediatype')} }; |
|
|
0
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
|
|
181
|
0
|
|
|
|
|
|
return $self; |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub _dynamic_property { |
|
185
|
0
|
|
|
0
|
|
|
my ($self, $prefix, $property) = @_; |
|
186
|
0
|
|
|
|
|
|
my $key; |
|
187
|
|
|
|
|
|
|
|
|
188
|
0
|
|
|
|
|
|
$property = lc($property); |
|
189
|
0
|
|
|
|
|
|
$property =~ s/::/_/g; |
|
190
|
0
|
|
|
|
|
|
$property =~ s/[^a-z0-9]/_/g; |
|
191
|
0
|
|
0
|
|
|
|
$_properties{$key = $prefix.'_'.$property} //= {}; |
|
192
|
|
|
|
|
|
|
|
|
193
|
0
|
|
|
|
|
|
return $key; |
|
194
|
|
|
|
|
|
|
} |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub _check_mediatype { |
|
197
|
0
|
|
|
0
|
|
|
my ($self, @mediasubtypes) = @_; |
|
198
|
0
|
|
|
|
|
|
my $v; |
|
199
|
|
|
|
|
|
|
|
|
200
|
0
|
0
|
|
|
|
|
return undef unless defined $self->{properties_values}{current}{mediatype}{raw}; |
|
201
|
|
|
|
|
|
|
|
|
202
|
0
|
|
|
|
|
|
$v = $self->{properties_values}{current}{mediatype}{raw}; |
|
203
|
|
|
|
|
|
|
|
|
204
|
0
|
|
|
|
|
|
foreach my $mediasubtype (@mediasubtypes) { |
|
205
|
0
|
0
|
|
|
|
|
return 1 if $v eq $mediasubtype; |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
|
|
208
|
0
|
|
|
|
|
|
return undef; |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub _pdf_extract_date { |
|
212
|
0
|
|
|
0
|
|
|
my ($self, $value) = @_; |
|
213
|
0
|
|
|
|
|
|
require DateTime::Format::Strptime; |
|
214
|
|
|
|
|
|
|
|
|
215
|
0
|
|
|
|
|
|
state $pdf_date_core_pattern = '%Y%m%d%H%M%S'; |
|
216
|
0
|
|
|
|
|
|
state $pdf_date_format_0 = DateTime::Format::Strptime->new('pattern' => $pdf_date_core_pattern, 'time_zone' => 'UTC'); |
|
217
|
0
|
|
|
|
|
|
my $dt; |
|
218
|
|
|
|
|
|
|
my $core; |
|
219
|
0
|
|
|
|
|
|
my $parser; |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# General format: D:YYYYMMDDHHmmSSOHH'mm' |
|
222
|
|
|
|
|
|
|
|
|
223
|
0
|
0
|
|
|
|
|
if (($core) = $value =~ /^D:([0-9]{14})Z'{0,2}$/) { |
|
|
|
0
|
|
|
|
|
|
|
224
|
0
|
|
|
|
|
|
$parser = $pdf_date_format_0; |
|
225
|
|
|
|
|
|
|
} elsif (my ($mycore, $tz_dir, $tz_h, $tz_m) = $value =~ /^D:([0-9]{14})(\+|\-)([0-9]{2})'([0-9]{2})'$/) { |
|
226
|
0
|
|
|
|
|
|
my $tz = sprintf('%s%s%s', $tz_dir, $tz_h, $tz_m); |
|
227
|
0
|
|
|
|
|
|
$core = $mycore; |
|
228
|
0
|
|
|
|
|
|
$parser = DateTime::Format::Strptime->new('pattern' => $pdf_date_core_pattern, 'time_zone' => $tz); |
|
229
|
|
|
|
|
|
|
} |
|
230
|
|
|
|
|
|
|
|
|
231
|
0
|
0
|
0
|
|
|
|
return undef unless defined($core) && defined($parser); |
|
232
|
|
|
|
|
|
|
|
|
233
|
0
|
|
|
|
|
|
return $parser->parse_datetime($core); |
|
234
|
|
|
|
|
|
|
} |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub _load_pdf { |
|
237
|
0
|
|
|
0
|
|
|
my ($self, $key, %opts) = @_; |
|
238
|
0
|
|
0
|
|
|
|
my $pv = ($self->{properties_values} //= {})->{current} //= {}; |
|
|
|
|
0
|
|
|
|
|
|
239
|
|
|
|
|
|
|
|
|
240
|
0
|
0
|
|
|
|
|
return if defined $self->{_loaded_pdf}; |
|
241
|
0
|
|
|
|
|
|
$self->{_loaded_pdf} = 1; |
|
242
|
|
|
|
|
|
|
|
|
243
|
0
|
0
|
|
|
|
|
return unless defined $self->{path}; |
|
244
|
0
|
0
|
|
|
|
|
return unless $self->_check_mediatype('application/pdf'); |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# Check for module; |
|
247
|
0
|
0
|
|
|
|
|
if (eval { |
|
248
|
0
|
|
|
|
|
|
require PDF::API2; |
|
249
|
0
|
|
|
|
|
|
PDF::API2->VERSION(2.044); |
|
250
|
0
|
|
|
|
|
|
PDF::API2->import(); |
|
251
|
0
|
|
|
|
|
|
1; |
|
252
|
|
|
|
|
|
|
}) { |
|
253
|
0
|
|
|
|
|
|
my $pdf = PDF::API2->open($self->{path}); |
|
254
|
0
|
|
|
|
|
|
my %info = $pdf->info_metadata(); |
|
255
|
|
|
|
|
|
|
|
|
256
|
0
|
|
|
|
|
|
$pv->{pdf_version} = {raw => $pdf->version}; |
|
257
|
0
|
|
|
|
|
|
$pv->{pdf_pages} = {raw => $pdf->page_count}; |
|
258
|
|
|
|
|
|
|
|
|
259
|
0
|
|
|
|
|
|
foreach my $key (keys %info) { |
|
260
|
0
|
0
|
|
|
|
|
if (defined(my $value = $info{$key})) { |
|
261
|
0
|
|
|
|
|
|
my $pv_key = 'pdf_info_'.lc($key); |
|
262
|
|
|
|
|
|
|
|
|
263
|
0
|
0
|
0
|
|
|
|
$value = $self->_pdf_extract_date($value) if ($_properties{$pv_key}{parsing} // '') eq 'pdf_date'; |
|
264
|
0
|
|
|
|
|
|
$pv->{$pv_key} = {raw => $value}; |
|
265
|
|
|
|
|
|
|
} |
|
266
|
|
|
|
|
|
|
} |
|
267
|
|
|
|
|
|
|
|
|
268
|
0
|
|
|
|
|
|
$pdf->close; |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub _load_odf { |
|
273
|
0
|
|
|
0
|
|
|
my ($self, $key, %opts) = @_; |
|
274
|
0
|
|
0
|
|
|
|
my $pv = ($self->{properties_values} //= {})->{current} //= {}; |
|
|
|
|
0
|
|
|
|
|
|
275
|
|
|
|
|
|
|
|
|
276
|
0
|
0
|
|
|
|
|
return if defined $self->{_loaded_odf}; |
|
277
|
0
|
|
|
|
|
|
$self->{_loaded_odf} = 1; |
|
278
|
|
|
|
|
|
|
|
|
279
|
0
|
0
|
|
|
|
|
return unless defined $self->{path}; |
|
280
|
0
|
0
|
|
|
|
|
return unless $self->_check_mediatype(qw(application/vnd.oasis.opendocument.text)); |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
# Check for module; |
|
283
|
0
|
0
|
|
|
|
|
if (eval { |
|
284
|
0
|
|
|
|
|
|
require OpenOffice::OODoc; |
|
285
|
0
|
|
|
|
|
|
require DateTime::Format::ISO8601; |
|
286
|
0
|
|
|
|
|
|
OpenOffice::OODoc->import(); |
|
287
|
0
|
|
|
|
|
|
DateTime::Format::ISO8601->import(); |
|
288
|
0
|
|
|
|
|
|
1; |
|
289
|
|
|
|
|
|
|
}) { |
|
290
|
0
|
|
|
|
|
|
my $document = odfDocument(file => $self->{path}); |
|
291
|
0
|
|
|
|
|
|
my $meta = odfMeta(file => $document); |
|
292
|
|
|
|
|
|
|
|
|
293
|
0
|
|
|
|
|
|
foreach my $key (@_odf_medadata_keys) { |
|
294
|
0
|
|
|
|
|
|
my $func = $meta->can($key); |
|
295
|
0
|
|
|
|
|
|
my $value = $meta->$func(); |
|
296
|
0
|
|
|
|
|
|
my $pv_key = 'odf_info_'.$key; |
|
297
|
0
|
0
|
0
|
|
|
|
next unless defined($value) && length($value); |
|
298
|
|
|
|
|
|
|
|
|
299
|
0
|
0
|
0
|
|
|
|
$value = DateTime::Format::ISO8601->parse_datetime($value) if ($_properties{$pv_key}{parsing} // '') eq 'iso8601'; |
|
300
|
|
|
|
|
|
|
|
|
301
|
0
|
|
|
|
|
|
$pv->{$pv_key} = {raw => $value}; |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
|
|
304
|
0
|
|
|
|
|
|
$pv->{odf_keywords} = [map {{raw => $_}} $meta->keywords]; |
|
|
0
|
|
|
|
|
|
|
|
305
|
0
|
0
|
|
|
|
|
delete $pv->{odf_keywords} unless scalar @{$pv->{odf_keywords}}; |
|
|
0
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
{ |
|
308
|
0
|
|
|
|
|
|
my %stats = $meta->statistic; |
|
|
0
|
|
|
|
|
|
|
|
309
|
0
|
|
|
|
|
|
foreach my $key (keys %stats) { |
|
310
|
0
|
|
|
|
|
|
my $pv_key = $self->_dynamic_property(odf_stats => $key); |
|
311
|
0
|
|
|
|
|
|
my $value = $stats{$key}; |
|
312
|
0
|
0
|
0
|
|
|
|
next unless defined($value) && length($value); |
|
313
|
0
|
|
|
|
|
|
$pv->{$pv_key} = {raw => $value}; |
|
314
|
|
|
|
|
|
|
} |
|
315
|
|
|
|
|
|
|
} |
|
316
|
|
|
|
|
|
|
|
|
317
|
0
|
|
|
|
|
|
foreach my $el ($meta->getUserPropertyElements) { |
|
318
|
0
|
|
|
|
|
|
my $pv_key = $self->_dynamic_property(odf_user_properties => $el->att('meta:name')); |
|
319
|
0
|
|
|
|
|
|
my $value = $el->text; |
|
320
|
0
|
|
|
|
|
|
$pv->{$pv_key} = {raw => $value}; |
|
321
|
|
|
|
|
|
|
} |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
} |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
sub _load_vmv0_decode_opcode { |
|
326
|
0
|
|
|
0
|
|
|
my ($self, $in) = @_; |
|
327
|
0
|
|
|
|
|
|
my ($op0, $op1) = unpack('CC', $in); |
|
328
|
0
|
|
|
|
|
|
my $code = ($op0 & 0370) >> 3; |
|
329
|
0
|
|
|
|
|
|
my $P = ($op0 & 0007) >> 0; |
|
330
|
0
|
|
|
|
|
|
my $codeX = ($op1 & 0300) >> 6; |
|
331
|
0
|
|
|
|
|
|
my $S = ($op1 & 0070) >> 3; |
|
332
|
0
|
|
|
|
|
|
my $T = ($op1 & 0007) >> 0; |
|
333
|
|
|
|
|
|
|
|
|
334
|
0
|
|
|
|
|
|
return {code => $code, P => $P, codeX => $codeX, S => $S, T => $T, first => $op0, second => $op1}; |
|
335
|
|
|
|
|
|
|
} |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
sub _load_vmv0__load_chunks { |
|
338
|
0
|
|
|
0
|
|
|
my ($self, %opts) = @_; |
|
339
|
0
|
|
|
|
|
|
my $fh = delete $opts{fh}; |
|
340
|
0
|
|
|
|
|
|
my $start = delete $opts{start}; |
|
341
|
0
|
|
|
|
|
|
my @res; |
|
342
|
|
|
|
|
|
|
|
|
343
|
0
|
0
|
|
|
|
|
return undef unless $fh; |
|
344
|
|
|
|
|
|
|
|
|
345
|
0
|
|
|
|
|
|
while (1) { |
|
346
|
0
|
0
|
|
|
|
|
$fh->seek($start, SEEK_SET) or die $!; |
|
347
|
0
|
0
|
|
|
|
|
if (read($fh, my $in, 2) != 2) { |
|
348
|
0
|
|
|
|
|
|
last; |
|
349
|
|
|
|
|
|
|
} else { |
|
350
|
0
|
|
|
|
|
|
my $opcode = $self->_load_vmv0_decode_opcode($in); |
|
351
|
0
|
|
|
|
|
|
my $opcode_length = 2; |
|
352
|
0
|
|
|
|
|
|
my $outer_length; |
|
353
|
|
|
|
|
|
|
my $inner_offset; |
|
354
|
0
|
|
|
|
|
|
my $length; |
|
355
|
0
|
|
|
|
|
|
my %new_opts = %opts; |
|
356
|
0
|
|
|
|
|
|
my $flags; |
|
357
|
|
|
|
|
|
|
my $type; |
|
358
|
0
|
|
|
|
|
|
my $identifier; |
|
359
|
|
|
|
|
|
|
|
|
360
|
0
|
0
|
0
|
|
|
|
last unless $opcode->{first} == 6 && $opcode->{codeX} == 0 && $opcode->{S} && $opcode->{T} < 4; |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# Read length: |
|
363
|
0
|
0
|
|
|
|
|
if ($opcode->{T} == 1) { |
|
|
|
0
|
|
|
|
|
|
|
364
|
0
|
0
|
|
|
|
|
last unless read($fh, $in, 2) == 2; |
|
365
|
0
|
|
|
|
|
|
$length = unpack('n', $in) * 2; |
|
366
|
0
|
|
|
|
|
|
$opcode_length += 2; |
|
367
|
|
|
|
|
|
|
} elsif ($opcode->{T} == 2) { |
|
368
|
0
|
0
|
|
|
|
|
last unless read($fh, $in, 4) == 4; |
|
369
|
0
|
|
|
|
|
|
$length = unpack('N', $in) * 2; |
|
370
|
0
|
|
|
|
|
|
$opcode_length += 4; |
|
371
|
|
|
|
|
|
|
} |
|
372
|
|
|
|
|
|
|
|
|
373
|
0
|
0
|
|
|
|
|
next unless defined $length; |
|
374
|
0
|
|
|
|
|
|
$inner_offset = $opcode_length; |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# Read flags and type: |
|
377
|
0
|
0
|
|
|
|
|
last unless read($fh, $in, 4) == 4; |
|
378
|
0
|
|
|
|
|
|
($flags, $type) = unpack('nn', $in); |
|
379
|
0
|
|
|
|
|
|
$inner_offset += 4; |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# Read identifier (if any): |
|
382
|
0
|
0
|
|
|
|
|
if ($flags & 0x0002) { |
|
383
|
0
|
0
|
|
|
|
|
last unless read($fh, $in, 2) == 2; |
|
384
|
0
|
|
|
|
|
|
$identifier = unpack('n', $in); |
|
385
|
0
|
|
|
|
|
|
$inner_offset += 2; |
|
386
|
|
|
|
|
|
|
} |
|
387
|
|
|
|
|
|
|
|
|
388
|
0
|
0
|
|
|
|
|
if (($flags & 0xC000) == 0x0000) { # SNI |
|
|
|
0
|
|
|
|
|
|
|
389
|
0
|
|
|
|
|
|
$type = Data::Identifier->new('039e0bb7-5dd3-40ee-a98c-596ff6cce405' => $type); |
|
390
|
|
|
|
|
|
|
} elsif (($flags & 0xC000) == 0x4000) { # SID |
|
391
|
0
|
|
|
|
|
|
$type = Data::Identifier->new(sid => $type); |
|
392
|
|
|
|
|
|
|
} else { |
|
393
|
0
|
|
|
|
|
|
$type = undef; |
|
394
|
|
|
|
|
|
|
} |
|
395
|
|
|
|
|
|
|
|
|
396
|
0
|
|
|
|
|
|
$outer_length = $length + $opcode_length; |
|
397
|
|
|
|
|
|
|
push(@res, File::Information::Chunk->_new(%opts, |
|
398
|
|
|
|
|
|
|
start => $start, |
|
399
|
|
|
|
|
|
|
size => $outer_length, |
|
400
|
|
|
|
|
|
|
outer_type => {ise => $_wk{'.chunk'}->{ise}}, |
|
401
|
0
|
0
|
|
|
|
|
inner_type => {raw => $type, ise => $type->ise}, |
|
402
|
|
|
|
|
|
|
inner_start => $start + $inner_offset, |
|
403
|
|
|
|
|
|
|
inner_size => $outer_length - $inner_offset - ($flags & 0x1 ? 1 : 0), |
|
404
|
|
|
|
|
|
|
)); |
|
405
|
|
|
|
|
|
|
|
|
406
|
0
|
|
|
|
|
|
$start += $outer_length; |
|
407
|
|
|
|
|
|
|
} |
|
408
|
|
|
|
|
|
|
} |
|
409
|
|
|
|
|
|
|
|
|
410
|
0
|
0
|
|
|
|
|
return undef unless scalar @res; |
|
411
|
0
|
|
|
|
|
|
return \@res; |
|
412
|
|
|
|
|
|
|
} |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
sub _load_vmv0__chunk { |
|
415
|
0
|
|
|
0
|
|
|
my ($self, %opts) = @_; |
|
416
|
|
|
|
|
|
|
|
|
417
|
0
|
0
|
|
|
|
|
if (defined(my $fh = delete $opts{fh})) { |
|
418
|
0
|
0
|
|
|
|
|
$fh->seek($opts{start}, SEEK_SET) or die $!; |
|
419
|
0
|
0
|
|
|
|
|
if (read($fh, my $in, 2) == 2) { |
|
420
|
0
|
|
|
|
|
|
my $opcode = $self->_load_vmv0_decode_opcode($in); |
|
421
|
0
|
0
|
0
|
|
|
|
if ($opcode->{first} == 0 && $opcode->{codeX} == 0 && ($opcode->{T} & 0x4)) { |
|
|
|
|
0
|
|
|
|
|
|
422
|
0
|
|
|
|
|
|
my $n = $opcode->{T} - 4; |
|
423
|
|
|
|
|
|
|
|
|
424
|
0
|
0
|
|
|
|
|
if ($n > 0) { |
|
425
|
0
|
|
|
|
|
|
$n *= 2; |
|
426
|
0
|
0
|
|
|
|
|
(read($fh, my $magic, $n) == $n) or croak 'IO error: Cannot read '.$n.' bytes'; |
|
427
|
0
|
0
|
|
|
|
|
if (length($magic) == $n) { |
|
428
|
0
|
|
|
|
|
|
$opts{outer_magic} = {raw => $magic}; |
|
429
|
|
|
|
|
|
|
} |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
|
|
432
|
0
|
|
|
|
|
|
my $section_type = $opcode->{S}; |
|
433
|
0
|
0
|
0
|
|
|
|
if ($section_type == 5 || $section_type == 6) { |
|
434
|
0
|
|
|
|
|
|
$opts{subchunks} = $self->_load_vmv0__load_chunks(%opts, start => $fh->tell, fh => $fh); |
|
435
|
|
|
|
|
|
|
} |
|
436
|
0
|
|
|
|
|
|
$opts{outer_type} = {ise => $_wk{'.section'}->{ise}}; |
|
437
|
0
|
|
|
|
|
|
$opts{inner_type} = {ise => $_vmv0_section_types{$section_type}->{ise}}; |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
} |
|
440
|
|
|
|
|
|
|
} |
|
441
|
|
|
|
|
|
|
|
|
442
|
0
|
|
|
|
|
|
return File::Information::Chunk->_new(%opts); |
|
443
|
|
|
|
|
|
|
} |
|
444
|
|
|
|
|
|
|
sub _load_vmv0 { |
|
445
|
0
|
|
|
0
|
|
|
my ($self, $key, %opts) = @_; |
|
446
|
0
|
|
0
|
|
|
|
my $pv = ($self->{properties_values} //= {})->{current} //= {}; |
|
|
|
|
0
|
|
|
|
|
|
447
|
|
|
|
|
|
|
|
|
448
|
0
|
0
|
|
|
|
|
return if defined $self->{_loaded_vmv0}; |
|
449
|
0
|
|
|
|
|
|
$self->{_loaded_vmv0} = 1; |
|
450
|
|
|
|
|
|
|
|
|
451
|
0
|
0
|
|
|
|
|
return unless $self->_check_mediatype(qw(application/vnd.sirtx.vmv0)); |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
{ |
|
454
|
0
|
|
|
|
|
|
my $inode = $self->parent->inode; |
|
|
0
|
|
|
|
|
|
|
|
455
|
0
|
|
|
|
|
|
my $data = $inode->peek(wanted => 1024); |
|
456
|
0
|
|
|
|
|
|
my %section_pointer; |
|
457
|
|
|
|
|
|
|
|
|
458
|
0
|
|
|
|
|
|
while (length($data)) { |
|
459
|
0
|
|
|
|
|
|
my $opcode = $self->_load_vmv0_decode_opcode(substr($data, 0, 2, '')); |
|
460
|
0
|
|
|
|
|
|
my ($op0, $op1, $code, $P, $codeX, $S, $T) = $opcode->@{qw(first second code P codeX S T)}; |
|
461
|
0
|
|
|
|
|
|
my $extra_as_num; |
|
462
|
0
|
|
|
|
|
|
my $extra_len = 0; |
|
463
|
0
|
|
|
|
|
|
my $extra; |
|
464
|
|
|
|
|
|
|
|
|
465
|
0
|
0
|
|
|
|
|
last if $code != 0; |
|
466
|
0
|
0
|
|
|
|
|
last if $P > 2; |
|
467
|
0
|
0
|
|
|
|
|
last if $codeX != 0; |
|
468
|
|
|
|
|
|
|
|
|
469
|
0
|
0
|
0
|
|
|
|
last if $op0 == 0 && $codeX == 0 && $S > 2; # last on non-opcode sections |
|
|
|
|
0
|
|
|
|
|
|
470
|
|
|
|
|
|
|
|
|
471
|
0
|
|
|
|
|
|
$extra_len = ($T & 0x3) * 2; |
|
472
|
|
|
|
|
|
|
|
|
473
|
0
|
|
|
|
|
|
$extra = substr($data, 0, $extra_len, ''); |
|
474
|
|
|
|
|
|
|
|
|
475
|
0
|
0
|
|
|
|
|
if ($extra_len == 0) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
476
|
0
|
|
|
|
|
|
$extra_as_num = 0; |
|
477
|
|
|
|
|
|
|
} elsif ($extra_len == 2) { |
|
478
|
0
|
|
|
|
|
|
$extra_as_num = unpack('n', $extra); |
|
479
|
|
|
|
|
|
|
} elsif ($extra_len == 4) { |
|
480
|
0
|
|
|
|
|
|
$extra_as_num = unpack('N', $extra); |
|
481
|
|
|
|
|
|
|
} |
|
482
|
|
|
|
|
|
|
|
|
483
|
0
|
0
|
|
|
|
|
if ($code == 0) { |
|
484
|
0
|
0
|
|
|
|
|
if ($P == 1) { |
|
485
|
0
|
0
|
|
|
|
|
if ($codeX == 0) { |
|
486
|
0
|
0
|
|
|
|
|
if (defined $extra_as_num) { |
|
487
|
0
|
0
|
|
|
|
|
if (defined(my $f = $_vmv0_code_P1_info{$S})) { |
|
|
|
0
|
|
|
|
|
|
|
488
|
0
|
|
|
|
|
|
$pv->{$f} = {raw => $extra_as_num*2}; |
|
489
|
|
|
|
|
|
|
} elsif ($S == 1) { |
|
490
|
0
|
|
0
|
|
|
|
$section_pointer{$extra_as_num*2} //= undef; |
|
491
|
|
|
|
|
|
|
} |
|
492
|
|
|
|
|
|
|
} |
|
493
|
|
|
|
|
|
|
} |
|
494
|
|
|
|
|
|
|
} |
|
495
|
|
|
|
|
|
|
} |
|
496
|
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
#warn sprintf('[code=%u, P=%u; codeX=%u, S=%u, T=%u; extra_len=%u, extra_as_num=%s]', $code, $P, $codeX, $S, $T, $extra_len, $extra_as_num // '<undef>'); |
|
498
|
|
|
|
|
|
|
} |
|
499
|
|
|
|
|
|
|
|
|
500
|
0
|
0
|
|
|
|
|
if (scalar keys %section_pointer) { |
|
501
|
0
|
|
|
|
|
|
my @pointers = sort {$a <=> $b} keys %section_pointer; |
|
|
0
|
|
|
|
|
|
|
|
502
|
0
|
|
|
|
|
|
my $fh = $inode->_get_fh; |
|
503
|
0
|
|
|
|
|
|
my @sections; |
|
504
|
|
|
|
|
|
|
my $last; |
|
505
|
|
|
|
|
|
|
|
|
506
|
0
|
|
|
|
|
|
$pv->{vmv0_section_pointer} = [map {{raw => $_}} @pointers]; |
|
|
0
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
|
508
|
0
|
|
|
|
|
|
require File::Information::Chunk; |
|
509
|
|
|
|
|
|
|
|
|
510
|
0
|
|
|
|
|
|
foreach my $c (@pointers) { |
|
511
|
0
|
0
|
|
|
|
|
if (defined $last) { |
|
512
|
0
|
|
|
|
|
|
push(@sections, $self->_load_vmv0__chunk(instance => $self->instance, path => $self->{path}, parent => $self, inode => $inode, start => $last, end => $c, fh => $fh)); |
|
513
|
|
|
|
|
|
|
} |
|
514
|
0
|
|
|
|
|
|
$last = $c; |
|
515
|
|
|
|
|
|
|
} |
|
516
|
0
|
0
|
|
|
|
|
if (defined $last) { |
|
517
|
0
|
|
|
|
|
|
push(@sections, $self->_load_vmv0__chunk(instance => $self->instance, path => $self->{path}, parent => $self, inode => $inode, start => $last, end => scalar($inode->get('size')), fh => $fh)); |
|
518
|
|
|
|
|
|
|
} |
|
519
|
0
|
|
|
|
|
|
$pv->{vmv0_section} = [map {{raw => $_}} @sections]; |
|
|
0
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
} |
|
521
|
|
|
|
|
|
|
} |
|
522
|
|
|
|
|
|
|
} |
|
523
|
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
sub _load_png { |
|
525
|
0
|
|
|
0
|
|
|
my ($self, $key, %opts) = @_; |
|
526
|
0
|
|
0
|
|
|
|
my $pv = ($self->{properties_values} //= {})->{current} //= {}; |
|
|
|
|
0
|
|
|
|
|
|
527
|
|
|
|
|
|
|
|
|
528
|
0
|
0
|
|
|
|
|
return if defined $self->{_loaded_png}; |
|
529
|
0
|
|
|
|
|
|
$self->{_loaded_png} = 1; |
|
530
|
|
|
|
|
|
|
|
|
531
|
0
|
0
|
|
|
|
|
return unless $self->_check_mediatype(qw(image/png)); |
|
532
|
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
{ |
|
534
|
0
|
|
|
|
|
|
my $inode = $self->parent->inode; |
|
|
0
|
|
|
|
|
|
|
|
535
|
0
|
|
|
|
|
|
my $data = $inode->peek(wanted => 1024); |
|
536
|
|
|
|
|
|
|
|
|
537
|
0
|
0
|
|
|
|
|
if (substr($data, 0, 8) eq "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a") { |
|
538
|
0
|
0
|
|
|
|
|
if (substr($data, 8, 8) eq "\0\0\0\x0dIHDR") { |
|
539
|
0
|
|
0
|
|
|
|
my $crc = eval {require Digest::CRC; Digest::CRC->new(type => 'crc32');} // eval { require Digest; Digest->new('CRC-32'); } // croak 'No CRC-32 support'; |
|
|
0
|
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
540
|
0
|
|
|
|
|
|
$crc->add(substr($data, 8 + 4, 4 + 13)); |
|
541
|
0
|
0
|
|
|
|
|
if (substr($data, 16 + 13, 4) eq pack('H8', $crc->hexdigest)) { |
|
542
|
0
|
|
|
|
|
|
my ($width, $height, $bit_depth, $color_type, $compression_method, $filter_method, $interlace_method) = unpack('NNCCCCC', substr($data, 16, 13)); |
|
543
|
0
|
|
|
|
|
|
$pv->{png_ihdr_width} = {raw => $width}; |
|
544
|
0
|
|
|
|
|
|
$pv->{png_ihdr_height} = {raw => $height}; |
|
545
|
0
|
|
|
|
|
|
$pv->{png_ihdr_bit_depth} = {raw => $bit_depth}; |
|
546
|
0
|
|
|
|
|
|
$pv->{png_ihdr_interlace_method} = {raw => $interlace_method}; |
|
547
|
0
|
0
|
|
|
|
|
if (defined(my $ct = $_PNG_colour_types{$color_type})) { |
|
548
|
0
|
|
|
|
|
|
$pv->{png_ihdr_color_type} = {raw => $color_type, ise => $ct->{ise}}; |
|
549
|
|
|
|
|
|
|
} |
|
550
|
0
|
0
|
|
|
|
|
if (defined(my $fm = $_PNG_filter_method{$filter_method})) { |
|
551
|
0
|
|
|
|
|
|
$pv->{png_ihdr_filter_method} = {raw => $filter_method, ise => $fm->{ise}}; |
|
552
|
|
|
|
|
|
|
} |
|
553
|
0
|
0
|
|
|
|
|
if (defined(my $cm = $_PNG_compression_method{$compression_method})) { |
|
554
|
0
|
|
|
|
|
|
$pv->{png_ihdr_compression_method} = {raw => $compression_method, ise => $cm->{ise}}; |
|
555
|
|
|
|
|
|
|
} |
|
556
|
|
|
|
|
|
|
} |
|
557
|
|
|
|
|
|
|
} |
|
558
|
|
|
|
|
|
|
} |
|
559
|
|
|
|
|
|
|
} |
|
560
|
|
|
|
|
|
|
} |
|
561
|
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
sub _load_gif { |
|
563
|
0
|
|
|
0
|
|
|
my ($self, $key, %opts) = @_; |
|
564
|
0
|
|
0
|
|
|
|
my $pv = ($self->{properties_values} //= {})->{current} //= {}; |
|
|
|
|
0
|
|
|
|
|
|
565
|
|
|
|
|
|
|
|
|
566
|
0
|
0
|
|
|
|
|
return if defined $self->{_loaded_gif}; |
|
567
|
0
|
|
|
|
|
|
$self->{_loaded_gif} = 1; |
|
568
|
|
|
|
|
|
|
|
|
569
|
0
|
0
|
|
|
|
|
return unless $self->_check_mediatype(qw(image/gif)); |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
{ |
|
572
|
0
|
|
|
|
|
|
my $inode = $self->parent->inode; |
|
|
0
|
|
|
|
|
|
|
|
573
|
0
|
|
|
|
|
|
my $data = $inode->peek(wanted => 1024); |
|
574
|
|
|
|
|
|
|
|
|
575
|
0
|
0
|
|
|
|
|
if (substr($data, 0, 6) eq 'GIF89a') { # TODO: check if the following code also holds true for GIF87a |
|
576
|
0
|
|
|
|
|
|
my ($width, $height) = unpack('vv', substr($data, 6, 4)); |
|
577
|
0
|
0
|
|
|
|
|
$pv->{gif_screen_width} = {raw => $width} if $width > 0; |
|
578
|
0
|
0
|
|
|
|
|
$pv->{gif_screen_height} = {raw => $height} if $height > 0; |
|
579
|
|
|
|
|
|
|
} |
|
580
|
|
|
|
|
|
|
} |
|
581
|
|
|
|
|
|
|
} |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
sub _load_gpl { |
|
584
|
0
|
|
|
0
|
|
|
my ($self, $key, %opts) = @_; |
|
585
|
0
|
|
0
|
|
|
|
my $pv = ($self->{properties_values} //= {})->{current} //= {}; |
|
|
|
|
0
|
|
|
|
|
|
586
|
|
|
|
|
|
|
|
|
587
|
0
|
0
|
|
|
|
|
return if defined $self->{_loaded_gpl}; |
|
588
|
0
|
|
|
|
|
|
$self->{_loaded_gpl} = 1; |
|
589
|
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
{ |
|
591
|
0
|
|
|
|
|
|
my $inode = $self->parent->inode; |
|
|
0
|
|
|
|
|
|
|
|
592
|
0
|
|
|
|
|
|
my $data = $inode->peek(wanted => 64); |
|
593
|
|
|
|
|
|
|
|
|
594
|
0
|
0
|
|
|
|
|
if ($data =~ /^GIMP Palette\r?\n/) { |
|
595
|
0
|
|
|
|
|
|
my $fh = $inode->_get_fh; |
|
596
|
0
|
|
|
|
|
|
my @colours; |
|
597
|
|
|
|
|
|
|
|
|
598
|
0
|
0
|
|
|
|
|
return unless eval { require Data::URIID::Colour; 1; }; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
|
|
600
|
0
|
|
|
|
|
|
while (defined(my $line = <$fh>)) { |
|
601
|
0
|
|
|
|
|
|
$line =~ s/\r?\n$//; |
|
602
|
0
|
|
|
|
|
|
$line =~ s/^\s*#.*$//; |
|
603
|
0
|
0
|
|
|
|
|
next unless length($line); |
|
604
|
0
|
0
|
|
|
|
|
if ($line eq 'GIMP Palette') { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
# magic, good, no-op |
|
606
|
|
|
|
|
|
|
} elsif ($line =~ /^Name:\s+(\S.+)$/) { |
|
607
|
0
|
|
|
|
|
|
$pv->{gpl_palette_name} = {raw => $1}; |
|
608
|
|
|
|
|
|
|
} elsif ($line =~ /^Columns:\s+([1-9][0-9]*)$/) { |
|
609
|
0
|
|
|
|
|
|
$pv->{gpl_palette_columns} = {raw => int($1)}; |
|
610
|
|
|
|
|
|
|
} elsif ($line =~ /^(0|[1-9][0-9]*)\s+(0|[1-9][0-9]*)\s+(0|[1-9][0-9]*)\s+(\S(?:.*\S)?)$/) { |
|
611
|
0
|
|
|
|
|
|
push(@colours, {raw => Data::URIID::Colour->new( |
|
612
|
|
|
|
|
|
|
rgb => sprintf('#%02x%02x%02x', $1, $2, $3), |
|
613
|
|
|
|
|
|
|
displayname => $4, |
|
614
|
|
|
|
|
|
|
)}); |
|
615
|
|
|
|
|
|
|
} elsif ($line =~ /^(0|[1-9][0-9]*)\s+(0|[1-9][0-9]*)\s+(0|[1-9][0-9]*)$/) { |
|
616
|
0
|
|
|
|
|
|
push(@colours, {raw => Data::URIID::Colour->new( |
|
617
|
|
|
|
|
|
|
rgb => sprintf('#%02x%02x%02x', $1, $2, $3), |
|
618
|
|
|
|
|
|
|
)}); |
|
619
|
|
|
|
|
|
|
} else { |
|
620
|
|
|
|
|
|
|
# BAD line!? |
|
621
|
|
|
|
|
|
|
} |
|
622
|
|
|
|
|
|
|
} |
|
623
|
|
|
|
|
|
|
|
|
624
|
0
|
0
|
|
|
|
|
$pv->{gpl_palette_colours} = \@colours if scalar @colours; |
|
625
|
|
|
|
|
|
|
} |
|
626
|
|
|
|
|
|
|
} |
|
627
|
|
|
|
|
|
|
} |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
sub _load_rgbtxt { |
|
630
|
0
|
|
|
0
|
|
|
my ($self, $key, %opts) = @_; |
|
631
|
0
|
|
0
|
|
|
|
my $pv = ($self->{properties_values} //= {})->{current} //= {}; |
|
|
|
|
0
|
|
|
|
|
|
632
|
|
|
|
|
|
|
|
|
633
|
0
|
0
|
|
|
|
|
return if defined $self->{_loaded_rgbtxt}; |
|
634
|
0
|
|
|
|
|
|
$self->{_loaded_rgbtxt} = 1; |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
{ |
|
637
|
0
|
|
|
|
|
|
my $inode = $self->parent->inode; |
|
|
0
|
|
|
|
|
|
|
|
638
|
0
|
|
|
|
|
|
my $data = $inode->peek(wanted => 64); |
|
639
|
|
|
|
|
|
|
|
|
640
|
0
|
0
|
|
|
|
|
if ($data =~ /^\! \$Xorg: rgb\.txt,v .+ Exp \$\r?\n/) { |
|
641
|
0
|
|
|
|
|
|
my $fh = $inode->_get_fh; |
|
642
|
0
|
|
|
|
|
|
my @colours; |
|
643
|
|
|
|
|
|
|
|
|
644
|
0
|
0
|
|
|
|
|
return unless eval { require Data::URIID::Colour; 1; }; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
|
|
646
|
0
|
|
|
|
|
|
while (defined(my $line = <$fh>)) { |
|
647
|
0
|
|
|
|
|
|
$line =~ s/\r?\n$//; |
|
648
|
0
|
0
|
|
|
|
|
next unless length($line); |
|
649
|
0
|
0
|
|
|
|
|
if ($line =~ /^(0|[1-9][0-9]*)\s+(0|[1-9][0-9]*)\s+(0|[1-9][0-9]*)\s+(\S(?:.*\S)?)$/) { |
|
650
|
0
|
|
|
|
|
|
push(@colours, {raw => Data::URIID::Colour->new( |
|
651
|
|
|
|
|
|
|
rgb => sprintf('#%02x%02x%02x', $1, $2, $3), |
|
652
|
|
|
|
|
|
|
displayname => $4, |
|
653
|
|
|
|
|
|
|
)}); |
|
654
|
|
|
|
|
|
|
} else { |
|
655
|
|
|
|
|
|
|
# BAD line!? |
|
656
|
|
|
|
|
|
|
} |
|
657
|
|
|
|
|
|
|
} |
|
658
|
|
|
|
|
|
|
|
|
659
|
0
|
0
|
|
|
|
|
$pv->{rgbtxt_palette_colours} = \@colours if scalar @colours; |
|
660
|
|
|
|
|
|
|
} |
|
661
|
|
|
|
|
|
|
} |
|
662
|
|
|
|
|
|
|
} |
|
663
|
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
sub _load_image_info { |
|
665
|
0
|
|
|
0
|
|
|
my ($self, $key, %opts) = @_; |
|
666
|
0
|
|
0
|
|
|
|
my $pv = ($self->{properties_values} //= {})->{current} //= {}; |
|
|
|
|
0
|
|
|
|
|
|
667
|
|
|
|
|
|
|
|
|
668
|
0
|
0
|
|
|
|
|
return if defined $self->{_loaded_image_info}; |
|
669
|
0
|
|
|
|
|
|
$self->{_loaded_image_info} = 1; |
|
670
|
|
|
|
|
|
|
|
|
671
|
0
|
0
|
|
|
|
|
return unless defined $self->{path}; |
|
672
|
|
|
|
|
|
|
|
|
673
|
0
|
|
|
|
|
|
foreach my $data (eval { |
|
674
|
0
|
|
|
|
|
|
require Image::Info; |
|
675
|
0
|
|
|
|
|
|
Image::Info->import(); |
|
676
|
0
|
|
|
|
|
|
Image::Info::image_info($self->{path}); |
|
677
|
|
|
|
|
|
|
}) { |
|
678
|
0
|
0
|
0
|
|
|
|
next if defined($data->{error}) && length($data->{error}); |
|
679
|
|
|
|
|
|
|
|
|
680
|
0
|
|
|
|
|
|
foreach my $key (@_image_info_keys) { |
|
681
|
0
|
|
|
|
|
|
my $pv_key = 'image_info_'.lc($key); |
|
682
|
0
|
|
|
|
|
|
my $value = delete $data->{$key}; |
|
683
|
|
|
|
|
|
|
|
|
684
|
0
|
0
|
0
|
|
|
|
next unless defined($value) && length($value); |
|
685
|
|
|
|
|
|
|
|
|
686
|
0
|
|
|
|
|
|
$pv->{$pv_key} = {raw => $value}; |
|
687
|
|
|
|
|
|
|
} |
|
688
|
0
|
|
|
|
|
|
foreach my $key (@_image_extra_keys) { |
|
689
|
0
|
|
|
|
|
|
my $pv_key = 'image_info_extra_'.lc($key =~ s/::/_/r); |
|
690
|
0
|
|
|
|
|
|
my $value = delete $data->{$key}; |
|
691
|
|
|
|
|
|
|
|
|
692
|
0
|
0
|
0
|
|
|
|
next unless defined($value) && length($value); |
|
693
|
|
|
|
|
|
|
|
|
694
|
0
|
|
|
|
|
|
$pv->{$pv_key} = {raw => $value}; |
|
695
|
|
|
|
|
|
|
} |
|
696
|
|
|
|
|
|
|
} |
|
697
|
|
|
|
|
|
|
} |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
sub _load_audio_scan { |
|
700
|
0
|
|
|
0
|
|
|
my ($self, $key, %opts) = @_; |
|
701
|
0
|
|
0
|
|
|
|
my $pv = ($self->{properties_values} //= {})->{current} //= {}; |
|
|
|
|
0
|
|
|
|
|
|
702
|
|
|
|
|
|
|
|
|
703
|
0
|
0
|
|
|
|
|
return if defined $self->{_loaded_audio_scan}; |
|
704
|
0
|
|
|
|
|
|
$self->{_loaded_audio_scan} = 1; |
|
705
|
|
|
|
|
|
|
|
|
706
|
0
|
0
|
|
|
|
|
return unless defined $self->{path}; |
|
707
|
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
# Check for module; |
|
709
|
0
|
0
|
|
|
|
|
if (defined(my $data = eval { |
|
710
|
0
|
|
|
|
|
|
local $ENV{AUDIO_SCAN_NO_ARTWORK} = 1; |
|
711
|
0
|
|
|
|
|
|
require Audio::Scan; |
|
712
|
0
|
|
|
|
|
|
Audio::Scan->import(); |
|
713
|
0
|
|
|
|
|
|
Audio::Scan->scan($self->{path}); |
|
714
|
|
|
|
|
|
|
})) { |
|
715
|
0
|
|
|
|
|
|
my $info = $data->{info}; |
|
716
|
0
|
|
|
|
|
|
my $tags = $data->{tags}; |
|
717
|
|
|
|
|
|
|
|
|
718
|
0
|
|
|
|
|
|
foreach my $key (keys %{$info}) { |
|
|
0
|
|
|
|
|
|
|
|
719
|
0
|
|
|
|
|
|
my $value = $info->{$key}; |
|
720
|
0
|
|
|
|
|
|
my $pv_key; |
|
721
|
|
|
|
|
|
|
|
|
722
|
0
|
0
|
0
|
|
|
|
next unless defined($value) && length($value); |
|
723
|
0
|
0
|
|
|
|
|
next if ref $value; |
|
724
|
|
|
|
|
|
|
|
|
725
|
0
|
|
|
|
|
|
$pv_key = $self->_dynamic_property(audio_scan_info => $key); |
|
726
|
0
|
|
|
|
|
|
$pv->{$pv_key} = {raw => $value}; |
|
727
|
|
|
|
|
|
|
} |
|
728
|
|
|
|
|
|
|
|
|
729
|
0
|
|
|
|
|
|
foreach my $key (keys %{$tags}) { |
|
|
0
|
|
|
|
|
|
|
|
730
|
0
|
|
|
|
|
|
my $value = $tags->{$key}; |
|
731
|
0
|
|
|
|
|
|
my $pv_key; |
|
732
|
|
|
|
|
|
|
|
|
733
|
0
|
0
|
0
|
|
|
|
next unless defined($value) && length($value); |
|
734
|
0
|
0
|
|
|
|
|
next if ref $value; |
|
735
|
|
|
|
|
|
|
|
|
736
|
0
|
|
|
|
|
|
$pv_key = $self->_dynamic_property(audio_scan_tags => $key); |
|
737
|
0
|
|
|
|
|
|
$pv->{$pv_key} = {raw => $value}; |
|
738
|
|
|
|
|
|
|
} |
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
{ |
|
741
|
0
|
|
|
|
|
|
my $style; |
|
|
0
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
|
|
743
|
0
|
0
|
|
|
|
|
if ($self->_check_mediatype(qw(application/ogg audio/ogg video/ogg audio/flac))) { |
|
|
|
0
|
|
|
|
|
|
|
744
|
0
|
|
|
|
|
|
$style = 'vorbiscomments'; |
|
745
|
|
|
|
|
|
|
} elsif ($self->_check_mediatype(qw(audio/x-wav))) { |
|
746
|
0
|
|
|
|
|
|
$style = 'riffwave'; |
|
747
|
|
|
|
|
|
|
} else { |
|
748
|
0
|
|
|
|
|
|
$style = 'id3'; # bad guess |
|
749
|
|
|
|
|
|
|
} |
|
750
|
|
|
|
|
|
|
|
|
751
|
0
|
0
|
0
|
|
|
|
if (defined($style) && defined(my $map = $_audio_scan_tags{$style})) { |
|
752
|
0
|
|
|
|
|
|
foreach my $key (keys %{$map}) { |
|
|
0
|
|
|
|
|
|
|
|
753
|
0
|
|
|
|
|
|
my $src_pv_key = $self->_dynamic_property(audio_scan_tags => $map->{$key}); |
|
754
|
0
|
|
|
|
|
|
my $pv_key = $self->_dynamic_property(audio_scan => $key); |
|
755
|
0
|
|
|
|
|
|
my $value = $pv->{$src_pv_key}; |
|
756
|
|
|
|
|
|
|
|
|
757
|
0
|
0
|
0
|
|
|
|
if (defined($value) && ref($value) eq 'HASH' && defined($value->{raw})) { |
|
|
|
|
0
|
|
|
|
|
|
758
|
0
|
|
|
|
|
|
$pv->{$pv_key} = {raw => $value->{raw}}; |
|
759
|
|
|
|
|
|
|
} |
|
760
|
|
|
|
|
|
|
} |
|
761
|
|
|
|
|
|
|
} |
|
762
|
|
|
|
|
|
|
} |
|
763
|
|
|
|
|
|
|
} |
|
764
|
|
|
|
|
|
|
} |
|
765
|
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
sub _load_barcodes { |
|
767
|
0
|
|
|
0
|
|
|
my ($self, $key, %opts) = @_; |
|
768
|
0
|
|
0
|
|
|
|
my $pv = ($self->{properties_values} //= {})->{current} //= {}; |
|
|
|
|
0
|
|
|
|
|
|
769
|
0
|
|
|
|
|
|
my @barcodes; |
|
770
|
|
|
|
|
|
|
|
|
771
|
0
|
0
|
|
|
|
|
return if defined $self->{_loaded_barcodes}; |
|
772
|
0
|
|
|
|
|
|
$self->{_loaded_barcodes} = 1; |
|
773
|
|
|
|
|
|
|
|
|
774
|
0
|
0
|
|
|
|
|
return unless defined $self->{path}; |
|
775
|
0
|
0
|
|
|
|
|
return unless eval { require Data::URIID::Barcode; 1; }; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
|
|
777
|
0
|
|
|
|
|
|
@barcodes = eval { Data::URIID::Barcode->sheet(filename => $self->{path}) }; |
|
|
0
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
|
|
779
|
0
|
0
|
|
|
|
|
if (scalar @barcodes) { |
|
780
|
0
|
|
|
|
|
|
$pv->{data_uriid_barcodes} = [map {{raw => $_}} @barcodes]; |
|
|
0
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
} |
|
782
|
|
|
|
|
|
|
} |
|
783
|
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
sub _load_libpng { |
|
785
|
0
|
|
|
0
|
|
|
my ($self, $key, %opts) = @_; |
|
786
|
0
|
|
0
|
|
|
|
my $pv = ($self->{properties_values} //= {})->{current} //= {}; |
|
|
|
|
0
|
|
|
|
|
|
787
|
|
|
|
|
|
|
|
|
788
|
0
|
0
|
|
|
|
|
return if defined $self->{_loaded_libpng}; |
|
789
|
0
|
|
|
|
|
|
$self->{_loaded_libpng} = 1; |
|
790
|
|
|
|
|
|
|
|
|
791
|
0
|
0
|
|
|
|
|
return unless $self->_check_mediatype(qw(image/png)); |
|
792
|
0
|
0
|
|
|
|
|
return unless defined $self->{path}; |
|
793
|
0
|
0
|
|
|
|
|
return unless eval { require Image::PNG::Libpng; 1; }; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
|
|
795
|
0
|
0
|
|
|
|
|
if (defined(my $png = eval {Image::PNG::Libpng::read_png_file($self->{path})})) { |
|
|
0
|
|
|
|
|
|
|
|
796
|
0
|
|
|
|
|
|
my $IHDR = $png->get_IHDR; |
|
797
|
|
|
|
|
|
|
|
|
798
|
0
|
|
|
|
|
|
$pv->{libpng_ihdr_width} = {raw => $IHDR->{width}}; |
|
799
|
0
|
|
|
|
|
|
$pv->{libpng_ihdr_height} = {raw => $IHDR->{height}}; |
|
800
|
|
|
|
|
|
|
|
|
801
|
0
|
0
|
|
|
|
|
if (defined(my $ct = $_PNG_colour_types{$IHDR->{color_type}})) { |
|
802
|
0
|
|
|
|
|
|
$pv->{libpng_ihdr_color_type} = {raw => $IHDR->{color_type}, ise => $ct->{ise}}; |
|
803
|
|
|
|
|
|
|
} |
|
804
|
|
|
|
|
|
|
|
|
805
|
0
|
0
|
|
|
|
|
if (defined(my $PLTE = eval {$png->get_PLTE})) { |
|
|
0
|
|
|
|
|
|
|
|
806
|
0
|
0
|
|
|
|
|
if (eval { require Data::URIID::Colour; 1; }) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
my @colours = map {{ |
|
808
|
0
|
|
|
|
|
|
raw => Data::URIID::Colour->new(rgb => sprintf('#%02x%02x%02x', $_->{red}, $_->{green}, $_->{blue})), |
|
809
|
0
|
|
|
|
|
|
}} @{$PLTE}; |
|
|
0
|
|
|
|
|
|
|
|
810
|
0
|
0
|
|
|
|
|
$pv->{libpng_plte_colours} = \@colours if scalar @colours; |
|
811
|
|
|
|
|
|
|
} |
|
812
|
|
|
|
|
|
|
} |
|
813
|
|
|
|
|
|
|
} |
|
814
|
|
|
|
|
|
|
} |
|
815
|
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
1; |
|
817
|
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
__END__ |
|
819
|
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
=pod |
|
821
|
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
=encoding UTF-8 |
|
823
|
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
=head1 NAME |
|
825
|
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
File::Information::Deep - generic module for extracting information from filesystems |
|
827
|
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
=head1 VERSION |
|
829
|
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
version v0.16 |
|
831
|
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
833
|
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
use File::Information; |
|
835
|
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
my File::Information::Deep $deep = $obj->deep; |
|
837
|
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
# ... |
|
839
|
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
B<Note:> This package inherits from L<File::Information::Base>. |
|
841
|
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
This package allows for deep inspection of files. |
|
843
|
|
|
|
|
|
|
This permits to read data directly from files, not just external metadata |
|
844
|
|
|
|
|
|
|
(such as filesystem attributes). |
|
845
|
|
|
|
|
|
|
This however comes at the price of performance. |
|
846
|
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
B<Note:> |
|
848
|
|
|
|
|
|
|
If you want to use data from deep inspection, you need to load this object (by calling C<$obj-E<gt>deep>) |
|
849
|
|
|
|
|
|
|
before calling any L<File::Information::Base/get> or similar methods. |
|
850
|
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
=head1 METHODS |
|
852
|
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
=head2 parent |
|
854
|
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
my File::Information::Base $parent = $deep->parent; |
|
856
|
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
Returns the parent that was used to create this object. |
|
858
|
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
=head1 AUTHOR |
|
860
|
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
Philipp Schafft <lion@cpan.org> |
|
862
|
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
864
|
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
This software is Copyright (c) 2024-2025 by Philipp Schafft <lion@cpan.org>. |
|
866
|
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
This is free software, licensed under: |
|
868
|
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
The Artistic License 2.0 (GPL Compatible) |
|
870
|
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
=cut |