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