JPEG(JFIF only) directly embedded into PDF
R=reed@google.com, djsollen@google.com, mtklein@google.com Author: halcanary@google.com Review URL: https://codereview.chromium.org/515493003
This commit is contained in:
parent
9694d63cf0
commit
daefa5b340
@ -143,6 +143,7 @@
|
||||
'../tests/OSPathTest.cpp',
|
||||
'../tests/ObjectPoolTest.cpp',
|
||||
'../tests/OnceTest.cpp',
|
||||
'../tests/PDFJpegEmbedTest.cpp',
|
||||
'../tests/PDFPrimitivesTest.cpp',
|
||||
'../tests/PackBitsTest.cpp',
|
||||
'../tests/PaintTest.cpp',
|
||||
|
@ -2182,8 +2182,8 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix,
|
||||
return;
|
||||
}
|
||||
|
||||
SkAutoTUnref<SkPDFImage> image(
|
||||
SkPDFImage::CreateImage(*bitmap, subset, fEncoder));
|
||||
SkAutoTUnref<SkPDFObject> image(
|
||||
SkPDFCreateImageObject(*bitmap, subset, fEncoder));
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "SkData.h"
|
||||
#include "SkFlate.h"
|
||||
#include "SkPDFCatalog.h"
|
||||
#include "SkPixelRef.h"
|
||||
#include "SkRect.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkString.h"
|
||||
@ -628,3 +629,91 @@ bool SkPDFImage::populate(SkPDFCatalog* catalog) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* This PDFObject assumes that its constructor was handed
|
||||
* Jpeg-encoded data that can be directly embedded into a PDF.
|
||||
*/
|
||||
class PDFJPEGImage : public SkPDFObject {
|
||||
SkAutoTUnref<SkData> fData;
|
||||
int fWidth;
|
||||
int fHeight;
|
||||
public:
|
||||
PDFJPEGImage(SkData* data, int width, int height)
|
||||
: fData(SkRef(data)), fWidth(width), fHeight(height) {}
|
||||
virtual void getResources(const SkTSet<SkPDFObject*>&,
|
||||
SkTSet<SkPDFObject*>*) SK_OVERRIDE {}
|
||||
virtual void emitObject(
|
||||
SkWStream* stream,
|
||||
SkPDFCatalog* catalog, bool indirect) SK_OVERRIDE {
|
||||
if (indirect) {
|
||||
this->emitIndirectObject(stream, catalog);
|
||||
return;
|
||||
}
|
||||
SkASSERT(fData.get());
|
||||
const char kPrefaceFormat[] =
|
||||
"<<"
|
||||
"/Type /XObject\n"
|
||||
"/Subtype /Image\n"
|
||||
"/Width %d\n"
|
||||
"/Height %d\n"
|
||||
"/ColorSpace /DeviceRGB\n"
|
||||
"/BitsPerComponent 8\n"
|
||||
"/Filter /DCTDecode\n"
|
||||
"/ColorTransform 0\n"
|
||||
"/Length " SK_SIZE_T_SPECIFIER "\n"
|
||||
">> stream\n";
|
||||
SkString preface(
|
||||
SkStringPrintf(kPrefaceFormat, fWidth, fHeight, fData->size()));
|
||||
const char kPostface[] = "\nendstream";
|
||||
stream->write(preface.c_str(), preface.size());
|
||||
stream->write(fData->data(), fData->size());
|
||||
stream->write(kPostface, sizeof(kPostface));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* If the bitmap is not subsetted, return its encoded data, if
|
||||
* availible.
|
||||
*/
|
||||
static inline SkData* ref_encoded_data(const SkBitmap& bm) {
|
||||
if ((NULL == bm.pixelRef())
|
||||
|| !bm.pixelRefOrigin().isZero()
|
||||
|| (bm.info().dimensions() != bm.pixelRef()->info().dimensions())) {
|
||||
return NULL;
|
||||
}
|
||||
return bm.pixelRef()->refEncodedData();
|
||||
}
|
||||
|
||||
/*
|
||||
* This functions may give false negatives but no false positives.
|
||||
*/
|
||||
static bool is_jfif_jpeg(SkData* data) {
|
||||
if (!data || (data->size() < 11)) {
|
||||
return false;
|
||||
}
|
||||
const uint8_t bytesZeroToThree[] = {0xFF, 0xD8, 0xFF, 0xE0};
|
||||
const uint8_t bytesSixToTen[] = {'J', 'F', 'I', 'F', 0};
|
||||
// 0 1 2 3 4 5 6 7 8 9 10
|
||||
// FF D8 FF E0 ?? ?? 'J' 'F' 'I' 'F' 00 ...
|
||||
return ((0 == memcmp(data->bytes(), bytesZeroToThree,
|
||||
sizeof(bytesZeroToThree)))
|
||||
&& (0 == memcmp(data->bytes() + 6, bytesSixToTen,
|
||||
sizeof(bytesSixToTen))));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
SkPDFObject* SkPDFCreateImageObject(
|
||||
const SkBitmap& bitmap,
|
||||
const SkIRect& subset,
|
||||
SkPicture::EncodeBitmap encoder) {
|
||||
if (SkIRect::MakeWH(bitmap.width(), bitmap.height()) == subset) {
|
||||
SkAutoTUnref<SkData> encodedData(ref_encoded_data(bitmap));
|
||||
if (is_jfif_jpeg(encodedData)) {
|
||||
return SkNEW_ARGS(PDFJPEGImage,
|
||||
(encodedData, bitmap.width(), bitmap.height()));
|
||||
}
|
||||
}
|
||||
return SkPDFImage::CreateImage(bitmap, subset, encoder);
|
||||
}
|
||||
|
@ -17,9 +17,20 @@
|
||||
#include "SkRefCnt.h"
|
||||
|
||||
class SkBitmap;
|
||||
class SkData;
|
||||
class SkPDFCatalog;
|
||||
struct SkIRect;
|
||||
|
||||
/**
|
||||
* Return the mose efficient availible encoding of the given bitmap.
|
||||
*
|
||||
* If the bitmap has encoded JPEG data and that data can be embedded
|
||||
* into the PDF output stream directly, use that. Otherwise, fall
|
||||
* back on SkPDFImage::CreateImage.
|
||||
*/
|
||||
SkPDFObject* SkPDFCreateImageObject(
|
||||
const SkBitmap&, const SkIRect& subset, SkPicture::EncodeBitmap);
|
||||
|
||||
/** \class SkPDFImage
|
||||
|
||||
An image XObject.
|
||||
|
101
tests/PDFJpegEmbedTest.cpp
Normal file
101
tests/PDFJpegEmbedTest.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkDocument.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkImageGenerator.h"
|
||||
#include "SkData.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkDecodingImageGenerator.h"
|
||||
|
||||
#include "Resources.h"
|
||||
#include "Test.h"
|
||||
|
||||
// Returned bitmap is lazy. Only lazy bitmaps hold onto the original data.
|
||||
static SkBitmap bitmap_from_data(SkData* data) {
|
||||
SkASSERT(data);
|
||||
SkBitmap bm;
|
||||
SkInstallDiscardablePixelRef(
|
||||
SkDecodingImageGenerator::Create(
|
||||
data, SkDecodingImageGenerator::Options()), &bm);
|
||||
return bm;
|
||||
}
|
||||
|
||||
static bool is_subset_of(SkData* smaller, SkData* larger) {
|
||||
SkASSERT(smaller && larger);
|
||||
if (smaller->size() > larger->size()) {
|
||||
return false;
|
||||
}
|
||||
size_t size = smaller->size();
|
||||
size_t size_diff = larger->size() - size;
|
||||
for (size_t i = 0; i <= size_diff; ++i) {
|
||||
if (0 == memcmp(larger->bytes() + i, smaller->bytes(), size)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static SkData* load_resource(
|
||||
skiatest::Reporter* r, const char* test, const char* filename) {
|
||||
SkString path(GetResourcePath(filename));
|
||||
SkData* data = SkData::NewFromFileName(path.c_str());
|
||||
if (!data && r->verbose()) {
|
||||
SkDebugf("\n%s: Resource '%s' can not be found.\n",
|
||||
test, filename);
|
||||
}
|
||||
return data; // May return NULL.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that for Jpeg files that use the JFIF colorspace, they are
|
||||
* directly embedded into the PDF (without re-encoding) when that
|
||||
* makes sense.
|
||||
*/
|
||||
DEF_TEST(PDFJpegEmbedTest, r) {
|
||||
const char test[] = "PDFJpegEmbedTest";
|
||||
SkAutoTUnref<SkData> mandrillData(
|
||||
load_resource(r, test, "mandrill_512_q075.jpg"));
|
||||
SkAutoTUnref<SkData> cmykData(load_resource(r, test, "CMYK.jpg"));
|
||||
if (!mandrillData || !cmykData) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkDynamicMemoryWStream pdf;
|
||||
SkAutoTUnref<SkDocument> document(SkDocument::CreatePDF(&pdf));
|
||||
SkCanvas* canvas = document->beginPage(642, 1028);
|
||||
|
||||
canvas->clear(SK_ColorLTGRAY);
|
||||
|
||||
SkBitmap bm1(bitmap_from_data(mandrillData));
|
||||
canvas->drawBitmap(bm1, 65.0, 0.0, NULL);
|
||||
SkBitmap bm2(bitmap_from_data(cmykData));
|
||||
canvas->drawBitmap(bm2, 0.0, 512.0, NULL);
|
||||
|
||||
canvas->flush();
|
||||
document->endPage();
|
||||
document->close();
|
||||
SkAutoTUnref<SkData> pdfData(pdf.copyToData());
|
||||
SkASSERT(pdfData);
|
||||
pdf.reset();
|
||||
|
||||
REPORTER_ASSERT(r, is_subset_of(mandrillData, pdfData));
|
||||
|
||||
// This JPEG uses a nonstandard colorspace - it can not be
|
||||
// embedded into the PDF directly.
|
||||
REPORTER_ASSERT(r, !is_subset_of(cmykData, pdfData));
|
||||
|
||||
// The following is for debugging purposes only.
|
||||
const char* outputPath = getenv("SKIA_TESTS_PDF_JPEG_EMBED_OUTPUT_PATH");
|
||||
if (outputPath) {
|
||||
SkFILEWStream output(outputPath);
|
||||
if (output.isValid()) {
|
||||
output.write(pdfData->data(), pdfData->size());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user