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
22 changes: 21 additions & 1 deletion core/fpdfapi/render/cpdf_renderstatus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,8 +455,21 @@ bool CPDF_RenderStatus::ProcessPath(CPDF_PathObject* path_obj,
const CFX_Matrix& mtObj2Device) {
CFX_FillRenderOptions::FillType fill_type = path_obj->filltype();
bool stroke = path_obj->stroke();
// EmbedPDF(bug-fix): upstream drew a pattern stroke before the path's solid
// fill, so with B and B* the fill covered the stroke's inner half. The
// pattern stroke is held back here and drawn after the fill.
const CPDF_Color* pattern_stroke =
stroke && path_obj->color_state().GetStrokeColor()->IsPattern()
? path_obj->color_state().GetStrokeColor()
: nullptr;
if (pattern_stroke) {
stroke = false;
}
ProcessPathPattern(path_obj, mtObj2Device, &fill_type, &stroke);
if (fill_type == CFX_FillRenderOptions::FillType::kNoFill && !stroke) {
if (pattern_stroke) {
DrawPathWithPattern(path_obj, mtObj2Device, pattern_stroke, true);
}
return true;
}

Expand All @@ -479,11 +492,18 @@ bool CPDF_RenderStatus::ProcessPath(CPDF_PathObject* path_obj,
return true;
}

return device_->DrawPath(
// EmbedPDF(bug-fix): the held-back pattern stroke goes on top of the fill.
// When DrawPath() fails, the caller redraws the whole object, stroke
// included, so the stroke is skipped here.
const bool result = device_->DrawPath(
*path_obj->path().GetObject(), &path_matrix,
path_obj->graph_state().GetObject(), fill_argb, stroke_argb,
GetFillOptionsForDrawPathWithBlend(options, path_obj, fill_type, stroke,
type3_char_));
if (result && pattern_stroke) {
DrawPathWithPattern(path_obj, mtObj2Device, pattern_stroke, true);
}
return result;
}

RetainPtr<CPDF_TransferFunc> CPDF_RenderStatus::GetTransferFunc(
Expand Down
34 changes: 34 additions & 0 deletions core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,47 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <cstdint>
#include <vector>

#include "public/cpp/fpdf_scopers.h"
#include "public/fpdfview.h"
#include "testing/embedder_test.h"
#include "testing/embedder_test_constants.h"
#include "testing/gtest/include/gtest/gtest.h"

class FPDFRenderPatternEmbedderTest : public EmbedderTest {};

TEST_F(FPDFRenderPatternEmbedderTest, ShadingStrokePaintsAfterSolidFill) {
ASSERT_TRUE(OpenDocument("gradient_pattern_stroke_order.pdf"));
ASSERT_EQ(3, FPDF_GetPageCount(document()));

for (int page_index = 0; page_index < 3; ++page_index) {
SCOPED_TRACE(page_index);
ScopedPage page = LoadScopedPage(page_index);
ASSERT_TRUE(page);
for (int scale : {1, 2, 4}) {
SCOPED_TRACE(scale);
const int size = 100 * scale;
const int stride = size * 4;
std::vector<uint8_t> pixels(size * stride);
ScopedFPDFBitmap bitmap(FPDFBitmap_CreateEx(size, size, FPDFBitmap_BGRA,
pixels.data(), stride));
ASSERT_TRUE(bitmap);
ASSERT_TRUE(FPDFBitmap_FillRect(bitmap.get(), 0, 0, size, size,
/*color=*/0xFFFFFFFF));
FPDF_RenderPageBitmap(bitmap.get(), page.get(), 0, 0, size, size,
/*rotate=*/0, /*flags=*/0);

// Both halves of the 10-point stroke stay visible for B, B* and S.
const int row = 50 * scale * stride;
EXPECT_LT(pixels[row + 17 * scale * 4 + 1], 20);
EXPECT_LT(pixels[row + 23 * scale * 4 + 1], 20);
EXPECT_EQ(pixels[row + 50 * scale * 4 + 1], 255);
}
}
}

TEST_F(FPDFRenderPatternEmbedderTest, LoadError547706) {
// Test shading where object is a dictionary instead of a stream.
ASSERT_TRUE(OpenDocument("bug_547706.pdf"));
Expand Down
90 changes: 90 additions & 0 deletions testing/resources/gradient_pattern_stroke_order.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{{header}}
{{object 1 0}} <<
/Type /Catalog
/Pages 2 0 R
>>
endobj
{{object 2 0}} <<
/Type /Pages
/MediaBox [0 0 100 100]
/Count 3
/Kids [3 0 R 8 0 R 10 0 R]
/Resources << /Pattern << /P0 5 0 R >> >>
>>
endobj
{{object 3 0}} <<
/Type /Page
/Parent 2 0 R
/Contents 4 0 R
>>
endobj
{{object 4 0}} <<
{{streamlen}}
>>
stream
1 1 1 rg
/Pattern CS
/P0 SCN
10 w
20 20 60 60 re B
endstream
endobj
{{object 5 0}} <<
/Type /Pattern
/PatternType 2
/Shading 6 0 R
>>
endobj
{{object 6 0}} <<
/ShadingType 2
/ColorSpace /DeviceRGB
/Coords [0 0 100 0]
/Extend [true true]
/Function 7 0 R
>>
endobj
{{object 7 0}} <<
/FunctionType 2
/Domain [0 1]
/C0 [1 0 0]
/C1 [0 0 1]
/N 1
>>
endobj
{{object 8 0}} <<
/Type /Page
/Parent 2 0 R
/Contents 9 0 R
>>
endobj
{{object 9 0}} <<
{{streamlen}}
>>
stream
1 1 1 rg
/Pattern CS
/P0 SCN
10 w
20 20 60 60 re B*
endstream
endobj
{{object 10 0}} <<
/Type /Page
/Parent 2 0 R
/Contents 11 0 R
>>
endobj
{{object 11 0}} <<
{{streamlen}}
>>
stream
/Pattern CS
/P0 SCN
10 w
20 20 60 60 re S
endstream
endobj
{{xref}}
{{trailer}}
{{startxref}}
%%EOF
Binary file not shown.