diff options
-rw-r--r-- | core/fxge/include/fx_ge_win32.h | 10 | ||||
-rw-r--r-- | core/fxge/win32/fx_win32_print.cpp | 172 | ||||
-rw-r--r-- | core/fxge/win32/win32_int.h | 7 | ||||
-rw-r--r-- | fpdfsdk/fpdfview.cpp | 11 | ||||
-rw-r--r-- | public/fpdfview.h | 28 |
5 files changed, 220 insertions, 8 deletions
diff --git a/core/fxge/include/fx_ge_win32.h b/core/fxge/include/fx_ge_win32.h index c0889c1852..5b57863ab5 100644 --- a/core/fxge/include/fx_ge_win32.h +++ b/core/fxge/include/fx_ge_win32.h @@ -16,6 +16,16 @@ class IFX_RenderDeviceDriver; +#if defined(PDFIUM_PRINT_TEXT_WITH_GDI) +typedef void (*PDFiumEnsureTypefaceCharactersAccessible)(const LOGFONT* font, + const wchar_t* text, + size_t text_length); + +extern bool g_pdfium_print_text_with_gdi; +extern PDFiumEnsureTypefaceCharactersAccessible + g_pdfium_typeface_accessible_func; +#endif + typedef struct WINDIB_Open_Args_ { int flags; diff --git a/core/fxge/win32/fx_win32_print.cpp b/core/fxge/win32/fx_win32_print.cpp index aef6062e73..e1071e79f0 100644 --- a/core/fxge/win32/fx_win32_print.cpp +++ b/core/fxge/win32/fx_win32_print.cpp @@ -10,12 +10,47 @@ #include <windows.h> +#include <algorithm> + #include "core/fxge/dib/dib_int.h" #include "core/fxge/ge/fx_text_int.h" #include "core/fxge/include/fx_freetype.h" #include "core/fxge/include/fx_ge_win32.h" #include "core/fxge/win32/win32_int.h" +#if defined(PDFIUM_PRINT_TEXT_WITH_GDI) +namespace { + +class ScopedState { + public: + ScopedState(HDC hDC, HFONT hFont) : m_hDC(hDC) { + m_iState = SaveDC(m_hDC); + m_hFont = SelectObject(m_hDC, hFont); + } + + ~ScopedState() { + HGDIOBJ hFont = SelectObject(m_hDC, m_hFont); + DeleteObject(hFont); + RestoreDC(m_hDC, m_iState); + } + + private: + HDC m_hDC; + HGDIOBJ m_hFont; + int m_iState; + + ScopedState(const ScopedState&) = delete; + void operator=(const ScopedState&) = delete; +}; + +} // namespace + +bool g_pdfium_print_text_with_gdi = false; + +PDFiumEnsureTypefaceCharactersAccessible g_pdfium_typeface_accessible_func = + nullptr; +#endif + CGdiPrinterDriver::CGdiPrinterDriver(HDC hDC) : CGdiDeviceDriver(hDC, FXDC_PRINTER), m_HorzSize(::GetDeviceCaps(m_hDC, HORZSIZE)), @@ -146,17 +181,138 @@ FX_BOOL CGdiPrinterDriver::StartDIBits(const CFX_DIBSource* pSource, bFlipY ? -full_rect.Height() : full_rect.Height(), nullptr, 0, blend_type); } - if (FXSYS_fabs(pMatrix->a) < 0.5f && FXSYS_fabs(pMatrix->d) < 0.5f) { - std::unique_ptr<CFX_DIBitmap> pTransformed( - pSource->SwapXY(pMatrix->c > 0, pMatrix->b < 0)); - if (!pTransformed) + if (FXSYS_fabs(pMatrix->a) >= 0.5f || FXSYS_fabs(pMatrix->d) >= 0.5f) + return FALSE; + + std::unique_ptr<CFX_DIBitmap> pTransformed( + pSource->SwapXY(pMatrix->c > 0, pMatrix->b < 0)); + if (!pTransformed) + return FALSE; + + return StretchDIBits(pTransformed.get(), color, full_rect.left, full_rect.top, + full_rect.Width(), full_rect.Height(), nullptr, 0, + blend_type); +} + +FX_BOOL CGdiPrinterDriver::DrawDeviceText(int nChars, + const FXTEXT_CHARPOS* pCharPos, + CFX_Font* pFont, + CFX_FontCache* pCache, + const CFX_Matrix* pObject2Device, + FX_FLOAT font_size, + uint32_t color) { +#if defined(PDFIUM_PRINT_TEXT_WITH_GDI) + if (!g_pdfium_print_text_with_gdi) + return FALSE; + + if (nChars < 1 || !pFont || !pFont->IsEmbedded() || !pFont->IsTTFont()) + return FALSE; + + // Font + // + // Note that |pFont| has the actual font to render with embedded within, but + // but unfortunately AddFontMemResourceEx() does not seem to cooperate. + // Loading font data to memory seems to work, but then enumerating the fonts + // fails to find it. This requires more investigation. In the meanwhile, + // assume the printing is happening on the machine that generated the PDF, so + // the embedded font, if not a web font, is available through GDI anyway. + // TODO(thestig): Figure out why AddFontMemResourceEx() does not work. + // Generalize this method to work for all PDFs with embedded fonts. + // In sandboxed environments, font loading may not work at all, so this may be + // the best possible effort. + LOGFONT lf = {}; + lf.lfHeight = -font_size; + lf.lfWeight = pFont->IsBold() ? FW_BOLD : FW_NORMAL; + lf.lfItalic = pFont->IsItalic(); + lf.lfCharSet = DEFAULT_CHARSET; + + const CFX_WideString wsName = pFont->GetFaceName().UTF8Decode(); + int iNameLen = std::min(wsName.GetLength(), LF_FACESIZE - 1); + memcpy(lf.lfFaceName, wsName.c_str(), sizeof(lf.lfFaceName[0]) * iNameLen); + lf.lfFaceName[iNameLen] = 0; + + HFONT hFont = CreateFontIndirect(&lf); + if (!hFont) + return FALSE; + + ScopedState state(m_hDC, hFont); + size_t nTextMetricSize = GetOutlineTextMetrics(m_hDC, 0, nullptr); + if (nTextMetricSize == 0) { + // Give up and fail if there is no way to get the font to try again. + if (!g_pdfium_typeface_accessible_func) return FALSE; - return StretchDIBits(pTransformed.get(), color, full_rect.left, - full_rect.top, full_rect.Width(), full_rect.Height(), - nullptr, 0, blend_type); + // Try to get the font. Any letter will do. + g_pdfium_typeface_accessible_func(&lf, L"A", 1); + nTextMetricSize = GetOutlineTextMetrics(m_hDC, 0, nullptr); + if (nTextMetricSize == 0) + return FALSE; } + + std::vector<BYTE> buf(nTextMetricSize); + OUTLINETEXTMETRIC* pTextMetric = + reinterpret_cast<OUTLINETEXTMETRIC*>(buf.data()); + if (GetOutlineTextMetrics(m_hDC, nTextMetricSize, pTextMetric) == 0) + return FALSE; + + // If the selected font is not the requested font, then bail out. This can + // happen with web fonts, for example. + wchar_t* wsSelectedName = reinterpret_cast<wchar_t*>( + buf.data() + reinterpret_cast<size_t>(pTextMetric->otmpFaceName)); + if (wsName != wsSelectedName) + return FALSE; + + // Transforms + SetGraphicsMode(m_hDC, GM_ADVANCED); + XFORM xform; + xform.eM11 = pObject2Device->GetA(); + xform.eM12 = pObject2Device->GetB(); + xform.eM21 = -pObject2Device->GetC(); + xform.eM22 = -pObject2Device->GetD(); + xform.eDx = pObject2Device->GetE(); + xform.eDy = pObject2Device->GetF(); + ModifyWorldTransform(m_hDC, &xform, MWT_LEFTMULTIPLY); + + // Color + int iUnusedAlpha; + FX_COLORREF rgb; + ArgbDecode(color, iUnusedAlpha, rgb); + SetTextColor(m_hDC, rgb); + SetBkMode(m_hDC, TRANSPARENT); + + // Text + CFX_WideString wsText; + for (int i = 0; i < nChars; ++i) { + // Only works with PDFs from Skia's PDF generator. Cannot handle arbitrary + // values from PDFs. + const FXTEXT_CHARPOS& charpos = pCharPos[i]; + ASSERT(charpos.m_OriginX == 0); + ASSERT(charpos.m_OriginY == 0); + ASSERT(charpos.m_AdjustMatrix[0] == 0); + ASSERT(charpos.m_AdjustMatrix[1] == 0); + ASSERT(charpos.m_AdjustMatrix[2] == 0); + ASSERT(charpos.m_AdjustMatrix[3] == 0); + wsText += charpos.m_GlyphIndex; + } + + // Draw + SetTextAlign(m_hDC, TA_LEFT | TA_BASELINE); + if (ExtTextOutW(m_hDC, 0, 0, ETO_GLYPH_INDEX, nullptr, wsText.c_str(), nChars, + nullptr)) { + return TRUE; + } + + // Give up and fail if there is no way to get the font to try again. + if (!g_pdfium_typeface_accessible_func) + return FALSE; + + // Try to get the font and draw again. + g_pdfium_typeface_accessible_func(&lf, wsText.c_str(), nChars); + return ExtTextOutW(m_hDC, 0, 0, ETO_GLYPH_INDEX, nullptr, wsText.c_str(), + nChars, nullptr); +#else return FALSE; +#endif } -#endif +#endif // _FX_OS_ == _FX_WIN32_DESKTOP_ || _FX_OS_ == _FX_WIN64_DESKTOP_ diff --git a/core/fxge/win32/win32_int.h b/core/fxge/win32/win32_int.h index e844f5cf24..c7a7606155 100644 --- a/core/fxge/win32/win32_int.h +++ b/core/fxge/win32/win32_int.h @@ -239,6 +239,13 @@ class CGdiPrinterDriver : public CGdiDeviceDriver { uint32_t render_flags, void*& handle, int blend_type) override; + FX_BOOL DrawDeviceText(int nChars, + const FXTEXT_CHARPOS* pCharPos, + CFX_Font* pFont, + CFX_FontCache* pCache, + const CFX_Matrix* pObject2Device, + FX_FLOAT font_size, + uint32_t color) override; const int m_HorzSize; const int m_VertSize; diff --git a/fpdfsdk/fpdfview.cpp b/fpdfsdk/fpdfview.cpp index 646fb46f72..34e7d23619 100644 --- a/fpdfsdk/fpdfview.cpp +++ b/fpdfsdk/fpdfview.cpp @@ -334,6 +334,17 @@ DLLEXPORT void STDCALL FPDF_SetSandBoxPolicy(FPDF_DWORD policy, return FSDK_SetSandBoxPolicy(policy, enable); } +#if defined(_WIN32) && defined(PDFIUM_PRINT_TEXT_WITH_GDI) +DLLEXPORT void STDCALL +FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func) { + g_pdfium_typeface_accessible_func = func; +} + +DLLEXPORT void STDCALL FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi) { + g_pdfium_print_text_with_gdi = !!use_gdi; +} +#endif + DLLEXPORT FPDF_DOCUMENT STDCALL FPDF_LoadDocument(FPDF_STRING file_path, FPDF_BYTESTRING password) { // NOTE: the creation of the file needs to be by the embedder on the diff --git a/public/fpdfview.h b/public/fpdfview.h index cea431a498..cbb577b2fd 100644 --- a/public/fpdfview.h +++ b/public/fpdfview.h @@ -211,6 +211,34 @@ DLLEXPORT void STDCALL FPDF_DestroyLibrary(); DLLEXPORT void STDCALL FPDF_SetSandBoxPolicy(FPDF_DWORD policy, FPDF_BOOL enable); +#if defined(_WIN32) && defined(PDFIUM_PRINT_TEXT_WITH_GDI) +// Pointer to a helper function to make |font| with |text| of |text_length| +// accessible when printing text with GDI. This is useful in sandboxed +// environments where PDFium's access to GDI may be restricted. +typedef void (*PDFiumEnsureTypefaceCharactersAccessible)(const LOGFONT* font, + const wchar_t* text, + size_t text_length); + +// Function: FPDF_SetTypefaceAccessibleFunc +// Set the function pointer that makes GDI fonts available in sandboxed +// environments. Experimental API. +// Parameters: +// func - A function pointer. See description above. +// Return value: +// None. +DLLEXPORT void STDCALL +FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func); + +// Function: FPDF_SetPrintTextWithGDI +// Set whether to use GDI to draw fonts when printing on Windows. +// Experimental API. +// Parameters: +// use_gdi - Set to true to enable printing text with GDI. +// Return value: +// None. +DLLEXPORT void STDCALL FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi); +#endif + // Function: FPDF_LoadDocument // Open and load a PDF document. // Parameters: |