Este artículo proviene de un motor de traducción automática.

Windows 8.1

Representación de contenidos de PDF en las aplicaciones de la Tienda Windows

Sridhar Poduri

Descargar el código de ejemplo

PDF como formato de archivo y un almacenamiento de documento está bien establecido en el mundo de hoy.Documentos tales como libros, manuales técnicos, guías de usuario, reportes y más se almacenan en formato PDF.Un documento permite ser consumida desde múltiples plataformas tanto como un visor de PDF compatible está disponible.Visualización de documentos PDF es en gran medida un nonissue, apoyar el procesamiento de contenido PDF sigue siendo un desafío, especialmente para los desarrolladores de aplicaciones Windows Store.Con 8.1 de Windows, Microsoft introdujo nuevas APIs que facilitan el proceso de rendering contenido PDF en aplicaciones Windows Store.

En este artículo, miraré en las diferentes maneras de hacer esta representación.En primer lugar, a enfocar las API que forman parte de la ejecución de Windows (WinRT) y son accesibles a usted a través de JavaScript, C#, .NET Visual Basic y C++.Entonces podrá enfocar las API nativas que permiten a los programadores de C++ representar contenido PDF directamente sobre una superficie de dibujo basado en DirectX.

Las API de Windows Runtime para la representación de PDF

El tiempo de ejecución de Windows para Windows 8.1 incluye un nuevo espacio de nombres, Windows.Data.Pdf, que contiene las nuevas clases de tiempo de ejecución y las estructuras que admiten la representación de PDF en aplicaciones Windows Store.En esta sección, analizaré las distintas clases que conforman el espacio de nombres Windows.Data.Pdf, utilizado para abrir documentos PDF, manejo de protección por contraseña, procesamiento de documentos, personalizar el proceso de renderizado y más.

Abrir documentos PDF abrir un documento PDF mediante programación es tan fácil como llamar al método estático LoadFromFileAsync de la clase de tiempo de ejecución PdfDocument.Esta clase es el punto de entrada inicial para trabajar con documentos PDF.El método LoadFromFileAsync acepta un objeto StorageFile y comienza el proceso de carga de la PdfDocument.Cargar un documento PDF a veces puede tomar mucho tiempo, lo que la API devuelve una operación asincrónica.Cuando haya finalizado la operación asincrónica, tienes una instancia válida del objeto PdfDocument, como se muestra aquí:

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

Además el método LoadFromFileAsync, la clase PdfDocument contiene un método estático de ayudante para crear una instancia de PdfDocument de un objeto de secuencia. Si ya tienes una referencia a un documento PDF como una instancia de RandomAccessStream, simplemente puede pasar al método LoadFromStreamAsync del objeto stream. Dependiendo de su situación, puede utilizar los métodos LoadFromFileAsync o LoadFromStreamAsync para crear una instancia del objeto PdfDocument. Una vez que tienes una instancia válida de PdfDocument, puede acceder a las páginas individuales en el documento.

Manejo de documentos PDF Password-Protected PDF docu­ments se utilizan para almacenar una amplia variedad de información, tales como crédito -­tarjeta de declaraciones u otros datos confidenciales. Algunos editores no desea que los usuarios han acceso sin restricciones a estos tipos de documentos y protegerlos con contraseñas. Se concede el acceso sólo a las aplicaciones cuyos binarios contienen la contraseña. Los métodos de la clase runtime PdfDocument LoadFromFileAsync y LoadFromStreamAsync contienen versiones sobrecargadas de los métodos que acepten una contraseña a través de un parámetro de cadena:

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

Si intenta cargar un documento protegido con contraseña sin especificar una contraseña, los métodos LoadFromFileAsync y LoadFromStreamAsync generará una excepción.

Acceder a las páginas de un documento PDF después de que se crea una instancia de un objeto PdfDocument, la propiedad Count devolverá el número de páginas del documento PDF. Usted simplemente puede iterar de 0 a la "cuenta – 1" van a tener acceso a las páginas PDF individuales. Cada página es de tipo clase runtime pdf. La clase runtime pdf tiene un método denominado PreparePageAsync que se inicia el proceso de preparación de una página PDF para la representación. Preparación de página implica análisis sintáctico y la carga de la página, inicializando Direct2D recursos para manejar apropiadamente los trazados gráficos y formas, inicializando DirectWrite para manejar el conjunto correcto de fuentes para la representación de texto y así sucesivamente. Si no me llamas PreparePageAsync antes de comenzar a procesar páginas en PDF, el proceso de renderizado llama PreparePageAsync para ti implícitamente. Sin embargo, usted debe llamar PreparePageAsync y tener listo para representar las páginas más que dejar que el proceso de renderizado a preparar la página. Preparar la página antes de la representación real ahorra tiempo en el proceso de representación real y es una técnica de optimización agradable.

Representación de las páginas PDF una vez que se preparan los objetos PDF, pueden ser prestados. La API de procesamiento codifica el PDF como una imagen y escribe los datos de imagen en una secuencia proporcionada por el desarrollador. La corriente puede ser fijado como la fuente de un control de imagen en la aplicación de interfaz de usuario o utilizarse para escribir los datos en disco para su uso más adelante.

La representación ocurre cuando se llama al método RenderToStreamAsync de la clase runtime pdf. El método RenderToStreamAsync acepta una instancia de un IRandomAccessStream o uno de sus tipos derivados y escribe los datos codificados en la secuencia.

Personalización de la representación de la página el proceso de representación por defecto consiste en codificar el PDF como una imagen PNG en las dimensiones reales de la página en el documento PDF. Puede cambiar la codificación predeterminada del PNG BMP o JPEG, aunque muy recomendable utilizar PNG de codificación para representar contenido en la pantalla porque es sin pérdidas y también no genera grandes mapas de bits. Sin embargo, si usted quiere generar imágenes desde las páginas PDF y almacenarlos en el disco para acceder más adelante, debe considerar el uso de codificación JPEG porque genera más pequeños archivos de imagen con una resolución aceptable. También puede especificar la SourceRect y DestinationWidth para representar solamente una porción de una página PDF — por ejemplo, en respuesta a una operación de zoom. También puede consultar para la representación en el modo de alto contraste utilizando la bandera Boolean IsHighContrastEnabled y establecer este parámetro en true. Puede crear una instancia de la estructura PdfPageRenderOptions y pasarlo a la versión de sobrecarga del método RenderToStreamAsync de la clase de PDF.

El código de cliente una aplicación simple demuestra lo fácil que es usar estas API para representar contenido PDF. Mi aplicación de ejemplo (PdfAPISample en la descarga de código adjunto) contiene una página principal con dos controles de botón, como se muestra en la figura 1.

The PDF App UI
Figura 1 la interfaz de usuario de la aplicación PDF

Los controladores de eventos de clic para ambos botones le pedirá al usuario que seleccione un documento PDF y representar la primera página. El controlador de eventos para el botón "Render w/opciones de PDF" utilizaremos el método sobrecargado RenderToStreamAsync y cambiar el color de fondo de página.

El controlador de eventos Button_Click cotiza en figura 2.

Figura 2 el controlador de eventos Button_Click para abrir y procesar 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;
                  });
                });
            });
          });
        });
    });
  });
}

Las API nativas para la representación de PDF

Las APIs de WinRT permite la fácil integración de contenido PDF en aplicaciones Windows Store y deben ser accesibles desde todos los idiomas admitidos por WinRT mediante la creación de un archivo de imagen o secuencia de cada página de un documento PDF. Sin embargo, ciertas clases de aplicaciones Windows Store tienen un requisito para representar contenido PDF directamente en la pantalla. Estas aplicaciones se suelen escribir utilizando C++ nativo y utilizan XAML o DirectX. Para estas aplicaciones, Windows 8.1 también incluye API nativas para contenido PDF representación sobre una superficie de DirectX.

Las API nativas para la representación de PDF están presentes en el ganado­dows.data.pdf.interop.h encabezado de archivo y requieren vinculación con la biblioteca estática windows.data.pdf.lib. Estas API están disponibles exclusivamente para los desarrolladores de C++ que quieren representar contenido PDF directamente en la pantalla.

PdfCreateRenderer PdfCreateRenderer la función es el punto de entrada para la API nativa. Acepta una instancia de un IDXGIDevice como entrada y devuelve una instancia de una interfaz IPdfRendererNative. El parámetro de entrada IDXGIDevice puede obtenerse un SurfaceImageSource de XAML, un VirtualSurfaceImageSource o un SwapChainBackgroundPanel de XAML. Quizá sepa que estos son los tipos de interoperabilidad XAML compatible para la mezcla de DirectX contenido en un marco XAML. Llamar a la función PdfCreateRenderer es fácil. Asegúrese de incluir link contra la biblioteca estática de windows.data.pdf.lib y windows.data.pdf.interop.h. Puede obtener una instancia de un IDXGIDevice de la instancia D3DDevice subyacente. Pasar la instancia de IDXGIDevice a la función PdfCreateRenderer. Si la función tiene éxito, devuelve una instancia válida de una interfaz IPdfRendererNative:

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

La interfaz IPdfRendererNative IPdfRendererNative la interfaz es la única compatible con la API nativa. La interfaz contiene dos métodos auxiliares: RenderPageToSurface y RenderPageToDeviceContext.

RenderPageToSurface es el método correcto a utilizar al representar contenido PDF a un XAML SurfaceImageSource o VirtualSurfaceImageSource. El método RenderPageToSurface toma un PDF como un parámetro de entrada junto con una instancia de DXGISurface que dibujar el contenido, un desplazamiento en el dispositivo para dibujar y una estructura PDF_RENDER_PARAMS opcional. Como examinar el método RenderPageToSurface, se sorprenderá al ver la entrada PDF es de tipo IUnknown. ¿Cómo obtengo un PDF de tipo IUnknown. Resulta que con la API de WinRT, una vez que obtienes una instancia válida de PDF de un objeto PdfDocument, puede utilizar safe_cast para lanzar un PDF a IUnknown.

Cuando se utiliza el SurfaceImageSource o VirtualSurface­tipos de interoperabilidad ImageSource, llamando BeginDraw devuelve un desplazamiento en el atlas que el XAML proporciona su aplicación para dibujar el contenido. Deberías pasar el desvío a RenderPageToSurface para asegurar que dibujo ocurre en la posición correcta.

El RenderPageToDeviceContext es el método correcto a utilizar al representar contenido PDF a un SwapChainBackgroundPanel de XAML. La RenderPageToDeviceContext toma un PDF como un parámetro de entrada junto con una instancia de ID2D1DeviceContext que dibujar el contenido y una estructura PDF_RENDER_PARAMS opcional.

Además de dibujar directamente en la pantalla, también se puede personalizar el procesamiento mediante el uso de la estructura PDF_RENDER_PARAMS. El RenderPageToSurface y el RenderPageToDeviceContext aceptan una instancia de PDF_RENDER_PARAMS. Las opciones en PDF_RENDER_PARAMS son similares a la estructura de WinRT PDFPageRenderOptions. Tenga en cuenta que ninguna de las API nativas son asíncrona métodos. La representación ocurre directamente en la pantalla sin incurrir en el costo de codificación y descodificación.

Una vez más, una aplicación simple demuestra cómo utilizar estas API para representar el contenido PDF. Esta aplicación de ejemplo (DxPdfApp en la descarga de código adjunto) contiene una página principal con un botón de control, como se muestra en la figura 3.

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

El código para abrir un documento y acceder a una página PDF sigue siendo la misma entre la API de WinRT y la API nativa. El principal cambio está en el proceso de renderizado. Mientras las APIs WinRT codificar la página PDF como una imagen y escribir el archivo imagen de disco, las APIs nativas utilizan un procesador basado en DirectX para dibujar en la pantalla, como se muestra en el contenido del PDF figura 4.

Figura 4 el contenido del PDF RenderPageRect método dibujo en pantalla

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

Información adicional

Hablé de la API de PDF WinRT en el punto 8.1 de Windows que le permite incorporar contenido PDF en aplicaciones Windows Store. También discutí las diferencias entre el uso de la API de WinRT o el C + + / DirectX API y los beneficios de cada enfoque. Por último, I proporciona un conjunto de recomendaciones que se debe utilizar API bajo ciertos escenarios. Para obtener más información sobre las APIs de PDF en Windows 8.1, revisa la documentación y la muestra de escaparate de visor PDF en MSDN en bit.ly/1bD72TO.

 

Sridhar Poduri es un program manager en Microsoft. Apasionado de C++ y autor del libro “Modern C++ and Windows Store Apps” (Sridhar Poduri, 2013), mantiene un blog periódico sobre C++ y Windows en tiempo de ejecución en sridharpoduri.com.

Gracias al siguiente experto técnico por su ayuda en la revisión de este artículo: Subramanian Iyer (Microsoft)
Subramanian Iyer es un desarrollador en el equipo de Windows de Microsoft y ha estado involucrado con el desarrollo de Windows durante los tres últimos lanzamientos. Ha sido una parte del equipo lector, que desarrolló uno de los primeros C + + / aplicaciones XAML para Windows 8. Un programador y un nuevo padre, había encontrado tiempo para publicar un par de apps en la Store de Windows, bajo el nombre de LSubs.