Поиск на сайте: Расширенный поиск


Новые программы oszone.net Читать ленту новостей RSS
CheckBootSpeed - это диагностический пакет на основе скриптов PowerShell, создающий отчет о скорости загрузки Windows 7 ...
Вы когда-нибудь хотели создать установочный диск Windows, который бы автоматически установил систему, не задавая вопросо...
Если после установки Windows XP у вас перестала загружаться Windows Vista или Windows 7, вам необходимо восстановить заг...
Программа подготовки документов и ведения учетных и отчетных данных по командировкам. Используются формы, утвержденные п...
Red Button – это мощная утилита для оптимизации и очистки всех актуальных клиентских версий операционной системы Windows...
OSzone.net Microsoft Разработка приложений Windows (до Windows 10) Рендеринг PDF-контента в приложениях Windows Store RSS

Рендеринг PDF-контента в приложениях Windows Store

Текущий рейтинг: 4 (проголосовало 2)
 Посетителей: 762 | Просмотров: 822 (сегодня 0)  Шрифт: - +

PDF — как формат хранилища документов и архивирования — является общепризнанным в современном мире. Такие документы, как книги, технические руководства, руководства пользователей, отчеты и многое другое, хранятся в формате PDF. Он позволяет использовать документы на разных платформах, если на них есть поддерживаемое средство просмотра PDF. Если просмотр PDF-документов не составляет труда, поддержка рендеринга PDF-контента остается трудной задачей, особенно для разработчиков приложений Windows Store. С появлением Windows 8.1 компания Microsoft ввела новые API, облегчающие процесс рендеринга PDF-контента в приложениях Windows Store.

В этой статье я рассмотрю различные способы выполнения такого рендеринга. Сначала я уделю внимание API, которые являются частью Windows Runtime (WinRT) и доступны через JavaScript, C#, Visual Basic .NET и C++. Затем я сосредоточусь на неуправляемых API, позволяющих разработчикам на C++ осуществлять рендеринг PDF-контента непосредственно на DirectX-поверхности рисования.

Windows Runtime API для рендеринга PDF

В Windows Runtime для Windows 8.1 включено новое пространство имен, Windows.Data.Pdf, которое содержит новые классы и структуры исполняющей среды, поддерживающие рендеринг PDF в приложениях Windows Store. В этом разделе мы обсудим различные классы, образующие пространство имен Windows.Data.Pdf; они используются для открытия PDF-документов, обработки защиты паролем, рендеринга документов, настройки процесса рендеринга и др.

Открытие PDF-документов Открытие таких документов программным способом не сложнее вызова статического метода LoadFromFileAsync из класса исполняющей среды PdfDocument. Этот класс является начальной точкой входа для работы с PDF-документами. Метод LoadFromFileAsync принимает объект StorageFile и начинает процесс загрузки PdfDocument. Загрузка PDF-документа иногда занимает длительное время, поэтому данный API возвращает асинхронную операцию. По завершении этой асинхронной операции вы получаете корректный экземпляр объекта PdfDocument:

// Получаем объект StorageFile, предлагая пользователю
// выбрать какой-нибудь файл .pdf
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.List;
openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
openPicker.FileTypeFilter.Add(".pdf");
StorageFile pdfFile = await openPicker.PickSingleFileAsync();
// Загружаем PdfDocument из выбранного файла
create_task(PdfDocument::LoadFromFileAsync(pdfFile)).then(
[this](PdfDocument^ pdfDoc)
{
// Здесь обрабатываем открытый PDF-документ
});

Помимо метода LoadFromFileAsync, класс PdfDocument содержит вспомогательный статический метод для создания экземпляра PdfDocument на основе объекта потока (stream object). Если вы уже храните ссылку на какой-либо PDF-документ как экземпляр RandomAccessStream, то можете просто передать этот объект потока методу LoadFromStreamAsync. В зависимости от вашего сценария вы можете использовать либо метод LoadFromFileAsync, либо метод LoadFromStreamAsync, чтобы создать экземпляр объекта PdfDocument. Получив корректный экземпляр PdfDocument, вы можете обращаться к индивидуальным страницам в документе.

С появлением Windows 8.1 компания Microsoft ввела новые API, облегчающие процесс рендеринга PDF-контента в приложениях Windows Store.

Обработка PDF-документов, защищенных паролем PDF-документы используются для хранения самой разнообразной информации, например выписок по кредитной карте (credit-card statements) или других конфиденциальных данных. Некоторые публикаторы не хотят, чтобы пользователи получали неограниченный доступ к документам такого рода, и защищают их паролями. Доступ выдается только тем приложениям, в двоичных файлах которых содержится пароль. У методов LoadFromFileAsync и LoadFromStreamAsync класса PdfDocument есть перегруженные версии, которые принимают пароль через строковый параметр:

// Загружаем PdfDocument, защищенный паролем,
// из выбранного файла
create_task(PdfDocument::LoadFromFileAsync(
pdfFile, "password")).then([this](PdfDocument^ pdfDoc){
// Здесь обрабатываем открытый PDF-документ
});

Если вы попытаетесь загрузить защищенный паролем документ, не указав пароль, методы LoadFromFileAsync и LoadFromStreamAsync сгенерируют исключение.

Доступ к страницам в PDF-документе После создания экземпляра объекта PdfDocument свойство Count вернет количество страниц в PDF-документе. Вы можете просто перебирать страницы в цикле в диапазоне от 0 до Count – 1, чтобы обращаться к индивидуальным страницам. Каждая страница имеет тип PdfPage. Класс PdfPage содержит метод PreparePageAsync, который начинает процесс подготовки PDF-страницы для рендеринга. Подготовка страницы включает разбор и загрузку страницы, инициализацию Direct2D-ресурсов для корректной обработки графических траекторий и фигур, инициализацию DirectWrite для обработки должного набора шрифтов с целью последующего рендеринга текста и т. д. Если вы не вызовите PreparePageAsync до начала рендеринга PDF-страниц, процесс рендеринга вызовет этот метод за вас в неявной форме. Однако вы должны сами вызывать PreparePageAsync и подготавливать страницы, а не полагаться в этом на процесс рендеринга. Подготовка страницы до самого рендеринга экономит время и является хорошей оптимизацией.

Рендеринг PDF-страниц Как только объекты PdfPage подготовлены, можно выполнять их рендеринг. Rendering API кодирует PdfPage как изображение и пишет данные этого изображения в поток данных, предоставленный разработчиком. Затем поток может быть задан как источник для элемента управления Image в UI приложения или записан на диск на использования в будущем.

Рендеринг происходит после вызова метода RenderToStreamAsync класса PdfPage. Этот метод принимает экземпляр IRandomAccessStream или один из его производных типов и записывает кодированные данные в поток.

Настройка рендеринга страницы Процесс рендеринга по умолчанию включает кодирование PdfPage как PNG-изображения с реальными размерами PDF-страницы в документе. Вы можете изменить кодирование по умолчанию с PNG на BMP или JPEG, хотя я настоятельно рекомендую использовать кодирование в PNG для визуализации на экране, так как это формат без потерь и, кроме того, он не приводит к созданию больших битовых карт. Однако, если вы хотите генерировать изображения из PDF-страниц и хранить их на диске для последующего применения, подумайте об использовании кодирования JPEG, поскольку в этом случае создаются файлы изображения меньших размеров с приемлемым разрешением. Вы также можете указать SourceRect и DestinationWidth, чтобы выполнять рендеринг лишь части PDF-страницы, например в ответ на операцию приближения (zoom-in operation). Можно выбрать и рендеринг в высококонтрастном режиме, установив булев флаг IsHighContrastEnabled в true. Вы можете создать экземпляр структуры PdfPageRenderOptions и передать ее в перегруженную версию метода RenderToStreamAsync класса PdfPage.

Клиентский код Простое приложение демонстрирует, насколько легко использовать эти API для рендеринга PDF-контента. Мое приложение-пример (PdfAPISample в сопутствующем исходном коде) содержит MainPage с двумя элементами управления Button, как показано на рис. 1.

*
Рис. 1. UI приложения-примера

Обработчики события щелчка для обеих кнопок будут предлагать пользователю выбрать PDF-документ и визуализировать первую страницу. Обработчик для кнопки Render PDF w/Options использует перегруженный метод RenderToStreamAsync и изменяет фоновый цвет страницы. Обработчик Button_Click приведен на рис. 2.

Рис. 2. Обработчик Button_Click, открывающий PDF-документ и выполняющий его рендеринг

void MainPage::Button_Click(Platform::Object^ sender,
  Windows::UI::Xaml::RoutedEventArgs^ args)
{
  m_streamVec->Clear();
  FileOpenPicker^ openPicker = ref new FileOpenPicker();
  openPicker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary;
  openPicker->ViewMode = PickerViewMode::List;
  openPicker->FileTypeFilter->Clear();
  openPicker->FileTypeFilter->Append(L".pdf");
  create_task(openPicker->PickSingleFileAsync())
  .then([this](StorageFile^ pdfFile)
  {
    m_ImagefileName = pdfFile->Name;
    create_task(PdfDocument::LoadFromFileAsync(pdfFile))
    .then([this](PdfDocument^ pdfDoc)
    {
      auto page = pdfDoc->GetPage(0);
      auto stream = ref new InMemoryRandomAccessStream();
      IAsyncAction^ action = page->RenderToStreamAsync(stream);
      auto actionTask = create_task(action);
      actionTask.then([this, stream, page]()
      {
        String^ img_name = m_ImagefileName + ".png";
        task<StorageFolder^> writeFolder(
          KnownFolders::PicturesLibrary->GetFolderAsync("Output"));
          writeFolder
          .then([this, img_name, stream, page](StorageFolder^ outputFolder)
          {
            task<StorageFile^> file(
            outputFolder->CreateFileAsync(img_name,
            CreationCollisionOption::ReplaceExisting));
        file.then([this, stream, page](StorageFile^ file1) {
          task<IRandomAccessStream^> writeStream(
            file1->OpenAsync(FileAccessMode::ReadWrite));
          writeStream.then(
          [this, stream, page](IRandomAccessStream^ fileStream) {
            IAsyncOperationWithProgress<unsigned long long
,             unsigned long long>^ progress
              = RandomAccessStream::CopyAndCloseAsync(
                stream->GetInputStreamAt(0),
                fileStream->GetOutputStreamAt(0));
                auto copyTask = create_task(progress);
                copyTask.then(
                   [this, stream, page, fileStream](
                   unsigned long long bytesWritten) {
                  stream->Seek(0);
                  auto bmp = ref new BitmapImage();
                  bmp->SetSource(fileStream);
                  auto page1 = ref new PdfPageAsImage();
                  page1->PdfPageImage = bmp;
                  m_streamVec->Append(page1);
                  pageView->ItemsSource = ImageCollection;
                  delete stream;
                  delete page;
                  });
                });
            });
          });
        });
    });
  });
}

Неуправляемые API для рендеринга PDF

WinRT API позволяют легко интегрировать PDF-контент в приложения Windows Store и доступны изо всех языков, поддерживаемых WinRT; при этом предполагается создание файла или потока изображения из каждой страницы в PDF-документе. Однако некоторые классы приложений Windows Store требуют осуществлять рендеринг PDF-контента прямо на экране. Такие приложения обычно пишутся на неуправляемом C++ и используют XAML или DirectX. Для этих приложений в Windows 8.1 также включены неуправляемые API, выполняющие рендеринг PDF-контента на DirectX-поверхности.

Неуправляемые API для рендеринга PDF присутствуют в заголовочном файле windows.data.pdf.interop.h и требуют компоновки со статической библиотекой windows.data.pdf.lib. Эти API доступны исключительно разработчикам на C++, желающим выполнять рендеринг PDF-контента непосредственно на экране.

Процесс рендеринга по умолчанию включает кодирование PdfPage как PNG-изображения с реальными размерами PDF-страницы в документе.

PdfCreateRenderer Эта функция — точка входа в неуправляемый API. Она принимает экземпляр IDXGIDevice и возвращает экземпляр интерфейса IPdfRendererNative. Входной параметр IDXGIDevice можно получить из XAML SurfaceImageSource, VirtualSurfaceImageSource или XAML SwapChainBackgroundPanel. Вероятно, вы знаете, что это interop-типы, поддерживаемые XAML для включения DirectX-контента в инфраструктуру XAML. Вызвать функцию PdfCreateRenderer несложно. Не забудьте включить в код файл windows.data.pdf.interop.h и связать его со статической библиотекой windows.data.pdf.lib. Получите экземпляр IDXGIDevice от нижележащего экземпляра D3DDevice. Передайте экземпляр IDXGIDevice функции PdfCreateRenderer. Если функция завершится успешно, она вернет экземпляр интерфейса IPdfRendererNative:

ComPtr<IDXGIDevice> dxgiDevice;
d3dDevice.As(&dxgiDevice)
ComPtr<IPdfRendererNative> pdfRenderer;
PdfCreateRenderer(dxgiDevice.Get(), &pdfRenderer)

Интерфейс IPdfRendererNative Это единственный интерфейс, поддерживаемый в неуправляемом API. Он содержит два вспомогательных метода: RenderPageToSurface и RenderPageToDeviceContext.

Метод RenderPageToSurface следует использовать при рендеринге PDF-контента либо в XAML SurfaceImageSource, либо в VirtualSurfaceImageSource. Метод RenderPageToSurface принимает PdfPage как входной параметр наряду с экземпляром DXGISurface, в котором будет рисоваться контент, смещением на устройстве, где будет происходить рисование, и необязательной структурой PDF_RENDER_PARAMS. Изучив метод RenderPageToSurface, вы, наверное, удивитесь, почему входной параметр PdfPage имеет тип IUnknown. Как получить PdfPage типа IUnknown? Оказывается, что в WinRT API, как только вы получаете допустимый экземпляр PdfPage из объекта PdfDocument, вы можете использовать safe_cast для приведения PdfPage к типу IUnknown.

Когда вы используете interop-тип SurfaceImageSource или VirtualSurfaceImageSource, вызов BeginDraw возвращает смещение на атласе (atlas), который инфраструктура XAML предоставляет вашему приложению для рисования контента. Вы должны передать это смещение в RenderPageToSurface, чтобы обеспечить рисование в правильной позиции.

Метод RenderPageToDeviceContext следует применять при рендеринге PDF-контента в XAML SwapChainBackgroundPanel. Он принимает PdfPage как входной параметр наряду с экземпляром ID2D1DeviceContext, в котором будет осуществляться рисование, и необязательную структуру PDF_RENDER_PARAMS.

Помимо рисования непосредственно на экране, рендеринг можно настраивать, используя структуру PDF_RENDER_PARAMS. Как RenderPageToSurface, так и RenderPageToDeviceContext принимают экземпляр этой структуры. Поля в ней похожи на таковые в WinRT-структуре PDFPageRenderOptions. Заметьте, что ни один неуправляемый API не является асинхронным. Рендеринг осуществляется на экране без издержек, связанных с кодированием и декодированием.

Если WinRT API кодируют PDF-страницу как изображение и записывают его в файл на диск, то неуправляемые API используют рендер на основе DirectX для рисования PDF-контента на экране.

И вновь простое приложение демонстрирует, как использовать эти API для рендеринга PDF-контента. Это приложение-пример (DxPdfApp в сопутствующем исходном коде) содержит MainPage с одной кнопкой (рис. 3).

*
Рис. 3. UI приложения-примера, использующего неуправляемые API

Код для открытия документа и обращения к PDF-странице остается одинаковым — что для WinRT API, что для неуправляемого API. Основное отличие в процессе рендеринга. Если WinRT API кодируют PDF-страницу как изображение и записывают его в файл на диск, то неуправляемые API используют рендер на основе DirectX для рисования PDF-контента на экране, как показано на рис. 4.

Рис. 4. Метод RenderPageRect рисует PDF-контент на экране

void PageImageSource::RenderPageRect(RECT rect)
{
  m_spRenderTask = m_spRenderTask.then([this, rect]() -> VSISData {
    VSISData vsisData;
    if (!is_task_cancellation_requested())
    {
      HRESULT hr = m_vsisNative->BeginDraw(
        rect,
        &(vsisData.dxgiSurface),
        &(vsisData.offset));
      if (SUCCEEDED(hr))
      {
        vsisData.fContinue = true;
      }
      else
      {
        vsisData.fContinue = false;
      }
    }
    else
    {
      cancel_current_task();
    }
    return vsisData;
  }, m_cts.get_token(), task_continuation_context::use_current())
  .then([this, rect](task<VSISData> beginDrawTask) -> VSISData {
    VSISData vsisData;
    try
    {
      vsisData = beginDrawTask.get();
      if ((m_pdfPage != nullptr) && vsisData.fContinue)
      {
        ComPtr<IPdfRendererNative> pdfRendererNative;
        m_renderer->GetPdfNativeRenderer(&pdfRendererNative);
        Windows::Foundation::Size pageSize = m_pdfPage->Size;
        float scale = min(static_cast<float>(
          m_width) / pageSize.Width,
          static_cast<float>(m_height) / pageSize.Height);
          IUnknown* pdfPageUnknown = (IUnknown*)
          reinterpret_cast<IUnknown*>(m_pdfPage);
          auto params = PdfRenderParams(D2D1::RectF((rect.left / scale),
            (rect.top / scale),
            (rect.right / scale),
            (rect.bottom / scale)),
            rect.right - rect.left,
            rect.bottom - rect.top,
            D2D1::ColorF(D2D1::ColorF::White),
            FALSE
            );
          pdfRendererNative->RenderPageToSurface(
            pdfPageUnknown,
            vsisData.dxgiSurface.Get(),
            vsisData.offset, &params);
      }
    }
    catch (task_canceled&)
    {
    }
    return vsisData;
  }, cancellation_token::none(), task_continuation_context::use_arbitrary())
  .then([this](task<VSISData> drawTask) {
    VSISData vsisData;
    try
    {
      vsisData = drawTask.get();
      if (vsisData.fContinue)
        m_vsisNative->EndDraw();
    }
    catch (task_canceled&)
    {
    }
  }, cancellation_token::none(), task_continuation_context::use_current());
}
}

Заключение

Я рассмотрел WinRT PDF API в Windows 8.1, который позволяет включать PDF-контент в приложения Windows Store. Кроме того, обсуждались различия между использованием WinRT API и C++/DirectX API, а также преимущества каждого подхода. Наконец, я дал ряд рекомендаций, когда следует применять тот или иной API. Подробнее о PDF API в Windows 8.1 см. документацию и демонстрационный пример средства просмотра PDF-файлов в MSDN по ссылке bit.ly/1bD72TO.

Автор: Сридхар Подури  •  Иcточник: MSDN Magazine  •  Опубликована: 14.04.2014
Нашли ошибку в тексте? Сообщите о ней автору: выделите мышкой и нажмите CTRL + ENTER
Теги:   Windows Store.


Оценить статью:
Вверх
Комментарии посетителей
Комментарии отключены. С вопросами по статьям обращайтесь в форум.