blob: 2a3e818cfd511468f5d914f9d7560998becabc8f [file] [edit]
// Copyright 2026 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/fxge/cfx_cttnametable.h"
#include <stdint.h>
#include <array>
#include "core/fxcrt/widestring.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::ElementsAre;
TEST(CFX_CTTNameTableTest, Empty) {
CFX_CTTNameTable table;
EXPECT_TRUE(table.GetFamilyNames().empty());
CFX_CTTNameTable empty_table(pdfium::span<const uint8_t>{});
EXPECT_TRUE(empty_table.GetFamilyNames().empty());
}
TEST(CFX_CTTNameTableTest, TruncatedHeader) {
static constexpr uint8_t kTruncatedHeader[] = {0x00, 0x00, 0x00, 0x01};
CFX_CTTNameTable table(kTruncatedHeader);
EXPECT_TRUE(table.GetFamilyNames().empty());
}
TEST(CFX_CTTNameTableTest, InvalidStringOffset) {
static constexpr uint8_t kInvalidStringOffset[] = {
0x00, 0x00, // format
0x00, 0x01, // count = 1
0xFF, 0xFF, // string_offset = 65535 (out of bounds)
};
CFX_CTTNameTable table(kInvalidStringOffset);
EXPECT_TRUE(table.GetFamilyNames().empty());
}
TEST(CFX_CTTNameTableTest, AddFamilyName) {
CFX_CTTNameTable table;
table.AddFamilyName(L"First");
table.AddFamilyName(L"Second");
ASSERT_THAT(table.GetFamilyNames(), ElementsAre(L"First", L"Second"));
}
TEST(CFX_CTTNameTableTest, ParseMacintoshRecord) {
// Minimal TT name table with 1 name record for Macintosh (Platform 1).
// Header:
// format: 0 (2 bytes)
// count: 1 (2 bytes)
// string_offset: 18 (2 bytes)
// NameRecord 0 (12 bytes at offset 6):
// platform_id: 1 (2 bytes)
// encoding_id: 0 (2 bytes)
// language_id: 0 (2 bytes)
// name_id: 1 (2 bytes)
// length: 4 (2 bytes)
// offset: 0 (2 bytes)
// String storage (starts at offset 18):
// "Test" (4 bytes, testing exact bounds without trailing padding)
static constexpr uint8_t kTableData[] = {
0x00, 0x00, // format
0x00, 0x01, // count = 1
0x00, 0x12, // string_offset = 18
0x00, 0x01, // platform_id = 1
0x00, 0x00, // encoding_id = 0
0x00, 0x00, // language_id = 0
0x00, 0x01, // name_id = 1
0x00, 0x04, // length = 4
0x00, 0x00, // offset = 0
'T', 'e', 's', 't' // string data
};
CFX_CTTNameTable table(kTableData);
EXPECT_THAT(table.GetFamilyNames(), ElementsAre(L"Test"));
}
TEST(CFX_CTTNameTableTest, ParseWindowsRecord) {
// Minimal TT name table with 1 name record for Windows (Platform 3).
// Uses big-endian UTF-16 encoding.
// Header:
// format: 0 (2 bytes)
// count: 1 (2 bytes)
// string_offset: 18 (2 bytes)
// NameRecord 0 (12 bytes at offset 6):
// platform_id: 3 (2 bytes)
// encoding_id: 1 (2 bytes)
// language_id: 0x0409 (2 bytes)
// name_id: 1 (2 bytes)
// length: 8 (2 bytes)
// offset: 0 (2 bytes)
// String storage (starts at offset 18):
// UTF-16BE "Test" (8 bytes)
static constexpr uint8_t kTableData[] = {
0x00, 0x00, // format
0x00, 0x01, // count = 1
0x00, 0x12, // string_offset = 18
0x00, 0x03, // platform_id = 3 (Windows)
0x00, 0x01, // encoding_id = 1
0x04, 0x09, // language_id = 0x0409
0x00, 0x01, // name_id = 1 (Family name)
0x00, 0x08, // length = 8 bytes
0x00, 0x00, // offset = 0
0x00, 'T', 0x00, 'e', 0x00, 's', 0x00, 't' // string data
};
CFX_CTTNameTable table(kTableData);
ASSERT_EQ(1u, table.GetFamilyNames().size());
EXPECT_EQ(L"Test", table.GetFamilyNames()[0]);
}
TEST(CFX_CTTNameTableTest, ParseNonFamilyNameRecord) {
// Name record with name_id != 1 (e.g. name_id = 2 for Subfamily/Style).
static constexpr uint8_t kTableData[] = {
0x00, 0x00, // format
0x00, 0x01, // count = 1
0x00, 0x12, // string_offset = 18
0x00, 0x03, // platform_id = 3
0x00, 0x01, // encoding_id = 1
0x04, 0x09, // language_id = 0x0409
0x00, 0x02, // name_id = 2 (not family name)
0x00, 0x08, // length = 8
0x00, 0x00, // offset = 0
0x00, 'B', 0x00, 'o', 0x00, 'l', 0x00, 'd' // "Bold"
};
CFX_CTTNameTable table(kTableData);
EXPECT_TRUE(table.GetFamilyNames().empty());
}
TEST(CFX_CTTNameTableTest, ParseOutOfBoundsRecord) {
// Offset points beyond string storage.
static constexpr uint8_t kTableData[] = {
0x00, 0x00, // format
0x00, 0x01, // count = 1
0x00, 0x12, // string_offset = 18
0x00, 0x01, // platform_id = 1
0x00, 0x00, // encoding_id = 0
0x00, 0x00, // language_id = 0
0x00, 0x01, // name_id = 1
0x00, 0x04, // length = 4
0x00, 0x20, // offset = 32 (out of bounds)
'T', 'e', 's', 't' // string data
};
CFX_CTTNameTable table(kTableData);
EXPECT_TRUE(table.GetFamilyNames().empty());
}