【问题标题】:How to map code points to unicode characters depending on the font used?如何根据使用的字体将代码点映射到 unicode 字符?
【发布时间】:2012-09-29 23:21:41
【问题描述】:

客户端打印标签并一直使用一组符号 (?) 字体来执行此操作。该应用程序使用单字节数据库(Oracle with Latin-1)。我要替换的旧应用程序不支持 Unicode。它以某种方式做得很好。我正在编写的替换应用程序应该处理旧数据。

从charmap 应用程序中挑选的符号通常映射到特定的Unicode 字符,但有时它们不会。例如,使用 LAB3 字体看起来像月亮的东西实际上是 U+2014 (EM DASH)。当用户将此字符粘贴到 Swing 文本字段时,该字符的代码点为 8212。它被“移动”到私人使用区域(Windows?Java?)。将此字符保存到数据库,甲骨文认为它不能被安全地编码,并用可怕的¿代替它。因此,我开始将字符移动 8000:保存时 -= 8000,显示字段时 += 8000。不幸的是,我发现其他字符没有移动相同的数量。例如,在一种特定字体中,ž 的代码点为 382,因此我将其移动了 +/-256 以“修复”它。

现在我害怕发现更多奇怪的偏移量,我想知道:我可以使用 Java 进行此映射吗?也许 TTF 字体有一个它编码的 255 个字形的列表,以及什么那些对应的Unicode字符,我可以“正确”吗?

现在我正在使用以下组件:

static String fromDatabase(String str, String fontFamily) {

  if (str != null && fontFamily != null) {
    Font font = new Font(fontFamily, Font.PLAIN, 1);

    boolean changed = false;
    char[] chars = str.toCharArray();
    for (int i = 0; i < chars.length; i++) {
      if (font.canDisplay(chars[i] + 0xF000)) {
        // WE8MSWIN1252 + WinXP
        chars[i] += 0xF000;
        changed = true;
      }
      else if (chars[i] >= 128 && font.canDisplay(chars[i] + 8000)) {
        // WE8ISO8859P1 + WinXP
        chars[i] += 8000;
        changed = true;
      }
      else if (font.canDisplay(chars[i] + 256)) {
        // ž in LAB1 Eastern = 382
        chars[i] += 256;
        changed = true;
      }
    }
    if (changed) str = new String(chars);
  }
  return str;
}

static String toDatabase(String str, String fontFamily) {

  if (str != null && fontFamily != null) {
    boolean changed = false;
    char[] chars = str.toCharArray();
    for (int i = 0; i < chars.length; i++) {
      int chr = chars[i];
      if (chars[i] > 0xF000) {
        // WE8MSWIN1252 + WinXP
        chars[i] -= 0xF000;
        changed = true;
      }
      else if (chars[i] > 8000) {
        // WE8ISO8859P1 + WinXP
        chars[i] = (char) (chars[i] - 8000);
        changed = true;
      }
      else if (chars[i] > 256) {
        // ž in LAB1 Eastern = 382
        chars[i] = (char) (chars[i] - 256);
        changed = true;
      }
    }
    if (changed) return new String(chars);
  }

  return str;
}

【问题讨论】:

  • 您使用的是什么字体,是 Windows 默认字体还是其他常用字体? U+2014=8212 因为2014 是十六进制,所以代码点没有移动。
  • 这些字体似乎是为客户定制设计的,名称如“LAB1 Western”、“LAB2 Cyrillic”和“LAB3 Baltish”等。
  • 我将编辑问题并删除关于“移动”的部分——我将两个问题混为一谈(当我的数据库使用 WE8MSWIN1252 而不是 WE8ISO8859P1 时,我的字符在 0XF000 范围内,剩余的您仍然可以在代码中看到)。
  • 我有两个问题:1) 数据库字符集是 Unicode 还是一些 1 字节的风格? 2) 您是否尝试将 swing/java 设置为与 DB 完全相同的字符集?
  • 数据库现在使用 WE8ISO8859P1 (Latin 1)。我没有将 Swing/Java 设置为相同的字符集。我该怎么做?我认为 Java 在内部使用 Unicode 和 UTF-16 编码?

标签: java windows oracle swing fonts


【解决方案1】:

字体文件肯定有从 Unicode 到字形的映射。不幸的是,字形是完全任意的,不需要与它映射到的角色有任何关系,就像你在 Moon/Em-Dash 中发现的那样。从单字节字符到 Unicode 代码点的映射可能在 Windows Code Page 1252 中找到,但这不是您想要的 - 您希望字符 0x97 等同于月亮字形,例如 ☽ FIRST QUARTER MOON U+263D 而不是 — EM DASH U+2014 .不幸的是,除了查看字体中的每个字符并将其与可用的 Unicode 字符进行比较之外,我无法提出任何建议。

【讨论】:

  • 我最终强制使用 Windows 1252 进行错误编码,客户端后来验证了每一个字形。客户还发现那些不起作用的字形在旧应用程序中也不起作用,所以就我而言,这解决了问题。
【解决方案2】:

只是为了提供关闭,这似乎是有效的:

static String fromDatabase(String str, String fontFamily) {
  if (str != null && fontFamily != null) {
    try {
      byte[] bytes = str.getBytes("ISO-8859-1"); // database encoding
      if (fontFamily.startsWith("LAB")) {
        str = new String(bytes, "Windows-1252");
      }
    }
    catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
  }
  return str;
}

static String toDatabase(String str, String fontFamily) {
  if (str != null && fontFamily != null) {
    try {
      if (fontFamily.startsWith("LAB")) {
        str = new String(str.getBytes("Windows-1252"), "ISO-8859-1");
      }
    }
    catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
  }
  return str;
}

public void exportFormData(EigentumsbezeichnungInformationFormData formData) throws ProcessingException {
  super.exportFormData(formData);
  formData.getWert().setValue(toDatabase(formData.getWert().getValue(), formData.getSchrift().getValue()));
}

public void importFormData(EigentumsbezeichnungInformationFormData formData) throws ProcessingException {
  super.importFormData(formData);
  getWertField().setValue(fromDatabase(formData.getWert().getValue(), formData.getSchrift().getValue()));
}

这是我的解释:数据库使用 ISO 8859-1(又名拉丁语 1)。十年前,客户委托了一堆特殊字体,故意说是Latin-1编码,但实际上显示的是不同的字符。注意这个例子中的 Omega 是如何代替 Ù!

此外,还使用了 Latin-1 未使用的一些字符。最简单的解决方案似乎假设正在使用相应的 Windows 代码页。这允许 Java 将“假 Windows-1252”数据库中的字节转码为 Unicode 并返回。翻译后,Swing 应用程序将使用显示 Omega 字形的字体显示一个 Ù。问题“解决了”。

【讨论】:

    猜你喜欢
    • 2011-04-04
    • 2021-07-10
    • 1970-01-01
    • 2012-02-16
    • 2011-08-31
    • 2018-07-04
    • 2023-03-10
    • 1970-01-01
    • 2021-11-08
    相关资源
    最近更新 更多