diff options
author | Michael Vrhel <michael.vrhel@artifex.com> | 2013-11-08 18:13:49 -0800 |
---|---|---|
committer | Michael Vrhel <michael.vrhel@artifex.com> | 2013-11-11 10:47:55 -0800 |
commit | 6afafa79e302e4d23b5c82182359619b59c64f6a (patch) | |
tree | f013fb415c1c7a1892c3b8f366e39671f293f033 /platform/winrt | |
parent | 7570e7fa8a40cb41db6a1ab14b225cf00c4f56f5 (diff) | |
download | mupdf-6afafa79e302e4d23b5c82182359619b59c64f6a.tar.xz |
DirectX printing added
After much research it was determined that use of the DirectX 2D methods is the proper way
to add printing support to a Windows 8 application. This enables the pages to be rendered
as they are requested by the print thread. This occurs for both the print preview screen
as well as the print to the output device.
Diffstat (limited to 'platform/winrt')
-rw-r--r-- | platform/winrt/mupdf_cpp/MainPage.xaml.cpp | 628 | ||||
-rw-r--r-- | platform/winrt/mupdf_cpp/MainPage.xaml.h | 57 | ||||
-rw-r--r-- | platform/winrt/mupdf_cpp/PrintPage.cpp | 165 | ||||
-rw-r--r-- | platform/winrt/mupdf_cpp/PrintPage.h | 57 | ||||
-rw-r--r-- | platform/winrt/mupdf_cpp/mupdf_cpp.vcxproj | 4 | ||||
-rw-r--r-- | platform/winrt/mupdf_cpp/mupdf_cpp.vcxproj.filters | 2 | ||||
-rw-r--r-- | platform/winrt/mupdf_cpp/pch.h | 9 | ||||
-rw-r--r-- | platform/winrt/mupdfwinrt/muctx.cpp | 19 | ||||
-rw-r--r-- | platform/winrt/mupdfwinrt/muctx.h | 5 | ||||
-rw-r--r-- | platform/winrt/mupdfwinrt/mudocument.cpp | 49 | ||||
-rw-r--r-- | platform/winrt/mupdfwinrt/mudocument.h | 2 |
11 files changed, 654 insertions, 343 deletions
diff --git a/platform/winrt/mupdf_cpp/MainPage.xaml.cpp b/platform/winrt/mupdf_cpp/MainPage.xaml.cpp index 36becdaa..9ef47e76 100644 --- a/platform/winrt/mupdf_cpp/MainPage.xaml.cpp +++ b/platform/winrt/mupdf_cpp/MainPage.xaml.cpp @@ -92,11 +92,12 @@ MainPage::MainPage() m_linkcolor="#40AC7225"; mu_doc = nullptr; m_docPages = ref new Platform::Collections::Vector<DocumentPage^>(); - m_printpages = ref new Platform::Collections::Vector<DocumentPage^>(); m_thumbnails = ref new Platform::Collections::Vector<DocumentPage^>(); m_page_link_list = ref new Platform::Collections::Vector<IVector<RectList^>^>(); m_text_list = ref new Platform::Collections::Vector<RectList^>(); m_linkset = ref new Platform::Collections::Vector<int>(); + + SetUpDirectX(); RegisterForPrinting(); CleanUp(); RecordMainThread(); @@ -104,6 +105,44 @@ MainPage::MainPage() _pageLoadedHandlerToken = Loaded += ref new RoutedEventHandler(this, &MainPage::Page_Loaded); } +/* You need a Direct3D device to create a Direct2D device. This gets stuff + set up for Direct2D printing support */ +void MainPage::SetUpDirectX() +{ + UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + ComPtr<IDXGIDevice> dxgi_device; + D2D1_FACTORY_OPTIONS options; + ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS)); + D3D_FEATURE_LEVEL feature_levels[] = + { + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1 + }; + ComPtr<ID3D11Device> device; + ComPtr<ID3D11DeviceContext> context; + +#if defined(_DEBUG) + options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; +#endif + + ThrowIfFailed(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, + creation_flags, feature_levels, ARRAYSIZE(feature_levels), + D3D11_SDK_VERSION, &device, &m_featureLevel, &context)); + ThrowIfFailed(device.As(&m_d3d_device)); + ThrowIfFailed(context.As(&m_d3d_context)); + ThrowIfFailed(m_d3d_device.As(&dxgi_device)); + ThrowIfFailed(D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, + __uuidof(ID2D1Factory1), &options, &m_d2d_factory)); + ThrowIfFailed(CoCreateInstance(CLSID_WICImagingFactory, nullptr, + CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_wic_factory))); + m_d2d_factory->CreateDevice(dxgi_device.Get(), &m_d2d_device); +} + /* Used during launch of application from file */ void MainPage::Page_Loaded(Object^ sender, RoutedEventArgs^ e) { @@ -215,6 +254,17 @@ void MainPage::Picker(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventAr if (!EnsureUnsnapped()) return; + /* If we are actively rendering a document to the print thread then notify the + user that they will need to wait */ + if (m_print_active) + { + int total_pages = GetPrintPageCount(); + auto str1 = "Cannot open new file. Currently rendering page " + + m_curr_print_count + " of " + total_pages + " for print queue"; + NotifyUser(str1, StatusMessage); + return; + } + FileOpenPicker^ openPicker = ref new FileOpenPicker(); openPicker->ViewMode = PickerViewMode::List; openPicker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary; @@ -408,7 +458,7 @@ void MainPage::CreateBlank(int width, int height) DataWriterStoreOperation^ result = dw->StoreAsync(); /* Block on the Async call */ - while(result->Status != AsyncStatus::Completed) { + while(result->Status != Windows::Foundation::AsyncStatus::Completed) { } /* And store in a the image brush */ bmp->SetSource(ras); @@ -438,8 +488,6 @@ void MainPage::CleanUp() m_docPages->Clear(); if (m_thumbnails != nullptr && m_thumbnails->Size > 0) m_thumbnails->Clear(); - if (m_printpages != nullptr && m_printpages->Size > 0) - m_printpages->Clear(); /* With the ref counting this should not leak */ if (m_page_link_list != nullptr && m_page_link_list->Size > 0) m_page_link_list->Clear(); @@ -474,6 +522,8 @@ void MainPage::CleanUp() m_rectlist_page = -1; m_Progress = 0.0; m_doczoom = 1.0; + m_print_active = false; + m_curr_print_count = 1; this->xaml_PageSlider->Minimum = m_slider_min; this->xaml_PageSlider->Maximum = m_slider_max; @@ -676,7 +726,6 @@ void MainPage::InitialRender() doc_page->LinkBox = nullptr; m_docPages->Append(doc_page); m_thumbnails->Append(doc_page); - m_printpages->Append(doc_page); /* Create empty lists for our links and specify that they have not been computed for these pages */ Vector<RectList^>^ temp_link = ref new Vector<RectList^>(); @@ -1556,95 +1605,79 @@ bool MainPage::IsNotStandardView() xaml_WebView->Visibility == Windows::UI::Xaml::Visibility::Visible); } -/* The following code is for print support. This is just a simple demonstration of - printing with MuPDF in the Windows 8 environment */ +/* The following code is for print support. */ void MainPage::RegisterForPrinting() { - m_printdoc = ref new PrintDocument(); - m_printdoc_source = m_printdoc->DocumentSource; - m_printdoc->Paginate += - ref new Windows::UI::Xaml::Printing::PaginateEventHandler(this, &MainPage::CreatePrintPreviewPages); - m_printdoc->GetPreviewPage += - ref new Windows::UI::Xaml::Printing::GetPreviewPageEventHandler(this, &MainPage::GetPrintPreviewPages); - m_printdoc->AddPages += - ref new Windows::UI::Xaml::Printing::AddPagesEventHandler(this, &MainPage::AddPrintPages); - - PrintManager^ printMan = PrintManager::GetForCurrentView(); - m_printTaskRequestedEventToken = - printMan->PrintTaskRequested += - ref new TypedEventHandler<PrintManager^, PrintTaskRequestedEventArgs^>(this, &MainPage::PrintTaskRequested); -} - -void MainPage::UnregisterForPrinting() -{ - // Remove the handler for printing initialization. - PrintManager^ printMan = PrintManager::GetForCurrentView(); - printMan->PrintTaskRequested -= m_printTaskRequestedEventToken; -} - -void MainPage::PrintTaskRequested(PrintManager^ sender, PrintTaskRequestedEventArgs^ e) -{ - auto printTaskRef = std::make_shared<PrintTask^>(nullptr); - *printTaskRef = e->Request->CreatePrintTask("MuPDF Printing", - ref new PrintTaskSourceRequestedHandler([this, printTaskRef](PrintTaskSourceRequestedArgs^ args) - { - PrintTask^ printTask = *printTaskRef; - PrintTaskOptionDetails^ printDetailedOptions = - PrintTaskOptionDetails::GetFromPrintTaskOptions(printTask->Options); - - // Some standard printer options - printDetailedOptions->DisplayedOptions->Clear(); - printDetailedOptions->DisplayedOptions->Append(Windows::Graphics::Printing::StandardPrintTaskOptions::MediaSize); - printDetailedOptions->DisplayedOptions->Append(Windows::Graphics::Printing::StandardPrintTaskOptions::Copies); - - // Our custom options - PrintCustomItemListOptionDetails^ resolution = - printDetailedOptions->CreateItemListOption("resolution", "Render Resolution"); - resolution->AddItem("sres72", "72 dpi"); - resolution->AddItem("sres150", "150 dpi"); - resolution->AddItem("sres300", "300 dpi"); - resolution->AddItem("sres600", "600 dpi"); - resolution->TrySetValue("sres600"); - m_printresolution = 600; - printDetailedOptions->DisplayedOptions->Append("resolution"); - - PrintCustomItemListOptionDetails^ scaling = printDetailedOptions->CreateItemListOption("scaling", "Scaling"); - scaling->AddItem("sScaleToFit", "Scale To Fit"); - scaling->AddItem("sCrop", "Crop"); - // Add the custom option to the option list. - printDetailedOptions->DisplayedOptions->Append("scaling"); - scaling->TrySetValue("sScaleToFit"); - m_printcrop = false; - printTask->Options->MediaSize = PrintMediaSize::NorthAmericaLetter; - - PrintCustomItemListOptionDetails^ pageFormat = printDetailedOptions->CreateItemListOption(L"PageRange", L"Page Range"); - pageFormat->AddItem(L"PrintAll", L"Print all"); - pageFormat->AddItem(L"PrintRange", L"Print Range"); - printDetailedOptions->DisplayedOptions->Append(L"PageRange"); - PrintCustomTextOptionDetails^ pageRangeEdit = printDetailedOptions->CreateTextOption(L"PageRangeEdit", L"Range"); - - printDetailedOptions->OptionChanged += - ref new TypedEventHandler<PrintTaskOptionDetails^, PrintTaskOptionChangedEventArgs^>(this, &MainPage::PrintOptionsChanged); - - // Invoked when the print job is completed. - printTask->Completed += ref new TypedEventHandler<PrintTask^, PrintTaskCompletedEventArgs^>( - [=](PrintTask^ sender, PrintTaskCompletedEventArgs^ e) - { - auto callback = ref new Windows::UI::Core::DispatchedHandler( - [=]() - { - ClearPrintCollection(); - m_printpagedesc = PrintPageDesc(); - if (e->Completion == Windows::Graphics::Printing::PrintTaskCompletion::Failed) - { - NotifyUser("Sorry, printing failed", StatusMessage); - } - }); - Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, callback); - }); - // Set the document source. - args->SetSource(PrintDocumentSource); - })); + m_print_manager = Windows::Graphics::Printing::PrintManager::GetForCurrentView(); + m_print_manager->PrintTaskRequested += + ref new TypedEventHandler<PrintManager^, PrintTaskRequestedEventArgs^>(this, &MainPage::SetPrintTask); +} + +void MainPage::SetPrintTask(PrintManager^ sender, PrintTaskRequestedEventArgs^ args) +{ + PrintTaskSourceRequestedHandler^ source_handler = + ref new PrintTaskSourceRequestedHandler([this](PrintTaskSourceRequestedArgs^ args)-> void{ + Microsoft::WRL::ComPtr<PrintPages> document_source; + ThrowIfFailed(Microsoft::WRL::MakeAndInitialize<PrintPages>(&document_source, reinterpret_cast<IUnknown*>(this))); + IPrintDocumentSource^ objSource(reinterpret_cast<IPrintDocumentSource^>(document_source.Get())); + args->SetSource(objSource); + }); + + PrintTask^ print_task = + args->Request->CreatePrintTask(L"MuPDF WinRT Print", source_handler); + + /* Call backs so that we know when we are all done with the printing */ + print_task->Progressing += + ref new TypedEventHandler<PrintTask^, PrintTaskProgressingEventArgs^>(this, &MainPage::PrintProgress); + print_task->Completed += + ref new TypedEventHandler<PrintTask^, PrintTaskCompletedEventArgs^>(this, &MainPage::PrintCompleted); + m_print_active = true; + m_curr_print_count = 0; + + PrintTaskOptionDetails^ printDetailedOptions = + PrintTaskOptionDetails::GetFromPrintTaskOptions(print_task->Options); + + // Some standard printer options + printDetailedOptions->DisplayedOptions->Clear(); + printDetailedOptions->DisplayedOptions->Append(Windows::Graphics::Printing::StandardPrintTaskOptions::MediaSize); + printDetailedOptions->DisplayedOptions->Append(Windows::Graphics::Printing::StandardPrintTaskOptions::Copies); + + // Our custom options + PrintCustomItemListOptionDetails^ resolution = + printDetailedOptions->CreateItemListOption("resolution", "Render Resolution"); + resolution->AddItem("sres96", "96dpi"); + resolution->AddItem("sres150", "150 dpi"); + resolution->AddItem("sres300", "300 dpi"); + resolution->AddItem("sres600", "600 dpi"); + resolution->TrySetValue("sres600"); + m_printresolution = 600; + printDetailedOptions->DisplayedOptions->Append("resolution"); + + PrintCustomItemListOptionDetails^ location = printDetailedOptions->CreateItemListOption("location", "Location"); + location->AddItem("sCenter", "Center"); + location->AddItem("sTopleft", "Top Left"); + // Add the custom option to the option list. + printDetailedOptions->DisplayedOptions->Append("location"); + location->TrySetValue("sCenter"); + m_centerprint = true; + print_task->Options->MediaSize = PrintMediaSize::NorthAmericaLetter; + + PrintCustomItemListOptionDetails^ pageFormat = printDetailedOptions->CreateItemListOption(L"PageRange", L"Page Range"); + pageFormat->AddItem(L"PrintAll", L"Print all"); + pageFormat->AddItem(L"PrintRange", L"Print Range"); + printDetailedOptions->DisplayedOptions->Append(L"PageRange"); + PrintCustomTextOptionDetails^ pageRangeEdit = printDetailedOptions->CreateTextOption(L"PageRangeEdit", L"Range"); + + printDetailedOptions->OptionChanged += + ref new TypedEventHandler<PrintTaskOptionDetails^, PrintTaskOptionChangedEventArgs^>(this, &MainPage::PrintOptionsChanged); +} + +int MainPage::GetPrintPageCount() +{ + if (m_ppage_num_list.size() > 0) + return m_ppage_num_list.size(); + else + return m_num_pages; } void MainPage::PrintOptionsChanged(PrintTaskOptionDetails^ sender, PrintTaskOptionChangedEventArgs^ args) @@ -1661,9 +1694,9 @@ void MainPage::PrintOptionsChanged(PrintTaskOptionDetails^ sender, PrintTaskOpti IPrintOptionDetails^ resolution = sender->Options->Lookup(optionId); String^ resolutionValue = safe_cast<String^>(resolution->Value); - if (resolutionValue == "sres72") + if (resolutionValue == "sres96") { - m_printresolution = 72; + m_printresolution = 96; } else if (resolutionValue == "sres150") { @@ -1680,18 +1713,18 @@ void MainPage::PrintOptionsChanged(PrintTaskOptionDetails^ sender, PrintTaskOpti } /* Need to update preview with a change of this one */ - if (optionId == "scaling") + if (optionId == "location") { IPrintOptionDetails^ scaling = sender->Options->Lookup(optionId); String^ scaleValue = safe_cast<String^>(scaling->Value); - if (scaleValue == "sScaleToFit") + if (scaleValue == "sCenter") { - m_printcrop = false; + m_centerprint = true; } - if (scaleValue == "sCrop") + if (scaleValue == "sTopleft") { - m_printcrop = true; + m_centerprint = false; } force_reset = true; } @@ -1821,241 +1854,204 @@ void MainPage::RemovePageRangeEdit(PrintTaskOptionDetails^ printTaskOptionDetail } } -void MainPage::ClearPrintCollection() +void MainPage::CreatePrintControl(_In_ IPrintDocumentPackageTarget* docPackageTarget, + _In_ D2D1_PRINT_CONTROL_PROPERTIES* printControlProperties) { - m_printlock.lock(); - for (int k = 0; k < m_num_pages; k++) - { - auto thumb_page = this->m_thumbnails->GetAt(k); - this->m_printpages->SetAt(k, thumb_page); - } - m_printlock.unlock(); + m_d2d_printcontrol = nullptr; + ThrowIfFailed(m_d2d_device->CreatePrintControl(m_wic_factory.Get(), docPackageTarget, + printControlProperties, &m_d2d_printcontrol)); } -/* Just use the thumbnail images for now */ -void MainPage::CreatePrintPreviewPages(Object^ sender, PaginateEventArgs^ e) +void MainPage::DrawPreviewSurface(float width, float height, float scale_in, + D2D1_RECT_F contentBox, uint32 page_num, + IPrintPreviewDxgiPackageTarget* previewTarget) { - InterlockedIncrement64(&m_requestCount); - PrintPageDesc ppageDescription; - PrintTaskOptionDetails^ printDetailedOptions = - PrintTaskOptionDetails::GetFromPrintTaskOptions(e->PrintTaskOptions); - PrintPageDescription DeviceDescription = - e->PrintTaskOptions->GetPageDescription(0); - - /* This has the media dimension */ - ppageDescription.pagesize = DeviceDescription.PageSize; - - ppageDescription.resolution.Width = DeviceDescription.DpiX; - ppageDescription.resolution.Height = DeviceDescription.DpiY; - - ppageDescription.margin.Width = (std::max)(DeviceDescription.ImageableRect.Left, - DeviceDescription.ImageableRect.Right - - DeviceDescription.PageSize.Width); - - ppageDescription.margin.Height = (std::max)(DeviceDescription.ImageableRect.Top, - DeviceDescription.ImageableRect.Bottom - - DeviceDescription.PageSize.Height); - - ppageDescription.printpagesize.Width = DeviceDescription.PageSize.Width - - ppageDescription.margin.Width * 2; - ppageDescription.printpagesize.Height = DeviceDescription.PageSize.Height - - ppageDescription.margin.Height * 2; - - ClearPrintCollection(); - PrintDocument^ printDocument = safe_cast<PrintDocument^>(sender); - m_printpagedesc = ppageDescription; + int dpi = 96; + int index_page_num = page_num - 1; + int ren_page_num = index_page_num; if (m_ppage_num_list.size() > 0) - { - printDocument->SetPreviewPageCount(m_ppage_num_list.size(), - PreviewPageCountType::Intermediate); - } - else - { - printDocument->SetPreviewPageCount(m_num_pages, - PreviewPageCountType::Intermediate); - } -} - -/* Set the page with the new raster information */ -void MainPage::UpdatePreview(int page_num, InMemoryRandomAccessStream^ ras, - Point ras_size, Page_Content_t content_type, double zoom_in) -{ - assert(IsMainThread()); + ren_page_num = m_ppage_num_list[page_num - 1] - 1; + + /* This goes on in a background thread. Hence is non-blocking for UI */ + assert(IsBackgroundThread()); + + /* Set up all the DirectX stuff */ + CD3D11_TEXTURE2D_DESC textureDesc(DXGI_FORMAT_B8G8R8A8_UNORM, + static_cast<uint32>(ceil(width * dpi / 96)), + static_cast<uint32>(ceil(height * dpi / 96)), + 1, 1, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); + ComPtr<ID3D11Texture2D> texture; + ThrowIfFailed(m_d3d_device->CreateTexture2D(&textureDesc, nullptr, &texture)); + ComPtr<IDXGISurface> dxgi_surface; + ThrowIfFailed(texture.As<IDXGISurface>(&dxgi_surface)); + + // Create a new D2D device context for rendering the preview surface. D2D + // device contexts are stateful, and hence a unique device context must be + // used on each thread. + ComPtr<ID2D1DeviceContext> d2d_context; + ThrowIfFailed(m_d2d_device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, + &d2d_context)); + // Update DPI for preview surface as well. + d2d_context->SetDpi(96, 96); + + D2D1_BITMAP_PROPERTIES1 bitmap_properties = + D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)); + + // Create surface bitmap on which page content is drawn. + ComPtr<ID2D1Bitmap1> d2d_surfacebitmap; + ThrowIfFailed(d2d_context->CreateBitmapFromDxgiSurface(dxgi_surface.Get(), + &bitmap_properties, &d2d_surfacebitmap)); + d2d_context->SetTarget(d2d_surfacebitmap.Get()); + + /* Figure out all the sizing */ + spatial_info_t spatial_info; + spatial_info.scale_factor = 1.0; + spatial_info.size.X = width; + spatial_info.size.Y = height; + Point ras_size = ComputePageSize(spatial_info, ren_page_num); + ras_size.X = ceil(ras_size.X); + ras_size.Y = ceil(ras_size.Y); + + Array<unsigned char>^ bmp_data; + int code = mu_doc->RenderPageBitmapSync(ren_page_num, ras_size.X, ras_size.Y, + true, &bmp_data); + D2D1_SIZE_U bit_map_rect; + bit_map_rect.width = ras_size.X; + bit_map_rect.height = ras_size.Y; + + D2D1_BITMAP_PROPERTIES1 bitmap_properties2 = + D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)); + + ID2D1Bitmap1 *bit_map; + ThrowIfFailed(d2d_context->CreateBitmap(bit_map_rect, &(bmp_data[0]), + ras_size.X * 4, &bitmap_properties2, + &bit_map)); + D2D1_SIZE_F size = bit_map->GetSize(); + + /* Handle centering */ + float y_offset = 0; + float x_offset = 0; + if (m_centerprint) + { + y_offset = (height - size.height) / 2.0; + x_offset = (width - size.width) / 2.0; + } + + d2d_context->BeginDraw(); + d2d_context->DrawBitmap(bit_map, D2D1::RectF(x_offset, y_offset, + size.width + x_offset, size.height + y_offset)); + ThrowIfFailed(d2d_context->EndDraw()); + ThrowIfFailed(previewTarget->DrawPage(page_num, dxgi_surface.Get(), dpi, dpi)); +} + +HRESULT MainPage::ClosePrintControl() +{ + return (m_d2d_printcontrol == nullptr) ? S_OK : m_d2d_printcontrol->Close(); +} + +void MainPage::PrintPage(uint32 page_num, D2D1_RECT_F image_area, D2D1_SIZE_F page_area, + float device_dpi, IStream* print_ticket) +{ + int dpi = m_printresolution; + int index_page_num = page_num - 1; + int ren_page_num = index_page_num; - WriteableBitmap ^bmp = ref new WriteableBitmap(ras_size.X, ras_size.Y); - bmp->SetSource(ras); + /* Windoze seems to hand me a bogus dpi */ + device_dpi = 96; - DocumentPage^ doc_page = ref new DocumentPage(); - doc_page->Image = bmp; - - doc_page->Height = ras_size.Y; - doc_page->Width = ras_size.X; - doc_page->Content = content_type; - doc_page->Zoom = zoom_in; - this->m_printpages->SetAt(page_num, doc_page); + if (m_ppage_num_list.size() > 0) + ren_page_num = m_ppage_num_list[page_num - 1] - 1; + + /* This goes on in a background thread. Hence is non-blocking for UI */ + assert(IsBackgroundThread()); + + /* Print command list set up */ + ComPtr<ID2D1DeviceContext> d2d_context; + ThrowIfFailed(m_d2d_device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, + &d2d_context)); + ComPtr<ID2D1CommandList> clist; + ThrowIfFailed(d2d_context->CreateCommandList(&clist)); + d2d_context->SetTarget(clist.Get()); + + /* Figure out all the sizing. Width and height here are for device_dpi */ + float width = image_area.right - image_area.left; + float height = image_area.bottom - image_area.top; + + spatial_info_t spatial_info; + spatial_info.scale_factor = 1.0; + /* width and height are based upon device dpi (96) and MuPDF native + resolution is 72dpi */ + spatial_info.size.X = (width /device_dpi) * (m_printresolution); + spatial_info.size.Y = (height /device_dpi) * (m_printresolution); + Point ras_size = ComputePageSize(spatial_info, ren_page_num); + ras_size.X = ceil(ras_size.X); + ras_size.Y = ceil(ras_size.Y); + + Array<unsigned char>^ bmp_data; + int code = mu_doc->RenderPageBitmapSync(ren_page_num, ras_size.X, ras_size.Y, + true, &bmp_data); + D2D1_SIZE_U bit_map_rect; + bit_map_rect.width = ras_size.X; + bit_map_rect.height = ras_size.Y; + + D2D1_BITMAP_PROPERTIES1 bitmap_properties2 = + D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)); + + ID2D1Bitmap1 *bit_map; + ThrowIfFailed(d2d_context->CreateBitmap(bit_map_rect, &(bmp_data[0]), + ras_size.X * 4, &bitmap_properties2, + &bit_map)); + D2D1_SIZE_F size = bit_map->GetSize(); + + /* Handle centering */ + float y_offset = 0; + float x_offset = 0; + if (m_centerprint) + { + /* Offsets need to be provided in the device dpi */ + y_offset = (page_area.height - (size.height * device_dpi / m_printresolution)) / 2.0; + x_offset = (page_area.width - (size.width * device_dpi / m_printresolution)) / 2.0; + } + + float image_height = (bit_map_rect.height / m_printresolution) * device_dpi; + float image_width = (bit_map_rect.width / m_printresolution) * device_dpi; + + d2d_context->BeginDraw(); + d2d_context->DrawBitmap(bit_map, D2D1::RectF(x_offset, y_offset, + image_width + x_offset, image_height + y_offset)); + ThrowIfFailed(d2d_context->EndDraw()); + ThrowIfFailed(clist->Close()); + ThrowIfFailed(m_d2d_printcontrol->AddPage(clist.Get(), page_area, print_ticket)); } -void MainPage::CleanUpPreview(int page_num) +void MainPage::RefreshPreview() { - for (int k = page_num - 1; k <= page_num + 1; k++) - { - if (k >= 0 && k < this->m_num_pages && k != page_num) - { - auto doc = this->m_printpages->GetAt(k); - if (doc->Content == THUMBNAIL) return; - - if (this->m_thumbnails->Size > k) - { - auto thumb_page = this->m_thumbnails->GetAt(k); - this->m_printpages->SetAt(k, thumb_page); - } - } - } + PrintPages *p_struct = (PrintPages*) m_print_struct; + p_struct->ResetPreview(); } -void MainPage::RefreshPreview() +/* This reference is needed so that we can reset preview when changes occur on options */ +void MainPage::SetPrintTarget(void *print_struct) { - auto callback = - ref new Windows::UI::Core::DispatchedHandler([this]() - { - m_printdoc->InvalidatePreview(); - }); - Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, callback); + m_print_struct = print_struct; } -void MainPage::GetPrintPreviewPages(Object^ sender, GetPreviewPageEventArgs^ e) +/* These are called by the print thread and as such we cannot do UI updates + directly. However we can keep a monitor of what is going on so that if + someone tries to open a new document while we are printing some monster doc, + we can let them know that they need to wait. */ +void MainPage::PrintProgress(PrintTask^ sender, PrintTaskProgressingEventArgs^ args) { - PrintDocument^ printDocument = safe_cast<PrintDocument^>(sender); - - LONGLONG requestNumber = 0; - InterlockedExchange64(&requestNumber, m_requestCount); - int index_page_num = e->PageNumber; - int ren_page_num = index_page_num; - - if (m_ppage_num_list.size() > 0) - { - ren_page_num = m_ppage_num_list[index_page_num - 1]; - } - auto doc_curr = this->m_printpages->GetAt(ren_page_num - 1); - if (doc_curr->Content == PRINT_PREVIEW) - { - auto bitmap = doc_curr->Image; - Image^ image = ref new Image(); - image->Source = bitmap; - image->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Center; - image->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Center; - image->Stretch = Stretch::UniformToFill; - image->Width = bitmap->PixelWidth; - image->Height = bitmap->PixelHeight; - printDocument->SetPreviewPage(index_page_num, image); - } - else - { - /* Determine the scale to fit case or crop */ - spatial_info_t spatial_info = InitSpatial(1.0); - int pagNum = ren_page_num - 1; - Point ras_size = ComputePageSize(spatial_info, pagNum); - float scale = 1.0; - - if (!(this->m_printcrop)) - { - float scaleY = m_printpagedesc.printpagesize.Height / ras_size.Y; - float scaleX = m_printpagedesc.printpagesize.Width / ras_size.X; - scale = (std::min)(scaleY, scaleX); - } - auto render_task = - create_task(mu_doc->RenderPageAsync(pagNum, ras_size.X, ras_size.Y, true)); - render_task.then([=] (InMemoryRandomAccessStream^ ras) - { - UpdatePreview(pagNum, ras, ras_size, PRINT_PREVIEW, scale); - auto doc_curr = this->m_printpages->GetAt(pagNum); - auto bitmap = doc_curr->Image; - Image^ image = ref new Image(); - image->Source = bitmap; - image->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Center; - image->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Center; - image->Stretch = Stretch::UniformToFill; - image->Width = bitmap->PixelWidth * scale; - image->Height = bitmap->PixelHeight * scale; - printDocument->SetPreviewPage(index_page_num, image); - }, task_continuation_context::use_current()); - } - this->CleanUpPreview(ren_page_num - 1); + assert(IsBackgroundThread()); + this->m_curr_print_count = args->DocumentPageCount; } -void MainPage::AddPrintPages(Object^ sender, AddPagesEventArgs^ e) +void MainPage::PrintCompleted(PrintTask^ sender, PrintTaskCompletedEventArgs^ args) { - PrintDocument^ printDocument = safe_cast<PrintDocument^>(sender); - - /* We create a list of tasks to create the pages for printing. Once all the - pages are created then we can go ahead and start adding them for printing */ - std::vector<concurrency::task<void>> createPageTasks; - double res_scale = ((double) m_printresolution / 72.0); - int page_count = m_num_pages; - - if (m_ppage_num_list.size() > 0) - page_count = m_ppage_num_list.size(); - - for(int i = 0; i < page_count; ++i) - { - m_printlock.lock(); - - int page_num = i; - if (m_ppage_num_list.size() > 0) - page_num = m_ppage_num_list[i] - 1; - - auto doc_curr = this->m_printpages->GetAt(page_num); - if (doc_curr->Content != FULL_RESOLUTION) - { - spatial_info_t spatial_info = InitSpatial(res_scale); - Point ras_size = ComputePageSize(spatial_info, page_num); - float scale = 1.0; - - if (!(this->m_printcrop)) - { - spatial_info = InitSpatial(1.0); - ras_size = ComputePageSize(spatial_info, page_num); - float scaleY = m_printpagedesc.printpagesize.Height / ras_size.Y; - float scaleX = m_printpagedesc.printpagesize.Width / ras_size.X; - scale = (std::min)(scaleY, scaleX); - spatial_info = InitSpatial(res_scale * scale); - ras_size = ComputePageSize(spatial_info, page_num); - } - auto render_task = - create_task(mu_doc->RenderPageAsync(page_num, ras_size.X, ras_size.Y, true)); - createPageTasks.push_back(render_task.then([=] (InMemoryRandomAccessStream^ ras) - { - UpdatePreview(page_num, ras, ras_size, FULL_RESOLUTION, 1.0); - }, task_continuation_context::use_current())); - } - m_printlock.unlock(); - } - - /* When all the tasks have finished then go ahead and add them to the print - document */ - concurrency::when_all(createPageTasks.begin(), createPageTasks.end()). then([=] - { - for (int i = 0; i < page_count; i++) - { - int page_num = i; - if (m_ppage_num_list.size() > 0) - page_num = m_ppage_num_list[i] - 1; - auto doc_curr = this->m_printpages->GetAt(page_num); - auto bitmap = doc_curr->Image; - Image^ image = ref new Image(); - image->Source = bitmap; - image->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Center; - image->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Center; - image->Stretch = Stretch::UniformToFill; - image->Width = bitmap->PixelWidth / res_scale; - image->Height = bitmap->PixelHeight / res_scale; - printDocument->AddPage(image); - } - /* All pages provided. */ - printDocument->AddPagesComplete(); - /* Reset the current page description as soon as possible since the - PrintTask.Completed event might fire later */ - m_printpagedesc = PrintPageDesc(); - }); + assert(IsBackgroundThread()); + m_print_active = false; } diff --git a/platform/winrt/mupdf_cpp/MainPage.xaml.h b/platform/winrt/mupdf_cpp/MainPage.xaml.h index 49f0cd75..f229123e 100644 --- a/platform/winrt/mupdf_cpp/MainPage.xaml.h +++ b/platform/winrt/mupdf_cpp/MainPage.xaml.h @@ -13,6 +13,7 @@ #include <assert.h> #include "DocumentPage.h" #include "status.h" +#include "PrintPage.h" using namespace Platform; using namespace Concurrency; @@ -62,7 +63,6 @@ typedef struct spatial_info_s namespace mupdf_cpp { - class PageRangeException { private: @@ -116,6 +116,15 @@ namespace mupdf_cpp /// </summary> public ref class MainPage sealed { + + inline void ThrowIfFailed(HRESULT hr) + { + if (FAILED(hr)) + { + throw Platform::Exception::CreateException(hr); + } + } + public: MainPage(); @@ -150,8 +159,8 @@ namespace mupdf_cpp Vector<IVector<RectList^>^>^ m_page_link_list; Vector<int>^ m_linkset; Vector<RectList^>^ m_text_list; - int m_rectlist_page; mudocument^ mu_doc; + int m_rectlist_page; bool m_file_open; int m_currpage; int m_searchpage; @@ -178,17 +187,26 @@ namespace mupdf_cpp double m_doczoom; /* Print related */ - Vector<DocumentPage^>^ m_printpages; - concurrency::critical_section m_printlock; - EventRegistrationToken m_printTaskRequestedEventToken; PrintDocument^ m_printdoc; IPrintDocumentSource^ m_printdoc_source; - LONGLONG m_requestCount; PrintPageDesc m_printpagedesc; int m_printresolution; - bool m_printcrop; + bool m_centerprint; bool m_pageRangeEditVisible; std::vector<int> m_ppage_num_list; + int m_curr_print_count; + bool m_print_active; + + /* DirectX Print Control */ + PrintManager ^m_print_manager; + Microsoft::WRL::ComPtr<ID3D11Device> m_d3d_device; + Microsoft::WRL::ComPtr<ID3D11DeviceContext> m_d3d_context; + Microsoft::WRL::ComPtr<ID2D1PrintControl> m_d2d_printcontrol; + Microsoft::WRL::ComPtr<ID2D1Device> m_d2d_device; + Microsoft::WRL::ComPtr<IWICImagingFactory2> m_wic_factory; + Microsoft::WRL::ComPtr<ID2D1Factory1> m_d2d_factory; + D3D_FEATURE_LEVEL m_featureLevel; + void *m_print_struct; void ReplaceImage(int page_num, InMemoryRandomAccessStream^ ras, Point ras_size, double zoom); void Picker(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); @@ -257,19 +275,26 @@ namespace mupdf_cpp /* Print Related */ void RegisterForPrinting(); - void UnregisterForPrinting(); - void PrintTaskRequested(PrintManager^ sender, PrintTaskRequestedEventArgs^ e); - void ClearPrintCollection(); - void CreatePrintPreviewPages(Object^ sender, PaginateEventArgs^ e); - void GetPrintPreviewPages(Object^ sender, GetPreviewPageEventArgs^ e); - void UpdatePreview(int page_num, InMemoryRandomAccessStream^ ras, - Point ras_size, Page_Content_t content_type, double zoom_in); - void CleanUpPreview(int new_page); - void AddPrintPages(Object^ sender, AddPagesEventArgs^ e); void PrintOptionsChanged(PrintTaskOptionDetails^ sender, PrintTaskOptionChangedEventArgs^ args); void RefreshPreview(); void RemovePageRangeEdit(PrintTaskOptionDetails^ printTaskOptionDetails); void SplitString(String^ string, wchar_t delimiter, std::vector<std::wstring>& words); void GetPagesInRange(String^ pageRange); + void SetPrintTask( PrintManager^, PrintTaskRequestedEventArgs^ args); + void SetUpDirectX(); + + internal: + void CreatePrintControl(IPrintDocumentPackageTarget* docPackageTarget, + D2D1_PRINT_CONTROL_PROPERTIES* printControlProperties); + void PrintPage(uint32 page_num, D2D1_RECT_F image_area, D2D1_SIZE_F page_area, + float device_dpi, IStream* print_ticket); + HRESULT ClosePrintControl(); + void DrawPreviewSurface(float width, float height, float scale, + D2D1_RECT_F contentBox, uint32 desiredJobPage, + IPrintPreviewDxgiPackageTarget* previewTarget); + int GetPrintPageCount(); + void SetPrintTarget(void *print_struct); + void PrintProgress(PrintTask^ sender, PrintTaskProgressingEventArgs^ args); + void PrintCompleted(PrintTask^ sender, PrintTaskCompletedEventArgs^ args); }; } diff --git a/platform/winrt/mupdf_cpp/PrintPage.cpp b/platform/winrt/mupdf_cpp/PrintPage.cpp new file mode 100644 index 00000000..5e14f191 --- /dev/null +++ b/platform/winrt/mupdf_cpp/PrintPage.cpp @@ -0,0 +1,165 @@ +#include "pch.h" +#include "PrintPage.h" + +using namespace Microsoft::WRL; +using namespace Windows::Graphics::Printing; + +#pragma region IDocumentPageSource Methods + +/* This is the interface to the print thread calls */ +IFACEMETHODIMP +PrintPages::GetPreviewPageCollection(IPrintDocumentPackageTarget* doc_target, + IPrintPreviewPageCollection** doc_collection) +{ + HRESULT hr = (doc_target != nullptr) ? S_OK : E_INVALIDARG; + + if (SUCCEEDED(hr)) + { + hr = doc_target->GetPackageTarget(ID_PREVIEWPACKAGETARGET_DXGI, + IID_PPV_ARGS(&m_dxgi_previewtarget)); + } + ComPtr<IPrintPreviewPageCollection> page_collection; + if (SUCCEEDED(hr)) + { + ComPtr<PrintPages> docSource(this); + hr = docSource.As<IPrintPreviewPageCollection>(&page_collection); + } + if (SUCCEEDED(hr)) + hr = page_collection.CopyTo(doc_collection); + + if (SUCCEEDED(hr)) + this->m_renderer->SetPrintTarget((void*) this); + return hr; +} + +IFACEMETHODIMP +PrintPages::MakeDocument(IInspectable* doc_options, IPrintDocumentPackageTarget* doc_target) +{ + if (doc_options == nullptr || doc_target == nullptr) + return E_INVALIDARG; + + PrintTaskOptions^ option = reinterpret_cast<PrintTaskOptions^>(doc_options); + PrintPageDescription page_desc = option->GetPageDescription(1); + + D2D1_PRINT_CONTROL_PROPERTIES print_properties; + + print_properties.rasterDPI = (float)(min(page_desc.DpiX, page_desc.DpiY)); + print_properties.colorSpace = D2D1_COLOR_SPACE_SRGB; + print_properties.fontSubset = D2D1_PRINT_FONT_SUBSET_MODE_DEFAULT; + + HRESULT hr = S_OK; + + try + { + m_renderer->CreatePrintControl(doc_target, &print_properties); + + D2D1_RECT_F imageableRect = D2D1::RectF(page_desc.ImageableRect.X, + page_desc.ImageableRect.Y, + page_desc.ImageableRect.X + page_desc.ImageableRect.Width, + page_desc.ImageableRect.Y + page_desc.ImageableRect.Height); + + D2D1_SIZE_F pageSize = D2D1::SizeF(page_desc.PageSize.Width, page_desc.PageSize.Height); + m_totalpages = m_renderer->GetPrintPageCount(); + + for (uint32 page_num = 1; page_num <= m_totalpages; ++page_num) + m_renderer->PrintPage(page_num, imageableRect, pageSize, page_desc.DpiX, nullptr); + } + catch (Platform::Exception^ e) + { + hr = e->HResult; + } + + HRESULT hrClose = m_renderer->ClosePrintControl(); + if (SUCCEEDED(hr)) + { + hr = hrClose; + } + return hr; +} + +#pragma endregion IDocumentPageSource Methods + +#pragma region IPrintPreviewPageCollection Methods + +IFACEMETHODIMP +PrintPages::Paginate(uint32 current_jobpage, IInspectable* doc_options) +{ + HRESULT hr = (doc_options != nullptr) ? S_OK : E_INVALIDARG; + + if (SUCCEEDED(hr)) + { + PrintTaskOptions^ option = reinterpret_cast<PrintTaskOptions^>(doc_options); + PrintPageDescription page_desc = option->GetPageDescription(current_jobpage); + + hr = m_dxgi_previewtarget->InvalidatePreview(); + m_totalpages = m_renderer->GetPrintPageCount(); + + if (SUCCEEDED(hr)) + hr = m_dxgi_previewtarget->SetJobPageCount(PageCountType::FinalPageCount, m_totalpages); + + if (SUCCEEDED(hr)) + { + m_width = page_desc.PageSize.Width; + m_height = page_desc.PageSize.Height; + m_imageable_rect = D2D1::RectF(page_desc.ImageableRect.X, page_desc.ImageableRect.Y, + page_desc.ImageableRect.X + page_desc.ImageableRect.Width, + page_desc.ImageableRect.Y + page_desc.ImageableRect.Height); + m_paginate_called = true; + } + } + return hr; +} + +float +PrintPages::TransformedPageSize(float desired_width, float desired_height, + Windows::Foundation::Size* preview_size) +{ + float scale = 1.0f; + + if (desired_width > 0 && desired_height > 0) + { + preview_size->Width = desired_width; + preview_size->Height = desired_height; + scale = m_width / desired_width; + } + else + { + preview_size->Width = 0; + preview_size->Height = 0; + } + return scale; +} + +void +PrintPages::ResetPreview() +{ + m_dxgi_previewtarget->InvalidatePreview(); +} + +IFACEMETHODIMP +PrintPages::MakePage(uint32 desired_jobpage, float width, float height) +{ + HRESULT hr = (width > 0 && height > 0) ? S_OK : E_INVALIDARG; + + if (desired_jobpage == JOB_PAGE_APPLICATION_DEFINED && m_paginate_called) + desired_jobpage = 1; + + if (SUCCEEDED(hr) && m_paginate_called) + { + Windows::Foundation::Size preview_size; + float scale = TransformedPageSize(width, height, &preview_size); + + try + { + m_renderer->DrawPreviewSurface(preview_size.Width, preview_size.Height, + scale, m_imageable_rect, desired_jobpage, + m_dxgi_previewtarget.Get()); + } + catch (Platform::Exception^ e) + { + hr = e->HResult; + } + } + return hr; +} +#pragma region IPrintPreviewPageCollection Methods diff --git a/platform/winrt/mupdf_cpp/PrintPage.h b/platform/winrt/mupdf_cpp/PrintPage.h new file mode 100644 index 00000000..725a5a43 --- /dev/null +++ b/platform/winrt/mupdf_cpp/PrintPage.h @@ -0,0 +1,57 @@ +#pragma once +#include <windows.graphics.printing.h> +#include <printpreview.h> +#include <documentsource.h> +#include "MainPage.xaml.h" + +using namespace Microsoft::WRL; +using namespace mupdf_cpp; + +/* This is the interface to the print thread calls */ +class PrintPages : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRtClassicComMix>, + ABI::Windows::Graphics::Printing::IPrintDocumentSource, + IPrintDocumentPageSource, + IPrintPreviewPageCollection> +{ +private: + InspectableClass(L"Windows.Graphics.Printing.IPrintDocumentSource", BaseTrust); + +public: + HRESULT RuntimeClassInitialize(IUnknown* pageRenderer) + { + HRESULT hr = (pageRenderer != nullptr) ? S_OK : E_INVALIDARG; + + if (SUCCEEDED(hr)) + { + m_paginate_called = false; + m_totalpages = 1; + m_height = 0.f; + m_width = 0.f; + m_renderer = reinterpret_cast<MainPage^>(pageRenderer); + } + return hr; + } + IFACEMETHODIMP GetPreviewPageCollection(IPrintDocumentPackageTarget* doc_target, + IPrintPreviewPageCollection** doc_collection); + IFACEMETHODIMP MakeDocument(IInspectable* doc_options, + IPrintDocumentPackageTarget* doc_target); + IFACEMETHODIMP Paginate(uint32 current_jobpage, IInspectable* doc_options); + IFACEMETHODIMP MakePage(uint32 desired_jobpage, float width, float height); + void ResetPreview(); + +private: + float TransformedPageSize(float desired_width, float desired_height, + Windows::Foundation::Size* preview_size); + uint32 m_totalpages; + bool m_paginate_called; + float m_height; + float m_width; + D2D1_RECT_F m_imageable_rect; + MainPage^ m_renderer; + + Microsoft::WRL::ComPtr<IPrintPreviewDxgiPackageTarget> m_dxgi_previewtarget; + + void DrawPreviewSurface(float width, float height, float scale_in, + D2D1_RECT_F contentBox, uint32 page_num, + IPrintPreviewDxgiPackageTarget* previewTarget); +}; diff --git a/platform/winrt/mupdf_cpp/mupdf_cpp.vcxproj b/platform/winrt/mupdf_cpp/mupdf_cpp.vcxproj index eeeb7634..baa51c03 100644 --- a/platform/winrt/mupdf_cpp/mupdf_cpp.vcxproj +++ b/platform/winrt/mupdf_cpp/mupdf_cpp.vcxproj @@ -173,7 +173,7 @@ <AdditionalIncludeDirectories>../../include/;../mupdfwinrt/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link> - <AdditionalDependencies>kernel32.lib;%(AdditionalDependencies);../$(Platform)/$(Configuration)/libmupdf_winRT.lib;../$(Platform)/$(Configuration)/libthirdparty_winRT.lib;../$(Platform)/$(Configuration)/libmupdf-nov8_winRT.lib;../$(Platform)/$(Configuration)/mupdfwinrt.lib</AdditionalDependencies> + <AdditionalDependencies>D3D11.lib;D2d1.lib;kernel32.lib;%(AdditionalDependencies);../$(Platform)/$(Configuration)/libmupdf_winRT.lib;../$(Platform)/$(Configuration)/libthirdparty_winRT.lib;../$(Platform)/$(Configuration)/libmupdf-nov8_winRT.lib;../$(Platform)/$(Configuration)/mupdfwinrt.lib</AdditionalDependencies> <AdditionalOptions>/APPCONTAINER %(AdditionalOptions)</AdditionalOptions> </Link> </ItemDefinitionGroup> @@ -192,6 +192,7 @@ </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="..\mupdfwinrt\status.h" /> + <ClInclude Include="PrintPage.h" /> <ClInclude Include="DocumentPage.h" /> <ClInclude Include="pch.h" /> <ClInclude Include="App.xaml.h"> @@ -224,6 +225,7 @@ <ClCompile Include="App.xaml.cpp"> <DependentUpon>App.xaml</DependentUpon> </ClCompile> + <ClCompile Include="PrintPage.cpp" /> <ClCompile Include="DocumentPage.cpp" /> <ClCompile Include="MainPage.xaml.cpp"> <DependentUpon>MainPage.xaml</DependentUpon> diff --git a/platform/winrt/mupdf_cpp/mupdf_cpp.vcxproj.filters b/platform/winrt/mupdf_cpp/mupdf_cpp.vcxproj.filters index c1791b26..0a387642 100644 --- a/platform/winrt/mupdf_cpp/mupdf_cpp.vcxproj.filters +++ b/platform/winrt/mupdf_cpp/mupdf_cpp.vcxproj.filters @@ -20,6 +20,7 @@ <ClCompile Include="pch.cpp" /> <ClCompile Include="DocumentPage.cpp" /> <ClCompile Include="RectList.cpp" /> + <ClCompile Include="PrintPage.cpp" /> </ItemGroup> <ItemGroup> <ClInclude Include="pch.h" /> @@ -28,6 +29,7 @@ <ClInclude Include="DocumentPage.h" /> <ClInclude Include="RectList.h" /> <ClInclude Include="..\mupdfwinrt\status.h" /> + <ClInclude Include="PrintPage.h" /> </ItemGroup> <ItemGroup> <AppxManifest Include="Package.appxmanifest" /> diff --git a/platform/winrt/mupdf_cpp/pch.h b/platform/winrt/mupdf_cpp/pch.h index fadf910d..b23c6b70 100644 --- a/platform/winrt/mupdf_cpp/pch.h +++ b/platform/winrt/mupdf_cpp/pch.h @@ -7,3 +7,12 @@ #include <collection.h> #include "App.xaml.h" +/* DirectX requirements */ +#include <wrl.h> +#include <wrl\client.h> +#include <dxgi.h> +#include <dxgi1_2.h> +#include <D2d1_1.h> +#include <D3D11.h> +#include <PrintPreview.h> +#include <Wincodec.h> diff --git a/platform/winrt/mupdfwinrt/muctx.cpp b/platform/winrt/mupdfwinrt/muctx.cpp index 98aa7fc6..73c44b05 100644 --- a/platform/winrt/mupdfwinrt/muctx.cpp +++ b/platform/winrt/mupdfwinrt/muctx.cpp @@ -434,7 +434,8 @@ fz_display_list * muctx::CreateDisplayList(int page_num, int *width, int *height /* Render display list bmp_data buffer. No lock needed for this operation */ status_t muctx::RenderPageMT(void *dlist, int page_width, int page_height, - unsigned char *bmp_data, int bmp_width, int bmp_height) + unsigned char *bmp_data, int bmp_width, int bmp_height, + bool flipy) { fz_device *dev = NULL; fz_pixmap *pix = NULL; @@ -453,8 +454,11 @@ status_t muctx::RenderPageMT(void *dlist, int page_width, int page_height, /* Figure out scale factors so that we get the desired size */ pctm = fz_scale(pctm, (float) bmp_width / page_width, (float) bmp_height / page_height); /* Flip on Y */ - ctm.f = bmp_height; - ctm.d = -ctm.d; + if (flipy) + { + ctm.f = bmp_height; + ctm.d = -ctm.d; + } pix = fz_new_pixmap_with_data(ctx_clone, fz_device_bgr(ctx_clone), bmp_width, bmp_height, bmp_data); fz_clear_pixmap_with_value(ctx_clone, pix, 255); @@ -479,7 +483,7 @@ status_t muctx::RenderPageMT(void *dlist, int page_width, int page_height, /* Render page_num to size width by height into bmp_data buffer. Lock needed. */ status_t muctx::RenderPage(int page_num, unsigned char *bmp_data, int bmp_width, - int bmp_height) + int bmp_height, bool flipy) { fz_device *dev = NULL; fz_pixmap *pix = NULL; @@ -500,8 +504,11 @@ status_t muctx::RenderPage(int page_num, unsigned char *bmp_data, int bmp_width, pctm = fz_scale(pctm, (float) bmp_width / page_size.X, (float) bmp_height / page_size.Y); /* Flip on Y */ - ctm.f = bmp_height; - ctm.d = -ctm.d; + if (flipy) + { + ctm.f = bmp_height; + ctm.d = -ctm.d; + } pix = fz_new_pixmap_with_data(mu_ctx, fz_device_bgr(mu_ctx), bmp_width, bmp_height, bmp_data); fz_clear_pixmap_with_value(mu_ctx, pix, 255); diff --git a/platform/winrt/mupdfwinrt/muctx.h b/platform/winrt/mupdfwinrt/muctx.h index cfaa1cae..33f6acc5 100644 --- a/platform/winrt/mupdfwinrt/muctx.h +++ b/platform/winrt/mupdfwinrt/muctx.h @@ -86,9 +86,10 @@ public: int GetPageCount(); status_t InitializeContext(); status_t RenderPage(int page_num, unsigned char *bmp_data, int bmp_width, - int bmp_height); + int bmp_height, bool flipy); status_t RenderPageMT(void *dlist, int page_width, int page_height, - unsigned char *bmp_data, int bmp_width, int bmp_height); + unsigned char *bmp_data, int bmp_width, int bmp_height, + bool flipy); fz_display_list* CreateDisplayList(int page_num, int *width, int *height); Point MeasurePage(int page_num); Point MeasurePage(fz_page *page); diff --git a/platform/winrt/mupdfwinrt/mudocument.cpp b/platform/winrt/mupdfwinrt/mudocument.cpp index 7c90ba18..d4855dd9 100644 --- a/platform/winrt/mupdfwinrt/mudocument.cpp +++ b/platform/winrt/mupdfwinrt/mudocument.cpp @@ -167,6 +167,50 @@ Windows::Foundation::IAsyncOperationWithProgress<int, double>^ }); } +/* Pack the page into a bitmap. This is used in the DirectX code for printing + not in the xaml related code. */ +int mudocument::RenderPageBitmapSync(int page_num, int bmp_width, int bmp_height, + bool use_dlist, Array<unsigned char>^* bit_map) +{ + status_t code; + /* Allocate space for bmp */ + Array<unsigned char>^ bmp_data = + ref new Array<unsigned char>(bmp_height * 4 * bmp_width); + + if (use_dlist) + { + void *dlist; + int page_height; + int page_width; + + mutex_lock.lock(); + /* This lock will keep out issues in mupdf as well as race conditions + in the page cache */ + dlist = (void*) mu_object.CreateDisplayList(page_num, &page_width, + &page_height); + /* Rendering of display list can occur with other threads so unlock */ + mutex_lock.unlock(); + code = mu_object.RenderPageMT(dlist, page_width, page_height, + &(bmp_data[0]), bmp_width, bmp_height, + false); + } + else + { + /* Rendering in immediate mode. Keep lock in place */ + mutex_lock.lock(); + code = mu_object.RenderPage(page_num, &(bmp_data[0]), bmp_width, + bmp_height, false); + mutex_lock.unlock(); + } + if (code != S_ISOK) + { + throw ref new FailureException("Page Rendering Failed"); + } + + *bit_map = bmp_data; + return (int) code; +} + /* Pack the page into a bmp stream */ Windows::Foundation::IAsyncOperation<InMemoryRandomAccessStream^>^ mudocument::RenderPageAsync(int page_num, int bmp_width, int bmp_height, @@ -200,14 +244,15 @@ Windows::Foundation::IAsyncOperation<InMemoryRandomAccessStream^>^ /* Rendering of display list can occur with other threads so unlock */ mutex_lock.unlock(); code = mu_object.RenderPageMT(dlist, page_width, page_height, - &(bmp_data[0]), bmp_width, bmp_height); + &(bmp_data[0]), bmp_width, bmp_height, + true); } else { /* Rendering in immediate mode. Keep lock in place */ mutex_lock.lock(); code = mu_object.RenderPage(page_num, &(bmp_data[0]), bmp_width, - bmp_height); + bmp_height, true); mutex_lock.unlock(); } if (code != S_ISOK) diff --git a/platform/winrt/mupdfwinrt/mudocument.h b/platform/winrt/mupdfwinrt/mudocument.h index eebe1fbe..c3356337 100644 --- a/platform/winrt/mupdfwinrt/mudocument.h +++ b/platform/winrt/mupdfwinrt/mudocument.h @@ -34,6 +34,8 @@ namespace mupdfwinrt Point GetPageSize(int page_num); Windows::Foundation::IAsyncOperation<InMemoryRandomAccessStream^>^ RenderPageAsync(int page_num, int width, int height, bool use_dlist); + int RenderPageBitmapSync(int page_num, int bmp_width, int bmp_height, + bool use_dlist, Array<unsigned char>^* bit_map); Windows::Foundation::IAsyncOperationWithProgress<int, double>^ SearchDocumentWithProgressAsync(String^ textToFind, int dir, int start_page, int num_pages); |