Condividi tramite


Il presente articolo è stato tradotto automaticamente.

Windows 8.1

Rendering di contenuto PDF nelle applicazioni Windows Store

Sridhar Poduri

Download del codice di esempio associato

PDF come un deposito di documento e il formato di archiviazione è affermata nel mondo di oggi.Documenti come libri, manuali tecnici, guide utente, rapporti e più sono memorizzati in formato PDF.Permette di essere consumati da più piattaforme come un visualizzatore di PDF supportato è disponibile un documento.Mentre la visualizzazione di documenti PDF è in gran parte un nonissue, sostenendo il rendering del contenuto PDF rimane una sfida, soprattutto per gli sviluppatori di app di Windows Store.Con 8.1 di Windows, Microsoft ha introdotto nuove API che facilitare il processo di rendering contenuti PDF in applicazioni Windows Store.

In questo articolo, potrai guardare i diversi modi per fare questo rendering.In primo luogo, mi concentrerò sulle API che fanno parte di Windows Runtime (WinRT) e sono accessibili a voi tramite JavaScript, c#, .NET Visual Basic e C++.Poi mi concentrerò sulle API native che consentono agli sviluppatori C++ di rendering PDF contenuto direttamente su una superficie di disegno basati su DirectX.

Le API di Windows Runtime per il Rendering PDF

Il Windows Runtime per Windows 8.1 include un nuovo spazio dei nomi, Windows.Data.Pdf, che contiene le nuove classi di runtime e le strutture che supportano il rendering PDF nelle applicazioni Windows Store.In questa sezione tratterò le varie classi che compongono lo spazio dei nomi Windows.Data.Pdf, utilizzata per aprire i documenti PDF, gestione delle password di protezione, rendendo i documenti, personalizzare il processo di rendering e di più.

Apertura dei documenti PDF apertura a livello di codice un documento PDF è facile come chiamare il metodo statico LoadFromFileAsync dalla classe di runtime PdfDocument.Questa classe è il punto di ingresso iniziale per lavorare con documenti PDF.Il metodo LoadFromFileAsync accetta un oggetto StorageFile e inizia il processo di caricamento il PdfDocument.Caricamento di un documento PDF a volte può richiedere molto tempo, quindi l'API restituisce un'operazione asincrona.Al termine dell'operazione asincrona, hai un'istanza valida dell'oggetto PdfDocument, come illustrato di seguito:

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

Oltre al metodo di LoadFromFileAsync, la classe PdfDocument contiene anche un metodo di supporto statico per creare un'istanza di PdfDocument da un oggetto stream. Se sei già in possesso un riferimento a un documento PDF come un'istanza di RandomAccessStream, è possibile passare semplicemente l'oggetto stream al metodo LoadFromStreamAsync. A seconda dello scenario, è possibile utilizzare i metodi LoadFromFileAsync o LoadFromStreamAsync per creare un'istanza dell'oggetto PdfDocument. Una volta che avete un'istanza valida di PdfDocument, è possibile accedere singole pagine del documento.

Gestione dei documenti PDF protetto da password PDF docu­menti vengono utilizzati per memorizzare un'ampia varietà di informazioni, quali il credito -­scheda dichiarazioni o altri dati riservati. Alcuni editori non desidera che gli utenti hanno accesso illimitato a questi tipi di documenti e proteggerli con password. L'accesso è concesso solo alle applicazioni in cui i binari contengono la password. I metodi LoadFromFileAsync e LoadFromStreamAsync della classe di runtime PdfDocument contengono versioni di overload di metodi che accettano una password tramite un parametro di stringa:

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

Se si tenta di caricare un documento protetto da password senza specificare una password, i metodi LoadFromFileAsync e LoadFromStreamAsync genererà un'eccezione.

L'accesso a pagine in un documento PDF dopo si crea un'istanza di un oggetto PdfDocument, la proprietà Count restituisce il numero di pagine del documento PDF. Può semplicemente scorrere da 0 al "Conte – 1" gamma per accedere alle singole pagine PDF. Ogni pagina è di tipo classe di runtime PdfPage. La classe di runtime PdfPage ha un metodo denominato PreparePageAsync che inizia il processo di preparazione di una pagina PDF per il rendering. Preparazione della pagina comporta l'analisi e il caricamento della pagina, l'inizializzazione di Direct2D risorse per la corretta gestione di percorsi grafici e forme, inizializzando DirectWrite per gestire il corretto set di font per il rendering di testo e così via. Se non chiami PreparePageAsync prima di iniziare a eseguire il rendering di pagine PDF, il processo di rendering chiama PreparePageAsync implicitamente. Tuttavia, si dovrebbe chiamare PreparePageAsync e sono pronti per il rendering di pagine piuttosto che lasciare che il processo di rendering a preparare la pagina. Preparare la pagina davanti il rendering effettivo consente di risparmiare tempo nel processo di rendering effettivo ed è una tecnica di ottimizzazione bello.

Rendendo le pagine PDF una volta che gli oggetti PdfPage sono preparati, possono essere resi. L'API di Rendering codifica il PdfPage come immagine e scrive i dati di immagine in un flusso fornito dallo sviluppatore. Il flusso può essere impostato come origine per un controllo Image nell'interfaccia utente dell'applicazione o essere utilizzato per scrivere i dati su disco da utilizzare successivamente.

Il rendering avviene dopo aver chiamato il metodo RenderToStreamAsync della classe di runtime PdfPage. Il metodo RenderToStreamAsync accetta un'istanza di un IRandomAccessStream o uno dei relativi tipi derivati e scrive i dati codificati nel flusso.

Personalizzazione del Rendering della pagina il processo di rendering predefinito comporta codifica il PdfPage come immagine PNG presso le reali dimensioni della pagina del documento PDF. È possibile cambiare la codifica predefinita da PNG BMP o JPEG, anche se fortemente consigliabile utilizzare PNG codifica per il rendering di contenuto sullo schermo perché è senza perdita e inoltre non genera grandi bitmap. Tuttavia, se si desidera generare le immagini dalle pagine PDF e memorizzarli su disco per accedere successivamente, è necessario utilizzare JPEG codifica perché genera file di immagine più piccoli con una risoluzione accettabile. È inoltre possibile specificare il SourceRect e DestinationWidth per eseguire il rendering solo una porzione di una pagina PDF — ad esempio, in risposta a un'operazione di zoom. È inoltre possibile controllare per il rendering in modalità contrasto elevato utilizzando il flag booleano IsHighContrastEnabled e impostare questo flag su true. Si può creare un'istanza della struttura PdfPageRenderOptions e passarlo per la versione di overload del metodo RenderToStreamAsync della classe PdfPage.

Il codice Client una semplice app dimostra come è facile utilizzare queste API per il rendering PDF contenuto. Mio esempio app (PdfAPISample nel download del codice) contiene un MainPage con due controlli Button, come mostrato Figura 1.

The PDF App UI
Figura 1 l'interfaccia utente App PDF

I gestori di evento click per entrambi i pulsanti richiederà all'utente di selezionare un documento PDF e rendere la prima pagina. Il gestore eventi del pulsante "PDF w/opzioni di rendering" sarà utilizzare il metodo di overload RenderToStreamAsync e cambiare il colore di sfondo della pagina.

Il gestore eventi Button_Click è elencato Figura 2.

Figura 2 il gestore eventi Button_Click per aprire e rendere un documento 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;
                  });
                });
            });
          });
        });
    });
  });
}

Le API Native per il Rendering PDF

L'APIs WinRT permettono la facile integrazione di contenuti PDF in applicazioni Windows Store e sono pensati per essere accessibile da tutti i linguaggi supportati WinRT creando un flusso o un file immagine da ogni pagina in un documento PDF. Tuttavia, alcune classi di applicazioni Windows Store hanno un requisito per il rendering PDF contenuto direttamente sullo schermo. Tali applicazioni sono solitamente scritti usando C++ nativo e utilizzano XAML o DirectX. Per queste applicazioni, Windows 8.1 include anche le API native per contenuto PDF rendering su una superficie di DirectX.

Le API native per il rendering PDF sono presenti nella vittoria­dows.data.pdf.interop.h intestazione del file e richiedono il collegamento con la libreria statica windows.data.pdf.lib. Queste API sono disponibili esclusivamente per gli sviluppatori in C++ che si desidera eseguire il rendering di contenuto PDF direttamente sullo schermo.

PdfCreateRenderer PdfCreateRenderer la funzione è il punto di ingresso per l'API nativa. Accetta un'istanza di un IDXGIDevice come input e restituisce un'istanza dell'interfaccia di IPdfRendererNative. Il parametro di input IDXGIDevice può essere ottenuto da un SurfaceImageSource di XAML, un VirtualSurfaceImageSource o un SwapChainBackgroundPanel di XAML. Si potrebbe sapere che questi sono i tipi di interoperabilità che XAML supporta per miscelazione DirectX contenuti in un quadro XAML. Chiamare la funzione PdfCreateRenderer è facile. Assicurarsi di includere windows.data.pdf.interop.h e link contro la libreria statica windows.data.pdf.lib. Ottenere un'istanza di un IDXGIDevice dall'istanza sottostante D3DDevice. Passare l'istanza IDXGIDevice alla funzione PdfCreateRenderer. Se la funzione ha esito positivo, restituisce un'istanza valida di un'interfaccia IPdfRendererNative:

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

L'interfaccia IPdfRendererNative The IPdfRendererNative è l'interfaccia unica supportata nell'API nativa. L'interfaccia contiene due metodi di supporto: RenderPageToSurface e RenderPageToDeviceContext.

RenderPageToSurface è il metodo corretto di utilizzare il rendering del contenuto PDF per XAML SurfaceImageSource o VirtualSurfaceImageSource. Il metodo RenderPageToSurface accetta un PdfPage come parametro di input insieme a un'istanza di DXGISurface per cui disegnare il contenuto, un offset sul dispositivo per disegnare e una struttura PDF_RENDER_PARAMS opzionale. Come si esamina il metodo RenderPageToSurface, potreste essere sorpresi di vedere l'ingresso di PdfPage essere di tipo IUnknown. Come si ottiene un PdfPage di tipo IUnknown? Si scopre che con l'API WinRT, una volta che ottenete un'istanza valida di PdfPage da un oggetto PdfDocument, è possibile utilizzare safe_cast per lanciare un PdfPage a IUnknown.

Quando si utilizza SurfaceImageSource o VirtualSurface­tipi di interoperabilità ImageSource, chiamando BeginDraw restituisce un offset in Atlante che il framework XAML fornisce l'applicazione per disegnare il contenuto. È necessario passare tale offset al RenderPageToSurface per garantire che il disegno accade nella posizione corretta.

Il RenderPageToDeviceContext è il metodo corretto di utilizzare il rendering del contenuto PDF a un SwapChainBackgroundPanel di XAML. Il RenderPageToDeviceContext prende un PdfPage come parametro di input insieme a un'istanza di ID2D1DeviceContext per cui disegnare il contenuto e la struttura PDF_RENDER_PARAMS opzionale.

Oltre a disegnare direttamente sullo schermo, il rendering può essere personalizzato anche utilizzando la struttura PDF_RENDER_PARAMS. Il RenderPageToSurface e il RenderPageToDeviceContext accettare un'istanza di PDF_RENDER_PARAMS. Le opzioni fornite in PDF_RENDER_PARAMS sono simili alla struttura WinRT PDFPageRenderOptions. Si noti che né le API native sono asincroni metodi. Il rendering avviene direttamente sullo schermo senza dover sostenere il costo di codifica e decodifica.

Ancora una volta, una semplice app viene illustrato come utilizzare queste API per il rendering PDF contenuto. Questa applicazione di esempio (DxPdfApp nel download del codice) contiene un MainPage con controllo di un pulsante, come illustrato nel Figura 3.

Native PDF API App UI
Figura 3 nativo PDF API App UI

Il codice per aprire un documento e accedere a una pagina PDF rimane lo stesso tra l'API WinRT e l'API nativa. Il cambiamento principale è nel processo di rendering. Mentre l'APIs WinRT codificare la pagina PDF come immagine e scrivere il file immagine su disco, le API native utilizzano un renderer basato su DirectX per disegnare il contenuto PDF sullo schermo, come mostrato Figura 4.

Figura 4 il metodo RenderPageRect disegno PDF contenuto sullo schermo

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

Ulteriori informazioni

Ho parlato di PDF API WinRT 8.1 di Windows che consente di incorporare contenuti PDF in applicazioni Windows Store. Discusso anche le differenze tra l'utilizzo dell'API WinRT o C + + / DirectX API e i vantaggi di ciascun approccio. Infine, fornito una serie di raccomandazioni che API dovrebbero essere utilizzate in determinate circostanze. Per ulteriori informazioni sulle API Windows 8.1 PDF, controlla la documentazione e il campione di vetrina visualizzatore PDF su MSDN al bit.ly/1bD72TO.

 

Sridhar Poduri è un program manager presso Microsoft. A C++ aficionado and author of the book, “Modern C++ and Windows Store Apps” (Sridhar Poduri, 2013), he blogs regularly about C++ and the Windows Runtime atsridharpoduri.com

Un ringraziamento al seguente esperto tecnico per la revisione dell'articolo:: Subramanian Iyer (Microsoft)
Subramanian Iyer è uno sviluppatore del team Windows in Microsoft ed è stato coinvolto con lo sviluppo di Windows sopra le ultime tre uscite. Egli ha fatto parte della squadra di lettore, che si è sviluppato uno dei primi C + + / apps XAML per Windows 8. Un programmatore e un nuovo genitore, aveva trovato qualche tempo a pubblicare un paio di applicazioni su Windows Store, sotto il nome di LSubs.