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/fpdfdoc/cpdf_annot.h"
8 
9 #include <algorithm>
10 #include <utility>
11 
12 #include "constants/annotation_common.h"
13 #include "constants/annotation_flags.h"
14 #include "core/fpdfapi/page/cpdf_form.h"
15 #include "core/fpdfapi/page/cpdf_page.h"
16 #include "core/fpdfapi/parser/cpdf_array.h"
17 #include "core/fpdfapi/parser/cpdf_boolean.h"
18 #include "core/fpdfapi/parser/cpdf_dictionary.h"
19 #include "core/fpdfapi/parser/cpdf_document.h"
20 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
21 #include "core/fpdfapi/render/cpdf_pagerendercache.h"
22 #include "core/fpdfapi/render/cpdf_rendercontext.h"
23 #include "core/fpdfapi/render/cpdf_renderoptions.h"
24 #include "core/fpdfdoc/cpvt_generateap.h"
25 #include "core/fxge/cfx_graphstatedata.h"
26 #include "core/fxge/cfx_pathdata.h"
27 #include "core/fxge/cfx_renderdevice.h"
28 #include "third_party/base/ptr_util.h"
29 
30 namespace {
31 
32 const char kPDFiumKey_HasGeneratedAP[] = "PDFIUM_HasGeneratedAP";
33 
IsTextMarkupAnnotation(CPDF_Annot::Subtype type)34 bool IsTextMarkupAnnotation(CPDF_Annot::Subtype type) {
35   return type == CPDF_Annot::Subtype::HIGHLIGHT ||
36          type == CPDF_Annot::Subtype::SQUIGGLY ||
37          type == CPDF_Annot::Subtype::STRIKEOUT ||
38          type == CPDF_Annot::Subtype::UNDERLINE;
39 }
40 
AnnotGetMatrix(const CPDF_Page * pPage,CPDF_Annot * pAnnot,CPDF_Annot::AppearanceMode mode,const CFX_Matrix * pUser2Device,CFX_Matrix * matrix)41 CPDF_Form* AnnotGetMatrix(const CPDF_Page* pPage,
42                           CPDF_Annot* pAnnot,
43                           CPDF_Annot::AppearanceMode mode,
44                           const CFX_Matrix* pUser2Device,
45                           CFX_Matrix* matrix) {
46   CPDF_Form* pForm = pAnnot->GetAPForm(pPage, mode);
47   if (!pForm)
48     return nullptr;
49 
50   CFX_Matrix form_matrix = pForm->GetDict()->GetMatrixFor("Matrix");
51   CFX_FloatRect form_bbox =
52       form_matrix.TransformRect(pForm->GetDict()->GetRectFor("BBox"));
53   matrix->MatchRect(pAnnot->GetRect(), form_bbox);
54   matrix->Concat(*pUser2Device);
55   return pForm;
56 }
57 
GetAnnotAPInternal(CPDF_Dictionary * pAnnotDict,CPDF_Annot::AppearanceMode eMode,bool bFallbackToNormal)58 CPDF_Stream* GetAnnotAPInternal(CPDF_Dictionary* pAnnotDict,
59                                 CPDF_Annot::AppearanceMode eMode,
60                                 bool bFallbackToNormal) {
61   CPDF_Dictionary* pAP = pAnnotDict->GetDictFor(pdfium::annotation::kAP);
62   if (!pAP)
63     return nullptr;
64 
65   const char* ap_entry = "N";
66   if (eMode == CPDF_Annot::Down)
67     ap_entry = "D";
68   else if (eMode == CPDF_Annot::Rollover)
69     ap_entry = "R";
70   if (bFallbackToNormal && !pAP->KeyExist(ap_entry))
71     ap_entry = "N";
72 
73   CPDF_Object* psub = pAP->GetDirectObjectFor(ap_entry);
74   if (!psub)
75     return nullptr;
76   if (CPDF_Stream* pStream = psub->AsStream())
77     return pStream;
78 
79   CPDF_Dictionary* pDict = psub->AsDictionary();
80   if (!pDict)
81     return nullptr;
82 
83   ByteString as = pAnnotDict->GetStringFor(pdfium::annotation::kAS);
84   if (as.IsEmpty()) {
85     ByteString value = pAnnotDict->GetStringFor("V");
86     if (value.IsEmpty()) {
87       const CPDF_Dictionary* pParentDict = pAnnotDict->GetDictFor("Parent");
88       value = pParentDict ? pParentDict->GetStringFor("V") : ByteString();
89     }
90     as = (!value.IsEmpty() && pDict->KeyExist(value)) ? value : "Off";
91   }
92   return pDict->GetStreamFor(as);
93 }
94 
95 }  // namespace
96 
CPDF_Annot(RetainPtr<CPDF_Dictionary> pDict,CPDF_Document * pDocument)97 CPDF_Annot::CPDF_Annot(RetainPtr<CPDF_Dictionary> pDict,
98                        CPDF_Document* pDocument)
99     : m_pAnnotDict(std::move(pDict)), m_pDocument(pDocument) {
100   Init();
101 }
102 
CPDF_Annot(CPDF_Dictionary * pDict,CPDF_Document * pDocument)103 CPDF_Annot::CPDF_Annot(CPDF_Dictionary* pDict, CPDF_Document* pDocument)
104     : m_pAnnotDict(pDict), m_pDocument(pDocument) {
105   Init();
106 }
107 
~CPDF_Annot()108 CPDF_Annot::~CPDF_Annot() {
109   ClearCachedAP();
110 }
111 
Init()112 void CPDF_Annot::Init() {
113   m_nSubtype = StringToAnnotSubtype(
114       m_pAnnotDict->GetStringFor(pdfium::annotation::kSubtype));
115   m_bIsTextMarkupAnnotation = IsTextMarkupAnnotation(m_nSubtype);
116   m_bHasGeneratedAP =
117       m_pAnnotDict->GetBooleanFor(kPDFiumKey_HasGeneratedAP, false);
118   GenerateAPIfNeeded();
119 }
120 
GenerateAPIfNeeded()121 void CPDF_Annot::GenerateAPIfNeeded() {
122   if (!ShouldGenerateAP())
123     return;
124   if (!CPVT_GenerateAP::GenerateAnnotAP(m_pDocument.Get(), m_pAnnotDict.Get(),
125                                         m_nSubtype)) {
126     return;
127   }
128 
129   m_pAnnotDict->SetNewFor<CPDF_Boolean>(kPDFiumKey_HasGeneratedAP, true);
130   m_bHasGeneratedAP = true;
131 }
132 
ShouldGenerateAP() const133 bool CPDF_Annot::ShouldGenerateAP() const {
134   // If AP dictionary exists and defines an appearance for normal mode, we use
135   // the appearance defined in the existing AP dictionary.
136   const CPDF_Dictionary* pAP =
137       m_pAnnotDict->GetDictFor(pdfium::annotation::kAP);
138   if (pAP && pAP->GetDictFor("N"))
139     return false;
140 
141   return !IsHidden();
142 }
143 
ShouldDrawAnnotation() const144 bool CPDF_Annot::ShouldDrawAnnotation() const {
145   if (IsHidden())
146     return false;
147   return m_bOpenState || m_nSubtype != CPDF_Annot::Subtype::POPUP;
148 }
149 
ClearCachedAP()150 void CPDF_Annot::ClearCachedAP() {
151   m_APMap.clear();
152 }
153 
GetSubtype() const154 CPDF_Annot::Subtype CPDF_Annot::GetSubtype() const {
155   return m_nSubtype;
156 }
157 
RectForDrawing() const158 CFX_FloatRect CPDF_Annot::RectForDrawing() const {
159   bool bShouldUseQuadPointsCoords =
160       m_bIsTextMarkupAnnotation && m_bHasGeneratedAP;
161   if (bShouldUseQuadPointsCoords)
162     return BoundingRectFromQuadPoints(m_pAnnotDict.Get());
163   return m_pAnnotDict->GetRectFor(pdfium::annotation::kRect);
164 }
165 
GetRect() const166 CFX_FloatRect CPDF_Annot::GetRect() const {
167   CFX_FloatRect rect = RectForDrawing();
168   rect.Normalize();
169   return rect;
170 }
171 
GetFlags() const172 uint32_t CPDF_Annot::GetFlags() const {
173   return m_pAnnotDict->GetIntegerFor(pdfium::annotation::kF);
174 }
175 
IsHidden() const176 bool CPDF_Annot::IsHidden() const {
177   return !!(GetFlags() & pdfium::annotation_flags::kHidden);
178 }
179 
GetAnnotAP(CPDF_Dictionary * pAnnotDict,CPDF_Annot::AppearanceMode eMode)180 CPDF_Stream* GetAnnotAP(CPDF_Dictionary* pAnnotDict,
181                         CPDF_Annot::AppearanceMode eMode) {
182   ASSERT(pAnnotDict);
183   return GetAnnotAPInternal(pAnnotDict, eMode, true);
184 }
185 
GetAnnotAPNoFallback(CPDF_Dictionary * pAnnotDict,CPDF_Annot::AppearanceMode eMode)186 CPDF_Stream* GetAnnotAPNoFallback(CPDF_Dictionary* pAnnotDict,
187                                   CPDF_Annot::AppearanceMode eMode) {
188   ASSERT(pAnnotDict);
189   return GetAnnotAPInternal(pAnnotDict, eMode, false);
190 }
191 
GetAPForm(const CPDF_Page * pPage,AppearanceMode mode)192 CPDF_Form* CPDF_Annot::GetAPForm(const CPDF_Page* pPage, AppearanceMode mode) {
193   CPDF_Stream* pStream = GetAnnotAP(m_pAnnotDict.Get(), mode);
194   if (!pStream)
195     return nullptr;
196 
197   auto it = m_APMap.find(pStream);
198   if (it != m_APMap.end())
199     return it->second.get();
200 
201   auto pNewForm = pdfium::MakeUnique<CPDF_Form>(
202       m_pDocument.Get(), pPage->m_pResources.Get(), pStream);
203   pNewForm->ParseContent();
204 
205   CPDF_Form* pResult = pNewForm.get();
206   m_APMap[pStream] = std::move(pNewForm);
207   return pResult;
208 }
209 
210 // static
RectFromQuadPointsArray(const CPDF_Array * pArray,size_t nIndex)211 CFX_FloatRect CPDF_Annot::RectFromQuadPointsArray(const CPDF_Array* pArray,
212                                                   size_t nIndex) {
213   ASSERT(pArray);
214   ASSERT(nIndex < pArray->size() / 8);
215 
216   // QuadPoints are defined with 4 pairs of numbers
217   // ([ pair0, pair1, pair2, pair3 ]), where
218   // pair0 = top_left
219   // pair1 = top_right
220   // pair2 = bottom_left
221   // pair3 = bottom_right
222   //
223   // On the other hand, /Rect is defined as 2 pairs [pair0, pair1] where:
224   // pair0 = bottom_left
225   // pair1 = top_right.
226 
227   return CFX_FloatRect(
228       pArray->GetNumberAt(4 + nIndex * 8), pArray->GetNumberAt(5 + nIndex * 8),
229       pArray->GetNumberAt(2 + nIndex * 8), pArray->GetNumberAt(3 + nIndex * 8));
230 }
231 
232 // static
BoundingRectFromQuadPoints(const CPDF_Dictionary * pAnnotDict)233 CFX_FloatRect CPDF_Annot::BoundingRectFromQuadPoints(
234     const CPDF_Dictionary* pAnnotDict) {
235   CFX_FloatRect ret;
236   const CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
237   size_t nQuadPointCount = pArray ? QuadPointCount(pArray) : 0;
238   if (nQuadPointCount == 0)
239     return ret;
240 
241   ret = RectFromQuadPointsArray(pArray, 0);
242   for (size_t i = 1; i < nQuadPointCount; ++i) {
243     CFX_FloatRect rect = RectFromQuadPointsArray(pArray, i);
244     ret.Union(rect);
245   }
246   return ret;
247 }
248 
249 // static
RectFromQuadPoints(const CPDF_Dictionary * pAnnotDict,size_t nIndex)250 CFX_FloatRect CPDF_Annot::RectFromQuadPoints(const CPDF_Dictionary* pAnnotDict,
251                                              size_t nIndex) {
252   const CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
253   size_t nQuadPointCount = pArray ? QuadPointCount(pArray) : 0;
254   if (nIndex >= nQuadPointCount)
255     return CFX_FloatRect();
256   return RectFromQuadPointsArray(pArray, nIndex);
257 }
258 
259 // static
StringToAnnotSubtype(const ByteString & sSubtype)260 CPDF_Annot::Subtype CPDF_Annot::StringToAnnotSubtype(
261     const ByteString& sSubtype) {
262   if (sSubtype == "Text")
263     return CPDF_Annot::Subtype::TEXT;
264   if (sSubtype == "Link")
265     return CPDF_Annot::Subtype::LINK;
266   if (sSubtype == "FreeText")
267     return CPDF_Annot::Subtype::FREETEXT;
268   if (sSubtype == "Line")
269     return CPDF_Annot::Subtype::LINE;
270   if (sSubtype == "Square")
271     return CPDF_Annot::Subtype::SQUARE;
272   if (sSubtype == "Circle")
273     return CPDF_Annot::Subtype::CIRCLE;
274   if (sSubtype == "Polygon")
275     return CPDF_Annot::Subtype::POLYGON;
276   if (sSubtype == "PolyLine")
277     return CPDF_Annot::Subtype::POLYLINE;
278   if (sSubtype == "Highlight")
279     return CPDF_Annot::Subtype::HIGHLIGHT;
280   if (sSubtype == "Underline")
281     return CPDF_Annot::Subtype::UNDERLINE;
282   if (sSubtype == "Squiggly")
283     return CPDF_Annot::Subtype::SQUIGGLY;
284   if (sSubtype == "StrikeOut")
285     return CPDF_Annot::Subtype::STRIKEOUT;
286   if (sSubtype == "Stamp")
287     return CPDF_Annot::Subtype::STAMP;
288   if (sSubtype == "Caret")
289     return CPDF_Annot::Subtype::CARET;
290   if (sSubtype == "Ink")
291     return CPDF_Annot::Subtype::INK;
292   if (sSubtype == "Popup")
293     return CPDF_Annot::Subtype::POPUP;
294   if (sSubtype == "FileAttachment")
295     return CPDF_Annot::Subtype::FILEATTACHMENT;
296   if (sSubtype == "Sound")
297     return CPDF_Annot::Subtype::SOUND;
298   if (sSubtype == "Movie")
299     return CPDF_Annot::Subtype::MOVIE;
300   if (sSubtype == "Widget")
301     return CPDF_Annot::Subtype::WIDGET;
302   if (sSubtype == "Screen")
303     return CPDF_Annot::Subtype::SCREEN;
304   if (sSubtype == "PrinterMark")
305     return CPDF_Annot::Subtype::PRINTERMARK;
306   if (sSubtype == "TrapNet")
307     return CPDF_Annot::Subtype::TRAPNET;
308   if (sSubtype == "Watermark")
309     return CPDF_Annot::Subtype::WATERMARK;
310   if (sSubtype == "3D")
311     return CPDF_Annot::Subtype::THREED;
312   if (sSubtype == "RichMedia")
313     return CPDF_Annot::Subtype::RICHMEDIA;
314   if (sSubtype == "XFAWidget")
315     return CPDF_Annot::Subtype::XFAWIDGET;
316   return CPDF_Annot::Subtype::UNKNOWN;
317 }
318 
319 // static
AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype)320 ByteString CPDF_Annot::AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype) {
321   if (nSubtype == CPDF_Annot::Subtype::TEXT)
322     return "Text";
323   if (nSubtype == CPDF_Annot::Subtype::LINK)
324     return "Link";
325   if (nSubtype == CPDF_Annot::Subtype::FREETEXT)
326     return "FreeText";
327   if (nSubtype == CPDF_Annot::Subtype::LINE)
328     return "Line";
329   if (nSubtype == CPDF_Annot::Subtype::SQUARE)
330     return "Square";
331   if (nSubtype == CPDF_Annot::Subtype::CIRCLE)
332     return "Circle";
333   if (nSubtype == CPDF_Annot::Subtype::POLYGON)
334     return "Polygon";
335   if (nSubtype == CPDF_Annot::Subtype::POLYLINE)
336     return "PolyLine";
337   if (nSubtype == CPDF_Annot::Subtype::HIGHLIGHT)
338     return "Highlight";
339   if (nSubtype == CPDF_Annot::Subtype::UNDERLINE)
340     return "Underline";
341   if (nSubtype == CPDF_Annot::Subtype::SQUIGGLY)
342     return "Squiggly";
343   if (nSubtype == CPDF_Annot::Subtype::STRIKEOUT)
344     return "StrikeOut";
345   if (nSubtype == CPDF_Annot::Subtype::STAMP)
346     return "Stamp";
347   if (nSubtype == CPDF_Annot::Subtype::CARET)
348     return "Caret";
349   if (nSubtype == CPDF_Annot::Subtype::INK)
350     return "Ink";
351   if (nSubtype == CPDF_Annot::Subtype::POPUP)
352     return "Popup";
353   if (nSubtype == CPDF_Annot::Subtype::FILEATTACHMENT)
354     return "FileAttachment";
355   if (nSubtype == CPDF_Annot::Subtype::SOUND)
356     return "Sound";
357   if (nSubtype == CPDF_Annot::Subtype::MOVIE)
358     return "Movie";
359   if (nSubtype == CPDF_Annot::Subtype::WIDGET)
360     return "Widget";
361   if (nSubtype == CPDF_Annot::Subtype::SCREEN)
362     return "Screen";
363   if (nSubtype == CPDF_Annot::Subtype::PRINTERMARK)
364     return "PrinterMark";
365   if (nSubtype == CPDF_Annot::Subtype::TRAPNET)
366     return "TrapNet";
367   if (nSubtype == CPDF_Annot::Subtype::WATERMARK)
368     return "Watermark";
369   if (nSubtype == CPDF_Annot::Subtype::THREED)
370     return "3D";
371   if (nSubtype == CPDF_Annot::Subtype::RICHMEDIA)
372     return "RichMedia";
373   if (nSubtype == CPDF_Annot::Subtype::XFAWIDGET)
374     return "XFAWidget";
375   return ByteString();
376 }
377 
378 // static
QuadPointCount(const CPDF_Array * pArray)379 size_t CPDF_Annot::QuadPointCount(const CPDF_Array* pArray) {
380   return pArray->size() / 8;
381 }
382 
DrawAppearance(CPDF_Page * pPage,CFX_RenderDevice * pDevice,const CFX_Matrix & mtUser2Device,AppearanceMode mode,const CPDF_RenderOptions * pOptions)383 bool CPDF_Annot::DrawAppearance(CPDF_Page* pPage,
384                                 CFX_RenderDevice* pDevice,
385                                 const CFX_Matrix& mtUser2Device,
386                                 AppearanceMode mode,
387                                 const CPDF_RenderOptions* pOptions) {
388   if (!ShouldDrawAnnotation())
389     return false;
390 
391   // It might happen that by the time this annotation instance was created,
392   // it was flagged as "hidden" (e.g. /F 2), and hence CPVT_GenerateAP decided
393   // to not "generate" its AP.
394   // If for a reason the object is no longer hidden, but still does not have
395   // its "AP" generated, generate it now.
396   GenerateAPIfNeeded();
397 
398   CFX_Matrix matrix;
399   CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, &mtUser2Device, &matrix);
400   if (!pForm)
401     return false;
402 
403   CPDF_RenderContext context(
404       pPage->GetDocument(), pPage->m_pPageResources.Get(),
405       static_cast<CPDF_PageRenderCache*>(pPage->GetRenderCache()));
406   context.AppendLayer(pForm, &matrix);
407   context.Render(pDevice, pOptions, nullptr);
408   return true;
409 }
410 
DrawInContext(const CPDF_Page * pPage,CPDF_RenderContext * pContext,const CFX_Matrix * pUser2Device,AppearanceMode mode)411 bool CPDF_Annot::DrawInContext(const CPDF_Page* pPage,
412                                CPDF_RenderContext* pContext,
413                                const CFX_Matrix* pUser2Device,
414                                AppearanceMode mode) {
415   if (!ShouldDrawAnnotation())
416     return false;
417 
418   // It might happen that by the time this annotation instance was created,
419   // it was flagged as "hidden" (e.g. /F 2), and hence CPVT_GenerateAP decided
420   // to not "generate" its AP.
421   // If for a reason the object is no longer hidden, but still does not have
422   // its "AP" generated, generate it now.
423   GenerateAPIfNeeded();
424 
425   CFX_Matrix matrix;
426   CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, pUser2Device, &matrix);
427   if (!pForm)
428     return false;
429 
430   pContext->AppendLayer(pForm, &matrix);
431   return true;
432 }
433 
DrawBorder(CFX_RenderDevice * pDevice,const CFX_Matrix * pUser2Device,const CPDF_RenderOptions * pOptions)434 void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice,
435                             const CFX_Matrix* pUser2Device,
436                             const CPDF_RenderOptions* pOptions) {
437   if (GetSubtype() == CPDF_Annot::Subtype::POPUP)
438     return;
439 
440   uint32_t annot_flags = GetFlags();
441   if (annot_flags & pdfium::annotation_flags::kHidden)
442     return;
443 
444   bool bPrinting = pDevice->GetDeviceType() == DeviceType::kPrinter ||
445                    (pOptions && pOptions->GetOptions().bPrintPreview);
446   if (bPrinting && (annot_flags & pdfium::annotation_flags::kPrint) == 0) {
447     return;
448   }
449   if (!bPrinting && (annot_flags & pdfium::annotation_flags::kNoView)) {
450     return;
451   }
452   CPDF_Dictionary* pBS = m_pAnnotDict->GetDictFor("BS");
453   char style_char;
454   float width;
455   CPDF_Array* pDashArray = nullptr;
456   if (!pBS) {
457     CPDF_Array* pBorderArray =
458         m_pAnnotDict->GetArrayFor(pdfium::annotation::kBorder);
459     style_char = 'S';
460     if (pBorderArray) {
461       width = pBorderArray->GetNumberAt(2);
462       if (pBorderArray->size() == 4) {
463         pDashArray = pBorderArray->GetArrayAt(3);
464         if (!pDashArray) {
465           return;
466         }
467         size_t nLen = pDashArray->size();
468         size_t i = 0;
469         for (; i < nLen; ++i) {
470           CPDF_Object* pObj = pDashArray->GetDirectObjectAt(i);
471           if (pObj && pObj->GetInteger()) {
472             break;
473           }
474         }
475         if (i == nLen) {
476           return;
477         }
478         style_char = 'D';
479       }
480     } else {
481       width = 1;
482     }
483   } else {
484     ByteString style = pBS->GetStringFor("S");
485     pDashArray = pBS->GetArrayFor("D");
486     style_char = style[1];
487     width = pBS->GetNumberFor("W");
488   }
489   if (width <= 0) {
490     return;
491   }
492   CPDF_Array* pColor = m_pAnnotDict->GetArrayFor(pdfium::annotation::kC);
493   uint32_t argb = 0xff000000;
494   if (pColor) {
495     int R = (int32_t)(pColor->GetNumberAt(0) * 255);
496     int G = (int32_t)(pColor->GetNumberAt(1) * 255);
497     int B = (int32_t)(pColor->GetNumberAt(2) * 255);
498     argb = ArgbEncode(0xff, R, G, B);
499   }
500   CFX_GraphStateData graph_state;
501   graph_state.m_LineWidth = width;
502   if (style_char == 'D') {
503     if (pDashArray) {
504       graph_state.m_DashArray =
505           ReadArrayElementsToVector(pDashArray, pDashArray->size());
506       if (graph_state.m_DashArray.size() % 2)
507         graph_state.m_DashArray.push_back(graph_state.m_DashArray.back());
508     } else {
509       graph_state.m_DashArray = {3.0f, 3.0f};
510     }
511   }
512 
513   CFX_FloatRect rect = GetRect();
514   rect.Deflate(width / 2, width / 2);
515   CFX_PathData path;
516   path.AppendFloatRect(rect);
517 
518   int fill_type = 0;
519   if (pOptions && pOptions->GetOptions().bNoPathSmooth)
520     fill_type |= FXFILL_NOPATHSMOOTH;
521 
522   pDevice->DrawPath(&path, pUser2Device, &graph_state, argb, argb, fill_type);
523 }
524