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 "SkData.h" 9#include "SkPaint.h" 10#include "SkPDFCanon.h" 11#include "SkPDFFormXObject.h" 12#include "SkPDFGraphicState.h" 13#include "SkPDFUtils.h" 14 15static const char* as_pdf_blend_mode_name(SkBlendMode mode) { 16 // PDF32000.book section 11.3.5 "Blend Mode" 17 switch (mode) { 18 case SkBlendMode::kScreen: return "Screen"; 19 case SkBlendMode::kOverlay: return "Overlay"; 20 case SkBlendMode::kDarken: return "Darken"; 21 case SkBlendMode::kLighten: return "Lighten"; 22 case SkBlendMode::kColorDodge: return "ColorDodge"; 23 case SkBlendMode::kColorBurn: return "ColorBurn"; 24 case SkBlendMode::kHardLight: return "HardLight"; 25 case SkBlendMode::kSoftLight: return "SoftLight"; 26 case SkBlendMode::kDifference: return "Difference"; 27 case SkBlendMode::kExclusion: return "Exclusion"; 28 case SkBlendMode::kMultiply: return "Multiply"; 29 case SkBlendMode::kHue: return "Hue"; 30 case SkBlendMode::kSaturation: return "Saturation"; 31 case SkBlendMode::kColor: return "Color"; 32 case SkBlendMode::kLuminosity: return "Luminosity"; 33 // Other blendmodes are either unsupported or handled in 34 // SkPDFDevice::setUpContentEntry. 35 default: return "Normal"; 36 } 37} 38 39static int to_stroke_cap(uint8_t cap) { 40 // PDF32000.book section 8.4.3.3 "Line Cap Style" 41 switch ((SkPaint::Cap)cap) { 42 case SkPaint::kButt_Cap: return 0; 43 case SkPaint::kRound_Cap: return 1; 44 case SkPaint::kSquare_Cap: return 2; 45 default: SkASSERT(false); return 0; 46 } 47} 48 49static int to_stroke_join(uint8_t join) { 50 // PDF32000.book section 8.4.3.4 "Line Join Style" 51 switch ((SkPaint::Join)join) { 52 case SkPaint::kMiter_Join: return 0; 53 case SkPaint::kRound_Join: return 1; 54 case SkPaint::kBevel_Join: return 2; 55 default: SkASSERT(false); return 0; 56 } 57} 58 59// If a SkXfermode is unsupported in PDF, this function returns 60// SrcOver, otherwise, it returns that Xfermode as a Mode. 61static uint8_t pdf_blend_mode(SkBlendMode mode) { 62 switch (mode) { 63 case SkBlendMode::kSrcOver: 64 case SkBlendMode::kMultiply: 65 case SkBlendMode::kScreen: 66 case SkBlendMode::kOverlay: 67 case SkBlendMode::kDarken: 68 case SkBlendMode::kLighten: 69 case SkBlendMode::kColorDodge: 70 case SkBlendMode::kColorBurn: 71 case SkBlendMode::kHardLight: 72 case SkBlendMode::kSoftLight: 73 case SkBlendMode::kDifference: 74 case SkBlendMode::kExclusion: 75 case SkBlendMode::kHue: 76 case SkBlendMode::kSaturation: 77 case SkBlendMode::kColor: 78 case SkBlendMode::kLuminosity: 79 return SkToU8((unsigned)mode); 80 default: 81 return SkToU8((unsigned)SkBlendMode::kSrcOver); 82 } 83} 84 85sk_sp<SkPDFDict> SkPDFGraphicState::GetGraphicStateForPaint(SkPDFCanon* canon, 86 const SkPaint& p) { 87 SkASSERT(canon); 88 if (SkPaint::kFill_Style == p.getStyle()) { 89 SkPDFFillGraphicState fillKey = {p.getAlpha(), pdf_blend_mode(p.getBlendMode())}; 90 auto& fillMap = canon->fFillGSMap; 91 if (sk_sp<SkPDFDict>* statePtr = fillMap.find(fillKey)) { 92 return *statePtr; 93 } 94 auto state = sk_make_sp<SkPDFDict>(); 95 state->reserve(2); 96 state->insertScalar("ca", fillKey.fAlpha / 255.0f); 97 state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode)); 98 fillMap.set(fillKey, state); 99 return state; 100 } else { 101 SkPDFStrokeGraphicState strokeKey = { 102 p.getStrokeWidth(), p.getStrokeMiter(), 103 SkToU8(p.getStrokeCap()), SkToU8(p.getStrokeJoin()), 104 p.getAlpha(), pdf_blend_mode(p.getBlendMode())}; 105 auto& sMap = canon->fStrokeGSMap; 106 if (sk_sp<SkPDFDict>* statePtr = sMap.find(strokeKey)) { 107 return *statePtr; 108 } 109 auto state = sk_make_sp<SkPDFDict>(); 110 state->reserve(8); 111 state->insertScalar("CA", strokeKey.fAlpha / 255.0f); 112 state->insertScalar("ca", strokeKey.fAlpha / 255.0f); 113 state->insertInt("LC", to_stroke_cap(strokeKey.fStrokeCap)); 114 state->insertInt("LJ", to_stroke_join(strokeKey.fStrokeJoin)); 115 state->insertScalar("LW", strokeKey.fStrokeWidth); 116 state->insertScalar("ML", strokeKey.fStrokeMiter); 117 state->insertBool("SA", true); // SA = Auto stroke adjustment. 118 state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)strokeKey.fBlendMode)); 119 sMap.set(strokeKey, state); 120 return state; 121 } 122} 123 124//////////////////////////////////////////////////////////////////////////////// 125 126static sk_sp<SkPDFStream> make_invert_function() { 127 // Acrobat crashes if we use a type 0 function, kpdf crashes if we use 128 // a type 2 function, so we use a type 4 function. 129 auto domainAndRange = sk_make_sp<SkPDFArray>(); 130 domainAndRange->reserve(2); 131 domainAndRange->appendInt(0); 132 domainAndRange->appendInt(1); 133 134 static const char psInvert[] = "{1 exch sub}"; 135 // Do not copy the trailing '\0' into the SkData. 136 auto invertFunction = sk_make_sp<SkPDFStream>( 137 SkData::MakeWithoutCopy(psInvert, strlen(psInvert))); 138 invertFunction->dict()->insertInt("FunctionType", 4); 139 invertFunction->dict()->insertObject("Domain", domainAndRange); 140 invertFunction->dict()->insertObject("Range", std::move(domainAndRange)); 141 return invertFunction; 142} 143 144sk_sp<SkPDFDict> SkPDFGraphicState::GetSMaskGraphicState( 145 sk_sp<SkPDFObject> sMask, 146 bool invert, 147 SkPDFSMaskMode sMaskMode, 148 SkPDFCanon* canon) { 149 // The practical chances of using the same mask more than once are unlikely 150 // enough that it's not worth canonicalizing. 151 auto sMaskDict = sk_make_sp<SkPDFDict>("Mask"); 152 if (sMaskMode == kAlpha_SMaskMode) { 153 sMaskDict->insertName("S", "Alpha"); 154 } else if (sMaskMode == kLuminosity_SMaskMode) { 155 sMaskDict->insertName("S", "Luminosity"); 156 } 157 sMaskDict->insertObjRef("G", std::move(sMask)); 158 if (invert) { 159 // Instead of calling SkPDFGraphicState::MakeInvertFunction, 160 // let the canon deduplicate this object. 161 sk_sp<SkPDFStream>& invertFunction = canon->fInvertFunction; 162 if (!invertFunction) { 163 invertFunction = make_invert_function(); 164 } 165 sMaskDict->insertObjRef("TR", invertFunction); 166 } 167 auto result = sk_make_sp<SkPDFDict>("ExtGState"); 168 result->insertObject("SMask", std::move(sMaskDict)); 169 return result; 170} 171