As discussed before in this blog, VFP can't display natively any character having its CHR() higher than 0xFF (decimal 255).
There are several very interesting fonts that bring very cool and up to date icons that we could use in our apps, such as the SEGOE MDL2 ASSETS, used by Windows 10 all over.
The Unicodes can be obtained directly by the CharMap.EXE or all over the web. Here's an excellent starting point: https://docs.microsoft.com/en-us/windows/uwp/design/style/segoe-ui-symbol-font
The samples below will use GDI+ to save any desired character as an image, allowing us to use those cool images in our apps. They use the _GDIPLUS.VCX FFC classes, but is really very easy to adapt to GdiPlusX as well, if needed.
Adapt it to your needs!
Basically a function that retrieves a single unicode character and saves it as an image file.
EXTRACT A SINGLE ICON
lcFile = "Print.bmp" lcUnicode = "e749" lcFont = "SEGOE MDL2 ASSETS" lnSize = 32 && Pixels lnForeColor = RGB(0, 0, 255) && Black lnBackColor = RGB(255, 255, 255) && White =MakeImageFromUnicode(m.lcFile, lcUnicode, lcFont, lnSize, lnForeColor, lnBackColor)
Save the code Below as "MakeImageFromUnicode.prg":
FUNCTION MakeImageFromUnicode(tcFileName, tcUnicode, tcFontName, tnImgSize, tnForeColor, tnBackColor) *!* tcUnicode allows up to 2 characters, that will be drawn one over the other *!* Par1: Main Unicode *!* Par2: Socondary Unicode *!* Par3: Mode, where 0=Center, 1=TopLeft, 2=TopRight, 3=BottLeft, 4=BottRight *!* Par4: Size of the 2nd character LOCAL lnChars, lnFactor, lnFontHeight, lnFontSize, lnHeight, lnLines, lnNewFontSize, lnWidth LOCAL lqUnicode LOCAL lcUnicode1, lcUnicode2, lnMode, lnSize2 IF EMPTY(m.tcFileName) OR EMPTY(m.tcUnicode) OR EMPTY(m.tcFontName) OR EMPTY(m.tnImgSize) RETURN ENDIF m.lnFontSize = 48 m.lnWidth = m.tnImgSize m.lnHeight = m.tnImgSize * Create a font object using the text object's settings. LOCAL loFont0 AS GpFont OF HOME() + "FFC/_GdiPlus.vcx" m.loFont0 = NEWOBJECT('gpFont', HOME() + 'FFC/_GdiPlus.vcx') m.loFont0.Create(m.tcFontName, m.lnFontSize, 0, 3) && 0 = Font Style LOCAL loGfx0 AS GpGraphics OF HOME() + "FFC/_GdiPlus.vcx" m.loGfx0 = NEWOBJECT('gpGraphics', home() + 'FFC\_GdiPlus.vcx') m.loGfx0.CreateFromHWnd(_screen.HWnd) m.lnChars = 0 m.lnLines = 0 LOCAL loSize AS gpSize OF HOME() + "FFC/_GdiPlus.vcx" m.loSize = m.loGfx0.MeasureStringA("A", m.loFont0, , , @m.lnChars, @m.lnLines) * lnFontWidth = loSize.W m.lnFontHeight = m.loSize.H m.lnFactor = m.lnFontHeight / m.tnImgSize m.lnNewFontSize = INT(m.lnFontSize / m.lnFactor) * Create a font object using the text object's settings. LOCAL loFont AS GpFont OF HOME() + "FFC/_GdiPlus.vcx" m.loFont = NEWOBJECT('gpFont', HOME() + 'FFC/_GdiPlus.vcx') m.loFont.Create(m.tcFontName, m.lnNewFontSize, 0, 3) && 0 = Font Style LOCAL loBMP AS GpBitmap OF HOME() + "FFC/_GdiPlus.vcx" m.loBMP = NEWOBJECT("gpBitmap", HOME() + "FFC/_GdiPlus.vcx") #DEFINE GdiPlus_PixelFormat_32BPPARGB 0x0026200a m.loBMP.Create(m.lnHeight, m.lnHeight, GdiPlus_PixelFormat_32BPPARGB) LOCAL loGfx AS GpGraphics OF HOME() + "FFC/_GdiPlus.vcx" m.loGfx = NEWOBJECT('gpGraphics', HOME() + 'FFC/_GdiPlus.vcx') m.loGfx.CreateFromImage(m.loBMP) * Setting the Backcolor LOCAL loBackColor AS GpColor OF HOME() + "FFC/_GdiPlus.vcx" IF EMPTY(m.tnBackColor) m.loBackColor = 0xFFFFFFFF && White background ELSE m.loBackColor = NEWOBJECT("gpColor", HOME() + 'FFC/_GdiPlus.vcx') m.loBackColor.FoxRGB = m.tnBackColor ENDIF m.loGfx.CLEAR(m.loBackColor) && Background * Create a rectangle LOCAL loRect AS GpRectangle OF HOME() + "FFC/_GdiPlus.vcx" m.loRect = NEWOBJECT("GPRectangle", HOME() + 'FFC/_GdiPlus.vcx', "", 0, 0, m.lnWidth, m.lnHeight) m.loRect.y = m.loRect.y + 1 * Setting the Forecolor LOCAL loColor AS GpColor OF HOME() + "FFC/_GdiPlus.vcx" IF EMPTY(m.tnForeColor) m.tnForeColor = 0 && Black ENDIF m.loColor = NEWOBJECT("gpColor", HOME() + 'FFC/_GdiPlus.vcx') m.loColor.FoxRGB = m.tnForeColor LOCAL loBrush AS GpSolidBrush OF HOME() + "FFC/_GdiPlus.vcx" m.loBrush = NEWOBJECT("gpSolidBrush", HOME() + 'FFC/_GdiPlus.vcx', "", m.loColor) * The character need to be drawn at the center of the image object * Get a basic string format object * StringAlignment enumeration * Applies to GpStringFormat::Alignment, GpStringFormat::LineAlignment #DEFINE GDIPLUS_STRINGALIGNMENT_Near 0 && in Left-To-Right locale, this is Left #DEFINE GDIPLUS_STRINGALIGNMENT_Center 1 #DEFINE GDIPLUS_STRINGALIGNMENT_Far 2 && in Left-To-Right locale, this is Right LOCAL loStringFormat AS gpStringFormat OF HOME() + "FFC/_GdiPlus.vcx" m.loStringFormat = NEWOBJECT("GpStringFormat", HOME() + "FFC/_GdiPlus.vcx") m.loStringFormat.Create() m.loStringFormat.Alignment = GDIPLUS_STRINGALIGNMENT_Center m.loStringFormat.LineAlignment = GDIPLUS_STRINGALIGNMENT_Center * Prepare the Unicode m.lcUnicode1 = GETWORDNUM(m.tcUnicode, 1, ",") m.lqUnicode = LEFT(BINTOC(EVALUATE("0x" + m.lcUnicode1), "4RS"), 2) * Draw the string m.loGfx.DrawStringW(m.lqUnicode, m.loFont, m.loRect, m.loStringFormat, m.loBrush) m.lcUnicode2 = GETWORDNUM(m.tcUnicode, 2, ",") IF NOT EMPTY(m.lcUnicode2) m.lqUnicode = LEFT(BINTOC(EVALUATE("0x" + m.lcUnicode2), "4RS"), 2) m.lnMode = VAL(GETWORDNUM(m.tcUnicode, 3, ",")) m.lnSize2 = VAL(GETWORDNUM(m.tcUnicode, 4, ",")) m.lnSize2 = EVL(m.lnSize2, 100) lnNewFontSize = CEILING(m.lnNewFontSize * (lnSize2/100)) m.loFont.Create(m.tcFontName, m.lnNewFontSize, 0, 3) && 0 = Font Style m.loStringFormat.Alignment = GDIPLUS_STRINGALIGNMENT_Center m.loStringFormat.LineAlignment = GDIPLUS_STRINGALIGNMENT_Center m.loRect.w = INT(m.lnWidth * (m.lnSize2 / 100)) m.loRect.H = INT(m.lnHeight * (m.lnSize2 / 100)) DO CASE CASE m.lnMode = 0 && No transformation, the 2nd image will be drawn over the original m.loRect.x = INT((m.lnWidth - m.loRect.w) / 2) m.loRect.Y = INT((m.lnHeight - m.loRect.H) / 2) CASE m.lnMode = 1 && Top-Left m.loRect.x = 0 m.loRect.Y = 0 CASE m.lnMode = 2 && Top-Right m.loRect.x = m.lnWidth - m.loRect.w m.loRect.Y = 0 CASE m.lnMode = 3 && Bottom-Left m.loRect.x = 0 m.loRect.Y = m.lnHeight - m.loRect.H CASE m.lnMode = 4 && Bottom-Right m.loRect.x = m.lnWidth - m.loRect.w m.loRect.Y = m.lnHeight - m.loRect.H OTHERWISE ENDCASE m.loRect.y = m.loRect.y + 1 m.loGfx.DrawStringW(m.lqUnicode, m.loFont, m.loRect, m.loStringFormat, m.loBrush) ENDIF * Save as image m.loBMP.SaveToFile(m.tcFileName, "image/bmp") RETURN ENDFUNC
The function also allows you to create new icons by merging two, in this case, the Printer and the Settings icon at the bottom right:
CUSTOMIZE YOUR ICONS
lcFile = "Print.bmp" lcUnicode = "e749,e713,4,25" lcFont = "SEGOE MDL2 ASSETS" lnSize = 32 && Pixels lnForeColor = RGB(0, 0, 128) && Dark Blue lnBackColor = RGB(255, 255, 128) && Yellow =MakeImageFromUnicode(m.lcFile, lcUnicode, lcFont, lnSize, lnForeColor, lnBackColor)
EXTRACTING ALL ICONS FROM A FONT
* Setup the initial 5 variables LOCAL lcFontName, lnImgSize, lnForeColor, lnBackColor, lcImageType m.lcFontName = "SEGOE MDL2 ASSETS" m.lnImgSize = 64 && The desired bmp size in pixels m.lnForeColor = RGB(0, 0, 0) && the ForeColor m.lnBackColor = RGB(255, 255, 255) && the BackColor m.lcImageType = "bmp" && available: bmp, jpg, gif, tif, png * Let's start LOCAL lcEmptyUnicode, lcFileName, lcHex, lcUnicode, lnChars, lnEmptyH, lnEmptyW, lnFactor LOCAL lnFontHeight, lnFontSize, lnFontWidth, lnHeight, lnLines, lnNewFontSize, lnWidth, loSizeReal, n m.lnFontSize = 48 m.lnWidth = m.lnImgSize m.lnHeight = m.lnImgSize m.lcImageType = LOWER(EVL(m.lcImageType, "bmp")) * Create a rectangle LOCAL loRect AS GpRectangle OF HOME() + "FFC/_GdiPlus.vcx" m.loRect = NEWOBJECT("GPRectangle", HOME() + 'FFC/_GdiPlus.vcx', "", 0, 0, m.lnWidth, m.lnHeight) m.loRect.y = m.loRect.y + 1 * The character need to be drawn at the center of the image object * Get a basic string format object * StringAlignment enumeration * Applies to GpStringFormat::Alignment, GpStringFormat::LineAlignment #DEFINE GDIPLUS_STRINGALIGNMENT_Near 0 && in Left-To-Right locale, this is Left #DEFINE GDIPLUS_STRINGALIGNMENT_Center 1 #DEFINE GDIPLUS_STRINGALIGNMENT_Far 2 && in Left-To-Right locale, this is Right LOCAL loStringFormat AS gpStringFormat OF HOME() + "FFC/_GdiPlus.vcx" m.loStringFormat = NEWOBJECT("GpStringFormat", HOME() + "FFC/_GdiPlus.vcx") m.loStringFormat.Create() m.loStringFormat.Alignment = GDIPLUS_STRINGALIGNMENT_Center m.loStringFormat.LineAlignment = GDIPLUS_STRINGALIGNMENT_Center * Create a font object using the text object's settings. LOCAL loFont0 AS GpFont OF HOME() + "FFC/_GdiPlus.vcx" m.loFont0 = NEWOBJECT('gpFont', HOME() + 'FFC/_GdiPlus.vcx') m.loFont0.Create(m.lcFontName, m.lnFontSize, 0, 3) && 0 = Font Style LOCAL loGfx0 AS GpGraphics OF HOME() + "FFC/_GdiPlus.vcx" m.loGfx0 = NEWOBJECT('gpGraphics', home() + 'FFC\_GdiPlus.vcx') m.loGfx0.CreateFromHWnd(_screen.HWnd) LOCAL loSize AS gpSize OF HOME() + "FFC/_GdiPlus.vcx" m.lnChars = 0 m.lnLines = 0 m.loSize = m.loGfx0.MeasureStringA("A", m.loFont0, , , @m.lnChars, @m.lnLines) m.lnFontWidth = m.loSize.W m.lnFontHeight = m.loSize.H m.lnFactor = m.lnFontHeight / m.lnImgSize m.lnNewFontSize = INT(m.lnFontSize / m.lnFactor) * Create a font object using the text object's settings. LOCAL loFont AS GpFont OF HOME() + "FFC/_GdiPlus.vcx" m.loFont = NEWOBJECT('gpFont', HOME() + 'FFC/_GdiPlus.vcx') m.loFont.Create(m.lcFontName, m.lnNewFontSize, 0, 3) && 0 = Font Style * Get the measure of the empty character, that will be used to avoid saving it several times m.lcEmptyUnicode = CHR(0) + CHR(0) LOCAL loSizeEmpty AS gpSize OF HOME() + "FFC/_GdiPlus.vcx" m.loSizeEmpty = m.loGfx0.MeasureStringW(m.lcEmptyUnicode, m.loFont, m.loRect, m.loStringFormat, @m.lnChars, @m.lnLines) m.lnEmptyW = m.loSizeEmpty.W m.lnEmptyH = m.loSizeEmpty.H LOCAL loBMP AS GpBitmap OF HOME() + "FFC/_GdiPlus.vcx" m.loBMP = NEWOBJECT("gpBitmap", HOME() + "FFC/_GdiPlus.vcx") #DEFINE GdiPlus_PixelFormat_32BPPARGB 0x0026200a m.loBMP.Create(m.lnHeight, m.lnHeight, GdiPlus_PixelFormat_32BPPARGB) LOCAL loGfx AS GpGraphics OF HOME() + "FFC/_GdiPlus.vcx" m.loGfx = NEWOBJECT('gpGraphics', HOME() + 'FFC/_GdiPlus.vcx') m.loGfx.CreateFromImage(m.loBMP) * Setting the Backcolor LOCAL loBackColor AS GpColor OF HOME() + "FFC/_GdiPlus.vcx" IF EMPTY(m.lnBackColor) m.loBackColor = 0xFFFFFFFF && White background ELSE m.loBackColor = NEWOBJECT("gpColor", HOME() + 'FFC/_GdiPlus.vcx') m.loBackColor.FoxRGB = m.lnBackColor ENDIF * Setting the Forecolor LOCAL loColor AS GpColor OF HOME() + "FFC/_GdiPlus.vcx" IF EMPTY(m.lnForeColor) m.lnForeColor = 0 && Black ENDIF m.loColor = NEWOBJECT("gpColor", HOME() + 'FFC/_GdiPlus.vcx') m.loColor.FoxRGB = m.lnForeColor LOCAL loBrush AS GpSolidBrush OF HOME() + "FFC/_GdiPlus.vcx" m.loBrush = NEWOBJECT("gpSolidBrush", HOME() + 'FFC/_GdiPlus.vcx', "", m.loColor) FOR m.n = 0xe001 TO 0xf8b3 && the last available found in charmap m.lcHex = TRANSFORM(m.n, "@0") m.lcHex = STRTRAN(m.lcHex, "0x0000", "") m.lcFileName = FORCEEXT(m.lcHex, m.lcImageType) * Prepare the Unicode m.lcUnicode = LEFT(BINTOC(EVALUATE("0x" + m.lcHex), "4RS"), 2) m.loSizeReal = m.loGfx0.MeasureStringW(m.lcUnicode, m.loFont, m.loRect, m.loStringFormat, @m.lnChars, @m.lnLines) m.loGfx.CLEAR(m.loBackColor) && Background * Draw the string m.loGfx.DrawStringW(m.lcUnicode, m.loFont, m.loRect, m.loStringFormat, m.loBrush) * Save as image m.loBMP.SaveToFile(m.lcFileName, "image/" + m.lcImageType) ENDFOR * Clear GDI+ objects m.loRect = NULL m.loStringFormat = NULL m.loColor = NULL m.loBackColor = NULL m.loBrush = NULL m.loSize = NULL m.loSizeEmpty = NULL m.loGfx0 = NULL m.loGfx = NULL m.loBMP = NULL m.loFont0 = NULL m.loFont = NULL RETURN
IMPORTANT
SEE ALSO
Unicode Controls in Visual FoxPro - A new faster and efficient aproach
Unicode Button Icons in Visual FoxPro
The code crashes because "m.pnEmptyH" and "m.pnEmptyW" are undefined
ReplyDeleteWhere exactly is this code saving the files to? An "image" sub-folder of what?
ReplyDelete