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
7 changes: 5 additions & 2 deletions docs/embedpdf/fork-ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ edits, and the copy happens outside any transaction. On a frozen base it writes
`cpdf_annot.cpp`. Writes that read first: the field pruning in `fpdf_flatten.cpp` compares fields
read const and takes an array mutable only when an entry goes.
**Guarded by.** `cpdf_read_only_graph_guard_unittest.cpp`,
`EPDFAnnotEmbedderTest.DrawingARedactionMarkWithoutAppearanceWritesNone`; layer mode's list of
`EPDFAnnotEmbedderTest.DrawingARedactionMarkWithoutAppearanceWritesNone`,
`EPDFStampResizeEmbedderTest.ADrawingFromALayersPageLeavesTheLayerAlone`; layer mode's list of
tests that write (`testing/embedpdf_layer_mode_writers.txt`): a test that only reads must leave
nothing in a layer.
**When upstream touches this.** A new read path that calls `GetMutable*` or the appearance
Expand Down Expand Up @@ -498,7 +499,9 @@ layer over a frozen base. Like the engine, it sends the form-fill environment no
doesn't draw with it. A document a test builds in memory goes through the layer too when the test
opens it with `EmbedderTest::OpenDocumentFromMemory()`, or with
`embedpdf_test::LoadMemDocumentForTest()` when it keeps the document itself; a test that writes
into a new document starts from `EmptyPdf()`, since `CreateEmptyDocument()` never makes a layer.
into a new document starts from `EmptyPdf()` (`NewTestDocument()` when it keeps the document), since
`CreateEmptyDocument()` and `FPDF_CreateNewDocument()` never make a layer. A test about plain
documents keeps one in layer mode too, and says why.
**Why.** Upstream's embedder tests then run through the layer's read and write paths, so a new
upstream feature is tested on a layer without anyone writing a layer test.
**Where.** `testing/embedpdf_layer_mode.{h,cpp}`, the `EmbedPDF(layer-mode)` hooks in
Expand Down
30 changes: 16 additions & 14 deletions fpdfsdk/epdf_action_embeddertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "core/fxcrt/bytestring.h"
#include "core/fxcrt/retain_ptr.h"
#include "fpdfsdk/cpdfsdk_helpers.h"
#include "fpdfsdk/epdf_edit_test_util.h"
#include "public/epdf_doc.h"
#include "public/epdf_form.h"
#include "public/epdf_layer.h"
Expand All @@ -29,6 +30,7 @@
#include "public/fpdf_edit.h"
#include "public/fpdf_javascript.h"
#include "testing/embedder_test.h"
#include "testing/embedpdf_layer_mode.h"
#include "testing/fx_string_testhelpers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/utils/file_util.h"
Expand Down Expand Up @@ -146,7 +148,7 @@ TEST_F(EPDFActionEmbedderTest, NamedJavaScriptIndexPairing) {
}

TEST_F(EPDFActionEmbedderTest, NextRenditionCycleAndMalformedEntry) {
ScopedFPDFDocument document(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument document(NewTestDocument());
ASSERT_TRUE(document);
CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document.get());
ASSERT_TRUE(doc);
Expand Down Expand Up @@ -223,7 +225,7 @@ TEST_F(EPDFActionEmbedderTest, DepthLimitMarksModelIncomplete) {
}

TEST_F(EPDFActionEmbedderTest, DocumentAndPageModelsAreDetached) {
ScopedFPDFDocument document(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument document(NewTestDocument());
ASSERT_TRUE(document);
ScopedFPDFPage page(FPDFPage_New(document.get(), 0, 300, 300));
ASSERT_TRUE(page);
Expand Down Expand Up @@ -483,7 +485,7 @@ TEST_F(EPDFActionEmbedderTest, NodeUriPayloadFromRealDocument) {
}

TEST_F(EPDFActionEmbedderTest, NodeDestPayloadFromCreatedGoTo) {
ScopedFPDFDocument document(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument document(NewTestDocument());
ASSERT_TRUE(document);
ScopedFPDFPage page(FPDFPage_New(document.get(), 0, 612, 792));
ASSERT_TRUE(page);
Expand Down Expand Up @@ -525,7 +527,7 @@ TEST_F(EPDFActionEmbedderTest, NodeDestPayloadFromCreatedGoTo) {
}

TEST_F(EPDFActionEmbedderTest, DestinationsByPageObjectNumber) {
ScopedFPDFDocument document(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument document(NewTestDocument());
ASSERT_TRUE(document);
ScopedFPDFPage first(FPDFPage_New(document.get(), 0, 612, 792));
ScopedFPDFPage second(FPDFPage_New(document.get(), 1, 612, 792));
Expand Down Expand Up @@ -588,7 +590,7 @@ TEST_F(EPDFActionEmbedderTest, DestinationsByPageObjectNumber) {
}

TEST_F(EPDFActionEmbedderTest, CreateNamedWritesTheName) {
ScopedFPDFDocument document(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument document(NewTestDocument());
ASSERT_TRUE(document);

FPDF_ACTION next = EPDFAction_CreateNamed(document.get(), "NextPage");
Expand All @@ -604,7 +606,7 @@ TEST_F(EPDFActionEmbedderTest, CreateNamedWritesTheName) {
}

TEST_F(EPDFActionEmbedderTest, NodeFilePathAndNamePayloadsFromSyntheticDicts) {
ScopedFPDFDocument document(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument document(NewTestDocument());
ASSERT_TRUE(document);
CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document.get());
ASSERT_TRUE(doc);
Expand Down Expand Up @@ -655,7 +657,7 @@ TEST_F(EPDFActionEmbedderTest, NodeFilePathAndNamePayloadsFromSyntheticDicts) {
}

TEST_F(EPDFActionEmbedderTest, HideTargetPayloadsMixedForms) {
ScopedFPDFDocument document(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument document(NewTestDocument());
ASSERT_TRUE(document);
CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document.get());
ASSERT_TRUE(doc);
Expand Down Expand Up @@ -754,7 +756,7 @@ TEST_F(EPDFActionEmbedderTest, PartialHideTargetListIsWithheld) {
}

TEST_F(EPDFActionEmbedderTest, ResetFormThreeStates) {
ScopedFPDFDocument document(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument document(NewTestDocument());
ASSERT_TRUE(document);
CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document.get());
ASSERT_TRUE(doc);
Expand Down Expand Up @@ -877,14 +879,14 @@ TEST_F(EPDFActionEmbedderTest, PayloadByteBudgetOverflowDropsNode) {

TEST_F(EPDFActionEmbedderTest, OpenActionDestinationForms) {
// Absent /OpenAction: both readers answer null.
ScopedFPDFDocument empty(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument empty(NewTestDocument());
ASSERT_TRUE(empty);
EXPECT_FALSE(EPDFDoc_GetOpenActionDest(empty.get()));
EXPECT_FALSE(EPDFDoc_GetOpenActionModel(empty.get()));
EXPECT_FALSE(EPDFDoc_GetOpenActionDest(nullptr));

// Destination form: the dest reads, the action model stays null.
ScopedFPDFDocument document(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument document(NewTestDocument());
ASSERT_TRUE(document);
ScopedFPDFPage page(FPDFPage_New(document.get(), 0, 612, 792));
ASSERT_TRUE(page);
Expand Down Expand Up @@ -934,7 +936,7 @@ TEST_F(EPDFActionEmbedderTest, OpenActionDestinationForms) {
}

TEST_F(EPDFActionEmbedderTest, DestinationArrayJunkBeyondViewIsIgnored) {
ScopedFPDFDocument document(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument document(NewTestDocument());
ASSERT_TRUE(document);
ScopedFPDFPage page(FPDFPage_New(document.get(), 0, 612, 792));
ASSERT_TRUE(page);
Expand Down Expand Up @@ -1004,7 +1006,7 @@ std::string GetSubmitURL(EPDF_ACTION_MODEL model, EPDF_ACTION_NODE_ID node) {

// Each creator's action reads back through the action model as written.
TEST_F(EPDFActionEmbedderTest, CreatorsReadBack) {
ScopedFPDFDocument document(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument document(NewTestDocument());
ASSERT_TRUE(document);
CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document.get());
RetainPtr<CPDF_Dictionary> field = doc->NewIndirect<CPDF_Dictionary>();
Expand Down Expand Up @@ -1099,7 +1101,7 @@ TEST_F(EPDFActionEmbedderTest, CreatorsReadBack) {
// /Next: one action is the entry itself; several, an array, read back in
// order. An action can't follow itself.
TEST_F(EPDFActionEmbedderTest, SetNextChainsActions) {
ScopedFPDFDocument document(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument document(NewTestDocument());
ASSERT_TRUE(document);
auto script = [&](const wchar_t* source) {
return EPDFAction_CreateJavaScript(document.get(),
Expand Down Expand Up @@ -1130,7 +1132,7 @@ TEST_F(EPDFActionEmbedderTest, SetNextChainsActions) {
EXPECT_FALSE(CPDFDictionaryFromFPDFAction(first)->KeyExist("Next"));

// An action of another document is refused.
ScopedFPDFDocument other(FPDF_CreateNewDocument());
embedpdf_test::ScopedTestDocument other(NewTestDocument());
FPDF_ACTION foreign = EPDFAction_CreateJavaScript(
other.get(), GetFPDFWideString(L"x();").get());
EXPECT_FALSE(EPDFAction_SetNext(document.get(), first, &foreign, 1));
Expand Down
17 changes: 8 additions & 9 deletions fpdfsdk/epdf_annot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3521,8 +3521,10 @@ static RetainPtr<CPDF_Stream> ImportPageAsDrawing(CPDF_Document* dest_doc,
CPDF_Document* src_doc,
int page_index,
float opacity) {
RetainPtr<CPDF_Dictionary> src_page_dict =
src_doc->GetMutablePageDictionary(page_index);
// The source is only read (rule reads-dont-write): a mutable getter would
// copy a layer's page into the layer.
RetainPtr<const CPDF_Dictionary> src_page_dict =
src_doc->GetPageDictionary(page_index);
if (!src_page_dict) {
return nullptr;
}
Expand Down Expand Up @@ -3586,8 +3588,9 @@ static RetainPtr<CPDF_Stream> ImportPageAsDrawing(CPDF_Document* dest_doc,
return nullptr;
}

// Build a Form XObject stream in the source document so that
// AnnotAppearanceExporter can deep-clone it with all resource dependencies.
// A Form XObject over the page's content and resources, which
// AnnotAppearanceExporter deep-clones with everything they refer to. It is
// no object of the source: adding it would write there.
auto xobj_dict = pdfium::MakeRetain<CPDF_Dictionary>();
xobj_dict->SetNewFor<CPDF_Name>(pdfium::annotation::kType, "XObject");
xobj_dict->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype, "Form");
Expand All @@ -3599,16 +3602,12 @@ static RetainPtr<CPDF_Stream> ImportPageAsDrawing(CPDF_Document* dest_doc,
xobj_dict->SetFor("Resources", src_resources->Clone());
}

auto src_stream = src_doc->NewIndirect<CPDF_Stream>(std::move(xobj_dict));
auto src_stream = pdfium::MakeRetain<CPDF_Stream>(std::move(xobj_dict));
src_stream->SetData(content_data);

// Clone the stream (and all its resource references) into dest_doc.
AnnotAppearanceExporter exporter(dest_doc, src_doc);
RetainPtr<CPDF_Stream> cloned_stream = exporter.ExportFormXObject(src_stream);

// Clean up temporary object from source document.
src_doc->DeleteIndirectObject(src_stream->GetObjNum());

if (!cloned_stream) {
return nullptr;
}
Expand Down
Loading