- Timestamp:
- 2021-02-26T19:39:51+13:00 (3 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
main/trunk/greenstone2/perllib/cpan/Image/ExifTool/WritePDF.pl
r24107 r34921 32 32 ); 33 33 34 # map for directories that we can add 35 my %pdfMap = ( 36 XMP => 'PDF', 37 ); 38 34 39 #------------------------------------------------------------------------------ 35 40 # Validate raw PDF values for writing (string date integer real boolean name) … … 38 43 sub CheckPDF($$$) 39 44 { 40 my ($e xifTool, $tagInfo, $valPtr) = @_;45 my ($et, $tagInfo, $valPtr) = @_; 41 46 my $format = $$tagInfo{Writable} || $tagInfo->{Table}->{WRITABLE}; 42 47 if (not $format) { 43 48 return 'No writable format'; 44 49 } elsif ($format eq 'string') { 45 # convert to Unicode if necessary 46 if ($$valPtr =~ /[\x80-\xff]/) { 47 $$valPtr = "\xfe\xff" . $exifTool->Encode($$valPtr,'UCS2','MM'); 48 } 50 # (encode later because list-type string tags need to be encoded as a unit) 49 51 } elsif ($format eq 'date') { 50 52 # be flexible about this for now … … 59 61 return 'Invalid PDF name' if $$valPtr =~ /\0/; 60 62 } else { 61 return "Invalid PDF format '$ format'";63 return "Invalid PDF format '${format}'"; 62 64 } 63 65 return undef; # value is OK … … 66 68 #------------------------------------------------------------------------------ 67 69 # Format value for writing to PDF file 68 # Inputs: 0) value, 1) format string (string,date,integer,real,boolean,name)70 # Inputs: 0) ExifTool ref, 1) value, 2) format string (string,date,integer,real,boolean,name) 69 71 # Returns: formatted value or undef on error 70 72 # Notes: Called at write time, so $pdfVer may be checked 71 sub WritePDFValue($$ )73 sub WritePDFValue($$$) 72 74 { 73 my ($ val, $format) = @_;75 my ($et, $val, $format) = @_; 74 76 if (not $format) { 75 77 return undef; 76 78 } elsif ($format eq 'string') { 79 # encode as UCS2 if it contains any special characters 80 $val = "\xfe\xff" . $et->Encode($val,'UCS2','MM') if $val =~ /[\x80-\xff]/; 77 81 EncodeString(\$val); 78 82 } elsif ($format eq 'date') { 79 83 # convert date to "D:YYYYmmddHHMMSS+-HH'MM'" format 80 $val =~ s/([-+]\d{2}):(\d{2})/$ 1'$2'/; # change timezone delimiters if necessary84 $val =~ s/([-+]\d{2}):(\d{2})/${1}'${2}'/; # change timezone delimiters if necessary 81 85 $val =~ tr/ ://d; # remove spaces and colons 82 86 $val = "D:$val"; # add leading "D:" … … 273 277 sub WritePDF($$) 274 278 { 275 my ($e xifTool, $dirInfo) = @_;279 my ($et, $dirInfo) = @_; 276 280 my $raf = $$dirInfo{RAF}; 277 281 my $outfile = $$dirInfo{OutFile}; … … 281 285 # make sure this is a PDF file 282 286 my $pos = $raf->Tell(); 283 $raf->Read($buff, 10) >= 8 or return 0; 284 $buff =~ /^%PDF-(\d+\.\d+)/ or return 0; 287 $raf->Read($buff, 1024) >= 8 or return 0; 288 $buff =~ /^(\s*)%PDF-(\d+\.\d+)/ or return 0; 289 $$et{PDFBase} = length $1; 285 290 $raf->Seek($pos, 0); 286 291 … … 288 293 my $newTool = new Image::ExifTool; 289 294 $newTool->Options(List => 1); 290 $newTool->Options(Password => $e xifTool->Options('Password'));295 $newTool->Options(Password => $et->Options('Password')); 291 296 $$newTool{PDF_CAPTURE} = \%capture; 292 297 my $info = $newTool->ImageInfo($raf, 'XMP', 'PDF:*', 'Error', 'Warning'); … … 294 299 # (note: can't just check $$info{PDFVersion} due to possibility of XMP-pdf:PDFVersion) 295 300 my $vers = $newTool->GetInfo('PDF:PDFVersion'); 296 ($pdfVer) = values %$vers; 297 $pdfVer or $exifTool->Error('Missing PDF:PDFVersion'), return 0; 301 # take highest version number if multiple versions in an incremental save 302 ($pdfVer) = sort { $b <=> $a } values %$vers; 303 $pdfVer or $et->Error('Missing PDF:PDFVersion'), return 0; 298 304 # check version number 299 305 if ($pdfVer > 1.7) { 300 if ($pdfVer >= 2.0) { 301 $exifTool->Error("Can't yet write PDF version $pdfVer"); # (future major version changes) 302 return 1; 303 } 304 $exifTool->Warn("ExifTool is untested with PDF version $pdfVer files", 1); 306 $et->Warn("The PDF $pdfVer specification is not freely available", 1); 307 # (so writing by ExifTool is based on trial and error) 305 308 } 306 309 # fail if we had any serious errors while extracting information 307 310 if ($capture{Error} or $$info{Error}) { 308 $e xifTool->Error($capture{Error} || $$info{Error});311 $et->Error($capture{Error} || $$info{Error}); 309 312 return 1; 310 313 } … … 313 316 next if $capture{$obj}; 314 317 # any warning we received may give a clue about why this object is missing 315 $e xifTool->Error($$info{Warning}) if $$info{Warning};316 $e xifTool->Error("Can't find $obj object");318 $et->Error($$info{Warning}) if $$info{Warning}; 319 $et->Error("Can't find $obj object"); 317 320 return 1; 318 321 } 322 $et->InitWriteDirs(\%pdfMap, 'XMP'); 319 323 320 324 # copy file up to start of previous exiftool update or end of file … … 327 331 $prevUpdate = $1; 328 332 # rewrite the file up to the original EOF 329 Image::ExifTool::CopyBlock($raf, $outfile, $prevUpdate ) or $rtn = -1;333 Image::ExifTool::CopyBlock($raf, $outfile, $prevUpdate + $$et{PDFBase}) or $rtn = -1; 330 334 # verify that we are now at the start of an ExifTool update 331 335 unless ($raf->Read($buff, length $beginComment) and $buff eq $beginComment) { 332 $e xifTool->Error('Previous ExifTool update is corrupted');336 $et->Error('Previous ExifTool update is corrupted'); 333 337 return $rtn; 334 338 } 335 $raf->Seek($prevUpdate , 0) or $rtn = -1;336 if ($ exifTool->{DEL_GROUP}->{'PDF-update'}) {337 $e xifTool->VPrint(0, " Reverted previous ExifTool updates\n");338 ++$$e xifTool{CHANGED};339 $raf->Seek($prevUpdate+$$et{PDFBase}, 0) or $rtn = -1; 340 if ($$et{DEL_GROUP}{'PDF-update'}) { 341 $et->VPrint(0, " Reverted previous ExifTool updates\n"); 342 ++$$et{CHANGED}; 339 343 return $rtn; 340 344 } 341 } elsif ($ exifTool->{DEL_GROUP}->{'PDF-update'}) {342 $e xifTool->Error('File contains no previous ExifTool update');345 } elsif ($$et{DEL_GROUP}{'PDF-update'}) { 346 $et->Error('File contains no previous ExifTool update'); 343 347 return $rtn; 344 348 } else { … … 348 352 } 349 353 } 350 $out = $e xifTool->Options('TextOut') if $exifTool->Options('Verbose');354 $out = $et->Options('TextOut') if $et->Options('Verbose'); 351 355 # 352 356 # create our new PDF objects to write … … 362 366 if ($prevUpdate) { 363 367 unless ($capture{Prev}) { 364 $e xifTool->Error("Can't locate trailer dictionary prior to last edit");368 $et->Error("Can't locate trailer dictionary prior to last edit"); 365 369 return $rtn; 366 370 } … … 378 382 # delete entire PDF group if specified 379 383 my $infoChanged = 0; 380 if ($ exifTool->{DEL_GROUP}->{PDF} and $capture{Info}) {384 if ($$et{DEL_GROUP}{PDF} and $capture{Info}) { 381 385 delete $capture{Info}; 382 386 $info = { XMP => $$info{XMP} }; # remove extracted PDF tags … … 395 399 # must encrypt all values in dictionary if they came from an encrypted stream 396 400 CryptObject($infoDict) if $$infoDict{_needCrypt}; 397 401 398 402 # must set line separator before calling WritePDFValue() 399 403 local $/ = $capture{newline}; 400 404 401 405 # rewrite PDF Info tags 402 my $newTags = $e xifTool->GetNewTagInfoHash(\%Image::ExifTool::PDF::Info);406 my $newTags = $et->GetNewTagInfoHash(\%Image::ExifTool::PDF::Info); 403 407 my $tagID; 404 408 foreach $tagID (sort keys %$newTags) { 405 409 my $tagInfo = $$newTags{$tagID}; 406 my $nvHash = $e xifTool->GetNewValueHash($tagInfo);410 my $nvHash = $et->GetNewValueHash($tagInfo); 407 411 my (@vals, $deleted); 408 412 my $tag = $$tagInfo{Name}; … … 421 425 } 422 426 for (;;) { 423 if ( Image::ExifTool::IsOverwriting($nvHash, $val) > 0) {427 if ($et->IsOverwriting($nvHash, $val) > 0) { 424 428 $deleted = 1; 425 $e xifTool->VerboseValue("- PDF:$tag", $val);429 $et->VerboseValue("- PDF:$tag", $val); 426 430 ++$infoChanged; 427 431 } else { … … 433 437 # don't write this out if we deleted all values 434 438 delete $$infoDict{$tagID} unless @vals; 439 } elsif ($$nvHash{EditOnly}) { 440 next; 435 441 } 436 442 # decide whether we want to write this tag 437 # ( always create native PDF information, so don't check IsCreating())443 # (native PDF information is always preferred, so don't check IsCreating) 438 444 next unless $deleted or $$tagInfo{List} or not exists $$infoDict{$tagID}; 439 445 440 446 # add new values to existing ones 441 my @newVals = Image::ExifTool::GetNewValues($nvHash);447 my @newVals = $et->GetNewValue($nvHash); 442 448 if (@newVals) { 443 449 push @vals, @newVals; … … 445 451 if ($out) { 446 452 foreach $val (@newVals) { 447 $e xifTool->VerboseValue("+ PDF:$tag", $val);453 $et->VerboseValue("+ PDF:$tag", $val); 448 454 } 449 455 } … … 457 463 my $writable = $$tagInfo{Writable} || $Image::ExifTool::PDF::Info{WRITABLE}; 458 464 if (not $$tagInfo{List}) { 459 $val = WritePDFValue( shift @vals, $writable);465 $val = WritePDFValue($et, shift(@vals), $writable); 460 466 } elsif ($$tagInfo{List} eq 'array') { 461 467 foreach $val (@vals) { 462 $val = WritePDFValue($ val, $writable);468 $val = WritePDFValue($et, $val, $writable); 463 469 defined $val or undef(@vals), last; 464 470 } 465 471 $val = @vals ? \@vals : undef; 466 472 } else { 467 $val = WritePDFValue( join($exifTool->Options('ListSep'), @vals), $writable);473 $val = WritePDFValue($et, join($et->Options('ListSep'), @vals), $writable); 468 474 } 469 475 if (defined $val) { … … 471 477 ++$infoChanged; 472 478 } else { 473 $e xifTool->Warn("Error converting $$tagInfo{Name} value");479 $et->Warn("Error converting $$tagInfo{Name} value"); 474 480 } 475 481 } 476 482 if ($infoChanged) { 477 $$e xifTool{CHANGED} += $infoChanged;483 $$et{CHANGED} += $infoChanged; 478 484 } elsif ($prevUpdate) { 479 485 # must still write Info dictionary if it was previously updated … … 503 509 ); 504 510 my $xmpTable = Image::ExifTool::GetTagTable('Image::ExifTool::XMP::Main'); 505 my $oldChanged = $$e xifTool{CHANGED};506 my $newXMP = $e xifTool->WriteDirectory(\%xmpInfo, $xmpTable);511 my $oldChanged = $$et{CHANGED}; 512 my $newXMP = $et->WriteDirectory(\%xmpInfo, $xmpTable); 507 513 $newXMP = $$info{XMP} ? ${$$info{XMP}} : '' unless defined $newXMP; 508 514 … … 510 516 # XMP is deleted as a block -- so check for this 511 517 unless ($newXMP or $$info{XMP}) { 512 $$e xifTool{CHANGED} = $oldChanged;513 $e xifTool->VPrint(0, " (XMP not changed -- still empty)\n");518 $$et{CHANGED} = $oldChanged; 519 $et->VPrint(0, " (XMP not changed -- still empty)\n"); 514 520 } 515 521 my ($metaChanged, $rootChanged); 516 522 517 if ($$e xifTool{CHANGED} != $oldChanged and defined $newXMP) {523 if ($$et{CHANGED} != $oldChanged and defined $newXMP) { 518 524 $metaChanged = 1; 519 525 } elsif ($prevUpdate and $capture{Root}->{Metadata}) { … … 550 556 my $rootRef = $$mainDict{Root}; 551 557 unless ($rootRef) { 552 $e xifTool->Error("Can't find Root dictionary");558 $et->Error("Can't find Root dictionary"); 553 559 return $rtn; 554 560 } … … 562 568 # write incremental update if anything was changed 563 569 # 564 if ($$e xifTool{CHANGED}) {570 if ($$et{CHANGED}) { 565 571 # remember position of original EOF 566 my $oldEOF = Tell($outfile) ;572 my $oldEOF = Tell($outfile) - $$et{PDFBase}; 567 573 Write($outfile, $beginComment) or $rtn = -1; 568 574 … … 578 584 } 579 585 # create new entry for xref table 580 $newXRef{$id} = [ Tell($outfile) + length($/), $gen, 'n' ];586 $newXRef{$id} = [ Tell($outfile) - $$et{PDFBase} + length($/), $gen, 'n' ]; 581 587 $keyExt = "$id $gen obj"; # (must set for stream encryption) 582 588 Write($outfile, $/, $keyExt) or $rtn = -1; … … 629 635 my $byte = unpack('H2',chr((hex($1) + 1) & 0xff)); 630 636 substr($id, 1, 2) = $byte; 631 } elsif ($id =~ /^\((.)/s) { 632 substr($id, 1, 1) = chr((ord($1) + 1) & 0xff); 637 } elsif ($id =~ /^\((.)/s and $1 ne '\\' and $1 ne ')' and $1 ne '(') { 638 my $ch = chr((ord($1) + 1) & 0xff); 639 # avoid generating characters that could cause problems 640 $ch = 'a' if $ch =~ /[()\\\x00-\x08\x0a-\x1f\x7f\xff]/; 641 substr($id, 1, 1) = $ch; 633 642 } 634 643 $mainDict->{ID}->[1] = $id; … … 636 645 637 646 # remember position of xref table in file (we will write this next) 638 my $startxref = Tell($outfile) + length($/);647 my $startxref = Tell($outfile) - $$et{PDFBase} + length($/); 639 648 640 649 # must write xref as a stream in xref-stream-only files … … 642 651 643 652 # create entry for the xref stream object itself 644 $newXRef{$nextObject++} = [ Tell($outfile) + length($/), 0, 'n' ];653 $newXRef{$nextObject++} = [ Tell($outfile) - $$et{PDFBase} + length($/), 0, 'n' ]; 645 654 $$mainDict{Size} = $nextObject; 646 655 # create xref stream and Index entry … … 655 664 my ($pos, $gen, $type) = @{$newXRef{$id}}; 656 665 if ($pos > 0xffffffff) { 657 $e xifTool->Error('Huge files not yet supported');666 $et->Error('Huge files not yet supported'); 658 667 last; 659 668 } … … 702 711 703 712 # nothing new changed, so copy over previous incremental update 704 $raf->Seek($prevUpdate , 0) or $rtn = -1;713 $raf->Seek($prevUpdate+$$et{PDFBase}, 0) or $rtn = -1; 705 714 while ($raf->Read($buff, 65536)) { 706 715 Write($outfile, $buff) or $rtn = -1; 707 716 } 717 } 718 if ($rtn > 0 and $$et{CHANGED} and ($$et{DEL_GROUP}{PDF} or $$et{DEL_GROUP}{XMP})) { 719 $et->Warn('ExifTool PDF edits are reversible. Deleted tags may be recovered!', 1); 708 720 } 709 721 undef $newTool; … … 738 750 =head1 AUTHOR 739 751 740 Copyright 2003-20 11, Phil Harvey (phil at owl.phy.queensu.ca)752 Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com) 741 753 742 754 This library is free software; you can redistribute it and/or modify it
Note:
See TracChangeset
for help on using the changeset viewer.