09 июля 2020

Уменьшение размера PDF файла

    Часто сгенерированный программой файл формата PDF имеет размер больше, чем нам хотелось бы. Возникает законное желание как-нибудь его уменьшить. Документ формата PDF представляет собой контейнер, который состоит из различных элементов: текст, изображения, шрифты и так далее. Все эти элементы можно сжать, а значит размер PDF файла можно уменьшить.
    Рассмотрим уменьшение размера документа в формате PDF на примере работы с Debenu Quick PDF Library, с помощью методов библиотеки и собственными средствами. Библиотека Debenu Quick PDF Library для уменьшения размера документа имеет несколько функций:
  1. CompressContent – функция сжимает текстовое содержимое документа. Ее необходимо вызвать после окончания формирования документа. К сожалению, эта функция не дает существенного уменьшения размера файла.
  2. CompressPage – эта функция аналогична функции CompressContent, но сжимает только выбранную страницу.
  3. CompressImages – функция позволяет указать, следует ли сжимать добавляемые в документ изображения. Ее нужно вызывать до вызова функций добавления изображений. Сжатию подлежат только изображения, которые добавляются в PDF в несжатом состоянии. А значит есть шанс, что размер уменьшится при добавлении изображений в формате BMP или TIFF, а для наиболее распространенных форматов, таких как JPEG и PNG, эта функция бесполезна.
  4. CompressFonts – функция позволяет указать, следует ли сжимать добавляемые в документ шрифты. Ее нужно вызывать до вызова функций добавления шрифтов. В результате моих экспериментов, эта функция единственная, которая давала заметное уменьшение размера файла.
Эффект от использования приведенных выше функций Debenu Quick PDF Library может быть несравнимо маленьким по сравнению с размером файла PDF-документа. Поэтому нужно придумать, что-то самому.
    У многих PDF-документов большую часть размера файла составляют изображения. А значит единственным способом уменьшить размер такого PDF-документа – это предварительно ухудшить до приемлемого состояния качество изображений. Например, изображения могут иметь большое разрешение, которое можно спокойно уменьшить во много раз и это не скажется на качестве PDF-документа. Можно через параметры программы предоставить пользователю самому определить нужный ему баланс между качеством изображений и размером результирующего файла.
    Добавим в класс, который формирует PDF-документ, поля с параметрами:
type
  TCreatePDF = class
  private
    FPDF: TDebenuPDFLibrary;
    // изменять или нет изображения
    FInJpegScale: Boolean;
    // качество сжатия изображения
    FInJpegQuality: Integer;
    // максимальное разрешение изображений
    FInJpegMaxWidth: Integer;  
    FInJpegMaxHeight: Integer;
  public
    property InJpegScale: Boolean read FInJpegScale 
                                  write FInJpegScale;
    property InJpegQuality: Integer read FInJpegQuality 
                                    write FInJpegQuality;
    property InJpegMaxWidth: Integer read FInJpegMaxWidth 
                                     write FInJpegMaxWidth;
    property InJpegMaxHeight: Integer read FInJpegMaxHeight
                                      write FInJpegMaxHeight;
и метод:
function AddScaledImageFromFile(const FileName: String; 
                                const Options: Integer): Integer;
Реализация методов класса:
constructor TCreatePDF.Create;
begin
  inherited;
  FInJpegScale := False;
  FInJpegQuality := 75;
  FInJpegMaxWidth := 0;
  FInJpegMaxHeight := 0;
  ...
end;

function TCreatePDF.AddScaledImageFromFile(const FileName: String; 
                                           const Options: Integer): Integer;
var
  jpg: TJPEGImage;
  ms : TStream;
  iNewWidth, iNewHeight: Integer;
  bNewJpegQuality: Boolean;
begin
  if FInJpegScale and SameText(ExtractFileExt(FileName), '.jpg')
    then try // jpeg, который надо обработать
      jpg := TJPEGImage.Create;
      try
        jpg.Loadfromfile(FileName);
        if (FInJpegQuality <> 0) and (jpg.CompressionQuality <> FInJpegQuality)
          then begin
             bNewJpegQuality := True;
             jpg.CompressionQuality := FInJpegQuality;
          end
          else bNewJpegQuality := False;
        if FInJpegMaxWidth > 0
          then iNewWidth := FInJpegMaxWidth
          else iNewWidth := jpg.Width;
        if FInJpegMaxHeight > 0
          then iNewHeight := FInJpegMaxHeight
          else iNewHeight := jpg.Height;
        if (jpg.Width > iNewWidth) or (jpg.Height > iNewHeight)
          then ScaleJpeg(jpg, iNewWidth, iNewHeight, HALFTONE)
          else if bNewJpegQuality then
                 jpg.Compress;
        // добавляем jpeg в PDF через поток
        ms := TMemoryStream.Create;
        try
          jpg.SaveToStream(ms);
          Result := FPDF.AddImageFromStream(ms, Options);
        finally
          ms.Free;
        end
      finally
        jpg.free;
      end;
    except
      on E: Exception do
        Result := 0;
    end
    else Result := FPDF.AddImageFromFile(FileName, Options);
end;
    Реализацию метода ScaleJpeg я подробно рассмотрел в статье "Изменение разрешения изображения". Так же можно воспользоваться class-helper'ом из продолжения этой статьи. То есть строку вызова функции изменения разрешения изображения "then ScaleJpeg(jpg, iNewWidth, iNewHeight, HALFTONE)" заменить на вызов метода "then jpg.Resize(iNewWidth, iNewHeight, HALFTONE)".
    Таким образом мы видим, что стандартные методы библиотеки Debenu Quick PDF Library почти не могут помочь в уменьшении размера создаваемого файла. Это, я думаю, касается и других подобных библиотек. Поэтому спасти программиста может только индивидуальный контроль различных элементов документа.

Комментариев нет:

Отправка комментария