Skip to content

Commit

Permalink
Add single unique font lookup method to DWriteFontProxy
Browse files Browse the repository at this point in the history
For unique local font matching, on Windows 10, we do not need to scan
fonts and build a lookup structure but instead we can perform lookups
directly against IDWriteFontSet using GetMatchingFonts() API. This API
is only available where IDWriteFactory3 is available, which means on
Windows 10.

Add a method to DWriteFontProxy to perform a single font lookup using
this API. Also add a method to dermine which mode should be used on the
current machine: single lookups or retrieving the shared-memory lookup
table that we built for Windows 7 and 8.

See the design document [1] for more details.

[1] https://docs.google.com/document/d/1yCZwVIF39S8WOgCUraT5OuUUaLSqWrxoG3mqdtnHnhs/edit#heading=h.s74lqnekmr53

Bug: 951978
Change-Id: I833139238a47ba9e72f643656a75c2e78c069343
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1565310
Commit-Queue: Dominik Röttsches <drott@chromium.org>
Reviewed-by: Ben Wagner <bungeman@google.com>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#650459}
  • Loading branch information
drott authored and Commit Bot committed Apr 12, 2019
1 parent 5eb875f commit 1163828
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 0 deletions.
9 changes: 9 additions & 0 deletions content/browser/renderer_host/dwrite_font_file_util_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/trace_event/trace_event.h"
#include "content/browser/renderer_host/dwrite_font_uma_logging_win.h"

namespace content {
Expand All @@ -33,8 +34,16 @@ bool FontFilePathAndTtcIndex(IDWriteFont* font,
MessageFilterError::ADD_FILES_FOR_FONT_CREATE_FACE_FAILED);
return false;
}
return FontFilePathAndTtcIndex(font_face.Get(), file_path, ttc_index);
}

bool FontFilePathAndTtcIndex(IDWriteFontFace* font_face,
base::string16& file_path,
uint32_t& ttc_index) {
TRACE_EVENT0("dwrite,fonts",
"dwrite_font_file_util::FontFilePathAndTtcIndex");
UINT32 file_count;
HRESULT hr;
hr = font_face->GetFiles(&file_count, nullptr);
if (FAILED(hr)) {
LogMessageFilterError(
Expand Down
4 changes: 4 additions & 0 deletions content/browser/renderer_host/dwrite_font_file_util_win.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <dwrite.h>
#include <dwrite_2.h>
#include <dwrite_3.h>
#include <set>

#include "base/location.h"
Expand All @@ -16,6 +17,9 @@ namespace content {

namespace dwrite_font_file_util {

bool FontFilePathAndTtcIndex(IDWriteFontFace* font,
base::string16& file_path,
uint32_t& ttc_index);
bool FontFilePathAndTtcIndex(IDWriteFont* font,
base::string16& file_path,
uint32_t& ttc_index);
Expand Down
93 changes: 93 additions & 0 deletions content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,94 @@ void DWriteFontProxyImpl::GetUniqueNameLookupTableIfAvailable(
DWriteFontLookupTableBuilder::GetInstance()->DuplicateMemoryRegion());
}

void DWriteFontProxyImpl::MatchUniqueFont(
const base::string16& unique_font_name,
MatchUniqueFontCallback callback) {
TRACE_EVENT0("dwrite,fonts", "DWriteFontProxyImpl::MatchUniqueFont");

DCHECK(base::FeatureList::IsEnabled(features::kFontSrcLocalMatching));
callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback),
base::FilePath(), 0);
InitializeDirectWrite();

// We must not get here if this version of DWrite can't handle performing the
// search.
DCHECK(factory3_.Get());
mswr::ComPtr<IDWriteFontSet> system_font_set;
HRESULT hr = factory3_->GetSystemFontSet(&system_font_set);
if (FAILED(hr))
return;

DCHECK(system_font_set->GetFontCount() > 0);

mswr::ComPtr<IDWriteFontSet> filtered_set;

auto filter_set = [&system_font_set, &filtered_set,
&unique_font_name](DWRITE_FONT_PROPERTY_ID property_id) {
TRACE_EVENT0("dwrite,fonts",
"DWriteFontProxyImpl::MatchUniqueFont::filter_set");
std::wstring unique_font_name_wide =
base::UTF16ToWide(unique_font_name).c_str();
DWRITE_FONT_PROPERTY search_property = {property_id,
unique_font_name_wide.c_str(), L""};
// GetMatchingFonts() matches all languages according to:
// https://docs.microsoft.com/en-us/windows/desktop/api/dwrite_3/ns-dwrite_3-dwrite_font_property
HRESULT hr =
system_font_set->GetMatchingFonts(&search_property, 1, &filtered_set);
return SUCCEEDED(hr);
};

// Search PostScript name first, otherwise try searching for full font name.
// Return if filtering failed.
if (!filter_set(DWRITE_FONT_PROPERTY_ID_POSTSCRIPT_NAME))
return;

if (!filtered_set->GetFontCount() &&
!filter_set(DWRITE_FONT_PROPERTY_ID_FULL_NAME)) {
return;
}

if (!filtered_set->GetFontCount())
return;

mswr::ComPtr<IDWriteFontFaceReference> first_font;
hr = filtered_set->GetFontFaceReference(0, &first_font);
if (FAILED(hr))
return;

mswr::ComPtr<IDWriteFontFace3> first_font_face_3;
hr = first_font->CreateFontFace(&first_font_face_3);
if (FAILED(hr))
return;

mswr::ComPtr<IDWriteFontFace> first_font_face;
hr = first_font_face_3.As<IDWriteFontFace>(&first_font_face);
if (FAILED(hr))
return;

base::string16 font_file_pathname;
uint32_t ttc_index;
bool result = FontFilePathAndTtcIndex(first_font_face.Get(),
font_file_pathname, ttc_index);
if (!result)
return;

base::FilePath path(base::UTF16ToWide(font_file_pathname));
std::move(callback).Run(path, ttc_index);
}

void DWriteFontProxyImpl::GetUniqueFontLookupMode(
GetUniqueFontLookupModeCallback callback) {
InitializeDirectWrite();
// If factory3_ is available, that means we can use IDWriteFontSet to filter
// for PostScript name and full font name directly and do not need to build
// the lookup table.
blink::mojom::UniqueFontLookupMode lookup_mode =
factory3_.Get() ? blink::mojom::UniqueFontLookupMode::kSingleLookups
: blink::mojom::UniqueFontLookupMode::kRetrieveTable;
std::move(callback).Run(lookup_mode);
}

void DWriteFontProxyImpl::GetUniqueNameLookupTable(
GetUniqueNameLookupTableCallback callback) {
DCHECK(base::FeatureList::IsEnabled(features::kFontSrcLocalMatching));
Expand Down Expand Up @@ -444,6 +532,11 @@ void DWriteFontProxyImpl::InitializeDirectWrite() {
// running an older version of DirectWrite (earlier than Win8.1).
factory.As<IDWriteFactory2>(&factory2_);

// QueryInterface for IDwriteFactory3, needed for MatchUniqueFont on Windows
// 10. May fail on older versions, in which case, unique font matching must be
// done through indexing system fonts using DWriteFontLookupTableBuilder.
factory.As<IDWriteFactory3>(&factory3_);

HRESULT hr = factory->GetSystemFontCollection(&collection_);
DCHECK(SUCCEEDED(hr));

Expand Down
6 changes: 6 additions & 0 deletions content/browser/renderer_host/dwrite_font_proxy_impl_win.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <dwrite.h>
#include <dwrite_2.h>
#include <dwrite_3.h>
#include <wrl.h>
#include <set>
#include <utility>
Expand Down Expand Up @@ -58,6 +59,10 @@ class CONTENT_EXPORT DWriteFontProxyImpl
uint32_t reading_direction,
const base::string16& base_family_name,
MapCharactersCallback callback) override;
void MatchUniqueFont(const base::string16& unique_font_name,
MatchUniqueFontCallback callback) override;
void GetUniqueFontLookupMode(
GetUniqueFontLookupModeCallback callback) override;

void GetUniqueNameLookupTableIfAvailable(
GetUniqueNameLookupTableIfAvailableCallback callback) override;
Expand All @@ -74,6 +79,7 @@ class CONTENT_EXPORT DWriteFontProxyImpl
bool direct_write_initialized_ = false;
Microsoft::WRL::ComPtr<IDWriteFontCollection> collection_;
Microsoft::WRL::ComPtr<IDWriteFactory2> factory2_;
Microsoft::WRL::ComPtr<IDWriteFactory3> factory3_;
Microsoft::WRL::ComPtr<IDWriteFontFallback> font_fallback_;
base::string16 windows_fonts_path_;
base::MappedReadOnlyRegion font_unique_name_table_memory_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@

#include <memory>

#include "base/file_version_info.h"
#include "base/files/file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
Expand Down Expand Up @@ -69,6 +71,12 @@ class DWriteFontProxyUniqueNameMatchingTest
table_builder_instance->SchedulePrepareFontUniqueNameTable();
}

bool SupportsSingleLookups() {
blink::mojom::UniqueFontLookupMode lookup_mode;
dwrite_font_proxy().GetUniqueFontLookupMode(&lookup_mode);
return lookup_mode == blink::mojom::UniqueFontLookupMode::kSingleLookups;
}

private:
base::test::ScopedFeatureList feature_list_;
base::ScopedTempDir scoped_temp_dir_;
Expand Down Expand Up @@ -247,6 +255,62 @@ TEST_F(DWriteFontProxyUniqueNameMatchingTest, TestFindUniqueFont) {
ASSERT_TRUE(lookup_table_results_were_tested);
}

TEST_F(DWriteFontProxyUniqueNameMatchingTest, TestSingleLookup) {
// Do not run this test on unsupported Windows versions.
if (!SupportsSingleLookups())
return;
for (auto& test_font_name_index : kExpectedTestFonts) {
base::FilePath result_path;
uint32_t ttc_index;
dwrite_font_proxy().MatchUniqueFont(
base::UTF8ToUTF16(test_font_name_index.font_name), &result_path,
&ttc_index);
ASSERT_GT(result_path.value().size(), 0u);
base::File unique_font_file(result_path,
base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_TRUE(unique_font_file.IsValid());
ASSERT_GT(unique_font_file.GetLength(), 0);
ASSERT_EQ(test_font_name_index.ttc_index, ttc_index);
}
}

TEST_F(DWriteFontProxyUniqueNameMatchingTest, TestSingleLookupUnavailable) {
// Do not run this test on unsupported Windows versions.
if (!SupportsSingleLookups())
return;
base::FilePath result_path;
uint32_t ttc_index;
std::string unavailable_font_name =
"Unavailable_Font_Name_56E7EA7E-2C69-4E23-99DC-750BC19B250E";
dwrite_font_proxy().MatchUniqueFont(base::UTF8ToUTF16(unavailable_font_name),
&result_path, &ttc_index);
ASSERT_EQ(result_path.value().size(), 0u);
ASSERT_EQ(ttc_index, 0u);
}

TEST_F(DWriteFontProxyUniqueNameMatchingTest, TestLookupMode) {
std::unique_ptr<FileVersionInfo> dwrite_version_info =
FileVersionInfo::CreateFileVersionInfo(
base::FilePath(FILE_PATH_LITERAL("DWrite.dll")));

std::string dwrite_version =
base::WideToUTF8(dwrite_version_info->product_version());

int dwrite_major_version_number =
std::stoi(dwrite_version.substr(0, dwrite_version.find(".")));

blink::mojom::UniqueFontLookupMode expected_lookup_mode;
if (dwrite_major_version_number >= 10) {
expected_lookup_mode = blink::mojom::UniqueFontLookupMode::kSingleLookups;
} else {
expected_lookup_mode = blink::mojom::UniqueFontLookupMode::kRetrieveTable;
}

blink::mojom::UniqueFontLookupMode lookup_mode;
dwrite_font_proxy().GetUniqueFontLookupMode(&lookup_mode);
ASSERT_EQ(lookup_mode, expected_lookup_mode);
}

} // namespace

} // namespace content
6 changes: 6 additions & 0 deletions content/test/dwrite_font_fake_sender_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ void FakeFontCollection::MapCharacters(
DWRITE_FONT_STRETCH_NORMAL)));
}

void FakeFontCollection::MatchUniqueFont(const base::string16& unique_font_name,
MatchUniqueFontCallback callback) {}

void FakeFontCollection::GetUniqueFontLookupMode(
GetUniqueFontLookupModeCallback callback) {}

void FakeFontCollection::GetUniqueNameLookupTable(
GetUniqueNameLookupTableCallback callback) {}

Expand Down
4 changes: 4 additions & 0 deletions content/test/dwrite_font_fake_sender_win.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ class FakeFontCollection : public blink::mojom::DWriteFontProxy {
uint32_t reading_direction,
const base::string16& base_family_name,
MapCharactersCallback callback) override;
void MatchUniqueFont(const base::string16& unique_font_name,
MatchUniqueFontCallback callback) override;
void GetUniqueFontLookupMode(
GetUniqueFontLookupModeCallback callback) override;
void GetUniqueNameLookupTableIfAvailable(
GetUniqueNameLookupTableIfAvailableCallback callback) override;
void GetUniqueNameLookupTable(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ struct MapCharactersResult {
DWriteFontStyle font_style;
};

enum UniqueFontLookupMode {
kRetrieveTable,
kSingleLookups
};

interface DWriteFontProxy {
// Locates the index of the specified font family within the system
// collection.
Expand All @@ -51,6 +56,26 @@ interface DWriteFontProxy {
=> (array<mojo_base.mojom.FilePath> file_paths,
array<mojo_base.mojom.File> file_handles);

// Returns which font unique name matching lookup mode is to be used on the
// current machine. On DirectWrite 10 and above, single lookups can be
// performed directly against DirectWrite API. On older DirectWrite (Windows
// 7-8.1), unique font lookups need to be performed against a shared memory
// region which contains the lookup table. Compare GetUniqueFontLookupTable()
// for lookup mode kRetrieveTable and MatchUniqueFont for
// lookup mode kSingleLookups.
[Sync]
GetUniqueFontLookupMode() => (UniqueFontLookupMode lookup_mode);

// On supported Windows versions, matches a unique PostScript or full font
// name against the installed fonts using DirectWrite API. Returns a file path
// and ttc_index from which the unique font can be instantiated. Check which
// mode is supported using GetFontUniqueNameLookupMode(). Returns empty path
// and 0 ttc index if no font is found. Must not be called if
// GetUniqueFontLookupMode() returned kRetrieveTable.
[Sync]
MatchUniqueFont(mojo_base.mojom.String16 font_unique_name)
=> (mojo_base.mojom.FilePath file_path, uint32 ttc_index);

// Synchronously returns a protobuf structured lookup list of
// (full_font_name|postscript_name) => (font_file + ttc_index) to the
// renderer process as a ReadOnlySharedMemoryRegion if it is available
Expand Down

0 comments on commit 1163828

Please sign in to comment.