Add API to set document language in PDF document Add experimental API FPDFCatalog_SetLanguage() that allows the user to set the /Lang entry in a PDF document's catalog. Make CPDF_Document::SetRootForTesting() public so that it can be used in new embedder tests without CPDF_TestDocument. Bug: 42270891 Change-Id: I1ea20bf80f9045bd9fd34e55fcaf58cf82023d3c Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/123112 Commit-Queue: Andy Phan <andyphan@chromium.org> Reviewed-by: Tom Sepez <tsepez@chromium.org> Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/parser/cpdf_document.h b/core/fpdfapi/parser/cpdf_document.h index a9a2922..4ddd6da 100644 --- a/core/fpdfapi/parser/cpdf_document.h +++ b/core/fpdfapi/parser/cpdf_document.h
@@ -161,10 +161,11 @@ void IncrementParsedPageCount() { ++m_ParsedPageCount; } uint32_t GetParsedPageCountForTesting() { return m_ParsedPageCount; } + void SetRootForTesting(RetainPtr<CPDF_Dictionary> root); + protected: void SetParser(std::unique_ptr<CPDF_Parser> pParser); - void SetRootForTesting(RetainPtr<CPDF_Dictionary> root); void ResizePageListForTesting(size_t size); private:
diff --git a/fpdfsdk/BUILD.gn b/fpdfsdk/BUILD.gn index b18ca03..abf055f 100644 --- a/fpdfsdk/BUILD.gn +++ b/fpdfsdk/BUILD.gn
@@ -127,6 +127,7 @@ "cpdfsdk_baannot_embeddertest.cpp", "fpdf_annot_embeddertest.cpp", "fpdf_attachment_embeddertest.cpp", + "fpdf_catalog_embeddertest.cpp", "fpdf_dataavail_embeddertest.cpp", "fpdf_doc_embeddertest.cpp", "fpdf_edit_embeddertest.cpp",
diff --git a/fpdfsdk/fpdf_catalog.cpp b/fpdfsdk/fpdf_catalog.cpp index 6147de8..6153346 100644 --- a/fpdfsdk/fpdf_catalog.cpp +++ b/fpdfsdk/fpdf_catalog.cpp
@@ -6,6 +6,8 @@ #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/cpdf_document.h" +#include "core/fpdfapi/parser/cpdf_string.h" +#include "core/fxcrt/retain_ptr.h" #include "fpdfsdk/cpdfsdk_helpers.h" FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV @@ -21,3 +23,23 @@ RetainPtr<const CPDF_Dictionary> pMarkInfo = pCatalog->GetDictFor("MarkInfo"); return pMarkInfo && pMarkInfo->GetIntegerFor("Marked") != 0; } + +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +FPDFCatalog_SetLanguage(FPDF_DOCUMENT document, FPDF_BYTESTRING language) { + if (!language) { + return false; + } + + CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document); + if (!doc) { + return false; + } + + RetainPtr<CPDF_Dictionary> catalog = doc->GetMutableRoot(); + if (!catalog) { + return false; + } + + catalog->SetNewFor<CPDF_String>("Lang", language); + return true; +}
diff --git a/fpdfsdk/fpdf_catalog_embeddertest.cpp b/fpdfsdk/fpdf_catalog_embeddertest.cpp new file mode 100644 index 0000000..ae8e697 --- /dev/null +++ b/fpdfsdk/fpdf_catalog_embeddertest.cpp
@@ -0,0 +1,68 @@ +// Copyright 2024 The PDFium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "public/fpdf_catalog.h" + +#include "core/fpdfapi/parser/cpdf_dictionary.h" +#include "core/fpdfapi/parser/cpdf_document.h" +#include "core/fpdfapi/parser/cpdf_string.h" +#include "fpdfsdk/cpdfsdk_helpers.h" +#include "public/fpdf_edit.h" +#include "testing/embedder_test.h" + +using FPDFCatalogTest = EmbedderTest; + +TEST_F(FPDFCatalogTest, SetLanguageInvalidDocument) { + // Document cannot be nullptr. + EXPECT_FALSE(FPDFCatalog_SetLanguage(nullptr, "en-US")); + + ScopedFPDFDocument doc(FPDF_CreateNewDocument()); + CPDF_Document* cpdf_doc = CPDFDocumentFromFPDFDocument(doc.get()); + + // Language cannot be null. + ASSERT_TRUE(cpdf_doc->GetRoot()); + EXPECT_FALSE(FPDFCatalog_SetLanguage(doc.get(), nullptr)); + + // Catalog cannot be nullptr. + cpdf_doc->SetRootForTesting(nullptr); + EXPECT_FALSE(FPDFCatalog_SetLanguage(doc.get(), "en-US")); +} + +TEST_F(FPDFCatalogTest, SetLanguageNewDocument) { + ScopedFPDFDocument doc(FPDF_CreateNewDocument()); + + const CPDF_Dictionary* catalog = + CPDFDocumentFromFPDFDocument(doc.get())->GetRoot(); + ASSERT_TRUE(catalog); + + // The new document shouldn't have any entry for /Lang. + EXPECT_FALSE(catalog->GetStringFor("Lang")); + + // Add a new entry. + EXPECT_TRUE(FPDFCatalog_SetLanguage(doc.get(), "en-US")); + + RetainPtr<const CPDF_String> result_language = catalog->GetStringFor("Lang"); + ASSERT_TRUE(result_language); + EXPECT_EQ("en-US", result_language->GetString()); +} + +TEST_F(FPDFCatalogTest, SetLanguageExistingDocument) { + ASSERT_TRUE(OpenDocument("tagged_table.pdf")); + + const CPDF_Dictionary* catalog = + CPDFDocumentFromFPDFDocument(document())->GetRoot(); + ASSERT_TRUE(catalog); + + // The PDF already has an existing entry for /Lang. + RetainPtr<const CPDF_String> result_language = catalog->GetStringFor("Lang"); + ASSERT_TRUE(result_language); + EXPECT_EQ("en-US", result_language->GetString()); + + // Replace the existing entry. + EXPECT_TRUE(FPDFCatalog_SetLanguage(document(), "hu")); + + result_language = catalog->GetStringFor("Lang"); + ASSERT_TRUE(result_language); + EXPECT_EQ("hu", result_language->GetString()); +}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c index a440d79..27ad99a 100644 --- a/fpdfsdk/fpdf_view_c_api_test.c +++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -120,6 +120,7 @@ // fpdf_catalog.h CHK(FPDFCatalog_IsTagged); + CHK(FPDFCatalog_SetLanguage); // fpdf_dataavail.h CHK(FPDFAvail_Create);
diff --git a/public/fpdf_catalog.h b/public/fpdf_catalog.h index 20725dc..033cca5 100644 --- a/public/fpdf_catalog.h +++ b/public/fpdf_catalog.h
@@ -25,6 +25,16 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFCatalog_IsTagged(FPDF_DOCUMENT document); +// Experimental API. +// Sets the language of |document| to |language|. +// +// document - handle to a document. +// language - the language to set to. +// +// Returns TRUE on success. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +FPDFCatalog_SetLanguage(FPDF_DOCUMENT document, FPDF_BYTESTRING language); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus