From fdb35ffb8d4bd283dd8f5905936f5c400fea3394 Mon Sep 17 00:00:00 2001 From: thestig Date: Mon, 18 Jul 2016 13:45:44 -0700 Subject: Implement CGdiPrinterDriver::DrawDeviceText(). This is sufficient to print text with GDI for PDFs generated by Chromium and cannot print any arbitrary PDF. Text that cannot be printed will be drawn as glyphs as before. BUG=409472 Review-Url: https://codereview.chromium.org/2113563003 --- core/fxge/win32/fx_win32_print.cpp | 172 +++++++++++++++++++++++++++++++++++-- core/fxge/win32/win32_int.h | 7 ++ 2 files changed, 171 insertions(+), 8 deletions(-) (limited to 'core/fxge/win32') 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 +#include + #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 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 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 buf(nTextMetricSize); + OUTLINETEXTMETRIC* pTextMetric = + reinterpret_cast(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( + buf.data() + reinterpret_cast(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; -- cgit v1.2.3