I'm trying to load fonts from memory in Direct2D, but keep getting error 0x80070057 ("The parameter is incorrect") from CreateCustomFontFileReference()
.
I'm following the instructions here to support Windows 7/8:
Custom Font Collections (Windows 7/8)
And this is my code that is failing:
template<class T>
bool is_uuid_of(REFIID iid) noexcept {
return iid == __uuidof(T);
}
template<class ... T>
HRESULT QueryIUnkownInterface(auto* obj, REFIID iid, void** ppvObject) noexcept {
if (iid == IID_IUnknown || (is_uuid_of<T>(iid) || ...)) {
*ppvObject = obj;
obj->AddRef();
return S_OK;
}
else {
*ppvObject = NULL;
return E_NOINTERFACE;
}
}
ULONG ReleaseIUnkown(auto* obj, std::atomic<ULONG>& count) noexcept {
ULONG new_count = count -= 1;
if (new_count == 0)
delete obj;
return new_count;
}
class MemoryFontStream : public IDWriteFontFileStream {
std::atomic<ULONG> count_ = { 1 };
public:
MemoryFontStream(std::span<const uint8_t> buff, uint64_t last_write_time) noexcept
: buff_{ buff }, last_write_time_{ last_write_time } {
printf("MemoryFontStream::MemoryFontStream()\n");
}
~MemoryFontStream() {
printf("MemoryFontStream::~MemoryFontStream()\n");
}
ULONG AddRef() noexcept override {
return count_ += 1;
}
ULONG Release() noexcept override {
return ReleaseIUnkown(this, count_);
}
HRESULT QueryInterface(REFIID iid, void** ppvObject) noexcept override {
return QueryIUnkownInterface<IDWriteFontFileStream>(this, iid, ppvObject);
}
HRESULT GetFileSize(UINT64* fileSize) noexcept override {
if (fileSize != nullptr) {
*fileSize = buff_.size();
return S_OK;
}
return E_FAIL;
}
HRESULT GetLastWriteTime(UINT64* lastWriteTime) noexcept override {
if (lastWriteTime != nullptr) {
*lastWriteTime = last_write_time_;
return S_OK;
}
return E_FAIL;
}
HRESULT ReadFileFragment(
void const** fragmentStart,
UINT64 fileOffset,
UINT64 fragmentSize,
void** fragmentContext
) noexcept override {
*fragmentStart = nullptr;
*fragmentContext = nullptr;
if (fileOffset <= buff_.size() && fragmentSize <= buff_.size() - fileOffset) {
*fragmentStart = buff_.data() + fileOffset;
}
return E_FAIL;
}
void ReleaseFileFragment(void* fragmentContext) noexcept override {}
private:
std::span<const uint8_t> buff_;
uint64_t last_write_time_ = 0;
};
class FontsEnumerator;
class FontsLoader : public IDWriteFontFileLoader {
std::atomic<ULONG> count_ = { 1 };
FontsEnumerator* enumerator_;
public:
FontsLoader(FontsEnumerator* enumerator) noexcept
: enumerator_{ enumerator } {
printf("FontsLoader::FontsLoader()\n");
}
~FontsLoader() {
printf("FontsLoader::~FontsLoader()\n");
}
ULONG AddRef() noexcept override {
return count_ += 1;
}
ULONG Release() noexcept override {
return ReleaseIUnkown(this, count_);
}
HRESULT QueryInterface(REFIID iid, void** ppvObject) noexcept override {
return QueryIUnkownInterface<IDWriteFontFileLoader>(this, iid, ppvObject);
}
HRESULT CreateStreamFromKey(
void const* fontFileReferenceKey,
UINT32 fontFileReferenceKeySize,
IDWriteFontFileStream** fontFileStream
) noexcept override;
};
struct FontBuff {
uint64_t last_write_time;
std::vector<uint8_t> buff;
};
struct FontBuffView {
uint64_t last_write_time;
std::span<const uint8_t> buff;
};
class FontsEnumerator : public IDWriteFontFileEnumerator {
std::atomic<ULONG> count_ = { 1 };
int current_pos_ = -1;
std::vector<FontBuffView> fonts_buffs_;
std::vector<size_t> keys_;
ComPtr<FontsLoader> fonts_loader_;
ComPtr<IDWriteFactory> factory_;
ComPtr<IDWriteFontFile> current_font_file_;
public:
FontsEnumerator(std::vector<FontBuffView> fonts_buffs, ComPtr<IDWriteFactory> factory)
: fonts_buffs_{ std::move(fonts_buffs) }, factory_{std::move(factory)} {
keys_.reserve(fonts_buffs_.size());
for (size_t i = 0; i < fonts_buffs_.size(); ++i) keys_.push_back(i);
if (!fonts_buffs_.empty()) {
fonts_loader_ = new FontsLoader(this);
}
printf("FontsEnumerator::FontsEnumerator()\n");
}
~FontsEnumerator() {
printf("FontsEnumerator::~FontsEnumerator()\n");
}
std::optional<FontBuffView> font_at(size_t i) const noexcept {
printf("FontsEnumerator::font_at(%zu)\n", i);
if (i < fonts_buffs_.size())
return fonts_buffs_[i];
return {};
}
ULONG AddRef() noexcept override {
return count_ += 1;
}
ULONG Release() noexcept override {
return ReleaseIUnkown(this, count_);
}
HRESULT QueryInterface(REFIID iid, void** ppvObject) noexcept override {
return QueryIUnkownInterface<IDWriteFontFileEnumerator>(this, iid, ppvObject);
}
HRESULT GetCurrentFontFile(IDWriteFontFile** fontFile) noexcept override {
if (fontFile == nullptr || (size_t)current_pos_ >= fonts_buffs_.size()
|| !fonts_loader_ || !current_font_file_)
return E_FAIL;
*fontFile = current_font_file_.Detach();
printf("-> FontsEnumerator::GetCurrentFontFile() succeeded\n");
return S_OK;
}
HRESULT MoveNext(BOOL* hasCurrentFile) noexcept override {
current_pos_ += 1;
*hasCurrentFile = (size_t)current_pos_ < fonts_buffs_.size();
if (*hasCurrentFile) {
printf("=> FontsEnumerator::MoveNext() found file at %d\n", current_pos_);
}
else {
printf("=> FontsEnumerator::MoveNext() didn't find a next file\n");
return S_OK;
}
size_t pos = (size_t)current_pos_;
printf("...CreateCustomFontFileReference(this[%p], keyp[%p => %zu], keysz[%zu], loader(%p))\n",
factory_.Get(), &keys_[pos], keys_[pos], sizeof(keys_[pos]), fonts_loader_.Get());
HRESULT hr = factory_->CreateCustomFontFileReference(&keys_[pos], sizeof(keys_[pos]),
fonts_loader_.Get(), ¤t_font_file_);
if (!SUCCEEDED(hr)) {
printf("=> FontsEnumerator::MoveNext() CreateCustomFontFileReference failed at(%d) HRESULT(0x%08X)!\n", current_pos_, hr);
return S_OK;
}
return S_OK;
}
};
HRESULT FontsLoader::CreateStreamFromKey(
void const* fontFileReferenceKey,
UINT32 fontFileReferenceKeySize,
IDWriteFontFileStream** fontFileStream
) noexcept {
printf("FontLoader::CreateStreamFromKey()\n");
if (fontFileReferenceKey == nullptr || fontFileReferenceKeySize != sizeof(size_t))
return E_FAIL;
size_t font_index = *reinterpret_cast<const size_t*>(fontFileReferenceKey);
auto font_view = enumerator_->font_at(font_index);
if (!font_view.has_value())
return E_FAIL;
*fontFileStream = new MemoryFontStream{ font_view->buff, font_view->last_write_time };
return S_OK;
}
class FontCollectionLoader : public IDWriteFontCollectionLoader {
std::atomic<ULONG> count_ = { 1 };
size_t key_ = reinterpret_cast<size_t>(this);
std::vector<FontBuff> fonts_buffs_;
ComPtr<IDWriteFactory> factory_;
public:
ULONG AddRef() noexcept override {
return count_ += 1;
}
ULONG Release() noexcept override {
return count_ -= 1;
}
HRESULT QueryInterface(REFIID iid, void** ppvObject) noexcept override {
return QueryIUnkownInterface<IDWriteFontCollectionLoader>(this, iid, ppvObject);
}
HRESULT CreateEnumeratorFromKey(
IDWriteFactory* factory,
void const* collectionKey,
UINT32 collectionKeySize,
IDWriteFontFileEnumerator** fontFileEnumerator
) noexcept override {
std::vector<FontBuffView> fonts_buffs;
fonts_buffs.reserve(fonts_buffs_.size());
for (const auto& fbuff : fonts_buffs_)
fonts_buffs.push_back(FontBuffView{ fbuff.last_write_time, std::span{ fbuff.buff } });
*fontFileEnumerator = new FontsEnumerator{ std::move(fonts_buffs), factory_};
//printf("FontCollectionLoader::CreateEnumeratorFromKey() returning S_OK\n");
return S_OK;
}
const void* key_ptr() const noexcept {
return reinterpret_cast<const void*>(&key_);
}
uint32_t key_size() const noexcept {
return sizeof(key_);
}
void set_factory(ComPtr<IDWriteFactory> factory) {
factory_ = std::move(factory);
}
void add_font(std::vector<uint8_t> buff) {
//printf("FontCollectionLoader::add_font(%p, %zu)\n", buff.data(), buff.size());
if (buff.empty())
return;
uint64_t last_write_time = std::chrono::system_clock::now().time_since_epoch().count();
fonts_buffs_.push_back(FontBuff{ last_write_time, std::move(buff) });
}
};
FontCollectionLoader font_collection_loader;
I register the font collection loader using RegisterFontCollectionLoader()
and add some fonts, then create a font collection to pass it to CreateTextFormat()
, like this:
ComPtr<IDWriteFontCollection> font_collection;
size_t* collection_key = new size_t{};
*collection_key = reinterpret_cast<size_t>(collection_key);
HRESULT hr = write_factory_->CreateCustomFontCollection(&font_collection_loader, collection_key,
sizeof(size_t), &font_collection);
if (!SUCCEEDED(hr)) {
printf("CreateCustomFontCollection failed HRESULT(0x%08X)\n", (int)hr);
}
This code has memory leaks, but it is not the problem now.
I added three fonts from memory buffers and tried this code, and this is an output sample:
FontsLoader::FontsLoader()
FontsEnumerator::FontsEnumerator()
=> FontsEnumerator::MoveNext() found file at 0
...CreateCustomFontFileReference(this[0000018361A016B0], keyp[0000018367FDA8C0 => 0], keysz[8], loader(0000018367FDA620))
=> FontsEnumerator::MoveNext() CreateCustomFontFileReference failed at(0) HRESULT(0x80070057)!
=> FontsEnumerator::MoveNext() found file at 1
...CreateCustomFontFileReference(this[0000018361A016B0], keyp[0000018367FDA8C8 => 1], keysz[8], loader(0000018367FDA620))
=> FontsEnumerator::MoveNext() CreateCustomFontFileReference failed at(1) HRESULT(0x80070057)!
=> FontsEnumerator::MoveNext() found file at 2
...CreateCustomFontFileReference(this[0000018361A016B0], keyp[0000018367FDA8D0 => 2], keysz[8], loader(0000018367FDA620))
=> FontsEnumerator::MoveNext() CreateCustomFontFileReference failed at(2) HRESULT(0x80070057)!
=> FontsEnumerator::MoveNext() didn't find a next file
FontsEnumerator::~FontsEnumerator()
As you can see, CreateCustomFontFileReference()
is always failing with HRESULT(0x80070057)
!