Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cpdf_renderstatus.cpp
Go to the documentation of this file.
1// Copyright 2016 The PDFium Authors
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 "core/fpdfapi/render/cpdf_renderstatus.h"
8
9#include <stdint.h>
10
11#include <algorithm>
12#include <memory>
13#include <numeric>
14#include <set>
15#include <utility>
16#include <vector>
17
18#include "build/build_config.h"
19#include "constants/transparency.h"
20#include "core/fpdfapi/font/cpdf_font.h"
21#include "core/fpdfapi/font/cpdf_type3char.h"
22#include "core/fpdfapi/font/cpdf_type3font.h"
23#include "core/fpdfapi/page/cpdf_docpagedata.h"
24#include "core/fpdfapi/page/cpdf_form.h"
25#include "core/fpdfapi/page/cpdf_formobject.h"
26#include "core/fpdfapi/page/cpdf_function.h"
27#include "core/fpdfapi/page/cpdf_graphicstates.h"
28#include "core/fpdfapi/page/cpdf_image.h"
29#include "core/fpdfapi/page/cpdf_imageobject.h"
30#include "core/fpdfapi/page/cpdf_occontext.h"
31#include "core/fpdfapi/page/cpdf_page.h"
32#include "core/fpdfapi/page/cpdf_pageimagecache.h"
33#include "core/fpdfapi/page/cpdf_pageobject.h"
34#include "core/fpdfapi/page/cpdf_pathobject.h"
35#include "core/fpdfapi/page/cpdf_shadingobject.h"
36#include "core/fpdfapi/page/cpdf_shadingpattern.h"
37#include "core/fpdfapi/page/cpdf_textobject.h"
38#include "core/fpdfapi/page/cpdf_tilingpattern.h"
39#include "core/fpdfapi/page/cpdf_transferfunc.h"
40#include "core/fpdfapi/parser/cpdf_array.h"
41#include "core/fpdfapi/parser/cpdf_document.h"
42#include "core/fpdfapi/parser/cpdf_stream.h"
43#include "core/fpdfapi/parser/fpdf_parser_utility.h"
44#include "core/fpdfapi/render/charposlist.h"
45#include "core/fpdfapi/render/cpdf_docrenderdata.h"
46#include "core/fpdfapi/render/cpdf_imagerenderer.h"
47#include "core/fpdfapi/render/cpdf_rendercontext.h"
48#include "core/fpdfapi/render/cpdf_renderoptions.h"
49#include "core/fpdfapi/render/cpdf_rendershading.h"
50#include "core/fpdfapi/render/cpdf_rendertiling.h"
51#include "core/fpdfapi/render/cpdf_scaledrenderbuffer.h"
52#include "core/fpdfapi/render/cpdf_textrenderer.h"
53#include "core/fpdfapi/render/cpdf_type3cache.h"
54#include "core/fxcrt/autorestorer.h"
55#include "core/fxcrt/data_vector.h"
56#include "core/fxcrt/fx_2d_size.h"
57#include "core/fxcrt/fx_safe_types.h"
58#include "core/fxcrt/fx_system.h"
59#include "core/fxcrt/span_util.h"
60#include "core/fxcrt/unowned_ptr.h"
61#include "core/fxge/cfx_defaultrenderdevice.h"
62#include "core/fxge/cfx_fillrenderoptions.h"
63#include "core/fxge/cfx_glyphbitmap.h"
64#include "core/fxge/cfx_path.h"
65#include "core/fxge/dib/cfx_dibitmap.h"
66#include "core/fxge/fx_font.h"
67#include "core/fxge/renderdevicedriver_iface.h"
68#include "core/fxge/text_char_pos.h"
69#include "core/fxge/text_glyph_pos.h"
70#include "third_party/base/check.h"
71#include "third_party/base/containers/contains.h"
72#include "third_party/base/containers/span.h"
73#include "third_party/base/notreached.h"
74
75namespace {
76
77constexpr int kRenderMaxRecursionDepth = 64;
78int g_CurrentRecursionDepth = 0;
79
80CFX_FillRenderOptions GetFillOptionsForDrawPathWithBlend(
81 const CPDF_RenderOptions::Options& options,
82 const CPDF_PathObject* path_obj,
84 bool is_stroke,
85 bool is_type3_char) {
86 CFX_FillRenderOptions fill_options(fill_type);
88 options.bRectAA) {
89 fill_options.rect_aa = true;
90 }
91 if (options.bNoPathSmooth) {
92 fill_options.aliased_path = true;
93 }
94 if (path_obj->general_state().GetStrokeAdjust()) {
95 fill_options.adjust_stroke = true;
96 }
97 if (is_stroke) {
98 fill_options.stroke = true;
99 }
100 if (is_type3_char) {
101 fill_options.text_mode = true;
102 }
103
104 return fill_options;
105}
106
107CFX_FillRenderOptions GetFillOptionsForDrawTextPath(
108 const CPDF_RenderOptions::Options& options,
109 const CPDF_TextObject* text_obj,
110 bool is_stroke,
111 bool is_fill) {
112 CFX_FillRenderOptions fill_options;
113 if (is_stroke && is_fill) {
114 fill_options.stroke = true;
115 fill_options.stroke_text_mode = true;
116 }
117 if (text_obj->general_state().GetStrokeAdjust()) {
118 fill_options.adjust_stroke = true;
119 }
120 if (options.bNoTextSmooth) {
121 fill_options.aliased_path = true;
122 }
123
124 return fill_options;
125}
126
127FXDIB_Format GetFormatForLuminosity(bool is_luminosity) {
128 if (!is_luminosity)
130#if BUILDFLAG(IS_APPLE)
131 return FXDIB_Format::kRgb32;
132#else
133 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
135 }
136 return FXDIB_Format::kRgb;
137#endif
138}
139
140bool IsAvailableMatrix(const CFX_Matrix& matrix) {
141 if (matrix.a == 0 || matrix.d == 0)
142 return matrix.b != 0 && matrix.c != 0;
143
144 if (matrix.b == 0 || matrix.c == 0)
145 return matrix.a != 0 && matrix.d != 0;
146
147 return true;
148}
149
150bool MissingFillColor(const CPDF_ColorState* pColorState) {
151 return !pColorState->HasRef() || pColorState->GetFillColor()->IsNull();
152}
153
154bool MissingStrokeColor(const CPDF_ColorState* pColorState) {
155 return !pColorState->HasRef() || pColorState->GetStrokeColor()->IsNull();
156}
157
158bool Type3CharMissingFillColor(const CPDF_Type3Char* pChar,
159 const CPDF_ColorState* pColorState) {
160 return pChar && (!pChar->colored() || MissingFillColor(pColorState));
161}
162
163bool Type3CharMissingStrokeColor(const CPDF_Type3Char* pChar,
164 const CPDF_ColorState* pColorState) {
165 return pChar && (!pChar->colored() || MissingStrokeColor(pColorState));
166}
167
168} // namespace
169
173
175
177 const CPDF_GraphicStates* pInitialStates) {
178 m_bPrint = m_pDevice->GetDeviceType() != DeviceType::kDisplay;
179 m_pPageResource.Reset(m_pContext->GetPageResources());
180 if (pInitialStates && !m_pType3Char) {
181 m_InitialStates = *pInitialStates;
182 if (pParentStatus) {
183 if (!m_InitialStates.color_state().HasFillColor()) {
184 m_InitialStates.mutable_color_state().SetFillColorRef(
185 pParentStatus->m_InitialStates.color_state().GetFillColorRef());
186 *m_InitialStates.mutable_color_state().GetMutableFillColor() =
187 *pParentStatus->m_InitialStates.color_state().GetFillColor();
188 }
189 if (!m_InitialStates.color_state().HasStrokeColor()) {
190 m_InitialStates.mutable_color_state().SetStrokeColorRef(
191 pParentStatus->m_InitialStates.color_state().GetFillColorRef());
192 *m_InitialStates.mutable_color_state().GetMutableStrokeColor() =
193 *pParentStatus->m_InitialStates.color_state().GetStrokeColor();
194 }
195 }
196 } else {
197 m_InitialStates.SetDefaultStates();
198 }
199}
200
202 const CPDF_PageObjectHolder* pObjectHolder,
203 const CFX_Matrix& mtObj2Device) {
204 CFX_FloatRect clip_rect = mtObj2Device.GetInverse().TransformRect(
205 CFX_FloatRect(m_pDevice->GetClipBox()));
206 for (const auto& pCurObj : *pObjectHolder) {
207 if (pCurObj.get() == m_pStopObj) {
208 m_bStopped = true;
209 return;
210 }
211 if (!pCurObj)
212 continue;
213
214 if (pCurObj->GetRect().left > clip_rect.right ||
215 pCurObj->GetRect().right < clip_rect.left ||
216 pCurObj->GetRect().bottom > clip_rect.top ||
217 pCurObj->GetRect().top < clip_rect.bottom) {
218 continue;
219 }
220 RenderSingleObject(pCurObj.get(), mtObj2Device);
221 if (m_bStopped)
222 return;
223 }
224}
225
227 const CFX_Matrix& mtObj2Device) {
228 AutoRestorer<int> restorer(&g_CurrentRecursionDepth);
229 if (++g_CurrentRecursionDepth > kRenderMaxRecursionDepth) {
230 return;
231 }
232 m_pCurObj = pObj;
233 if (!m_Options.CheckPageObjectVisible(pObj)) {
234 return;
235 }
236 ProcessClipPath(pObj->clip_path(), mtObj2Device);
237 if (ProcessTransparency(pObj, mtObj2Device)) {
238 return;
239 }
240 ProcessObjectNoClip(pObj, mtObj2Device);
241}
242
244 const CFX_Matrix& mtObj2Device,
245 PauseIndicatorIface* pPause) {
246 if (m_pImageRenderer) {
247 if (m_pImageRenderer->Continue(pPause))
248 return true;
249
250 if (!m_pImageRenderer->GetResult())
251 DrawObjWithBackground(pObj, mtObj2Device);
252 m_pImageRenderer.reset();
253 return false;
254 }
255
256 m_pCurObj = pObj;
257 if (!m_Options.CheckPageObjectVisible(pObj))
258 return false;
259
260 ProcessClipPath(pObj->clip_path(), mtObj2Device);
261 if (ProcessTransparency(pObj, mtObj2Device))
262 return false;
263
264 if (!pObj->IsImage()) {
265 ProcessObjectNoClip(pObj, mtObj2Device);
266 return false;
267 }
268
269 m_pImageRenderer = std::make_unique<CPDF_ImageRenderer>(this);
270 if (!m_pImageRenderer->Start(pObj->AsImage(), mtObj2Device, false,
271 BlendMode::kNormal)) {
272 if (!m_pImageRenderer->GetResult())
273 DrawObjWithBackground(pObj, mtObj2Device);
274 m_pImageRenderer.reset();
275 return false;
276 }
277 return ContinueSingleObject(pObj, mtObj2Device, pPause);
278}
279
280FX_RECT CPDF_RenderStatus::GetObjectClippedRect(
281 const CPDF_PageObject* pObj,
282 const CFX_Matrix& mtObj2Device) const {
283 FX_RECT rect = pObj->GetTransformedBBox(mtObj2Device);
284 rect.Intersect(m_pDevice->GetClipBox());
285 return rect;
286}
287
288void CPDF_RenderStatus::ProcessObjectNoClip(CPDF_PageObject* pObj,
289 const CFX_Matrix& mtObj2Device) {
290 bool bRet = false;
291 switch (pObj->GetType()) {
293 bRet = ProcessText(pObj->AsText(), mtObj2Device, nullptr);
294 break;
296 bRet = ProcessPath(pObj->AsPath(), mtObj2Device);
297 break;
299 bRet = ProcessImage(pObj->AsImage(), mtObj2Device);
300 break;
302 ProcessShading(pObj->AsShading(), mtObj2Device);
303 return;
305 bRet = ProcessForm(pObj->AsForm(), mtObj2Device);
306 break;
307 }
308 if (!bRet)
309 DrawObjWithBackground(pObj, mtObj2Device);
310}
311
312bool CPDF_RenderStatus::DrawObjWithBlend(CPDF_PageObject* pObj,
313 const CFX_Matrix& mtObj2Device) {
314 switch (pObj->GetType()) {
316 return ProcessPath(pObj->AsPath(), mtObj2Device);
318 return ProcessImage(pObj->AsImage(), mtObj2Device);
320 return ProcessForm(pObj->AsForm(), mtObj2Device);
321 default:
322 return false;
323 }
324}
325
326void CPDF_RenderStatus::DrawObjWithBackground(CPDF_PageObject* pObj,
327 const CFX_Matrix& mtObj2Device) {
328 FX_RECT rect = GetObjectClippedRect(pObj, mtObj2Device);
329 if (rect.IsEmpty())
330 return;
331
332 int res = (pObj->IsImage() && m_bPrint) ? 0 : 300;
334 if (!buffer.Initialize(m_pContext, m_pDevice, rect, pObj, &m_Options, res)) {
335 return;
336 }
337 RetainPtr<const CPDF_Dictionary> pFormResource;
338 CFX_Matrix matrix = mtObj2Device * buffer.GetMatrix();
339 const CPDF_FormObject* pFormObj = pObj->AsForm();
340 if (pFormObj)
341 pFormResource = pFormObj->form()->GetDict()->GetDictFor("Resources");
342 CPDF_RenderStatus status(m_pContext, buffer.GetDevice());
343 status.SetOptions(m_Options);
345 status.SetTransparency(m_Transparency);
346 status.SetDropObjects(m_bDropObjects);
347 status.SetFormResource(std::move(pFormResource));
348 status.Initialize(nullptr, nullptr);
349 status.RenderSingleObject(pObj, matrix);
350 buffer.OutputToDevice();
351}
352
353bool CPDF_RenderStatus::ProcessForm(const CPDF_FormObject* pFormObj,
354 const CFX_Matrix& mtObj2Device) {
355 RetainPtr<const CPDF_Dictionary> pOC =
356 pFormObj->form()->GetDict()->GetDictFor("OC");
357 if (pOC && !m_Options.CheckOCGDictVisible(pOC.Get()))
358 return true;
359
360 CFX_Matrix matrix = pFormObj->form_matrix() * mtObj2Device;
361 RetainPtr<const CPDF_Dictionary> pResources =
362 pFormObj->form()->GetDict()->GetDictFor("Resources");
363 CPDF_RenderStatus status(m_pContext, m_pDevice);
364 status.SetOptions(m_Options);
365 status.SetStopObject(m_pStopObj);
366 status.SetTransparency(m_Transparency);
367 status.SetDropObjects(m_bDropObjects);
368 status.SetFormResource(std::move(pResources));
369 status.Initialize(this, &pFormObj->graphic_states());
370 status.m_curBlend = m_curBlend;
371 {
372 CFX_RenderDevice::StateRestorer restorer(m_pDevice);
373 status.RenderObjectList(pFormObj->form(), matrix);
374 m_bStopped = status.m_bStopped;
375 }
376 return true;
377}
378
379bool CPDF_RenderStatus::ProcessPath(CPDF_PathObject* path_obj,
380 const CFX_Matrix& mtObj2Device) {
381 CFX_FillRenderOptions::FillType fill_type = path_obj->filltype();
382 bool stroke = path_obj->stroke();
383 ProcessPathPattern(path_obj, mtObj2Device, &fill_type, &stroke);
384 if (fill_type == CFX_FillRenderOptions::FillType::kNoFill && !stroke)
385 return true;
386
387 // If the option to convert fill paths to stroke is enabled for forced color,
388 // set |fill_type| to FillType::kNoFill and |stroke| to true.
389 CPDF_RenderOptions::Options& options = m_Options.GetOptions();
390 if (m_Options.ColorModeIs(CPDF_RenderOptions::Type::kForcedColor) &&
391 options.bConvertFillToStroke &&
392 fill_type != CFX_FillRenderOptions::FillType::kNoFill) {
393 stroke = true;
395 }
396
397 uint32_t fill_argb = fill_type != CFX_FillRenderOptions::FillType::kNoFill
398 ? GetFillArgb(path_obj)
399 : 0;
400 uint32_t stroke_argb = stroke ? GetStrokeArgb(path_obj) : 0;
401 CFX_Matrix path_matrix = path_obj->matrix() * mtObj2Device;
402 if (!IsAvailableMatrix(path_matrix))
403 return true;
404
405 return m_pDevice->DrawPathWithBlend(
406 *path_obj->path().GetObject(), &path_matrix,
407 path_obj->graph_state().GetObject(), fill_argb, stroke_argb,
408 GetFillOptionsForDrawPathWithBlend(options, path_obj, fill_type, stroke,
409 m_pType3Char),
410 m_curBlend);
411}
412
414 RetainPtr<const CPDF_Object> pObj) const {
415 DCHECK(pObj);
416 auto* pDocCache = CPDF_DocRenderData::FromDocument(m_pContext->GetDocument());
417 return pDocCache ? pDocCache->GetTransferFunc(std::move(pObj)) : nullptr;
418}
419
421 if (Type3CharMissingFillColor(m_pType3Char, &pObj->color_state())) {
422 return m_T3FillColor;
423 }
424
425 return GetFillArgbForType3(pObj);
426}
427
429 const CPDF_ColorState* pColorState = &pObj->color_state();
430 if (MissingFillColor(pColorState))
431 pColorState = &m_InitialStates.color_state();
432
433 FX_COLORREF colorref = pColorState->GetFillColorRef();
434 if (colorref == 0xFFFFFFFF)
435 return 0;
436
437 int32_t alpha =
438 static_cast<int32_t>((pObj->general_state().GetFillAlpha() * 255));
439 RetainPtr<const CPDF_Object> pTR = pObj->general_state().GetTR();
440 if (pTR) {
442 pObj->mutable_general_state().SetTransferFunc(
443 GetTransferFunc(std::move(pTR)));
444 }
446 colorref =
447 pObj->general_state().GetTransferFunc()->TranslateColor(colorref);
448 }
449 }
450 return m_Options.TranslateObjectFillColor(
451 AlphaAndColorRefToArgb(alpha, colorref), pObj->GetType());
452}
453
454FX_ARGB CPDF_RenderStatus::GetStrokeArgb(CPDF_PageObject* pObj) const {
455 const CPDF_ColorState* pColorState = &pObj->color_state();
456 if (Type3CharMissingStrokeColor(m_pType3Char, pColorState))
457 return m_T3FillColor;
458
459 if (MissingStrokeColor(pColorState))
460 pColorState = &m_InitialStates.color_state();
461
462 FX_COLORREF colorref = pColorState->GetStrokeColorRef();
463 if (colorref == 0xFFFFFFFF)
464 return 0;
465
466 int32_t alpha = static_cast<int32_t>(pObj->general_state().GetStrokeAlpha() *
467 255); // not rounded.
468 RetainPtr<const CPDF_Object> pTR = pObj->general_state().GetTR();
469 if (pTR) {
471 pObj->mutable_general_state().SetTransferFunc(
472 GetTransferFunc(std::move(pTR)));
473 }
475 colorref =
476 pObj->general_state().GetTransferFunc()->TranslateColor(colorref);
477 }
478 }
479 return m_Options.TranslateObjectStrokeColor(
480 AlphaAndColorRefToArgb(alpha, colorref), pObj->GetType());
481}
482
484 const CFX_Matrix& mtObj2Device) {
485 if (!ClipPath.HasRef()) {
486 if (m_LastClipPath.HasRef()) {
487 m_pDevice->RestoreState(true);
488 m_LastClipPath.SetNull();
489 }
490 return;
491 }
492 if (m_LastClipPath == ClipPath)
493 return;
494
495 m_LastClipPath = ClipPath;
496 m_pDevice->RestoreState(true);
497 for (size_t i = 0; i < ClipPath.GetPathCount(); ++i) {
498 const CFX_Path* pPath = ClipPath.GetPath(i).GetObject();
499 if (!pPath)
500 continue;
501
502 if (pPath->GetPoints().empty()) {
503 CFX_Path empty_path;
504 empty_path.AppendRect(-1, -1, 0, 0);
505 m_pDevice->SetClip_PathFill(empty_path, nullptr,
506 CFX_FillRenderOptions::WindingOptions());
507 } else {
508 m_pDevice->SetClip_PathFill(
509 *pPath, &mtObj2Device,
510 CFX_FillRenderOptions{ClipPath.GetClipType(i)});
511 }
512 }
513
514 if (ClipPath.GetTextCount() == 0)
515 return;
516
517 if (!m_bPrint &&
518 !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP)) {
519 return;
520 }
521
522 std::unique_ptr<CFX_Path> pTextClippingPath;
523 for (size_t i = 0; i < ClipPath.GetTextCount(); ++i) {
524 CPDF_TextObject* pText = ClipPath.GetText(i);
525 if (pText) {
526 if (!pTextClippingPath)
527 pTextClippingPath = std::make_unique<CFX_Path>();
528 ProcessText(pText, mtObj2Device, pTextClippingPath.get());
529 continue;
530 }
531
532 if (!pTextClippingPath)
533 continue;
534
536 if (m_Options.GetOptions().bNoTextSmooth) {
537 fill_options.aliased_path = true;
538 }
539 m_pDevice->SetClip_PathFill(*pTextClippingPath, nullptr, fill_options);
540 pTextClippingPath.reset();
541 }
542}
543
544bool CPDF_RenderStatus::ClipPattern(const CPDF_PageObject* page_obj,
545 const CFX_Matrix& mtObj2Device,
546 bool stroke) {
547 if (page_obj->IsPath())
548 return SelectClipPath(page_obj->AsPath(), mtObj2Device, stroke);
549 if (page_obj->IsImage()) {
550 m_pDevice->SetClip_Rect(page_obj->GetTransformedBBox(mtObj2Device));
551 return true;
552 }
553 return false;
554}
555
556bool CPDF_RenderStatus::SelectClipPath(const CPDF_PathObject* path_obj,
557 const CFX_Matrix& mtObj2Device,
558 bool stroke) {
559 CFX_Matrix path_matrix = path_obj->matrix() * mtObj2Device;
560 if (stroke) {
561 return m_pDevice->SetClip_PathStroke(*path_obj->path().GetObject(),
562 &path_matrix,
563 path_obj->graph_state().GetObject());
564 }
565 CFX_FillRenderOptions fill_options(path_obj->filltype());
566 if (m_Options.GetOptions().bNoPathSmooth) {
567 fill_options.aliased_path = true;
568 }
569 return m_pDevice->SetClip_PathFill(*path_obj->path().GetObject(),
570 &path_matrix, fill_options);
571}
572
573bool CPDF_RenderStatus::ProcessTransparency(CPDF_PageObject* pPageObj,
574 const CFX_Matrix& mtObj2Device) {
575 const BlendMode blend_type = pPageObj->general_state().GetBlendType();
576 RetainPtr<CPDF_Dictionary> pSMaskDict =
577 pPageObj->mutable_general_state().GetMutableSoftMask();
578 if (pSMaskDict) {
579 if (pPageObj->IsImage() &&
580 pPageObj->AsImage()->GetImage()->GetDict()->KeyExist("SMask")) {
581 pSMaskDict = nullptr;
582 }
583 }
584 RetainPtr<const CPDF_Dictionary> pFormResource;
585 float group_alpha = 1.0f;
586 CPDF_Transparency transparency = m_Transparency;
587 bool bGroupTransparent = false;
588 const CPDF_FormObject* pFormObj = pPageObj->AsForm();
589 if (pFormObj) {
590 group_alpha = pFormObj->general_state().GetFillAlpha();
591 transparency = pFormObj->form()->GetTransparency();
592 bGroupTransparent = transparency.IsIsolated();
593 pFormResource = pFormObj->form()->GetDict()->GetDictFor("Resources");
594 }
595 bool bTextClip =
596 (pPageObj->clip_path().HasRef() &&
597 pPageObj->clip_path().GetTextCount() > 0 && !m_bPrint &&
598 !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP));
599 if (!pSMaskDict && group_alpha == 1.0f && blend_type == BlendMode::kNormal &&
600 !bTextClip && !bGroupTransparent) {
601 return false;
602 }
603 if (m_bPrint) {
604 bool bRet = false;
605 int rendCaps = m_pDevice->GetRenderCaps();
606 if (!(transparency.IsIsolated() || pSMaskDict || bTextClip) &&
607 (rendCaps & FXRC_BLEND_MODE)) {
608 BlendMode oldBlend = m_curBlend;
609 m_curBlend = blend_type;
610 bRet = DrawObjWithBlend(pPageObj, mtObj2Device);
611 m_curBlend = oldBlend;
612 }
613 if (!bRet) {
614 DrawObjWithBackground(pPageObj, mtObj2Device);
615 }
616 return true;
617 }
618 FX_RECT rect = pPageObj->GetTransformedBBox(mtObj2Device);
619 rect.Intersect(m_pDevice->GetClipBox());
620 if (rect.IsEmpty())
621 return true;
622
623 int width = rect.Width();
624 int height = rect.Height();
625 CFX_DefaultRenderDevice bitmap_device;
626 RetainPtr<CFX_DIBitmap> backdrop;
627 if (!transparency.IsIsolated() &&
628 (m_pDevice->GetRenderCaps() & FXRC_GET_BITS)) {
629 backdrop = pdfium::MakeRetain<CFX_DIBitmap>();
630 if (!m_pDevice->CreateCompatibleBitmap(backdrop, width, height))
631 return true;
632 m_pDevice->GetDIBits(backdrop, rect.left, rect.top);
633 }
634 if (!bitmap_device.Create(width, height, FXDIB_Format::kArgb, backdrop))
635 return true;
636
637 CFX_Matrix new_matrix = mtObj2Device;
638 new_matrix.Translate(-rect.left, -rect.top);
639
640 RetainPtr<CFX_DIBitmap> pTextMask;
641 if (bTextClip) {
642 pTextMask = pdfium::MakeRetain<CFX_DIBitmap>();
643 if (!pTextMask->Create(width, height, FXDIB_Format::k8bppMask))
644 return true;
645
646 CFX_DefaultRenderDevice text_device;
647 text_device.Attach(pTextMask);
648 for (size_t i = 0; i < pPageObj->clip_path().GetTextCount(); ++i) {
649 CPDF_TextObject* textobj = pPageObj->clip_path().GetText(i);
650 if (!textobj)
651 break;
652
653 // TODO(thestig): Should we check the return value here?
655 &text_device, textobj->GetCharCodes(), textobj->GetCharPositions(),
656 textobj->text_state().GetFont().Get(),
657 textobj->text_state().GetFontSize(), textobj->GetTextMatrix(),
658 &new_matrix, textobj->graph_state().GetObject(), 0xffffffff, 0,
660 }
661 }
662 CPDF_RenderStatus bitmap_render(m_pContext, &bitmap_device);
663 bitmap_render.SetOptions(m_Options);
664 bitmap_render.SetStopObject(m_pStopObj);
665 bitmap_render.SetStdCS(true);
666 bitmap_render.SetDropObjects(m_bDropObjects);
667 bitmap_render.SetFormResource(std::move(pFormResource));
668 bitmap_render.Initialize(nullptr, nullptr);
669 bitmap_render.ProcessObjectNoClip(pPageObj, new_matrix);
670#if defined(PDF_USE_SKIA)
671 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
672 // Safe because `CFX_SkiaDeviceDriver` always uses pre-multiplied alpha.
673 // TODO(crbug.com/pdfium/2011): Remove the need for this.
674 bitmap_device.GetBitmap()->ForcePreMultiply();
675 }
676#endif
677 m_bStopped = bitmap_render.m_bStopped;
678 if (pSMaskDict) {
679 CFX_Matrix smask_matrix =
680 *pPageObj->general_state().GetSMaskMatrix() * mtObj2Device;
681 RetainPtr<CFX_DIBBase> pSMaskSource =
682 LoadSMask(pSMaskDict.Get(), &rect, smask_matrix);
683 if (pSMaskSource)
684 bitmap_device.MultiplyAlphaMask(pSMaskSource);
685 }
686 if (pTextMask) {
687 bitmap_device.MultiplyAlphaMask(pTextMask);
688 pTextMask.Reset();
689 }
690 if (transparency.IsGroup()) {
691 bitmap_device.MultiplyAlpha(group_alpha);
692 }
693 transparency = m_Transparency;
694 if (pPageObj->IsForm()) {
695 transparency.SetGroup();
696 }
697#if defined(PDF_USE_SKIA)
698 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
699 bitmap_device.GetBitmap()->UnPreMultiply();
700 }
701#endif
702 CompositeDIBitmap(bitmap_device.GetBitmap(), rect.left, rect.top,
703 /*mask_argb=*/0, /*alpha=*/1.0f, blend_type, transparency);
704 return true;
705}
706
707FX_RECT CPDF_RenderStatus::GetClippedBBox(const FX_RECT& rect) const {
708 FX_RECT bbox = rect;
709 bbox.Intersect(m_pDevice->GetClipBox());
710 return bbox;
711}
712
713RetainPtr<CFX_DIBitmap> CPDF_RenderStatus::GetBackdrop(
714 const CPDF_PageObject* pObj,
715 const FX_RECT& bbox,
716 bool bBackAlphaRequired) {
717 int width = bbox.Width();
718 int height = bbox.Height();
719 auto pBackdrop = pdfium::MakeRetain<CFX_DIBitmap>();
720 if (bBackAlphaRequired && !m_bDropObjects) {
721 if (!pBackdrop->Create(width, height, FXDIB_Format::kArgb)) {
722 return nullptr;
723 }
724 } else {
725 if (!m_pDevice->CreateCompatibleBitmap(pBackdrop, width, height)) {
726 return nullptr;
727 }
728 }
729
730 const int cap_to_check =
731 pBackdrop->IsAlphaFormat() ? FXRC_ALPHA_OUTPUT : FXRC_GET_BITS;
732 if (m_pDevice->GetRenderCaps() & cap_to_check) {
733 m_pDevice->GetDIBits(pBackdrop, bbox.left, bbox.top);
734 return pBackdrop;
735 }
736 CFX_Matrix FinalMatrix = m_DeviceMatrix;
737 FinalMatrix.Translate(-bbox.left, -bbox.top);
738 if (!pBackdrop->IsAlphaFormat()) {
739 pBackdrop->Clear(0xffffffff);
740 }
741
742 CFX_DefaultRenderDevice device;
743 device.Attach(pBackdrop);
744 m_pContext->Render(&device, pObj, &m_Options, &FinalMatrix);
745 return pBackdrop;
746}
747
748std::unique_ptr<CPDF_GraphicStates> CPDF_RenderStatus::CloneObjStates(
749 const CPDF_GraphicStates* pSrcStates,
750 bool stroke) {
751 if (!pSrcStates)
752 return nullptr;
753
754 auto pStates = std::make_unique<CPDF_GraphicStates>(*pSrcStates);
755 const CPDF_Color* pObjColor = stroke
758 if (!pObjColor->IsNull()) {
759 pStates->mutable_color_state().SetFillColorRef(
760 stroke ? pSrcStates->color_state().GetStrokeColorRef()
762 pStates->mutable_color_state().SetStrokeColorRef(
763 pStates->color_state().GetFillColorRef());
764 }
765 return pStates;
766}
767
768bool CPDF_RenderStatus::ProcessText(CPDF_TextObject* textobj,
769 const CFX_Matrix& mtObj2Device,
770 CFX_Path* clipping_path) {
771 if (textobj->GetCharCodes().empty())
772 return true;
773
774 const TextRenderingMode text_render_mode =
775 textobj->text_state().GetTextMode();
776 if (text_render_mode == TextRenderingMode::MODE_INVISIBLE)
777 return true;
778
779 RetainPtr<CPDF_Font> pFont = textobj->text_state().GetFont();
780 if (pFont->IsType3Font())
781 return ProcessType3Text(textobj, mtObj2Device);
782
783 bool is_fill = false;
784 bool is_stroke = false;
785 bool is_clip = false;
786 if (clipping_path) {
787 is_clip = true;
788 } else {
789 switch (text_render_mode) {
792 is_fill = true;
793 break;
796 if (pFont->HasFace())
797 is_stroke = true;
798 else
799 is_fill = true;
800 break;
803 is_fill = true;
804 if (pFont->HasFace())
805 is_stroke = true;
806 break;
808 // Already handled above, but the compiler is not smart enough to
809 // realize it.
810 NOTREACHED_NORETURN();
812 return true;
814 NOTREACHED_NORETURN();
815 }
816 }
817 FX_ARGB stroke_argb = 0;
818 FX_ARGB fill_argb = 0;
819 bool bPattern = false;
820 if (is_stroke) {
821 if (textobj->color_state().GetStrokeColor()->IsPattern()) {
822 bPattern = true;
823 } else {
824 stroke_argb = GetStrokeArgb(textobj);
825 }
826 }
827 if (is_fill) {
828 if (textobj->color_state().GetFillColor()->IsPattern()) {
829 bPattern = true;
830 } else {
831 fill_argb = GetFillArgb(textobj);
832 }
833 }
834 CFX_Matrix text_matrix = textobj->GetTextMatrix();
835 if (!IsAvailableMatrix(text_matrix))
836 return true;
837
838 float font_size = textobj->text_state().GetFontSize();
839 if (bPattern) {
840 DrawTextPathWithPattern(textobj, mtObj2Device, pFont.Get(), font_size,
841 text_matrix, is_fill, is_stroke);
842 return true;
843 }
844 if (is_clip || is_stroke) {
845 const CFX_Matrix* pDeviceMatrix = &mtObj2Device;
846 CFX_Matrix device_matrix;
847 if (is_stroke) {
848 pdfium::span<const float> pCTM = textobj->text_state().GetCTM();
849 if (pCTM[0] != 1.0f || pCTM[3] != 1.0f) {
850 CFX_Matrix ctm(pCTM[0], pCTM[1], pCTM[2], pCTM[3], 0, 0);
851 text_matrix *= ctm.GetInverse();
852 device_matrix = ctm * mtObj2Device;
853 pDeviceMatrix = &device_matrix;
854 }
855 }
856 return CPDF_TextRenderer::DrawTextPath(
857 m_pDevice, textobj->GetCharCodes(), textobj->GetCharPositions(),
858 pFont.Get(), font_size, text_matrix, pDeviceMatrix,
859 textobj->graph_state().GetObject(), fill_argb, stroke_argb,
860 clipping_path,
861 GetFillOptionsForDrawTextPath(m_Options.GetOptions(), textobj,
862 is_stroke, is_fill));
863 }
864 text_matrix.Concat(mtObj2Device);
865 return CPDF_TextRenderer::DrawNormalText(
866 m_pDevice, textobj->GetCharCodes(), textobj->GetCharPositions(),
867 pFont.Get(), font_size, text_matrix, fill_argb, m_Options);
868}
869
870// TODO(npm): Font fallback for type 3 fonts? (Completely separate code!!)
871bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj,
872 const CFX_Matrix& mtObj2Device) {
873 CPDF_Type3Font* pType3Font = textobj->text_state().GetFont()->AsType3Font();
874 if (pdfium::Contains(m_Type3FontCache, pType3Font))
875 return true;
876
877 FX_ARGB fill_argb = GetFillArgbForType3(textobj);
878 int fill_alpha = FXARGB_A(fill_argb);
879 if (m_bPrint && fill_alpha < 255)
880 return false;
881
882 CFX_Matrix text_matrix = textobj->GetTextMatrix();
883 CFX_Matrix char_matrix = pType3Font->GetFontMatrix();
884 float font_size = textobj->text_state().GetFontSize();
885 char_matrix.Scale(font_size, font_size);
886
887 // Must come before |glyphs|, because |glyphs| points into |refTypeCache|.
888 std::set<RetainPtr<CPDF_Type3Cache>> refTypeCache;
889 std::vector<TextGlyphPos> glyphs;
890 if (!m_bPrint)
891 glyphs.resize(textobj->GetCharCodes().size());
892
893 for (size_t iChar = 0; iChar < textobj->GetCharCodes().size(); ++iChar) {
894 uint32_t charcode = textobj->GetCharCodes()[iChar];
895 if (charcode == static_cast<uint32_t>(-1))
896 continue;
897
898 CPDF_Type3Char* pType3Char = pType3Font->LoadChar(charcode);
899 if (!pType3Char)
900 continue;
901
902 CFX_Matrix matrix = char_matrix;
903 matrix.e += iChar > 0 ? textobj->GetCharPositions()[iChar - 1] : 0;
904 matrix.Concat(text_matrix);
905 matrix.Concat(mtObj2Device);
906 if (!pType3Char->LoadBitmapFromSoleImageOfForm()) {
907 if (!glyphs.empty()) {
908 for (size_t i = 0; i < iChar; ++i) {
909 const TextGlyphPos& glyph = glyphs[i];
910 if (!glyph.m_pGlyph)
911 continue;
912
913 absl::optional<CFX_Point> point = glyph.GetOrigin({0, 0});
914 if (!point.has_value())
915 continue;
916
917 m_pDevice->SetBitMask(glyph.m_pGlyph->GetBitmap(), point->x, point->y,
918 fill_argb);
919 }
920 glyphs.clear();
921 }
922
923 std::unique_ptr<CPDF_GraphicStates> pStates =
924 CloneObjStates(&textobj->graphic_states(), false);
925 CPDF_RenderOptions options = m_Options;
926 options.GetOptions().bForceHalftone = true;
927 options.GetOptions().bRectAA = true;
928
929 const auto* pForm = static_cast<const CPDF_Form*>(pType3Char->form());
930 RetainPtr<const CPDF_Dictionary> pFormResource =
931 pForm->GetDict()->GetDictFor("Resources");
932
933 if (fill_alpha == 255) {
934 CPDF_RenderStatus status(m_pContext, m_pDevice);
935 status.SetOptions(options);
936 status.SetTransparency(pForm->GetTransparency());
937 status.SetType3Char(pType3Char);
938 status.SetFillColor(fill_argb);
939 status.SetDropObjects(m_bDropObjects);
940 status.SetFormResource(std::move(pFormResource));
941 status.Initialize(this, pStates.get());
942 status.m_Type3FontCache = m_Type3FontCache;
943 status.m_Type3FontCache.emplace_back(pType3Font);
944
945 CFX_RenderDevice::StateRestorer restorer(m_pDevice);
946 status.RenderObjectList(pForm, matrix);
947 } else {
948 FX_RECT rect =
949 matrix.TransformRect(pForm->CalcBoundingBox()).GetOuterRect();
950 if (!rect.Valid())
951 continue;
952
953 CFX_DefaultRenderDevice bitmap_device;
954 if (!bitmap_device.Create(rect.Width(), rect.Height(),
955 FXDIB_Format::kArgb, nullptr)) {
956 return true;
957 }
958 CPDF_RenderStatus status(m_pContext, &bitmap_device);
959 status.SetOptions(options);
960 status.SetTransparency(pForm->GetTransparency());
961 status.SetType3Char(pType3Char);
962 status.SetFillColor(fill_argb);
963 status.SetDropObjects(m_bDropObjects);
964 status.SetFormResource(std::move(pFormResource));
965 status.Initialize(this, pStates.get());
966 status.m_Type3FontCache = m_Type3FontCache;
967 status.m_Type3FontCache.emplace_back(pType3Font);
968 matrix.Translate(-rect.left, -rect.top);
969 status.RenderObjectList(pForm, matrix);
970 m_pDevice->SetDIBits(bitmap_device.GetBitmap(), rect.left, rect.top);
971 }
972 } else if (pType3Char->GetBitmap()) {
973 if (m_bPrint) {
974 CFX_Matrix image_matrix = pType3Char->matrix() * matrix;
975 CPDF_ImageRenderer renderer(this);
976 if (renderer.Start(pType3Char->GetBitmap(), fill_argb, image_matrix,
977 FXDIB_ResampleOptions(), false)) {
978 renderer.Continue(nullptr);
979 }
980 if (!renderer.GetResult())
981 return false;
982 } else {
983 CPDF_Document* pDoc = pType3Font->GetDocument();
984 RetainPtr<CPDF_Type3Cache> pCache =
985 CPDF_DocRenderData::FromDocument(pDoc)->GetCachedType3(pType3Font);
986
987 const CFX_GlyphBitmap* pBitmap = pCache->LoadGlyph(charcode, matrix);
988 if (!pBitmap)
989 continue;
990
991 refTypeCache.insert(std::move(pCache));
992
993 CFX_Point origin(FXSYS_roundf(matrix.e), FXSYS_roundf(matrix.f));
994 if (glyphs.empty()) {
995 FX_SAFE_INT32 left = origin.x;
996 left += pBitmap->left();
997 if (!left.IsValid())
998 continue;
999
1000 FX_SAFE_INT32 top = origin.y;
1001 top -= pBitmap->top();
1002 if (!top.IsValid())
1003 continue;
1004
1005 m_pDevice->SetBitMask(pBitmap->GetBitmap(), left.ValueOrDie(),
1006 top.ValueOrDie(), fill_argb);
1007 } else {
1008 glyphs[iChar].m_pGlyph = pBitmap;
1009 glyphs[iChar].m_Origin = origin;
1010 }
1011 }
1012 }
1013 }
1014
1015 if (glyphs.empty())
1016 return true;
1017
1018 FX_RECT rect = GetGlyphsBBox(glyphs, 0);
1019 auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
1020 if (!pBitmap->Create(rect.Width(), rect.Height(), FXDIB_Format::k8bppMask))
1021 return true;
1022
1023 for (const TextGlyphPos& glyph : glyphs) {
1024 if (!glyph.m_pGlyph || !glyph.m_pGlyph->GetBitmap()->IsMaskFormat())
1025 continue;
1026
1027 absl::optional<CFX_Point> point = glyph.GetOrigin({rect.left, rect.top});
1028 if (!point.has_value())
1029 continue;
1030
1031 pBitmap->CompositeMask(
1032 point->x, point->y, glyph.m_pGlyph->GetBitmap()->GetWidth(),
1033 glyph.m_pGlyph->GetBitmap()->GetHeight(), glyph.m_pGlyph->GetBitmap(),
1034 fill_argb, 0, 0, BlendMode::kNormal, nullptr, false);
1035 }
1036 m_pDevice->SetBitMask(pBitmap, rect.left, rect.top, fill_argb);
1037 return true;
1038}
1039
1040void CPDF_RenderStatus::DrawTextPathWithPattern(const CPDF_TextObject* textobj,
1041 const CFX_Matrix& mtObj2Device,
1042 CPDF_Font* pFont,
1043 float font_size,
1044 const CFX_Matrix& mtTextMatrix,
1045 bool fill,
1046 bool stroke) {
1047 if (!stroke) {
1048 std::vector<std::unique_ptr<CPDF_TextObject>> pCopy;
1049 pCopy.push_back(textobj->Clone());
1050
1051 CPDF_PathObject path;
1053 path.mutable_clip_path().CopyClipPath(m_LastClipPath);
1054 path.mutable_clip_path().AppendTexts(&pCopy);
1055 path.mutable_color_state() = textobj->color_state();
1056 path.mutable_general_state() = textobj->general_state();
1058 path.SetRect(textobj->GetRect());
1059
1060 AutoRestorer<UnownedPtr<const CPDF_PageObject>> restorer2(&m_pCurObj);
1061 RenderSingleObject(&path, mtObj2Device);
1062 return;
1063 }
1064
1065 std::vector<TextCharPos> char_pos_list = GetCharPosList(
1066 textobj->GetCharCodes(), textobj->GetCharPositions(), pFont, font_size);
1067 for (const TextCharPos& charpos : char_pos_list) {
1068 auto* font = charpos.m_FallbackFontPosition == -1
1069 ? pFont->GetFont()
1070 : pFont->GetFontFallback(charpos.m_FallbackFontPosition);
1071 const CFX_Path* pPath =
1072 font->LoadGlyphPath(charpos.m_GlyphIndex, charpos.m_FontCharWidth);
1073 if (!pPath)
1074 continue;
1075
1076 CPDF_PathObject path;
1077 path.mutable_graph_state() = textobj->graph_state();
1078 path.mutable_color_state() = textobj->color_state();
1079
1080 CFX_Matrix matrix = charpos.GetEffectiveMatrix(CFX_Matrix(
1081 font_size, 0, 0, font_size, charpos.m_Origin.x, charpos.m_Origin.y));
1082 matrix.Concat(mtTextMatrix);
1083 path.set_stroke(stroke);
1084 path.set_filltype(fill ? CFX_FillRenderOptions::FillType::kWinding
1085 : CFX_FillRenderOptions::FillType::kNoFill);
1086 path.path().Append(*pPath, &matrix);
1087 path.SetPathMatrix(CFX_Matrix());
1088 ProcessPath(&path, mtObj2Device);
1089 }
1090}
1091
1092void CPDF_RenderStatus::DrawShadingPattern(CPDF_ShadingPattern* pattern,
1093 const CPDF_PageObject* pPageObj,
1094 const CFX_Matrix& mtObj2Device,
1095 bool stroke) {
1096 if (!pattern->Load())
1097 return;
1098
1099 CFX_RenderDevice::StateRestorer restorer(m_pDevice);
1100 if (!ClipPattern(pPageObj, mtObj2Device, stroke))
1101 return;
1102
1103 FX_RECT rect = GetObjectClippedRect(pPageObj, mtObj2Device);
1104 if (rect.IsEmpty())
1105 return;
1106
1107 CFX_Matrix matrix = pattern->pattern_to_form() * mtObj2Device;
1108 int alpha =
1111 CPDF_RenderShading::Draw(m_pDevice, m_pContext, m_pCurObj, pattern, matrix,
1112 rect, alpha, m_Options);
1113}
1114
1115void CPDF_RenderStatus::ProcessShading(const CPDF_ShadingObject* pShadingObj,
1116 const CFX_Matrix& mtObj2Device) {
1117 FX_RECT rect = GetObjectClippedRect(pShadingObj, mtObj2Device);
1118 if (rect.IsEmpty())
1119 return;
1120
1121 CFX_Matrix matrix = pShadingObj->matrix() * mtObj2Device;
1122 CPDF_RenderShading::Draw(
1123 m_pDevice, m_pContext, m_pCurObj, pShadingObj->pattern(), matrix, rect,
1124 FXSYS_roundf(255 * pShadingObj->general_state().GetFillAlpha()),
1125 m_Options);
1126}
1127
1128void CPDF_RenderStatus::DrawTilingPattern(CPDF_TilingPattern* pattern,
1129 CPDF_PageObject* pPageObj,
1130 const CFX_Matrix& mtObj2Device,
1131 bool stroke) {
1132 const std::unique_ptr<CPDF_Form> pPatternForm = pattern->Load(pPageObj);
1133 if (!pPatternForm)
1134 return;
1135
1136 CFX_RenderDevice::StateRestorer restorer(m_pDevice);
1137 if (!ClipPattern(pPageObj, mtObj2Device, stroke))
1138 return;
1139
1140 FX_RECT clip_box = m_pDevice->GetClipBox();
1141 if (clip_box.IsEmpty())
1142 return;
1143
1144 RetainPtr<CFX_DIBitmap> pScreen =
1145 CPDF_RenderTiling::Draw(this, pPageObj, pattern, pPatternForm.get(),
1146 mtObj2Device, clip_box, stroke);
1147 if (!pScreen)
1148 return;
1149
1150 constexpr FX_ARGB kMask = 0;
1151 CompositeDIBitmap(pScreen, clip_box.left, clip_box.top, kMask, /*alpha=*/1.0f,
1153}
1154
1155void CPDF_RenderStatus::DrawPathWithPattern(CPDF_PathObject* path_obj,
1156 const CFX_Matrix& mtObj2Device,
1157 const CPDF_Color* pColor,
1158 bool stroke) {
1159 RetainPtr<CPDF_Pattern> pattern = pColor->GetPattern();
1160 if (!pattern)
1161 return;
1162
1163 if (CPDF_TilingPattern* pTilingPattern = pattern->AsTilingPattern())
1164 DrawTilingPattern(pTilingPattern, path_obj, mtObj2Device, stroke);
1165 else if (CPDF_ShadingPattern* pShadingPattern = pattern->AsShadingPattern())
1166 DrawShadingPattern(pShadingPattern, path_obj, mtObj2Device, stroke);
1167}
1168
1169void CPDF_RenderStatus::ProcessPathPattern(
1170 CPDF_PathObject* path_obj,
1171 const CFX_Matrix& mtObj2Device,
1172 CFX_FillRenderOptions::FillType* fill_type,
1173 bool* stroke) {
1174 DCHECK(fill_type);
1175 DCHECK(stroke);
1176
1177 if (*fill_type != CFX_FillRenderOptions::FillType::kNoFill) {
1178 const CPDF_Color& FillColor = *path_obj->color_state().GetFillColor();
1179 if (FillColor.IsPattern()) {
1180 DrawPathWithPattern(path_obj, mtObj2Device, &FillColor, false);
1182 }
1183 }
1184 if (*stroke) {
1185 const CPDF_Color& StrokeColor = *path_obj->color_state().GetStrokeColor();
1186 if (StrokeColor.IsPattern()) {
1187 DrawPathWithPattern(path_obj, mtObj2Device, &StrokeColor, true);
1188 *stroke = false;
1189 }
1190 }
1191}
1192
1193bool CPDF_RenderStatus::ProcessImage(CPDF_ImageObject* pImageObj,
1194 const CFX_Matrix& mtObj2Device) {
1195 CPDF_ImageRenderer render(this);
1196 if (render.Start(pImageObj, mtObj2Device, m_bStdCS, m_curBlend))
1197 render.Continue(nullptr);
1198 return render.GetResult();
1199}
1200
1202 const RetainPtr<CFX_DIBitmap>& pDIBitmap,
1203 int left,
1204 int top,
1205 FX_ARGB mask_argb,
1206 float alpha,
1207 BlendMode blend_mode,
1208 const CPDF_Transparency& transparency) {
1209 CHECK(pDIBitmap);
1210
1211 if (blend_mode == BlendMode::kNormal) {
1212 if (!pDIBitmap->IsMaskFormat()) {
1213 if (alpha != 1.0f) {
1214 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
1215 std::unique_ptr<CFX_ImageRenderer> dummy;
1216 CFX_Matrix matrix = CFX_RenderDevice::GetFlipMatrix(
1217 pDIBitmap->GetWidth(), pDIBitmap->GetHeight(), left, top);
1218 m_pDevice->StartDIBits(std::move(pDIBitmap), alpha, /*argb=*/0,
1219 matrix, FXDIB_ResampleOptions(), &dummy);
1220 return;
1221 }
1222 pDIBitmap->MultiplyAlpha(alpha);
1223 }
1224 if (m_pDevice->SetDIBits(pDIBitmap, left, top)) {
1225 return;
1226 }
1227 } else {
1228 uint32_t fill_argb = m_Options.TranslateColor(mask_argb);
1229 if (alpha != 1.0f) {
1230 uint8_t* fill_argb8 = reinterpret_cast<uint8_t*>(&fill_argb);
1231 fill_argb8[3] *= FXSYS_roundf(alpha * 255) / 255;
1232 }
1233 if (m_pDevice->SetBitMask(pDIBitmap, left, top, fill_argb)) {
1234 return;
1235 }
1236 }
1237 }
1238 bool bIsolated = transparency.IsIsolated();
1239 bool bBackAlphaRequired =
1240 blend_mode != BlendMode::kNormal && bIsolated && !m_bDropObjects;
1241 bool bGetBackGround =
1242 ((m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT)) ||
1243 (!(m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT) &&
1244 (m_pDevice->GetRenderCaps() & FXRC_GET_BITS) && !bBackAlphaRequired);
1245 if (bGetBackGround) {
1246 if (bIsolated || !transparency.IsGroup()) {
1247 if (!pDIBitmap->IsMaskFormat())
1248 m_pDevice->SetDIBitsWithBlend(pDIBitmap, left, top, blend_mode);
1249 return;
1250 }
1251
1252 FX_RECT rect(left, top, left + pDIBitmap->GetWidth(),
1253 top + pDIBitmap->GetHeight());
1254 rect.Intersect(m_pDevice->GetClipBox());
1255 RetainPtr<CFX_DIBitmap> pClone;
1256 if (m_pDevice->GetBackDrop() && m_pDevice->GetBitmap()) {
1257 pClone = m_pDevice->GetBackDrop()->ClipTo(rect);
1258 if (!pClone)
1259 return;
1260
1261 pClone->CompositeBitmap(0, 0, pClone->GetWidth(), pClone->GetHeight(),
1262 m_pDevice->GetBitmap(), rect.left, rect.top,
1263 BlendMode::kNormal, nullptr, false);
1264 left = std::min(left, 0);
1265 top = std::min(top, 0);
1266 if (pDIBitmap->IsMaskFormat()) {
1267 pClone->CompositeMask(0, 0, pClone->GetWidth(), pClone->GetHeight(),
1268 pDIBitmap, mask_argb, left, top, blend_mode,
1269 nullptr, false);
1270 } else {
1271 pClone->CompositeBitmap(0, 0, pClone->GetWidth(), pClone->GetHeight(),
1272 pDIBitmap, left, top, blend_mode, nullptr,
1273 false);
1274 }
1275 } else {
1276 pClone = pDIBitmap;
1277 }
1278 if (m_pDevice->GetBackDrop()) {
1279 m_pDevice->SetDIBits(pClone, rect.left, rect.top);
1280 } else {
1281 if (!pDIBitmap->IsMaskFormat()) {
1282 m_pDevice->SetDIBitsWithBlend(pDIBitmap, rect.left, rect.top,
1283 blend_mode);
1284 }
1285 }
1286 return;
1287 }
1288 FX_RECT bbox = GetClippedBBox(FX_RECT(left, top, left + pDIBitmap->GetWidth(),
1289 top + pDIBitmap->GetHeight()));
1290 RetainPtr<CFX_DIBitmap> pBackdrop = GetBackdrop(
1291 m_pCurObj, bbox, blend_mode != BlendMode::kNormal && bIsolated);
1292 if (!pBackdrop)
1293 return;
1294
1295 if (pDIBitmap->IsMaskFormat()) {
1296 pBackdrop->CompositeMask(left - bbox.left, top - bbox.top,
1297 pDIBitmap->GetWidth(), pDIBitmap->GetHeight(),
1298 pDIBitmap, mask_argb, 0, 0, blend_mode, nullptr,
1299 false);
1300 } else {
1301 pBackdrop->CompositeBitmap(left - bbox.left, top - bbox.top,
1302 pDIBitmap->GetWidth(), pDIBitmap->GetHeight(),
1303 pDIBitmap, 0, 0, blend_mode, nullptr, false);
1304 }
1305
1306 auto pBackdrop1 = pdfium::MakeRetain<CFX_DIBitmap>();
1307 pBackdrop1->Create(pBackdrop->GetWidth(), pBackdrop->GetHeight(),
1309 pBackdrop1->Clear((uint32_t)-1);
1310 pBackdrop1->CompositeBitmap(0, 0, pBackdrop->GetWidth(),
1311 pBackdrop->GetHeight(), pBackdrop, 0, 0,
1312 BlendMode::kNormal, nullptr, false);
1313 pBackdrop = std::move(pBackdrop1);
1314 m_pDevice->SetDIBits(pBackdrop, bbox.left, bbox.top);
1315}
1316
1317RetainPtr<CFX_DIBitmap> CPDF_RenderStatus::LoadSMask(
1318 CPDF_Dictionary* pSMaskDict,
1319 FX_RECT* pClipRect,
1320 const CFX_Matrix& mtMatrix) {
1321 if (!pSMaskDict)
1322 return nullptr;
1323
1324 RetainPtr<CPDF_Stream> pGroup =
1325 pSMaskDict->GetMutableStreamFor(pdfium::transparency::kG);
1326 if (!pGroup)
1327 return nullptr;
1328
1329 std::unique_ptr<CPDF_Function> pFunc;
1330 RetainPtr<const CPDF_Object> pFuncObj =
1331 pSMaskDict->GetDirectObjectFor(pdfium::transparency::kTR);
1332 if (pFuncObj && (pFuncObj->IsDictionary() || pFuncObj->IsStream()))
1333 pFunc = CPDF_Function::Load(std::move(pFuncObj));
1334
1335 CFX_Matrix matrix = mtMatrix;
1336 matrix.Translate(-pClipRect->left, -pClipRect->top);
1337
1338 CPDF_Form form(m_pContext->GetDocument(),
1339 m_pContext->GetMutablePageResources(), pGroup);
1340 form.ParseContent();
1341
1342 CFX_DefaultRenderDevice bitmap_device;
1343 bool bLuminosity =
1346 int width = pClipRect->right - pClipRect->left;
1347 int height = pClipRect->bottom - pClipRect->top;
1348 FXDIB_Format format = GetFormatForLuminosity(bLuminosity);
1349 if (!bitmap_device.Create(width, height, format, nullptr))
1350 return nullptr;
1351
1353 const FX_ARGB background_color =
1354 bLuminosity
1355 ? GetBackgroundColor(pSMaskDict, pGroup->GetDict().Get(), &nCSFamily)
1356 : 0;
1357 bitmap_device.Clear(background_color);
1358
1359 RetainPtr<const CPDF_Dictionary> pFormResource =
1360 form.GetDict()->GetDictFor("Resources");
1361 CPDF_RenderOptions options;
1364 CPDF_RenderStatus status(m_pContext, &bitmap_device);
1365 status.SetOptions(options);
1366 status.SetGroupFamily(nCSFamily);
1367 status.SetLoadMask(bLuminosity);
1368 status.SetStdCS(true);
1369 status.SetFormResource(std::move(pFormResource));
1370 status.SetDropObjects(m_bDropObjects);
1371 status.Initialize(nullptr, nullptr);
1372 status.RenderObjectList(&form, matrix);
1373
1374 auto pMask = pdfium::MakeRetain<CFX_DIBitmap>();
1375 if (!pMask->Create(width, height, FXDIB_Format::k8bppMask))
1376 return nullptr;
1377
1378 pdfium::span<uint8_t> dest_buf = pMask->GetWritableBuffer();
1379 RetainPtr<const CFX_DIBitmap> bitmap = bitmap_device.GetBitmap();
1380 pdfium::span<const uint8_t> src_buf = bitmap->GetBuffer();
1381 int dest_pitch = pMask->GetPitch();
1382 int src_pitch = bitmap->GetPitch();
1383 DataVector<uint8_t> transfers(256);
1384 if (pFunc) {
1385 std::vector<float> results(pFunc->CountOutputs());
1386 for (size_t i = 0; i < transfers.size(); ++i) {
1387 float input = i / 255.0f;
1388 pFunc->Call(pdfium::span_from_ref(input), results);
1389 transfers[i] = FXSYS_roundf(results[0] * 255);
1390 }
1391 } else {
1392 // Fill |transfers| with 0, 1, ... N.
1393 std::iota(transfers.begin(), transfers.end(), 0);
1394 }
1395 if (bLuminosity) {
1396 const int Bpp = bitmap->GetBPP() / 8;
1397 for (int row = 0; row < height; row++) {
1398 const size_t dest_offset = Fx2DSizeOrDie(row, dest_pitch);
1399 const size_t src_offset = Fx2DSizeOrDie(row, src_pitch);
1400 uint8_t* dest_pos = dest_buf.subspan(dest_offset).data();
1401 const uint8_t* src_pos = src_buf.subspan(src_offset).data();
1402 for (int col = 0; col < width; col++) {
1403 *dest_pos++ = transfers[FXRGB2GRAY(src_pos[2], src_pos[1], *src_pos)];
1404 src_pos += Bpp;
1405 }
1406 }
1407 } else if (pFunc) {
1408 int size = dest_pitch * height;
1409 for (int i = 0; i < size; i++) {
1410 dest_buf[i] = transfers[src_buf[i]];
1411 }
1412 } else {
1413 fxcrt::spancpy(dest_buf, src_buf.first(dest_pitch * height));
1414 }
1415 return pMask;
1416}
1417
1418FX_ARGB CPDF_RenderStatus::GetBackgroundColor(
1419 const CPDF_Dictionary* pSMaskDict,
1420 const CPDF_Dictionary* pGroupDict,
1421 CPDF_ColorSpace::Family* pCSFamily) {
1422 static constexpr FX_ARGB kDefaultColor = ArgbEncode(255, 0, 0, 0);
1423 RetainPtr<const CPDF_Array> pBC =
1424 pSMaskDict->GetArrayFor(pdfium::transparency::kBC);
1425 if (!pBC)
1426 return kDefaultColor;
1427
1428 RetainPtr<const CPDF_Object> pCSObj;
1429 RetainPtr<const CPDF_Dictionary> pGroup =
1430 pGroupDict ? pGroupDict->GetDictFor("Group") : nullptr;
1431 if (pGroup)
1432 pCSObj = pGroup->GetDirectObjectFor(pdfium::transparency::kCS);
1434 CPDF_DocPageData::FromDocument(m_pContext->GetDocument())
1435 ->GetColorSpace(pCSObj.Get(), nullptr);
1436 if (!pCS)
1437 return kDefaultColor;
1438
1439 CPDF_ColorSpace::Family family = pCS->GetFamily();
1440 if (family == CPDF_ColorSpace::Family::kLab || pCS->IsSpecial() ||
1441 (family == CPDF_ColorSpace::Family::kICCBased && !pCS->IsNormal())) {
1442 return kDefaultColor;
1443 }
1444
1445 // Store Color Space Family to use in CPDF_RenderStatus::Initialize().
1446 *pCSFamily = family;
1447
1448 uint32_t comps = std::max(8u, pCS->CountComponents());
1449 size_t count = std::min<size_t>(8, pBC->size());
1450 std::vector<float> floats = ReadArrayElementsToVector(pBC.Get(), count);
1451 floats.resize(comps);
1452
1453 float R;
1454 float G;
1455 float B;
1456 pCS->GetRGB(floats, &R, &G, &B);
1457 return ArgbEncode(255, static_cast<int>(R * 255), static_cast<int>(G * 255),
1458 static_cast<int>(B * 255));
1459}
bool Create(int width, int height, FXDIB_Format format, RetainPtr< CFX_DIBitmap > pBackdropBitmap)
bool Attach(RetainPtr< CFX_DIBitmap > pBitmap)
CFX_Matrix & operator=(const CFX_Matrix &other)=default
CFX_Matrix & operator*=(const CFX_Matrix &other)
CFX_Matrix operator*(const CFX_Matrix &right) const
void Translate(int32_t x, int32_t y)
CFX_Matrix GetInverse() const
void Scale(float sx, float sy)
void Concat(const CFX_Matrix &right)
void AppendRect(float left, float bottom, float right, float top)
Definition cfx_path.cpp:309
bool MultiplyAlpha(float alpha)
bool MultiplyAlphaMask(const RetainPtr< const CFX_DIBBase > &mask)
bool HasRef() const
FX_COLORREF GetStrokeColorRef() const
FX_COLORREF GetFillColorRef() const
const CPDF_Color * GetStrokeColor() const
bool HasRef() const
const CPDF_Color * GetFillColor() const
bool IsPattern() const
bool IsNull() const
Definition cpdf_color.h:31
ByteString GetByteStringFor(const ByteString &key) const
CPDF_Document * GetDocument() const
Definition cpdf_font.h:128
const CPDF_Form * form() const
const CFX_Matrix & form_matrix() const
void ParseContent()
Definition cpdf_form.cpp:62
float GetStrokeAlpha() const
RetainPtr< CPDF_TransferFunc > GetTransferFunc() const
BlendMode GetBlendType() const
const CFX_Matrix * GetSMaskMatrix() const
const CPDF_ColorState & color_state() const
CPDF_ImageRenderer(CPDF_RenderStatus *pStatus)
bool Continue(PauseIndicatorIface *pPause)
bool Start(CPDF_ImageObject *pImageObject, const CFX_Matrix &mtObj2Device, bool bStdCS, BlendMode blendType)
const CPDF_Transparency & GetTransparency() const
const CPDF_GraphicStates & graphic_states() const
virtual const CPDF_PathObject * AsPath() const
void SetRect(const CFX_FloatRect &rect)
const CPDF_ColorState & color_state() const
const CPDF_GeneralState & general_state() const
virtual bool IsImage() const
const CPDF_ClipPath & clip_path() const
virtual Type GetType() const =0
CPDF_GeneralState & mutable_general_state()
FX_RECT GetTransformedBBox(const CFX_Matrix &matrix) const
virtual CPDF_ShadingObject * AsShading()
const CFX_FloatRect & GetRect() const
virtual CPDF_ImageObject * AsImage()
virtual CPDF_FormObject * AsForm()
virtual CPDF_TextObject * AsText()
const CPDF_TextState & text_state() const
virtual CPDF_PathObject * AsPath()
const CFX_GraphState & graph_state() const
virtual bool IsForm() const
CPDF_ClipPath & mutable_clip_path()
virtual bool IsPath() const
CPDF_ColorState & mutable_color_state()
bool stroke() const
void set_filltype(CFX_FillRenderOptions::FillType fill_type)
CPDF_Path & path()
const CFX_Matrix & matrix() const
CFX_FillRenderOptions::FillType filltype() const
void AppendFloatRect(const CFX_FloatRect &rect)
Definition cpdf_path.cpp:49
const CFX_Matrix & pattern_to_form() const
void SetColorMode(Type mode)
void SetDropObjects(bool bDropObjects)
void SetType3Char(CPDF_Type3Char *pType3Char)
RetainPtr< CPDF_TransferFunc > GetTransferFunc(RetainPtr< const CPDF_Object > pObject) const
void SetDeviceMatrix(const CFX_Matrix &matrix)
CPDF_RenderStatus(CPDF_RenderContext *pContext, CFX_RenderDevice *pDevice)
void SetFillColor(FX_ARGB color)
void SetOptions(const CPDF_RenderOptions &options)
void RenderSingleObject(CPDF_PageObject *pObj, const CFX_Matrix &mtObj2Device)
bool ContinueSingleObject(CPDF_PageObject *pObj, const CFX_Matrix &mtObj2Device, PauseIndicatorIface *pPause)
void SetTransparency(const CPDF_Transparency &transparency)
void RenderObjectList(const CPDF_PageObjectHolder *pObjectHolder, const CFX_Matrix &mtObj2Device)
void SetStdCS(bool bStdCS)
FX_ARGB GetFillArgbForType3(CPDF_PageObject *pObj) const
void SetGroupFamily(CPDF_ColorSpace::Family family)
void DrawShadingPattern(CPDF_ShadingPattern *pattern, const CPDF_PageObject *pPageObj, const CFX_Matrix &mtObj2Device, bool stroke)
void Initialize(const CPDF_RenderStatus *pParentStatus, const CPDF_GraphicStates *pInitialStates)
void CompositeDIBitmap(const RetainPtr< CFX_DIBitmap > &pDIBitmap, int left, int top, FX_ARGB mask_argb, float alpha, BlendMode blend_mode, const CPDF_Transparency &transparency)
void DrawTilingPattern(CPDF_TilingPattern *pattern, CPDF_PageObject *pPageObj, const CFX_Matrix &mtObj2Device, bool stroke)
void SetFormResource(RetainPtr< const CPDF_Dictionary > pRes)
FX_ARGB GetFillArgb(CPDF_PageObject *pObj) const
void SetLoadMask(bool bLoadMask)
void ProcessClipPath(const CPDF_ClipPath &ClipPath, const CFX_Matrix &mtObj2Device)
const CFX_Matrix & GetMatrix() const
const CFX_Matrix & matrix() const
CFX_Matrix GetTextMatrix() const
static bool DrawTextPath(CFX_RenderDevice *pDevice, pdfium::span< const uint32_t > char_codes, pdfium::span< const float > char_pos, CPDF_Font *pFont, float font_size, const CFX_Matrix &mtText2User, const CFX_Matrix *pUser2Device, const CFX_GraphStateData *pGraphState, FX_ARGB fill_argb, FX_ARGB stroke_argb, CFX_Path *pClippingPath, const CFX_FillRenderOptions &fill_options)
bool IsIsolated() const
CPDF_Transparency & operator=(const CPDF_Transparency &other)
const CFX_Matrix & matrix() const
RetainPtr< CFX_DIBitmap > GetBitmap()
const CPDF_Font::FormIface * form() const
bool LoadBitmapFromSoleImageOfForm()
bool colored() const
CPDF_Type3Char * LoadChar(uint32_t charcode)
CFX_Matrix & GetFontMatrix()
bool operator!=(const char *ptr) const
Definition bytestring.h:130
TextRenderingMode
BlendMode
Definition fx_dib.h:49
#define FXRGB2GRAY(r, g, b)
Definition fx_dib.h:131
#define FXARGB_A(argb)
Definition fx_dib.h:124
constexpr FX_ARGB ArgbEncode(uint32_t a, uint32_t r, uint32_t g, uint32_t b)
Definition fx_dib.h:118
FXDIB_Format
Definition fx_dib.h:19
int FXSYS_roundf(float f)
Definition fx_system.cpp:92
const char kSoftMaskSubType[]
#define CHECK(cvref)
#define FXDC_RENDER_CAPS
#define FXRC_ALPHA_OUTPUT
#define FXRC_BLEND_MODE
#define FXRC_GET_BITS
#define FXRC_SOFT_CLIP
constexpr CFX_FillRenderOptions(FillType fill_type)
static constexpr CFX_FillRenderOptions WindingOptions()
int Height() const
int32_t bottom
bool Valid() const
int32_t right
int Width() const
int32_t top
int32_t left
bool IsEmpty() const