Coverage Report

Created: 2021-08-22 09:07

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