• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/pdf/SkPDFGraphicState.h"
9 
10 #include "include/core/SkData.h"
11 #include "include/core/SkPaint.h"
12 #include "include/docs/SkPDFDocument.h"
13 #include "include/private/SkTo.h"
14 #include "src/pdf/SkPDFDocumentPriv.h"
15 #include "src/pdf/SkPDFFormXObject.h"
16 #include "src/pdf/SkPDFUtils.h"
17 
as_pdf_blend_mode_name(SkBlendMode mode)18 static const char* as_pdf_blend_mode_name(SkBlendMode mode) {
19     const char* name = SkPDFUtils::BlendModeName(mode);
20     SkASSERT(name);
21     return name;
22 }
23 
to_stroke_cap(uint8_t cap)24 static int to_stroke_cap(uint8_t cap) {
25     // PDF32000.book section 8.4.3.3 "Line Cap Style"
26     switch ((SkPaint::Cap)cap) {
27         case SkPaint::kButt_Cap:   return 0;
28         case SkPaint::kRound_Cap:  return 1;
29         case SkPaint::kSquare_Cap: return 2;
30         default: SkASSERT(false);  return 0;
31     }
32 }
33 
to_stroke_join(uint8_t join)34 static int to_stroke_join(uint8_t join) {
35     // PDF32000.book section 8.4.3.4 "Line Join Style"
36     switch ((SkPaint::Join)join) {
37         case SkPaint::kMiter_Join: return 0;
38         case SkPaint::kRound_Join: return 1;
39         case SkPaint::kBevel_Join: return 2;
40         default: SkASSERT(false);  return 0;
41     }
42 }
43 
44 // If a SkXfermode is unsupported in PDF, this function returns
45 // SrcOver, otherwise, it returns that Xfermode as a Mode.
pdf_blend_mode(SkBlendMode mode)46 static uint8_t pdf_blend_mode(SkBlendMode mode) {
47     if (!SkPDFUtils::BlendModeName(mode)
48         || SkBlendMode::kXor  == mode
49         || SkBlendMode::kPlus == mode)
50     {
51         mode = SkBlendMode::kSrcOver;
52     }
53     return SkToU8((unsigned)mode);
54 }
55 
GetGraphicStateForPaint(SkPDFDocument * doc,const SkPaint & p)56 SkPDFIndirectReference SkPDFGraphicState::GetGraphicStateForPaint(SkPDFDocument* doc,
57                                                                   const SkPaint& p) {
58     SkASSERT(doc);
59     if (SkPaint::kFill_Style == p.getStyle()) {
60         SkPDFFillGraphicState fillKey = {p.getColor4f().fA, pdf_blend_mode(p.getBlendMode())};
61         auto& fillMap = doc->fFillGSMap;
62         if (SkPDFIndirectReference* statePtr = fillMap.find(fillKey)) {
63             return *statePtr;
64         }
65         SkPDFDict state;
66         state.reserve(2);
67         state.insertColorComponentF("ca", fillKey.fAlpha);
68         state.insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode));
69         SkPDFIndirectReference ref = doc->emit(state);
70         fillMap.set(fillKey, ref);
71         return ref;
72     } else {
73         SkPDFStrokeGraphicState strokeKey = {
74             p.getStrokeWidth(),
75             p.getStrokeMiter(),
76             p.getColor4f().fA,
77             SkToU8(p.getStrokeCap()),
78             SkToU8(p.getStrokeJoin()),
79             pdf_blend_mode(p.getBlendMode())
80         };
81         auto& sMap = doc->fStrokeGSMap;
82         if (SkPDFIndirectReference* statePtr = sMap.find(strokeKey)) {
83             return *statePtr;
84         }
85         SkPDFDict state;
86         state.reserve(8);
87         state.insertColorComponentF("CA", strokeKey.fAlpha);
88         state.insertColorComponentF("ca", strokeKey.fAlpha);
89         state.insertInt("LC", to_stroke_cap(strokeKey.fStrokeCap));
90         state.insertInt("LJ", to_stroke_join(strokeKey.fStrokeJoin));
91         state.insertScalar("LW", strokeKey.fStrokeWidth);
92         state.insertScalar("ML", strokeKey.fStrokeMiter);
93         state.insertBool("SA", true);  // SA = Auto stroke adjustment.
94         state.insertName("BM", as_pdf_blend_mode_name((SkBlendMode)strokeKey.fBlendMode));
95         SkPDFIndirectReference ref = doc->emit(state);
96         sMap.set(strokeKey, ref);
97         return ref;
98     }
99 }
100 
101 ////////////////////////////////////////////////////////////////////////////////
102 
make_invert_function(SkPDFDocument * doc)103 static SkPDFIndirectReference make_invert_function(SkPDFDocument* doc) {
104     // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
105     // a type 2 function, so we use a type 4 function.
106     static const char psInvert[] = "{1 exch sub}";
107     // Do not copy the trailing '\0' into the SkData.
108     auto invertFunction = SkData::MakeWithoutCopy(psInvert, strlen(psInvert));
109 
110     std::unique_ptr<SkPDFDict> dict = SkPDFMakeDict();
111     dict->insertInt("FunctionType", 4);
112     dict->insertObject("Domain", SkPDFMakeArray(0, 1));
113     dict->insertObject("Range", SkPDFMakeArray(0, 1));
114     return SkPDFStreamOut(std::move(dict), SkMemoryStream::Make(std::move(invertFunction)), doc);
115 }
116 
GetSMaskGraphicState(SkPDFIndirectReference sMask,bool invert,SkPDFSMaskMode sMaskMode,SkPDFDocument * doc)117 SkPDFIndirectReference SkPDFGraphicState::GetSMaskGraphicState(SkPDFIndirectReference sMask,
118                                                                bool invert,
119                                                                SkPDFSMaskMode sMaskMode,
120                                                                SkPDFDocument* doc) {
121     // The practical chances of using the same mask more than once are unlikely
122     // enough that it's not worth canonicalizing.
123     auto sMaskDict = SkPDFMakeDict("Mask");
124     if (sMaskMode == kAlpha_SMaskMode) {
125         sMaskDict->insertName("S", "Alpha");
126     } else if (sMaskMode == kLuminosity_SMaskMode) {
127         sMaskDict->insertName("S", "Luminosity");
128     }
129     sMaskDict->insertRef("G", sMask);
130     if (invert) {
131         // let the doc deduplicate this object.
132         if (doc->fInvertFunction == SkPDFIndirectReference()) {
133             doc->fInvertFunction = make_invert_function(doc);
134         }
135         sMaskDict->insertRef("TR", doc->fInvertFunction);
136     }
137     SkPDFDict result("ExtGState");
138     result.insertObject("SMask", std::move(sMaskDict));
139     return doc->emit(result);
140 }
141