6 de agosto de 2021

Iconos de Segoe MDL2 Assets en VFP9 con GDI+

Articulo original: Segoe MDL2 Assets Icons in VFP9 with Gdi+
http://vfpimaging.blogspot.com/2021/04/segoe-mdl2-assets-icons-in-vfp9-with-gdi.html
Autor: Cesar Ch.
Traducido por: Luis María Guayán


Como se discutió anteriormente en este blog, VFP no puede mostrar de forma nativa ningún carácter que tenga su CHR() mayor que 0xFF (decimal 255).

Hay varias fuentes muy interesantes que traen íconos muy interesantes y actualizados que podríamos usar en nuestras aplicaciones, como SEGOE MDL2 ASSETS, utilizado por Windows 10 en todas partes.

Los Unicodes se pueden obtener directamente a través de CharMap.EXE o en toda la web. Aquí hay un excelente punto de partida: https://docs.microsoft.com/en-us/windows/uwp/design/style/segoe-ui-symbol-font

Los ejemplos a continuación usan GDI+ para guardar cualquier carácter deseado como una imagen, lo que nos permitirá usar esas imágenes geniales en nuestras aplicaciones. Usan las clases _GDIPLUS.VCX FFC, pero también es muy fácil de adaptar a GdiPlusX, si es necesario.

¡Adáptalo a tus necesidades!

Básicamente, una función que recupera un solo carácter Unicode y lo guarda como un archivo de imagen.

Uso:
Para obtener el icono "Imprimir":

EXTRAIGA UN ÚNICO ICONO

lcFile = "Imprimir.bmp"
lcUnicode = "e749"
lcFont = "ACTIVOS SEGOE MDL2"
lnSize = 32 && píxeles
lnForeColor = RGB (0, 0, 255) && Negro
lnBackColor = RGB (255, 255, 255) && Blanco
= MakeImageFromUnicode (m.lcFile, lcUnicode, lcFont, lnSize, lnForeColor, lnBackColor)

Guarde el siguiente código como "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

La función también le permite crear nuevos íconos fusionando dos, en este caso, el ícono de Impresora y Configuración en la parte inferior derecha:

PERSONALIZA TUS ICONOS

* 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

EXTRACCIÓN DE TODOS LOS ICONOS DE UNA FUENTE

La función anterior se puede adaptar para extraer todos los caracteres de una fuente determinada, utilizando un bucle.

Las fuentes suelen tener algunos códigos que no se utilizan, por lo que en el siguiente código utilicé un truco simple para detectar las dimensiones vacías de la fuente, y cada vez que se cumplan las mismas condiciones en el bucle, se descartará el Unicode.

Simplemente ejecute el siguiente código para extraer todos los íconos de cualquier fuente determinada, con el tamaño y los colores de imagen deseados. ¡Ajuste las variables iniciales para que se adapten a sus necesidades!

* 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)
  IF m.loSizeReal.W == m.pnEmptyW AND m.loSizeReal.H == m.pnEmptyH
    LOOP
  ENDIF

  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

IMPORTANTE

No olvide que todas las fuentes tienen licencia. Eso significa que primero debe verificar si está autorizado a distribuir las imágenes generadas. Asegúrese de leer el EULA y ver qué puede o no puede hacer con ellos, ¿de acuerdo?

VEA TAMBIEN

1 comentario :

  1. Muy bueno...
    En el primer ejemplo cambiar nombre de la fuente "ACTIVOS SEGOE MDL2" por "SEGOE MDL2 ASSETS"

    ResponderBorrar

Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.