mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Introduce, and implement as usefully as I can in all front ends, a
new function in the drawing API which permits the display of text from outside basic ASCII. A fallback mechanism is provided so that puzzles can give a list of strings they'd like to display in order of preference and the system will return the best one it can manage; puzzles are required to cope with ASCII-only front ends. [originally from svn r8793]
This commit is contained in:
@ -373,7 +373,7 @@ public class PuzzleApplet extends JApplet implements Runtime.CallJavaCB {
|
||||
case 7: // string
|
||||
gg.setColor(colors[arg2]);
|
||||
{
|
||||
String text = runtime.cstring(arg3);
|
||||
String text = runtime.utfstring(arg3);
|
||||
Font ft = new Font((xarg3 & 0x10) != 0 ? "Monospaced" : "Dialog",
|
||||
Font.PLAIN, 100);
|
||||
int height100 = this.getFontMetrics(ft).getHeight();
|
||||
|
61
devel.but
61
devel.but
@ -1848,6 +1848,54 @@ the back end function \cw{colours()} (\k{backend-colours}).
|
||||
|
||||
This function may be used for both drawing and printing.
|
||||
|
||||
The character set used to encode the text passed to this function is
|
||||
specified \e{by the drawing object}, although it must be a superset
|
||||
of ASCII. If a puzzle wants to display text that is not contained in
|
||||
ASCII, it should use the \cw{text_fallback()} function
|
||||
(\k{drawing-text-fallback}) to query the drawing object for an
|
||||
appropriate representation of the characters it wants.
|
||||
|
||||
\S{drawing-text-fallback} \cw{text_fallback()}
|
||||
|
||||
\c char *text_fallback(drawing *dr, const char *const *strings,
|
||||
\c int nstrings);
|
||||
|
||||
This function is used to request a translation of UTF-8 text into
|
||||
whatever character encoding is expected by the drawing object's
|
||||
implementation of \cw{draw_text()}.
|
||||
|
||||
The input is a list of strings encoded in UTF-8: \cw{nstrings} gives
|
||||
the number of strings in the list, and \cw{strings[0]},
|
||||
\cw{strings[1]}, ..., \cw{strings[nstrings-1]} are the strings
|
||||
themselves.
|
||||
|
||||
The returned string (which is dynamically allocated and must be
|
||||
freed when finished with) is derived from the first string in the
|
||||
list that the drawing object expects to be able to display reliably;
|
||||
it will consist of that string translated into the character set
|
||||
expected by \cw{draw_text()}.
|
||||
|
||||
Drawing implementations are not required to handle anything outside
|
||||
ASCII, but are permitted to assume that \e{some} string will be
|
||||
successfully translated. So every call to this function must include
|
||||
a string somewhere in the list (presumably the last element) which
|
||||
consists of nothing but ASCII, to be used by any front end which
|
||||
cannot handle anything else.
|
||||
|
||||
For example, if a puzzle wished to display a string including a
|
||||
multiplication sign (U+00D7 in Unicode, represented by the bytes C3
|
||||
97 in UTF-8), it might do something like this:
|
||||
|
||||
\c static const char *const times_signs[] = { "\xC3\x97", "x" };
|
||||
\c char *times_sign = text_fallback(dr, times_signs, 2);
|
||||
\c sprintf(buffer, "%d%s%d", width, times_sign, height);
|
||||
\c draw_text(dr, x, y, font, size, align, colour, buffer);
|
||||
\c sfree(buffer);
|
||||
|
||||
which would draw a string with a times sign in the middle on
|
||||
platforms that support it, and fall back to a simple ASCII \cq{x}
|
||||
where there was no alternative.
|
||||
|
||||
\S{drawing-clip} \cw{clip()}
|
||||
|
||||
\c void clip(drawing *dr, int x, int y, int w, int h);
|
||||
@ -2442,6 +2490,19 @@ Implementations of this API which do not provide printing services
|
||||
may define this function pointer to be \cw{NULL}; it will never be
|
||||
called unless printing is attempted.
|
||||
|
||||
\S{drawingapi-text-fallback} \cw{text_fallback()}
|
||||
|
||||
\c char *(*text_fallback)(void *handle, const char *const *strings,
|
||||
\c int nstrings);
|
||||
|
||||
This function behaves exactly like the back end \cw{text_fallback()}
|
||||
function; see \k{drawing-text-fallback}.
|
||||
|
||||
Implementations of this API which do not support any characters
|
||||
outside ASCII may define this function pointer to be \cw{NULL}, in
|
||||
which case the central code in \cw{drawing.c} will provide a default
|
||||
implementation.
|
||||
|
||||
\H{drawingapi-frontend} The drawing API as called by the front end
|
||||
|
||||
There are a small number of functions provided in \cw{drawing.c}
|
||||
|
33
drawing.c
33
drawing.c
@ -127,6 +127,39 @@ void end_draw(drawing *dr)
|
||||
dr->api->end_draw(dr->handle);
|
||||
}
|
||||
|
||||
char *text_fallback(drawing *dr, const char *const *strings, int nstrings)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* If the drawing implementation provides one of these, use it.
|
||||
*/
|
||||
if (dr && dr->api->text_fallback)
|
||||
return dr->api->text_fallback(dr->handle, strings, nstrings);
|
||||
|
||||
/*
|
||||
* Otherwise, do the simple thing and just pick the first string
|
||||
* that fits in plain ASCII. It will then need no translation
|
||||
* out of UTF-8.
|
||||
*/
|
||||
for (i = 0; i < nstrings; i++) {
|
||||
const char *p;
|
||||
|
||||
for (p = strings[i]; *p; p++)
|
||||
if (*p & 0x80)
|
||||
break;
|
||||
if (!*p)
|
||||
return dupstr(strings[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* The caller was responsible for making sure _some_ string in
|
||||
* the list was in plain ASCII.
|
||||
*/
|
||||
assert(!"Should never get here");
|
||||
return NULL; /* placate optimiser */
|
||||
}
|
||||
|
||||
void status_bar(drawing *dr, char *text)
|
||||
{
|
||||
char *rewritten;
|
||||
|
18
gtk.c
18
gtk.c
@ -499,6 +499,17 @@ void gtk_end_draw(void *handle)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_PANGO
|
||||
char *gtk_text_fallback(void *handle, const char *const *strings, int nstrings)
|
||||
{
|
||||
/*
|
||||
* We assume Pango can cope with any UTF-8 likely to be emitted
|
||||
* by a puzzle.
|
||||
*/
|
||||
return dupstr(strings[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct drawing_api gtk_drawing = {
|
||||
gtk_draw_text,
|
||||
gtk_draw_rect,
|
||||
@ -516,7 +527,12 @@ const struct drawing_api gtk_drawing = {
|
||||
gtk_blitter_save,
|
||||
gtk_blitter_load,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
|
||||
NULL, /* line_width */
|
||||
NULL, NULL, /* line_width, line_dotted */
|
||||
#ifdef USE_PANGO
|
||||
gtk_text_fallback,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void destroy(GtkWidget *widget, gpointer data)
|
||||
|
11
nestedvm.c
11
nestedvm.c
@ -167,6 +167,16 @@ void nestedvm_end_draw(void *handle)
|
||||
_call_java(4,2,0,0);
|
||||
}
|
||||
|
||||
char *nestedvm_text_fallback(void *handle, const char *const *strings,
|
||||
int nstrings)
|
||||
{
|
||||
/*
|
||||
* We assume Java can cope with any UTF-8 likely to be emitted
|
||||
* by a puzzle.
|
||||
*/
|
||||
return dupstr(strings[0]);
|
||||
}
|
||||
|
||||
const struct drawing_api nestedvm_drawing = {
|
||||
nestedvm_draw_text,
|
||||
nestedvm_draw_rect,
|
||||
@ -185,6 +195,7 @@ const struct drawing_api nestedvm_drawing = {
|
||||
nestedvm_blitter_load,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
|
||||
NULL, NULL, /* line_width, line_dotted */
|
||||
nestedvm_text_fallback,
|
||||
};
|
||||
|
||||
int jcallback_key_event(int x, int y, int keyval)
|
||||
|
2
nullfe.c
2
nullfe.c
@ -17,6 +17,8 @@ void draw_polygon(drawing *dr, int *coords, int npoints,
|
||||
int fillcolour, int outlinecolour) {}
|
||||
void draw_circle(drawing *dr, int cx, int cy, int radius,
|
||||
int fillcolour, int outlinecolour) {}
|
||||
char *text_fallback(drawing *dr, const char *const *strings, int nstrings)
|
||||
{ return dupstr(strings[0]); }
|
||||
void clip(drawing *dr, int x, int y, int w, int h) {}
|
||||
void unclip(drawing *dr) {}
|
||||
void start_draw(drawing *dr) {}
|
||||
|
15
osx.m
15
osx.m
@ -1344,7 +1344,8 @@ static void osx_draw_text(void *handle, int x, int y, int fonttype,
|
||||
int fontsize, int align, int colour, char *text)
|
||||
{
|
||||
frontend *fe = (frontend *)handle;
|
||||
NSString *string = [NSString stringWithCString:text];
|
||||
NSString *string = [NSString stringWithCString:text
|
||||
encoding:NSUTF8StringEncoding];
|
||||
NSDictionary *attr;
|
||||
NSFont *font;
|
||||
NSSize size;
|
||||
@ -1378,6 +1379,15 @@ static void osx_draw_text(void *handle, int x, int y, int fonttype,
|
||||
|
||||
[string drawAtPoint:point withAttributes:attr];
|
||||
}
|
||||
static char *osx_text_fallback(void *handle, const char *const *strings,
|
||||
int nstrings)
|
||||
{
|
||||
/*
|
||||
* We assume OS X can cope with any UTF-8 likely to be emitted
|
||||
* by a puzzle.
|
||||
*/
|
||||
return dupstr(strings[0]);
|
||||
}
|
||||
struct blitter {
|
||||
int w, h;
|
||||
int x, y;
|
||||
@ -1478,7 +1488,8 @@ const struct drawing_api osx_drawing = {
|
||||
osx_blitter_save,
|
||||
osx_blitter_load,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
|
||||
NULL, /* line_width */
|
||||
NULL, NULL, /* line_width, line_dotted */
|
||||
osx_text_fallback,
|
||||
};
|
||||
|
||||
void deactivate_timer(frontend *fe)
|
||||
|
74
ps.c
74
ps.c
@ -109,7 +109,7 @@ static void ps_draw_text(void *handle, int x, int y, int fonttype,
|
||||
y = ps->ytop - y;
|
||||
ps_setcolour(ps, colour);
|
||||
ps_printf(ps, "/%s findfont %d scalefont setfont\n",
|
||||
fonttype == FONT_FIXED ? "Courier" : "Helvetica",
|
||||
fonttype == FONT_FIXED ? "Courier-L1" : "Helvetica-L1",
|
||||
fontsize);
|
||||
if (align & ALIGN_VCENTRE) {
|
||||
ps_printf(ps, "newpath 0 0 moveto (X) true charpath flattenpath"
|
||||
@ -242,6 +242,49 @@ static void ps_line_dotted(void *handle, int dotted)
|
||||
}
|
||||
}
|
||||
|
||||
static char *ps_text_fallback(void *handle, const char *const *strings,
|
||||
int nstrings)
|
||||
{
|
||||
/*
|
||||
* We can handle anything in ISO 8859-1, and we'll manually
|
||||
* translate it out of UTF-8 for the purpose.
|
||||
*/
|
||||
int i, maxlen;
|
||||
char *ret;
|
||||
|
||||
maxlen = 0;
|
||||
for (i = 0; i < nstrings; i++) {
|
||||
int len = strlen(strings[i]);
|
||||
if (maxlen < len) maxlen = len;
|
||||
}
|
||||
|
||||
ret = snewn(maxlen + 1, char);
|
||||
|
||||
for (i = 0; i < nstrings; i++) {
|
||||
const char *p = strings[i];
|
||||
char *q = ret;
|
||||
|
||||
while (*p) {
|
||||
int c = (unsigned char)*p++;
|
||||
if (c < 0x80) {
|
||||
*q++ = c; /* ASCII */
|
||||
} else if ((c == 0xC2 || c == 0xC3) && (*p & 0xC0) == 0x80) {
|
||||
*q++ = (c << 6) | (*p++ & 0x3F); /* top half of 8859-1 */
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!*p) {
|
||||
*q = '\0';
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
assert(!"Should never reach here");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ps_begin_doc(void *handle, int pages)
|
||||
{
|
||||
psdata *ps = (psdata *)handle;
|
||||
@ -259,6 +302,34 @@ static void ps_begin_doc(void *handle, int pages)
|
||||
fputs("%%IncludeResource: font Helvetica\n", ps->fp);
|
||||
fputs("%%IncludeResource: font Courier\n", ps->fp);
|
||||
fputs("%%EndSetup\n", ps->fp);
|
||||
fputs("%%BeginProlog\n", ps->fp);
|
||||
/*
|
||||
* Re-encode Helvetica and Courier into ISO-8859-1, which gives
|
||||
* us times and divide signs - and also (according to the
|
||||
* Language Reference Manual) a bonus in that the ASCII '-' code
|
||||
* point now points to a minus sign instead of a hyphen.
|
||||
*/
|
||||
fputs("/Helvetica findfont " /* get the font dictionary */
|
||||
"dup maxlength dict dup begin " /* create and open a new dict */
|
||||
"exch " /* move the original font to top of stack */
|
||||
"{1 index /FID ne {def} {pop pop} ifelse} forall "
|
||||
/* copy everything except FID */
|
||||
"/Encoding ISOLatin1Encoding def "
|
||||
/* set the thing we actually wanted to change */
|
||||
"/FontName /Helvetica-L1 def " /* set a new font name */
|
||||
"FontName end exch definefont" /* and define the font */
|
||||
"\n", ps->fp);
|
||||
fputs("/Courier findfont " /* get the font dictionary */
|
||||
"dup maxlength dict dup begin " /* create and open a new dict */
|
||||
"exch " /* move the original font to top of stack */
|
||||
"{1 index /FID ne {def} {pop pop} ifelse} forall "
|
||||
/* copy everything except FID */
|
||||
"/Encoding ISOLatin1Encoding def "
|
||||
/* set the thing we actually wanted to change */
|
||||
"/FontName /Courier-L1 def " /* set a new font name */
|
||||
"FontName end exch definefont" /* and define the font */
|
||||
"\n", ps->fp);
|
||||
fputs("%%EndProlog\n", ps->fp);
|
||||
}
|
||||
|
||||
static void ps_begin_page(void *handle, int number)
|
||||
@ -333,6 +404,7 @@ static const struct drawing_api ps_drawing = {
|
||||
ps_end_doc,
|
||||
ps_line_width,
|
||||
ps_line_dotted,
|
||||
ps_text_fallback,
|
||||
};
|
||||
|
||||
psdata *ps_init(FILE *outfile, int colour)
|
||||
|
@ -189,6 +189,7 @@ void unclip(drawing *dr);
|
||||
void start_draw(drawing *dr);
|
||||
void draw_update(drawing *dr, int x, int y, int w, int h);
|
||||
void end_draw(drawing *dr);
|
||||
char *text_fallback(drawing *dr, const char *const *strings, int nstrings);
|
||||
void status_bar(drawing *dr, char *text);
|
||||
blitter *blitter_new(drawing *dr, int w, int h);
|
||||
void blitter_free(drawing *dr, blitter *bl);
|
||||
@ -516,6 +517,8 @@ struct drawing_api {
|
||||
void (*end_doc)(void *handle);
|
||||
void (*line_width)(void *handle, float width);
|
||||
void (*line_dotted)(void *handle, int dotted);
|
||||
char *(*text_fallback)(void *handle, const char *const *strings,
|
||||
int nstrings);
|
||||
};
|
||||
|
||||
/*
|
||||
|
26
windows.c
26
windows.c
@ -601,10 +601,8 @@ static void win_draw_text(void *handle, int x, int y, int fonttype,
|
||||
HFONT oldfont;
|
||||
TEXTMETRIC tm;
|
||||
SIZE size;
|
||||
#ifdef _WIN32_WCE
|
||||
TCHAR wText[256];
|
||||
MultiByteToWideChar (CP_ACP, 0, text, -1, wText, 256);
|
||||
#endif
|
||||
MultiByteToWideChar (CP_UTF8, 0, text, -1, wText, 256);
|
||||
|
||||
oldfont = SelectObject(fe->hdc, fe->fonts[i].font);
|
||||
if (GetTextMetrics(fe->hdc, &tm)) {
|
||||
@ -613,11 +611,7 @@ static void win_draw_text(void *handle, int x, int y, int fonttype,
|
||||
else
|
||||
xy.y -= tm.tmAscent;
|
||||
}
|
||||
#ifndef _WIN32_WCE
|
||||
if (GetTextExtentPoint32(fe->hdc, text, strlen(text), &size))
|
||||
#else
|
||||
if (GetTextExtentPoint32(fe->hdc, wText, wcslen(wText), &size))
|
||||
#endif
|
||||
if (GetTextExtentPoint32W(fe->hdc, wText, wcslen(wText), &size))
|
||||
{
|
||||
if (align & ALIGN_HCENTRE)
|
||||
xy.x -= size.cx / 2;
|
||||
@ -626,11 +620,7 @@ static void win_draw_text(void *handle, int x, int y, int fonttype,
|
||||
}
|
||||
SetBkMode(fe->hdc, TRANSPARENT);
|
||||
win_text_colour(fe, colour);
|
||||
#ifndef _WIN32_WCE
|
||||
TextOut(fe->hdc, xy.x, xy.y, text, strlen(text));
|
||||
#else
|
||||
ExtTextOut(fe->hdc, xy.x, xy.y, 0, NULL, wText, wcslen(wText), NULL);
|
||||
#endif
|
||||
ExtTextOutW(fe->hdc, xy.x, xy.y, 0, NULL, wText, wcslen(wText), NULL);
|
||||
SelectObject(fe->hdc, oldfont);
|
||||
}
|
||||
}
|
||||
@ -956,6 +946,15 @@ static void win_end_doc(void *handle)
|
||||
}
|
||||
}
|
||||
|
||||
char *win_text_fallback(void *handle, const char *const *strings, int nstrings)
|
||||
{
|
||||
/*
|
||||
* We assume Windows can cope with any UTF-8 likely to be
|
||||
* emitted by a puzzle.
|
||||
*/
|
||||
return dupstr(strings[0]);
|
||||
}
|
||||
|
||||
const struct drawing_api win_drawing = {
|
||||
win_draw_text,
|
||||
win_draw_rect,
|
||||
@ -980,6 +979,7 @@ const struct drawing_api win_drawing = {
|
||||
win_end_doc,
|
||||
win_line_width,
|
||||
win_line_dotted,
|
||||
win_text_fallback,
|
||||
};
|
||||
|
||||
void print(frontend *fe)
|
||||
|
Reference in New Issue
Block a user