Home | History | Annotate | Download | only in pdf
      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 
     15 static 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 
     39 static 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 
     49 static 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.
     61 static 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 
     85 sk_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 
    126 static 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 
    144 sk_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