-#include "image.h"
+#define _WIN32_WINNT 0x500
+#include "imager.h"
#define STRICT
#include <windows.h>
if (i_wf_bbox(facename, size, text, text_len, bbox)) {
// we have the bbox
}
- i_wf_text(face, im, tx, ty, cl, size, text, len, align, aa);
- i_wf_cp(face, im, tx, ty, channel, size, text, len, align, aa)
+ i_wf_text(face, im, tx, ty, cl, size, text, len, align, aa, utf8);
+ i_wf_cp(face, im, tx, ty, channel, size, text, len, align, aa, utf8)
=head1 DESCRIPTION
#define fixed(x) ((x).value + ((x).fract) / 65536.0)
-static void set_logfont(char *face, int size, LOGFONT *lf);
+static void set_logfont(const char *face, int size, LOGFONT *lf);
-static LPVOID render_text(char *face, int size, char *text, int length, int aa,
- HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm);
+static LPVOID render_text(const char *face, int size, const char *text, int length, int aa,
+ HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm, int utf8);
+static LPWSTR utf8_to_wide_string(char const *text, int text_len, int *wide_chars);
/*
-=item i_wf_bbox(face, size, text, length, bbox)
+=item i_wf_bbox(face, size, text, length, bbox, utf8)
Calculate a bounding box for the text.
=cut
*/
-int i_wf_bbox(char *face, int size, char *text, int length, int *bbox) {
+int i_wf_bbox(const char *face, int size, const char *text, int length, int *bbox,
+ int utf8) {
LOGFONT lf;
HFONT font, oldFont;
HDC dc;
TEXTMETRIC tm;
ABC first, last;
GLYPHMETRICS gm;
- int i;
MAT2 mat;
+ int ascent, descent, max_ascent = -size, min_descent = size;
+ const char *workp;
+ int work_len;
+
+ mm_log((1, "i_wf_bbox(face %s, size %d, text %p, length %d, bbox %p, utf8 %d)\n", face, size, text, length, bbox, utf8));
set_logfont(face, size, &lf);
font = CreateFontIndirect(&lf);
dc = GetDC(NULL);
oldFont = (HFONT)SelectObject(dc, font);
-#if 1
- if (!GetTextExtentPoint32(dc, text, length, &sz)
- || !GetTextMetrics(dc, &tm)) {
- SelectObject(dc, oldFont);
- ReleaseDC(NULL, dc);
- DeleteObject(font);
- return 0;
- }
- /* if there's a way to get a characters ascent/descent reliably, I can't
- see it. GetGlyphOutline() seems to return the same size for
- all characters.
- */
- bbox[1] = bbox[4] = tm.tmDescent;
- bbox[2] = sz.cx;
- bbox[3] = bbox[5] = tm.tmAscent;
-
- if (GetCharABCWidths(dc, text[0], text[0], &first)
- && GetCharABCWidths(dc, text[length-1], text[length-1], &last)) {
- bbox[0] = first.abcA;
- if (last.abcC < 0)
- bbox[2] -= last.abcC;
- }
- else {
- bbox[0] = 0;
+ {
+ char facename[100];
+ if (GetTextFace(dc, sizeof(facename), facename)) {
+ mm_log((1, " face: %s\n", facename));
+ }
}
-#else
- for (i = 0; i < length; ++i) {
- memset(&gm, 0, sizeof(gm));
+
+ workp = text;
+ work_len = 0;
+ while (work_len > 0) {
+ unsigned long c;
+ unsigned char cp;
+
+ if (utf8) {
+ c = i_utf8_advance(&workp, &work_len);
+ if (c == ~0UL) {
+ i_push_error(0, "invalid UTF8 character");
+ return 0;
+ }
+ }
+ else {
+ c = (unsigned char)*text++;
+ --work_len;
+ }
+
+ cp = c > '~' ? '.' : c < ' ' ? '.' : c;
+
memset(&mat, 0, sizeof(mat));
mat.eM11.value = 1;
mat.eM22.value = 1;
- if (GetGlyphOutline(dc, GGO_METRICS, text[i], &gm, 0, NULL, &mat) != GDI_ERROR) {
- printf("%02X: black (%d, %d) origin (%d, %d) cell(%d, %d)\n",
- text[i], gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmptGlyphOrigin.x,
- gm.gmptGlyphOrigin.y, gm.gmCellIncX, gm.gmCellIncY);
- printf(" : mat [ %-8f %-8f ]\n", fixed(mat.eM11), fixed(mat.eM12));
- printf(" [ %-8f %-8f ]\n", fixed(mat.eM21), fixed(mat.eM22));
+ if (GetGlyphOutline(dc, (UINT)c, GGO_METRICS, &gm, 0, NULL, &mat) != GDI_ERROR) {
+ mm_log((2, " glyph '%c' (%02x): bbx (%u,%u) org (%d,%d) inc(%d,%d)\n",
+ cp, c, gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmptGlyphOrigin.x,
+ gm.gmptGlyphOrigin.y, gm.gmCellIncX, gm.gmCellIncY));
+
+ ascent = gm.gmptGlyphOrigin.y;
+ descent = ascent - gm.gmBlackBoxY;
+ if (ascent > max_ascent) max_ascent = ascent;
+ if (descent < min_descent) min_descent = descent;
}
else {
- printf("Could not get metrics for '\\x%02X'\n", text[i]);
+ mm_log((1, " glyph '%c' (%02x): error %d\n", cp, c, GetLastError()));
+ }
+ }
+
+ if (utf8) {
+ int wide_chars;
+ LPWSTR wide_text = utf8_to_wide_string(text, length, &wide_chars);
+
+ if (!wide_text)
+ return 0;
+
+ if (!GetTextExtentPoint32W(dc, wide_text, wide_chars, &sz)
+ || !GetTextMetrics(dc, &tm)) {
+ SelectObject(dc, oldFont);
+ ReleaseDC(NULL, dc);
+ DeleteObject(font);
+ return 0;
}
- if (GetCharABCWidths(dc, text[i], text[i], &first)) {
- printf("%02X: %d %d %d\n", text[i], first.abcA, first.abcB, first.abcC);
+
+ myfree(wide_text);
+ }
+ else {
+ if (!GetTextExtentPoint32(dc, text, length, &sz)
+ || !GetTextMetrics(dc, &tm)) {
+ SelectObject(dc, oldFont);
+ ReleaseDC(NULL, dc);
+ DeleteObject(font);
+ return 0;
}
}
-#endif
+ bbox[BBOX_GLOBAL_DESCENT] = tm.tmDescent;
+ bbox[BBOX_DESCENT] = min_descent == size ? tm.tmDescent : min_descent;
+ bbox[BBOX_POS_WIDTH] = sz.cx;
+ bbox[BBOX_ADVANCE_WIDTH] = sz.cx;
+ bbox[BBOX_GLOBAL_ASCENT] = tm.tmAscent;
+ bbox[BBOX_ASCENT] = max_ascent == -size ? tm.tmAscent : max_ascent;
+
+ if (length
+ && GetCharABCWidths(dc, text[0], text[0], &first)
+ && GetCharABCWidths(dc, text[length-1], text[length-1], &last)) {
+ mm_log((1, "first: %d A: %d B: %d C: %d\n", text[0],
+ first.abcA, first.abcB, first.abcC));
+ mm_log((1, "last: %d A: %d B: %d C: %d\n", text[length-1],
+ last.abcA, last.abcB, last.abcC));
+ bbox[BBOX_NEG_WIDTH] = first.abcA;
+ bbox[BBOX_RIGHT_BEARING] = last.abcC;
+ if (last.abcC < 0)
+ bbox[BBOX_POS_WIDTH] -= last.abcC;
+ }
+ else {
+ bbox[BBOX_NEG_WIDTH] = 0;
+ bbox[BBOX_RIGHT_BEARING] = 0;
+ }
SelectObject(dc, oldFont);
ReleaseDC(NULL, dc);
DeleteObject(font);
- return 6;
+ mm_log((1, " bbox=> negw=%d glob_desc=%d pos_wid=%d glob_asc=%d desc=%d asc=%d adv_width=%d rightb=%d\n", bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6], bbox[7]));
+
+ return BBOX_RIGHT_BEARING + 1;
}
/*
*/
int
-i_wf_text(char *face, i_img *im, int tx, int ty, i_color *cl, int size,
- char *text, int len, int align, int aa) {
+i_wf_text(const char *face, i_img *im, int tx, int ty, const i_color *cl, int size,
+ const char *text, int len, int align, int aa, int utf8) {
unsigned char *bits;
HBITMAP bm;
SIZE sz;
TEXTMETRIC tm;
int top;
- bits = render_text(face, size, text, len, aa, &bm, &sz, &tm);
+ bits = render_text(face, size, text, len, aa, &bm, &sz, &tm, utf8);
if (!bits)
return 0;
line_width = sz.cx * 3;
line_width = (line_width + 3) / 4 * 4;
top = ty;
- if (align)
+ if (align) {
top -= tm.tmAscent;
+ }
+ else {
+ int bbox[BOUNDING_BOX_COUNT];
+
+ i_wf_bbox(face, size, text, len, bbox, utf8);
+ top -= tm.tmAscent - bbox[BBOX_ASCENT];
+ }
for (y = 0; y < sz.cy; ++y) {
for (x = 0; x < sz.cx; ++x) {
i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
for (ch = 0; ch < im->channels; ++ch) {
pel.channel[ch] =
- ((255-scale) * pel.channel[ch] + scale*cl->channel[ch]) / 255.0;
+ ((255-scale) * pel.channel[ch] + scale*cl->channel[ch]) / 255;
}
i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
}
*/
int
-i_wf_cp(char *face, i_img *im, int tx, int ty, int channel, int size,
- char *text, int len, int align, int aa) {
+i_wf_cp(const char *face, i_img *im, int tx, int ty, int channel, int size,
+ const char *text, int len, int align, int aa, int utf8) {
unsigned char *bits;
HBITMAP bm;
SIZE sz;
int line_width;
int x, y;
- int ch;
TEXTMETRIC tm;
int top;
- bits = render_text(face, size, text, len, aa, &bm, &sz, &tm);
+ bits = render_text(face, size, text, len, aa, &bm, &sz, &tm, utf8);
if (!bits)
return 0;
line_width = sz.cx * 3;
line_width = (line_width + 3) / 4 * 4;
top = ty;
- if (align)
+ if (align) {
top -= tm.tmAscent;
+ }
+ else {
+ int bbox[BOUNDING_BOX_COUNT];
+
+ i_wf_bbox(face, size, text, len, bbox, utf8);
+ top -= tm.tmAscent - bbox[BBOX_ASCENT];
+ }
for (y = 0; y < sz.cy; ++y) {
for (x = 0; x < sz.cx; ++x) {
return 1;
}
+/*
+=item i_wf_addfont(char const *filename, char const *resource_file)
+
+Adds a TTF font file as usable by the application.
+
+The font is always added as private to the application.
+
+=cut
+ */
+int
+i_wf_addfont(char const *filename) {
+ i_clear_error();
+
+ if (AddFontResourceEx(filename, FR_PRIVATE, 0)) {
+ return 1;
+ }
+ else {
+ i_push_errorf(0, "Could not add resource: %ld", GetLastError());
+ return 0;
+ }
+}
+
/*
=back
=cut
*/
-static void set_logfont(char *face, int size, LOGFONT *lf) {
+static void set_logfont(const char *face, int size, LOGFONT *lf) {
memset(lf, 0, sizeof(LOGFONT));
lf->lfHeight = -size; /* character height rather than cell height */
- lf->lfCharSet = ANSI_CHARSET;
+ lf->lfCharSet = DEFAULT_CHARSET;
lf->lfOutPrecision = OUT_TT_PRECIS;
lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf->lfQuality = PROOF_QUALITY;
=cut
*/
-static LPVOID render_text(char *face, int size, char *text, int length, int aa,
- HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm) {
+static LPVOID render_text(const char *face, int size, const char *text, int length, int aa,
+ HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm, int utf8) {
BITMAPINFO bmi;
BITMAPINFOHEADER *bmih = &bmi.bmiHeader;
HDC dc, bmpDc;
SIZE sz;
HBITMAP bm, oldBm;
LPVOID bits;
-
+ int wide_count;
+ LPWSTR wide_text;
+
dc = GetDC(NULL);
set_logfont(face, size, &lf);
lf.lfQuality = aa ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY;
#endif
+ if (utf8) {
+ wide_text = utf8_to_wide_string(text, length, &wide_count);
+ }
+ else {
+ wide_text = NULL;
+ }
+
bmpDc = CreateCompatibleDC(dc);
if (bmpDc) {
font = CreateFontIndirect(&lf);
if (font) {
oldFont = SelectObject(bmpDc, font);
- GetTextExtentPoint32(bmpDc, text, length, &sz);
+ if (utf8)
+ GetTextExtentPoint32W(bmpDc, wide_text, wide_count, &sz);
+ else
+ GetTextExtentPoint32(bmpDc, text, length, &sz);
GetTextMetrics(bmpDc, tm);
memset(&bmi, 0, sizeof(bmi));
bmih->biBitCount = 24;
bmih->biCompression = BI_RGB;
bmih->biSizeImage = 0;
- bmih->biXPelsPerMeter = 72 / 2.54 * 100;
+ bmih->biXPelsPerMeter = (LONG)(72 / 2.54 * 100);
bmih->biYPelsPerMeter = bmih->biXPelsPerMeter;
bmih->biClrUsed = 0;
bmih->biClrImportant = 0;
oldBm = SelectObject(bmpDc, bm);
SetTextColor(bmpDc, RGB(255, 255, 255));
SetBkColor(bmpDc, RGB(0, 0, 0));
- TextOut(bmpDc, 0, 0, text, length);
+ if (utf8) {
+ TextOutW(bmpDc, 0, 0, wide_text, wide_count);
+ }
+ else {
+ TextOut(bmpDc, 0, 0, text, length);
+ }
SelectObject(bmpDc, oldBm);
}
else {
DeleteObject(font);
DeleteDC(bmpDc);
ReleaseDC(NULL, dc);
+ if (wide_text)
+ myfree(wide_text);
return NULL;
}
SelectObject(bmpDc, oldFont);
DeleteObject(font);
}
else {
+ if (wide_text)
+ myfree(wide_text);
i_push_errorf(0, "Could not create logical font: %ld",
GetLastError());
DeleteDC(bmpDc);
DeleteDC(bmpDc);
}
else {
+ if (wide_text)
+ myfree(wide_text);
i_push_errorf(0, "Could not create rendering DC: %ld", GetLastError());
ReleaseDC(NULL, dc);
return NULL;
}
+ if (wide_text)
+ myfree(wide_text);
+
ReleaseDC(NULL, dc);
*pbm = bm;
}
/*
+=item utf8_to_wide_string(text, text_len, wide_chars)
+
+=cut
+*/
+
+static
+LPWSTR
+utf8_to_wide_string(char const *text, int text_len, int *wide_chars) {
+ int wide_count = MultiByteToWideChar(CP_UTF8, 0, text, text_len, NULL, 0);
+ LPWSTR result;
+
+ if (wide_count < 0) {
+ i_push_errorf(0, "Could not convert utf8: %ld", GetLastError());
+ return NULL;
+ }
+ ++wide_count;
+ result = mymalloc(sizeof(WCHAR) * wide_count);
+ if (MultiByteToWideChar(CP_UTF8, 0, text, text_len, result, wide_count) < 0) {
+ i_push_errorf(0, "Could not convert utf8: %ld", GetLastError());
+ return NULL;
+ }
+
+ result[wide_count-1] = (WCHAR)'\0';
+ *wide_chars = wide_count - 1;
+
+ return result;
+}
+
+
+/*
+=back
+
=head1 BUGS
Should really use a structure so we can set more attributes.