blob: d1c6cee5e5908e2ab3ffcd6a796d2f695865a1ad [file] [log] [blame]
/*
* Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include "awt_p.h"
#include <string.h>
#include "java_awt_Component.h"
#include "java_awt_Font.h"
#include "java_awt_FontMetrics.h"
#include "sun_awt_X11GraphicsEnvironment.h"
#include "awt_Font.h"
#include "java_awt_Dimension.h"
#include "multi_font.h"
#include "Disposer.h"
#endif /* !HEADLESS */
#include <jni.h>
#ifndef HEADLESS
#include <jni_util.h>
#define defaultXLFD "-*-helvetica-*-*-*-*-12-*-*-*-*-*-iso8859-1"
struct FontIDs fontIDs;
struct PlatformFontIDs platformFontIDs;
static void pDataDisposeMethod(JNIEnv *env, jlong pData);
/* #define FONT_DEBUG 2 */
/* 1- print failures, 2- print all, 3- terminate on failure */
#if FONT_DEBUG
static XFontStruct *XLoadQueryFontX(Display *display, char *name)
{
XFontStruct *result = NULL;
result = XLoadQueryFont(display, name);
#if FONT_DEBUG < 2
if (result == NULL)
#endif
fprintf(stderr, "XLoadQueryFont(\"%s\") -> 0x%x.\n", name, result);
#if FONT_DEBUG >= 3
if (result == NULL)
exit(-1);
#endif
return result;
}
#define XLoadQueryFont XLoadQueryFontX
#endif
#endif /* !HEADLESS */
/*
* Class: java_awt_Font
* Method: initIDs
* Signature: ()V
*/
/* This function gets called from the static initializer for Font.java
to initialize the fieldIDs for fields that may be accessed from C */
JNIEXPORT void JNICALL
Java_java_awt_Font_initIDs
(JNIEnv *env, jclass cls)
{
#ifndef HEADLESS
CHECK_NULL(fontIDs.pData = (*env)->GetFieldID(env, cls, "pData", "J"));
CHECK_NULL(fontIDs.style = (*env)->GetFieldID(env, cls, "style", "I"));
CHECK_NULL(fontIDs.size = (*env)->GetFieldID(env, cls, "size", "I"));
CHECK_NULL(fontIDs.getPeer = (*env)->GetMethodID(env, cls, "getFontPeer",
"()Ljava/awt/peer/FontPeer;"));
CHECK_NULL(fontIDs.getFamily = (*env)->GetMethodID(env, cls, "getFamily_NoClientCode",
"()Ljava/lang/String;"));
#endif /* !HEADLESS */
}
#ifndef HEADLESS
/* fieldIDs for FontDescriptor fields that may be accessed from C */
static struct FontDescriptorIDs {
jfieldID nativeName;
jfieldID charsetName;
} fontDescriptorIDs;
#endif /* !HEADLESS */
/*
* Class: sun_awt_FontDescriptor
* Method: initIDs
* Signature: ()V
*/
/* This function gets called from the static initializer for
FontDescriptor.java to initialize the fieldIDs for fields
that may be accessed from C */
JNIEXPORT void JNICALL
Java_sun_awt_FontDescriptor_initIDs
(JNIEnv *env, jclass cls)
{
#ifndef HEADLESS
CHECK_NULL(fontDescriptorIDs.nativeName =
(*env)->GetFieldID(env, cls, "nativeName", "Ljava/lang/String;"));
CHECK_NULL(fontDescriptorIDs.charsetName =
(*env)->GetFieldID(env, cls, "charsetName", "Ljava/lang/String;"));
#endif /* !HEADLESS */
}
/*
* Class: sun_awt_PlatformFont
* Method: initIDs
* Signature: ()V
*/
/* This function gets called from the static initializer for
PlatformFont.java to initialize the fieldIDs for fields
that may be accessed from C */
JNIEXPORT void JNICALL
Java_sun_awt_PlatformFont_initIDs
(JNIEnv *env, jclass cls)
{
#ifndef HEADLESS
CHECK_NULL(platformFontIDs.componentFonts =
(*env)->GetFieldID(env, cls, "componentFonts",
"[Lsun/awt/FontDescriptor;"));
CHECK_NULL(platformFontIDs.fontConfig =
(*env)->GetFieldID(env,cls, "fontConfig",
"Lsun/awt/FontConfiguration;"));
CHECK_NULL(platformFontIDs.makeConvertedMultiFontString =
(*env)->GetMethodID(env, cls, "makeConvertedMultiFontString",
"(Ljava/lang/String;)[Ljava/lang/Object;"));
CHECK_NULL(platformFontIDs.makeConvertedMultiFontChars =
(*env)->GetMethodID(env, cls, "makeConvertedMultiFontChars",
"([CII)[Ljava/lang/Object;"));
#endif /* !HEADLESS */
}
#ifndef HEADLESS
XFontStruct *
loadFont(Display * display, char *name, int32_t pointSize)
{
XFontStruct *f = NULL;
/* try the exact xlfd name in font configuration file */
f = XLoadQueryFont(display, name);
if (f != NULL) {
return f;
}
/*
* try nearly font
*
* 1. specify FAMILY_NAME, WEIGHT_NAME, SLANT, POINT_SIZE,
* CHARSET_REGISTRY and CHARSET_ENCODING.
* 2. change POINT_SIZE to PIXEL_SIZE
* 3. change FAMILY_NAME to *
* 4. specify only PIXEL_SIZE and CHARSET_REGISTRY/ENCODING
* 5. change PIXEL_SIZE +1/-1/+2/-2...+4/-4
* 6. default font pattern
*/
{
/*
* This code assumes the name contains exactly 14 '-' delimiter.
* If not use default pattern.
*/
int32_t i, length, pixelSize;
Boolean useDefault = FALSE;
char buffer[BUFSIZ], buffer2[BUFSIZ];
char *family = NULL, *style = NULL, *slant = NULL, *encoding = NULL;
char *start = NULL, *end = NULL;
if (strlen(name) > BUFSIZ - 1) {
useDefault = TRUE;
} else {
strcpy(buffer, name);
}
#define NEXT_HYPHEN\
start = end + 1;\
end = strchr(start, '-');\
if (end == NULL) {\
useDefault = TRUE;\
break;\
}\
*end = '\0'
do {
end = buffer;
/* skip FOUNDRY */
NEXT_HYPHEN;
/* set FAMILY_NAME */
NEXT_HYPHEN;
family = start;
/* set STYLE_NAME */
NEXT_HYPHEN;
style = start;
/* set SLANT */
NEXT_HYPHEN;
slant = start;
/* skip SETWIDTH_NAME, ADD_STYLE_NAME, PIXEL_SIZE
POINT_SIZE, RESOLUTION_X, RESOLUTION_Y, SPACING
and AVERAGE_WIDTH */
NEXT_HYPHEN;
NEXT_HYPHEN;
NEXT_HYPHEN;
NEXT_HYPHEN;
NEXT_HYPHEN;
NEXT_HYPHEN;
NEXT_HYPHEN;
NEXT_HYPHEN;
/* set CHARSET_REGISTRY and CHARSET_ENCODING */
encoding = end + 1;
}
while (0);
#define TRY_LOAD\
f = XLoadQueryFont(display, buffer2);\
if (f != NULL) {\
strcpy(name, buffer2);\
return f;\
}
if (!useDefault) {
char *altstyle = NULL;
/* Regular is the style for TrueType fonts -- Type1, F3 use roman */
if (strcmp(style, "regular") == 0) {
altstyle = "roman";
}
#if defined(__linux__) || defined(MACOSX)
if (!strcmp(family, "lucidasans")) {
family = "lucida";
}
#endif
/* try 1. */
jio_snprintf(buffer2, sizeof(buffer2),
"-*-%s-%s-%s-*-*-*-%d-*-*-*-*-%s",
family, style, slant, pointSize, encoding);
TRY_LOAD;
if (altstyle != NULL) {
jio_snprintf(buffer2, sizeof(buffer2),
"-*-%s-%s-%s-*-*-*-%d-*-*-*-*-%s",
family, altstyle, slant, pointSize, encoding);
TRY_LOAD;
}
/* search bitmap font */
pixelSize = pointSize / 10;
/* try 2. */
jio_snprintf(buffer2, sizeof(buffer2),
"-*-%s-%s-%s-*-*-%d-*-*-*-*-*-%s",
family, style, slant, pixelSize, encoding);
TRY_LOAD;
if (altstyle != NULL) {
jio_snprintf(buffer2, sizeof(buffer2),
"-*-%s-%s-%s-*-*-%d-*-*-*-*-*-%s",
family, altstyle, slant, pixelSize, encoding);
TRY_LOAD;
}
/* try 3 */
jio_snprintf(buffer2, sizeof(buffer2),
"-*-*-%s-%s-*-*-%d-*-*-*-*-*-%s",
style, slant, pixelSize, encoding);
TRY_LOAD;
if (altstyle != NULL) {
jio_snprintf(buffer2, sizeof(buffer2),
"-*-*-%s-%s-*-*-%d-*-*-*-*-*-%s",
altstyle, slant, pixelSize, encoding);
TRY_LOAD;
}
/* try 4 */
jio_snprintf(buffer2, sizeof(buffer2),
"-*-*-*-%s-*-*-%d-*-*-*-*-*-%s",
slant, pixelSize, encoding);
TRY_LOAD;
/* try 5. */
jio_snprintf(buffer2, sizeof(buffer2),
"-*-*-*-*-*-*-%d-*-*-*-*-*-%s",
pixelSize, encoding);
TRY_LOAD;
/* try 6. */
for (i = 1; i < 4; i++) {
if (pixelSize < i)
break;
jio_snprintf(buffer2, sizeof(buffer2),
"-*-%s-%s-%s-*-*-%d-*-*-*-*-*-%s",
family, style, slant, pixelSize + i, encoding);
TRY_LOAD;
jio_snprintf(buffer2, sizeof(buffer2),
"-*-%s-%s-%s-*-*-%d-*-*-*-*-*-%s",
family, style, slant, pixelSize - i, encoding);
TRY_LOAD;
jio_snprintf(buffer2, sizeof(buffer2),
"-*-*-*-*-*-*-%d-*-*-*-*-*-%s",
pixelSize + i, encoding);
TRY_LOAD;
jio_snprintf(buffer2, sizeof(buffer2),
"-*-*-*-*-*-*-%d-*-*-*-*-*-%s",
pixelSize - i, encoding);
TRY_LOAD;
}
}
}
strcpy(name, defaultXLFD);
return XLoadQueryFont(display, defaultXLFD);
}
/*
* Hardwired list of mappings for generic font names "Helvetica",
* "TimesRoman", "Courier", "Dialog", and "DialogInput".
*/
static char *defaultfontname = "fixed";
static char *defaultfoundry = "misc";
static char *anyfoundry = "*";
static char *anystyle = "*-*";
static char *isolatin1 = "iso8859-1";
static char *
Style(int32_t s)
{
switch (s) {
case java_awt_Font_ITALIC:
return "medium-i";
case java_awt_Font_BOLD:
return "bold-r";
case java_awt_Font_BOLD + java_awt_Font_ITALIC:
return "bold-i";
case java_awt_Font_PLAIN:
default:
return "medium-r";
}
}
static int32_t
awtJNI_FontName(JNIEnv * env, jstring name, char **foundry, char **facename, char **encoding)
{
char *cname = NULL;
if (JNU_IsNull(env, name)) {
return 0;
}
cname = (char *) JNU_GetStringPlatformChars(env, name, NULL);
if (cname == NULL) {
(*env)->ExceptionClear(env);
JNU_ThrowOutOfMemoryError(env, "Could not create font name");
return 0;
}
/* additional default font names */
if (strcmp(cname, "serif") == 0) {
*foundry = "adobe";
*facename = "times";
*encoding = isolatin1;
} else if (strcmp(cname, "sansserif") == 0) {
*foundry = "adobe";
*facename = "helvetica";
*encoding = isolatin1;
} else if (strcmp(cname, "monospaced") == 0) {
*foundry = "adobe";
*facename = "courier";
*encoding = isolatin1;
} else if (strcmp(cname, "helvetica") == 0) {
*foundry = "adobe";
*facename = "helvetica";
*encoding = isolatin1;
} else if (strcmp(cname, "timesroman") == 0) {
*foundry = "adobe";
*facename = "times";
*encoding = isolatin1;
} else if (strcmp(cname, "courier") == 0) {
*foundry = "adobe";
*facename = "courier";
*encoding = isolatin1;
} else if (strcmp(cname, "dialog") == 0) {
*foundry = "b&h";
*facename = "lucida";
*encoding = isolatin1;
} else if (strcmp(cname, "dialoginput") == 0) {
*foundry = "b&h";
*facename = "lucidatypewriter";
*encoding = isolatin1;
} else if (strcmp(cname, "zapfdingbats") == 0) {
*foundry = "itc";
*facename = "zapfdingbats";
*encoding = "*-*";
} else {
#ifdef DEBUG
jio_fprintf(stderr, "Unknown font: %s\n", cname);
#endif
*foundry = defaultfoundry;
*facename = defaultfontname;
*encoding = isolatin1;
}
if (cname != NULL)
JNU_ReleaseStringPlatformChars(env, name, (const char *) cname);
return 1;
}
struct FontData *
awtJNI_GetFontData(JNIEnv * env, jobject font, char **errmsg)
{
/* We are going to create at most 4 outstanding local refs in this
* function. */
if ((*env)->EnsureLocalCapacity(env, 4) < 0) {
return NULL;
}
if (!JNU_IsNull(env, font) && awtJNI_IsMultiFont(env, font)) {
JNU_CHECK_EXCEPTION_RETURN(env, NULL);
struct FontData *fdata = NULL;
int32_t i, size;
char *fontsetname = NULL;
char *nativename = NULL;
Boolean doFree = FALSE;
jobjectArray componentFonts = NULL;
jobject peer = NULL;
jobject fontDescriptor = NULL;
jstring fontDescriptorName = NULL;
jstring charsetName = NULL;
fdata = (struct FontData *) JNU_GetLongFieldAsPtr(env,font,
fontIDs.pData);
if (fdata != NULL && fdata->flist != NULL) {
return fdata;
}
size = (*env)->GetIntField(env, font, fontIDs.size);
fdata = (struct FontData *) malloc(sizeof(struct FontData));
peer = (*env)->CallObjectMethod(env, font, fontIDs.getPeer);
componentFonts =
(*env)->GetObjectField(env, peer, platformFontIDs.componentFonts);
/* We no longer need peer */
(*env)->DeleteLocalRef(env, peer);
fdata->charset_num = (*env)->GetArrayLength(env, componentFonts);
fdata->flist = (awtFontList *) malloc(sizeof(awtFontList)
* fdata->charset_num);
fdata->xfont = NULL;
for (i = 0; i < fdata->charset_num; i++) {
/*
* set xlfd name
*/
fontDescriptor = (*env)->GetObjectArrayElement(env, componentFonts, i);
fontDescriptorName =
(*env)->GetObjectField(env, fontDescriptor,
fontDescriptorIDs.nativeName);
if (!JNU_IsNull(env, fontDescriptorName)) {
nativename = (char *) JNU_GetStringPlatformChars(env, fontDescriptorName, NULL);
if (nativename == NULL) {
nativename = "";
doFree = FALSE;
} else {
doFree = TRUE;
}
} else {
nativename = "";
doFree = FALSE;
}
fdata->flist[i].xlfd = malloc(strlen(nativename)
+ strlen(defaultXLFD));
jio_snprintf(fdata->flist[i].xlfd, strlen(nativename) + 10,
nativename, size * 10);
if (nativename != NULL && doFree)
JNU_ReleaseStringPlatformChars(env, fontDescriptorName, (const char *) nativename);
/*
* set charset_name
*/
charsetName =
(*env)->GetObjectField(env, fontDescriptor,
fontDescriptorIDs.charsetName);
fdata->flist[i].charset_name = (char *)
JNU_GetStringPlatformChars(env, charsetName, NULL);
if (fdata->flist[i].charset_name == NULL) {
(*env)->ExceptionClear(env);
JNU_ThrowOutOfMemoryError(env, "Could not create charset name");
return NULL;
}
/* We are done with the objects. */
(*env)->DeleteLocalRef(env, fontDescriptor);
(*env)->DeleteLocalRef(env, fontDescriptorName);
(*env)->DeleteLocalRef(env, charsetName);
/*
* set load & XFontStruct
*/
fdata->flist[i].load = 0;
/*
* This appears to be a bogus check. The actual intent appears
* to be to find out whether this is the "base" font in a set,
* rather than iso8859_1 explicitly. Note that iso8859_15 will
* and must also pass this test.
*/
if (fdata->xfont == NULL &&
strstr(fdata->flist[i].charset_name, "8859_1")) {
fdata->flist[i].xfont =
loadFont(awt_display, fdata->flist[i].xlfd, size * 10);
if (fdata->flist[i].xfont != NULL) {
fdata->flist[i].load = 1;
fdata->xfont = fdata->flist[i].xfont;
fdata->flist[i].index_length = 1;
} else {
/* Free any already allocated storage and fonts */
int j = i;
for (j = 0; j <= i; j++) {
free((void *)fdata->flist[j].xlfd);
JNU_ReleaseStringPlatformChars(env, NULL,
fdata->flist[j].charset_name);
if (fdata->flist[j].load) {
XFreeFont(awt_display, fdata->flist[j].xfont);
}
}
free((void *)fdata->flist);
free((void *)fdata);
if (errmsg != NULL) {
*errmsg = "java/lang" "NullPointerException";
}
(*env)->DeleteLocalRef(env, componentFonts);
return NULL;
}
}
}
(*env)->DeleteLocalRef(env, componentFonts);
/*
* XFontSet will create if the peer of TextField/TextArea
* are used.
*/
fdata->xfs = NULL;
JNU_SetLongFieldFromPtr(env,font,fontIDs.pData,fdata);
Disposer_AddRecord(env, font, pDataDisposeMethod, ptr_to_jlong(fdata));
return fdata;
} else {
JNU_CHECK_EXCEPTION_RETURN(env, NULL);
Display *display = NULL;
struct FontData *fdata = NULL;
char fontSpec[1024];
int32_t height;
int32_t oheight;
int32_t above = 0; /* tries above height */
int32_t below = 0; /* tries below height */
char *foundry = NULL;
char *name = NULL;
char *encoding = NULL;
char *style = NULL;
XFontStruct *xfont = NULL;
jstring family = NULL;
if (JNU_IsNull(env, font)) {
if (errmsg != NULL) {
*errmsg = "java/lang" "NullPointerException";
}
return (struct FontData *) NULL;
}
display = XDISPLAY;
fdata = (struct FontData *) JNU_GetLongFieldAsPtr(env,font,fontIDs.pData);
if (fdata != NULL && fdata->xfont != NULL) {
return fdata;
}
family = (*env)->CallObjectMethod(env, font, fontIDs.getFamily);
if (!awtJNI_FontName(env, family, &foundry, &name, &encoding)) {
if (errmsg != NULL) {
*errmsg = "java/lang" "NullPointerException";
}
(*env)->DeleteLocalRef(env, family);
return (struct FontData *) NULL;
}
style = Style((*env)->GetIntField(env, font, fontIDs.style));
oheight = height = (*env)->GetIntField(env, font, fontIDs.size);
while (1) {
jio_snprintf(fontSpec, sizeof(fontSpec), "-%s-%s-%s-*-*-%d-*-*-*-*-*-%s",
foundry,
name,
style,
height,
encoding);
/*fprintf(stderr,"LoadFont: %s\n", fontSpec); */
xfont = XLoadQueryFont(display, fontSpec);
/* XXX: sometimes XLoadQueryFont returns a bogus font structure */
/* with negative ascent. */
if (xfont == (Font) NULL || xfont->ascent < 0) {
if (xfont != NULL) {
XFreeFont(display, xfont);
}
if (foundry != anyfoundry) { /* Use ptr comparison here, not strcmp */
/* Try any other foundry before messing with the sizes */
foundry = anyfoundry;
continue;
}
/* We couldn't find the font. We'll try to find an */
/* alternate by searching for heights above and below our */
/* preferred height. We try for 4 heights above and below. */
/* If we still can't find a font we repeat the algorithm */
/* using misc-fixed as the font. If we then fail, then we */
/* give up and signal an error. */
if (above == below) {
above++;
height = oheight + above;
} else {
below++;
if (below > 4) {
if (name != defaultfontname || style != anystyle) {
name = defaultfontname;
foundry = defaultfoundry;
height = oheight;
style = anystyle;
encoding = isolatin1;
above = below = 0;
continue;
} else {
if (errmsg != NULL) {
*errmsg = "java/io/" "FileNotFoundException";
}
(*env)->DeleteLocalRef(env, family);
return (struct FontData *) NULL;
}
}
height = oheight - below;
}
continue;
} else {
fdata = ZALLOC(FontData);
if (fdata == NULL) {
if (errmsg != NULL) {
*errmsg = "java/lang" "OutOfMemoryError";
}
} else {
fdata->xfont = xfont;
JNU_SetLongFieldFromPtr(env,font,fontIDs.pData,fdata);
Disposer_AddRecord(env, font, pDataDisposeMethod,
ptr_to_jlong(fdata));
}
(*env)->DeleteLocalRef(env, family);
return fdata;
}
}
/* not reached */
}
}
/*
* Registered with the 2D disposer to be called after the Font is GC'd.
*/
static void pDataDisposeMethod(JNIEnv *env, jlong pData)
{
struct FontData *fdata = NULL;
int32_t i = 0;
Display *display = XDISPLAY;
AWT_LOCK();
fdata = (struct FontData *)pData;
if (fdata == NULL) {
AWT_UNLOCK();
return;
}
if (fdata->xfs != NULL) {
XFreeFontSet(display, fdata->xfs);
}
/* AWT fonts are always "multifonts" and probably have been in
* all post 1.0 releases, so this test for multi fonts is
* probably not needed, and the singleton xfont is probably never used.
*/
if (fdata->charset_num > 0) {
for (i = 0; i < fdata->charset_num; i++) {
free((void *)fdata->flist[i].xlfd);
JNU_ReleaseStringPlatformChars(env, NULL,
fdata->flist[i].charset_name);
if (fdata->flist[i].load) {
XFreeFont(display, fdata->flist[i].xfont);
}
}
free((void *)fdata->flist);
/* Don't free fdata->xfont because it is equal to fdata->flist[i].xfont
for some 'i' */
} else {
if (fdata->xfont != NULL) {
XFreeFont(display, fdata->xfont);
}
}
free((void *)fdata);
AWT_UNLOCK();
}
#endif /* !HEADLESS */