Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions core/fpdfapi/page/cpdf_docpagedata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,10 @@ RetainPtr<CPDF_Font> CPDF_DocPageData::GetFont(
return pdfium::WrapRetain(it->second.Get());
}

// EmbedPDF(layer-seam): an unpromoted base object uses the base's entry.
if (fallback_ && CanUseFallbackForObject(font_dict.Get())) {
// EmbedPDF(layer-seam): an unpromoted base object uses the base's entry,
// except a Type3 font: its glyphs are content, see GetImage().
if (fallback_ && CanUseFallbackForObject(font_dict.Get()) &&
font_dict->GetByteStringFor("Subtype") != "Type3") {
return fallback_->GetFont(font_dict);
}

Expand Down Expand Up @@ -432,13 +434,16 @@ RetainPtr<CPDF_Pattern> CPDF_DocPageData::GetPattern(
return pdfium::WrapRetain(it->second.Get());
}

// EmbedPDF(layer-seam): an unpromoted base object uses the base's entry.
if (fallback_ && CanUseFallbackForObject(pPatternObj.Get())) {
// EmbedPDF(layer-seam): an unpromoted base object uses the base's entry,
// except a tiling pattern: its cell is content, see GetImage().
const int pattern_type = pPatternObj->GetDict()->GetIntegerFor("PatternType");
if (fallback_ && CanUseFallbackForObject(pPatternObj.Get()) &&
pattern_type != CPDF_Pattern::kTiling) {
return fallback_->GetPattern(pPatternObj, matrix);
}

RetainPtr<CPDF_Pattern> pattern;
switch (pPatternObj->GetDict()->GetIntegerFor("PatternType")) {
switch (pattern_type) {
case CPDF_Pattern::kTiling:
pattern = pdfium::MakeRetain<CPDF_TilingPattern>(GetDocument(),
pPatternObj, matrix);
Expand Down Expand Up @@ -491,6 +496,9 @@ RetainPtr<CPDF_Image> CPDF_DocPageData::GetImage(uint32_t dwStreamObjNum) {
// CPDF_Image carries a document back-pointer and CPDF_PageImageCache rejects
// cross-document images. Build a document-local CPDF_Image even when the
// underlying stream falls through to a shared base document.
// Content makes images of the document that parses it, so what holds content
// of its own (a tiling pattern's cell, a Type3 font's glyphs) never uses the
// base's entry either: GetPattern(), GetFont().
auto pImage = pdfium::MakeRetain<CPDF_Image>(GetDocument(), dwStreamObjNum);
image_map_[dwStreamObjNum] = pImage;
return pImage;
Expand Down
7 changes: 6 additions & 1 deletion core/fpdfapi/page/cpdf_imageloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ bool CPDF_ImageLoader::Start(const CPDF_ImageObject* pImage,
CPDF_ColorSpace::Family eFamily,
bool bLoadMask,
const CFX_Size& max_size_required) {
cache_ = pPageImageCache;
// EmbedPDF(bug-fix): the page image cache refuses an image of another
// document, and Finish() then read the cache's current entry: null (a crash)
// or the previous image's (nothing drawn). Such an image loads uncached.
cache_ = pPageImageCache && pPageImageCache->Accepts(*pImage->GetImage())
? pPageImageCache
: nullptr;
image_object_ = pImage;
bool should_continue;
if (cache_) {
Expand Down
8 changes: 7 additions & 1 deletion core/fpdfapi/page/cpdf_pageimagecache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ void CPDF_PageImageCache::ClearImageCacheEntry(const CPDF_Stream* pStream) {
image_cache_.erase(it);
}

// EmbedPDF(bug-fix): see the header.
bool CPDF_PageImageCache::Accepts(const CPDF_Image& image) const {
return page_->GetDocument() == image.GetDocument();
}

bool CPDF_PageImageCache::StartGetCachedBitmap(
RetainPtr<CPDF_Image> pImage,
const CPDF_Dictionary* pFormResources,
Expand All @@ -175,7 +180,8 @@ bool CPDF_PageImageCache::StartGetCachedBitmap(
bool bLoadMask,
const CFX_Size& max_size_required) {
// A cross-document image may have come from the embedder.
if (page_->GetDocument() != pImage->GetDocument()) {
// EmbedPDF(bug-fix): the rule lives in Accepts(); the loader asks it first.
if (!Accepts(*pImage)) {
return false;
}

Expand Down
4 changes: 4 additions & 0 deletions core/fpdfapi/page/cpdf_pageimagecache.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class CPDF_PageImageCache {
uint32_t GetTimeCount() const { return time_count_; }
CPDF_Page* GetPage() const { return page_; }

// EmbedPDF(bug-fix): whether StartGetCachedBitmap() loads |image|. An image
// of another document is not the cache's: see CPDF_ImageLoader::Start().
bool Accepts(const CPDF_Image& image) const;

bool StartGetCachedBitmap(RetainPtr<CPDF_Image> pImage,
const CPDF_Dictionary* pFormResources,
const CPDF_Dictionary* pPageResources,
Expand Down
6 changes: 0 additions & 6 deletions core/fpdfapi/render/cpdf_docrenderdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,6 @@ RetainPtr<CPDF_Type3Cache> CPDF_DocRenderData::GetCachedType3(
return pdfium::WrapRetain(it->second.Get());
}

// EmbedPDF(layer-seam): an unpromoted base font uses the base's cache.
if (fallback_ && font->GetFontDictObjNum() != 0 &&
!GetDocument()->IsObjectPromoted(font->GetFontDictObjNum())) {
return fallback_->GetCachedType3(font);
}

auto cache = pdfium::MakeRetain<CPDF_Type3Cache>(font);
type3_face_map_[font].Reset(cache.Get());
return cache;
Expand Down
37 changes: 37 additions & 0 deletions fpdfsdk/epdf_layer_embeddertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2013,3 +2013,40 @@ TEST_F(EPDFLayerEmbedderTest, LayerDeltaReplaysRemoveAllAnnots) {

EPDF_ReleaseBaseDocument(base);
}

// A layer draws the images inside content: a tiling pattern's one large cell,
// a pattern of small cells, a Type3 glyph. Each page renders as the file does
// on its own. The layer took that content from its base, parsed for the base,
// so its images were the base's, which the layer page's image cache refuses:
// the render crashed.
TEST_F(EPDFLayerEmbedderTest, LayerDrawsImagesInPatternsAndType3Glyphs) {
static constexpr char kFile[] = "layer_content_images.pdf";
static constexpr int kPageCount = 3;
std::vector<std::string> expected;
ASSERT_TRUE(OpenDocument(kFile));
for (int page_index = 0; page_index < kPageCount; ++page_index) {
ScopedPage page = LoadScopedPage(page_index);
ASSERT_TRUE(page);
ScopedFPDFBitmap bitmap = RenderPage(page.get());
expected.push_back(HashBitmap(bitmap.get()));
}

FileAccessForTesting base_access(kFile);
EPDF_BASE_DOCUMENT base = EPDF_LoadBaseDocument(&base_access, nullptr);
ASSERT_TRUE(base);
{
EPDFLayerOpenStatus status = EPDFLayerOpenStatus_kOpenFailed;
ScopedFPDFDocument layer(
EPDFLayer_OpenLayer(base, nullptr, nullptr, &status));
ASSERT_TRUE(layer);
for (int page_index = 0; page_index < kPageCount; ++page_index) {
ScopedFPDFPage page(FPDF_LoadPage(layer.get(), page_index));
ASSERT_TRUE(page);
ScopedFPDFBitmap bitmap = RenderPage(page.get());
EXPECT_EQ(expected[page_index], HashBitmap(bitmap.get()))
<< "page " << page_index;
}
}

EPDF_ReleaseBaseDocument(base);
}
26 changes: 26 additions & 0 deletions fpdfsdk/epdf_view_embeddertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -961,3 +961,29 @@ TEST_F(EPDFViewEmbedderTest, StandardFontsDrawWithBuiltInFaces) {
EXPECT_EQ((std::vector<std::string>{"Tinos", "Arimo"}),
HelloWorldFontFamilies(page.get()));
}

// An image object of another document draws as one of the page's own (rule
// bug-fix). The page image cache refuses it, and the image loader then read
// the cache's current entry: with none yet, a crash.
TEST_F(EPDFViewEmbedderTest, ImageOfAnotherDocumentDraws) {
ScopedFPDFBitmap red(FPDFBitmap_Create(2, 2, 0));
ASSERT_TRUE(red);
ASSERT_TRUE(FPDFBitmap_FillRect(red.get(), 0, 0, 2, 2, 0xFFFF0000));
// Draws an image of |image_document| on a new page of |document|.
auto render = [&red](FPDF_DOCUMENT document, FPDF_DOCUMENT image_document) {
ScopedFPDFPage page(FPDFPage_New(document, 0, 200, 200));
FPDF_PAGEOBJECT image = FPDFPageObj_NewImageObj(image_document);
EXPECT_TRUE(FPDFImageObj_SetBitmap(nullptr, 0, image, red.get()));
EXPECT_TRUE(FPDFImageObj_SetMatrix(image, 100, 0, 0, 100, 50, 50));
FPDFPage_InsertObject(page.get(), image);
ScopedFPDFBitmap bitmap = RenderPage(page.get());
return HashBitmap(bitmap.get());
};

ScopedFPDFDocument document(FPDF_CreateNewDocument());
ScopedFPDFDocument other(FPDF_CreateNewDocument());
ASSERT_TRUE(document);
ASSERT_TRUE(other);
EXPECT_EQ(render(document.get(), document.get()),
render(document.get(), other.get()));
}
151 changes: 151 additions & 0 deletions testing/resources/layer_content_images.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
{{header}}
{{object 1 0}} <<
/Type /Catalog
/Pages 2 0 R
>>
endobj
{{object 2 0}} <<
/Type /Pages
/MediaBox [0 0 200 200]
/Count 3
/Kids [3 0 R 4 0 R 5 0 R]
>>
endobj
% A tiling pattern whose one cell is larger than the area it fills, the way
% Chrome prints a CSS background.
{{object 3 0}} <<
/Type /Page
/Parent 2 0 R
/Resources <<
/Pattern << /P1 10 0 R >>
>>
/Contents 6 0 R
>>
endobj
% A tiling pattern of small cells.
{{object 4 0}} <<
/Type /Page
/Parent 2 0 R
/Resources <<
/Pattern << /P2 11 0 R >>
>>
/Contents 7 0 R
>>
endobj
% A Type3 glyph that draws a rectangle and the image.
{{object 5 0}} <<
/Type /Page
/Parent 2 0 R
/Resources <<
/Font << /F1 12 0 R >>
>>
/Contents 8 0 R
>>
endobj
{{object 6 0}} <<
{{streamlen}}
>>
stream
/Pattern cs /P1 scn
20 20 160 160 re f
endstream
endobj
{{object 7 0}} <<
{{streamlen}}
>>
stream
/Pattern cs /P2 scn
0 0 200 200 re f
endstream
endobj
{{object 8 0}} <<
{{streamlen}}
>>
stream
BT
/F1 100 Tf
50 50 Td
(a) Tj
ET
endstream
endobj
% Red and green on top, blue and yellow below.
{{object 9 0}} <<
/Type /XObject
/Subtype /Image
/Width 2
/Height 2
/ColorSpace /DeviceRGB
/BitsPerComponent 8
/Filter /ASCIIHexDecode
{{streamlen}}
>>
stream
FF0000 00FF00 0000FF FFFF00>
endstream
endobj
{{object 10 0}} <<
/Type /Pattern
/PatternType 1
/PaintType 1
/TilingType 1
/BBox [0 0 200 200]
/XStep 202
/YStep 202
/Resources <<
/XObject << /Im1 9 0 R >>
>>
{{streamlen}}
>>
stream
q 200 0 0 200 0 0 cm /Im1 Do Q
endstream
endobj
{{object 11 0}} <<
/Type /Pattern
/PatternType 1
/PaintType 1
/TilingType 1
/BBox [0 0 20 20]
/XStep 20
/YStep 20
/Resources <<
/XObject << /Im1 9 0 R >>
>>
{{streamlen}}
>>
stream
q 20 0 0 20 0 0 cm /Im1 Do Q
endstream
endobj
{{object 12 0}} <<
/Type /Font
/Subtype /Type3
/FontBBox [0 0 1000 1000]
/FontMatrix [0.001 0 0 0.001 0 0]
/FirstChar 97
/LastChar 97
/Widths [1000]
/Encoding <<
/Type /Encoding
/Differences [97 /a]
>>
/CharProcs << /a 13 0 R >>
/Resources <<
/XObject << /Im1 9 0 R >>
>>
>>
endobj
{{object 13 0}} <<
{{streamlen}}
>>
stream
1000 0 d0
0 0 500 1000 re f
q 500 0 0 1000 500 0 cm /Im1 Do Q
endstream
endobj
{{xref}}
{{trailer}}
{{startxref}}
%%EOF
Binary file added testing/resources/layer_content_images.pdf
Binary file not shown.