1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include <windows.h>
8
9 #include <algorithm>
10 #include <memory>
11 #include <vector>
12
13 #include "core/fxcrt/fx_system.h"
14 #include "core/fxge/cfx_windowsdevice.h"
15 #include "core/fxge/dib/dib_int.h"
16 #include "core/fxge/fx_freetype.h"
17 #include "core/fxge/ge/fx_text_int.h"
18 #include "core/fxge/win32/cpsoutput.h"
19 #include "core/fxge/win32/win32_int.h"
20 #include "third_party/base/ptr_util.h"
21
22 #if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
23 namespace {
24
25 class ScopedState {
26 public:
ScopedState(HDC hDC,HFONT hFont)27 ScopedState(HDC hDC, HFONT hFont) : m_hDC(hDC) {
28 m_iState = SaveDC(m_hDC);
29 m_hFont = SelectObject(m_hDC, hFont);
30 }
31
~ScopedState()32 ~ScopedState() {
33 HGDIOBJ hFont = SelectObject(m_hDC, m_hFont);
34 DeleteObject(hFont);
35 RestoreDC(m_hDC, m_iState);
36 }
37
38 private:
39 HDC m_hDC;
40 HGDIOBJ m_hFont;
41 int m_iState;
42
43 ScopedState(const ScopedState&) = delete;
44 void operator=(const ScopedState&) = delete;
45 };
46
47 } // namespace
48
49 bool g_pdfium_print_text_with_gdi = false;
50
51 PDFiumEnsureTypefaceCharactersAccessible g_pdfium_typeface_accessible_func =
52 nullptr;
53 #endif
54
CGdiPrinterDriver(HDC hDC)55 CGdiPrinterDriver::CGdiPrinterDriver(HDC hDC)
56 : CGdiDeviceDriver(hDC, FXDC_PRINTER),
57 m_HorzSize(::GetDeviceCaps(m_hDC, HORZSIZE)),
58 m_VertSize(::GetDeviceCaps(m_hDC, VERTSIZE)) {}
59
~CGdiPrinterDriver()60 CGdiPrinterDriver::~CGdiPrinterDriver() {}
61
GetDeviceCaps(int caps_id) const62 int CGdiPrinterDriver::GetDeviceCaps(int caps_id) const {
63 if (caps_id == FXDC_HORZ_SIZE)
64 return m_HorzSize;
65 if (caps_id == FXDC_VERT_SIZE)
66 return m_VertSize;
67 return CGdiDeviceDriver::GetDeviceCaps(caps_id);
68 }
69
SetDIBits(const CFX_DIBSource * pSource,uint32_t color,const FX_RECT * pSrcRect,int left,int top,int blend_type)70 bool CGdiPrinterDriver::SetDIBits(const CFX_DIBSource* pSource,
71 uint32_t color,
72 const FX_RECT* pSrcRect,
73 int left,
74 int top,
75 int blend_type) {
76 if (pSource->IsAlphaMask()) {
77 FX_RECT clip_rect(left, top, left + pSrcRect->Width(),
78 top + pSrcRect->Height());
79 return StretchDIBits(pSource, color, left - pSrcRect->left,
80 top - pSrcRect->top, pSource->GetWidth(),
81 pSource->GetHeight(), &clip_rect, 0,
82 FXDIB_BLEND_NORMAL);
83 }
84 ASSERT(pSource && !pSource->IsAlphaMask() && pSrcRect);
85 ASSERT(blend_type == FXDIB_BLEND_NORMAL);
86 if (pSource->HasAlpha())
87 return false;
88
89 CFX_DIBExtractor temp(pSource);
90 CFX_DIBitmap* pBitmap = temp.GetBitmap();
91 if (!pBitmap)
92 return false;
93
94 return GDI_SetDIBits(pBitmap, pSrcRect, left, top);
95 }
96
StretchDIBits(const CFX_DIBSource * pSource,uint32_t color,int dest_left,int dest_top,int dest_width,int dest_height,const FX_RECT * pClipRect,uint32_t flags,int blend_type)97 bool CGdiPrinterDriver::StretchDIBits(const CFX_DIBSource* pSource,
98 uint32_t color,
99 int dest_left,
100 int dest_top,
101 int dest_width,
102 int dest_height,
103 const FX_RECT* pClipRect,
104 uint32_t flags,
105 int blend_type) {
106 if (pSource->IsAlphaMask()) {
107 int alpha = FXARGB_A(color);
108 if (pSource->GetBPP() != 1 || alpha != 255)
109 return false;
110
111 if (dest_width < 0 || dest_height < 0) {
112 std::unique_ptr<CFX_DIBitmap> pFlipped =
113 pSource->FlipImage(dest_width < 0, dest_height < 0);
114 if (!pFlipped)
115 return false;
116
117 if (dest_width < 0)
118 dest_left += dest_width;
119 if (dest_height < 0)
120 dest_top += dest_height;
121
122 return GDI_StretchBitMask(pFlipped.get(), dest_left, dest_top,
123 abs(dest_width), abs(dest_height), color,
124 flags);
125 }
126
127 CFX_DIBExtractor temp(pSource);
128 CFX_DIBitmap* pBitmap = temp.GetBitmap();
129 if (!pBitmap)
130 return false;
131 return GDI_StretchBitMask(pBitmap, dest_left, dest_top, dest_width,
132 dest_height, color, flags);
133 }
134
135 if (pSource->HasAlpha())
136 return false;
137
138 if (dest_width < 0 || dest_height < 0) {
139 std::unique_ptr<CFX_DIBitmap> pFlipped =
140 pSource->FlipImage(dest_width < 0, dest_height < 0);
141 if (!pFlipped)
142 return false;
143
144 if (dest_width < 0)
145 dest_left += dest_width;
146 if (dest_height < 0)
147 dest_top += dest_height;
148
149 return GDI_StretchDIBits(pFlipped.get(), dest_left, dest_top,
150 abs(dest_width), abs(dest_height), flags);
151 }
152
153 CFX_DIBExtractor temp(pSource);
154 CFX_DIBitmap* pBitmap = temp.GetBitmap();
155 if (!pBitmap)
156 return false;
157 return GDI_StretchDIBits(pBitmap, dest_left, dest_top, dest_width,
158 dest_height, flags);
159 }
160
StartDIBits(const CFX_DIBSource * pSource,int bitmap_alpha,uint32_t color,const CFX_Matrix * pMatrix,uint32_t render_flags,void * & handle,int blend_type)161 bool CGdiPrinterDriver::StartDIBits(const CFX_DIBSource* pSource,
162 int bitmap_alpha,
163 uint32_t color,
164 const CFX_Matrix* pMatrix,
165 uint32_t render_flags,
166 void*& handle,
167 int blend_type) {
168 if (bitmap_alpha < 255 || pSource->HasAlpha() ||
169 (pSource->IsAlphaMask() && (pSource->GetBPP() != 1))) {
170 return false;
171 }
172 CFX_FloatRect unit_rect = pMatrix->GetUnitRect();
173 FX_RECT full_rect = unit_rect.GetOuterRect();
174 if (FXSYS_fabs(pMatrix->b) < 0.5f && pMatrix->a != 0 &&
175 FXSYS_fabs(pMatrix->c) < 0.5f && pMatrix->d != 0) {
176 bool bFlipX = pMatrix->a < 0;
177 bool bFlipY = pMatrix->d > 0;
178 return StretchDIBits(pSource, color,
179 bFlipX ? full_rect.right : full_rect.left,
180 bFlipY ? full_rect.bottom : full_rect.top,
181 bFlipX ? -full_rect.Width() : full_rect.Width(),
182 bFlipY ? -full_rect.Height() : full_rect.Height(),
183 nullptr, 0, blend_type);
184 }
185 if (FXSYS_fabs(pMatrix->a) >= 0.5f || FXSYS_fabs(pMatrix->d) >= 0.5f)
186 return false;
187
188 std::unique_ptr<CFX_DIBitmap> pTransformed =
189 pSource->SwapXY(pMatrix->c > 0, pMatrix->b < 0);
190 if (!pTransformed)
191 return false;
192
193 return StretchDIBits(pTransformed.get(), color, full_rect.left, full_rect.top,
194 full_rect.Width(), full_rect.Height(), nullptr, 0,
195 blend_type);
196 }
197
DrawDeviceText(int nChars,const FXTEXT_CHARPOS * pCharPos,CFX_Font * pFont,const CFX_Matrix * pObject2Device,FX_FLOAT font_size,uint32_t color)198 bool CGdiPrinterDriver::DrawDeviceText(int nChars,
199 const FXTEXT_CHARPOS* pCharPos,
200 CFX_Font* pFont,
201 const CFX_Matrix* pObject2Device,
202 FX_FLOAT font_size,
203 uint32_t color) {
204 #if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
205 if (!g_pdfium_print_text_with_gdi)
206 return false;
207
208 if (nChars < 1 || !pFont || !pFont->IsEmbedded() || !pFont->IsTTFont())
209 return false;
210
211 // Scale factor used to minimize the kerning problems caused by rounding
212 // errors below. Value choosen based on the title of https://crbug.com/18383
213 const double kScaleFactor = 10;
214
215 // Font
216 //
217 // Note that |pFont| has the actual font to render with embedded within, but
218 // but unfortunately AddFontMemResourceEx() does not seem to cooperate.
219 // Loading font data to memory seems to work, but then enumerating the fonts
220 // fails to find it. This requires more investigation. In the meanwhile,
221 // assume the printing is happening on the machine that generated the PDF, so
222 // the embedded font, if not a web font, is available through GDI anyway.
223 // TODO(thestig): Figure out why AddFontMemResourceEx() does not work.
224 // Generalize this method to work for all PDFs with embedded fonts.
225 // In sandboxed environments, font loading may not work at all, so this may be
226 // the best possible effort.
227 LOGFONT lf = {};
228 lf.lfHeight = -font_size * kScaleFactor;
229 lf.lfWeight = pFont->IsBold() ? FW_BOLD : FW_NORMAL;
230 lf.lfItalic = pFont->IsItalic();
231 lf.lfCharSet = DEFAULT_CHARSET;
232
233 const CFX_WideString wsName = pFont->GetFaceName().UTF8Decode();
234 int iNameLen = std::min(wsName.GetLength(), LF_FACESIZE - 1);
235 memcpy(lf.lfFaceName, wsName.c_str(), sizeof(lf.lfFaceName[0]) * iNameLen);
236 lf.lfFaceName[iNameLen] = 0;
237
238 HFONT hFont = CreateFontIndirect(&lf);
239 if (!hFont)
240 return false;
241
242 ScopedState state(m_hDC, hFont);
243 size_t nTextMetricSize = GetOutlineTextMetrics(m_hDC, 0, nullptr);
244 if (nTextMetricSize == 0) {
245 // Give up and fail if there is no way to get the font to try again.
246 if (!g_pdfium_typeface_accessible_func)
247 return false;
248
249 // Try to get the font. Any letter will do.
250 g_pdfium_typeface_accessible_func(&lf, L"A", 1);
251 nTextMetricSize = GetOutlineTextMetrics(m_hDC, 0, nullptr);
252 if (nTextMetricSize == 0)
253 return false;
254 }
255
256 std::vector<BYTE> buf(nTextMetricSize);
257 OUTLINETEXTMETRIC* pTextMetric =
258 reinterpret_cast<OUTLINETEXTMETRIC*>(buf.data());
259 if (GetOutlineTextMetrics(m_hDC, nTextMetricSize, pTextMetric) == 0)
260 return false;
261
262 // If the selected font is not the requested font, then bail out. This can
263 // happen with web fonts, for example.
264 wchar_t* wsSelectedName = reinterpret_cast<wchar_t*>(
265 buf.data() + reinterpret_cast<size_t>(pTextMetric->otmpFaceName));
266 if (wsName != wsSelectedName)
267 return false;
268
269 // Transforms
270 SetGraphicsMode(m_hDC, GM_ADVANCED);
271 XFORM xform;
272 xform.eM11 = pObject2Device->a / kScaleFactor;
273 xform.eM12 = pObject2Device->b / kScaleFactor;
274 xform.eM21 = -pObject2Device->c / kScaleFactor;
275 xform.eM22 = -pObject2Device->d / kScaleFactor;
276 xform.eDx = pObject2Device->e;
277 xform.eDy = pObject2Device->f;
278 ModifyWorldTransform(m_hDC, &xform, MWT_LEFTMULTIPLY);
279
280 // Color
281 int iUnusedAlpha;
282 FX_COLORREF rgb;
283 ArgbDecode(color, iUnusedAlpha, rgb);
284 SetTextColor(m_hDC, rgb);
285 SetBkMode(m_hDC, TRANSPARENT);
286
287 // Text
288 CFX_WideString wsText;
289 std::vector<INT> spacing(nChars);
290 FX_FLOAT fPreviousOriginX = 0;
291 for (int i = 0; i < nChars; ++i) {
292 // Only works with PDFs from Skia's PDF generator. Cannot handle arbitrary
293 // values from PDFs.
294 const FXTEXT_CHARPOS& charpos = pCharPos[i];
295 ASSERT(charpos.m_AdjustMatrix[0] == 0);
296 ASSERT(charpos.m_AdjustMatrix[1] == 0);
297 ASSERT(charpos.m_AdjustMatrix[2] == 0);
298 ASSERT(charpos.m_AdjustMatrix[3] == 0);
299 ASSERT(charpos.m_Origin.y == 0);
300
301 // Round the spacing to the nearest integer, but keep track of the rounding
302 // error for calculating the next spacing value.
303 FX_FLOAT fOriginX = charpos.m_Origin.x * kScaleFactor;
304 FX_FLOAT fPixelSpacing = fOriginX - fPreviousOriginX;
305 spacing[i] = FXSYS_round(fPixelSpacing);
306 fPreviousOriginX = fOriginX - (fPixelSpacing - spacing[i]);
307
308 wsText += charpos.m_GlyphIndex;
309 }
310
311 // Draw
312 SetTextAlign(m_hDC, TA_LEFT | TA_BASELINE);
313 if (ExtTextOutW(m_hDC, 0, 0, ETO_GLYPH_INDEX, nullptr, wsText.c_str(), nChars,
314 nChars > 1 ? &spacing[1] : nullptr)) {
315 return true;
316 }
317
318 // Give up and fail if there is no way to get the font to try again.
319 if (!g_pdfium_typeface_accessible_func)
320 return false;
321
322 // Try to get the font and draw again.
323 g_pdfium_typeface_accessible_func(&lf, wsText.c_str(), nChars);
324 return !!ExtTextOutW(m_hDC, 0, 0, ETO_GLYPH_INDEX, nullptr, wsText.c_str(),
325 nChars, nChars > 1 ? &spacing[1] : nullptr);
326 #else
327 return false;
328 #endif
329 }
330
CPSPrinterDriver(HDC hDC,int pslevel,bool bCmykOutput)331 CPSPrinterDriver::CPSPrinterDriver(HDC hDC, int pslevel, bool bCmykOutput)
332 : m_hDC(hDC), m_bCmykOutput(bCmykOutput) {
333 m_HorzSize = ::GetDeviceCaps(m_hDC, HORZSIZE);
334 m_VertSize = ::GetDeviceCaps(m_hDC, VERTSIZE);
335 m_Width = ::GetDeviceCaps(m_hDC, HORZRES);
336 m_Height = ::GetDeviceCaps(m_hDC, VERTRES);
337 m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL);
338 m_pPSOutput = pdfium::MakeUnique<CPSOutput>(m_hDC);
339 m_PSRenderer.Init(m_pPSOutput.get(), pslevel, m_Width, m_Height, bCmykOutput);
340 HRGN hRgn = ::CreateRectRgn(0, 0, 1, 1);
341 int ret = ::GetClipRgn(hDC, hRgn);
342 if (ret == 1) {
343 ret = ::GetRegionData(hRgn, 0, NULL);
344 if (ret) {
345 RGNDATA* pData = reinterpret_cast<RGNDATA*>(FX_Alloc(uint8_t, ret));
346 ret = ::GetRegionData(hRgn, ret, pData);
347 if (ret) {
348 CFX_PathData path;
349 for (uint32_t i = 0; i < pData->rdh.nCount; i++) {
350 RECT* pRect =
351 reinterpret_cast<RECT*>(pData->Buffer + pData->rdh.nRgnSize * i);
352 path.AppendRect(static_cast<FX_FLOAT>(pRect->left),
353 static_cast<FX_FLOAT>(pRect->bottom),
354 static_cast<FX_FLOAT>(pRect->right),
355 static_cast<FX_FLOAT>(pRect->top));
356 }
357 m_PSRenderer.SetClip_PathFill(&path, nullptr, FXFILL_WINDING);
358 }
359 FX_Free(pData);
360 }
361 }
362 ::DeleteObject(hRgn);
363 }
364
~CPSPrinterDriver()365 CPSPrinterDriver::~CPSPrinterDriver() {
366 EndRendering();
367 }
368
GetDeviceCaps(int caps_id) const369 int CPSPrinterDriver::GetDeviceCaps(int caps_id) const {
370 switch (caps_id) {
371 case FXDC_DEVICE_CLASS:
372 return FXDC_PRINTER;
373 case FXDC_PIXEL_WIDTH:
374 return m_Width;
375 case FXDC_PIXEL_HEIGHT:
376 return m_Height;
377 case FXDC_BITS_PIXEL:
378 return m_nBitsPerPixel;
379 case FXDC_RENDER_CAPS:
380 return m_bCmykOutput ? FXRC_BIT_MASK | FXRC_CMYK_OUTPUT : FXRC_BIT_MASK;
381 case FXDC_HORZ_SIZE:
382 return m_HorzSize;
383 case FXDC_VERT_SIZE:
384 return m_VertSize;
385 }
386 return 0;
387 }
388
StartRendering()389 bool CPSPrinterDriver::StartRendering() {
390 return m_PSRenderer.StartRendering();
391 }
392
EndRendering()393 void CPSPrinterDriver::EndRendering() {
394 m_PSRenderer.EndRendering();
395 }
396
SaveState()397 void CPSPrinterDriver::SaveState() {
398 m_PSRenderer.SaveState();
399 }
400
RestoreState(bool bKeepSaved)401 void CPSPrinterDriver::RestoreState(bool bKeepSaved) {
402 m_PSRenderer.RestoreState(bKeepSaved);
403 }
404
SetClip_PathFill(const CFX_PathData * pPathData,const CFX_Matrix * pObject2Device,int fill_mode)405 bool CPSPrinterDriver::SetClip_PathFill(const CFX_PathData* pPathData,
406 const CFX_Matrix* pObject2Device,
407 int fill_mode) {
408 m_PSRenderer.SetClip_PathFill(pPathData, pObject2Device, fill_mode);
409 return true;
410 }
411
SetClip_PathStroke(const CFX_PathData * pPathData,const CFX_Matrix * pObject2Device,const CFX_GraphStateData * pGraphState)412 bool CPSPrinterDriver::SetClip_PathStroke(
413 const CFX_PathData* pPathData,
414 const CFX_Matrix* pObject2Device,
415 const CFX_GraphStateData* pGraphState) {
416 m_PSRenderer.SetClip_PathStroke(pPathData, pObject2Device, pGraphState);
417 return true;
418 }
419
DrawPath(const CFX_PathData * pPathData,const CFX_Matrix * pObject2Device,const CFX_GraphStateData * pGraphState,FX_ARGB fill_color,FX_ARGB stroke_color,int fill_mode,int blend_type)420 bool CPSPrinterDriver::DrawPath(const CFX_PathData* pPathData,
421 const CFX_Matrix* pObject2Device,
422 const CFX_GraphStateData* pGraphState,
423 FX_ARGB fill_color,
424 FX_ARGB stroke_color,
425 int fill_mode,
426 int blend_type) {
427 if (blend_type != FXDIB_BLEND_NORMAL) {
428 return false;
429 }
430 return m_PSRenderer.DrawPath(pPathData, pObject2Device, pGraphState,
431 fill_color, stroke_color, fill_mode & 3);
432 }
433
GetClipBox(FX_RECT * pRect)434 bool CPSPrinterDriver::GetClipBox(FX_RECT* pRect) {
435 *pRect = m_PSRenderer.GetClipBox();
436 return true;
437 }
438
SetDIBits(const CFX_DIBSource * pBitmap,uint32_t color,const FX_RECT * pSrcRect,int left,int top,int blend_type)439 bool CPSPrinterDriver::SetDIBits(const CFX_DIBSource* pBitmap,
440 uint32_t color,
441 const FX_RECT* pSrcRect,
442 int left,
443 int top,
444 int blend_type) {
445 if (blend_type != FXDIB_BLEND_NORMAL)
446 return false;
447 return m_PSRenderer.SetDIBits(pBitmap, color, left, top);
448 }
449
StretchDIBits(const CFX_DIBSource * pBitmap,uint32_t color,int dest_left,int dest_top,int dest_width,int dest_height,const FX_RECT * pClipRect,uint32_t flags,int blend_type)450 bool CPSPrinterDriver::StretchDIBits(const CFX_DIBSource* pBitmap,
451 uint32_t color,
452 int dest_left,
453 int dest_top,
454 int dest_width,
455 int dest_height,
456 const FX_RECT* pClipRect,
457 uint32_t flags,
458 int blend_type) {
459 if (blend_type != FXDIB_BLEND_NORMAL)
460 return false;
461 return m_PSRenderer.StretchDIBits(pBitmap, color, dest_left, dest_top,
462 dest_width, dest_height, flags);
463 }
464
StartDIBits(const CFX_DIBSource * pBitmap,int bitmap_alpha,uint32_t color,const CFX_Matrix * pMatrix,uint32_t render_flags,void * & handle,int blend_type)465 bool CPSPrinterDriver::StartDIBits(const CFX_DIBSource* pBitmap,
466 int bitmap_alpha,
467 uint32_t color,
468 const CFX_Matrix* pMatrix,
469 uint32_t render_flags,
470 void*& handle,
471 int blend_type) {
472 if (blend_type != FXDIB_BLEND_NORMAL)
473 return false;
474
475 if (bitmap_alpha < 255)
476 return false;
477
478 handle = nullptr;
479 return m_PSRenderer.DrawDIBits(pBitmap, color, pMatrix, render_flags);
480 }
481
DrawDeviceText(int nChars,const FXTEXT_CHARPOS * pCharPos,CFX_Font * pFont,const CFX_Matrix * pObject2Device,FX_FLOAT font_size,uint32_t color)482 bool CPSPrinterDriver::DrawDeviceText(int nChars,
483 const FXTEXT_CHARPOS* pCharPos,
484 CFX_Font* pFont,
485 const CFX_Matrix* pObject2Device,
486 FX_FLOAT font_size,
487 uint32_t color) {
488 return m_PSRenderer.DrawText(nChars, pCharPos, pFont, pObject2Device,
489 font_size, color);
490 }
491
GetPlatformSurface() const492 void* CPSPrinterDriver::GetPlatformSurface() const {
493 return m_hDC;
494 }
495