`
squirel
  • 浏览: 22302 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

openJDK字体渲染修正

阅读更多
现在Java的字体渲染比以前进步多了,但最近装了个Monaco,却发现无论如何不能在idea中很好地显示,在eclipse里很正常,于是切换到eclipse,没用几下就放弃了,eclipse的Scala插件实在太不成熟了,简单的命名重构居然把我的代码改的面目全非,通不过编译了,相比之下idea的Scala插件虽然也有一些问题,但基本还是能用的。google看能不能在idea里正常显示monaco,于是找到这里,2010年的问题,看来这个问题已经存在很久了。

看了回复,大致是两个解决办法:
  1. 修改字体文件
  2. 修改jdk
先试简单的办法,用fontforge把字体的hints和Instructions去掉,一看,果然显示正常了,但一编辑,问题就来了,光标位置字符会出现重叠,根本不能用,看来不是这么简单可以解决的,于是试plan B,参考openJDK fontfix修改代码。

和Linux一样,openJDK也是用freeType2来渲染字体,但相比较,openJDK存在以下几点不同:
  • Linux下的fontconfig可以对单个字体配置不同的渲染参数,但openJDK只能全局设置,就是awt.useSystemAAFontSettings这个参数了,这造成了某些字体不能很好地显示,这可以说是最大的局限
  • 不能使用autohint
  • hintslight也不能用
  • lcdfilter没有开启
要解决这些问题,一种方法是在awt.useSystemAAFontSettings参数之外再为JDK增加字体配置信息,这就比较复杂了,改动太大;还是一种方法是直接读取系统的配置信息,这个简单一些,本文就采用这个方案,并且把系统限定为Linux,字体配置采用fontconfig,所以不能在windows下编译。

openJDK的字体渲染是在jdk/src/share/native/sun/font/freetypeScaler.c这个文件中完成的,只需要修改一个文件就行了。

第一步首先要读取Linux fontconfig的字体配置信息,增加下面两个函数:
static FcPattern* matchedPattern(const FcChar8* family, double ptSize) {
    FcPattern* fcPattern = 0;
    fcPattern = FcPatternCreate();
    FcValue fcValue;
    fcValue.type = FcTypeString;
    fcValue.u.s = family;
    FcPatternAdd(fcPattern, FC_FAMILY, fcValue, FcTrue);
    FcPatternAddBool(fcPattern, FC_SCALABLE, FcTrue);
    FcPatternAddDouble(fcPattern, FC_SIZE, ptSize);
    FcConfigSubstitute(0, fcPattern, FcMatchPattern);
    FcDefaultSubstitute(fcPattern);
    FcResult res;
    FcPattern *pattern = 0;
    pattern = FcFontMatch(0, fcPattern, &res);
    FcPatternDestroy(fcPattern);
    return pattern;
}

typedef struct {
    FT_Int32 loadFlags;
    FT_Render_Mode renderMode;
    FT_LcdFilter lcdFilter;
} RenderProperty;

static void readFontconfig(FTScalerInfo* scalerInfo, FTScalerContext* context, RenderProperty* rp) {

   FcPattern *pattern = matchedPattern((const FcChar8 *)scalerInfo->face->family_name, context->ptsz);

   FT_Int32 load_flags = FT_LOAD_DEFAULT;
   FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL;
   FT_LcdFilter lcd_filter = FT_LCD_FILTER_NONE;

   if (TEXT_AA_OFF == context->aaType) {
      load_flags = FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO;
      render_mode = FT_RENDER_MODE_MONO;
   } else {
      FcBool hinting = FcTrue;
      FcPatternGetBool (pattern, FC_HINTING, 0, &hinting);

      FcBool autohint = FcFalse;
      FcPatternGetBool(pattern, FC_AUTOHINT, 0, &autohint);

      int hint_style = FC_HINT_FULL;
      FcPatternGetInteger(pattern, FC_HINT_STYLE, 0, &hint_style);

      if (!hinting || hint_style == FC_HINT_NONE) {
         load_flags |= FT_LOAD_NO_HINTING;
         if (autohint) load_flags |= FT_LOAD_FORCE_AUTOHINT;
      } else if (FC_HINT_NONE < hint_style && hint_style < FC_HINT_FULL) {
         load_flags |= FT_LOAD_TARGET_LIGHT;
         render_mode = FT_RENDER_MODE_LIGHT;
      } else
         load_flags |= FT_LOAD_TARGET_NORMAL;
      
      switch (context->aaType) {  
      case TEXT_AA_LCD_HRGB:  
      case TEXT_AA_LCD_HBGR:  
         load_flags |= FT_LOAD_TARGET_LCD;  
         render_mode = FT_RENDER_MODE_LCD;  
         break;  
      case TEXT_AA_LCD_VRGB:  
      case TEXT_AA_LCD_VBGR:  
         load_flags |= FT_LOAD_TARGET_LCD_V;  
         render_mode = FT_RENDER_MODE_LCD_V;  
      }  
   }

   if (FT_RENDER_MODE_LCD_V == render_mode || FT_RENDER_MODE_LCD == render_mode) {
      int lcdfilter = FC_LCD_NONE;
      FcPatternGetInteger(pattern, FC_LCD_FILTER, 0, &lcdfilter);
      switch (lcdfilter) {
      case FC_LCD_NONE:
         lcd_filter = FT_LCD_FILTER_NONE;
         break;
      case FC_LCD_LIGHT:
         lcd_filter = FT_LCD_FILTER_LIGHT;
         break;
      case FC_LCD_LEGACY:
         lcd_filter = FT_LCD_FILTER_LEGACY;
         break;
      default:
         lcd_filter = FT_LCD_FILTER_DEFAULT;
      }
   }   

   FcPatternDestroy(pattern);

   rp->loadFlags = load_flags;
   rp->renderMode = render_mode;
   rp->lcdFilter = lcd_filter;
}

需要说明的是,虽然是想全部基于fontconfig的配置信息来渲染字体,但是java的AA设置不仅可以通过awt.useSystemAAFontSettings参数来设置,还可以在java程序内指定,java渲染字体的时候也会根据AA来做一些不同的处理,所以,不能简单地忽略AA设置,而全部采用fontconfig的配置,否则就可能导致显示不正常。最终的结果是fontconfig和awt.useSystemAAFontSettings合在一起来配置openJDK的字体。

awt.useSystemAAFontSettings有4个值:on,off,lcd,lcdv,就是要不要antialias,要的话用哪种antialias算法,它覆盖了fontconfig中的antialias,rgba这两个配置项和hintstyle的部分配置参数,所以读取fontconfig配置信息时,会忽略antialias和rgba这两个配置项,hintstyle的值也是优先采用awt.useSystemAAFontSettings的值,没有覆盖到的才会用fontconfig hintstyle中配置的值(hintslight)。

实际地渲染是在Java_sun_font_FreetypeFontScaler_getGlyphImageNative这个函数中完成的,修改如下:
    //先在函数开始的地方加上这一段,创建一个RenderProperty结构体,用来保存读取的字体配置信息
    RenderProperty* rp = NULL;
    rp = (RenderProperty*) calloc(1, sizeof(RenderProperty));
    if (NULL == rp) {
        return ptr_to_jlong(getNullGlyphImage());
    }

/**这段要注释掉
    if (context->aaType == TEXT_AA_OFF) {
        target = FT_LOAD_TARGET_MONO;
    } else if (context->aaType == TEXT_AA_ON) {
        target = FT_LOAD_TARGET_NORMAL;
    } else if (context->aaType == TEXT_AA_LCD_HRGB ||
               context->aaType == TEXT_AA_LCD_HBGR) {
        target = FT_LOAD_TARGET_LCD;
    } else {
        target = FT_LOAD_TARGET_LCD_V;
    }    
    renderFlags |= target;
*/    

    //读取配置,设置lcdfilter
    readFontconfig(scalerInfo, context, rp);
    renderFlags |= rp->loadFlags;    
    FT_Library_SetLcdFilter(scalerInfo->library, rp->lcdFilter);

    glyph_index = FT_Get_Char_Index(scalerInfo->face, glyphCode);

    error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);

    ......

    if (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE) {
        FT_Render_Glyph(ftglyph, rp->renderMode);//renderMode改成读取过来的
    }
    free(rp);

因为使用了fontconfig的函数,别忘了在文件头上加上#include <fontconfig/fontconfig.h>,同时Make文件也得改一下,具体参考openJDK fontfix。好了,重新build openJDK吧,如何build参考openJDK fontfix的build脚本或这里, build完再运行idea,看看字体是不是跟eclipse里一样了,这下不用羡慕eclipse漂亮的字体了吧。

最后再次说明一下,请注意freetypeScaler.c是放在share/native下的,也就是要求是跨平台的,但fontconfig.h是特定于linux的,所以这样改是破坏了跨平台性的,我只是改来自己用用。如果有需要,可以下载附件里patch,应用然后自己编译一下。



  • 大小: 140.6 KB
分享到:
评论
2 楼 squirel 2014-01-10  
那个没有源代码,改不了
1 楼 chhx 2013-12-01  
问下楼主 oraclejdk 怎么改呢?

相关推荐

    linux或java环境缺少缺少字体 字体.zip

    在IT行业中,尤其是在Linux和Java开发环境中,遇到字体缺失的问题是常见的挑战。这不仅影响了程序的正常运行,还可能对界面展示造成困扰。本文将详细介绍如何解决在Linux和Java环境下缺少字体的问题。 首先,我们要...

    harmonyos2-openjdk-freetype:Debian和Ubuntu的OpenJDKFreeType字体修复

    字体渲染中的错误,该错误已在 OpenJDK 12 中修复。该错误本身在此页面的报告分类标题下进行了描述。 有关此存储库的更多信息以及 (Java Swing) 和 (JavaFX) 使用不同版本的 FreeType 库的详细测试结果,请参阅 。 ...

    ubuntu下openjdk7 openjdk8

    在Ubuntu操作系统中,OpenJDK(Open Source Java Development Kit)是Oracle JDK的开源替代品,提供了Java编程语言的实现和Java虚拟机(JVM)。OpenJDK7和OpenJDK8是两个不同的版本,分别对应Java 7和Java 8。这两个...

    openjdk11openjdk11openjdk11openjdk11openjdk11openjdk11openjdk11o

    openjdk11 full javafx

    openjdk-6.tar.gz

    OpenJDK 6是Java开发工具包的一个开源实现,它为开发者提供了运行和构建Java应用程序所需的环境。在Linux操作系统上,OpenJDK 6扮演着与Oracle JDK相似的角色,但它是免费的,并且遵循GNU General Public License...

    openjdk1.8.zip

    标题“openjdk1.8.zip”表明这是一个包含OpenJDK 1.8版本的压缩文件,主要用于在特定操作系统上进行离线安装。OpenJDK(Open Source Java Development Kit)是Java平台标准版(Java SE)的一个开源实现,由甲骨文...

    openjdk 1.7 windows 64位

    OpenJDK 1.7,也被称为Java Development Kit (JDK) 7,是由OpenJDK社区开发的一个开源实现,它是Java平台标准版(Java SE)的免费版本。这个版本的OpenJDK专为Windows 64位操作系统设计,因此在64位Windows环境下...

    openjdk-8.zip

    OpenJDK 8是Java开发工具包(Java Development Kit)的一个开源实现,它为开发者提供了构建、测试和运行Java应用程序所需的工具和Java虚拟机(JVM)。Ubuntu是一个流行的Linux操作系统,广泛用于服务器和桌面环境。...

    openjdk8 for ubuntu

    openjdk-8-jdk_8u91-b14-0ubuntu4~14.04_amd64.deb openjdk-8-jre_8u91-b14-0ubuntu4~14.04_amd64.deb openjdk-8-jdk-headless_8u91-b14-0ubuntu4~14.04_amd64.deb openjdk-8-jre-headless_8u91-b14-0ubuntu4~14.04_...

    openjdk1.6

    OpenJDK 1.6是Java Development Kit的一个开源实现,它是Java编程语言和Java平台标准版(Java SE)的一部分。OpenJDK项目的目标是提供一个免费、开源的Java实现,鼓励社区参与和透明度,使得开发者可以查看并修改源...

    java17 openjdk17

    java17 openjdk17

    openjdk 1.8 离线 rpm 安装包

    安装顺序可能很重要,通常从最基础的依赖开始,如 tzdata 和字体,最后安装 OpenJDK 本身。 在安装完成后,可以通过 `java -version` 命令验证 Java 是否成功安装并设置为默认版本。对于开发人员来说,还需要确保 `...

    openjdk 17.01 windows版本 解压安装包

    OpenJDK 17.0.1 是一个开源的Java Development Kit(JDK)版本,由OpenJDK项目开发并维护。它遵循GNU General Public License(GPL)版本2,为开发者提供了一个免费的、高性能的Java运行环境和开发工具集。OpenJDK是...

    openjdk11.0.16安装包

    openjdk和jdk的区别如下: openjdk是jdk的开源版本,源代码完全相同,但是部分功能无法使用。 openjdk只包含最精简的JDK,而jdk包含很多其他软件包。 openjdk采用GPL V2协议,而jdk采用JRL协议。 openjdk没有部署...

    openjdk1.8 windows64位操作系统

    标题中的"openjdk1.8 windows64位操作系统"指的是OpenJDK 1.8版本在Windows 64位操作系统的环境下使用的Java开发工具包。OpenJDK是Java Development Kit的开源实现,由甲骨文公司(Oracle)和其他贡献者共同维护。...

    openjdk-17.0.2(openjdk-17.0.2_windows-x64_bin.zip)

    在OpenJDK的版本管理中,大版本号如17代表主要功能版本,而小版本号如0.2则是对上一个主要版本的次要更新,通常包含错误修正和安全性增强。 OpenJDK 包含了多个组件,例如Java虚拟机(JVM)、Java类库、编译器...

    openjdk-17.0.2(openjdk-17.0.2_macos-x64_bin.tar.gz)

    **OpenJDK 17.0.2:深入解析与应用** OpenJDK 17.0.2 是一个开放源代码实现的Java Development Kit,它遵循Java SE(标准版)平台规范。此版本专为macOS x64体系结构设计,确保在苹果操作系统上提供高性能和稳定性...

    OpenJdk-7.zip

    OpenJDK-7是Java开发工具包(Java Development Kit)的一个开源实现,主要由Oracle公司维护,用于开发和运行Java应用程序。Ubuntu是一个基于Debian的Linux操作系统,它为开发者提供了丰富的开发环境。在Ubuntu系统中...

    java-7-openjdk-amd64.tar.gz

    把这些JRL许可证形式的Sun/OracleJDK源码和对应版本的OpenJDK源码进行比较,发现除了文件头的版权注释之外,其余代码基本上都是相同的,只有字体渲染部分存在一点差异,Oracle JDK采用了商业实现,而OpenJDK使用的是...

    openjdk 19 linux版本 解压安装包

    OpenJDK 19是Java Development Kit的一个开源实现,它为Linux操作系统提供了Java运行环境和开发工具。在本文中,我们将深入探讨如何在Linux系统上解压并安装OpenJDK 19。首先,让我们了解OpenJDK及其与JDK的关系。 ...

Global site tag (gtag.js) - Google Analytics