java - 为什么Windows和Linux(java)上的字体渲染有所不同

标签 java graphics fonts centos

我有一段Java代码,它在缓冲区上绘制文本并将其保存为单色BMP。我在Windows 7和CentOS 6.3上执行了该程序。

使用的字体是arial。 Windows中生成的图像清晰,字符呈现均匀。为什么在Cent OS上,字符太细,看起来像平台缺失而无法渲染某些像素。

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class TimesB extends Canvas {
    private Image img;

    public TimesB() {
    setBackground(Color.white);
    }

      public static void main(String s[]) throws IOException {
        WindowListener l = new WindowAdapter() {
            public void windowClosing(WindowEvent e) {System.exit(0);}
            public void windowClosed(WindowEvent e) {System.exit(0);}
        };


        int width = 400, height = 300;

        // TYPE_INT_ARGB specifies the image format: 8-bit RGBA packed
        // into integer pixels
        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);

        Graphics2D ig2 = bi.createGraphics();
        ig2.setBackground(Color.WHITE);




        Font font = new Font("Arial", Font.PLAIN, 18);
        ig2.setFont(font);


        FontRenderContext frc = ig2.getFontRenderContext();



        System.out.println("Transform Type: " + frc.getTransformType());

        System.out.println("Transform: " + frc.getTransform());

        System.out.println("Anti- Aliasing: " + frc.getAntiAliasingHint());

        String message = "Cantaloupemelone weissfleischig hell, mit Kernen!";

        FontMetrics fontMetrics = ig2.getFontMetrics();
        int stringWidth = fontMetrics.stringWidth(message);
        int stringHeight = fontMetrics.getAscent();
     //   ig2.setPaint(Color.black);

        ig2.drawString(message, 10,30);

        message = "Imazalil, Theiabendazol und orthophenylphenol";
        ig2.drawString(message, 10,60);

        message = "gleichmabig feucht halten, Staunsse vermiden";
        ig2.drawString(message, 10,90);


        WritableRaster raster = bi.getRaster();
             // Put the pixels on the raster. Note that only values 0 and 1 are used for the pixels.
             // You could even use other values: in this type of image, even values are black and odd
             // values are white.
             for(int h=0;h<height;h++)
               for(int w=0;w<width;w++){
                  int iVal =  raster.getSample(w, h, 0);

                  if(iVal == 0) raster.setSample(w,h,0,1);
                  else
                      raster.setSample(w,h,0,0);

               }




       ImageIO.write(bi, "BMP", new File("D:\\project\\Java\\yourImageName_w.BMP"));



    }
}

这不仅适用于Java,甚至与C++一起使用的freetype库也会产生相同的输出。

是因为底层的图形层吗?如何解决?
如何使Linux字体的渲染效果与Windows一样好?

**我无法附加输出,因为我需要10点声望!

最佳答案

传统96dpi屏幕的像素密度太低,无法准确绘制复杂的小形状(例如字母)(对于CJK字形来说更糟)。

因此,在此类屏幕上绘制文本是一种折衷方案:

  • 是否要以glypĥ失真和
    屠夫的字形设计?
  • 您接受一些颜色边缘化,这将有助于平滑形状(子像素定位)吗?
  • 您是否希望一种字体大小的文本运行完全平衡,
    更改字体大小时非线性调整大小的成本?
  • 您是否想要一个可以处理任何字体的文本引擎或文本
    引擎严重依赖仅在某些情况下存在的特殊规则
    字体?
  • 您是否以特定的像素密度为目标进行了积极的优化,但代价是在任何具有不同密度的屏幕上渲染质量下降,并且无法过渡到其他硬件?
  • 您是否优化每个字形的对比度(将茎向像素线移动)或尝试保留茎的位置(适用于流文本和形成连字的任何字形组合)?

  • 这些问题中的任何一个都没有“好的”答案(还有许多其他问题),只有妥协,而不同的系统也会做出不同的妥协。

    为了“在任何地方运行”,SUN过去在JVM中包括自己的专有字体渲染器,尽管最近的Java运行时倾向于使用系统。

    Arial因其长期的Microsoft折衷方案而成为特例。它旨在解决Windows文本呈现问题,而Windows文本呈现旨在隐藏Arial设计问题。

    Windows历史上一直偏重于“高对比度,高失真,非线性调整大小,特殊规则,仅96dpi”选择。因此,Windows用户倾向于发现任何非Windows字体渲染模糊的问题。非Windows用户往往会发现Windows文本渲染失真且难看。

    OSX做出了不同的选择。苹果在设计社区中占有较高的市场份额,因此使计算机文本形状更接近纸上的形状(高密度介质,无形状失真)比获得更高的对比度(但降低形状保真度)更为重要。设计社区使用了很多不同的字体,Apple不能像Microsoft这样的特殊字体。这是高dpi对苹果而言比较容易的原因之一。

    Linux取得了其他成就。主要是为了充分利用所有字体,因为它无法调试像Apple或Microsoft这样经过特殊调整的字体集。 (Linux拥有大量的可调参数,使其像Windows,OSX或其他类似东西)。并且由于有大量的可调参数,因此文本堆栈希望应用程序对其进行设置(通常是从GUI环境继承的)。当您在无头模式下使用freetype(您需要在代码中设置可调参数)时,或者在使用Java(除非我错了,JVM仍然没有设置这些可调参数,这取决于DE来设置)时,这是一个问题。它们,或在显式jvm标志上)。

    Android从Linux继承而来,这就是为什么它可以在不同版本之间更改系统字体而没有特殊问题的原因。智能手机屏幕的高像素密度会有所帮助。

    但是民意调查一再表明,任何计算机用户都将最熟悉其惯用系统的文本呈现,会偏爱其惯用系统的字体,并且讨厌其他字体和其他类型的文本呈现(并迫使他反复使用另一种折衷方案几个月后,偏好会发生变化)。当前没有一种字体渲染比其他字体更好或更差。对于舒适性而言,重要的是大脑解码形状的速度。大脑会日复一日地看到自己的文字进行自我训练。

    因此,在Windows上使用Apple文本渲染的Apple软件遭到了强烈的拒绝,就像Windows在Linux上模拟软件一样。

    呈现文本的“好方法”是让您的用户的系统以习惯的方式呈现文本。 “坏”的方法是尝试重现您的习惯。

    它应该随着高dpi屏幕的变化而改变,因为它们 promise 使像素网格拟合成为文本失真和折衷的根源。但是人们仍然会讨厌暴露于他们不习惯使用的字体(除非是小剂量,否则标题或其他有限的效果)。最有可能的长期影响是屏幕衬线字体的复兴。衬线字体在低密度屏幕上很糟糕,但是人们似乎更喜欢在高密度媒体(例如印刷书籍)上使用它们。

    在位图上预渲染文本时,还可以通过增大大小然后缩小位图来避免大多数系统渲染工件。许多系统文本呈现魔术已调整到本地屏幕,并且无论如何都不能按原样转换到另一个屏幕。

    关于java - 为什么Windows和Linux(java)上的字体渲染有所不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28627918/

    相关文章:

    java - 仅当输入值不为空时,查询生成器才创建更新语句

    java - 如何创建 'get' 方法来返回 JButton

    java - 可以使用graphics2d或grahics向JTextArea中的文本添加效果,例如alpha,阴影吗?

    c++ - 如何准备一个恒定的基准环境

    emacs - 希腊字符影响 emacs 中的行间距

    css - 字体不变

    java - 何时使用 JDBC 对连接、语句和结果集调用 getWarnings()?

    java - 如何在 JSP 中清洁打印 Collections 对象

    r - 在多个绘图之间添加文本

    java - 保存并加载字体/字体颜色