Reputation: 1605
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)
!
Upvotes: 0
Views: 113
Reputation: 1605
The problem was that the interface implementing IDWriteFontFileLoader
had to be registered in the DirectWrite
factory with RegisterFontFileLoader
. This is why it was failing with invalid parameter.
The code contains other memory leaks and lifetime issues which are causing crashes but this is outside the scope of the quesion
Upvotes: 1