Get a font filename based on the font handle (HFONT) - c++

I came across a situation where we needed to know the filename of a font currently in use by a QFont. Knowing that a QFont can give us the font family and Windows HFONT handle.
The font family is not enough, because manipulating styles like Bold or Italic can result in Windows selecting a different font file. (f.e. arial.ttf, arialbd.ttf, arialbi.ttf, ariali.ttf).
This code sample should give us <path>\arial.ttf:
QFont font("Arial", 12);
FindFontFileName(font.handle());
while this code sample should give us <path>\arialbi.ttf
QFont font("Arial", 12);
font.setStyle(QFont::StyleItalic);
font.setWeight(QFont::Bold);
FindFontFileName(font.handle());

The Windows API Font and Text Functions doesn't contain a function that returns the filename of a font. So a more creative solution has to be worked out.
The solution is to use the GetFontData function, which will give us the exact copy of the original font file. The only thing that's left is comparing this data with the contents of all installed/known fonts.
Lookup table
We will first create a lookup table (FontList) of all installed/known fonts:
#define FONT_FINGERPRINT_SIZE 256
struct FontListItem
{
std::string FileName;
int FingerPrintOffset;
char FingerPrint[FONT_FINGERPRINT_SIZE];
};
std::multimap< size_t, std::shared_ptr<FontListItem> > FontList;
The FingerPrint is a random part read from the font file in order to distinguish between fonts of the same filesize. You could also use a hash (f.e. SHA1) of the complete file to establish this.
Adding fonts
Method for adding a single font to this list is pretty straightforward:
void AddFontToList(const std::string& fontFileName)
{
std::ifstream file(fontFileName, std::ios::binary | std::ios::ate);
if (!file.is_open())
return;
size_t fileSize = file.tellg();
if (fileSize < FONT_FINGERPRINT_SIZE)
return;
std::shared_ptr<FontListItem> fontListItem(new FontListItem());
fontListItem->FileName = fontFileName;
fontListItem->FingerPrintOffset = rand() % (fileSize - FONT_FINGERPRINT_SIZE);
file.seekg(fontListItem->FingerPrintOffset);
file.read(fontListItem->FingerPrint, FONT_FINGERPRINT_SIZE);
FontList.insert(std::pair<size_t, std::shared_ptr<FontListItem> >(fileSize, fontListItem));
}
A Qt way to add all Windows fonts to the lookup table goes like this:
const QDir dir(QString(getenv("WINDIR")) + "\\fonts");
dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
foreach (const QFileInfo fileInfo, dir.entryInfoList())
AddFontToList(fileInfo.absoluteFilePath().toUtf8().constData());
File enumeration can also be done using FindFirstFile/FindNextFile Windows API functions, but would be less readable for the purpose of this answer.
GetFontData helper
Then we create a wrapper function for the GetFontData function that creates a DC, selects the font by the HFONT handle and returns the fonts data:
bool GetFontData(const HFONT fontHandle, std::vector<char>& data)
{
bool result = false;
HDC hdc = ::CreateCompatibleDC(NULL);
if (hdc != NULL)
{
::SelectObject(hdc, fontHandle);
const size_t size = ::GetFontData(hdc, 0, 0, NULL, 0);
if (size > 0)
{
char* buffer = new char[size];
if (::GetFontData(hdc, 0, 0, buffer, size) == size)
{
data.resize(size);
memcpy(&data[0], buffer, size);
result = true;
}
delete[] buffer;
}
::DeleteDC(hdc);
}
return result;
}
Font filename lookup
Now we're all set for looking up the exact filename of a font by only knowing the HFONT handle:
std::string FindFontFileName(const HFONT fontHandle)
{
std::vector<char> data;
if (GetFontData(fontHandle, data))
{
for (auto i = FontList.lower_bound(data.size()); i != FontList.upper_bound(data.size()); ++i)
{
if (memcmp(&data[i->second->FingerPrintOffset], i->second->FingerPrint, FONT_FINGERPRINT_SIZE) == 0)
return i->second->FileName;
}
}
return std::string();
}

Related

Printing different documents silently in C++

I have folder of different documents like: pdf, txt, rtf, images.
My case is to send all documents to the printer (print it). Used framework is MFC and WinAPI. Current implementation has dialog box for choose documents and another dialog for choose printer.
Then question appears, how to print it all? Do I need to convert every documents to PDF, then merge it and print one pdf document? I will appreciate any advice in that field.
void CMultipleDocsPrintTestDlg::OnBnClickedButton1()
{
TCHAR strFilter[] = { _T("Rule Profile (*.pdf)||") };
// Create buffer for file names.
const DWORD numberOfFileNames = 100;
const DWORD fileNameMaxLength = MAX_PATH + 1;
const DWORD bufferSize = (numberOfFileNames * fileNameMaxLength) + 1;
CFileDialog fileDlg(TRUE, _T("pdf"), NULL, OFN_ALLOWMULTISELECT, strFilter);
TCHAR* filenamesBuffer = new TCHAR[bufferSize];
// Initialize beginning and end of buffer.
filenamesBuffer[0] = NULL;
filenamesBuffer[bufferSize - 1] = NULL;
// Attach buffer to OPENFILENAME member.
fileDlg.GetOFN().lpstrFile = filenamesBuffer;
fileDlg.GetOFN().nMaxFile = bufferSize;
// Create array for file names.
CString fileNameArray[numberOfFileNames];
if (fileDlg.DoModal() == IDOK)
{
// Retrieve file name(s).
POSITION fileNamesPosition = fileDlg.GetStartPosition();
int iCtr = 0;
while (fileNamesPosition != NULL)
{
fileNameArray[iCtr++] = fileDlg.GetNextPathName(fileNamesPosition);
}
}
// Release file names buffer.
delete[] filenamesBuffer;
CPrintDialog dlg(FALSE);
dlg.m_pd.Flags |= PD_PRINTSETUP;
CString printerName;
if (dlg.DoModal() == IDOK)
{
printerName = dlg.GetDeviceName();
}
// What next ???
}
You could make use of ShellExecute to do this. The parameter lpOperation can be set to print. To quote:
Prints the file specified by lpFile. If lpFile is not a document file, the function fails.
As mentioned in a similar discussion here on StackOverflow (ShellExecute, "Print") you should keep in mind:
You need to make sure that the machine's associations are configured to handle the print verb.
You referred to pdf, txt, rtf, images which should all be supported I would think by this mechanism.
ShellExecute(NULL, "print", fileNameArray[0], nullptr, nullptr, SW_SHOWNORMAL);
The last parameter might have to be changed (SW_SHOWNORMAL). This code would be put in a loop so you could call it for each file. And note that the above code snippet has not been tested.

How to get the IDWriteFont object from the font file downloaded in windows OS and not installed yet

Hi I am using DWrite APIs to get the system fonts metadata using DWrite APIs. I am doing the following.
HR(DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown **>(&factory)));
// Get the system font collection.
IDWriteFontCollection *collection = NULL;
HR(factory->GetSystemFontCollection(&collection, TRUE));
// Get the number of font families in the collection.
int familyCount = collection->GetFontFamilyCount();
// track unique font Ids we've already added
// using a set so we don't get any duplicates.
for (int i = 0; i < familyCount; i++)
{
IDWriteFontFamily *family = NULL;
// Get the font family.
HR(collection->GetFontFamily(i, &family));
int fontCount = family->GetFontCount();
for (int j = 0; j < fontCount; j++)
{
IDWriteFont *font = NULL;
HR(family->GetFont(j, &font));
fontMetaDataPtr* result = resultFromFont(font);
}
}
This will give me the information of the all the installed fonts. Now I need to get the information for the new fonts which I have downloaded, but there is no API in DWrite in which I can get IDWriteFont *font Object using font file path ex. "C:\Downloads\Fonts.ttf"
Can you share any working example for this.
I tried following code but it didn't work as it looks like font installing temporary not updating the collection.
int result = AddFontResourceW(wideFontFilePath.c_str());
if (result > 0) {
IDWriteFactory *factory = NULL;
HR(DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown **>(&factory)));
// Get the system font collection.
IDWriteFontCollection *collection = NULL;
HR(factory->GetSystemFontCollection(&collection, TRUE));
IDWriteFontFile *fontFile = NULL;
// Get the system font collection.
HR(factory->CreateFontFileReference(utf8ToUtf16(fontfile.c_str()), NULL, &fontFile));
if (fontFile) {
BOOL isSupported;
DWRITE_FONT_FILE_TYPE fileType;
DWRITE_FONT_FACE_TYPE fontFaceType;
UINT32 numFaces;
IDWriteFontFace* fontFace = NULL;
fontFile->Analyze(&isSupported, &fileType, &fontFaceType, &numFaces);
factory->CreateFontFace(fontFaceType, 1, &fontFile, 0, DWRITE_FONT_SIMULATIONS_NONE, &fontFace);
IDWriteFont *font = NULL;
collection->GetFontFromFontFace(fontFace, &font);
fontMetaDataPtr* result = resultFromFont(font);
}
}
result = RemoveFontResourceW(wideFontFilePath.c_str());
I found a work around for this problem
HR(DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory3),
reinterpret_cast<IUnknown **>(&factory)));
HR(factory->CreateFontSetBuilder(&fontSetBuilder));
HR(factory->CreateFontFileReference(utf8ToUtf16(fontFilePath.c_str()), NULL, &fontFile));
if (fontFile) {
fontFile->Analyze(&isSupported, &fileType, &fontFaceType, &numberOfFonts);
// If there are more than 1 fonts are there, then we are taking only first index of the font
// TODO: need to check for multiple fonts
IDWriteFontFaceReference* fontFaceReference;
factory->CreateFontFaceReference(fontFile, 0, DWRITE_FONT_SIMULATIONS_NONE, &fontFaceReference);
fontSetBuilder->AddFontFaceReference(fontFaceReference);
IDWriteFontSet* customFontSet;
fontSetBuilder->CreateFontSet(&customFontSet);
UINT32 fontCnt = customFontSet->GetFontCount();
if (fontCnt > 1) {
// TODO: need to check for multiple fonts
}
string postscriptName = getStringFromFontSet(customFontSet, DWRITE_FONT_PROPERTY_ID_POSTSCRIPT_NAME);
}
like this using IDWriteFontSet and DWRITE_FONT_PROPERTY_ID enum from dwrite_3.h helped me to get my solution.

Specific filepath to store Screen Record using CGDisplayStream in OSX

I have been working on a c++ command line tool to record screen. After some searching I have come up with this following code. Looks like screen is being recorded when I compile and run the code. I am looking for functions where I can provide the specific filepath where the screen record is to be stored. Also I would like to append the timestamp along with filename. If anybody has better approach or method to this problem please suggest here. Any leads are appreciated. Thanks
#include <ApplicationServices/ApplicationServices.h>
int main(int argc, const char * argv[]) {
// insert code here...
CGRect mainMonitor = CGDisplayBounds(CGMainDisplayID());
CGFloat monitorHeight = CGRectGetHeight(mainMonitor);
CGFloat monitorWidth = CGRectGetWidth(mainMonitor);
const void *keys[1] = { kCGDisplayStreamSourceRect };
const void *values[1] = { CGRectCreateDictionaryRepresentation(CGRectMake(0, 0, 100, 100)) };
CFDictionaryRef properties = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CGDisplayStreamRef stream = CGDisplayStreamCreate(CGMainDisplayID(), monitorWidth, monitorHeight, '420f' , properties, ^(CGDisplayStreamFrameStatus status, uint64_t displayTime, IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef){});
CGDirectDisplayID displayID = CGMainDisplayID();
CGImageRef image_create = CGDisplayCreateImage(displayID);
CFRunLoopSourceRef runLoop = CGDisplayStreamGetRunLoopSource(stream);
// CFRunLoopAddSource(<#CFRunLoopRef rl#>, runLoop, <#CFRunLoopMode mode#>);
CGError err = CGDisplayStreamStart(stream);
if (err == CGDisplayNoErr) {
std::cout<<"WORKING"<<std::endl;
sleep(5);
} else {
std::cout<<"Error: "<<err<<std::endl;
}
//std::cout << "Hello, World!\n";
return 0;
}
You should do that in the callback which you provide in CGDisplayStreamCreate. You can access the pixels via IOSurfaceGetBaseAddress (see other IOSurface functions). If you don't want to do the pixel twiddling yourself, you could create a CVPixelBuffer with CVPixelBufferCreateWithBytes from the IOSurface and then create a CIImage with [CIImage imageWithCVImageBuffer] and save that to file as seen here.

I am getting a different font height from DirectWrite font text metric than I requested as the height parameter

Can anyone explain this?
Code below:
// - change font size to specified point(pt) size.
// - returns false if there is an error, or true otherwise.
bool GraphicsDirect2D::TextSetFontSize(int size)
{
if (size <= 0)
{
assert(0 && "Error: Invalid size parameter.");
return false;
}
// if size has not changed, return
float currentFontSizeDIP = m_pDWTextFormat->GetFontSize();
float currentFontSizePoints = ConvertDIPToPointSize(currentFontSizeDIP);
if (currentFontSizePoints == size)
return true;
// otherwise, create new object for new text size
IDWriteTextFormat *pNewTextFormat = NULL;
if (FAILED(m_pDWFactory->CreateTextFormat(
StringToWString(m_textCurrentSettings.strFont).c_str(),
NULL,
DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
ConvertPointSizeToDIP(size),
L"en-us",
&pNewTextFormat)))
{
assert(0 && "Error.");
return false;
}
{
IDWriteTextLayout *m_pDWTextLayout = NULL;
DWRITE_TEXT_METRICS tm;
if (FAILED(m_pDWFactory->CreateTextLayout(
L"Arial",
0,
pNewTextFormat,
100,
100,
&m_pDWTextLayout)))
{
Destroy();
assert(0 && "Error.");
return false;
}
if (FAILED(m_pDWTextLayout->GetMetrics(&tm)))
{
assert(0 && "Error.");
}
m_fontHeight = tm.height;
m_pDWTextLayout->Release();
}
// release the old object
if (m_pDWTextFormat)
{
m_pDWTextFormat->Release();
m_pDWTextFormat = NULL;
}
// and point to the new one
m_pDWTextFormat = pNewTextFormat;
m_textCurrentSettings.size = size;
return true;
}
This function is from a class I'm using to provide a graphics interface for Direct2D (and DirectWrite for text). The function replaces a font it is storing as the 'current' one to one of a new size, and updates a size variable for reference.
The problem is that if I pass in 16 to the function (for example), the font I am given is BIGGER than point size 16 when created using Direct2D. (Using WinGDI it produces my reference size of 16 on the screen.) The Win32 function is my benchmark, and the directD version creates a bigger font than the Win32 code.
I have converted the point size input to this function as I think I need to, to provide DIP values to the Direct2D CreateTextFormat() function. Then I create a layout with a 0 length string to get the height for the default font created. This is then different to what I asked for to start with ...
I can't find any documentation that describes this in enough detail to get my head around this. Every site on the functions and structures just parrots the same stuff the windows website says about the structure contents. (Grrrr!!) Nowhere does it actually say if the text metric function output is in DIP or not, I'm assuming it is but can't see an obvious conversion from another format to get back to 16!! Please help :)
Thanks

How to use fontconfig to get font list (C/C++)?

I hear that fontconfig is the best option for getting fonts in linux. Unfortunately, I've been looking through their developer documentation and I have absolutely no clue what I'm doing. It would appear there is no simple function to get a list of system fonts. I have to perform a pattern search instead... right?
In short, what is the best way to get a list of true-type fonts (their family, face, and directory) with fontconfig? Of course, if there's something better than fontconfig, I'm certainly open to other solutions.
I had a similar question, and found this post (the fontconfig documentation is a little difficult to get through). MindaugasJ's response was useful, but watch out for the extra lines calling things like FcPatternPrint() or printing out the results of FcNameUnparse(). In addition, you need to add a FC_FILE argument to the list of arguments passed to FcObjectSetBuild. Something like this:
FcConfig* config = FcInitLoadConfigAndFonts();
FcPattern* pat = FcPatternCreate();
FcObjectSet* os = FcObjectSetBuild (FC_FAMILY, FC_STYLE, FC_LANG, FC_FILE, (char *) 0);
FcFontSet* fs = FcFontList(config, pat, os);
printf("Total matching fonts: %d\n", fs->nfont);
for (int i=0; fs && i < fs->nfont; ++i) {
FcPattern* font = fs->fonts[i];
FcChar8 *file, *style, *family;
if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch &&
FcPatternGetString(font, FC_FAMILY, 0, &family) == FcResultMatch &&
FcPatternGetString(font, FC_STYLE, 0, &style) == FcResultMatch)
{
printf("Filename: %s (family %s, style %s)\n", file, family, style);
}
}
if (fs) FcFontSetDestroy(fs);
I had a slightly different problem to solve in that I needed to find the font file to pass to freetype's FC_New_Face() function given some font "name". This code is able to use fontconfig to find the best file to match a name:
FcConfig* config = FcInitLoadConfigAndFonts();
// configure the search pattern,
// assume "name" is a std::string with the desired font name in it
FcPattern* pat = FcNameParse((const FcChar8*)(name.c_str()));
FcConfigSubstitute(config, pat, FcMatchPattern);
FcDefaultSubstitute(pat);
// find the font
FcResult res;
FcPattern* font = FcFontMatch(config, pat, &res);
if (font)
{
FcChar8* file = NULL;
if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch)
{
// save the file to another std::string
fontFile = (char*)file;
}
FcPatternDestroy(font);
}
FcPatternDestroy(pat);
This is not exactly what you are asking for, but it will give you the list of fonts available.
#include <fontconfig.h>
FcPattern *pat;
FcFontSet *fs;
FcObjectSet *os;
FcChar8 *s, *file;
FcConfig *config;
FcBool result;
int i;
result = FcInit();
config = FcConfigGetCurrent();
FcConfigSetRescanInterval(config, 0);
// show the fonts (debugging)
pat = FcPatternCreate();
os = FcObjectSetBuild (FC_FAMILY, FC_STYLE, FC_LANG, (char *) 0);
fs = FcFontList(config, pat, os);
printf("Total fonts: %d", fs->nfont);
for (i=0; fs && i < fs->nfont; i++) {
FcPattern *font = fs->fonts[i];//FcFontSetFont(fs, i);
FcPatternPrint(font);
s = FcNameUnparse(font);
if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) {
printf("Filename: %s", file);
}
printf("Font: %s", s);
free(s);
}
if (fs) FcFontSetDestroy(fs);