/* Copyright 1997 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /***************************************************/ /* File : Unifont.c */ /* */ /* Purpose: Routines to output ISO10646 characters */ /* using UTF-8. */ /* */ /* Author : K.J.Bracey */ /* */ /* History: 20-Aug-97: Created. */ /***************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <math.h> #include "swis.h" #include "Unicode/iso10646.h" #include "Unicode/utf8.h" #include "event.h" #include "toolbox.h" #include "Global.h" #include "FromROSLib.h" #include "Utils.h" #include "Unifont.h" #define Service_International 0x43 #define Service_International_DefineUCS 7 #ifdef UNIFONT static UCS4 map[0x40]; static char olddefs[0x40][9]; static char sprarea[300]; static char *sprptr; static char *maskptr; static char *bmpptr; static int factors[4]; static int xoff, yoff; /*************************************************/ /* unifont_start_redraw() */ /* */ /* Sets up the system font ISO10646 renderer at */ /* the start of a redraw loop. Must be called */ /* before unifont_write0 is called. */ /*************************************************/ _kernel_oserror *unifont_start_redraw() { int i; for (i = 0xC0; i < 0x100; i++) map[i - 0xC0] = 0xFFFFFFFFu; return NULL; } /*************************************************/ /* unifont_widechar_init() */ /* */ /* Prepare all the necessary sprite gubbins for */ /* the bitmap CJK renderer, and load in the */ /* font file. */ /* */ /* Assumes: Called once only before the first */ /* CJK character is plotted. */ /*************************************************/ static _kernel_oserror *unifont_widechar_init() { _kernel_oserror *e; /* * Prepare a small sprite area in which we prepare the 16x16 * sprite mask to plot */ ((int *)sprarea)[0] = 300; ((int *)sprarea)[2] = 16; e = _swix(OS_SpriteOp, _INR(0,1), 256+9, sprarea); if (e) return e; e = _swix(OS_SpriteOp, _INR(0,6), 256+15, sprarea, "sprite", 0, 16, 16, 18); if (e) return e; e = _swix(OS_SpriteOp, _INR(0,2), 256+29, sprarea, "sprite"); if (e) return e; e = _swix(OS_SpriteOp, _INR(0,2)|_OUT(2), 256+24, sprarea, "sprite", &sprptr); if (e) return e; maskptr = sprptr + ((int *)sprptr)[9]; /* * Load the big bitmap file (yum). */ bmpptr = malloc(1703996); if (!bmpptr) return make_no_memory_error(12); ((int *)bmpptr)[0] = 1703996; ((int *)bmpptr)[2] = 16; e = _swix(OS_SpriteOp, _INR(0,1), 256+9, bmpptr); if (e) return e; e = _swix(OS_SpriteOp, _INR(0,2), 256+10, bmpptr, "Browse:Widefont"); if (e) return e; e = _swix(OS_SpriteOp, _INR(0,2)|_OUT(2), 256+24, bmpptr, "bmp", &bmpptr); if (e) return e; bmpptr = bmpptr + ((int *)bmpptr)[8]; return NULL; } /*************************************************/ /* unifont_prepare_widechar() */ /* */ /* Set up the transformation matrix etc before */ /* plotting the first CJK character in each call */ /* to unifont_write0(). */ /* */ /* Parameters: Width of the system font in */ /* pixels, as set with VDU 23,17,7; */ /* */ /* Height of the system font in */ /* pixels, as set with VDU 23,17,7. */ /*************************************************/ static _kernel_oserror *unifont_prepare_widechar(int xs, int ys) { static int widechar_initted; _kernel_oserror *e; if (!widechar_initted) { e = unifont_widechar_init(); if (e) return e; widechar_initted = 1; } factors[0] = xs; factors[1] = ys; factors[2] = 8; factors[3] = 16; xoff = xs * wimpt_dx() * 2; yoff = (ys - 1) * wimpt_dy(); return NULL; } /*************************************************/ /* unifont_plot_widechar() */ /* */ /* Plot a CJK character. */ /* */ /* Parameters: ISO10646 character code; */ /* */ /* Pointer to current x-coordinate; */ /* */ /* Pointer to current y-coordinate. */ /* */ /* Returns: The coordinates are updated */ /* according to the size of the */ /* character. */ /* */ /* Assumes: unifont_widechar(c) == 1. */ /*************************************************/ static _kernel_oserror *unifont_plot_widechar(UCS4 c, int *xp, int *yp) { _kernel_oserror *e; int y; char *p = bmpptr + (c & 255) * 2 + ((c - 0x3000) >> 8) * 2 * 256 * 16; char *s = maskptr; for (y = 0; y < 16; y++) { s[0] = p[0]; s[1] = p[1]; s += 4; p += 0x200; } e = _swix(OS_SpriteOp, _INR(0,6), 512+50, sprarea, sprptr, *xp, *yp - yoff, 0, factors); if (e) return e; *xp += xoff; return _swix(OS_Plot, _INR(0,2), 0, xoff, 0); } /*************************************************/ /* unifont_write0() */ /* */ /* Print a UTF-8 string in the system font, */ /* using 16x16 bitmaps for CJK characters, and */ /* a new Service_International reason code to */ /* find funny characters. */ /* */ /* Parameters: Pointer to the 0-terminated */ /* string (UTF-8 encoded); */ /* */ /* x-coordinate to plot at; */ /* */ /* y-coordinate to plot at; */ /* */ /* Width of the system font in */ /* pixels, as set with VDU 23,17,7; */ /* */ /* Height of the system font in */ /* pixels, as set with VDU 23,17,7. */ /* */ /* Assumes: VDU 23,17,7 already called to set */ /* character size; */ /* */ /* Graphics background colour set to */ /* desired text foreground colour(!) */ /* */ /* The current alphabet has */ /* characters 0x20 to 0x7E the same */ /* as ASCII/ISO10646; */ /* */ /* unifont_start_redraw called at */ /* start of this redraw. */ /*************************************************/ _kernel_oserror *unifont_write0(const char *s, int x, int y, int xs, int ys) { _kernel_oserror *e; int n; UCS4 code; char c; int widecharready = 0; int xstep = xs * wimpt_dx(); while (*s != 0 && *s != 10 && *s != 13) { /* Get the next code from the UTF-8 string */ s += UTF8_to_UCS4(s, &code); /* If it's ASCII, just print that code */ if (code < 0x7F && code >= 0x20) { e = _swix(OS_WriteI + code, 0); if (e) return e; x += xstep; continue; } if (unifont_widechar(code)) { if (!widecharready) { e = unifont_prepare_widechar(xs, ys); if (e) return e; widecharready = 1; } e = unifont_plot_widechar(code, &x, &y); if (e) return e; continue; } /* Let c = the character we will print. */ c = 0xC0 + (code & 0x3F); /* Is that character already defined correctly? */ if (map[c - 0xC0] != code) { /* No, ask someone to redefine it. First note the previous * definition if we haven't touched it yet */ if (map[c - 0xC0] == 0xFFFFFFFFu) { olddefs[c - 0xC0][0] = c; e = _swix(OS_Word, _INR(0,1), 10, olddefs[c - 0xC0]); if (e) return e; } /* Issue the service call asking someone to define the * character. */ e = _swix(OS_ServiceCall, _INR(1,4)|_OUT(1), Service_International, Service_International_DefineUCS, c, code, &n); if (e) return e; /* If no-one defined it, try to define the replacement * character here instead. */ if (n) { e = _swix(OS_ServiceCall, _INR(1,4)|_OUT(1), Service_International, Service_International_DefineUCS, c, 0xFFFD, &n); if (e) return e; /* If we can't even get the replacement character, print * ? instead. Else, mark this character as having been * found (okay, so it actually failed, but it will speed * up redraw of text containing a lot of instances of an * unknown character. */ if (n) c = '?'; else map[c - 0xC0] = code; } else /* Note that we've defined this one for next time */ map[c - 0xC0] = code; } /* And print it! */ e = _swix(OS_WriteI + c, 0); if (e) return e; x += xstep; } return NULL; } /*************************************************/ /* unifont_end_redraw() */ /* */ /* Clear up at the end of a redraw, resetting */ /* the system font back to what it was. */ /*************************************************/ _kernel_oserror *unifont_end_redraw() { int i; _kernel_oserror *e; /* Put back all the character definitions we've screwed around with */ for (i = 0xC0; i < 0x100; i++) if (map[i - 0xC0] != 0xFFFFFFFFu) { e = _swix(OS_WriteI + 23, 0); if (e) return e; e = _swix(OS_WriteN, _INR(0,1), olddefs[i - 0xC0], 9); if (e) return e; } return NULL; } /*************************************************/ /* unifont_widechar() */ /* */ /* Is a character a "wide" one - ie one plotted */ /* from a special 16x16 bitmap rather than using */ /* the system font. */ /* */ /* Parameters: The ISO 10646 character code. */ /* */ /* Returns: 1 if a wide character, 0 if not. */ /*************************************************/ int unifont_widechar(UCS4 c) { if (c < 0x3000) return 0; if (c < 0xD800) return 1; /* 3000-D7FF: CJK + Hangul */ if (c < 0xF900) return 0; if (c < 0xFB00) return 1; /* F900-FAFF: CJK Compat */ if (c < 0xFE30) return 0; if (c < 0xFE70) return 1; /* FE30-FE70: CJK Compat + Small form variants */ if (c < 0xFF00) return 0; if (c < 0xFF60) return 1; /* FF00-FF5F: Fullwidth ASCII */ if (c < 0xFFE0) return 0; if (c < 0xFFE8) return 1; /* FFE0-FFE7: Fullwidth others */ return 0; } #endif