123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- /*
- * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include "print_job.h"
- #include "printing.h"
- #include <fpdfview.h>
- #include <objbase.h>
- #include <shlobj.h>
- #include <shlwapi.h>
- #include <tchar.h>
- #include <codecvt>
- #include <fstream>
- #include <iterator>
- #include <numeric>
- namespace nfet {
- const auto pdfDpi = 72;
- std::string toUtf8(std::wstring wstr) {
- int cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr,
- 0, nullptr, nullptr);
- LPSTR lpMultiByteStr = (LPSTR)malloc(cbMultiByte);
- cbMultiByte =
- WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, lpMultiByteStr,
- cbMultiByte, nullptr, nullptr);
- std::string ret = lpMultiByteStr;
- free(lpMultiByteStr);
- return ret;
- }
- std::string toUtf8(TCHAR* tstr) {
- #ifndef UNICODE
- #error "Non unicode build not supported"
- #endif
- if (!tstr) {
- return std::string{};
- }
- return toUtf8(std::wstring{tstr});
- }
- std::wstring fromUtf8(std::string str) {
- auto len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(),
- static_cast<int>(str.length()), nullptr, 0);
- if (len <= 0) {
- return L"";
- }
- auto wstr = std::wstring{};
- wstr.resize(len);
- MultiByteToWideChar(CP_UTF8, 0, str.c_str(), static_cast<int>(str.length()),
- &wstr[0], len);
- return wstr;
- }
- PrintJob::PrintJob(Printing* printing, int index)
- : printing{printing}, index{index} {}
- bool PrintJob::printPdf(const std::string& name,
- std::string printer,
- double width,
- double height,
- bool usePrinterSettings) {
- documentName = name;
- auto dm = static_cast<DEVMODE*>(GlobalAlloc(0, sizeof(DEVMODE)));
- if (usePrinterSettings) {
- dm = nullptr; // to use default driver config
- } else {
- ZeroMemory(dm, sizeof(DEVMODE));
- dm->dmSize = sizeof(DEVMODE);
- dm->dmFields =
- DM_ORIENTATION | DM_PAPERSIZE | DM_PAPERLENGTH | DM_PAPERWIDTH;
- dm->dmPaperSize = 0;
- if (width > height) {
- dm->dmOrientation = DMORIENT_LANDSCAPE;
- dm->dmPaperWidth = static_cast<short>(round(height * 254 / 72));
- dm->dmPaperLength = static_cast<short>(round(width * 254 / 72));
- } else {
- dm->dmOrientation = DMORIENT_PORTRAIT;
- dm->dmPaperWidth = static_cast<short>(round(width * 254 / 72));
- dm->dmPaperLength = static_cast<short>(round(height * 254 / 72));
- }
- }
- if (printer.empty()) {
- PRINTDLG pd;
- // Initialize PRINTDLG
- ZeroMemory(&pd, sizeof(pd));
- pd.lStructSize = sizeof(pd);
- // Initialize PRINTDLG
- pd.hwndOwner = nullptr;
- pd.hDevMode = dm;
- pd.hDevNames = nullptr; // Don't forget to free or store hDevNames.
- pd.hDC = nullptr;
- pd.Flags = PD_USEDEVMODECOPIES | PD_RETURNDC | PD_PRINTSETUP |
- PD_NOSELECTION | PD_NOPAGENUMS;
- pd.nCopies = 1;
- pd.nFromPage = 0xFFFF;
- pd.nToPage = 0xFFFF;
- pd.nMinPage = 1;
- pd.nMaxPage = 0xFFFF;
- auto r = PrintDlg(&pd);
- if (r != 1) {
- printing->onCompleted(this, false, "");
- DeleteDC(hDC);
- GlobalFree(hDevNames);
- ClosePrinter(hDevMode);
- return true;
- }
- hDC = pd.hDC;
- hDevMode = pd.hDevMode;
- hDevNames = pd.hDevNames;
- } else {
- hDC = CreateDC(TEXT("WINSPOOL"), fromUtf8(printer).c_str(), nullptr, dm);
- if (!hDC) {
- return false;
- }
- hDevMode = dm;
- hDevNames = nullptr;
- }
- auto dpiX = static_cast<double>(GetDeviceCaps(hDC, LOGPIXELSX)) / pdfDpi;
- auto dpiY = static_cast<double>(GetDeviceCaps(hDC, LOGPIXELSY)) / pdfDpi;
- auto pageWidth =
- static_cast<double>(GetDeviceCaps(hDC, PHYSICALWIDTH)) / dpiX;
- auto pageHeight =
- static_cast<double>(GetDeviceCaps(hDC, PHYSICALHEIGHT)) / dpiY;
- auto printableWidth = static_cast<double>(GetDeviceCaps(hDC, HORZRES)) / dpiX;
- auto printableHeight =
- static_cast<double>(GetDeviceCaps(hDC, VERTRES)) / dpiY;
- auto marginLeft =
- static_cast<double>(GetDeviceCaps(hDC, PHYSICALOFFSETX)) / dpiX;
- auto marginTop =
- static_cast<double>(GetDeviceCaps(hDC, PHYSICALOFFSETY)) / dpiY;
- auto marginRight = pageWidth - printableWidth - marginLeft;
- auto marginBottom = pageHeight - printableHeight - marginTop;
- printing->onLayout(this, pageWidth, pageHeight, marginLeft, marginTop,
- marginRight, marginBottom);
- return true;
- }
- std::vector<Printer> PrintJob::listPrinters() {
- LPTSTR defaultPrinter;
- DWORD size = 0;
- GetDefaultPrinter(nullptr, &size);
- defaultPrinter = static_cast<LPTSTR>(malloc(size * sizeof(TCHAR)));
- if (!GetDefaultPrinter(defaultPrinter, &size)) {
- size = 0;
- }
- auto printers = std::vector<Printer>{};
- DWORD needed = 0;
- DWORD returned = 0;
- const auto flags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
- EnumPrinters(flags, nullptr, 2, nullptr, 0, &needed, &returned);
- auto buffer = (PRINTER_INFO_2*)malloc(needed);
- if (!buffer) {
- return printers;
- }
- auto result = EnumPrinters(flags, nullptr, 2, (LPBYTE)buffer, needed, &needed,
- &returned);
- if (result == 0) {
- free(buffer);
- return printers;
- }
- for (DWORD i = 0; i < returned; i++) {
- printers.push_back(Printer{
- toUtf8(buffer[i].pPrinterName), toUtf8(buffer[i].pPrinterName),
- toUtf8(buffer[i].pDriverName), toUtf8(buffer[i].pLocation),
- toUtf8(buffer[i].pComment),
- size > 0 && _tcsncmp(buffer[i].pPrinterName, defaultPrinter, size) == 0,
- (buffer[i].Status &
- (PRINTER_STATUS_NOT_AVAILABLE | PRINTER_STATUS_ERROR |
- PRINTER_STATUS_OFFLINE | PRINTER_STATUS_PAUSED)) == 0});
- }
- free(buffer);
- free(defaultPrinter);
- return printers;
- }
- void PrintJob::writeJob(std::vector<uint8_t> data) {
- auto dpiX = static_cast<double>(GetDeviceCaps(hDC, LOGPIXELSX)) / pdfDpi;
- auto dpiY = static_cast<double>(GetDeviceCaps(hDC, LOGPIXELSY)) / pdfDpi;
- DOCINFO docInfo;
- ZeroMemory(&docInfo, sizeof(docInfo));
- docInfo.cbSize = sizeof(docInfo);
- auto docName = fromUtf8(documentName);
- docInfo.lpszDocName = docName.c_str();
- auto r = StartDoc(hDC, &docInfo);
- FPDF_LIBRARY_CONFIG config;
- config.version = 2;
- config.m_pUserFontPaths = nullptr;
- config.m_pIsolate = nullptr;
- config.m_v8EmbedderSlot = 0;
- FPDF_InitLibraryWithConfig(&config);
- auto doc = FPDF_LoadMemDocument64(data.data(), data.size(), nullptr);
- if (!doc) {
- FPDF_DestroyLibrary();
- return;
- }
- auto pages = FPDF_GetPageCount(doc);
- auto marginLeft = GetDeviceCaps(hDC, PHYSICALOFFSETX);
- auto marginTop = GetDeviceCaps(hDC, PHYSICALOFFSETY);
- for (auto pageNum = 0; pageNum < pages; pageNum++) {
- StartPage(hDC);
- auto page = FPDF_LoadPage(doc, pageNum);
- if (!page) {
- EndPage(hDC);
- continue;
- }
- auto pdfWidth = FPDF_GetPageWidth(page);
- auto pdfHeight = FPDF_GetPageHeight(page);
- int bWidth = static_cast<int>(pdfWidth * dpiX);
- int bHeight = static_cast<int>(pdfHeight * dpiY);
- FPDF_RenderPage(hDC, page, -marginLeft, -marginTop, bWidth, bHeight, 0,
- FPDF_ANNOT | FPDF_PRINTING);
- FPDF_ClosePage(page);
- r = EndPage(hDC);
- }
- FPDF_CloseDocument(doc);
- FPDF_DestroyLibrary();
- EndDoc(hDC);
- DeleteDC(hDC);
- GlobalFree(hDevNames);
- ClosePrinter(hDevMode);
- printing->onCompleted(this, true, "");
- }
- void PrintJob::cancelJob(const std::string& error) {}
- bool PrintJob::sharePdf(std::vector<uint8_t> data, const std::string& name) {
- TCHAR lpTempPathBuffer[MAX_PATH];
- auto ret = GetTempPath(MAX_PATH, lpTempPathBuffer);
- if (ret > MAX_PATH || (ret == 0)) {
- return false;
- }
- auto filename = fromUtf8(toUtf8(lpTempPathBuffer) + "\\" + name);
- auto output_file =
- std::basic_ofstream<uint8_t>{filename, std::ios::out | std::ios::binary};
- output_file.write(data.data(), data.size());
- output_file.close();
- SHELLEXECUTEINFO ShExecInfo;
- ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
- ShExecInfo.fMask = 0;
- ShExecInfo.hwnd = nullptr;
- ShExecInfo.lpVerb = TEXT("open");
- ShExecInfo.lpFile = filename.c_str();
- ShExecInfo.lpParameters = nullptr;
- ShExecInfo.lpDirectory = nullptr;
- ShExecInfo.nShow = SW_SHOWDEFAULT;
- ShExecInfo.hInstApp = nullptr;
- ret = ShellExecuteEx(&ShExecInfo);
- return ret == TRUE;
- }
- void PrintJob::pickPrinter(void* result) {}
- void PrintJob::rasterPdf(std::vector<uint8_t> data,
- std::vector<int> pages,
- double scale) {
- FPDF_LIBRARY_CONFIG config;
- config.version = 2;
- config.m_pUserFontPaths = nullptr;
- config.m_pIsolate = nullptr;
- config.m_v8EmbedderSlot = 0;
- FPDF_InitLibraryWithConfig(&config);
- auto doc = FPDF_LoadMemDocument64(data.data(), data.size(), nullptr);
- if (!doc) {
- FPDF_DestroyLibrary();
- printing->onPageRasterEnd(this, "Cannot raster a malformed PDF file");
- return;
- }
- auto pageCount = FPDF_GetPageCount(doc);
- if (pages.size() == 0) {
- // Use all pages
- pages.resize(pageCount);
- std::iota(std::begin(pages), std::end(pages), 0);
- }
- for (auto n : pages) {
- if (n >= pageCount) {
- continue;
- }
- auto page = FPDF_LoadPage(doc, n);
- if (!page) {
- continue;
- }
- auto width = FPDF_GetPageWidth(page);
- auto height = FPDF_GetPageHeight(page);
- auto bWidth = static_cast<int>(width * scale);
- auto bHeight = static_cast<int>(height * scale);
- auto bitmap = FPDFBitmap_Create(bWidth, bHeight, 0);
- FPDFBitmap_FillRect(bitmap, 0, 0, bWidth, bHeight, 0xffffffff);
- FPDF_RenderPageBitmap(bitmap, page, 0, 0, bWidth, bHeight, 0,
- FPDF_ANNOT | FPDF_LCD_TEXT);
- uint8_t* p = static_cast<uint8_t*>(FPDFBitmap_GetBuffer(bitmap));
- auto stride = FPDFBitmap_GetStride(bitmap);
- size_t l = static_cast<size_t>(bHeight * stride);
- // BGRA to RGBA conversion
- for (auto y = 0; y < bHeight; y++) {
- auto offset = y * stride;
- for (auto x = 0; x < bWidth; x++) {
- auto t = p[offset];
- p[offset] = p[offset + 2];
- p[offset + 2] = t;
- offset += 4;
- }
- }
- printing->onPageRasterized(std::vector<uint8_t>{p, p + l}, bWidth, bHeight,
- this);
- FPDFBitmap_Destroy(bitmap);
- FPDF_ClosePage(page);
- }
- FPDF_CloseDocument(doc);
- FPDF_DestroyLibrary();
- printing->onPageRasterEnd(this, "");
- }
- std::map<std::string, bool> PrintJob::printingInfo() {
- return std::map<std::string, bool>{
- {"directPrint", true}, {"dynamicLayout", true}, {"canPrint", true},
- {"canListPrinters", true}, {"canConvertHtml", false}, {"canShare", true},
- {"canRaster", true},
- };
- }
- } // namespace nfet
|