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_font.h" 15 #include "core/fxge/cfx_windowsrenderdevice.h" 16 #include "core/fxge/dib/cfx_dibextractor.h" 17 #include "core/fxge/dib/cfx_imagerenderer.h" 18 #include "core/fxge/dib/cstretchengine.h" 19 #include "core/fxge/fx_freetype.h" 20 #include "core/fxge/win32/cpsoutput.h" 21 #include "core/fxge/win32/win32_int.h" 22 #include "third_party/base/ptr_util.h" 23 24 #if defined(PDFIUM_PRINT_TEXT_WITH_GDI) 25 namespace { 26 27 class ScopedState { 28 public: 29 ScopedState(HDC hDC, HFONT hFont) : m_hDC(hDC) { 30 m_iState = SaveDC(m_hDC); 31 m_hFont = SelectObject(m_hDC, hFont); 32 } 33 34 ~ScopedState() { 35 HGDIOBJ hFont = SelectObject(m_hDC, m_hFont); 36 DeleteObject(hFont); 37 RestoreDC(m_hDC, m_iState); 38 } 39 40 private: 41 HDC m_hDC; 42 HGDIOBJ m_hFont; 43 int m_iState; 44 45 ScopedState(const ScopedState&) = delete; 46 void operator=(const ScopedState&) = delete; 47 }; 48 49 } // namespace 50 51 bool g_pdfium_print_text_with_gdi = false; 52 53 PDFiumEnsureTypefaceCharactersAccessible g_pdfium_typeface_accessible_func = 54 nullptr; 55 #endif 56 57 CGdiPrinterDriver::CGdiPrinterDriver(HDC hDC) 58 : CGdiDeviceDriver(hDC, FXDC_PRINTER), 59 m_HorzSize(::GetDeviceCaps(m_hDC, HORZSIZE)), 60 m_VertSize(::GetDeviceCaps(m_hDC, VERTSIZE)) {} 61 62 CGdiPrinterDriver::~CGdiPrinterDriver() {} 63 64 int CGdiPrinterDriver::GetDeviceCaps(int caps_id) const { 65 if (caps_id == FXDC_HORZ_SIZE) 66 return m_HorzSize; 67 if (caps_id == FXDC_VERT_SIZE) 68 return m_VertSize; 69 return CGdiDeviceDriver::GetDeviceCaps(caps_id); 70 } 71 72 bool CGdiPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBSource>& pSource, 73 uint32_t color, 74 const FX_RECT* pSrcRect, 75 int left, 76 int top, 77 int blend_type) { 78 if (pSource->IsAlphaMask()) { 79 FX_RECT clip_rect(left, top, left + pSrcRect->Width(), 80 top + pSrcRect->Height()); 81 return StretchDIBits(pSource, color, left - pSrcRect->left, 82 top - pSrcRect->top, pSource->GetWidth(), 83 pSource->GetHeight(), &clip_rect, 0, 84 FXDIB_BLEND_NORMAL); 85 } 86 ASSERT(pSource && !pSource->IsAlphaMask() && pSrcRect); 87 ASSERT(blend_type == FXDIB_BLEND_NORMAL); 88 if (pSource->HasAlpha()) 89 return false; 90 91 CFX_DIBExtractor temp(pSource); 92 RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap(); 93 if (!pBitmap) 94 return false; 95 96 return GDI_SetDIBits(pBitmap, pSrcRect, left, top); 97 } 98 99 bool CGdiPrinterDriver::StretchDIBits(const RetainPtr<CFX_DIBSource>& pSource, 100 uint32_t color, 101 int dest_left, 102 int dest_top, 103 int dest_width, 104 int dest_height, 105 const FX_RECT* pClipRect, 106 uint32_t flags, 107 int blend_type) { 108 if (pSource->IsAlphaMask()) { 109 int alpha = FXARGB_A(color); 110 if (pSource->GetBPP() != 1 || alpha != 255) 111 return false; 112 113 if (dest_width < 0 || dest_height < 0) { 114 RetainPtr<CFX_DIBitmap> pFlipped = 115 pSource->FlipImage(dest_width < 0, dest_height < 0); 116 if (!pFlipped) 117 return false; 118 119 if (dest_width < 0) 120 dest_left += dest_width; 121 if (dest_height < 0) 122 dest_top += dest_height; 123 124 return GDI_StretchBitMask(pFlipped, dest_left, dest_top, abs(dest_width), 125 abs(dest_height), color, flags); 126 } 127 128 CFX_DIBExtractor temp(pSource); 129 RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap(); 130 if (!pBitmap) 131 return false; 132 return GDI_StretchBitMask(pBitmap, dest_left, dest_top, dest_width, 133 dest_height, color, flags); 134 } 135 136 if (pSource->HasAlpha()) 137 return false; 138 139 if (dest_width < 0 || dest_height < 0) { 140 RetainPtr<CFX_DIBitmap> pFlipped = 141 pSource->FlipImage(dest_width < 0, dest_height < 0); 142 if (!pFlipped) 143 return false; 144 145 if (dest_width < 0) 146 dest_left += dest_width; 147 if (dest_height < 0) 148 dest_top += dest_height; 149 150 return GDI_StretchDIBits(pFlipped, dest_left, dest_top, abs(dest_width), 151 abs(dest_height), flags); 152 } 153 154 CFX_DIBExtractor temp(pSource); 155 RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap(); 156 if (!pBitmap) 157 return false; 158 return GDI_StretchDIBits(pBitmap, dest_left, dest_top, dest_width, 159 dest_height, flags); 160 } 161 162 bool CGdiPrinterDriver::StartDIBits(const RetainPtr<CFX_DIBSource>& pSource, 163 int bitmap_alpha, 164 uint32_t color, 165 const CFX_Matrix* pMatrix, 166 uint32_t render_flags, 167 std::unique_ptr<CFX_ImageRenderer>* handle, 168 int blend_type) { 169 if (bitmap_alpha < 255 || pSource->HasAlpha() || 170 (pSource->IsAlphaMask() && (pSource->GetBPP() != 1))) { 171 return false; 172 } 173 CFX_FloatRect unit_rect = pMatrix->GetUnitRect(); 174 FX_RECT full_rect = unit_rect.GetOuterRect(); 175 if (fabs(pMatrix->b) < 0.5f && pMatrix->a != 0 && fabs(pMatrix->c) < 0.5f && 176 pMatrix->d != 0) { 177 bool bFlipX = pMatrix->a < 0; 178 bool bFlipY = pMatrix->d > 0; 179 return StretchDIBits(pSource, color, 180 bFlipX ? full_rect.right : full_rect.left, 181 bFlipY ? full_rect.bottom : full_rect.top, 182 bFlipX ? -full_rect.Width() : full_rect.Width(), 183 bFlipY ? -full_rect.Height() : full_rect.Height(), 184 nullptr, 0, blend_type); 185 } 186 if (fabs(pMatrix->a) >= 0.5f || fabs(pMatrix->d) >= 0.5f) 187 return false; 188 189 RetainPtr<CFX_DIBitmap> pTransformed = 190 pSource->SwapXY(pMatrix->c > 0, pMatrix->b < 0); 191 if (!pTransformed) 192 return false; 193 194 return StretchDIBits(pTransformed, color, full_rect.left, full_rect.top, 195 full_rect.Width(), full_rect.Height(), nullptr, 0, 196 blend_type); 197 } 198 199 bool CGdiPrinterDriver::DrawDeviceText(int nChars, 200 const FXTEXT_CHARPOS* pCharPos, 201 CFX_Font* pFont, 202 const CFX_Matrix* pObject2Device, 203 float font_size, 204 uint32_t color) { 205 #if defined(PDFIUM_PRINT_TEXT_WITH_GDI) 206 if (!g_pdfium_print_text_with_gdi) 207 return false; 208 209 if (nChars < 1 || !pFont || !pFont->IsEmbedded() || !pFont->IsTTFont()) 210 return false; 211 212 // Scale factor used to minimize the kerning problems caused by rounding 213 // errors below. Value chosen based on the title of https://crbug.com/18383 214 const double kScaleFactor = 10; 215 216 // Font 217 // 218 // Note that |pFont| has the actual font to render with embedded within, but 219 // but unfortunately AddFontMemResourceEx() does not seem to cooperate. 220 // Loading font data to memory seems to work, but then enumerating the fonts 221 // fails to find it. This requires more investigation. In the meanwhile, 222 // assume the printing is happening on the machine that generated the PDF, so 223 // the embedded font, if not a web font, is available through GDI anyway. 224 // TODO(thestig): Figure out why AddFontMemResourceEx() does not work. 225 // Generalize this method to work for all PDFs with embedded fonts. 226 // In sandboxed environments, font loading may not work at all, so this may be 227 // the best possible effort. 228 LOGFONT lf = {}; 229 lf.lfHeight = -font_size * kScaleFactor; 230 lf.lfWeight = pFont->IsBold() ? FW_BOLD : FW_NORMAL; 231 lf.lfItalic = pFont->IsItalic(); 232 lf.lfCharSet = DEFAULT_CHARSET; 233 234 const WideString wsName = pFont->GetFaceName().UTF8Decode(); 235 size_t iNameLen = 236 std::min(wsName.GetLength(), static_cast<size_t>(LF_FACESIZE - 1)); 237 memcpy(lf.lfFaceName, wsName.c_str(), sizeof(lf.lfFaceName[0]) * iNameLen); 238 lf.lfFaceName[iNameLen] = 0; 239 240 HFONT hFont = CreateFontIndirect(&lf); 241 if (!hFont) 242 return false; 243 244 ScopedState state(m_hDC, hFont); 245 size_t nTextMetricSize = GetOutlineTextMetrics(m_hDC, 0, nullptr); 246 if (nTextMetricSize == 0) { 247 // Give up and fail if there is no way to get the font to try again. 248 if (!g_pdfium_typeface_accessible_func) 249 return false; 250 251 // Try to get the font. Any letter will do. 252 g_pdfium_typeface_accessible_func(&lf, L"A", 1); 253 nTextMetricSize = GetOutlineTextMetrics(m_hDC, 0, nullptr); 254 if (nTextMetricSize == 0) 255 return false; 256 } 257 258 std::vector<BYTE> buf(nTextMetricSize); 259 OUTLINETEXTMETRIC* pTextMetric = 260 reinterpret_cast<OUTLINETEXTMETRIC*>(buf.data()); 261 if (GetOutlineTextMetrics(m_hDC, nTextMetricSize, pTextMetric) == 0) 262 return false; 263 264 // If the selected font is not the requested font, then bail out. This can 265 // happen with web fonts, for example. 266 wchar_t* wsSelectedName = reinterpret_cast<wchar_t*>( 267 buf.data() + reinterpret_cast<size_t>(pTextMetric->otmpFaceName)); 268 if (wsName != wsSelectedName) 269 return false; 270 271 // Transforms 272 SetGraphicsMode(m_hDC, GM_ADVANCED); 273 XFORM xform; 274 xform.eM11 = pObject2Device->a / kScaleFactor; 275 xform.eM12 = pObject2Device->b / kScaleFactor; 276 xform.eM21 = -pObject2Device->c / kScaleFactor; 277 xform.eM22 = -pObject2Device->d / kScaleFactor; 278 xform.eDx = pObject2Device->e; 279 xform.eDy = pObject2Device->f; 280 ModifyWorldTransform(m_hDC, &xform, MWT_LEFTMULTIPLY); 281 282 // Color 283 int iUnusedAlpha; 284 FX_COLORREF rgb; 285 std::tie(iUnusedAlpha, rgb) = ArgbToColorRef(color); 286 SetTextColor(m_hDC, rgb); 287 SetBkMode(m_hDC, TRANSPARENT); 288 289 // Text 290 WideString wsText; 291 std::vector<INT> spacing(nChars); 292 float fPreviousOriginX = 0; 293 for (int i = 0; i < nChars; ++i) { 294 // Only works with PDFs from Skia's PDF generator. Cannot handle arbitrary 295 // values from PDFs. 296 const FXTEXT_CHARPOS& charpos = pCharPos[i]; 297 ASSERT(charpos.m_AdjustMatrix[0] == 0); 298 ASSERT(charpos.m_AdjustMatrix[1] == 0); 299 ASSERT(charpos.m_AdjustMatrix[2] == 0); 300 ASSERT(charpos.m_AdjustMatrix[3] == 0); 301 ASSERT(charpos.m_Origin.y == 0); 302 303 // Round the spacing to the nearest integer, but keep track of the rounding 304 // error for calculating the next spacing value. 305 float fOriginX = charpos.m_Origin.x * kScaleFactor; 306 float fPixelSpacing = fOriginX - fPreviousOriginX; 307 spacing[i] = FXSYS_round(fPixelSpacing); 308 fPreviousOriginX = fOriginX - (fPixelSpacing - spacing[i]); 309 310 wsText += charpos.m_GlyphIndex; 311 } 312 313 // Draw 314 SetTextAlign(m_hDC, TA_LEFT | TA_BASELINE); 315 if (ExtTextOutW(m_hDC, 0, 0, ETO_GLYPH_INDEX, nullptr, wsText.c_str(), nChars, 316 nChars > 1 ? &spacing[1] : nullptr)) { 317 return true; 318 } 319 320 // Give up and fail if there is no way to get the font to try again. 321 if (!g_pdfium_typeface_accessible_func) 322 return false; 323 324 // Try to get the font and draw again. 325 g_pdfium_typeface_accessible_func(&lf, wsText.c_str(), nChars); 326 return !!ExtTextOutW(m_hDC, 0, 0, ETO_GLYPH_INDEX, nullptr, wsText.c_str(), 327 nChars, nChars > 1 ? &spacing[1] : nullptr); 328 #else 329 return false; 330 #endif 331 } 332 333 CPSPrinterDriver::CPSPrinterDriver(HDC hDC, int pslevel, bool bCmykOutput) 334 : m_hDC(hDC), m_bCmykOutput(bCmykOutput) { 335 m_HorzSize = ::GetDeviceCaps(m_hDC, HORZSIZE); 336 m_VertSize = ::GetDeviceCaps(m_hDC, VERTSIZE); 337 m_Width = ::GetDeviceCaps(m_hDC, HORZRES); 338 m_Height = ::GetDeviceCaps(m_hDC, VERTRES); 339 m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL); 340 341 m_PSRenderer.Init(pdfium::MakeRetain<CPSOutput>(m_hDC), pslevel, m_Width, 342 m_Height, bCmykOutput); 343 HRGN hRgn = ::CreateRectRgn(0, 0, 1, 1); 344 int ret = ::GetClipRgn(hDC, hRgn); 345 if (ret == 1) { 346 ret = ::GetRegionData(hRgn, 0, NULL); 347 if (ret) { 348 RGNDATA* pData = reinterpret_cast<RGNDATA*>(FX_Alloc(uint8_t, ret)); 349 ret = ::GetRegionData(hRgn, ret, pData); 350 if (ret) { 351 CFX_PathData path; 352 for (uint32_t i = 0; i < pData->rdh.nCount; i++) { 353 RECT* pRect = 354 reinterpret_cast<RECT*>(pData->Buffer + pData->rdh.nRgnSize * i); 355 path.AppendRect(static_cast<float>(pRect->left), 356 static_cast<float>(pRect->bottom), 357 static_cast<float>(pRect->right), 358 static_cast<float>(pRect->top)); 359 } 360 m_PSRenderer.SetClip_PathFill(&path, nullptr, FXFILL_WINDING); 361 } 362 FX_Free(pData); 363 } 364 } 365 ::DeleteObject(hRgn); 366 } 367 368 CPSPrinterDriver::~CPSPrinterDriver() { 369 EndRendering(); 370 } 371 372 int CPSPrinterDriver::GetDeviceCaps(int caps_id) const { 373 switch (caps_id) { 374 case FXDC_DEVICE_CLASS: 375 return FXDC_PRINTER; 376 case FXDC_PIXEL_WIDTH: 377 return m_Width; 378 case FXDC_PIXEL_HEIGHT: 379 return m_Height; 380 case FXDC_BITS_PIXEL: 381 return m_nBitsPerPixel; 382 case FXDC_RENDER_CAPS: 383 return m_bCmykOutput ? FXRC_BIT_MASK | FXRC_CMYK_OUTPUT : FXRC_BIT_MASK; 384 case FXDC_HORZ_SIZE: 385 return m_HorzSize; 386 case FXDC_VERT_SIZE: 387 return m_VertSize; 388 } 389 return 0; 390 } 391 392 bool CPSPrinterDriver::StartRendering() { 393 return m_PSRenderer.StartRendering(); 394 } 395 396 void CPSPrinterDriver::EndRendering() { 397 m_PSRenderer.EndRendering(); 398 } 399 400 void CPSPrinterDriver::SaveState() { 401 m_PSRenderer.SaveState(); 402 } 403 404 void CPSPrinterDriver::RestoreState(bool bKeepSaved) { 405 m_PSRenderer.RestoreState(bKeepSaved); 406 } 407 408 bool CPSPrinterDriver::SetClip_PathFill(const CFX_PathData* pPathData, 409 const CFX_Matrix* pObject2Device, 410 int fill_mode) { 411 m_PSRenderer.SetClip_PathFill(pPathData, pObject2Device, fill_mode); 412 return true; 413 } 414 415 bool CPSPrinterDriver::SetClip_PathStroke( 416 const CFX_PathData* pPathData, 417 const CFX_Matrix* pObject2Device, 418 const CFX_GraphStateData* pGraphState) { 419 m_PSRenderer.SetClip_PathStroke(pPathData, pObject2Device, pGraphState); 420 return true; 421 } 422 423 bool CPSPrinterDriver::DrawPath(const CFX_PathData* pPathData, 424 const CFX_Matrix* pObject2Device, 425 const CFX_GraphStateData* pGraphState, 426 FX_ARGB fill_color, 427 FX_ARGB stroke_color, 428 int fill_mode, 429 int blend_type) { 430 if (blend_type != FXDIB_BLEND_NORMAL) { 431 return false; 432 } 433 return m_PSRenderer.DrawPath(pPathData, pObject2Device, pGraphState, 434 fill_color, stroke_color, fill_mode & 3); 435 } 436 437 bool CPSPrinterDriver::GetClipBox(FX_RECT* pRect) { 438 *pRect = m_PSRenderer.GetClipBox(); 439 return true; 440 } 441 442 bool CPSPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap, 443 uint32_t color, 444 const FX_RECT* pSrcRect, 445 int left, 446 int top, 447 int blend_type) { 448 if (blend_type != FXDIB_BLEND_NORMAL) 449 return false; 450 return m_PSRenderer.SetDIBits(pBitmap, color, left, top); 451 } 452 453 bool CPSPrinterDriver::StretchDIBits(const RetainPtr<CFX_DIBSource>& pBitmap, 454 uint32_t color, 455 int dest_left, 456 int dest_top, 457 int dest_width, 458 int dest_height, 459 const FX_RECT* pClipRect, 460 uint32_t flags, 461 int blend_type) { 462 if (blend_type != FXDIB_BLEND_NORMAL) 463 return false; 464 return m_PSRenderer.StretchDIBits(pBitmap, color, dest_left, dest_top, 465 dest_width, dest_height, flags); 466 } 467 468 bool CPSPrinterDriver::StartDIBits(const RetainPtr<CFX_DIBSource>& pBitmap, 469 int bitmap_alpha, 470 uint32_t color, 471 const CFX_Matrix* pMatrix, 472 uint32_t render_flags, 473 std::unique_ptr<CFX_ImageRenderer>* handle, 474 int blend_type) { 475 if (blend_type != FXDIB_BLEND_NORMAL) 476 return false; 477 478 if (bitmap_alpha < 255) 479 return false; 480 481 *handle = nullptr; 482 return m_PSRenderer.DrawDIBits(pBitmap, color, pMatrix, render_flags); 483 } 484 485 bool CPSPrinterDriver::DrawDeviceText(int nChars, 486 const FXTEXT_CHARPOS* pCharPos, 487 CFX_Font* pFont, 488 const CFX_Matrix* pObject2Device, 489 float font_size, 490 uint32_t color) { 491 return m_PSRenderer.DrawText(nChars, pCharPos, pFont, pObject2Device, 492 font_size, color); 493 } 494 495 CTextOnlyPrinterDriver::CTextOnlyPrinterDriver(HDC hDC) 496 : m_hDC(hDC), 497 m_Width(INT_MAX), 498 m_Height(INT_MAX), 499 m_HorzSize(INT_MAX), 500 m_VertSize(INT_MAX), 501 m_OriginY(0.0f), 502 m_SetOrigin(false) { 503 m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL); 504 } 505 506 CTextOnlyPrinterDriver::~CTextOnlyPrinterDriver() { 507 EndRendering(); 508 } 509 510 int CTextOnlyPrinterDriver::GetDeviceCaps(int caps_id) const { 511 switch (caps_id) { 512 case FXDC_DEVICE_CLASS: 513 return FXDC_PRINTER; 514 case FXDC_PIXEL_WIDTH: 515 return m_Width; 516 case FXDC_PIXEL_HEIGHT: 517 return m_Height; 518 case FXDC_BITS_PIXEL: 519 return m_nBitsPerPixel; 520 case FXDC_RENDER_CAPS: 521 return 0; 522 case FXDC_HORZ_SIZE: 523 return m_HorzSize; 524 case FXDC_VERT_SIZE: 525 return m_VertSize; 526 } 527 return 0; 528 } 529 530 bool CTextOnlyPrinterDriver::SetClip_PathFill(const CFX_PathData* pPathData, 531 const CFX_Matrix* pObject2Device, 532 int fill_mode) { 533 return true; 534 } 535 536 bool CTextOnlyPrinterDriver::SetClip_PathStroke( 537 const CFX_PathData* pPathData, 538 const CFX_Matrix* pObject2Device, 539 const CFX_GraphStateData* pGraphState) { 540 return false; 541 } 542 543 bool CTextOnlyPrinterDriver::DrawPath(const CFX_PathData* pPathData, 544 const CFX_Matrix* pObject2Device, 545 const CFX_GraphStateData* pGraphState, 546 uint32_t fill_color, 547 uint32_t stroke_color, 548 int fill_mode, 549 int blend_type) { 550 return false; 551 } 552 553 bool CTextOnlyPrinterDriver::SetDIBits(const RetainPtr<CFX_DIBSource>& pBitmap, 554 uint32_t color, 555 const FX_RECT* pSrcRect, 556 int left, 557 int top, 558 int blend_type) { 559 return false; 560 } 561 562 bool CTextOnlyPrinterDriver::GetClipBox(FX_RECT* pRect) { 563 pRect->left = 0; 564 pRect->right = m_Width; 565 pRect->top = 0; 566 pRect->bottom = m_Height; 567 return true; 568 } 569 570 bool CTextOnlyPrinterDriver::StretchDIBits( 571 const RetainPtr<CFX_DIBSource>& pBitmap, 572 uint32_t color, 573 int dest_left, 574 int dest_top, 575 int dest_width, 576 int dest_height, 577 const FX_RECT* pClipRect, 578 uint32_t flags, 579 int blend_type) { 580 return false; 581 } 582 583 bool CTextOnlyPrinterDriver::StartDIBits( 584 const RetainPtr<CFX_DIBSource>& pBitmap, 585 int bitmap_alpha, 586 uint32_t color, 587 const CFX_Matrix* pMatrix, 588 uint32_t render_flags, 589 std::unique_ptr<CFX_ImageRenderer>* handle, 590 int blend_type) { 591 return false; 592 } 593 594 bool CTextOnlyPrinterDriver::DrawDeviceText(int nChars, 595 const FXTEXT_CHARPOS* pCharPos, 596 CFX_Font* pFont, 597 const CFX_Matrix* pObject2Device, 598 float font_size, 599 uint32_t color) { 600 if (g_pdfium_print_mode != 1) 601 return false; 602 if (nChars < 1 || !pFont || !pFont->IsEmbedded() || !pFont->IsTTFont()) 603 return false; 604 605 // Scale factor used to minimize the kerning problems caused by rounding 606 // errors below. Value chosen based on the title of https://crbug.com/18383 607 const double kScaleFactor = 10; 608 609 WideString wsText; 610 int totalLength = nChars; 611 612 // Detect new lines and add clrf characters (since this is Windows only). 613 // These characters are removed by SkPDF, but the new line information is 614 // preserved in the text location. clrf characters seem to be ignored by 615 // label printers that use this driver. 616 if (m_SetOrigin && 617 FXSYS_round(m_OriginY) != FXSYS_round(pObject2Device->f * kScaleFactor)) { 618 wsText += L"\r\n"; 619 totalLength += 2; 620 } 621 m_OriginY = pObject2Device->f * kScaleFactor; 622 m_SetOrigin = true; 623 624 // Text 625 for (int i = 0; i < nChars; ++i) { 626 // Only works with PDFs from Skia's PDF generator. Cannot handle arbitrary 627 // values from PDFs. 628 const FXTEXT_CHARPOS& charpos = pCharPos[i]; 629 ASSERT(charpos.m_AdjustMatrix[0] == 0); 630 ASSERT(charpos.m_AdjustMatrix[1] == 0); 631 ASSERT(charpos.m_AdjustMatrix[2] == 0); 632 ASSERT(charpos.m_AdjustMatrix[3] == 0); 633 ASSERT(charpos.m_Origin.y == 0); 634 635 wsText += charpos.m_Unicode; 636 } 637 size_t len = totalLength; 638 ByteString text = ByteString::FromUnicode(wsText); 639 while (len > 0) { 640 char buffer[1026]; 641 size_t send_len = std::min(len, static_cast<size_t>(1024)); 642 *(reinterpret_cast<uint16_t*>(buffer)) = send_len; 643 memcpy(buffer + 2, text.c_str(), send_len); 644 ::GdiComment(m_hDC, send_len + 2, reinterpret_cast<const BYTE*>(buffer)); 645 len -= send_len; 646 text.Right(len); 647 } 648 return true; 649 } 650