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