Dieser Artikel wurde maschinell übersetzt.

Windows 8.1

Wiedergabe von PDF-Inhalten in Windows Store-Apps

Sridhar Poduri

PDF-Datei als eine Dokumentenablage und Archivierung Format ist weit verbreitet in der Welt von heute.Dokumente wie Bücher, technische Handbücher, Bedienungsanleitungen und Berichte werden im PDF-Format gespeichert.Sie können ein Dokument aus mehreren Plattformen verwendet werden, solange ein unterstützter PDF-Viewer verfügbar ist.Während der Anzeige von PDF-Dokumenten weitgehend kein Thema ist, bleibt die unterstützen der Darstellung von PDF-Inhalten eine Herausforderung, vor allem für Windows-Speicher-app-Entwickler.Mit Windows-8.1 führte Microsoft neue APIs, die den Prozess zur Darstellung von PDF-Inhalten in Windows Store apps vereinfachen.

In diesem Artikel werde ich die verschiedenen Möglichkeiten dazu diese Darstellung ansehen.Zunächst konzentriere ich mich auf die APIs, die Teil der Windows-Runtime (WinRT) und sind für Sie per JavaScript, c#, .NET Visual Basic und C++ verfügbar.Dann konzentriere ich mich auf die systemeigenen APIs, die C++-Entwickler PDF-Inhalte direkt auf einer DirectX-basierte Zeichenoberfläche Rendern lassen.

Die Windows-Runtime-APIs für PDF-Erstellung

Die Windows-Runtime für Windows 8.1 enthält einen neuen Namespace, Windows.Data.Pdf, enthält die neue Runtime-Klassen und Strukturen, die PDF-Erstellung in Windows Store apps unterstützen.In diesem Abschnitt werde ich erläutern die verschiedenen Klassen, aus denen sich der Windows.Data.Pdf-Namespace, verwendet zum Öffnen von PDF-Dokumenten, Umgang mit Kennwortschutz, Rendern von Dokumenten, Anpassen der Rendering-Prozess und vieles mehr.

Öffnen von PDF-Dokumenten Öffnen eines PDF-Dokuments programmgesteuert ist so einfach wie das Aufrufen der statischen Methode LoadFromFileAsync aus der PdfDocument-Runtime-Klasse.Diese Klasse ist der erste Einstiegspunkt für die Arbeit mit PDF-Dokumenten.Die LoadFromFileAsync-Methode akzeptiert ein StorageFile-Objekt und startet den Prozess der PdfDocument zu laden.Laden eines PDF-Dokuments kann manchmal eine lange dauern, sodass die API einen asynchronen Vorgang zurückgibt.Wenn der asynchrone Vorgang abgeschlossen wurde, müssen Sie eine gültige Instanz des PdfDocument-Objekts, wie hier gezeigt:

// Obtain a StorageFile object by prompting the user to select a .pdf file
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.List;
openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
openPicker.FileTypeFilter.Add(".pdf");
StorageFile pdfFile = await openPicker.PickSingleFileAsync();
// Load a PdfDocument from the selected file
create_task(PdfDocument::LoadFromFileAsync(pdfFile)).then(
  [this](PdfDocument^ pdfDoc)
{
  // Handle opened Pdf document here.
});

Neben der LoadFromFileAsync-Methode enthält die PdfDocument-Klasse auch eine statische Hilfsmethode um eine PdfDocument-Instanz aus einem Streamobjekt zu erstellen. Falls Sie bereits einen Verweis auf ein PDF-Dokument als eine RandomAccessStream-Instanz besitzen, können Sie einfach das Stream-Objekt an die LoadFromStreamAsync-Methode übergeben. Je nach Szenario können Sie entweder die LoadFromFileAsync oder die LoadFromStreamAsync-Methode verwenden, um eine Instanz des PdfDocument-Objekts zu erstellen. Sobald Sie eine gültige PdfDocument-Instanz haben, können Sie einzelne Seiten im Dokument zugreifen.

Umgang mit Password-Protected PDF-Dokumente PDF-Doku­Ments werden verwendet, um eine Vielzahl von Informationen, z. B. Kredit - speichern­Karte, Aussagen oder anderen vertraulichen Daten. Einige Verlage sollen nicht Benutzer haben uneingeschränkten Zugriff auf diese Arten von Dokumenten und sie mit Passwörtern schützen. Zugriff ist nur für Anwendungen gewährt deren Binärdateien das Kennwort enthalten. Die LoadFromFileAsync und LoadFromStreamAsync Methoden der Klasse PdfDocument Runtime enthalten überladene Versionen der Methoden, die ein Kennwort über einen String-Parameter akzeptieren:

// Load a PdfDocument that's protected by a password
// Load a PdfDocument from the selected file
create_task(PdfDocument::LoadFromFileAsync(
  pdfFile, "password")).then([this](PdfDocument^ pdfDoc){
  Handle opened Pdf document here.
});

Wenn Sie versuchen, ein Passwort-geschütztes Dokument Laden ohne Angabe eines Passworts, löst die Methoden LoadFromFileAsync und LoadFromStreamAsync eine Ausnahme.

Zugriff auf die Seiten in einem PDF-Dokument nach dem Erstellen einer Instanz eines PdfDocument-Objekts wird die Count-Eigenschaft die Anzahl der Seiten im PDF-Dokument zurück. Sie können einfach durchlaufen und von 0 auf den "Count-1" Bereich erhalten Sie Zugriff auf einzelne PDF-Seiten. Jede Seite ist vom Typ PdfPage-Runtime-Klasse. Die PdfPage-Runtime-Klasse verfügt über eine Methode namens PreparePageAsync, die den Prozess der Vorbereitung einer PDF-Seite für die Wiedergabe beginnt. Seite Vorbereitung umfasst die Analyse und beim Laden der Seite, initialisieren Direct2D-Ressourcen für die korrekte Handhabung von Grafik Pfade und Formen, initialisieren DirectWrite für den Umgang mit des richtigen Satz von Schriftarten zum Rendern von Text und so weiter. Wenn Sie nicht PreparePageAsync aufrufen, bevor Sie zum Rendern von PDF-Seiten beginnen, ruft der Render-Prozess PreparePageAsync implizit für Sie. Jedoch sollten Sie rufen Sie PreparePageAsync und haben die Seiten fertig für das Rendering anstatt lassen den Renderingprozess Seite vorbereiten. Vorbereitung der Seite vor das eigentliche Rendern spart Zeit in der eigentlichen Rendering-Prozess und ist eine schöne Optimierungstechnik.

Rendern von PDF-Seiten sobald die PdfPage-Objekte vorbereitet sind, können sie dargestellt werden. Die Rendering-API die PdfPage als Bild codiert und schreibt die Bilddaten in einen Stream vom Entwickler geliefert. Der Stream kann dann entweder als Quelle für ein Image-Steuerelement in der Benutzeroberfläche der Anwendung festlegen oder schreiben die Daten auf der Festplatte für die Verwendung später verwendet werden.

Das Rendering geschieht, wenn der Aufruf der RenderToStreamAsync-Methode der Klasse Runtime PdfPage. Die RenderToStreamAsync-Methode akzeptiert eine Instanz eines IRandomAccessStream oder einer seiner abgeleiteten Typen und schreibt die codierten Daten in den Stream.

Anpassen der Seitenrendering der Standard-Rendering-Prozess beinhaltet Codierung der PdfPage als PNG-Bild über die tatsächlichen Dimension der PDF-Seite im Dokument. Sie können ändern die Standardcodierung von PNG, BMP oder JPEG, obwohl ich dringend empfohlen, dass Sie PNG-Kodierung für die Wiedergabe auf dem Bildschirm Inhalt, weil es verlustfrei ist und auch nicht für große Bitmaps verwenden. Jedoch, wenn Sie Bilder aus der PDF-Seiten zu generieren und sie später für den Zugriff auf die Festplatte speichern möchten, sollten Sie verwenden JPEG-Kodierung weil sie kleinere Bilddateien mit einer annehmbaren Auflösung generiert. Sie können auch angeben, die SourceRect und DestinationWidth, um nur einen Teil einer PDF-Seite zu rendern – z. B. in Reaktion auf eine Zoom-in-Vorgang. Sie können auch zum Rendern in kontrastreichen Modus überprüfen, indem mithilfe der boolesches Flag IsHighContrastEnabled und dieses Flag auf True festlegen. Sie können erstellen Sie eine Instanz der PdfPageRenderOptions-Struktur und übergeben sie an die überladene Version der RenderToStreamAsync-Methode der Klasse PdfPage.

Der Client-Code eine einfache app zeigt, wie einfach es ist, die diese APIs verwenden, um PDF Inhalt gerendert. Mein Beispiel-app (PdfAPISample im Codedownload) enthält eine Hauptseite mit zwei Button-Steuerelemente, wie im Abbildung 1.

The PDF App UI
Abbildung 1 der PDF-App-Benutzeroberfläche

Die Click-Ereignishandler für beide Schaltflächen fordert den Benutzer auf ein PDF-Dokument auswählen und die erste Seite zu rendern. Der Ereignishandler für die Schaltfläche "Rendern PDF w/Optionen" wird verwenden Sie die überladene Methode der RenderToStreamAsync und die Hintergrundfarbe der Seite ändern.

Der Button_Click-Ereignis-Handler notiert Abbildung 2.

Abbildung 2 der Button_Click-Ereignishandler zu öffnen und zu rendern ein PDF-Dokuments

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;
                  });
                });
            });
          });
        });
    });
  });
}

Die systemeigenen APIs für PDF-Erstellung

Die WinRT APIs erlauben eine einfache Integration von PDF-Inhalten in Windows Store apps und sollen alle WinRT unterstützten Sprachen über eine Bild-Datei oder einen Stream aus einzelnen Seiten in einem PDF-Dokument erstellen. Bestimmte Klassen von Windows Store apps haben jedoch eine Voraussetzung, um PDF-Inhalte direkt auf dem Bildschirm darzustellen. Solche apps sind in der Regel mit systemeigenem C++ geschrieben und XAML oder DirectX zu nutzen. Für diese Anwendungen auch Windows 8.1 systemeigenen APIs zum Rendern von PDF-Inhalt auf einer Fläche von DirectX.

Die systemeigenen APIs für PDF-Erstellung sind in den Sieg­dows.data.pdf.interop.h Header Datei und erfordern die Verknüpfung mit der statischen windows.data.pdf.lib-Bibliothek. Diese APIs sind exklusiv für C++-Entwickler, die PDF-Inhalte direkt auf dem Bildschirm gerendert werden sollen.

PdfCreateRenderer die PdfCreateRenderer-Funktion ist der Einstiegspunkt für die native API. Es akzeptiert eine Instanz eines IDXGIDevice als Eingabe und gibt eine Instanz einer IPdfRendererNative-Schnittstelle. Der IDXGIDevice input-Parameter kann entweder ein XAML-SurfaceImageSource, ein VirtualSurfaceImageSource oder ein XAML-SwapChainBackgroundPanel abgerufen werden. Sie wissen, dass dies die Interop-Typen sind, die XAML unterstützt für das Mischen von DirectX-Inhalt in einem XAML-Rahmen. Aufrufen der PdfCreateRenderer-Funktion ist einfach. Stellen Sie sicher, windows.data.pdf.interop.h und Link gegen die statische windows.data.pdf.lib-Bibliothek enthalten. Erhalten Sie eine Instanz eines IDXGIDevice aus der zugrunde liegenden D3DDevice-Instanz. Übergeben Sie die IDXGIDevice-Instanz an die PdfCreateRenderer-Funktion. Wenn die Funktion erfolgreich ist, gibt es eine gültige Instanz einer IPdfRendererNative-Schnittstelle:

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

Die IPdfRendererNative-Schnittstelle die IPdfRendererNative-Schnittstelle ist nur in der nativen API unterstützt. Die Schnittstelle enthält zwei Hilfsmethoden: RenderPageToSurface und RenderPageToDeviceContext.

RenderPageToSurface ist die richtige Methode beim Wiedergeben von PDF-Inhalten für eine XAML-SurfaceImageSource oder VirtualSurfaceImageSource verwendet. Die RenderPageToSurface-Methode nimmt eine PdfPage als Eingabeparameter zusammen mit einer Instanz von DXGISurface zu dem gezeichnet werden soll der Inhalt, ein Offset auf das Gerät ziehen und eine optionale PDF_RENDER_PARAMS-Struktur. Wenn Sie die RenderPageToSurface-Methode untersuchen, könnten Sie überrascht zu sehen, die PdfPage Eingang ist vom Typ IUnknown sein. Wie bekommt man ein PdfPage vom Typ IUnknown? Es stellt sich heraus, dass mit der WinRT-API, sobald Sie eine gültige PdfPage-Instanz aus einem PdfDocument-Objekt, erhalten Sie Safe_cast verwenden, um eine PdfPage IUnknown umgewandelt.

Bei Verwendung der SurfaceImageSource oder VirtualSurface­ImageSource Interop-Typen, Aufruf BeginDraw gibt einen Offset in der Atlas, dass das XAML-Framework Ihre Anwendung stellt, um Inhalt zu ziehen. Sie sollten diesem Offset übergeben, um RenderPageToSurface zu gewährleisten, dass die Zeichnung an der richtigen Position passiert.

Die RenderPageToDeviceContext ist die richtige Methode beim Wiedergeben von PDF-Inhalten für eine XAML-SwapChainBackgroundPanel verwendet. Die RenderPageToDeviceContext dauert eine PdfPage als Eingabeparameter sowie eine ID2D1DeviceContext-Instanz an, um den Inhalt und eine optionale PDF_RENDER_PARAMS-Struktur zu zeichnen.

Neben der Zeichnung direkt auf dem Bildschirm, kann die Wiedergabe auch angepasst werden, mithilfe der PDF_RENDER_PARAMS-Struktur. Der RenderPageToSurface und RenderPageToDeviceContext nicht nur eine Instanz von PDF_RENDER_PARAMS. Die Optionen im PDF_RENDER_PARAMS sind ähnlich wie die WinRT PDFPageRenderOptions-Struktur. Beachten Sie, dass weder der systemeigenen APIs asynchrone Methoden. Das Rendering findet direkt auf dem Bildschirm, ohne dass die Kosten für die Codierung und Decodierung.

Wieder veranschaulicht eine einfache app diese APIs verwenden, um PDF-Inhalte zu rendern. Diese Beispielanwendung (DxPdfApp im Codedownload) enthält eine Hauptseite mit einem Button-Steuerelement, wie in Abbildung 3.

Native PDF API App UI
Abbildung 3 Native PDF API App UI

Der Code zum Öffnen eines Dokuments und Zugriff auf eine PDF-Seite bleibt gleich zwischen der WinRT-API und die native API. Die wesentliche Änderung ist in der Rendering-Prozess. Während die WinRT APIs codieren die PDF-Seite als Bild und schreiben Sie der Image-Datei auf der Festplatte, die systemeigenen APIs verwenden einen DirectX-basierte Renderer des PDF-Inhalts auf dem Bildschirm, wie in dargestellt gezeichnet Abbildung 4.

Abbildung 4 die RenderPageRect-Methode zeichnen PDF-Inhalte auf dem Bildschirm

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());
}
}

Weitere Informationen

Ich besprach die WinRT PDF-API in Windows-8.1, mit dem Sie PDF-Inhalte in Windows Store apps zu integrieren. Ich diskutierte auch die Unterschiede zwischen der Verwendung der WinRT-API oder die C + + / DirectX-API und die Vorteile der einzelnen Konzepte. Schließlich stellte ich einen Satz von Empfehlungen für die API im Rahmen bestimmter Szenarien verwendet werden soll. Für weitere Informationen zu den PDF-APIs in Windows 8.1, lesen Sie die Dokumentation und PDF-Viewer-Schaufenster-Beispiel auf MSDN unter bit.ly/1bD72TO.

 

Sridhar Poduri ist Programmmanager bei Microsoft. Er ist ein begeisterter Anhänger von C++ und Autor des Buchs „Modern C++ and Windows Store Apps“ (Sridhar Poduri, 2013). Seinen Blog zu C++ und Windows-Runtime, den er regelmäßig schreibt, finden Sie unter sridharpoduri.com.

Unser Dank gilt dem folgenden technischen Experten für die Durchsicht dieses Artikels: Subramanian Iyer (Microsoft)
Subramanian Iyer war ist ein Entwickler im Windows-Team bei Microsoft und über die letzten drei Versionen mit Windows-Entwicklung beteiligt. Er hat schon ein Teil das Reader-Team, das eine der erste C + + / XAML-apps für Windows 8. Ein Programmierer und ein neues übergeordnetes Element, hatte er einige Zeit gefunden, ein paar apps auf dem Windows-Lager unter dem Namen LSubs veröffentlichen.