Page 1 of 1

Problem with gdiplus fontCollection:families property (with workaround code)

Posted: 14 Jun 2021 1:08
by choibakk
PDC,

I wanted to use a free TTF windows font to display the chess clock in the GUI display of NADYA3 Chess using an LCD font, and have it loading the font as a private font collection. During the process I found what I believe to be a bug in VIP, but I'll let you decide because I still often times feel like a complete beginner. In any case, I provide my workaround and have done the work to determine what I believe to be the problem. I could be doing something wrong, as I'm not a GDI/GDIPLUS/SDK Windows coder by any means, and I struggle through it.

This code loads a custom true-type font using gdi plus without having to globally install it on the users' windows OS.

Problem: I believe the code accessing the "families" property of the "fontCollection" interface is incorrect. I believe the return data from "gdipGetFontCollectionFamilyList" is a pointer to an array of
gpFontFamily pointers to the underlying structures.

The first example shows how I access the list of font families in a private font collection and successfully create the font, in two related, ways and then shows what throws an exception using VIP (if I am using VIP properly) The second example shows how I believe PDC intended for it to work.

ALSO: I'm not very good at all at writing "pretty" VIP prolog, and this looks kind of choppy to me, so feel free to show me how I could make it "prettier". Martin and others seems to be pretty helpful with people about that but I don't know if he works for PDC.

Example #1: Note Method #1 and Method #2 succeed, and Method #3 fails. The code is commented. (I borrowed a dialog constructor for my example). Please note, my custom TTF file only has a single font family and this code takes that into account.

Code: Select all

clauses     new(Parent) :-         dialog::new(Parent),         generatedInitialize(),         init_display(),         gdiplus_native::gdipNewPrivateFontCollection(PrivateFontCollection)=0,         gdiplus_native::gdipPrivateAddFontFile(PrivateFontCollection, "lcd.ttf")=0,         %  I should be able to skip all the code between here and "Method #3"         gdiplus_native::gdipGetFontCollectionFamilyCount(PrivateFontCollection, Count)=0,         Array = memory::allocHeap(Count * sizeOfDomain(pointer), memory::contextType_gdiplus),         gdiplus_native::gdipGetFontCollectionFamilyList(PrivateFontCollection, Count, Array, Found)=0,         Ptr = uncheckedConvert(pointer, memory::getInteger64(Array)),         FontF = uncheckedConvert(gdiplus_native::gpFontFamily, Ptr),         % -- Method #1 (Works)         gdiplus_native::gdipCreateFont(FontF, 12, gdiplus_native::fontStyleRegular, gdiplus_native::unitPoint, NativeFont)=0,         Font1 = font::newNative(NativeFont),         % -- Method #2 (Works)         FontFamily = fontFamily::newNative(FontF),         Font2 = font::createFromFontFamily(FontFamily, 12, gdiplus_native::fontStyleRegular, gdiplus_native::unitPoint),         % -- Method #3 (Fails)         FontFamilies = fontCollection::newNative(PrivateFontCollection),         [FirstFamily|_] = FontFamilies:families,  % <---- I believe the underlying code is accesing the pointer array incorrectly.             %  THE NEXT LINE OF CODE THROWS THE EXCEPTION         Font3 = font::createFromFontFamily(FirstFamily, 12, gdiplus_native::fontStyleRegular, gdiplus_native::unitPoint),         %  WISHLIST ITEM - Would be totally cool to be able to add an optional parameter to pass a single, or list of,         %    "gpFontCollection" items (like my "PrivateFontCollection" variable) to vpiCommonDialogs::getFont predicate, as in:         %  Font = vpiCommonDialogs::getFont(getFont(), [PrivateFontCollection]),         ! ;         succeed.
Example #2: I believe this how PDC intended this to work.

Code: Select all

        gdiplus_native::gdipNewPrivateFontCollection(PrivateFontCollection)=0,         gdiplus_native::gdipPrivateAddFontFile(PrivateFontCollection, "lcd.ttf")=0,         FontFamilies = fontCollection::newNative(PrivateFontCollection),         [FirstFamily|_] = FontFamilies:families,         Font = font::createFromFontFamily(FirstFamily, 12, gdiplus_native::fontStyleRegular, gdiplus_native::unitPoint),
Again, I could be overlooking something.

Cheers,
Craig Hoibakk

Re: Problem with gdiplus fontCollection:families property (with workaround code)

Posted: 14 Jun 2021 11:32
by Thomas Linder Puls
You are right, there is a problem in fontCollection::families.

The code lacks a call to memory::getPointer

Code: Select all

clauses     families() = Families :-         checkStatus(gdiplus_native::gdipGetFontCollectionFamilyCount(nativeFontCollection, Count)),         Array = memory::allocHeap(Count * sizeOfDomain(pointer), memory::contextType_gdiplus),         checkStatus(gdiplus_native::gdipGetFontCollectionFamilyList(nativeFontCollection, Count, Array, Found)),         Families =             gdiplusSupport::arrayToList(Array, Found,                 { (P0) = tuple(P1, Family) :-                     Family = fontfamily::newNative(uncheckedConvert(gdiplus_native::gpFontFamily, memory::getPointer(P0))),                     P1 = memory::pointerAddLite(P0, sizeOfDomain(pointer))                 }).
It will be fixed in the next build. In that build we will also add privateFontCollection::addFontFile.

Code: Select all

interface privateFontCollection supports fontCollection   predicates     addFontFile : (string File).   end interface privateFontCollection
Regarding the other things you mention.
  • I think you can safely take advices from Martin (who is not a PDC employee).
  • The font dialog is a actually standard Windows dialog that only supports the installed fonts.
  • Stylistically I think you should raise exceptions for errors instead of failing (notice how gdiplusSupport::checkStatus is used throughout the gdi+ code.
When the next build is released you can write like this:

Code: Select all

        PrivateFontCollection = privateFontCollection::create(),         PrivateFontCollection:addFontFile(@"lcd.ttf"),         [FirstFamily | _] == PrivateFontCollection:families,         Font = font::createFromFontFamily(FirstFamily, 12, gdiplus_native::fontStyleRegular, gdiplus_native::unitPoint),