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