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 "SkPDFGraphicState.h" 9 10 #include "SkData.h" 11 #include "SkPDFDocument.h" 12 #include "SkPDFDocumentPriv.h" 13 #include "SkPDFFormXObject.h" 14 #include "SkPDFUtils.h" 15 #include "SkPaint.h" 16 #include "SkTo.h" 17 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 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 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. 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 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 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 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