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
15 changes: 15 additions & 0 deletions core/libs/prx/libSceFreeType/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_library(libSceFreeType SHARED EXCLUDE_FROM_ALL
Export.cpp
src/GuestObjects.cpp
src/Names.cpp
)
target_include_directories(libSceFreeType PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceFreeType PRIVATE freetype)
Expand All @@ -20,7 +21,21 @@ if(BUILD_TESTING)
target_link_libraries(guest_freetype_charmap_tests PRIVATE libSceFreeType freetype libc)
configure_windows_unwind(guest_freetype_charmap_tests)
add_test(NAME guest_freetype_charmap COMMAND guest_freetype_charmap_tests)
add_test_executable(guest_freetype_names_tests ${LIBS_INCLUDE_DIR}/tests/GuestFreeTypeNames.cpp)
target_include_directories(guest_freetype_names_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_freetype_names_tests PRIVATE libSceFreeType freetype libc)
configure_windows_unwind(guest_freetype_names_tests)
add_test(NAME guest_freetype_names COMMAND guest_freetype_names_tests)
add_test_executable(guest_freetype_sfnt_names_tests ${LIBS_INCLUDE_DIR}/tests/GuestFreeTypeSfntNames.cpp)
target_include_directories(guest_freetype_sfnt_names_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_freetype_sfnt_names_tests PRIVATE libSceFreeType freetype libc)
configure_windows_unwind(guest_freetype_sfnt_names_tests)
add_test(NAME guest_freetype_sfnt_names COMMAND guest_freetype_sfnt_names_tests)
if(WIN32)
set_tests_properties(guest_freetype_sfnt_names PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeType>")
set_tests_properties(guest_freetype_names PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeType>")
set_tests_properties(guest_freetype PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeType>")
set_tests_properties(guest_freetype_charmap PROPERTIES ENVIRONMENT_MODIFICATION
Expand Down
10 changes: 10 additions & 0 deletions core/libs/prx/libSceFreeType/include/GuestFreeType.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ struct GuestCharMapRec {
std::uint16_t encoding_id;
};

struct GuestSfntName {
std::uint16_t platform_id;
std::uint16_t encoding_id;
std::uint16_t language_id;
std::uint16_t name_id;
unsigned char* string;
std::uint32_t string_len;
};

struct GuestSizeMetrics {
std::uint16_t x_ppem;
std::uint16_t y_ppem;
Expand Down Expand Up @@ -152,6 +161,7 @@ struct GuestFaceRec {
static_assert(sizeof(GuestGeneric) == 16 && offsetof(GuestGeneric, finalizer) == 8);
static_assert(sizeof(GuestBitmapSize) == 32 && offsetof(GuestBitmapSize, size) == 8);
static_assert(sizeof(GuestCharMapRec) == 16 && offsetof(GuestCharMapRec, platform_id) == 12);
static_assert(sizeof(GuestSfntName) == 24 && offsetof(GuestSfntName, string) == 8 && offsetof(GuestSfntName, string_len) == 16);
static_assert(sizeof(GuestSizeMetrics) == 56 && offsetof(GuestSizeMetrics, x_scale) == 8);
static_assert(sizeof(GuestSizeRec) == 88 && offsetof(GuestSizeRec, metrics) == 24);
static_assert(sizeof(GuestBitmap) == 40 && offsetof(GuestBitmap, buffer) == 16 && offsetof(GuestBitmap, palette) == 32);
Expand Down
55 changes: 55 additions & 0 deletions core/libs/prx/libSceFreeType/src/Names.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "prx/libSceFreeType/include/GuestObjects.hpp"
#include "prx/libc/include/GuestArena.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"

#include FT_SFNT_NAMES_H

#include <cstdint>

using namespace FreeType;

extern "C" {

int APS5_VABI FT_Get_Glyph_Name_nid_postfix(GuestFaceRec* face, std::uint32_t glyphIndex, void* buffer, std::uint32_t bufferSize) {
const GuestFace* owner = FaceOf(face);
const FT_Face host = owner ? owner->host : nullptr;
if (!host || !buffer || bufferSize == 0) return FT_Get_Glyph_Name(host, glyphIndex, buffer, bufferSize);
GuestArena::HostWrite write(buffer, bufferSize);
if (static_cast<std::int64_t>(glyphIndex) >= host->num_glyphs) {
static_cast<char*>(buffer)[0] = '\0';
return FT_Err_Invalid_Glyph_Index;
}
return FT_Get_Glyph_Name(host, glyphIndex, buffer, bufferSize);
}

std::uint32_t APS5_VABI FT_Get_Name_Index_nid_postfix(GuestFaceRec* face, const char* glyphName) {
const GuestFace* owner = FaceOf(face);
return FT_Get_Name_Index(owner ? owner->host : nullptr, glyphName);
}

const char* APS5_VABI FT_Get_Postscript_Name_nid_postfix(GuestFaceRec* face) {
const GuestFace* owner = FaceOf(face);
return FT_Get_Postscript_Name(owner ? owner->host : nullptr);
}

std::uint32_t APS5_VABI FT_Get_Sfnt_Name_Count_nid_postfix(GuestFaceRec* face) {
const GuestFace* owner = FaceOf(face);
return FT_Get_Sfnt_Name_Count(owner ? owner->host : nullptr);
}

int APS5_VABI FT_Get_Sfnt_Name_nid_postfix(GuestFaceRec* face, std::uint32_t index, GuestSfntName* name) {
const GuestFace* owner = FaceOf(face);
FT_SfntName native{};
const FT_Error error = FT_Get_Sfnt_Name(owner ? owner->host : nullptr, index, name ? &native : nullptr);
if (!error) {
name->platform_id = native.platform_id;
name->encoding_id = native.encoding_id;
name->language_id = native.language_id;
name->name_id = native.name_id;
name->string = native.string;
name->string_len = native.string_len;
}
return error;
}

}
128 changes: 128 additions & 0 deletions core/libs/tests/GuestFreeTypeNames.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#include "FontFixture.hpp"
#include "prx/libSceFreeType/include/GuestObjects.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"

#include <array>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <utility>
#include <vector>

using namespace FreeType;

extern "C" {
int APS5_VABI FT_Get_Glyph_Name_nid_postfix(GuestFaceRec*, std::uint32_t, void*, std::uint32_t);
std::uint32_t APS5_VABI FT_Get_Name_Index_nid_postfix(GuestFaceRec*, const char*);
const char* APS5_VABI FT_Get_Postscript_Name_nid_postfix(GuestFaceRec*);
}

static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "FreeType names check failed at line %d\n", line);
std::abort();
}
}
#define Require(value) Check((value), __LINE__)

static std::vector<unsigned char> NamedFont(const std::string& postscript, const std::string& first, const std::string& second) {
std::vector<unsigned char> post(32, 0);
post[1] = 2;
for (const int value : {3, 0, 258, 259}) FontFixture::Put16(post, value);
for (const auto* name : {&first, &second}) {
post.push_back(static_cast<unsigned char>(name->size()));
post.insert(post.end(), name->begin(), name->end());
}
auto names = FontFixture::Words({0, 1, 18, 3, 1, 0x0409, 6, static_cast<int>(postscript.size() * 2), 0});
for (const unsigned char ch : postscript) FontFixture::Put16(names, ch);
return FontFixture::BuildFont(3, {{"post", post}, {"name", names}});
}

struct FacePair {
std::vector<unsigned char> bytes;
GuestFace guest;
FT_Face oracle = nullptr;

FacePair(GuestLibrary& library, FT_Library nativeLibrary, std::vector<unsigned char> font) : bytes(std::move(font)) {
guest.library = &library;
Require(FT_New_Memory_Face(library.host, bytes.data(), static_cast<FT_Long>(bytes.size()), 0, &guest.host) == 0);
Require(FT_New_Memory_Face(nativeLibrary, bytes.data(), static_cast<FT_Long>(bytes.size()), 0, &oracle) == 0);
SyncFace(guest);
}

~FacePair() {
Require(FT_Done_Face(guest.host) == 0);
Require(FT_Done_Face(oracle) == 0);
}
};

static void CompareGlyph(GuestFaceRec* guest, FT_Face native, std::uint32_t index, std::uint32_t size) {
std::array<char, 32> expected;
expected.fill('?');
auto actual = expected;
const FT_Error error = FT_Get_Glyph_Name(native, index, expected.data() + 3, size);
Require(FT_Get_Glyph_Name_nid_postfix(guest, index, actual.data() + 3, size) == error);
Require(actual == expected);
}

static void CheckFace(FacePair& face, bool names) {
Require((FT_HAS_GLYPH_NAMES(face.oracle) != 0) == names);
for (const std::uint32_t index : {0u, 1u, 2u, 3u, 0x7FFFFFFFu}) {
for (const std::uint32_t size : {0u, 1u, 2u, 5u, 24u}) CompareGlyph(&face.guest.rec, face.oracle, index, size);
Require(FT_Get_Glyph_Name_nid_postfix(&face.guest.rec, index, nullptr, 24) == FT_Get_Glyph_Name(face.oracle, index, nullptr, 24));
}
for (const std::uint32_t index : {0x80000000u, 0xFFFFFFFFu}) {
for (const std::uint32_t size : {0u, 1u, 24u}) {
std::array<char, 32> expected;
expected.fill('?');
auto actual = expected;
if (size) expected[3] = '\0';
Require(FT_Get_Glyph_Name_nid_postfix(&face.guest.rec, index, actual.data() + 3, size) ==
(size ? FT_Err_Invalid_Glyph_Index : FT_Err_Invalid_Argument));
Require(actual == expected);
}
}
for (const char* name : {".notdef", "alpha.custom", "beta", "gamma.custom", "delta", "ALPHA.CUSTOM", "missing", ""}) {
Require(FT_Get_Name_Index_nid_postfix(&face.guest.rec, name) == FT_Get_Name_Index(face.oracle, name));
}
Require(FT_Get_Name_Index_nid_postfix(&face.guest.rec, nullptr) == 0);
const char* actual = FT_Get_Postscript_Name_nid_postfix(&face.guest.rec);
const char* expected = FT_Get_Postscript_Name(face.oracle);
Require((actual == nullptr) == (expected == nullptr));
Require(!actual || std::strcmp(actual, expected) == 0);
Require(actual == FT_Get_Postscript_Name(face.guest.host));
}

int main() {
GuestLibrary library{};
FT_Library nativeLibrary = nullptr;
Require(FT_Init_FreeType(&library.host) == 0);
Require(FT_Init_FreeType(&nativeLibrary) == 0);
{
FacePair first(library, nativeLibrary, NamedFont("NameFixture-A", "alpha.custom", "beta"));
FacePair second(library, nativeLibrary, NamedFont("NameFixture-B", "gamma.custom", "delta"));
std::vector<unsigned char> post(32, 0);
post[1] = 3;
FacePair unnamed(library, nativeLibrary, FontFixture::BuildFont(3, {{"post", post}}));
CheckFace(first, true);
Require(FT_Get_Name_Index_nid_postfix(&first.guest.rec, "alpha.custom") == 1);
Require(FT_Get_Name_Index_nid_postfix(&first.guest.rec, "beta") == 2);
const char* retained = FT_Get_Postscript_Name_nid_postfix(&first.guest.rec);
Require(retained && std::strcmp(retained, "NameFixture-A") == 0);
CheckFace(second, true);
Require(FT_Get_Name_Index_nid_postfix(&second.guest.rec, "gamma.custom") == 1);
Require(FT_Get_Name_Index_nid_postfix(&second.guest.rec, "delta") == 2);
CheckFace(unnamed, false);
Require(FT_Get_Postscript_Name_nid_postfix(&unnamed.guest.rec) == nullptr);
Require(FT_Get_Postscript_Name_nid_postfix(&first.guest.rec) == retained && std::strcmp(retained, "NameFixture-A") == 0);
for (const std::uint32_t size : {0u, 1u, 24u}) CompareGlyph(nullptr, nullptr, 0, size);
Require(FT_Get_Glyph_Name_nid_postfix(nullptr, 0, nullptr, 0) == FT_Err_Invalid_Face_Handle);
Require(FT_Get_Name_Index_nid_postfix(nullptr, "alpha.custom") == 0);
Require(FT_Get_Name_Index_nid_postfix(nullptr, nullptr) == 0);
Require(FT_Get_Postscript_Name_nid_postfix(nullptr) == nullptr);
}
Require(FT_Done_FreeType(library.host) == 0);
Require(FT_Done_FreeType(nativeLibrary) == 0);
}
144 changes: 144 additions & 0 deletions core/libs/tests/GuestFreeTypeSfntNames.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#include "FontFixture.hpp"
#include "prx/libSceFreeType/include/GuestObjects.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"

#include FT_SFNT_NAMES_H

#include <array>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <utility>
#include <vector>

using namespace FreeType;

extern "C" {
std::uint32_t APS5_VABI FT_Get_Sfnt_Name_Count_nid_postfix(GuestFaceRec*);
int APS5_VABI FT_Get_Sfnt_Name_nid_postfix(GuestFaceRec*, std::uint32_t, GuestSfntName*);
}

static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "FreeType SFNT names check failed at line %d\n", line);
std::abort();
}
}
#define Require(value) Check((value), __LINE__)

struct NameRecord {
std::uint16_t platform;
std::uint16_t encoding;
std::uint16_t language;
std::uint16_t name;
std::vector<unsigned char> bytes;
};

static std::vector<unsigned char> NamedFont(const std::vector<NameRecord>& records) {
auto table = FontFixture::Words({0, static_cast<int>(records.size()), 6 + 12 * static_cast<int>(records.size())});
int offset = 0;
for (const auto& record : records) {
for (const int value : {static_cast<int>(record.platform), static_cast<int>(record.encoding), static_cast<int>(record.language),
static_cast<int>(record.name), static_cast<int>(record.bytes.size()), offset}) FontFixture::Put16(table, value);
offset += static_cast<int>(record.bytes.size());
}
for (const auto& record : records) table.insert(table.end(), record.bytes.begin(), record.bytes.end());
return FontFixture::BuildFont(3, {{"name", table}});
}

struct FacePair {
std::vector<unsigned char> bytes;
GuestFace guest;
FT_Face oracle = nullptr;

FacePair(GuestLibrary& library, FT_Library nativeLibrary, std::vector<unsigned char> font) : bytes(std::move(font)) {
guest.library = &library;
Require(FT_New_Memory_Face(library.host, bytes.data(), static_cast<FT_Long>(bytes.size()), 0, &guest.host) == 0);
Require(FT_New_Memory_Face(nativeLibrary, bytes.data(), static_cast<FT_Long>(bytes.size()), 0, &oracle) == 0);
SyncFace(guest);
}

~FacePair() {
Require(FT_Done_Face(guest.host) == 0);
Require(FT_Done_Face(oracle) == 0);
}
};

static GuestSfntName Query(FacePair& face, std::uint32_t index, const NameRecord& record) {
struct GuardedName {
std::array<unsigned char, 8> before;
GuestSfntName value;
std::array<unsigned char, 8> after;
} actual;
std::memset(&actual, 0xA5, sizeof(actual));
FT_SfntName expected{};
Require(FT_Get_Sfnt_Name(face.oracle, index, &expected) == 0);
Require(FT_Get_Sfnt_Name_nid_postfix(&face.guest.rec, index, &actual.value) == 0);
for (const auto byte : actual.before) Require(byte == 0xA5);
for (const auto byte : actual.after) Require(byte == 0xA5);
const auto& name = actual.value;
Require(name.platform_id == expected.platform_id && name.platform_id == record.platform);
Require(name.encoding_id == expected.encoding_id && name.encoding_id == record.encoding);
Require(name.language_id == expected.language_id && name.language_id == record.language);
Require(name.name_id == expected.name_id && name.name_id == record.name);
Require(name.string_len == expected.string_len && name.string_len == record.bytes.size());
Require(std::memcmp(name.string, expected.string, name.string_len) == 0);
Require(std::memcmp(name.string, record.bytes.data(), name.string_len) == 0);
FT_SfntName native{};
Require(FT_Get_Sfnt_Name(face.guest.host, index, &native) == 0 && name.string == native.string);
return name;
}

static void InvalidQuery(GuestFaceRec* guest, FT_Face native, std::uint32_t index) {
GuestSfntName actual;
std::memset(&actual, 0xA5, sizeof(actual));
std::array<unsigned char, sizeof(actual)> before;
std::memcpy(before.data(), &actual, sizeof(actual));
FT_SfntName expected{};
const FT_Error error = FT_Get_Sfnt_Name(native, index, &expected);
Require(error != 0);
Require(FT_Get_Sfnt_Name_nid_postfix(guest, index, &actual) == error);
Require(std::memcmp(&actual, before.data(), sizeof(actual)) == 0);
Require(FT_Get_Sfnt_Name_nid_postfix(guest, index, nullptr) == FT_Get_Sfnt_Name(native, index, nullptr));
}

int main() {
GuestLibrary library{};
FT_Library nativeLibrary = nullptr;
Require(FT_Init_FreeType(&library.host) == 0);
Require(FT_Init_FreeType(&nativeLibrary) == 0);
{
const std::vector<NameRecord> records = {
{1, 0, 0, 1, {'F', 'a', 'm', 'i', 'l', 'y'}},
{3, 1, 0x0409, 6, {0, 'S', 0, 'f', 0, 'n', 0, 't'}},
{3, 10, 0x0411, 256, {0, 'A', 0, 0, 0, 'B'}}
};
FacePair first(library, nativeLibrary, NamedFont(records));
FacePair empty(library, nativeLibrary, FontFixture::BuildFont(3, {}));
Require(FT_Get_Sfnt_Name_Count_nid_postfix(&first.guest.rec) == records.size());
Require(FT_Get_Sfnt_Name_Count_nid_postfix(&first.guest.rec) == FT_Get_Sfnt_Name_Count(first.oracle));
std::vector<GuestSfntName> retained;
for (std::uint32_t index = 0; index < records.size(); ++index) retained.push_back(Query(first, index, records[index]));
{
const std::vector<NameRecord> other = {{3, 1, 0x0409, 6, {0, 'O', 0, 't', 0, 'h', 0, 'e', 0, 'r'}}};
FacePair second(library, nativeLibrary, NamedFont(other));
Require(FT_Get_Sfnt_Name_Count_nid_postfix(&second.guest.rec) == other.size());
Query(second, 0, other[0]);
}
for (std::uint32_t index = 0; index < records.size(); ++index) {
Require(Query(first, index, records[index]).string == retained[index].string);
Require(std::memcmp(retained[index].string, records[index].bytes.data(), retained[index].string_len) == 0);
Require(FT_Get_Sfnt_Name_nid_postfix(&first.guest.rec, index, nullptr) == FT_Get_Sfnt_Name(first.oracle, index, nullptr));
}
Require(FT_Get_Sfnt_Name_Count_nid_postfix(&empty.guest.rec) == 0);
Require(FT_Get_Sfnt_Name_Count_nid_postfix(nullptr) == 0);
for (const std::uint32_t index : {0u, 3u, 0x80000000u, 0xFFFFFFFFu}) {
if (index >= records.size()) InvalidQuery(&first.guest.rec, first.oracle, index);
InvalidQuery(&empty.guest.rec, empty.oracle, index);
InvalidQuery(nullptr, nullptr, index);
}
}
Require(FT_Done_FreeType(library.host) == 0);
Require(FT_Done_FreeType(nativeLibrary) == 0);
}
Loading
Loading