SkPDF: refactor streams, experimental threading
All PDF Streams are immediatly serialized, and are never long-lived in memory. if EXPERIMENTAL fExecutor is set on the Document, streams are compressed in parallel. Results for PDFBigDocBench: without patch 1807885.01 μs with patch without executor 1802808.35 μs with patch with executor 246313.72 μs SkPDFStreamOut() function replaces SkPDFStream classes. Page resources are all serialized early. Several Document-level objects are serialzied early. SkUUID introduced as top-level object. Many {insert|append}ObjRef() converted to memory efficient {insert|append}Ref(). Bug: skia:8630 Change-Id: Ic336917d0c8b9ac1c2423b43bfe9b49a3533fbff Reviewed-on: https://skia-review.googlesource.com/c/176588 Auto-Submit: Hal Canary <halcanary@google.com> Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Hal Canary <halcanary@google.com>
This commit is contained in:
parent
7dfe6d9ea7
commit
9a3f554154
@ -10,6 +10,7 @@
|
||||
#include "Resources.h"
|
||||
#include "SkAutoPixmapStorage.h"
|
||||
#include "SkData.h"
|
||||
#include "SkExecutor.h"
|
||||
#include "SkFloatToDecimal.h"
|
||||
#include "SkGradientShader.h"
|
||||
#include "SkImage.h"
|
||||
@ -79,20 +80,6 @@ DEF_BENCH(return new PDFScalarBench("PDFScalar_random", next_any);)
|
||||
#include "SkPDFUtils.h"
|
||||
|
||||
namespace {
|
||||
static void test_pdf_object_serialization(const sk_sp<SkPDFObject> object) {
|
||||
// SkDebugWStream wStream;
|
||||
SkNullWStream wStream;
|
||||
SkPDFObjNumMap objNumMap;
|
||||
objNumMap.addObjectRecursively(object.get());
|
||||
for (size_t i = 0; i < objNumMap.objects().size(); ++i) {
|
||||
SkPDFObject* object = objNumMap.objects()[i].get();
|
||||
wStream.writeDecAsText(i + 1);
|
||||
wStream.writeText(" 0 obj\n");
|
||||
object->emitObject(&wStream);
|
||||
wStream.writeText("\nendobj\n");
|
||||
}
|
||||
}
|
||||
|
||||
class PDFImageBench : public Benchmark {
|
||||
public:
|
||||
PDFImageBench() {}
|
||||
@ -184,11 +171,11 @@ protected:
|
||||
SkASSERT(fAsset);
|
||||
if (!fAsset) { return; }
|
||||
while (loops-- > 0) {
|
||||
sk_sp<SkPDFObject> object =
|
||||
sk_make_sp<SkPDFSharedStream>(
|
||||
std::unique_ptr<SkStreamAsset>(fAsset->duplicate()));
|
||||
test_pdf_object_serialization(object);
|
||||
}
|
||||
SkNullWStream wStream;
|
||||
SkPDFDocument doc(&wStream, SkPDF::Metadata());
|
||||
doc.beginPage(256, 256);
|
||||
(void)SkPDFStreamOut(nullptr, fAsset->duplicate(), &doc, true);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -229,8 +216,9 @@ struct PDFShaderBench : public Benchmark {
|
||||
while (loops-- > 0) {
|
||||
SkNullWStream nullStream;
|
||||
SkPDFDocument doc(&nullStream, SkPDF::Metadata());
|
||||
sk_sp<SkPDFObject> shader = SkPDFMakeShader(&doc, fShader.get(), SkMatrix::I(),
|
||||
{0, 0, 400, 400}, SK_ColorBLACK);
|
||||
doc.beginPage(256, 256);
|
||||
(void) SkPDFMakeShader(&doc, fShader.get(), SkMatrix::I(),
|
||||
{0, 0, 400, 400}, SK_ColorBLACK);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -262,5 +250,125 @@ DEF_BENCH(return new PDFColorComponentBench;)
|
||||
DEF_BENCH(return new PDFShaderBench;)
|
||||
DEF_BENCH(return new WritePDFTextBenchmark;)
|
||||
|
||||
#if 0
|
||||
#include "SkExecutor.h"
|
||||
namespace {
|
||||
void big_pdf_test(const SkBitmap& background) {
|
||||
static const char* kText[] = {
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do",
|
||||
"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad",
|
||||
"minim veniam, quis nostrud exercitation ullamco laboris nisi ut",
|
||||
"aliquip ex ea commodo consequat. Duis aute irure dolor in",
|
||||
"reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla",
|
||||
"pariatur. Excepteur sint occaecat cupidatat non proident, sunt in",
|
||||
"culpa qui officia deserunt mollit anim id est laborum.",
|
||||
"",
|
||||
"Sed ut perspiciatis, unde omnis iste natus error sit voluptatem",
|
||||
"accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae",
|
||||
"ab illo inventore veritatis et quasi architecto beatae vitae dicta",
|
||||
"sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit,",
|
||||
"aspernatur aut odit aut fugit, sed quia consequuntur magni dolores",
|
||||
"eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,",
|
||||
"qui dolorem ipsum, quia dolor sit amet consectetur adipiscing velit,",
|
||||
"sed quia non numquam do eius modi tempora incididunt, ut labore et",
|
||||
"dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam,",
|
||||
"quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi",
|
||||
"ut aliquid ex ea commodi consequatur? Quis autem vel eum iure",
|
||||
"reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae",
|
||||
"consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla",
|
||||
"pariatur?",
|
||||
"",
|
||||
"At vero eos et accusamus et iusto odio dignissimos ducimus, qui",
|
||||
"blanditiis praesentium voluptatum deleniti atque corrupti, quos",
|
||||
"dolores et quas molestias excepturi sint, obcaecati cupiditate non",
|
||||
"provident, similique sunt in culpa, qui officia deserunt mollitia",
|
||||
"animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis",
|
||||
"est et expedita distinctio. Nam libero tempore, cum soluta nobis est",
|
||||
"eligendi optio, cumque nihil impedit, quo minus id, quod maxime",
|
||||
"placeat, facere possimus, omnis voluptas assumenda est, omnis dolor",
|
||||
"repellendus. Temporibus autem quibusdam et aut officiis debitis aut",
|
||||
"rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint",
|
||||
"et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente",
|
||||
"delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut",
|
||||
"perferendis doloribus asperiores repellat",
|
||||
"",
|
||||
"Sed ut perspiciatis, unde omnis iste natus error sit voluptatem",
|
||||
"accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae",
|
||||
"ab illo inventore veritatis et quasi architecto beatae vitae dicta",
|
||||
"sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit,",
|
||||
"aspernatur aut odit aut fugit, sed quia consequuntur magni dolores",
|
||||
"eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,",
|
||||
"qui dolorem ipsum, quia dolor sit amet consectetur adipiscing velit,",
|
||||
"sed quia non numquam do eius modi tempora incididunt, ut labore et",
|
||||
"dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam,",
|
||||
"quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi",
|
||||
"ut aliquid ex ea commodi consequatur? Quis autem vel eum iure",
|
||||
"reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae",
|
||||
"consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla",
|
||||
"pariatur?",
|
||||
"",
|
||||
};
|
||||
//SkFILEWStream wStream("/tmp/big_pdf.pdf");
|
||||
SkNullWStream wStream;
|
||||
SkPDF::Metadata metadata;
|
||||
//std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool();
|
||||
//metadata.fExecutor = executor.get();
|
||||
sk_sp<SkDocument> doc = SkPDF::MakeDocument(&wStream, metadata);
|
||||
|
||||
SkCanvas* canvas = nullptr;
|
||||
float x = 36;
|
||||
float y = 36;
|
||||
constexpr size_t kLineCount = SK_ARRAY_COUNT(kText);
|
||||
constexpr int kLoopCount = 200;
|
||||
SkPaint paint;
|
||||
paint.setTextEncoding(kUTF8_SkTextEncoding);
|
||||
for (int loop = 0; loop < kLoopCount; ++loop) {
|
||||
for (size_t line = 0; line < kLineCount; ++line) {
|
||||
y += paint.getFontSpacing();
|
||||
if (!canvas || y > 792 - 36) {
|
||||
y = 36 + paint.getFontSpacing();
|
||||
canvas = doc->beginPage(612, 792);
|
||||
background.notifyPixelsChanged();
|
||||
canvas->drawBitmap(background, 0, 0);
|
||||
}
|
||||
canvas->drawText(kText[line], strlen(kText[line]), x, y, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SkBitmap make_background() {
|
||||
SkBitmap background;
|
||||
SkBitmap bitmap;
|
||||
bitmap.allocN32Pixels(32, 32);
|
||||
bitmap.eraseColor(SK_ColorWHITE);
|
||||
SkCanvas tmp(bitmap);
|
||||
SkPaint gray;
|
||||
gray.setColor(SkColorSetARGB(0xFF, 0xEE, 0xEE, 0xEE));
|
||||
tmp.drawRect({0,0,16,16}, gray);
|
||||
tmp.drawRect({16,16,32,32}, gray);
|
||||
SkPaint shader;
|
||||
shader.setShader(
|
||||
SkShader::MakeBitmapShader(
|
||||
bitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
|
||||
background.allocN32Pixels(612, 792);
|
||||
SkCanvas tmp2(background);
|
||||
tmp2.drawPaint(shader);
|
||||
return background;
|
||||
}
|
||||
|
||||
struct PDFBigDocBench : public Benchmark {
|
||||
SkBitmap fBackground;
|
||||
void onDelayedSetup() override { fBackground = make_background(); }
|
||||
const char* onGetName() override { return "PDFBigDocBench"; }
|
||||
bool isSuitableFor(Backend backend) override {
|
||||
return backend == kNonRendering_Backend;
|
||||
}
|
||||
void onDraw(int loops, SkCanvas*) override {
|
||||
while (loops-- > 0) { big_pdf_test(fBackground); }
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
DEF_BENCH(return new PDFBigDocBench;)
|
||||
#endif
|
||||
|
||||
#endif // SK_SUPPORT_PDF
|
||||
|
@ -1638,6 +1638,10 @@ Error PDFSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const
|
||||
metadata.fCreator = "Skia/DM";
|
||||
metadata.fRasterDPI = fRasterDpi;
|
||||
metadata.fPDFA = fPDFA;
|
||||
#if 0
|
||||
std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool();
|
||||
metadata.fExecutor = executor.get();
|
||||
#endif
|
||||
sk_sp<SkDocument> doc = SkPDF::MakeDocument(dst, metadata);
|
||||
if (!doc) {
|
||||
return "SkPDF::MakeDocument() returned nullptr";
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "SkString.h"
|
||||
#include "SkTime.h"
|
||||
|
||||
class SkExecutor;
|
||||
|
||||
namespace SkPDF {
|
||||
|
||||
/** Table 333 in PDF 32000-1:2008
|
||||
@ -141,6 +143,15 @@ struct Metadata {
|
||||
* should retain ownership.
|
||||
*/
|
||||
const StructureElementNode* fStructureElementTreeRoot = nullptr;
|
||||
|
||||
/** Executor to handle threaded work within PDF Backend. If this is nullptr,
|
||||
then all work will be done serially on the main thread. To have worker
|
||||
threads assist with various tasks, set this to a valid SkExecutor
|
||||
instance. Currently used for executing Deflate algorithm in parallel.
|
||||
|
||||
Experimental.
|
||||
*/
|
||||
SkExecutor* fExecutor = nullptr;
|
||||
};
|
||||
|
||||
/** Associate a node ID with subsequent drawing commands in an
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "SkColorData.h"
|
||||
#include "SkData.h"
|
||||
#include "SkDeflate.h"
|
||||
#include "SkExecutor.h"
|
||||
#include "SkImage.h"
|
||||
#include "SkImageInfoPriv.h"
|
||||
#include "SkJpegInfo.h"
|
||||
@ -120,12 +121,13 @@ static SkPDFIndirectReference do_deflated_alpha(const SkPixmap& pm, SkPDFDocumen
|
||||
return ref;
|
||||
}
|
||||
|
||||
static SkPDFIndirectReference do_deflated_image(const SkPixmap& pm,
|
||||
SkPDFDocument* doc,
|
||||
bool isOpaque) {
|
||||
static void do_deflated_image(const SkPixmap& pm,
|
||||
SkPDFDocument* doc,
|
||||
bool isOpaque,
|
||||
SkPDFIndirectReference ref) {
|
||||
SkPDFIndirectReference sMask;
|
||||
if (!isOpaque) {
|
||||
sMask = doc->reserve();
|
||||
sMask = doc->reserveRef();
|
||||
}
|
||||
SkDynamicMemoryWStream buffer;
|
||||
SkDeflateWStream deflateWStream(&buffer);
|
||||
@ -167,7 +169,6 @@ static SkPDFIndirectReference do_deflated_image(const SkPixmap& pm,
|
||||
deflateWStream.write(byteBuffer, dst - byteBuffer);
|
||||
}
|
||||
deflateWStream.finalize();
|
||||
SkPDFIndirectReference ref = doc->reserve();
|
||||
SkWStream* stream = doc->beginObject(ref);
|
||||
emit_dict(stream, pm.info().dimensions(), colorSpace,
|
||||
sMask.fValue != -1 ? &sMask : nullptr,
|
||||
@ -177,11 +178,10 @@ static SkPDFIndirectReference do_deflated_image(const SkPixmap& pm,
|
||||
if (!isOpaque) {
|
||||
do_deflated_alpha(pm, doc, sMask);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
static bool do_jpeg(const SkData& data, SkPDFDocument* doc, SkISize size,
|
||||
SkPDFIndirectReference* result) {
|
||||
SkPDFIndirectReference ref) {
|
||||
SkISize jpegSize;
|
||||
SkEncodedInfo::Color jpegColorType;
|
||||
SkEncodedOrigin exifOrientation;
|
||||
@ -199,10 +199,8 @@ static bool do_jpeg(const SkData& data, SkPDFDocument* doc, SkISize size,
|
||||
#ifdef SK_PDF_IMAGE_STATS
|
||||
gJpegImageObjects.fetch_add(1);
|
||||
#endif
|
||||
SkPDFIndirectReference ref = doc->reserve();
|
||||
*result = ref;
|
||||
SkWStream* stream = doc->beginObject(ref);
|
||||
|
||||
SkWStream* stream = doc->beginObject(ref);
|
||||
SkPDFDict pdfDict("XObject");
|
||||
pdfDict.insertName("Subtype", "Image");
|
||||
pdfDict.insertInt("Width", jpegSize.width());
|
||||
@ -247,26 +245,44 @@ static SkBitmap to_pixels(const SkImage* image) {
|
||||
return bm;
|
||||
}
|
||||
|
||||
SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
|
||||
SkPDFDocument* doc,
|
||||
int encodingQuality) {
|
||||
SkPDFIndirectReference result;
|
||||
void serialize_image(const SkImage* img,
|
||||
int encodingQuality,
|
||||
SkPDFDocument* doc,
|
||||
SkPDFIndirectReference ref) {
|
||||
SkASSERT(img);
|
||||
SkASSERT(doc);
|
||||
SkASSERT(encodingQuality >= 0);
|
||||
SkISize dimensions = img->dimensions();
|
||||
sk_sp<SkData> data = img->refEncodedData();
|
||||
if (data && do_jpeg(*data, doc, dimensions, &result)) {
|
||||
return result;
|
||||
if (data && do_jpeg(*data, doc, dimensions, ref)) {
|
||||
return;
|
||||
}
|
||||
SkBitmap bm = to_pixels(img);
|
||||
SkPixmap pm = bm.pixmap();
|
||||
bool isOpaque = pm.isOpaque() || pm.computeIsOpaque();
|
||||
if (encodingQuality <= 100 && isOpaque) {
|
||||
sk_sp<SkData> data = img->encodeToData(SkEncodedImageFormat::kJPEG, encodingQuality);
|
||||
if (data && do_jpeg(*data, doc, dimensions, &result)) {
|
||||
return result;
|
||||
if (data && do_jpeg(*data, doc, dimensions, ref)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return do_deflated_image(pm, doc, isOpaque);
|
||||
do_deflated_image(pm, doc, isOpaque, ref);
|
||||
}
|
||||
|
||||
SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
|
||||
SkPDFDocument* doc,
|
||||
int encodingQuality) {
|
||||
SkASSERT(img);
|
||||
SkASSERT(doc);
|
||||
SkPDFIndirectReference ref = doc->reserveRef();
|
||||
if (SkExecutor* executor = doc->executor()) {
|
||||
SkRef(img);
|
||||
executor->add([img, encodingQuality, doc, ref]() {
|
||||
serialize_image(img, encodingQuality, doc, ref);
|
||||
SkSafeUnref(img);
|
||||
});
|
||||
return ref;
|
||||
}
|
||||
serialize_image(img, encodingQuality, doc, ref);
|
||||
return ref;
|
||||
}
|
||||
|
@ -34,11 +34,11 @@ public:
|
||||
SkPDFCanon& operator=(SkPDFCanon&&);
|
||||
SkPDFCanon& operator=(const SkPDFCanon&) = delete;
|
||||
|
||||
SkTHashMap<SkPDFImageShaderKey, sk_sp<SkPDFObject>> fImageShaderMap;
|
||||
SkTHashMap<SkPDFImageShaderKey, SkPDFIndirectReference> fImageShaderMap;
|
||||
|
||||
SkPDFGradientShader::HashMap fGradientPatternMap;
|
||||
|
||||
SkTHashMap<SkBitmapKey, sk_sp<SkPDFObject>> fPDFBitmapMap;
|
||||
SkTHashMap<SkBitmapKey, SkPDFIndirectReference> fPDFBitmapMap;
|
||||
|
||||
SkTHashMap<uint32_t, std::unique_ptr<SkAdvancedTypefaceMetrics>> fTypefaceMetrics;
|
||||
SkTHashMap<uint32_t, std::vector<SkString>> fType1GlyphNames;
|
||||
@ -47,12 +47,11 @@ public:
|
||||
SkTHashMap<uint32_t, SkPDFIndirectReference> fType3FontDescriptors;
|
||||
SkTHashMap<uint64_t, SkPDFFont> fFontMap;
|
||||
|
||||
SkTHashMap<SkPDFStrokeGraphicState, sk_sp<SkPDFDict>> fStrokeGSMap;
|
||||
SkTHashMap<SkPDFFillGraphicState, sk_sp<SkPDFDict>> fFillGSMap;
|
||||
SkTHashMap<SkPDFStrokeGraphicState, SkPDFIndirectReference> fStrokeGSMap;
|
||||
SkTHashMap<SkPDFFillGraphicState, SkPDFIndirectReference> fFillGSMap;
|
||||
|
||||
sk_sp<SkPDFStream> fInvertFunction;
|
||||
sk_sp<SkPDFDict> fNoSmaskGraphicState;
|
||||
sk_sp<SkPDFArray> fRangeObject;
|
||||
SkPDFIndirectReference fInvertFunction;
|
||||
SkPDFIndirectReference fNoSmaskGraphicState;
|
||||
};
|
||||
|
||||
#endif // SkPDFCanon_DEFINED
|
||||
|
@ -107,6 +107,11 @@ sk_sp<SkImage> alpha_image_to_greyscale_image(const SkImage* mask) {
|
||||
return SkImage::MakeFromBitmap(greyBitmap);
|
||||
}
|
||||
|
||||
static int add_resource(SkTHashSet<SkPDFIndirectReference>& resources, SkPDFIndirectReference ref) {
|
||||
resources.add(ref);
|
||||
return ref.fValue;
|
||||
}
|
||||
|
||||
static void draw_points(SkCanvas::PointMode mode,
|
||||
size_t count,
|
||||
const SkPoint* points,
|
||||
@ -429,7 +434,7 @@ public:
|
||||
if (shape->isEmpty()) {
|
||||
shape = nullptr;
|
||||
}
|
||||
fDevice->finishContentEntry(fClipStack, fBlendMode, std::move(fDstFormXObject), shape);
|
||||
fDevice->finishContentEntry(fClipStack, fBlendMode, fDstFormXObject, shape);
|
||||
}
|
||||
}
|
||||
|
||||
@ -474,7 +479,7 @@ private:
|
||||
SkPDFDevice* fDevice = nullptr;
|
||||
SkDynamicMemoryWStream* fContentStream = nullptr;
|
||||
SkBlendMode fBlendMode;
|
||||
sk_sp<SkPDFObject> fDstFormXObject;
|
||||
SkPDFIndirectReference fDstFormXObject;
|
||||
SkPath fShape;
|
||||
const SkClipStack* fClipStack;
|
||||
};
|
||||
@ -497,9 +502,9 @@ void SkPDFDevice::reset() {
|
||||
fLinkToURLs = std::vector<RectWithData>();
|
||||
fLinkToDestinations = std::vector<RectWithData>();
|
||||
fNamedDestinations = std::vector<NamedDestination>();
|
||||
fGraphicStateResources = std::vector<sk_sp<SkPDFObject>>();
|
||||
fXObjectResources = std::vector<sk_sp<SkPDFObject>>();
|
||||
fShaderResources = std::vector<sk_sp<SkPDFObject>>();
|
||||
fGraphicStateResources.reset();
|
||||
fXObjectResources.reset();
|
||||
fShaderResources.reset();
|
||||
fFontResources.reset();
|
||||
fContent.reset();
|
||||
fActiveStackState = GraphicStackState();
|
||||
@ -797,23 +802,24 @@ static int find_or_add(std::vector<sk_sp<T>>* vec, sk_sp<U> object) {
|
||||
return index;
|
||||
}
|
||||
|
||||
void SkPDFDevice::setGraphicState(sk_sp<SkPDFObject> gs, SkDynamicMemoryWStream* content) {
|
||||
SkPDFUtils::ApplyGraphicState(find_or_add(&fGraphicStateResources, std::move(gs)), content);
|
||||
void SkPDFDevice::setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream* content) {
|
||||
SkPDFUtils::ApplyGraphicState(add_resource(fGraphicStateResources, gs), content);
|
||||
}
|
||||
|
||||
void SkPDFDevice::addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice,
|
||||
SkDynamicMemoryWStream* contentStream) {
|
||||
this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState(
|
||||
maskDevice->makeFormXObjectFromDevice(true), false,
|
||||
SkPDFGraphicState::kLuminosity_SMaskMode, this->getCanon()), contentStream);
|
||||
SkPDFGraphicState::kLuminosity_SMaskMode, fDocument), contentStream);
|
||||
}
|
||||
|
||||
void SkPDFDevice::clearMaskOnGraphicState(SkDynamicMemoryWStream* contentStream) {
|
||||
// The no-softmask graphic state is used to "turn off" the mask for later draw calls.
|
||||
sk_sp<SkPDFDict>& noSMaskGS = this->getCanon()->fNoSmaskGraphicState;
|
||||
SkPDFIndirectReference& noSMaskGS = this->getCanon()->fNoSmaskGraphicState;
|
||||
if (!noSMaskGS) {
|
||||
noSMaskGS = sk_make_sp<SkPDFDict>("ExtGState");
|
||||
noSMaskGS->insertName("SMask", "None");
|
||||
SkPDFDict tmp("ExtGState");
|
||||
tmp.insertName("SMask", "None");
|
||||
noSMaskGS = fDocument->emit(tmp);
|
||||
}
|
||||
this->setGraphicState(noSMaskGS, contentStream);
|
||||
}
|
||||
@ -1240,12 +1246,10 @@ void SkPDFDevice::internalDrawGlyphRun(
|
||||
// Not yet specified font or need to switch font.
|
||||
font = SkPDFFont::GetFontResource(fDocument, glyphCache.get(), typeface, gid);
|
||||
SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource are met.
|
||||
SkPDFIndirectReference ref = font->indirectReference();
|
||||
fFontResources.add(ref);
|
||||
|
||||
glyphPositioner.flush();
|
||||
glyphPositioner.setWideChars(font->multiByteGlyphs());
|
||||
SkPDFWriteResourceName(out, SkPDFResourceType::kFont, ref.fValue);
|
||||
SkPDFWriteResourceName(out, SkPDFResourceType::kFont,
|
||||
add_resource(fFontResources, font->indirectReference()));
|
||||
out->writeText(" ");
|
||||
SkPDFUtils::AppendScalar(textSize, out);
|
||||
out->writeText(" Tf\n");
|
||||
@ -1275,9 +1279,10 @@ void SkPDFDevice::drawVertices(const SkVertices*, const SkVertices::Bone[], int,
|
||||
// TODO: implement drawVertices
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawFormXObject(sk_sp<SkPDFObject> xObject, SkDynamicMemoryWStream* content) {
|
||||
void SkPDFDevice::drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream* content) {
|
||||
SkASSERT(xObject);
|
||||
SkPDFWriteResourceName(content, SkPDFResourceType::kXObject,
|
||||
find_or_add(&fXObjectResources, std::move(xObject)));
|
||||
add_resource(fXObjectResources, xObject));
|
||||
content->writeText(" Do\n");
|
||||
}
|
||||
|
||||
@ -1335,18 +1340,20 @@ sk_sp<SkSurface> SkPDFDevice::makeSurface(const SkImageInfo& info, const SkSurfa
|
||||
return SkSurface::MakeRaster(info, &props);
|
||||
}
|
||||
|
||||
static std::vector<SkPDFIndirectReference> sort(const SkTHashSet<SkPDFIndirectReference>& src) {
|
||||
std::vector<SkPDFIndirectReference> dst;
|
||||
dst.reserve(src.count());
|
||||
src.foreach([&dst](SkPDFIndirectReference ref) { dst.push_back(ref); } );
|
||||
std::sort(dst.begin(), dst.end(),
|
||||
[](SkPDFIndirectReference a, SkPDFIndirectReference b) { return a.fValue < b.fValue; });
|
||||
return dst;
|
||||
}
|
||||
|
||||
sk_sp<SkPDFDict> SkPDFDevice::makeResourceDict() {
|
||||
std::vector<SkPDFIndirectReference> fonts;
|
||||
fonts.reserve(fFontResources.count());
|
||||
fFontResources.foreach([&fonts](SkPDFIndirectReference ref) { fonts.push_back(ref); } );
|
||||
fFontResources.reset();
|
||||
std::sort(fonts.begin(), fonts.end(),
|
||||
[](SkPDFIndirectReference a, SkPDFIndirectReference b) { return a.fValue < b.fValue; });
|
||||
return SkPDFMakeResourceDict(std::move(fGraphicStateResources),
|
||||
std::move(fShaderResources),
|
||||
std::move(fXObjectResources),
|
||||
std::move(fonts));
|
||||
return SkPDFMakeResourceDict(sort(fGraphicStateResources),
|
||||
sort(fShaderResources),
|
||||
sort(fXObjectResources),
|
||||
sort(fFontResources));
|
||||
}
|
||||
|
||||
std::unique_ptr<SkStreamAsset> SkPDFDevice::content() {
|
||||
@ -1446,13 +1453,13 @@ sk_sp<SkPDFArray> SkPDFDevice::getAnnotations() {
|
||||
for (const RectWithData& rectWithURL : fLinkToURLs) {
|
||||
SkRect r;
|
||||
fInitialTransform.mapRect(&r, rectWithURL.rect);
|
||||
array->appendObjRef(create_link_to_url(rectWithURL.data.get(), r));
|
||||
array->appendRef(fDocument->emit(*create_link_to_url(rectWithURL.data.get(), r)));
|
||||
}
|
||||
for (const RectWithData& linkToDestination : fLinkToDestinations) {
|
||||
SkRect r;
|
||||
fInitialTransform.mapRect(&r, linkToDestination.rect);
|
||||
array->appendObjRef(
|
||||
create_link_named_dest(linkToDestination.data.get(), r));
|
||||
array->appendRef(
|
||||
fDocument->emit(*create_link_named_dest(linkToDestination.data.get(), r)));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
@ -1461,6 +1468,7 @@ void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) const {
|
||||
for (const NamedDestination& dest : fNamedDestinations) {
|
||||
auto pdfDest = sk_make_sp<SkPDFArray>();
|
||||
pdfDest->reserve(5);
|
||||
// TODO(halcanry) reserve an IndirectReference for each page with beginPage()
|
||||
pdfDest->appendObjRef(sk_ref_sp(page));
|
||||
pdfDest->appendName("XYZ");
|
||||
SkPoint p = fInitialTransform.mapXY(dest.point.x(), dest.point.y());
|
||||
@ -1472,7 +1480,7 @@ void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) const {
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<SkPDFObject> SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
|
||||
SkPDFIndirectReference SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
|
||||
SkMatrix inverseTransform = SkMatrix::I();
|
||||
if (!fInitialTransform.isIdentity()) {
|
||||
if (!fInitialTransform.invert(&inverseTransform)) {
|
||||
@ -1482,8 +1490,8 @@ sk_sp<SkPDFObject> SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
|
||||
}
|
||||
const char* colorSpace = alpha ? "DeviceGray" : nullptr;
|
||||
|
||||
sk_sp<SkPDFObject> xobject =
|
||||
SkPDFMakeFormXObject(this->content(),
|
||||
SkPDFIndirectReference xobject =
|
||||
SkPDFMakeFormXObject(fDocument, this->content(),
|
||||
SkPDFMakeArray(0, 0, this->width(), this->height()),
|
||||
this->makeResourceDict(), inverseTransform, colorSpace);
|
||||
// We always draw the form xobjects that we create back into the device, so
|
||||
@ -1493,10 +1501,11 @@ sk_sp<SkPDFObject> SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
|
||||
return xobject;
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawFormXObjectWithMask(sk_sp<SkPDFObject> xObject,
|
||||
sk_sp<SkPDFObject> mask,
|
||||
void SkPDFDevice::drawFormXObjectWithMask(SkPDFIndirectReference xObject,
|
||||
SkPDFIndirectReference sMask,
|
||||
SkBlendMode mode,
|
||||
bool invertClip) {
|
||||
SkASSERT(sMask);
|
||||
SkPaint paint;
|
||||
paint.setBlendMode(mode);
|
||||
ScopedContentEntry content(this, nullptr, SkMatrix::I(), paint);
|
||||
@ -1504,9 +1513,9 @@ void SkPDFDevice::drawFormXObjectWithMask(sk_sp<SkPDFObject> xObject,
|
||||
return;
|
||||
}
|
||||
this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState(
|
||||
std::move(mask), invertClip, SkPDFGraphicState::kAlpha_SMaskMode,
|
||||
fDocument->canon()), content.stream());
|
||||
this->drawFormXObject(std::move(xObject), content.stream());
|
||||
sMask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode,
|
||||
fDocument), content.stream());
|
||||
this->drawFormXObject(xObject, content.stream());
|
||||
this->clearMaskOnGraphicState(content.stream());
|
||||
}
|
||||
|
||||
@ -1519,8 +1528,8 @@ SkDynamicMemoryWStream* SkPDFDevice::setUpContentEntry(const SkClipStack* clipSt
|
||||
const SkMatrix& matrix,
|
||||
const SkPaint& paint,
|
||||
bool hasText,
|
||||
sk_sp<SkPDFObject>* dst) {
|
||||
*dst = nullptr;
|
||||
SkPDFIndirectReference* dst) {
|
||||
SkASSERT(!*dst);
|
||||
SkBlendMode blendMode = paint.getBlendMode();
|
||||
|
||||
// Dst xfer mode doesn't draw source at all.
|
||||
@ -1570,7 +1579,7 @@ SkDynamicMemoryWStream* SkPDFDevice::setUpContentEntry(const SkClipStack* clipSt
|
||||
|
||||
void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
|
||||
SkBlendMode blendMode,
|
||||
sk_sp<SkPDFObject> dst,
|
||||
SkPDFIndirectReference dst,
|
||||
SkPath* shape) {
|
||||
SkASSERT(blendMode != SkBlendMode::kDst);
|
||||
if (treat_as_regular_pdf_blend_mode(blendMode)) {
|
||||
@ -1618,7 +1627,7 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
|
||||
|
||||
SkPaint stockPaint;
|
||||
|
||||
sk_sp<SkPDFObject> srcFormXObject;
|
||||
SkPDFIndirectReference srcFormXObject;
|
||||
if (this->isContentEmpty()) {
|
||||
// If nothing was drawn and there's no shape, then the draw was a
|
||||
// no-op, but dst needs to be restored for that to be true.
|
||||
@ -1628,7 +1637,7 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
|
||||
if (shape == nullptr || blendMode == SkBlendMode::kDstOut ||
|
||||
blendMode == SkBlendMode::kSrcATop) {
|
||||
ScopedContentEntry content(this, nullptr, SkMatrix::I(), stockPaint);
|
||||
this->drawFormXObject(std::move(dst), content.stream());
|
||||
this->drawFormXObject(dst, content.stream());
|
||||
return;
|
||||
} else {
|
||||
blendMode = SkBlendMode::kClear;
|
||||
@ -1691,8 +1700,8 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
|
||||
if (blendMode == SkBlendMode::kSrcIn ||
|
||||
blendMode == SkBlendMode::kSrcOut ||
|
||||
blendMode == SkBlendMode::kSrcATop) {
|
||||
this->drawFormXObjectWithMask(std::move(srcFormXObject), std::move(dst),
|
||||
SkBlendMode::kSrcOver, blendMode == SkBlendMode::kSrcOut);
|
||||
this->drawFormXObjectWithMask(srcFormXObject, dst, SkBlendMode::kSrcOver,
|
||||
blendMode == SkBlendMode::kSrcOut);
|
||||
return;
|
||||
} else {
|
||||
SkBlendMode mode = SkBlendMode::kSrcOver;
|
||||
@ -1700,8 +1709,7 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
|
||||
this->drawFormXObjectWithMask(srcFormXObject, dst, SkBlendMode::kSrcOver, false);
|
||||
mode = SkBlendMode::kMultiply;
|
||||
}
|
||||
this->drawFormXObjectWithMask(std::move(dst), std::move(srcFormXObject), mode,
|
||||
blendMode == SkBlendMode::kDstOut);
|
||||
this->drawFormXObjectWithMask(dst, srcFormXObject, mode, blendMode == SkBlendMode::kDstOut);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1728,7 +1736,6 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
|
||||
entry->fShaderIndex = -1;
|
||||
|
||||
// PDF treats a shader as a color, so we only set one or the other.
|
||||
sk_sp<SkPDFObject> pdfShader;
|
||||
SkShader* shader = paint.getShader();
|
||||
if (shader) {
|
||||
if (SkShader::kColor_GradientType == shader->asAGradient(nullptr)) {
|
||||
@ -1759,24 +1766,25 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
|
||||
SkIRect bounds;
|
||||
clipStackBounds.roundOut(&bounds);
|
||||
|
||||
pdfShader = SkPDFMakeShader(fDocument, shader, transform, bounds, paint.getColor());
|
||||
SkPDFIndirectReference pdfShader
|
||||
= SkPDFMakeShader(fDocument, shader, transform, bounds, paint.getColor());
|
||||
|
||||
if (pdfShader) {
|
||||
// pdfShader has been canonicalized so we can directly compare pointers.
|
||||
entry->fShaderIndex = find_or_add(&fShaderResources, std::move(pdfShader));
|
||||
entry->fShaderIndex = add_resource(fShaderResources, pdfShader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<SkPDFDict> newGraphicState;
|
||||
SkPDFIndirectReference newGraphicState;
|
||||
if (color == paint.getColor4f()) {
|
||||
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), paint);
|
||||
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument, paint);
|
||||
} else {
|
||||
SkPaint newPaint = paint;
|
||||
newPaint.setColor4f(color, nullptr);
|
||||
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), newPaint);
|
||||
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument, newPaint);
|
||||
}
|
||||
entry->fGraphicStateIndex = find_or_add(&fGraphicStateResources, std::move(newGraphicState));
|
||||
entry->fGraphicStateIndex = add_resource(fGraphicStateResources, std::move(newGraphicState));
|
||||
|
||||
if (hasText) {
|
||||
entry->fTextScaleX = paint.getTextScaleX();
|
||||
@ -1814,15 +1822,6 @@ static bool is_integral(const SkRect& r) {
|
||||
is_integer(r.bottom());
|
||||
}
|
||||
|
||||
namespace {
|
||||
// This struct will go away when fIndirectReference goes away.
|
||||
struct PDFObj final : public SkPDFObject {
|
||||
PDFObj(SkPDFIndirectReference ref) { fIndirectReference = ref; }
|
||||
// emitObject() is never called since the Object already has a indirect ref.
|
||||
void emitObject(SkWStream*) const override { SK_ABORT("DO NOT REACH HERE"); }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset,
|
||||
const SkRect* src,
|
||||
const SkRect& dst,
|
||||
@ -2046,18 +2045,17 @@ void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset,
|
||||
}
|
||||
|
||||
SkBitmapKey key = imageSubset.key();
|
||||
sk_sp<SkPDFObject>* pdfimagePtr = fDocument->canon()->fPDFBitmapMap.find(key);
|
||||
sk_sp<SkPDFObject> pdfimage = pdfimagePtr ? *pdfimagePtr : nullptr;
|
||||
if (!pdfimage) {
|
||||
SkPDFIndirectReference* pdfimagePtr = fDocument->canon()->fPDFBitmapMap.find(key);
|
||||
SkPDFIndirectReference pdfimage = pdfimagePtr ? *pdfimagePtr : SkPDFIndirectReference();
|
||||
if (!pdfimagePtr) {
|
||||
SkASSERT(imageSubset);
|
||||
auto ref = SkPDFSerializeImage(imageSubset.image().get(), fDocument,
|
||||
pdfimage = SkPDFSerializeImage(imageSubset.image().get(), fDocument,
|
||||
fDocument->metadata().fEncodingQuality);
|
||||
SkASSERT(ref.fValue > 0);
|
||||
pdfimage = sk_make_sp<PDFObj>(ref);
|
||||
SkASSERT((key != SkBitmapKey{{0, 0, 0, 0}, 0}));
|
||||
fDocument->canon()->fPDFBitmapMap.set(key, pdfimage);
|
||||
}
|
||||
this->drawFormXObject(std::move(pdfimage), content.stream());
|
||||
SkASSERT(pdfimage != SkPDFIndirectReference());
|
||||
this->drawFormXObject(pdfimage, content.stream());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -33,7 +33,6 @@ class SkPDFDocument;
|
||||
class SkPDFDict;
|
||||
class SkPDFFont;
|
||||
class SkPDFObject;
|
||||
class SkPDFStream;
|
||||
class SkRRect;
|
||||
struct SkPDFIndirectReference;
|
||||
|
||||
@ -167,9 +166,9 @@ private:
|
||||
std::vector<RectWithData> fLinkToDestinations;
|
||||
std::vector<NamedDestination> fNamedDestinations;
|
||||
|
||||
std::vector<sk_sp<SkPDFObject>> fGraphicStateResources;
|
||||
std::vector<sk_sp<SkPDFObject>> fXObjectResources;
|
||||
std::vector<sk_sp<SkPDFObject>> fShaderResources;
|
||||
SkTHashSet<SkPDFIndirectReference> fGraphicStateResources;
|
||||
SkTHashSet<SkPDFIndirectReference> fXObjectResources;
|
||||
SkTHashSet<SkPDFIndirectReference> fShaderResources;
|
||||
SkTHashSet<SkPDFIndirectReference> fFontResources;
|
||||
int fNodeId;
|
||||
|
||||
@ -199,10 +198,10 @@ private:
|
||||
SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
|
||||
|
||||
// Set alpha to true if making a transparency group form x-objects.
|
||||
sk_sp<SkPDFObject> makeFormXObjectFromDevice(bool alpha = false);
|
||||
SkPDFIndirectReference makeFormXObjectFromDevice(bool alpha = false);
|
||||
|
||||
void drawFormXObjectWithMask(sk_sp<SkPDFObject> xObject,
|
||||
sk_sp<SkPDFObject> mask,
|
||||
void drawFormXObjectWithMask(SkPDFIndirectReference xObject,
|
||||
SkPDFIndirectReference sMask,
|
||||
SkBlendMode,
|
||||
bool invertClip);
|
||||
|
||||
@ -211,11 +210,11 @@ private:
|
||||
// setUpContentEntry and finishContentEntry can be used directly, but
|
||||
// the preferred method is to use the ScopedContentEntry helper class.
|
||||
SkDynamicMemoryWStream* setUpContentEntry(const SkClipStack* clipStack,
|
||||
const SkMatrix& matrix,
|
||||
const SkPaint& paint,
|
||||
bool hasText,
|
||||
sk_sp<SkPDFObject>* dst);
|
||||
void finishContentEntry(const SkClipStack*, SkBlendMode, sk_sp<SkPDFObject> dst, SkPath* shape);
|
||||
const SkMatrix& matrix,
|
||||
const SkPaint& paint,
|
||||
bool hasText,
|
||||
SkPDFIndirectReference* dst);
|
||||
void finishContentEntry(const SkClipStack*, SkBlendMode, SkPDFIndirectReference, SkPath*);
|
||||
bool isContentEmpty();
|
||||
|
||||
void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
|
||||
@ -248,8 +247,8 @@ private:
|
||||
|
||||
void addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice, SkDynamicMemoryWStream*);
|
||||
void clearMaskOnGraphicState(SkDynamicMemoryWStream*);
|
||||
void setGraphicState(sk_sp<SkPDFObject>, SkDynamicMemoryWStream*);
|
||||
void drawFormXObject(sk_sp<SkPDFObject>, SkDynamicMemoryWStream*);
|
||||
void setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream*);
|
||||
void drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream*);
|
||||
|
||||
bool hasEmptyClip() const { return this->cs().isEmpty(this->bounds()); }
|
||||
|
||||
|
@ -47,10 +47,6 @@ SkPDFObjectSerializer::SkPDFObjectSerializer() = default;
|
||||
|
||||
SkPDFObjectSerializer::~SkPDFObjectSerializer() = default;
|
||||
|
||||
SkPDFObjectSerializer::SkPDFObjectSerializer(SkPDFObjectSerializer&&) = default;
|
||||
|
||||
SkPDFObjectSerializer& SkPDFObjectSerializer::operator=(SkPDFObjectSerializer&&) = default;
|
||||
|
||||
|
||||
#define SKPDF_MAGIC "\xD3\xEB\xE9\xE1"
|
||||
#ifndef SK_BUILD_FOR_WIN
|
||||
@ -59,16 +55,13 @@ static_assert((SKPDF_MAGIC[1] & 0x7F) == "Skia"[1], "");
|
||||
static_assert((SKPDF_MAGIC[2] & 0x7F) == "Skia"[2], "");
|
||||
static_assert((SKPDF_MAGIC[3] & 0x7F) == "Skia"[3], "");
|
||||
#endif
|
||||
void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream,
|
||||
const SkPDF::Metadata& md) {
|
||||
void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream) {
|
||||
fBaseOffset = wStream->bytesWritten();
|
||||
static const char kHeader[] = "%PDF-1.4\n%" SKPDF_MAGIC "\n";
|
||||
wStream->writeText(kHeader);
|
||||
// The PDF spec recommends including a comment with four
|
||||
// bytes, all with their high bits set. "\xD3\xEB\xE9\xE1" is
|
||||
// "Skia" with the high bits set.
|
||||
fInfoDict = SkPDFMetadata::MakeDocumentInformationDict(md);
|
||||
this->serializeObject(fInfoDict, wStream);
|
||||
}
|
||||
#undef SKPDF_MAGIC
|
||||
|
||||
@ -86,8 +79,7 @@ void SkPDFObjectSerializer::endObject(SkWStream* wStream) {
|
||||
|
||||
void SkPDFObjectSerializer::serializeObject(const sk_sp<SkPDFObject>& object,
|
||||
SkWStream* wStream) {
|
||||
SkPDFObjNumMap objNumMap;
|
||||
objNumMap.fNextObjectNumber = fNextObjectNumber;
|
||||
SkPDFObjNumMap objNumMap(this);
|
||||
objNumMap.addObjectRecursively(object.get());
|
||||
|
||||
for (const sk_sp<SkPDFObject>& object : objNumMap.fObjects) {
|
||||
@ -96,18 +88,18 @@ void SkPDFObjectSerializer::serializeObject(const sk_sp<SkPDFObject>& object,
|
||||
this->endObject(wStream);
|
||||
object->drop();
|
||||
}
|
||||
fNextObjectNumber = objNumMap.fNextObjectNumber; // save for later.
|
||||
}
|
||||
|
||||
|
||||
// Xref table and footer
|
||||
void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
|
||||
const sk_sp<SkPDFObject> docCatalog,
|
||||
sk_sp<SkPDFObject> id) {
|
||||
SkPDFIndirectReference infoDict,
|
||||
SkPDFIndirectReference docCatalog,
|
||||
SkUUID uuid) {
|
||||
int xRefFileOffset = this->offset(wStream).fValue;
|
||||
// Include the special zeroth object in the count.
|
||||
|
||||
int objCount = SkToS32(fNextObjectNumber);
|
||||
int objCount = SkToS32(fNextObjectNumber.load());
|
||||
wStream->writeText("xref\n0 ");
|
||||
wStream->writeDecAsText(objCount);
|
||||
wStream->writeText("\n0000000000 65535 f \n");
|
||||
@ -118,12 +110,12 @@ void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
|
||||
}
|
||||
SkPDFDict trailerDict;
|
||||
trailerDict.insertInt("Size", objCount);
|
||||
SkASSERT(docCatalog);
|
||||
trailerDict.insertObjRef("Root", docCatalog);
|
||||
SkASSERT(fInfoDict);
|
||||
trailerDict.insertObjRef("Info", std::move(fInfoDict));
|
||||
if (id) {
|
||||
trailerDict.insertObject("ID", std::move(id));
|
||||
SkASSERT(docCatalog != SkPDFIndirectReference());
|
||||
trailerDict.insertRef("Root", docCatalog);
|
||||
SkASSERT(infoDict != SkPDFIndirectReference());
|
||||
trailerDict.insertRef("Info", infoDict);
|
||||
if (SkUUID() != uuid) {
|
||||
trailerDict.insertObject("ID", SkPDFMetadata::MakePdfId(uuid, uuid));
|
||||
}
|
||||
wStream->writeText("trailer\n");
|
||||
trailerDict.emitObject(wStream);
|
||||
@ -216,6 +208,7 @@ SkPDFDocument::SkPDFDocument(SkWStream* stream,
|
||||
if (fMetadata.fStructureElementTreeRoot) {
|
||||
fTagRoot = recursiveBuildTagTree(*fMetadata.fStructureElementTreeRoot, nullptr);
|
||||
}
|
||||
fExecutor = metadata.fExecutor;
|
||||
}
|
||||
|
||||
SkPDFDocument::~SkPDFDocument() {
|
||||
@ -225,28 +218,34 @@ SkPDFDocument::~SkPDFDocument() {
|
||||
|
||||
SkPDFIndirectReference SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) {
|
||||
SkASSERT(object);
|
||||
fObjectSerializer.serializeObject(object, this->getStream());
|
||||
if (object->fIndirectReference == SkPDFIndirectReference()) {
|
||||
SkAutoMutexAcquire autoMutexAcquire(fMutex);
|
||||
fObjectSerializer.serializeObject(object, this->getStream());
|
||||
}
|
||||
SkASSERT(object->fIndirectReference != SkPDFIndirectReference());
|
||||
return object->fIndirectReference;
|
||||
}
|
||||
|
||||
SkPDFIndirectReference SkPDFDocument::emit(const SkPDFObject& object){
|
||||
SkPDFIndirectReference ref = this->reserve();
|
||||
SkPDFIndirectReference SkPDFDocument::emit(const SkPDFObject& object, SkPDFIndirectReference ref){
|
||||
object.emitObject(this->beginObject(ref));
|
||||
this->endObject();
|
||||
return ref;
|
||||
}
|
||||
|
||||
SkPDFIndirectReference SkPDFDocument::reserve() {
|
||||
SkPDFIndirectReference SkPDFDocument::reserveRef() {
|
||||
++fOutstandingRefs;
|
||||
return SkPDFIndirectReference{fObjectSerializer.fNextObjectNumber++};
|
||||
return fObjectSerializer.reserve();
|
||||
};
|
||||
|
||||
SkWStream* SkPDFDocument::beginObject(SkPDFIndirectReference ref) {
|
||||
--fOutstandingRefs;
|
||||
fMutex.acquire();
|
||||
return fObjectSerializer.beginObject(ref, this->getStream());
|
||||
};
|
||||
|
||||
void SkPDFDocument::endObject() {
|
||||
fObjectSerializer.endObject(this->getStream());
|
||||
fMutex.release();
|
||||
fSemaphore.signal();
|
||||
};
|
||||
|
||||
static SkSize operator*(SkISize u, SkScalar s) { return SkSize{u.width() * s, u.height() * s}; }
|
||||
@ -256,18 +255,22 @@ SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height) {
|
||||
SkASSERT(fCanvas.imageInfo().dimensions().isZero());
|
||||
if (fPages.empty()) {
|
||||
// if this is the first page if the document.
|
||||
fObjectSerializer.serializeHeader(this->getStream(), fMetadata);
|
||||
{
|
||||
SkAutoMutexAcquire autoMutexAcquire(fMutex);
|
||||
fObjectSerializer.serializeHeader(this->getStream());
|
||||
}
|
||||
|
||||
fInfoDict = this->serialize(SkPDFMetadata::MakeDocumentInformationDict(fMetadata));
|
||||
fDests = sk_make_sp<SkPDFDict>();
|
||||
if (fMetadata.fPDFA) {
|
||||
SkPDFMetadata::UUID uuid = SkPDFMetadata::CreateUUID(fMetadata);
|
||||
fUUID = SkPDFMetadata::CreateUUID(fMetadata);
|
||||
// We use the same UUID for Document ID and Instance ID since this
|
||||
// is the first revision of this document (and Skia does not
|
||||
// support revising existing PDF documents).
|
||||
// If we are not in PDF/A mode, don't use a UUID since testing
|
||||
// works best with reproducible outputs.
|
||||
fID = SkPDFMetadata::MakePdfId(uuid, uuid);
|
||||
fXMP = SkPDFMetadata::MakeXMPObject(fMetadata, uuid, uuid);
|
||||
fObjectSerializer.serializeObject(fXMP, this->getStream());
|
||||
fXMP = this->serialize(
|
||||
SkPDFMetadata::MakeXMPObject(fMetadata, fUUID, fUUID));
|
||||
}
|
||||
}
|
||||
// By scaling the page at the device level, we will create bitmap layer
|
||||
@ -295,7 +298,7 @@ void SkPDFDocument::onEndPage() {
|
||||
auto page = sk_make_sp<SkPDFDict>("Page");
|
||||
|
||||
SkSize mediaSize = fPageDevice->imageInfo().dimensions() * fInverseRasterScale;
|
||||
auto contentObject = sk_make_sp<SkPDFStream>(fPageDevice->content());
|
||||
std::unique_ptr<SkStreamAsset> pageContent = fPageDevice->content();
|
||||
auto resourceDict = fPageDevice->makeResourceDict();
|
||||
auto annotations = fPageDevice->getAnnotations();
|
||||
fPageDevice->appendDestinations(fDests.get(), page.get());
|
||||
@ -307,8 +310,7 @@ void SkPDFDocument::onEndPage() {
|
||||
if (annotations) {
|
||||
page->insertObject("Annots", std::move(annotations));
|
||||
}
|
||||
this->serialize(contentObject);
|
||||
page->insertObjRef("Contents", std::move(contentObject));
|
||||
page->insertRef("Contents", SkPDFStreamOut(nullptr, std::move(pageContent), this));
|
||||
// The StructParents unique identifier for each page is just its
|
||||
// 0-based page index.
|
||||
page->insertInt("StructParents", static_cast<int>(fPages.size()));
|
||||
@ -320,14 +322,14 @@ void SkPDFDocument::onAbort() {
|
||||
}
|
||||
|
||||
void SkPDFDocument::reset() {
|
||||
fObjectSerializer = SkPDFObjectSerializer();
|
||||
reset_object(&fObjectSerializer);
|
||||
fCanon = SkPDFCanon();
|
||||
reset_object(&fCanvas);
|
||||
fPages = std::vector<sk_sp<SkPDFDict>>();
|
||||
fDests = nullptr;
|
||||
fPageDevice = nullptr;
|
||||
fID = nullptr;
|
||||
fXMP = nullptr;
|
||||
fUUID = SkUUID();
|
||||
fXMP = SkPDFIndirectReference();
|
||||
fMetadata = SkPDF::Metadata();
|
||||
fRasterScale = 1;
|
||||
fInverseRasterScale = 1;
|
||||
@ -447,14 +449,14 @@ static sk_sp<SkData> SkSrgbIcm() {
|
||||
return SkData::MakeWithoutCopy(kProfile, kProfileLength);
|
||||
}
|
||||
|
||||
static sk_sp<SkPDFStream> make_srgb_color_profile() {
|
||||
sk_sp<SkPDFStream> stream = sk_make_sp<SkPDFStream>(SkSrgbIcm());
|
||||
stream->dict()->insertInt("N", 3);
|
||||
stream->dict()->insertObject("Range", SkPDFMakeArray(0, 1, 0, 1, 0, 1));
|
||||
return stream;
|
||||
static SkPDFIndirectReference make_srgb_color_profile(SkPDFDocument* doc) {
|
||||
sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
|
||||
dict->insertInt("N", 3);
|
||||
dict->insertObject("Range", SkPDFMakeArray(0, 1, 0, 1, 0, 1));
|
||||
return SkPDFStreamOut(std::move(dict), SkMemoryStream::Make(SkSrgbIcm()), doc, true);
|
||||
}
|
||||
|
||||
static sk_sp<SkPDFArray> make_srgb_output_intents() {
|
||||
static sk_sp<SkPDFArray> make_srgb_output_intents(SkPDFDocument* doc) {
|
||||
// sRGB is specified by HTML, CSS, and SVG.
|
||||
auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
|
||||
outputIntent->insertName("S", "GTS_PDFA1");
|
||||
@ -462,8 +464,7 @@ static sk_sp<SkPDFArray> make_srgb_output_intents() {
|
||||
outputIntent->insertString("OutputConditionIdentifier",
|
||||
"Custom");
|
||||
outputIntent->insertString("Info","sRGB IEC61966-2.1");
|
||||
outputIntent->insertObjRef("DestOutputProfile",
|
||||
make_srgb_color_profile());
|
||||
outputIntent->insertRef("DestOutputProfile", make_srgb_color_profile(doc));
|
||||
auto intentArray = sk_make_sp<SkPDFArray>();
|
||||
intentArray->appendObject(std::move(outputIntent));
|
||||
return intentArray;
|
||||
@ -533,11 +534,11 @@ void SkPDFDocument::onClose(SkWStream* stream) {
|
||||
}
|
||||
auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
|
||||
if (fMetadata.fPDFA) {
|
||||
SkASSERT(fXMP);
|
||||
docCatalog->insertObjRef("Metadata", fXMP);
|
||||
SkASSERT(fXMP != SkPDFIndirectReference());
|
||||
docCatalog->insertRef("Metadata", fXMP);
|
||||
// Don't specify OutputIntents if we are not in PDF/A mode since
|
||||
// no one has ever asked for this feature.
|
||||
docCatalog->insertObject("OutputIntents", make_srgb_output_intents());
|
||||
docCatalog->insertObject("OutputIntents", make_srgb_output_intents(this));
|
||||
}
|
||||
|
||||
sk_sp<SkPDFDict> pageTree = generate_page_tree(fPages);
|
||||
@ -593,15 +594,23 @@ void SkPDFDocument::onClose(SkWStream* stream) {
|
||||
}
|
||||
}
|
||||
}
|
||||
fObjectSerializer.serializeObject(docCatalog, this->getStream());
|
||||
auto docCatalogRef = this->serialize(docCatalog);
|
||||
for (const SkPDFFont* f : get_fonts(fCanon)) {
|
||||
f->emitSubset(this);
|
||||
}
|
||||
SkASSERT(fOutstandingRefs == 0);
|
||||
fObjectSerializer.serializeFooter(this->getStream(), docCatalog, fID);
|
||||
while (fOutstandingRefs > 0) {
|
||||
fSemaphore.wait();
|
||||
--fOutstandingRefs;
|
||||
}
|
||||
|
||||
{
|
||||
SkAutoMutexAcquire autoMutexAcquire(fMutex);
|
||||
fObjectSerializer.serializeFooter(this->getStream(), fInfoDict, docCatalogRef, fUUID);
|
||||
}
|
||||
this->reset();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SkPDF::SetNodeId(SkCanvas* canvas, int nodeID) {
|
||||
|
@ -8,13 +8,17 @@
|
||||
#define SkPDFDocumentPriv_DEFINED
|
||||
|
||||
#include "SkCanvas.h"
|
||||
#include "SkPDFDocument.h"
|
||||
#include "SkPDFCanon.h"
|
||||
#include "SkPDFDocument.h"
|
||||
#include "SkPDFFont.h"
|
||||
#include "SkPDFMetadata.h"
|
||||
#include "SkUUID.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
class SkPDFDevice;
|
||||
class SkPDFTag;
|
||||
class SkExecutor;
|
||||
|
||||
const char* SkPDFGetNodeIdKey();
|
||||
|
||||
@ -31,23 +35,26 @@ struct SkPDFOffsetMap {
|
||||
// Logically part of SkPDFDocument (like SkPDFCanon), but separate to
|
||||
// keep similar functionality together.
|
||||
struct SkPDFObjectSerializer {
|
||||
int fNextObjectNumber = 1;
|
||||
std::atomic<int> fNextObjectNumber = {1};
|
||||
SkPDFIndirectReference reserve() { return SkPDFIndirectReference{fNextObjectNumber++}; }
|
||||
SkPDFOffsetMap fOffsets;
|
||||
sk_sp<SkPDFObject> fInfoDict;
|
||||
size_t fBaseOffset = SIZE_MAX;
|
||||
|
||||
SkPDFObjectSerializer();
|
||||
~SkPDFObjectSerializer();
|
||||
SkPDFObjectSerializer(SkPDFObjectSerializer&&);
|
||||
SkPDFObjectSerializer& operator=(SkPDFObjectSerializer&&);
|
||||
SkPDFObjectSerializer(SkPDFObjectSerializer&&) = delete;
|
||||
SkPDFObjectSerializer& operator=(SkPDFObjectSerializer&&) = delete;
|
||||
SkPDFObjectSerializer(const SkPDFObjectSerializer&) = delete;
|
||||
SkPDFObjectSerializer& operator=(const SkPDFObjectSerializer&) = delete;
|
||||
|
||||
SkWStream* beginObject(SkPDFIndirectReference, SkWStream*);
|
||||
void endObject(SkWStream*);
|
||||
void serializeHeader(SkWStream*, const SkPDF::Metadata&);
|
||||
void serializeHeader(SkWStream*);
|
||||
void serializeObject(const sk_sp<SkPDFObject>&, SkWStream*);
|
||||
void serializeFooter(SkWStream*, const sk_sp<SkPDFObject>, sk_sp<SkPDFObject>);
|
||||
void serializeFooter(SkWStream*,
|
||||
SkPDFIndirectReference infoDict,
|
||||
SkPDFIndirectReference docCatalog,
|
||||
SkUUID uuid);
|
||||
SkPDFFileOffset offset(SkWStream*);
|
||||
};
|
||||
|
||||
@ -73,7 +80,8 @@ public:
|
||||
after calling serialize, since those changes will be too late.
|
||||
*/
|
||||
SkPDFIndirectReference serialize(const sk_sp<SkPDFObject>&);
|
||||
SkPDFIndirectReference emit(const SkPDFObject&);
|
||||
SkPDFIndirectReference emit(const SkPDFObject&, SkPDFIndirectReference);
|
||||
SkPDFIndirectReference emit(const SkPDFObject& o) { return this->emit(o, this->reserveRef()); }
|
||||
SkPDFCanon* canon() { return &fCanon; }
|
||||
const SkPDF::Metadata& metadata() const { return fMetadata; }
|
||||
|
||||
@ -81,10 +89,12 @@ public:
|
||||
// Returns -1 if no mark ID.
|
||||
int getMarkIdForNodeId(int nodeId);
|
||||
|
||||
SkPDFIndirectReference reserve();
|
||||
SkPDFIndirectReference reserveRef();
|
||||
SkWStream* beginObject(SkPDFIndirectReference);
|
||||
void endObject();
|
||||
|
||||
SkExecutor* executor() const { return fExecutor; }
|
||||
|
||||
private:
|
||||
sk_sp<SkPDFTag> recursiveBuildTagTree(const SkPDF::StructureElementNode& node,
|
||||
sk_sp<SkPDFTag> parent);
|
||||
@ -95,11 +105,13 @@ private:
|
||||
std::vector<sk_sp<SkPDFDict>> fPages;
|
||||
sk_sp<SkPDFDict> fDests;
|
||||
sk_sp<SkPDFDevice> fPageDevice;
|
||||
sk_sp<SkPDFObject> fID;
|
||||
sk_sp<SkPDFObject> fXMP;
|
||||
SkUUID fUUID;
|
||||
SkPDFIndirectReference fInfoDict;
|
||||
SkPDFIndirectReference fXMP;
|
||||
SkPDF::Metadata fMetadata;
|
||||
SkScalar fRasterScale = 1;
|
||||
SkScalar fInverseRasterScale = 1;
|
||||
SkExecutor* fExecutor = nullptr;
|
||||
|
||||
// For tagged PDFs.
|
||||
|
||||
@ -110,7 +122,9 @@ private:
|
||||
// A mapping from node ID to tag for fast lookup.
|
||||
SkTHashMap<int, sk_sp<SkPDFTag>> fNodeIdToTag;
|
||||
|
||||
int fOutstandingRefs = 0;
|
||||
std::atomic<int> fOutstandingRefs = {0};
|
||||
SkMutex fMutex;
|
||||
SkSemaphore fSemaphore;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
@ -225,7 +225,7 @@ SkPDFFont* SkPDFFont::GetFontResource(SkPDFDocument* doc,
|
||||
firstNonZeroGlyph = subsetCode;
|
||||
lastGlyph = SkToU16(SkTMin<int>((int)lastGlyph, 254 + (int)subsetCode));
|
||||
}
|
||||
auto ref = doc->reserve();
|
||||
auto ref = doc->reserveRef();
|
||||
return canon->fFontMap.set(
|
||||
fontID, SkPDFFont(std::move(typeface), firstNonZeroGlyph, lastGlyph, type, ref));
|
||||
}
|
||||
@ -321,11 +321,13 @@ static void emit_subset_type0(const SkPDFFont& font, SkPDFDocument* doc) {
|
||||
stream_to_data(std::move(fontAsset)), font.glyphUsage(),
|
||||
metrics.fFontName.c_str(), ttcIndex);
|
||||
if (subsetFontData) {
|
||||
size_t len = subsetFontData->size();
|
||||
sk_sp<SkPDFStream> subsetStream = sk_make_sp<SkPDFStream>(
|
||||
std::move(subsetFontData));
|
||||
subsetStream->dict()->insertInt("Length1", SkToInt(len));
|
||||
descriptor->insertRef("FontFile2", doc->serialize(subsetStream));
|
||||
sk_sp<SkPDFDict> tmp = sk_make_sp<SkPDFDict>();
|
||||
tmp->insertInt("Length1", SkToInt(subsetFontData->size()));
|
||||
descriptor->insertRef(
|
||||
"FontFile2",
|
||||
SkPDFStreamOut(std::move(tmp),
|
||||
SkMemoryStream::Make(std::move(subsetFontData)),
|
||||
doc, true));
|
||||
break;
|
||||
}
|
||||
// If subsetting fails, fall back to original font data.
|
||||
@ -335,15 +337,19 @@ static void emit_subset_type0(const SkPDFFont& font, SkPDFDocument* doc) {
|
||||
if (!fontAsset || fontAsset->getLength() == 0) { break; }
|
||||
}
|
||||
#endif // SK_PDF_SUBSET_SUPPORTED
|
||||
auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontAsset));
|
||||
fontStream->dict()->insertInt("Length1", fontSize);
|
||||
descriptor->insertRef("FontFile2", doc->serialize(fontStream));
|
||||
sk_sp<SkPDFDict> tmp = sk_make_sp<SkPDFDict>();
|
||||
tmp->insertInt("Length1", fontSize);
|
||||
descriptor->insertRef("FontFile2",
|
||||
SkPDFStreamOut(std::move(tmp), std::move(fontAsset),
|
||||
doc, true));
|
||||
break;
|
||||
}
|
||||
case SkAdvancedTypefaceMetrics::kType1CID_Font: {
|
||||
auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontAsset));
|
||||
fontStream->dict()->insertName("Subtype", "CIDFontType0C");
|
||||
descriptor->insertRef("FontFile3", doc->serialize(fontStream));
|
||||
sk_sp<SkPDFDict> tmp = sk_make_sp<SkPDFDict>();
|
||||
tmp->insertName("Subtype", "CIDFontType0C");
|
||||
descriptor->insertRef("FontFile3",
|
||||
SkPDFStreamOut(std::move(tmp), std::move(fontAsset),
|
||||
doc, true));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -398,17 +404,15 @@ static void emit_subset_type0(const SkPDFFont& font, SkPDFDocument* doc) {
|
||||
const std::vector<SkUnichar>& glyphToUnicode =
|
||||
SkPDFFont::GetUnicodeMap(font.typeface(), canon);
|
||||
SkASSERT(SkToSizeT(font.typeface()->countGlyphs()) == glyphToUnicode.size());
|
||||
fontDict.insertRef("ToUnicode",
|
||||
doc->serialize(
|
||||
SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
|
||||
&font.glyphUsage(),
|
||||
font.multiByteGlyphs(),
|
||||
font.firstGlyphID(),
|
||||
font.lastGlyphID())));
|
||||
std::unique_ptr<SkStreamAsset> toUnicode =
|
||||
SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
|
||||
&font.glyphUsage(),
|
||||
font.multiByteGlyphs(),
|
||||
font.firstGlyphID(),
|
||||
font.lastGlyphID());
|
||||
fontDict.insertRef("ToUnicode", SkPDFStreamOut(nullptr, std::move(toUnicode), doc));
|
||||
|
||||
SkWStream* stream = doc->beginObject(font.indirectReference());
|
||||
fontDict.emitObject(stream);
|
||||
doc->endObject();
|
||||
doc->emit(fontDict, font.indirectReference());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -431,11 +435,13 @@ static SkPDFIndirectReference make_type1_font_descriptor(SkPDFDocument* doc,
|
||||
sk_sp<SkData> fontData = SkPDFConvertType1FontStream(std::move(rawFontData),
|
||||
&header, &data, &trailer);
|
||||
if (fontData) {
|
||||
SkPDFStream fontStream(std::move(fontData));
|
||||
fontStream.dict()->insertInt("Length1", header);
|
||||
fontStream.dict()->insertInt("Length2", data);
|
||||
fontStream.dict()->insertInt("Length3", trailer);
|
||||
descriptor.insertRef("FontFile", doc->emit(fontStream));
|
||||
sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
|
||||
dict->insertInt("Length1", header);
|
||||
dict->insertInt("Length2", data);
|
||||
dict->insertInt("Length3", trailer);
|
||||
auto fontStream = SkMemoryStream::Make(std::move(fontData));
|
||||
descriptor.insertRef("FontFile", SkPDFStreamOut(std::move(dict),
|
||||
std::move(fontStream), doc, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -514,9 +520,7 @@ static void emit_subset_type1(const SkPDFFont& pdfFont, SkPDFDocument* doc) {
|
||||
encoding->insertObject("Differences", std::move(encDiffs));
|
||||
font.insertObject("Encoding", std::move(encoding));
|
||||
|
||||
SkWStream* stream = doc->beginObject(pdfFont.indirectReference());
|
||||
font.emitObject(stream);
|
||||
doc->endObject();
|
||||
doc->emit(font, pdfFont.indirectReference());
|
||||
}
|
||||
|
||||
void SkPDFFont::GetType1GlyphNames(const SkTypeface& face, SkString* dst) {
|
||||
@ -717,7 +721,8 @@ static void emit_subset_type3(const SkPDFFont& pdfFont, SkPDFDocument* doc) {
|
||||
content.writeText(" Do\n");
|
||||
}
|
||||
}
|
||||
charProcs->insertRef(characterName, doc->emit(SkPDFStream(content.detachAsStream())));
|
||||
charProcs->insertRef(characterName, SkPDFStreamOut(nullptr,
|
||||
content.detachAsStream(), doc));
|
||||
}
|
||||
encDiffs->appendName(std::move(characterName));
|
||||
widthArray->appendScalar(advance);
|
||||
@ -750,20 +755,18 @@ static void emit_subset_type3(const SkPDFFont& pdfFont, SkPDFDocument* doc) {
|
||||
|
||||
const std::vector<SkUnichar>& glyphToUnicode = SkPDFFont::GetUnicodeMap(typeface, canon);
|
||||
SkASSERT(glyphToUnicode.size() == SkToSizeT(typeface->countGlyphs()));
|
||||
sk_sp<SkPDFStream> toUnicodeCmap = SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
|
||||
&subset,
|
||||
false,
|
||||
firstGlyphID,
|
||||
lastGlyphID);
|
||||
font.insertRef("ToUnicode", doc->serialize(toUnicodeCmap));
|
||||
auto toUnicodeCmap = SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
|
||||
&subset,
|
||||
false,
|
||||
firstGlyphID,
|
||||
lastGlyphID);
|
||||
font.insertRef("ToUnicode", SkPDFStreamOut(nullptr, std::move(toUnicodeCmap), doc));
|
||||
font.insertRef("FontDescriptor", type3_descriptor(doc, typeface, cache.get()));
|
||||
font.insertObject("Widths", std::move(widthArray));
|
||||
font.insertObject("Encoding", std::move(encoding));
|
||||
font.insertObject("CharProcs", std::move(charProcs));
|
||||
|
||||
SkWStream* stream = doc->beginObject(pdfFont.indirectReference());
|
||||
font.emitObject(stream);
|
||||
doc->endObject();
|
||||
doc->emit(font, pdfFont.indirectReference());
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,20 +9,20 @@
|
||||
#include "SkPDFFormXObject.h"
|
||||
#include "SkPDFUtils.h"
|
||||
|
||||
sk_sp<SkPDFObject> SkPDFMakeFormXObject(std::unique_ptr<SkStreamAsset> content,
|
||||
sk_sp<SkPDFArray> mediaBox,
|
||||
sk_sp<SkPDFDict> resourceDict,
|
||||
const SkMatrix& inverseTransform,
|
||||
const char* colorSpace) {
|
||||
auto form = sk_make_sp<SkPDFStream>(std::move(content));
|
||||
form->dict()->insertName("Type", "XObject");
|
||||
form->dict()->insertName("Subtype", "Form");
|
||||
SkPDFIndirectReference SkPDFMakeFormXObject(SkPDFDocument* doc,
|
||||
std::unique_ptr<SkStreamAsset> content,
|
||||
sk_sp<SkPDFArray> mediaBox,
|
||||
sk_sp<SkPDFDict> resourceDict,
|
||||
const SkMatrix& inverseTransform,
|
||||
const char* colorSpace) {
|
||||
sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
|
||||
dict->insertName("Type", "XObject");
|
||||
dict->insertName("Subtype", "Form");
|
||||
if (!inverseTransform.isIdentity()) {
|
||||
sk_sp<SkPDFObject> mat(SkPDFUtils::MatrixToArray(inverseTransform));
|
||||
form->dict()->insertObject("Matrix", std::move(mat));
|
||||
dict->insertObject("Matrix", SkPDFUtils::MatrixToArray(inverseTransform));
|
||||
}
|
||||
form->dict()->insertObject("Resources", std::move(resourceDict));
|
||||
form->dict()->insertObject("BBox", std::move(mediaBox));
|
||||
dict->insertObject("Resources", std::move(resourceDict));
|
||||
dict->insertObject("BBox", std::move(mediaBox));
|
||||
|
||||
// Right now FormXObject is only used for saveLayer, which implies
|
||||
// isolated blending. Do this conditionally if that changes.
|
||||
@ -34,6 +34,6 @@ sk_sp<SkPDFObject> SkPDFMakeFormXObject(std::unique_ptr<SkStreamAsset> content,
|
||||
group->insertName("CS", colorSpace);
|
||||
}
|
||||
group->insertBool("I", true); // Isolated.
|
||||
form->dict()->insertObject("Group", std::move(group));
|
||||
return std::move(form);
|
||||
dict->insertObject("Group", std::move(group));
|
||||
return SkPDFStreamOut(std::move(dict), std::move(content), doc);
|
||||
}
|
||||
|
@ -12,14 +12,17 @@
|
||||
#include "SkPDFDevice.h"
|
||||
#include "SkPDFTypes.h"
|
||||
|
||||
class SkPDFDocument;
|
||||
|
||||
/** A form XObject is a self contained description of a graphics
|
||||
object. A form XObject is a page object with slightly different
|
||||
syntax, that can be drawn into a page content stream, just like a
|
||||
bitmap XObject can be drawn into a page content stream.
|
||||
*/
|
||||
sk_sp<SkPDFObject> SkPDFMakeFormXObject(std::unique_ptr<SkStreamAsset> content,
|
||||
sk_sp<SkPDFArray> mediaBox,
|
||||
sk_sp<SkPDFDict> resourceDict,
|
||||
const SkMatrix& inverseTransform,
|
||||
const char* colorSpace);
|
||||
SkPDFIndirectReference SkPDFMakeFormXObject(SkPDFDocument* doc,
|
||||
std::unique_ptr<SkStreamAsset> content,
|
||||
sk_sp<SkPDFArray> mediaBox,
|
||||
sk_sp<SkPDFDict> resourceDict,
|
||||
const SkMatrix& inverseTransform,
|
||||
const char* colorSpace);
|
||||
#endif
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "SkPDFFormXObject.h"
|
||||
#include "SkPDFGraphicState.h"
|
||||
#include "SkPDFResourceDict.h"
|
||||
#include "SkPDFTypes.h"
|
||||
#include "SkPDFUtils.h"
|
||||
|
||||
static uint32_t hash(const SkShader::GradientInfo& v) {
|
||||
@ -573,20 +574,19 @@ static bool split_perspective(const SkMatrix in, SkMatrix* affine,
|
||||
return true;
|
||||
}
|
||||
|
||||
static sk_sp<SkPDFStream> make_ps_function(
|
||||
std::unique_ptr<SkStreamAsset> psCode,
|
||||
sk_sp<SkPDFArray> domain,
|
||||
sk_sp<SkPDFObject> range) {
|
||||
auto result = sk_make_sp<SkPDFStream>(std::move(psCode));
|
||||
result->dict()->insertInt("FunctionType", 4);
|
||||
result->dict()->insertObject("Domain", std::move(domain));
|
||||
result->dict()->insertObject("Range", std::move(range));
|
||||
return result;
|
||||
static SkPDFIndirectReference make_ps_function(std::unique_ptr<SkStreamAsset> psCode,
|
||||
sk_sp<SkPDFArray> domain,
|
||||
sk_sp<SkPDFObject> range,
|
||||
SkPDFDocument* doc) {
|
||||
sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
|
||||
dict->insertInt("FunctionType", 4);
|
||||
dict->insertObject("Domain", std::move(domain));
|
||||
dict->insertObject("Range", std::move(range));
|
||||
return SkPDFStreamOut(std::move(dict), std::move(psCode), doc);
|
||||
}
|
||||
|
||||
|
||||
static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
|
||||
const SkPDFGradientShader::Key& state) {
|
||||
static SkPDFIndirectReference make_function_shader(SkPDFDocument* doc,
|
||||
const SkPDFGradientShader::Key& state) {
|
||||
SkPoint transformPoints[2];
|
||||
const SkShader::GradientInfo& info = state.fInfo;
|
||||
SkMatrix finalMatrix = state.fCanvasTransform;
|
||||
@ -670,7 +670,7 @@ static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
|
||||
case SkShader::kColor_GradientType:
|
||||
case SkShader::kNone_GradientType:
|
||||
default:
|
||||
return nullptr;
|
||||
return SkPDFIndirectReference();
|
||||
}
|
||||
|
||||
// Move any scaling (assuming a unit gradient) or translation
|
||||
@ -691,14 +691,14 @@ static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
|
||||
if (finalMatrix.hasPerspective()) {
|
||||
if (!split_perspective(finalMatrix,
|
||||
&finalMatrix, &perspectiveInverseOnly)) {
|
||||
return nullptr;
|
||||
return SkPDFIndirectReference();
|
||||
}
|
||||
}
|
||||
|
||||
SkRect bbox;
|
||||
bbox.set(state.fBBox);
|
||||
if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &bbox)) {
|
||||
return nullptr;
|
||||
return SkPDFIndirectReference();
|
||||
}
|
||||
SkDynamicMemoryWStream functionCode;
|
||||
|
||||
@ -707,7 +707,7 @@ static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
|
||||
if (state.fType == SkShader::kConical_GradientType) {
|
||||
SkMatrix inverseMapperMatrix;
|
||||
if (!mapperMatrix.invert(&inverseMapperMatrix)) {
|
||||
return nullptr;
|
||||
return SkPDFIndirectReference();
|
||||
}
|
||||
inverseMapperMatrix.mapPoints(infoCopy.fPoint, 2);
|
||||
infoCopy.fRadius[0] = inverseMapperMatrix.mapRadius(info.fRadius[0]);
|
||||
@ -735,13 +735,10 @@ static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
|
||||
bbox.bottom());
|
||||
pdfShader->insertObject("Domain", domain);
|
||||
|
||||
sk_sp<SkPDFArray>& rangeObject = canon->fRangeObject;
|
||||
if (!rangeObject) {
|
||||
rangeObject = SkPDFMakeArray(0, 1, 0, 1, 0, 1);
|
||||
}
|
||||
pdfShader->insertObjRef("Function",
|
||||
make_ps_function(functionCode.detachAsStream(), std::move(domain),
|
||||
rangeObject));
|
||||
sk_sp<SkPDFArray> rangeObject = SkPDFMakeArray(0, 1, 0, 1, 0, 1);
|
||||
pdfShader->insertRef("Function",
|
||||
make_ps_function(functionCode.detachAsStream(), std::move(domain),
|
||||
std::move(rangeObject), doc));
|
||||
}
|
||||
|
||||
pdfShader->insertInt("ShadingType", shadingType);
|
||||
@ -752,38 +749,40 @@ static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
|
||||
pdfFunctionShader->insertObject("Matrix", SkPDFUtils::MatrixToArray(finalMatrix));
|
||||
pdfFunctionShader->insertObject("Shading", std::move(pdfShader));
|
||||
|
||||
return pdfFunctionShader;
|
||||
return doc->serialize(pdfFunctionShader);
|
||||
}
|
||||
|
||||
static sk_sp<SkPDFObject> find_pdf_shader(SkPDFDocument* doc,
|
||||
SkPDFGradientShader::Key key,
|
||||
bool keyHasAlpha);
|
||||
static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc,
|
||||
SkPDFGradientShader::Key key,
|
||||
bool keyHasAlpha);
|
||||
|
||||
static sk_sp<SkPDFDict> get_gradient_resource_dict(sk_sp<SkPDFObject> functionShader,
|
||||
sk_sp<SkPDFObject> gState) {
|
||||
std::vector<sk_sp<SkPDFObject>> patternShaders;
|
||||
if (functionShader) {
|
||||
patternShaders.push_back(std::move(functionShader));
|
||||
static sk_sp<SkPDFDict> get_gradient_resource_dict(SkPDFIndirectReference functionShader,
|
||||
SkPDFIndirectReference gState) {
|
||||
std::vector<SkPDFIndirectReference> patternShaders;
|
||||
if (functionShader != SkPDFIndirectReference()) {
|
||||
patternShaders.push_back(functionShader);
|
||||
}
|
||||
std::vector<sk_sp<SkPDFObject>> graphicStates;
|
||||
if (gState) {
|
||||
graphicStates.push_back(std::move(gState));
|
||||
std::vector<SkPDFIndirectReference> graphicStates;
|
||||
if (gState != SkPDFIndirectReference()) {
|
||||
graphicStates.push_back(gState);
|
||||
}
|
||||
return SkPDFMakeResourceDict(std::move(graphicStates),
|
||||
std::move(patternShaders),
|
||||
std::vector<sk_sp<SkPDFObject>>(),
|
||||
std::vector<SkPDFIndirectReference>(),
|
||||
std::vector<SkPDFIndirectReference>());
|
||||
}
|
||||
|
||||
// Creates a content stream which fills the pattern P0 across bounds.
|
||||
// @param gsIndex A graphics state resource index to apply, or <0 if no
|
||||
// graphics state to apply.
|
||||
static std::unique_ptr<SkStreamAsset> create_pattern_fill_content(int gsIndex, SkRect& bounds) {
|
||||
static std::unique_ptr<SkStreamAsset> create_pattern_fill_content(int gsIndex,
|
||||
int patternIndex,
|
||||
SkRect& bounds) {
|
||||
SkDynamicMemoryWStream content;
|
||||
if (gsIndex >= 0) {
|
||||
SkPDFUtils::ApplyGraphicState(gsIndex, &content);
|
||||
}
|
||||
SkPDFUtils::ApplyPattern(0, &content);
|
||||
SkPDFUtils::ApplyPattern(patternIndex, &content);
|
||||
SkPDFUtils::AppendRectangle(bounds, &content);
|
||||
SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType, &content);
|
||||
return content.detachAsStream();
|
||||
@ -818,7 +817,7 @@ static SkPDFGradientShader::Key clone_key(const SkPDFGradientShader::Key& k) {
|
||||
return clone;
|
||||
}
|
||||
|
||||
static sk_sp<SkPDFObject> create_smask_graphic_state(SkPDFDocument* doc,
|
||||
static SkPDFIndirectReference create_smask_graphic_state(SkPDFDocument* doc,
|
||||
const SkPDFGradientShader::Key& state) {
|
||||
SkASSERT(state.fType != SkShader::kNone_GradientType);
|
||||
SkPDFGradientShader::Key luminosityState = clone_key(state);
|
||||
@ -829,21 +828,23 @@ static sk_sp<SkPDFObject> create_smask_graphic_state(SkPDFDocument* doc,
|
||||
luminosityState.fHash = hash(luminosityState);
|
||||
|
||||
SkASSERT(!gradient_has_alpha(luminosityState));
|
||||
sk_sp<SkPDFObject> luminosityShader = find_pdf_shader(doc, std::move(luminosityState), false);
|
||||
sk_sp<SkPDFDict> resources = get_gradient_resource_dict(std::move(luminosityShader), nullptr);
|
||||
SkPDFIndirectReference luminosityShader = find_pdf_shader(doc, std::move(luminosityState), false);
|
||||
sk_sp<SkPDFDict> resources = get_gradient_resource_dict(luminosityShader,
|
||||
SkPDFIndirectReference());
|
||||
SkRect bbox = SkRect::Make(state.fBBox);
|
||||
sk_sp<SkPDFObject> alphaMask = SkPDFMakeFormXObject(create_pattern_fill_content(-1, bbox),
|
||||
SkPDFUtils::RectToArray(bbox),
|
||||
std::move(resources),
|
||||
SkMatrix::I(),
|
||||
"DeviceRGB");
|
||||
SkPDFIndirectReference alphaMask =
|
||||
SkPDFMakeFormXObject(doc,
|
||||
create_pattern_fill_content(-1, luminosityShader.fValue, bbox),
|
||||
SkPDFUtils::RectToArray(bbox),
|
||||
std::move(resources),
|
||||
SkMatrix::I(),
|
||||
"DeviceRGB");
|
||||
return SkPDFGraphicState::GetSMaskGraphicState(
|
||||
std::move(alphaMask), false,
|
||||
SkPDFGraphicState::kLuminosity_SMaskMode, doc->canon());
|
||||
alphaMask, false, SkPDFGraphicState::kLuminosity_SMaskMode, doc);
|
||||
}
|
||||
|
||||
static sk_sp<SkPDFStream> make_alpha_function_shader(SkPDFDocument* doc,
|
||||
const SkPDFGradientShader::Key& state) {
|
||||
static SkPDFIndirectReference make_alpha_function_shader(SkPDFDocument* doc,
|
||||
const SkPDFGradientShader::Key& state) {
|
||||
SkASSERT(state.fType != SkShader::kNone_GradientType);
|
||||
SkPDFGradientShader::Key opaqueState = clone_key(state);
|
||||
for (int i = 0; i < opaqueState.fInfo.fColorCount; i++) {
|
||||
@ -853,24 +854,22 @@ static sk_sp<SkPDFStream> make_alpha_function_shader(SkPDFDocument* doc,
|
||||
|
||||
SkASSERT(!gradient_has_alpha(opaqueState));
|
||||
SkRect bbox = SkRect::Make(state.fBBox);
|
||||
sk_sp<SkPDFObject> colorShader = find_pdf_shader(doc, std::move(opaqueState), false);
|
||||
SkPDFIndirectReference colorShader = find_pdf_shader(doc, std::move(opaqueState), false);
|
||||
if (!colorShader) {
|
||||
return nullptr;
|
||||
return SkPDFIndirectReference();
|
||||
}
|
||||
|
||||
// Create resource dict with alpha graphics state as G0 and
|
||||
// pattern shader as P0, then write content stream.
|
||||
sk_sp<SkPDFObject> alphaGs = create_smask_graphic_state(doc, state);
|
||||
SkPDFIndirectReference alphaGsRef = create_smask_graphic_state(doc, state);
|
||||
|
||||
sk_sp<SkPDFDict> resourceDict =
|
||||
get_gradient_resource_dict(std::move(colorShader), std::move(alphaGs));
|
||||
sk_sp<SkPDFDict> resourceDict = get_gradient_resource_dict(colorShader, alphaGsRef);
|
||||
|
||||
std::unique_ptr<SkStreamAsset> colorStream(create_pattern_fill_content(0, bbox));
|
||||
auto alphaFunctionShader = sk_make_sp<SkPDFStream>(std::move(colorStream));
|
||||
|
||||
SkPDFUtils::PopulateTilingPatternDict(alphaFunctionShader->dict(), bbox,
|
||||
std::unique_ptr<SkStreamAsset> colorStream =
|
||||
create_pattern_fill_content(alphaGsRef.fValue, colorShader.fValue, bbox);
|
||||
sk_sp<SkPDFDict> alphaFunctionShader = sk_make_sp<SkPDFDict>();
|
||||
SkPDFUtils::PopulateTilingPatternDict(alphaFunctionShader.get(), bbox,
|
||||
std::move(resourceDict), SkMatrix::I());
|
||||
return alphaFunctionShader;
|
||||
return SkPDFStreamOut(std::move(alphaFunctionShader), std::move(colorStream), doc);
|
||||
}
|
||||
|
||||
static SkPDFGradientShader::Key make_key(const SkShader* shader,
|
||||
@ -896,25 +895,25 @@ static SkPDFGradientShader::Key make_key(const SkShader* shader,
|
||||
return key;
|
||||
}
|
||||
|
||||
static sk_sp<SkPDFObject> find_pdf_shader(SkPDFDocument* doc,
|
||||
SkPDFGradientShader::Key key,
|
||||
bool keyHasAlpha) {
|
||||
static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc,
|
||||
SkPDFGradientShader::Key key,
|
||||
bool keyHasAlpha) {
|
||||
SkASSERT(gradient_has_alpha(key) == keyHasAlpha);
|
||||
SkPDFCanon* canon = doc->canon();
|
||||
if (sk_sp<SkPDFObject>* ptr = canon->fGradientPatternMap.find(key)) {
|
||||
auto& gradientPatternMap = doc->canon()->fGradientPatternMap;
|
||||
if (SkPDFIndirectReference* ptr = gradientPatternMap.find(key)) {
|
||||
return *ptr;
|
||||
}
|
||||
sk_sp<SkPDFObject> pdfShader;
|
||||
SkPDFIndirectReference pdfShader;
|
||||
if (keyHasAlpha) {
|
||||
pdfShader = make_alpha_function_shader(doc, key);
|
||||
} else {
|
||||
pdfShader = make_function_shader(canon, key);
|
||||
pdfShader = make_function_shader(doc, key);
|
||||
}
|
||||
canon->fGradientPatternMap.set(std::move(key), pdfShader);
|
||||
gradientPatternMap.set(std::move(key), pdfShader);
|
||||
return pdfShader;
|
||||
}
|
||||
|
||||
sk_sp<SkPDFObject> SkPDFGradientShader::Make(SkPDFDocument* doc,
|
||||
SkPDFIndirectReference SkPDFGradientShader::Make(SkPDFDocument* doc,
|
||||
SkShader* shader,
|
||||
const SkMatrix& canvasTransform,
|
||||
const SkIRect& bbox) {
|
||||
|
@ -17,10 +17,10 @@ struct SkIRect;
|
||||
|
||||
namespace SkPDFGradientShader {
|
||||
|
||||
sk_sp<SkPDFObject> Make(SkPDFDocument* doc,
|
||||
SkShader* shader,
|
||||
const SkMatrix& matrix,
|
||||
const SkIRect& surfaceBBox);
|
||||
SkPDFIndirectReference Make(SkPDFDocument* doc,
|
||||
SkShader* shader,
|
||||
const SkMatrix& matrix,
|
||||
const SkIRect& surfaceBBox);
|
||||
|
||||
struct Key {
|
||||
SkShader::GradientType fType;
|
||||
@ -37,7 +37,7 @@ struct KeyHash {
|
||||
uint32_t operator()(const Key& k) const { return k.fHash; }
|
||||
};
|
||||
|
||||
using HashMap = SkTHashMap<Key, sk_sp<SkPDFObject>, KeyHash>;
|
||||
using HashMap = SkTHashMap<Key, SkPDFIndirectReference, KeyHash>;
|
||||
|
||||
inline bool operator==(const SkShader::GradientInfo& u, const SkShader::GradientInfo& v) {
|
||||
return u.fColorCount == v.fColorCount
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "SkData.h"
|
||||
#include "SkPDFCanon.h"
|
||||
#include "SkPDFDocumentPriv.h"
|
||||
#include "SkPDFFormXObject.h"
|
||||
#include "SkPDFUtils.h"
|
||||
#include "SkPaint.h"
|
||||
@ -52,21 +53,23 @@ static uint8_t pdf_blend_mode(SkBlendMode mode) {
|
||||
return SkToU8((unsigned)mode);
|
||||
}
|
||||
|
||||
sk_sp<SkPDFDict> SkPDFGraphicState::GetGraphicStateForPaint(SkPDFCanon* canon,
|
||||
const SkPaint& p) {
|
||||
SkPDFIndirectReference SkPDFGraphicState::GetGraphicStateForPaint(SkPDFDocument* doc,
|
||||
const SkPaint& p) {
|
||||
SkPDFCanon* canon = doc->canon();
|
||||
SkASSERT(canon);
|
||||
if (SkPaint::kFill_Style == p.getStyle()) {
|
||||
SkPDFFillGraphicState fillKey = {p.getColor4f().fA, pdf_blend_mode(p.getBlendMode())};
|
||||
auto& fillMap = canon->fFillGSMap;
|
||||
if (sk_sp<SkPDFDict>* statePtr = fillMap.find(fillKey)) {
|
||||
if (SkPDFIndirectReference* statePtr = fillMap.find(fillKey)) {
|
||||
return *statePtr;
|
||||
}
|
||||
auto state = sk_make_sp<SkPDFDict>();
|
||||
state->reserve(2);
|
||||
state->insertColorComponentF("ca", fillKey.fAlpha);
|
||||
state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode));
|
||||
fillMap.set(fillKey, state);
|
||||
return state;
|
||||
SkPDFDict state;
|
||||
state.reserve(2);
|
||||
state.insertColorComponentF("ca", fillKey.fAlpha);
|
||||
state.insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode));
|
||||
SkPDFIndirectReference ref = doc->emit(state);
|
||||
fillMap.set(fillKey, ref);
|
||||
return ref;
|
||||
} else {
|
||||
SkPDFStrokeGraphicState strokeKey = {
|
||||
p.getStrokeWidth(),
|
||||
@ -77,65 +80,63 @@ sk_sp<SkPDFDict> SkPDFGraphicState::GetGraphicStateForPaint(SkPDFCanon* canon,
|
||||
pdf_blend_mode(p.getBlendMode())
|
||||
};
|
||||
auto& sMap = canon->fStrokeGSMap;
|
||||
if (sk_sp<SkPDFDict>* statePtr = sMap.find(strokeKey)) {
|
||||
if (SkPDFIndirectReference* statePtr = sMap.find(strokeKey)) {
|
||||
return *statePtr;
|
||||
}
|
||||
auto state = sk_make_sp<SkPDFDict>();
|
||||
state->reserve(8);
|
||||
state->insertColorComponentF("CA", strokeKey.fAlpha);
|
||||
state->insertColorComponentF("ca", strokeKey.fAlpha);
|
||||
state->insertInt("LC", to_stroke_cap(strokeKey.fStrokeCap));
|
||||
state->insertInt("LJ", to_stroke_join(strokeKey.fStrokeJoin));
|
||||
state->insertScalar("LW", strokeKey.fStrokeWidth);
|
||||
state->insertScalar("ML", strokeKey.fStrokeMiter);
|
||||
state->insertBool("SA", true); // SA = Auto stroke adjustment.
|
||||
state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)strokeKey.fBlendMode));
|
||||
sMap.set(strokeKey, state);
|
||||
return state;
|
||||
SkPDFDict state;
|
||||
state.reserve(8);
|
||||
state.insertColorComponentF("CA", strokeKey.fAlpha);
|
||||
state.insertColorComponentF("ca", strokeKey.fAlpha);
|
||||
state.insertInt("LC", to_stroke_cap(strokeKey.fStrokeCap));
|
||||
state.insertInt("LJ", to_stroke_join(strokeKey.fStrokeJoin));
|
||||
state.insertScalar("LW", strokeKey.fStrokeWidth);
|
||||
state.insertScalar("ML", strokeKey.fStrokeMiter);
|
||||
state.insertBool("SA", true); // SA = Auto stroke adjustment.
|
||||
state.insertName("BM", as_pdf_blend_mode_name((SkBlendMode)strokeKey.fBlendMode));
|
||||
SkPDFIndirectReference ref = doc->emit(state);
|
||||
sMap.set(strokeKey, ref);
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static sk_sp<SkPDFStream> make_invert_function() {
|
||||
static SkPDFIndirectReference make_invert_function(SkPDFDocument* doc) {
|
||||
// Acrobat crashes if we use a type 0 function, kpdf crashes if we use
|
||||
// a type 2 function, so we use a type 4 function.
|
||||
auto domainAndRange = SkPDFMakeArray(0, 1);
|
||||
|
||||
static const char psInvert[] = "{1 exch sub}";
|
||||
// Do not copy the trailing '\0' into the SkData.
|
||||
auto invertFunction = sk_make_sp<SkPDFStream>(
|
||||
SkData::MakeWithoutCopy(psInvert, strlen(psInvert)));
|
||||
invertFunction->dict()->insertInt("FunctionType", 4);
|
||||
invertFunction->dict()->insertObject("Domain", domainAndRange);
|
||||
invertFunction->dict()->insertObject("Range", std::move(domainAndRange));
|
||||
return invertFunction;
|
||||
auto invertFunction = SkData::MakeWithoutCopy(psInvert, strlen(psInvert));
|
||||
|
||||
sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
|
||||
dict->insertInt("FunctionType", 4);
|
||||
dict->insertObject("Domain", SkPDFMakeArray(0, 1));
|
||||
dict->insertObject("Range", SkPDFMakeArray(0, 1));
|
||||
return SkPDFStreamOut(std::move(dict), SkMemoryStream::Make(std::move(invertFunction)), doc);
|
||||
}
|
||||
|
||||
sk_sp<SkPDFDict> SkPDFGraphicState::GetSMaskGraphicState(
|
||||
sk_sp<SkPDFObject> sMask,
|
||||
bool invert,
|
||||
SkPDFSMaskMode sMaskMode,
|
||||
SkPDFCanon* canon) {
|
||||
SkPDFIndirectReference SkPDFGraphicState::GetSMaskGraphicState(SkPDFIndirectReference sMask,
|
||||
bool invert,
|
||||
SkPDFSMaskMode sMaskMode,
|
||||
SkPDFDocument* doc) {
|
||||
// The practical chances of using the same mask more than once are unlikely
|
||||
// enough that it's not worth canonicalizing.
|
||||
SkPDFCanon* canon = doc->canon();
|
||||
auto sMaskDict = sk_make_sp<SkPDFDict>("Mask");
|
||||
if (sMaskMode == kAlpha_SMaskMode) {
|
||||
sMaskDict->insertName("S", "Alpha");
|
||||
} else if (sMaskMode == kLuminosity_SMaskMode) {
|
||||
sMaskDict->insertName("S", "Luminosity");
|
||||
}
|
||||
sMaskDict->insertObjRef("G", std::move(sMask));
|
||||
sMaskDict->insertRef("G", sMask);
|
||||
if (invert) {
|
||||
// Instead of calling SkPDFGraphicState::MakeInvertFunction,
|
||||
// let the canon deduplicate this object.
|
||||
sk_sp<SkPDFStream>& invertFunction = canon->fInvertFunction;
|
||||
if (!invertFunction) {
|
||||
invertFunction = make_invert_function();
|
||||
if (canon->fInvertFunction == SkPDFIndirectReference()) {
|
||||
canon->fInvertFunction = make_invert_function(doc);
|
||||
}
|
||||
sMaskDict->insertObjRef("TR", invertFunction);
|
||||
sMaskDict->insertRef("TR", canon->fInvertFunction);
|
||||
}
|
||||
auto result = sk_make_sp<SkPDFDict>("ExtGState");
|
||||
result->insertObject("SMask", std::move(sMaskDict));
|
||||
return result;
|
||||
SkPDFDict result("ExtGState");
|
||||
result.insertObject("SMask", std::move(sMaskDict));
|
||||
return doc->emit(result);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ namespace SkPDFGraphicState {
|
||||
|
||||
/** Get the graphic state for the passed SkPaint.
|
||||
*/
|
||||
sk_sp<SkPDFDict> GetGraphicStateForPaint(SkPDFCanon*, const SkPaint&);
|
||||
SkPDFIndirectReference GetGraphicStateForPaint(SkPDFDocument*, const SkPaint&);
|
||||
|
||||
/** Make a graphic state that only sets the passed soft mask.
|
||||
* @param sMask The form xobject to use as a soft mask.
|
||||
@ -38,12 +38,10 @@ namespace SkPDFGraphicState {
|
||||
*
|
||||
* These are not de-duped.
|
||||
*/
|
||||
sk_sp<SkPDFDict> GetSMaskGraphicState(sk_sp<SkPDFObject> sMask,
|
||||
bool invert,
|
||||
SkPDFSMaskMode sMaskMode,
|
||||
SkPDFCanon* canon);
|
||||
|
||||
sk_sp<SkPDFStream> MakeInvertFunction();
|
||||
SkPDFIndirectReference GetSMaskGraphicState(SkPDFIndirectReference sMask,
|
||||
bool invert,
|
||||
SkPDFSMaskMode sMaskMode,
|
||||
SkPDFDocument* doc);
|
||||
}
|
||||
|
||||
SK_BEGIN_REQUIRE_DENSE
|
||||
|
@ -205,7 +205,7 @@ void SkPDFAppendCmapSections(const SkUnichar* glyphToUnicode,
|
||||
append_bfrange_section(bfrangeEntries, multiByteGlyphs, cmap);
|
||||
}
|
||||
|
||||
sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
|
||||
std::unique_ptr<SkStreamAsset> SkPDFMakeToUnicodeCmap(
|
||||
const SkUnichar* glyphToUnicode,
|
||||
const SkPDFGlyphUse* subset,
|
||||
bool multiByteGlyphs,
|
||||
@ -216,6 +216,5 @@ sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
|
||||
SkPDFAppendCmapSections(glyphToUnicode, subset, &cmap, multiByteGlyphs,
|
||||
firstGlyphID, lastGlyphID);
|
||||
append_cmap_footer(&cmap);
|
||||
return sk_make_sp<SkPDFStream>(
|
||||
std::unique_ptr<SkStreamAsset>(cmap.detachAsStream()));
|
||||
return cmap.detachAsStream();
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "SkPDFFont.h"
|
||||
#include "SkStream.h"
|
||||
|
||||
sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
|
||||
std::unique_ptr<SkStreamAsset> SkPDFMakeToUnicodeCmap(
|
||||
const SkUnichar* glyphToUnicode,
|
||||
const SkPDFGlyphUse* subset,
|
||||
bool multiByteGlyphs,
|
||||
|
@ -150,8 +150,7 @@ sk_sp<SkPDFObject> SkPDFMetadata::MakeDocumentInformationDict(
|
||||
return std::move(dict);
|
||||
}
|
||||
|
||||
SkPDFMetadata::UUID SkPDFMetadata::CreateUUID(
|
||||
const SkPDF::Metadata& metadata) {
|
||||
SkUUID SkPDFMetadata::CreateUUID(const SkPDF::Metadata& metadata) {
|
||||
// The main requirement is for the UUID to be unique; the exact
|
||||
// format of the data that will be hashed is not important.
|
||||
SkMD5 md5;
|
||||
@ -177,22 +176,22 @@ SkPDFMetadata::UUID SkPDFMetadata::CreateUUID(
|
||||
// See RFC 4122, page 6-7.
|
||||
digest.data[6] = (digest.data[6] & 0x0F) | 0x30;
|
||||
digest.data[8] = (digest.data[6] & 0x3F) | 0x80;
|
||||
static_assert(sizeof(digest) == sizeof(UUID), "uuid_size");
|
||||
SkPDFMetadata::UUID uuid;
|
||||
static_assert(sizeof(digest) == sizeof(SkUUID), "uuid_size");
|
||||
SkUUID uuid;
|
||||
memcpy(&uuid, &digest, sizeof(digest));
|
||||
return uuid;
|
||||
}
|
||||
|
||||
sk_sp<SkPDFObject> SkPDFMetadata::MakePdfId(const UUID& doc,
|
||||
const UUID& instance) {
|
||||
sk_sp<SkPDFObject> SkPDFMetadata::MakePdfId(const SkUUID& doc,
|
||||
const SkUUID& instance) {
|
||||
// /ID [ <81b14aafa313db63dbd6f981e49f94f4>
|
||||
// <81b14aafa313db63dbd6f981e49f94f4> ]
|
||||
auto array = sk_make_sp<SkPDFArray>();
|
||||
static_assert(sizeof(SkPDFMetadata::UUID) == 16, "uuid_size");
|
||||
static_assert(sizeof(SkUUID) == 16, "uuid_size");
|
||||
array->appendString(
|
||||
SkString(reinterpret_cast<const char*>(&doc), sizeof(UUID)));
|
||||
SkString(reinterpret_cast<const char*>(&doc), sizeof(SkUUID)));
|
||||
array->appendString(
|
||||
SkString(reinterpret_cast<const char*>(&instance), sizeof(UUID)));
|
||||
SkString(reinterpret_cast<const char*>(&instance), sizeof(SkUUID)));
|
||||
return std::move(array);
|
||||
}
|
||||
|
||||
@ -208,7 +207,7 @@ static void hexify(const uint8_t** inputPtr, char** outputPtr, int count) {
|
||||
}
|
||||
}
|
||||
|
||||
static SkString uuid_to_string(const SkPDFMetadata::UUID& uuid) {
|
||||
static SkString uuid_to_string(const SkUUID& uuid) {
|
||||
// 8-4-4-4-12
|
||||
char buffer[36]; // [32 + 4]
|
||||
char* ptr = buffer;
|
||||
@ -306,8 +305,8 @@ const SkString escape_xml(const SkString& input,
|
||||
|
||||
sk_sp<SkPDFObject> SkPDFMetadata::MakeXMPObject(
|
||||
const SkPDF::Metadata& metadata,
|
||||
const UUID& doc,
|
||||
const UUID& instance) {
|
||||
const SkUUID& doc,
|
||||
const SkUUID& instance) {
|
||||
static const char templateString[] =
|
||||
"<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n"
|
||||
"<x:xmpmeta xmlns:x=\"adobe:ns:meta/\"\n"
|
||||
|
@ -9,22 +9,19 @@
|
||||
#define SkPDFMetadata_DEFINED
|
||||
|
||||
#include "SkPDFDocument.h"
|
||||
#include "SkUUID.h"
|
||||
|
||||
class SkPDFObject;
|
||||
|
||||
namespace SkPDFMetadata {
|
||||
sk_sp<SkPDFObject> MakeDocumentInformationDict(const SkPDF::Metadata&);
|
||||
|
||||
struct UUID {
|
||||
uint8_t fData[16];
|
||||
};
|
||||
SkUUID CreateUUID(const SkPDF::Metadata&);
|
||||
|
||||
UUID CreateUUID(const SkPDF::Metadata&);
|
||||
|
||||
sk_sp<SkPDFObject> MakePdfId(const UUID& doc, const UUID& instance);
|
||||
sk_sp<SkPDFObject> MakePdfId(const SkUUID& doc, const SkUUID& instance);
|
||||
|
||||
sk_sp<SkPDFObject> MakeXMPObject(const SkPDF::Metadata&,
|
||||
const UUID& doc,
|
||||
const UUID& instance);
|
||||
const SkUUID& doc,
|
||||
const SkUUID& instance);
|
||||
}
|
||||
#endif // SkPDFMetadata_DEFINED
|
||||
|
@ -59,18 +59,6 @@ static SkString resource(SkPDFResourceType type, int index) {
|
||||
return SkString(buffer, (size_t)(end - buffer));
|
||||
}
|
||||
|
||||
static void add_subdict(std::vector<sk_sp<SkPDFObject>> resourceList,
|
||||
SkPDFResourceType type,
|
||||
SkPDFDict* dst) {
|
||||
if (!resourceList.empty()) {
|
||||
auto resources = sk_make_sp<SkPDFDict>();
|
||||
for (size_t i = 0; i < resourceList.size(); i++) {
|
||||
resources->insertObjRef(resource(type, SkToInt(i)), std::move(resourceList[i]));
|
||||
}
|
||||
dst->insertObject(resource_name(type), std::move(resources));
|
||||
}
|
||||
}
|
||||
|
||||
static void add_subdict(const std::vector<SkPDFIndirectReference>& resourceList,
|
||||
SkPDFResourceType type,
|
||||
SkPDFDict* dst) {
|
||||
@ -83,14 +71,14 @@ static void add_subdict(const std::vector<SkPDFIndirectReference>& resourceList,
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<SkPDFDict> SkPDFMakeResourceDict(std::vector<sk_sp<SkPDFObject>> graphicStateResources,
|
||||
std::vector<sk_sp<SkPDFObject>> shaderResources,
|
||||
std::vector<sk_sp<SkPDFObject>> xObjectResources,
|
||||
sk_sp<SkPDFDict> SkPDFMakeResourceDict(std::vector<SkPDFIndirectReference> graphicStateResources,
|
||||
std::vector<SkPDFIndirectReference> shaderResources,
|
||||
std::vector<SkPDFIndirectReference> xObjectResources,
|
||||
std::vector<SkPDFIndirectReference> fontResources) {
|
||||
auto dict = sk_make_sp<SkPDFDict>();
|
||||
add_subdict(std::move(graphicStateResources), SkPDFResourceType::kExtGState, dict.get());
|
||||
add_subdict(std::move(shaderResources), SkPDFResourceType::kPattern, dict.get());
|
||||
add_subdict(std::move(xObjectResources), SkPDFResourceType::kXObject, dict.get());
|
||||
add_subdict(fontResources, SkPDFResourceType::kFont, dict.get());
|
||||
add_subdict(graphicStateResources, SkPDFResourceType::kExtGState, dict.get());
|
||||
add_subdict(shaderResources, SkPDFResourceType::kPattern, dict.get());
|
||||
add_subdict(xObjectResources, SkPDFResourceType::kXObject, dict.get());
|
||||
add_subdict(fontResources, SkPDFResourceType::kFont, dict.get());
|
||||
return dict;
|
||||
}
|
||||
|
@ -32,9 +32,9 @@ enum class SkPDFResourceType {
|
||||
*
|
||||
* Any arguments can be nullptr.
|
||||
*/
|
||||
sk_sp<SkPDFDict> SkPDFMakeResourceDict(std::vector<sk_sp<SkPDFObject>> graphicStateResources,
|
||||
std::vector<sk_sp<SkPDFObject>> shaderResources,
|
||||
std::vector<sk_sp<SkPDFObject>> xObjectResources,
|
||||
sk_sp<SkPDFDict> SkPDFMakeResourceDict(std::vector<SkPDFIndirectReference> graphicStateResources,
|
||||
std::vector<SkPDFIndirectReference> shaderResources,
|
||||
std::vector<SkPDFIndirectReference> xObjectResources,
|
||||
std::vector<SkPDFIndirectReference> fontResources);
|
||||
|
||||
/**
|
||||
|
@ -37,9 +37,9 @@ static void draw_bitmap_matrix(SkCanvas* canvas, const SkBitmap& bm,
|
||||
canvas->drawBitmap(bm, 0, 0, &paint);
|
||||
}
|
||||
|
||||
static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
|
||||
const SkPDFImageShaderKey& key,
|
||||
SkImage* image) {
|
||||
static SkPDFIndirectReference make_image_shader(SkPDFDocument* doc,
|
||||
const SkPDFImageShaderKey& key,
|
||||
SkImage* image) {
|
||||
SkASSERT(image);
|
||||
|
||||
// The image shader pattern cell will be drawn into a separate device
|
||||
@ -52,7 +52,7 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
|
||||
finalMatrix.preConcat(key.fShaderTransform);
|
||||
SkRect deviceBounds = SkRect::Make(key.fBBox);
|
||||
if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &deviceBounds)) {
|
||||
return nullptr;
|
||||
return SkPDFIndirectReference();
|
||||
}
|
||||
|
||||
SkRect bitmapBounds = SkRect::Make(image->bounds());
|
||||
@ -244,22 +244,23 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
|
||||
}
|
||||
}
|
||||
|
||||
auto imageShader = sk_make_sp<SkPDFStream>(patternDevice->content());
|
||||
auto imageShader = patternDevice->content();
|
||||
sk_sp<SkPDFDict> resourceDict = patternDevice->makeResourceDict();
|
||||
SkPDFUtils::PopulateTilingPatternDict(imageShader->dict(), patternBBox,
|
||||
sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
|
||||
SkPDFUtils::PopulateTilingPatternDict(dict.get(), patternBBox,
|
||||
std::move(resourceDict), finalMatrix);
|
||||
return imageShader;
|
||||
return SkPDFStreamOut(std::move(dict), std::move(imageShader), doc);
|
||||
}
|
||||
|
||||
// Generic fallback for unsupported shaders:
|
||||
// * allocate a surfaceBBox-sized bitmap
|
||||
// * shade the whole area
|
||||
// * use the result as a bitmap shader
|
||||
static sk_sp<SkPDFObject> make_fallback_shader(SkPDFDocument* doc,
|
||||
SkShader* shader,
|
||||
const SkMatrix& canvasTransform,
|
||||
const SkIRect& surfaceBBox,
|
||||
SkColor paintColor) {
|
||||
static SkPDFIndirectReference make_fallback_shader(SkPDFDocument* doc,
|
||||
SkShader* shader,
|
||||
const SkMatrix& canvasTransform,
|
||||
const SkIRect& surfaceBBox,
|
||||
SkColor paintColor) {
|
||||
// TODO(vandebo) This drops SKComposeShader on the floor. We could
|
||||
// handle compose shader by pulling things up to a layer, drawing with
|
||||
// the first shader, applying the xfer mode and drawing again with the
|
||||
@ -280,7 +281,7 @@ static sk_sp<SkPDFObject> make_fallback_shader(SkPDFDocument* doc,
|
||||
// MakeImageShader's behavior).
|
||||
SkRect shaderRect = SkRect::Make(surfaceBBox);
|
||||
if (!SkPDFUtils::InverseTransformBBox(canvasTransform, &shaderRect)) {
|
||||
return nullptr;
|
||||
return SkPDFIndirectReference();
|
||||
}
|
||||
// Clamp the bitmap size to about 1M pixels
|
||||
static const SkScalar kMaxBitmapArea = 1024 * 1024;
|
||||
@ -324,18 +325,18 @@ static SkColor adjust_color(SkShader* shader, SkColor paintColor) {
|
||||
return paintColor & SK_ColorBLACK;
|
||||
}
|
||||
|
||||
sk_sp<SkPDFObject> SkPDFMakeShader(SkPDFDocument* doc,
|
||||
SkShader* shader,
|
||||
const SkMatrix& canvasTransform,
|
||||
const SkIRect& surfaceBBox,
|
||||
SkColor paintColor) {
|
||||
SkPDFIndirectReference SkPDFMakeShader(SkPDFDocument* doc,
|
||||
SkShader* shader,
|
||||
const SkMatrix& canvasTransform,
|
||||
const SkIRect& surfaceBBox,
|
||||
SkColor paintColor) {
|
||||
SkASSERT(shader);
|
||||
SkASSERT(doc);
|
||||
if (SkShader::kNone_GradientType != shader->asAGradient(nullptr)) {
|
||||
return SkPDFGradientShader::Make(doc, shader, canvasTransform, surfaceBBox);
|
||||
}
|
||||
if (surfaceBBox.isEmpty()) {
|
||||
return nullptr;
|
||||
return SkPDFIndirectReference();
|
||||
}
|
||||
SkBitmap image;
|
||||
SkPDFImageShaderKey key = {
|
||||
@ -350,11 +351,11 @@ sk_sp<SkPDFObject> SkPDFMakeShader(SkPDFDocument* doc,
|
||||
if (SkImage* skimg = shader->isAImage(&key.fShaderTransform, key.fImageTileModes)) {
|
||||
key.fBitmapKey = SkBitmapKeyFromImage(skimg);
|
||||
SkPDFCanon* canon = doc->canon();
|
||||
sk_sp<SkPDFObject>* shaderPtr = canon->fImageShaderMap.find(key);
|
||||
SkPDFIndirectReference* shaderPtr = canon->fImageShaderMap.find(key);
|
||||
if (shaderPtr) {
|
||||
return *shaderPtr;
|
||||
}
|
||||
sk_sp<SkPDFObject> pdfShader = make_image_shader(doc, key, skimg);
|
||||
SkPDFIndirectReference pdfShader = make_image_shader(doc, key, skimg);
|
||||
canon->fImageShaderMap.set(std::move(key), pdfShader);
|
||||
return pdfShader;
|
||||
}
|
||||
|
@ -38,11 +38,11 @@ struct SkIRect;
|
||||
* @param paintColor Color+Alpha of the paint. Color is usually ignored,
|
||||
* unless it is a alpha shader.
|
||||
*/
|
||||
sk_sp<SkPDFObject> SkPDFMakeShader(SkPDFDocument* doc,
|
||||
SkShader* shader,
|
||||
const SkMatrix& ctm,
|
||||
const SkIRect& surfaceBBox,
|
||||
SkColor paintColor);
|
||||
SkPDFIndirectReference SkPDFMakeShader(SkPDFDocument* doc,
|
||||
SkShader* shader,
|
||||
const SkMatrix& ctm,
|
||||
const SkIRect& surfaceBBox,
|
||||
SkColor paintColor);
|
||||
|
||||
SK_BEGIN_REQUIRE_DENSE
|
||||
struct SkPDFImageShaderKey {
|
||||
|
@ -9,7 +9,9 @@
|
||||
|
||||
#include "SkData.h"
|
||||
#include "SkDeflate.h"
|
||||
#include "SkExecutor.h"
|
||||
#include "SkMakeUnique.h"
|
||||
#include "SkPDFDocumentPriv.h"
|
||||
#include "SkPDFUtils.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkStreamPriv.h"
|
||||
@ -203,6 +205,7 @@ void SkPDFUnion::emitObject(SkWStream* stream) const {
|
||||
fObject->emitObject(stream);
|
||||
return;
|
||||
case Type::kRef:
|
||||
SkASSERT(fIntValue >= 0);
|
||||
stream->writeDecAsText(fIntValue);
|
||||
stream->writeText(" 0 R"); // Generation number is always 0.
|
||||
return;
|
||||
@ -496,143 +499,71 @@ void SkPDFDict::insertString(const char key[], SkString value) {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkPDFSharedStream::SkPDFSharedStream(std::unique_ptr<SkStreamAsset> data)
|
||||
: fAsset(std::move(data)) {
|
||||
SkASSERT(fAsset);
|
||||
}
|
||||
|
||||
SkPDFSharedStream::~SkPDFSharedStream() { this->drop(); }
|
||||
|
||||
void SkPDFSharedStream::drop() {
|
||||
fAsset = nullptr;
|
||||
fDict.drop();
|
||||
}
|
||||
|
||||
#ifdef SK_PDF_LESS_COMPRESSION
|
||||
void SkPDFSharedStream::emitObject(SkWStream* stream) const {
|
||||
SkASSERT(fAsset);
|
||||
std::unique_ptr<SkStreamAsset> dup(fAsset->duplicate());
|
||||
SkASSERT(dup && dup->hasLength());
|
||||
size_t length = dup->getLength();
|
||||
stream->writeText("<<");
|
||||
fDict.emitAll(stream);
|
||||
stream->writeText("\n");
|
||||
SkPDFUnion::Name("Length").emitObject(stream);
|
||||
stream->writeText(" ");
|
||||
SkPDFUnion::Int(length).emitObject(stream);
|
||||
stream->writeText("\n>>stream\n");
|
||||
SkStreamCopy(stream, dup.get());
|
||||
stream->writeText("\nendstream");
|
||||
}
|
||||
#else
|
||||
void SkPDFSharedStream::emitObject(SkWStream* stream) const {
|
||||
SkASSERT(fAsset);
|
||||
SkDynamicMemoryWStream buffer;
|
||||
SkDeflateWStream deflateWStream(&buffer);
|
||||
// Since emitObject is const, this function doesn't change the dictionary.
|
||||
std::unique_ptr<SkStreamAsset> dup(fAsset->duplicate()); // Cheap copy
|
||||
SkASSERT(dup);
|
||||
SkStreamCopy(&deflateWStream, dup.get());
|
||||
deflateWStream.finalize();
|
||||
size_t length = buffer.bytesWritten();
|
||||
stream->writeText("<<");
|
||||
fDict.emitAll(stream);
|
||||
stream->writeText("\n");
|
||||
SkPDFUnion::Name("Length").emitObject(stream);
|
||||
stream->writeText(" ");
|
||||
SkPDFUnion::Int(length).emitObject(stream);
|
||||
stream->writeText("\n");
|
||||
SkPDFUnion::Name("Filter").emitObject(stream);
|
||||
stream->writeText(" ");
|
||||
SkPDFUnion::Name("FlateDecode").emitObject(stream);
|
||||
stream->writeText(">>");
|
||||
stream->writeText(" stream\n");
|
||||
buffer.writeToAndReset(stream);
|
||||
stream->writeText("\nendstream");
|
||||
}
|
||||
#endif
|
||||
|
||||
void SkPDFSharedStream::addResources(
|
||||
SkPDFObjNumMap* catalog) const {
|
||||
SkASSERT(fAsset);
|
||||
fDict.addResources(catalog);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkPDFStream::SkPDFStream(sk_sp<SkData> data) {
|
||||
this->setData(skstd::make_unique<SkMemoryStream>(std::move(data)));
|
||||
}
|
||||
|
||||
SkPDFStream::SkPDFStream(std::unique_ptr<SkStreamAsset> stream) {
|
||||
this->setData(std::move(stream));
|
||||
}
|
||||
|
||||
SkPDFStream::SkPDFStream() {}
|
||||
|
||||
SkPDFStream::~SkPDFStream() {}
|
||||
|
||||
void SkPDFStream::addResources(SkPDFObjNumMap* catalog) const {
|
||||
SkASSERT(fCompressedData);
|
||||
fDict.addResources(catalog);
|
||||
}
|
||||
|
||||
void SkPDFStream::drop() {
|
||||
fCompressedData.reset(nullptr);
|
||||
fDict.drop();
|
||||
}
|
||||
|
||||
void SkPDFStream::emitObject(SkWStream* stream) const {
|
||||
SkASSERT(fCompressedData);
|
||||
fDict.emitObject(stream);
|
||||
// duplicate (a cheap operation) preserves const on fCompressedData.
|
||||
std::unique_ptr<SkStreamAsset> dup(fCompressedData->duplicate());
|
||||
SkASSERT(dup);
|
||||
SkASSERT(dup->hasLength());
|
||||
stream->writeText(" stream\n");
|
||||
stream->writeStream(dup.get(), dup->getLength());
|
||||
stream->writeText("\nendstream");
|
||||
}
|
||||
|
||||
void SkPDFStream::setData(std::unique_ptr<SkStreamAsset> stream) {
|
||||
SkASSERT(!fCompressedData); // Only call this function once.
|
||||
SkASSERT(stream);
|
||||
static void serialize_stream(const SkPDFDict* origDict,
|
||||
SkStreamAsset* stream,
|
||||
bool deflate,
|
||||
SkPDFDocument* doc,
|
||||
SkPDFIndirectReference ref) {
|
||||
// Code assumes that the stream starts at the beginning.
|
||||
SkASSERT(stream && stream->hasLength());
|
||||
|
||||
#ifdef SK_PDF_LESS_COMPRESSION
|
||||
fCompressedData = std::move(stream);
|
||||
SkASSERT(fCompressedData && fCompressedData->hasLength());
|
||||
fDict.insertInt("Length", fCompressedData->getLength());
|
||||
#else
|
||||
|
||||
SkASSERT(stream->hasLength());
|
||||
SkDynamicMemoryWStream compressedData;
|
||||
SkDeflateWStream deflateWStream(&compressedData);
|
||||
if (stream->getLength() > 0) {
|
||||
SkStreamCopy(&deflateWStream, stream.get());
|
||||
std::unique_ptr<SkStreamAsset> tmp;
|
||||
SkPDFDict dict;
|
||||
static const size_t kMinimumSavings = strlen("/Filter_/FlateDecode_");
|
||||
if (deflate && stream->getLength() > kMinimumSavings) {
|
||||
SkDynamicMemoryWStream compressedData;
|
||||
SkDeflateWStream deflateWStream(&compressedData);
|
||||
SkStreamCopy(&deflateWStream, stream);
|
||||
deflateWStream.finalize();
|
||||
if (stream->getLength() > compressedData.bytesWritten() + kMinimumSavings) {
|
||||
tmp = compressedData.detachAsStream();
|
||||
stream = tmp.get();
|
||||
dict.insertName("Filter", "FlateDecode");
|
||||
} else {
|
||||
SkAssertResult(stream->rewind());
|
||||
}
|
||||
}
|
||||
deflateWStream.finalize();
|
||||
size_t compressedLength = compressedData.bytesWritten();
|
||||
size_t originalLength = stream->getLength();
|
||||
dict.insertInt("Length", stream->getLength());
|
||||
|
||||
if (originalLength <= compressedLength + strlen("/Filter_/FlateDecode_")) {
|
||||
SkAssertResult(stream->rewind());
|
||||
fCompressedData = std::move(stream);
|
||||
fDict.insertInt("Length", originalLength);
|
||||
return;
|
||||
SkWStream* dst = doc->beginObject(ref);
|
||||
dst->writeText("<<");
|
||||
if (origDict) {
|
||||
origDict->emitAll(dst);
|
||||
}
|
||||
fCompressedData = compressedData.detachAsStream();
|
||||
fDict.insertName("Filter", "FlateDecode");
|
||||
fDict.insertInt("Length", compressedLength);
|
||||
#endif
|
||||
dict.emitAll(dst);
|
||||
dst->writeText(">> stream\n");
|
||||
dst->writeStream(stream, stream->getLength());
|
||||
dst->writeText("\nendstream");
|
||||
doc->endObject();
|
||||
}
|
||||
|
||||
SkPDFIndirectReference SkPDFStreamOut(sk_sp<SkPDFDict> dict,
|
||||
std::unique_ptr<SkStreamAsset> content,
|
||||
SkPDFDocument* doc,
|
||||
bool deflate) {
|
||||
SkPDFIndirectReference ref = doc->reserveRef();
|
||||
if (SkExecutor* executor = doc->executor()) {
|
||||
SkPDFDict* dictPtr = dict.release();
|
||||
SkStreamAsset* contentPtr = content.release();
|
||||
// Pass ownership of both pointers into a std::function, which should
|
||||
// only be executed once.
|
||||
executor->add([dictPtr, contentPtr, deflate, doc, ref]() {
|
||||
serialize_stream(dictPtr, contentPtr, deflate, doc, ref);
|
||||
SkSafeUnref(dictPtr);
|
||||
delete contentPtr;
|
||||
});
|
||||
return ref;
|
||||
}
|
||||
serialize_stream(dict.get(), content.get(), deflate, doc, ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SkPDFObjNumMap::addObjectRecursively(SkPDFObject* obj) {
|
||||
SkASSERT(fIndirectReferenceSource != nullptr);
|
||||
if (obj && obj->fIndirectReference.fValue == -1) {
|
||||
obj->fIndirectReference.fValue = fNextObjectNumber++;
|
||||
obj->fIndirectReference = fIndirectReferenceSource->reserve();
|
||||
fObjects.emplace_back(sk_ref_sp(obj));
|
||||
obj->addResources(this);
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ class SkPDFObject;
|
||||
class SkStreamAsset;
|
||||
class SkString;
|
||||
class SkWStream;
|
||||
struct SkPDFObjectSerializer;
|
||||
|
||||
#ifdef SK_PDF_IMAGE_STATS
|
||||
#include <atomic>
|
||||
@ -34,6 +35,7 @@ class SkWStream;
|
||||
|
||||
struct SkPDFIndirectReference {
|
||||
int fValue = -1;
|
||||
explicit operator bool() { return fValue != -1; }
|
||||
};
|
||||
|
||||
inline static bool operator==(SkPDFIndirectReference u, SkPDFIndirectReference v) {
|
||||
@ -44,6 +46,7 @@ inline static bool operator!=(SkPDFIndirectReference u, SkPDFIndirectReference v
|
||||
return u.fValue != v.fValue;
|
||||
}
|
||||
|
||||
|
||||
/** \class SkPDFObject
|
||||
|
||||
A PDF Object is the base class for primitive elements in a PDF file. A
|
||||
@ -352,67 +355,16 @@ private:
|
||||
SkDEBUGCODE(bool fDumped;)
|
||||
};
|
||||
|
||||
/** \class SkPDFSharedStream
|
||||
#ifdef SK_PDF_LESS_COMPRESSION
|
||||
static constexpr bool kSkPDFDefaultDoDeflate = false;
|
||||
#else
|
||||
static constexpr bool kSkPDFDefaultDoDeflate = true;
|
||||
#endif
|
||||
|
||||
This class takes an asset and assumes that it is backed by
|
||||
long-lived shared data (for example, an open file
|
||||
descriptor). That is: no memory savings can be made by holding on
|
||||
to a compressed version instead.
|
||||
*/
|
||||
class SkPDFSharedStream final : public SkPDFObject {
|
||||
public:
|
||||
SkPDFSharedStream(std::unique_ptr<SkStreamAsset> data);
|
||||
~SkPDFSharedStream() override;
|
||||
SkPDFDict* dict() { return &fDict; }
|
||||
void emitObject(SkWStream*) const override;
|
||||
void addResources(SkPDFObjNumMap*) const override;
|
||||
void drop() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<SkStreamAsset> fAsset;
|
||||
SkPDFDict fDict;
|
||||
typedef SkPDFObject INHERITED;
|
||||
};
|
||||
|
||||
/** \class SkPDFStream
|
||||
|
||||
This class takes an asset and assumes that it is the only owner of
|
||||
the asset's data. It immediately compresses the asset to save
|
||||
memory.
|
||||
*/
|
||||
|
||||
class SkPDFStream final : public SkPDFObject {
|
||||
|
||||
public:
|
||||
/** Create a PDF stream. A Length entry is automatically added to the
|
||||
* stream dictionary.
|
||||
* @param data The data part of the stream.
|
||||
* @param stream The data part of the stream. */
|
||||
explicit SkPDFStream(sk_sp<SkData> data);
|
||||
explicit SkPDFStream(std::unique_ptr<SkStreamAsset> stream);
|
||||
~SkPDFStream() override;
|
||||
|
||||
SkPDFDict* dict() { return &fDict; }
|
||||
|
||||
// The SkPDFObject interface.
|
||||
void emitObject(SkWStream* stream) const override;
|
||||
void addResources(SkPDFObjNumMap*) const final;
|
||||
void drop() override;
|
||||
|
||||
protected:
|
||||
/* Create a PDF stream with no data. The setData method must be called to
|
||||
* set the data. */
|
||||
SkPDFStream();
|
||||
|
||||
/** Only call this function once. */
|
||||
void setData(std::unique_ptr<SkStreamAsset> stream);
|
||||
|
||||
private:
|
||||
std::unique_ptr<SkStreamAsset> fCompressedData;
|
||||
SkPDFDict fDict;
|
||||
|
||||
typedef SkPDFDict INHERITED;
|
||||
};
|
||||
SkPDFIndirectReference SkPDFStreamOut(sk_sp<SkPDFDict> dict,
|
||||
std::unique_ptr<SkStreamAsset> stream,
|
||||
SkPDFDocument* doc,
|
||||
bool deflate = kSkPDFDefaultDoDeflate);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -423,6 +375,8 @@ private:
|
||||
*/
|
||||
class SkPDFObjNumMap : SkNoncopyable {
|
||||
public:
|
||||
SkPDFObjNumMap(SkPDFObjectSerializer* s) : fIndirectReferenceSource(s) {}
|
||||
|
||||
/** Add the passed object to the catalog, as well as all its dependencies.
|
||||
* @param obj The object to add. If nullptr, this is a noop.
|
||||
*/
|
||||
@ -438,8 +392,8 @@ public:
|
||||
|
||||
private:
|
||||
friend struct SkPDFObjectSerializer;
|
||||
SkPDFObjectSerializer* fIndirectReferenceSource;
|
||||
std::vector<sk_sp<SkPDFObject>> fObjects;
|
||||
int fNextObjectNumber = 1;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
18
src/pdf/SkUUID.h
Normal file
18
src/pdf/SkUUID.h
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2018 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
#ifndef SkUUID_DEFINED
|
||||
#define SkUUID_DEFINED
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
struct SkUUID {
|
||||
uint8_t fData[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
};
|
||||
|
||||
static inline bool operator==(const SkUUID& u, const SkUUID& v) {
|
||||
return 0 == memcmp(u.fData, v.fData, sizeof(u.fData));
|
||||
}
|
||||
static inline bool operator!=(const SkUUID& u, const SkUUID& v) { return !(u == v); }
|
||||
|
||||
#endif // SkUUID_DEFINED
|
@ -23,6 +23,7 @@
|
||||
#include "SkPDFCanon.h"
|
||||
#include "SkPDFDevice.h"
|
||||
#include "SkPDFDocument.h"
|
||||
#include "SkPDFDocumentPriv.h"
|
||||
#include "SkPDFFont.h"
|
||||
#include "SkPDFTypes.h"
|
||||
#include "SkPDFUtils.h"
|
||||
@ -77,51 +78,11 @@ static void assert_emit_eq(skiatest::Reporter* reporter,
|
||||
assert_eq(reporter, result, string);
|
||||
}
|
||||
|
||||
static void TestPDFStream(skiatest::Reporter* reporter) {
|
||||
char streamBytes[] = "Test\nFoo\tBar";
|
||||
auto streamData = skstd::make_unique<SkMemoryStream>(
|
||||
streamBytes, strlen(streamBytes), true);
|
||||
auto stream = sk_make_sp<SkPDFStream>(std::move(streamData));
|
||||
assert_emit_eq(reporter,
|
||||
*stream,
|
||||
"<</Length 12>> stream\nTest\nFoo\tBar\nendstream");
|
||||
stream->dict()->insertInt("Attribute", 42);
|
||||
assert_emit_eq(reporter,
|
||||
*stream,
|
||||
"<</Length 12\n/Attribute 42>> stream\n"
|
||||
"Test\nFoo\tBar\nendstream");
|
||||
|
||||
{
|
||||
char streamBytes2[] = "This is a longer string, so that compression "
|
||||
"can do something with it. With shorter strings, "
|
||||
"the short circuit logic cuts in and we end up "
|
||||
"with an uncompressed string.";
|
||||
auto stream = sk_make_sp<SkPDFStream>(
|
||||
SkData::MakeWithCopy(streamBytes2, strlen(streamBytes2)));
|
||||
|
||||
SkDynamicMemoryWStream compressedByteStream;
|
||||
SkDeflateWStream deflateWStream(&compressedByteStream);
|
||||
deflateWStream.write(streamBytes2, strlen(streamBytes2));
|
||||
deflateWStream.finalize();
|
||||
|
||||
SkDynamicMemoryWStream expected;
|
||||
expected.writeText("<</Filter /FlateDecode\n/Length 116>> stream\n");
|
||||
compressedByteStream.writeToStream(&expected);
|
||||
compressedByteStream.reset();
|
||||
expected.writeText("\nendstream");
|
||||
sk_sp<SkData> expectedResultData2(expected.detachAsData());
|
||||
SkString result = emit_to_string(*stream);
|
||||
#ifndef SK_PDF_LESS_COMPRESSION
|
||||
assert_eql(reporter,
|
||||
result,
|
||||
(const char*)expectedResultData2->data(),
|
||||
expectedResultData2->size());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void TestObjectNumberMap(skiatest::Reporter* reporter) {
|
||||
SkPDFObjNumMap objNumMap;
|
||||
SkPDFObjectSerializer pdfObjectSerializer;
|
||||
SkNullWStream nullstream;
|
||||
pdfObjectSerializer.serializeHeader(&nullstream);
|
||||
SkPDFObjNumMap objNumMap(&pdfObjectSerializer);
|
||||
sk_sp<SkPDFArray> a1(new SkPDFArray);
|
||||
sk_sp<SkPDFArray> a2(new SkPDFArray);
|
||||
sk_sp<SkPDFArray> a3(new SkPDFArray);
|
||||
@ -145,7 +106,10 @@ static void TestObjectRef(skiatest::Reporter* reporter) {
|
||||
sk_sp<SkPDFArray> a2(new SkPDFArray);
|
||||
a2->appendObjRef(a1);
|
||||
|
||||
SkPDFObjNumMap catalog;
|
||||
SkPDFObjectSerializer pdfObjectSerializer;
|
||||
SkNullWStream nullstream;
|
||||
pdfObjectSerializer.serializeHeader(&nullstream);
|
||||
SkPDFObjNumMap catalog(&pdfObjectSerializer);
|
||||
catalog.addObjectRecursively(a1.get());
|
||||
REPORTER_ASSERT(reporter, catalog.getObjectNumber(a1.get()) == 1);
|
||||
|
||||
@ -264,7 +228,10 @@ static void TestPDFArray(skiatest::Reporter* reporter) {
|
||||
"(Another String) [-1]]");
|
||||
|
||||
sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
|
||||
SkPDFObjNumMap catalog;
|
||||
SkPDFObjectSerializer pdfObjectSerializer;
|
||||
SkNullWStream nullstream;
|
||||
pdfObjectSerializer.serializeHeader(&nullstream);
|
||||
SkPDFObjNumMap catalog(&pdfObjectSerializer);
|
||||
catalog.addObjectRecursively(referencedArray.get());
|
||||
REPORTER_ASSERT(reporter, catalog.getObjectNumber(
|
||||
referencedArray.get()) == 1);
|
||||
@ -328,8 +295,12 @@ static void TestPDFDict(skiatest::Reporter* reporter) {
|
||||
assert_emit_eq(reporter, *dict, "<</Type /DType>>");
|
||||
|
||||
sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
|
||||
SkPDFObjNumMap catalog;
|
||||
SkPDFObjectSerializer pdfObjectSerializer;
|
||||
SkNullWStream nullstream;
|
||||
pdfObjectSerializer.serializeHeader(&nullstream);
|
||||
SkPDFObjNumMap catalog(&pdfObjectSerializer);
|
||||
catalog.addObjectRecursively(referencedArray.get());
|
||||
|
||||
REPORTER_ASSERT(reporter, catalog.getObjectNumber(
|
||||
referencedArray.get()) == 1);
|
||||
dict->insertObjRef("n1", std::move(referencedArray));
|
||||
@ -341,7 +312,6 @@ DEF_TEST(SkPDF_Primitives, reporter) {
|
||||
TestPDFUnion(reporter);
|
||||
TestPDFArray(reporter);
|
||||
TestPDFDict(reporter);
|
||||
TestPDFStream(reporter);
|
||||
TestObjectNumberMap(reporter);
|
||||
TestObjectRef(reporter);
|
||||
test_issue1083();
|
||||
|
Loading…
Reference in New Issue
Block a user