source: main/trunk/greenstone2/perllib/cpan/Image/ExifTool/FLIF.pm@ 34921

Last change on this file since 34921 was 34921, checked in by anupama, 3 years ago

Committing the improvements to EmbeddedMetaPlugin's processing of Keywords vs other metadata fields. Keywords were literally stored as arrays of words rather than phrases in PDFs (at least in Diego's sample PDF), whereas other meta fields like Subjects and Creators stored them as arrays of phrases. To get both to work, Kathy updated EXIF to a newer version, to retrieve the actual EXIF values stored in the PDF. And Kathy and Dr Bainbridge came up with a new option that I added called apply_join_before_split_to_metafields that's a regex which can list the metadata fields to apply the join_before_split to and whcih previously always got applied to all metadata fields. Now it's applied to any *Keywords metafields by default, as that's the metafield we have experience of that behaves differently to the others, as it stores by word instead of phrases. Tested on Diego's sample PDF. Diego has double-checked it to works on his sample PDF too, setting the split char to ; and turning on the join_before_split and leaving apply_join_before_split_to_metafields at its default of .*Keywords. File changes are strings.properties for the tooltip, the plugin introducing the option and working with it and Kathy's EXIF updates affecting cpan/File and cpan/Image.

File size: 11.6 KB
Line 
1#------------------------------------------------------------------------------
2# File: FLIF.pm
3#
4# Description: Read/write FLIF meta information
5#
6# Revisions: 2016/10/11 - P. Harvey Created
7# 2016/10/14 - PH Added write support
8#
9# References: 1) http://flif.info/
10# 2) https://github.com/FLIF-hub/FLIF/blob/master/doc/metadata
11#------------------------------------------------------------------------------
12
13package Image::ExifTool::FLIF;
14
15use strict;
16use vars qw($VERSION);
17use Image::ExifTool qw(:DataAccess :Utils);
18
19$VERSION = '1.02';
20
21my %flifMap = (
22 EXIF => 'FLIF',
23 XMP => 'FLIF',
24 ICC_Profile => 'FLIF',
25 IFD0 => 'EXIF',
26 IFD1 => 'IFD0',
27 ExifIFD => 'IFD0',
28 GPS => 'IFD0',
29 SubIFD => 'IFD0',
30 GlobParamIFD => 'IFD0',
31 PrintIM => 'IFD0',
32 InteropIFD => 'ExifIFD',
33 MakerNotes => 'ExifIFD',
34);
35
36# FLIF tags
37%Image::ExifTool::FLIF::Main = (
38 GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' },
39 VARS => { HEX_ID => 0 },
40 NOTES => q{
41 Information extracted from Free Lossless Image Format files. See
42 L<http://flif.info/> for more information.
43 },
44#
45# header information
46#
47 0 => {
48 Name => 'ImageType',
49 PrintConv => {
50 '1' => 'Grayscale (non-interlaced)',
51 '3' => 'RGB (non-interlaced)',
52 '4' => 'RGBA (non-interlaced)',
53 'A' => 'Grayscale (interlaced)',
54 'C' => 'RGB (interlaced)',
55 'D' => 'RGBA (interlaced)',
56 'Q' => 'Grayscale Animation (non-interlaced)',
57 'S' => 'RGB Animation (non-interlaced)',
58 'T' => 'RGBA Animation (non-interlaced)',
59 'a' => 'Grayscale Animation (interlaced)',
60 'c' => 'RGB Animation (interlaced)',
61 'd' => 'RGBA Animation (interlaced)',
62 },
63 },
64 1 => {
65 Name => 'BitDepth',
66 PrintConv => {
67 '0' => 'Custom',
68 '1' => 8,
69 '2' => 16,
70 },
71 },
72 2 => 'ImageWidth',
73 3 => 'ImageHeight',
74 4 => 'AnimationFrames',
75 5 => {
76 Name => 'Encoding',
77 PrintConv => {
78 0 => 'FLIF16',
79 },
80 },
81#
82# metadata chunks
83#
84 iCCP => {
85 Name => 'ICC_Profile',
86 SubDirectory => {
87 TagTable => 'Image::ExifTool::ICC_Profile::Main',
88 },
89 },
90 eXif => {
91 Name => 'EXIF',
92 SubDirectory => {
93 TagTable => 'Image::ExifTool::Exif::Main',
94 ProcessProc => \&Image::ExifTool::ProcessTIFF,
95 WriteProc => \&Image::ExifTool::WriteTIFF,
96 Start => 6, # (skip "Exif\0\0" header)
97 Header => "Exif\0\0",
98 },
99 },
100 eXmp => {
101 Name => 'XMP',
102 SubDirectory => {
103 TagTable => 'Image::ExifTool::XMP::Main',
104 },
105 },
106 # tRko - list of truncation offsets
107 # \0 - FLIF16-format image data
108);
109
110#------------------------------------------------------------------------------
111# Read variable-length FLIF integer
112# Inputs: 0) raf reference, 1) number to add to returned value
113# Returns: integer, or undef on EOF
114sub GetVarInt($;$)
115{
116 my ($raf, $add) = @_;
117 my ($val, $buff);
118 for ($val=0; ; $val<<=7) {
119 $raf->Read($buff, 1) or return undef;
120 my $byte = ord($buff);
121 $val |= ($byte & 0x7f);
122 last unless $byte & 0x80;
123 }
124 return $val + ($add || 0);
125}
126
127#------------------------------------------------------------------------------
128# Construct variable-length FLIF integer
129# Inputs: 0) integer
130# Returns: FLIF variable-length integer byte stream
131sub SetVarInt($)
132{
133 my $val = shift;
134 my $buff = '';
135 my $high = 0;
136 for (;;) {
137 $buff = chr(($val & 0x7f) | $high) . $buff;
138 last unless $val >>= 7;
139 $high = 0x80;
140 }
141 return $buff;
142}
143
144#------------------------------------------------------------------------------
145# Read FLIF header
146# Inputs: 0) RAF ref
147# Returns: Scalar context: binary header block
148# List context: header values (4 or 5 elements: type,depth,width,height[,frames])
149# or undef if this isn't a valid FLIF file header
150sub ReadFLIFHeader($)
151{
152 my $raf = shift;
153 my ($buff, @vals);
154
155 # verify this is a valid FLIF file
156 return () unless $raf->Read($buff, 6) == 6 and $buff =~ /^FLIF([0-\x6f])([0-2])/;
157
158 # decode header information ("FLIF" chunk)
159 push @vals, $1, $2; # type, depth
160 push @vals, GetVarInt($raf,+1), GetVarInt($raf,+1); # width, height (+1 each)
161 push @vals, GetVarInt($raf,+2) if $vals[0] gt 'H'; # frames (+2)
162
163 return () unless defined $vals[-1];
164 return @vals if wantarray; # return the decoded header values
165
166 # return the binary header block
167 my $hdrLen = $raf->Tell();
168 return () unless $raf->Seek(0,0) and $raf->Read($buff, $hdrLen) == $hdrLen;
169 return $buff;
170}
171
172#------------------------------------------------------------------------------
173# WriteFLIF : Write FLIF image
174# Inputs: 0) ExifTool object reference, 1) dirInfo reference
175# Returns: 1 on success, 0 if this wasn't a valid FLIF file, or -1 if
176# an output file was specified and a write error occurred
177sub WriteFLIF($$)
178{
179 my ($et, $dirInfo) = @_;
180 my $raf = $$dirInfo{RAF};
181 my ($buff, $soi, @addTags, %doneTag);
182
183 # verify FLIF header and copy it to the output file
184 $buff = ReadFLIFHeader($raf) or return 0;
185 my $outfile = $$dirInfo{OutFile};
186 Write($outfile, $buff) or return -1;
187
188 $et->InitWriteDirs(\%flifMap);
189 my $tagTablePtr = GetTagTable('Image::ExifTool::FLIF::Main');
190
191 # loop through the FLIF chunks
192 for (;;) {
193 my ($tag, $size, $inflated);
194 # read new tag (or soi) unless we already hit the soi (start of image)
195 if (not defined $soi) {
196 $raf->Read($buff, 4) == 4 or $et->Error('Unexpected EOF'), last;
197 if ($buff lt ' ') {
198 $soi = $buff; # we have hit the start of image (no more metadata)
199 # make list of new tags to add
200 foreach $tag ('eXif', 'eXmp', 'iCCP') {
201 push @addTags, $tag if $$et{ADD_DIRS}{$$tagTablePtr{$tag}{Name}} and not $doneTag{$tag};
202 }
203 }
204 }
205 if (not defined $soi) {
206 $tag = $buff;
207 $size = GetVarInt($raf); # read the data size
208 } elsif (@addTags) {
209 $tag = shift @addTags;
210 ($buff, $size) = ('', 0); # create metadata from scratch
211 } else {
212 # finish copying file (no more metadata to add)
213 Write($outfile, $soi) or return -1;
214 Write($outfile, $buff) or return -1 while $raf->Read($buff, 65536);
215 last; # all done!
216 }
217 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
218 if ($tagInfo and $$tagInfo{SubDirectory} and $$et{EDIT_DIRS}{$$tagInfo{Name}}) {
219 $doneTag{$tag} = 1; # prevent adding this back again later
220 unless (defined $soi) {
221 $raf->Read($buff, $size) == $size or $et->Error("Truncated FLIF $tag chunk"), last;
222 }
223 # rewrite the compressed data
224 if (eval { require IO::Uncompress::RawInflate } and eval { require IO::Compress::RawDeflate } ) {
225 if (length $buff == 0) {
226 $inflated = $buff; # (creating from scratch, so no need to inflate)
227 } elsif (not IO::Uncompress::RawInflate::rawinflate(\$buff => \$inflated)) {
228 $et->Error("Error inflating FLIF $tag chunk"), last;
229 }
230 my $subdir = $$tagInfo{SubDirectory};
231 my %subdirInfo = (
232 DirName => $$tagInfo{Name},
233 DataPt => \$inflated,
234 DirStart => length($inflated) ? $$subdir{Start} : undef,
235 ReadOnly => 1, # (used only by WriteXMP)
236 );
237 my $subTable = GetTagTable($$subdir{TagTable});
238 $inflated = $et->WriteDirectory(\%subdirInfo, $subTable, $$subdir{WriteProc});
239 if (defined $inflated) {
240 next unless length $inflated; # (delete directory if length is zero)
241 $inflated = $$subdir{Header} . $inflated if $$subdir{Header}; # (add back header if necessary)
242 unless (IO::Compress::RawDeflate::rawdeflate(\$inflated => \$buff)) {
243 $et->Error("Error deflating FLIF $tag chunk"), last;
244 }
245 }
246 } else {
247 $et->WarnOnce('Install IO::Compress::RawDeflate to write FLIF metadata');
248 }
249 Write($outfile, $tag, SetVarInt(length $buff), $buff) or return -1;
250 } elsif (not defined $soi) {
251 Write($outfile, $tag, SetVarInt($size)) or return -1;
252 Image::ExifTool::CopyBlock($raf, $outfile, $size) or return -1;
253 }
254 }
255 return 1;
256}
257
258#------------------------------------------------------------------------------
259# Extract information from an FLIF file
260# Inputs: 0) ExifTool object reference, 1) DirInfo reference
261# Returns: 1 on success, 0 if this wasn't a valid FLIF file
262sub ProcessFLIF($$)
263{
264 my ($et, $dirInfo) = @_;
265 my $raf = $$dirInfo{RAF};
266 my ($buff, $tag, $inflated);
267
268 # verify this is a valid FLIF file and read the header
269 my @vals = ReadFLIFHeader($raf) or return 0;
270
271 $et->SetFileType();
272 my $tagTablePtr = GetTagTable('Image::ExifTool::FLIF::Main');
273 my $verbose = $et->Options('Verbose');
274
275 # save the header information
276 $et->VPrint(0, "FLIF header:\n") if $verbose;
277 for ($tag=0; defined $vals[$tag]; ++$tag) {
278 $et->HandleTag($tagTablePtr, $tag, $vals[$tag]);
279 }
280
281 # loop through the FLIF chunks
282 for (;;) {
283 $raf->Read($tag, 4) == 4 or $et->Warn('Unexpected EOF'), last;
284 my $byte = ord substr($tag, 0, 1);
285 # all done if we arrived at the image chunk
286 $byte < 32 and $et->HandleTag($tagTablePtr, 5, $byte), last;
287 my $size = GetVarInt($raf);
288 $et->VPrint(0, "FLIF $tag ($size bytes):\n") if $verbose;
289 if ($$tagTablePtr{$tag}) {
290 $raf->Read($buff, $size) == $size or $et->Warn("Truncated FLIF $tag chunk"), last;
291 $et->VerboseDump(\$buff, Addr => $raf->Tell() - $size) if $verbose > 2;
292 # inflate the compressed data
293 if (eval { require IO::Uncompress::RawInflate }) {
294 if (IO::Uncompress::RawInflate::rawinflate(\$buff => \$inflated)) {
295 $et->HandleTag($tagTablePtr, $tag, $inflated,
296 DataPt => \$inflated,
297 Size => length $inflated,
298 Extra => ' inflated',
299 );
300 } else {
301 $et->Warn("Error inflating FLIF $tag chunk");
302 }
303 } else {
304 $et->WarnOnce('Install IO::Uncompress::RawInflate to decode FLIF metadata');
305 }
306 } else {
307 $raf->Seek($size, 1) or $et->Warn('Seek error'), last;
308 }
309 }
310 return 1;
311}
312
3131; # end
314
315__END__
316
317=head1 NAME
318
319Image::ExifTool::FLIF - Read/write FLIF meta information
320
321=head1 SYNOPSIS
322
323This module is used by Image::ExifTool
324
325=head1 DESCRIPTION
326
327This module contains routines required by Image::ExifTool to read and write
328meta information in FLIF (Free Lossless Image Format) images.
329
330=head1 AUTHOR
331
332Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
333
334This library is free software; you can redistribute it and/or modify it
335under the same terms as Perl itself.
336
337=head1 REFERENCES
338
339=over 4
340
341=item L<http://flif.info/>
342
343=item L<https://github.com/FLIF-hub/FLIF/blob/master/doc/metadata>
344
345=back
346
347=head1 SEE ALSO
348
349L<Image::ExifTool::TagNames/FLIF Tags>,
350L<Image::ExifTool(3pm)|Image::ExifTool>
351
352=cut
353
Note: See TracBrowser for help on using the repository browser.