DjVu.pm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. #------------------------------------------------------------------------------
  2. # File: DjVu.pm
  3. #
  4. # Description: Read DjVu archive meta information
  5. #
  6. # Revisions: 09/25/2008 - P. Harvey Created
  7. #
  8. # References: 1) http://djvu.sourceforge.net/ (DjVu v3 specification, Nov 2005)
  9. # 2) http://www.djvu.org/
  10. #
  11. # Notes: DjVu files are recognized and the IFF structure is processed
  12. # by Image::ExifTool::AIFF
  13. #------------------------------------------------------------------------------
  14. package Image::ExifTool::DjVu;
  15. use strict;
  16. use vars qw($VERSION);
  17. use Image::ExifTool qw(:DataAccess :Utils);
  18. $VERSION = '1.05';
  19. sub ParseAnt($);
  20. sub ProcessAnt($$$);
  21. sub ProcessMeta($$$);
  22. sub ProcessBZZ($$$);
  23. # DjVu chunks that we parse (ref 4)
  24. %Image::ExifTool::DjVu::Main = (
  25. GROUPS => { 2 => 'Image' },
  26. NOTES => q{
  27. Information is extracted from the following chunks in DjVu images. See
  28. L<http://www.djvu.org/> for the DjVu specification.
  29. },
  30. INFO => {
  31. SubDirectory => { TagTable => 'Image::ExifTool::DjVu::Info' },
  32. },
  33. FORM => {
  34. TypeOnly => 1, # extract chunk type only, then descend into chunk
  35. SubDirectory => { TagTable => 'Image::ExifTool::DjVu::Form' },
  36. },
  37. ANTa => {
  38. SubDirectory => { TagTable => 'Image::ExifTool::DjVu::Ant' },
  39. },
  40. ANTz => {
  41. Name => 'CompressedAnnotation',
  42. SubDirectory => {
  43. TagTable => 'Image::ExifTool::DjVu::Ant',
  44. ProcessProc => \&ProcessBZZ,
  45. }
  46. },
  47. INCL => 'IncludedFileID',
  48. );
  49. # information in the DjVu INFO chunk
  50. %Image::ExifTool::DjVu::Info = (
  51. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  52. GROUPS => { 2 => 'Image' },
  53. FORMAT => 'int8u',
  54. PRIORITY => 0, # first INFO block takes priority
  55. 0 => {
  56. Name => 'ImageWidth',
  57. Format => 'int16u',
  58. },
  59. 2 => {
  60. Name => 'ImageHeight',
  61. Format => 'int16u',
  62. },
  63. 4 => {
  64. Name => 'DjVuVersion',
  65. Description => 'DjVu Version',
  66. Format => 'int8u[2]',
  67. # (this may be just one byte as with version 0.16)
  68. ValueConv => '$val=~/(\d+) (\d+)/ ? "$2.$1" : "0.$val"',
  69. },
  70. 6 => {
  71. Name => 'SpatialResolution',
  72. Format => 'int16u',
  73. ValueConv => '(($val & 0xff)<<8) + ($val>>8)', # (little-endian!)
  74. },
  75. 8 => {
  76. Name => 'Gamma',
  77. ValueConv => '$val / 10',
  78. },
  79. 9 => {
  80. Name => 'Orientation',
  81. Mask => 0x07, # (upper 5 bits reserved)
  82. PrintConv => {
  83. 1 => 'Horizontal (normal)',
  84. 2 => 'Rotate 180',
  85. 5 => 'Rotate 90 CW',
  86. 6 => 'Rotate 270 CW',
  87. },
  88. },
  89. );
  90. # information in the DjVu FORM chunk
  91. %Image::ExifTool::DjVu::Form = (
  92. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  93. GROUPS => { 2 => 'Image' },
  94. 0 => {
  95. Name => 'SubfileType',
  96. Format => 'undef[4]',
  97. Priority => 0,
  98. PrintConv => {
  99. DJVU => 'Single-page image',
  100. DJVM => 'Multi-page document',
  101. PM44 => 'Color IW44',
  102. BM44 => 'Grayscale IW44',
  103. DJVI => 'Shared component',
  104. THUM => 'Thumbnail image',
  105. },
  106. },
  107. );
  108. # tags found in the DjVu annotation chunk (ANTz or ANTa)
  109. %Image::ExifTool::DjVu::Ant = (
  110. PROCESS_PROC => \&Image::ExifTool::DjVu::ProcessAnt,
  111. GROUPS => { 2 => 'Image' },
  112. NOTES => 'Information extracted from annotation chunks.',
  113. # Note: For speed, ProcessAnt() pre-scans for known tag ID's, so if any
  114. # new tags are added here they must also be added to the pre-scan check
  115. metadata => {
  116. SubDirectory => { TagTable => 'Image::ExifTool::DjVu::Meta' }
  117. },
  118. xmp => {
  119. Name => 'XMP',
  120. SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' }
  121. },
  122. );
  123. # tags found in the DjVu annotation metadata
  124. %Image::ExifTool::DjVu::Meta = (
  125. PROCESS_PROC => \&Image::ExifTool::DjVu::ProcessMeta,
  126. GROUPS => { 1 => 'DjVu-Meta', 2 => 'Image' },
  127. NOTES => q{
  128. This table lists the standard DjVu metadata tags, but ExifTool will extract
  129. any tags that exist even if they don't appear here. The DjVu v3
  130. documentation endorses tags borrowed from two standards: 1) BibTeX
  131. bibliography system tags (all lowercase Tag ID's in the table below), and 2)
  132. PDF DocInfo tags (capitalized Tag ID's).
  133. },
  134. # BibTeX tags (ref http://en.wikipedia.org/wiki/BibTeX)
  135. address => { Groups => { 2 => 'Location' } },
  136. annote => { Name => 'Annotation' },
  137. author => { Groups => { 2 => 'Author' } },
  138. booktitle => { Name => 'BookTitle' },
  139. chapter => { },
  140. crossref => { Name => 'CrossRef' },
  141. edition => { },
  142. eprint => { Name => 'EPrint' },
  143. howpublished=> { Name => 'HowPublished' },
  144. institution => { },
  145. journal => { },
  146. key => { },
  147. month => { Groups => { 2 => 'Time' } },
  148. note => { },
  149. number => { },
  150. organization=> { },
  151. pages => { },
  152. publisher => { },
  153. school => { },
  154. series => { },
  155. title => { },
  156. type => { },
  157. url => { Name => 'URL' },
  158. volume => { },
  159. year => { Groups => { 2 => 'Time' } },
  160. # PDF tags (same as Image::ExifTool::PDF::Info)
  161. Title => { },
  162. Author => { Groups => { 2 => 'Author' } },
  163. Subject => { },
  164. Keywords => { },
  165. Creator => { },
  166. Producer => { },
  167. CreationDate => {
  168. Name => 'CreateDate',
  169. Groups => { 2 => 'Time' },
  170. # RFC 3339 date/time format
  171. ValueConv => 'require Image::ExifTool::XMP; Image::ExifTool::XMP::ConvertXMPDate($val)',
  172. PrintConv => '$self->ConvertDateTime($val)',
  173. },
  174. ModDate => {
  175. Name => 'ModifyDate',
  176. Groups => { 2 => 'Time' },
  177. ValueConv => 'require Image::ExifTool::XMP; Image::ExifTool::XMP::ConvertXMPDate($val)',
  178. PrintConv => '$self->ConvertDateTime($val)',
  179. },
  180. Trapped => {
  181. # remove leading '/' from '/True' or '/False'
  182. ValueConv => '$val=~s{^/}{}; $val',
  183. },
  184. );
  185. #------------------------------------------------------------------------------
  186. # Parse DjVu annotation "s-expression" syntax (recursively)
  187. # Inputs: 0) data ref (with pos($$dataPt) set to start of annotation)
  188. # Returns: reference to list of tokens/references, or undef if no tokens,
  189. # and the position in $$dataPt is set to end of last token
  190. # Notes: The DjVu annotation syntax is not well documented, so I make
  191. # a number of assumptions here!
  192. sub ParseAnt($)
  193. {
  194. my $dataPt = shift;
  195. my (@toks, $tok, $more);
  196. # (the DjVu annotation syntax really sucks, and requires that every
  197. # single token be parsed in order to properly scan through the items)
  198. Tok: for (;;) {
  199. # find the next token
  200. last unless $$dataPt =~ /(\S)/sg; # get next non-space character
  201. if ($1 eq '(') { # start of list
  202. $tok = ParseAnt($dataPt);
  203. } elsif ($1 eq ')') { # end of list
  204. $more = 1;
  205. last;
  206. } elsif ($1 eq '"') { # quoted string
  207. $tok = '';
  208. for (;;) {
  209. # get string up to the next quotation mark
  210. # this doesn't work in perl 5.6.2! grrrr
  211. # last Tok unless $$dataPt =~ /(.*?)"/sg;
  212. # $tok .= $1;
  213. my $pos = pos($$dataPt);
  214. last Tok unless $$dataPt =~ /"/sg;
  215. $tok .= substr($$dataPt, $pos, pos($$dataPt)-1-$pos);
  216. # we're good unless quote was escaped by odd number of backslashes
  217. last unless $tok =~ /(\\+)$/ and length($1) & 0x01;
  218. $tok .= '"'; # quote is part of the string
  219. }
  220. # convert C escape sequences (allowed in quoted text)
  221. $tok = eval qq{"$tok"};
  222. } else { # key name
  223. pos($$dataPt) = pos($$dataPt) - 1;
  224. # allow anything in key but whitespace, braces and double quotes
  225. # (this is one of those assumptions I mentioned)
  226. $$dataPt =~ /([^\s()"]+)/sg;
  227. $tok = $1;
  228. }
  229. push @toks, $tok if defined $tok;
  230. }
  231. # prevent further parsing unless more after this
  232. pos($$dataPt) = length $$dataPt unless $more;
  233. return @toks ? \@toks : undef;
  234. }
  235. #------------------------------------------------------------------------------
  236. # Process DjVu annotation chunk (ANTa or decoded ANTz)
  237. # Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) tag table ref
  238. # Returns: 1 on success
  239. sub ProcessAnt($$$)
  240. {
  241. my ($et, $dirInfo, $tagTablePtr) = @_;
  242. my $dataPt = $$dirInfo{DataPt};
  243. # quick pre-scan to check for metadata or XMP
  244. return 1 unless $$dataPt =~ /\(\s*(metadata|xmp)[\s("]/s;
  245. # parse annotations into a tree structure
  246. pos($$dataPt) = 0;
  247. my $toks = ParseAnt($dataPt) or return 0;
  248. # process annotations individually
  249. my $ant;
  250. foreach $ant (@$toks) {
  251. next unless ref $ant eq 'ARRAY' and @$ant >= 2;
  252. my $tag = shift @$ant;
  253. next if ref $tag or not defined $$tagTablePtr{$tag};
  254. if ($tag eq 'metadata') {
  255. # ProcessMeta() takes array reference
  256. $et->HandleTag($tagTablePtr, $tag, $ant);
  257. } else {
  258. next if ref $$ant[0]; # only process simple values
  259. $et->HandleTag($tagTablePtr, $tag, $$ant[0]);
  260. }
  261. }
  262. return 1;
  263. }
  264. #------------------------------------------------------------------------------
  265. # Process DjVu metadata
  266. # Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) tag table ref
  267. # Returns: 1 on success
  268. # Notes: input dirInfo DataPt is a reference to a list of pre-parsed metadata entries
  269. sub ProcessMeta($$$)
  270. {
  271. my ($et, $dirInfo, $tagTablePtr) = @_;
  272. my $dataPt = $$dirInfo{DataPt};
  273. return 0 unless ref $$dataPt eq 'ARRAY';
  274. $et->VerboseDir('Metadata', scalar @$$dataPt);
  275. my ($item, $err);
  276. foreach $item (@$$dataPt) {
  277. # make sure item is a simple tag/value pair
  278. $err=1, next unless ref $item eq 'ARRAY' and @$item >= 2 and
  279. not ref $$item[0] and not ref $$item[1];
  280. # add any new tags to the table
  281. unless ($$tagTablePtr{$$item[0]}) {
  282. my $name = $$item[0];
  283. $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
  284. length $name or $err = 1, next;
  285. AddTagToTable($tagTablePtr, $$item[0], { Name => ucfirst($name) });
  286. }
  287. $et->HandleTag($tagTablePtr, $$item[0], $$item[1]);
  288. }
  289. $err and $et->Warn('Ignored invalid metadata entry(s)');
  290. return 1;
  291. }
  292. #------------------------------------------------------------------------------
  293. # Process BZZ-compressed data (in DjVu images)
  294. # Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) tag table ref
  295. # Returns: 1 on success
  296. sub ProcessBZZ($$$)
  297. {
  298. my ($et, $dirInfo, $tagTablePtr) = @_;
  299. require Image::ExifTool::BZZ;
  300. my $buff = Image::ExifTool::BZZ::Decode($$dirInfo{DataPt});
  301. unless (defined $buff) {
  302. $et->Warn("Error decoding $$dirInfo{DirName}");
  303. return 0;
  304. }
  305. my $verbose = $et->Options('Verbose');
  306. if ($verbose >= 3) {
  307. # dump the decoded data in very verbose mode
  308. $et->VerboseDir("Decoded $$dirInfo{DirName}", 0, length $buff);
  309. $et->VerboseDump(\$buff);
  310. }
  311. $$dirInfo{DataPt} = \$buff;
  312. $$dirInfo{DataLen} = $$dirInfo{DirLen} = length $buff;
  313. # process the data using the default process proc for this table
  314. my $processProc = $$tagTablePtr{PROCESS_PROC} or return 0;
  315. return &$processProc($et, $dirInfo, $tagTablePtr);
  316. }
  317. 1; # end
  318. __END__
  319. =head1 NAME
  320. Image::ExifTool::DjVu - Read DjVu meta information
  321. =head1 SYNOPSIS
  322. This module is used by Image::ExifTool
  323. =head1 DESCRIPTION
  324. This module contains definitions required by Image::ExifTool to extract meta
  325. information from DjVu images. Parsing of the DjVu IFF structure is done by
  326. Image::ExifTool::AIFF.
  327. =head1 AUTHOR
  328. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  329. This library is free software; you can redistribute it and/or modify it
  330. under the same terms as Perl itself.
  331. =head1 REFERENCES
  332. =over 4
  333. =item L<http://djvu.sourceforge.net/>
  334. =item L<http://www.djvu.org/>
  335. =back
  336. =head1 SEE ALSO
  337. L<Image::ExifTool::TagNames/DjVu Tags>,
  338. L<Image::ExifTool::AIFF(3pm)|Image::ExifTool::AIFF>,
  339. L<Image::ExifTool(3pm)|Image::ExifTool>
  340. =cut