Как удалить все изображения из PDF, не повреждая его с помощью CAM::PDF?
Сценарий ниже может удалить все изображения из файла PDF, используя CAM::PDF
, Вывод, однако, поврежден. Тем не менее, читатели PDF могут открыть его, но жалуются на ошибки. Например, mupdf
говорит:
error: no XObject subtype specified
error: cannot draw xobject/image
warning: Ignoring errors during rendering
mupdf: warning: Errors found on page
Сейчас, CAM::PDF
на странице CPAN ( здесь) перечислены deleteObject()
метод в разделе "Более глубокие утилиты", что, вероятно, означает, что он не предназначен для публичного использования. Кроме того, он предупреждает, что:
Эта функция НЕ заботится о зависимостях от этого объекта.
Мой вопрос: как правильно удалить объекты из файла PDF, используя CAM::PDF
? Если проблема связана с зависимостями, как я могу удалить объект, заботясь о его зависимостях?
Чтобы узнать, как удалить изображения из PDF-файла с помощью других инструментов, см. Соответствующий вопрос здесь.
use CAM::PDF;
my $pdf = new CAM::PDF ( shift ) or die $CAM::PDF::errstr;
foreach my $objnum ( sort { $a <=> $b } keys %{ $pdf->{xref} } ) {
my $xobj = $pdf->dereference ( $objnum );
if ( $xobj->{value}->{type} eq 'dictionary' ) {
my $im = $xobj->{value}->{value};
if
(
defined $im->{Type} and defined $im->{Subtype}
and $pdf->getValue ( $im->{Type} ) eq 'XObject'
and $pdf->getValue ( $im->{Subtype} ) eq 'Image'
)
{
$pdf->deleteObject ( $objnum );
}
}
}
$pdf->cleanoutput ( '-' );
2 ответа
Это использует CAM::PDF, но использует немного другой подход. Вместо того, чтобы пытаться удалить изображения, что довольно сложно, оно заменяет каждое изображение прозрачным.
Во-первых, обратите внимание, что мы можем использовать магию изображения для создания пустого PDF, который содержит только прозрачное изображение:
% convert -size 200x100 xc:none transparent.pdf
Если мы просмотрим сгенерированный PDF в текстовом редакторе, мы можем найти основной объект изображения:
8 0 obj
<<
/Type /XObject
/Subtype /Image
/Name /Im0
...
Здесь важно отметить, что мы создали прозрачное изображение как объект № 8.
Затем возникает вопрос импорта этого объекта и использования его для замены каждого из реальных изображений в PDF-файле, эффективно удаляя их.
use warnings; use strict;
use CAM::PDF;
my $pdf = new CAM::PDF ( shift ) or die $CAM::PDF::errstr;
my $trans_pdf = CAM::PDF->new("transparent.pdf") || die "$CAM::PDF::errstr\n";
my $trans_objnum = 8; # object number of transparent image
foreach my $objnum ( sort { $a <=> $b } keys %{ $pdf->{xref} } ) {
my $xobj = $pdf->dereference ( $objnum );
if ( $xobj->{value}->{type} eq 'dictionary' ) {
my $im = $xobj->{value}->{value};
if
(
defined $im->{Type} and defined $im->{Subtype}
and $pdf->getValue ( $im->{Type} ) eq 'XObject'
and $pdf->getValue ( $im->{Subtype} ) eq 'Image'
) {
$pdf->replaceObject ( $objnum, $trans_pdf, $trans_objnum, 1 );
}
}
}
$pdf->cleanoutput ( '-' );
Сценарий теперь заменяет каждое изображение в PDF импортированным прозрачным объектом изображения (объект № 8 из transparent.pdf
).
Другой подход, который действительно удаляет изображения:
- найти и удалить изображения XObjects в списках ресурсов,
- сохранить массив с именами удаленных ресурсов,
- заменить одинаковые пробелы на соответствующие
Do
операторы в каждом содержании страницы, - очистить и распечатать.
Обратите внимание, что подход дварринга более безопасен, так как он не должен вызывать $doc->cleanse
в конце. Согласно CAM::PDF
документация ( здесь), cleanse
метод
Удалить неиспользуемые предметы. ВНИМАНИЕ: эта функция разбивает некоторые документы PDF, потому что она удаляет объекты, которые строго являются частью иерархии модели страницы, но которые в любом случае необходимы (например, некоторые объекты определения шрифта).
Я не знаю, сколько проблем с использованием cleanse
может быть.
use CAM::PDF;
my $doc = new CAM::PDF ( shift ) or die $CAM::PDF::errstr;
# delete image XObjects among resources
# but keep their names
my @names;
foreach my $objnum ( sort { $a <=> $b } keys %{ $doc->{xref} } ) {
my $obj = $doc->dereference( $objnum );
next unless $obj->{value}->{type} eq 'dictionary';
my $n = $obj->{value}->{value};
my $resources = $doc->getValue ( $n->{Resources} ) or next;
my $resource = $doc->getValue ( $resources->{XObject} ) or next;
foreach my $name ( sort keys $resource ) {
my $im = $doc->getValue ( $resource->{$name} ) or next;
next unless defined $im->{Type}
and defined $im->{Subtype}
and $doc->getValue ( $im->{Type} ) eq 'XObject'
and $doc->getValue ( $im->{Subtype} ) eq 'Image';
delete $resource->{$name};
push @names, $name;
}
}
# delete the corresponding Do operators
if ( @names ) {
foreach my $p ( 1 .. $doc->numPages ) {
my $content = $doc->getPageContent ( $p );
my $s;
foreach my $name ( @names ) {
++$s if $content =~ s{( / \Q$name\E \s+ Do \b )} { ' ' x length $1 }xeg;
}
$doc->setPageContent ( $p, $content ) if $s;
}
}
$doc->cleanse;
$doc->cleanoutput;