Java中的TrueType解析?

发布于 2024-07-14 05:42:47 字数 90 浏览 7 评论 0原文

是否有一个简单的库可以解析 True Type 字体文件并为我提供矢量/点列表供我自己渲染它? 我说的是类似于 freetype 的东西,但是是针对 Java 的。

Is there a simple library that can parse a True Type font file and give me a list of vectors/points for me to render it myself? I'm talking about something similar to freetype, but for Java.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

泛泛之交 2024-07-21 05:42:47

Java 可以开箱即用地做到这一点。 前一段时间,我探索了这个领域。

我使用 Processing 进行简单/快速的图形渲染,但访问字体信息的代码是纯 Java 。 所以也许你会对我的小程序感兴趣。 我刚刚将其粘贴到 Pastebin

/**
Get a font and display glyphs with additional information
(bounding boxes, anchor & control points...)

by Philippe Lhoste <PhiLho(a)GMX.net> http://Phi.Lho.free.fr & http://PhiLho.deviantART.com
*/
/* File/Project history:
 1.01.000 -- 2008/08/28 (PL) -- Some improvements.
 1.00.000 -- 2008/08/28 (PL) -- Creation.
*/
/* Copyright notice: For details, see the following file:
http://Phi.Lho.free.fr/softwares/PhiLhoSoft/PhiLhoSoftLicence.txt
This program is distributed under the zlib/libpng license.
Copyright (c) 2008 Philippe Lhoste / PhiLhoSoft
*/

import java.awt.font.*;
import java.awt.geom.*;

final static int DEMO_ID = 2;
final static String FONT_NAME = "Times New Roman";
final static String STRING = "p@";

int FONT_SIZE, SCALE;
Graphics2D g2;
AffineTransform transform;
HashMap colorList = new HashMap();

void setup()
{
  size(1000, 700);

  smooth();
  noLoop();
  background(150);
  noFill();

  g2 = ((PGraphicsJava2D) g).g2;

  // Move drawing to a convenient place
  transform = new AffineTransform();
  if (DEMO_ID == 1)
  {
    FONT_SIZE = 1;
    SCALE = 300;
    transform.translate(40, 320);
    transform.scale(SCALE, SCALE);
  }
  else if (DEMO_ID == 2)
  {
    FONT_SIZE = 2;
    SCALE = 300;
    transform.translate(50, 500);
    transform.scale(SCALE, SCALE);
  }
  else if (DEMO_ID == 3)
  {
    FONT_SIZE = 2;
    SCALE = 300;
    transform.translate(50, 500);
    transform.scale(SCALE, SCALE);
  }
//  g2.setTransform(transform);

  // And show the origin (and scale) of drawing
  SetColor(#FF0000); // Update g2
  strokeWeight(5);
  Line2D.Double line = new Line2D.Double(-0.1, -0.1, 0.1, 0.1);
  g2.draw(transform.createTransformedShape(line));
  line = new Line2D.Double(-0.1, 0.1, 0.1, -0.1);
  g2.draw(transform.createTransformedShape(line));
  strokeWeight(2);

  // Now, we get the font
  Font font = new Font(FONT_NAME, Font.PLAIN, FONT_SIZE);
  g2.setFont(font);
  FontRenderContext frc = g2.getFontRenderContext();

  if (DEMO_ID == 1)
  {
//~     String str = "%&@";
    GlyphVector glyphVector = font.createGlyphVector(frc, STRING);
    for (int i = 0; i < STRING.length(); i++)
    {
      strokeWeight(2);

      SetColor(#00FF00);
      Shape vbs = glyphVector.getGlyphVisualBounds(i);
      g2.draw(transform.createTransformedShape(vbs));

      SetColor(#005000);
      Rectangle r = glyphVector.getGlyphPixelBounds(i, null, 0.0f, 0.0f);
      g2.draw(transform.createTransformedShape(r));

      SetColor(#A0A000);
      Shape lbs = glyphVector.getGlyphLogicalBounds(i);
      g2.draw(transform.createTransformedShape(lbs));

      SetColor(#0050F0);
      strokeWeight(5);

      Shape shape = glyphVector.getGlyphOutline(i);
      g2.draw(transform.createTransformedShape(shape));
    }

    // Draw the whole string at once
    SetColor(#F0A000);
    strokeWeight(1);

    Shape shape = glyphVector.getOutline();
    g2.draw(transform.createTransformedShape(shape));
  }
  else if (DEMO_ID == 2)
  {
    GlyphVector glyphVector = font.createGlyphVector(frc, STRING);
    for (int i = 0; i < STRING.length(); i++)
    {
      SetColor(#0050F0);
      strokeWeight(5);

      Shape shape = glyphVector.getGlyphOutline(i);
      g2.draw(transform.createTransformedShape(shape));

      HighlightPoints(shape);
    }
  }
  else if (DEMO_ID == 3) // To test the SEG_CUBICTO case!
  {
    SetColor(#0050F0);
    strokeWeight(5);

    GeneralPath shape = new GeneralPath();
    shape.moveTo(1.5, 0.0);
    shape.lineTo(1.8, 0.0);
    shape.quadTo(2.0, 0.3, 1.5, 0.3);
    shape.lineTo(1.1, 0.3);
    shape.curveTo(0.7, 0.3, 0.5, -0.5, 1.2, -0.2);
    shape.lineTo(1.5, -0.3);
    shape.closePath();

    g2.draw(transform.createTransformedShape(shape));

    HighlightPoints(shape);
  }
}

void HighlightPoints(Shape shape)
{
  strokeWeight(1);

  PathIterator iterator = shape.getPathIterator(null);
  float[] coords = new float[6];
  float px = 0, py = 0;
  while (!iterator.isDone())
  {
    int type = iterator.currentSegment(coords);
    switch (type)
    {
    case PathIterator.SEG_MOVETO: // One point
      print("Move ");
      px = coords[0];
      py = coords[1];
      DrawMovePoint(px, py);
      break;
    case PathIterator.SEG_LINETO:
      print("Line ");
      px = coords[0];
      py = coords[1];
      DrawLinePoint(px, py);
      break;
    case PathIterator.SEG_QUADTO: // Two points
      print("Quad ");
      DrawControlLine(coords[0], coords[1], px, py);
      px = coords[2];
      py = coords[3];
      DrawControlLine(coords[0], coords[1], px, py);
      DrawControlPoint(coords[0], coords[1]);
      DrawQuadPoint(px, py);
      break;
    case PathIterator.SEG_CUBICTO: // Three points
      print("Cubic "); // Not seen yet...
      DrawControlLine(coords[0], coords[1], px, py);  // Connect to last point
      px = coords[4];
      py = coords[5];
      DrawControlLine(coords[2], coords[3], px, py);
      DrawControlPoint(coords[0], coords[1]);
      DrawControlPoint(coords[2], coords[3]);
      DrawCubicPoint(px, py);
      break;
    case PathIterator.SEG_CLOSE:  // No points
    }
    iterator.next();
  }
}

void DrawMovePoint(float x, float y)
{
  float radius = 2.5 / SCALE;
  Ellipse2D.Float e = new Ellipse2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
  SetColor(#FF00A0);
  g2.fill(transform.createTransformedShape(e));
  SetColor(#FF00FF);
  g2.draw(transform.createTransformedShape(e));
}

void DrawLinePoint(float x, float y)
{
  float radius = 4.5 / SCALE;
  Ellipse2D.Float e = new Ellipse2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
  SetColor(#FF00FF);
  g2.draw(transform.createTransformedShape(e));
}

void DrawQuadPoint(float x, float y)
{
  SetColor(#80FF00);
  float radius = 3.0 / SCALE;
  Rectangle2D.Float r = new Rectangle2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
  g2.draw(transform.createTransformedShape(r));
}

void DrawCubicPoint(float x, float y)
{
  SetColor(#FF8000);
  float radius = 5.0 / SCALE;
  Rectangle2D.Float r = new Rectangle2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
  g2.draw(transform.createTransformedShape(r));
}

void DrawControlPoint(float x, float y)
{
  SetColor(#D0E000);
  float radius = 3.0 / SCALE;
  Ellipse2D.Float e = new Ellipse2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
  g2.draw(transform.createTransformedShape(e));
}

void DrawControlLine(float x1, float y1, float x2, float y2)
{
  SetColor(#FFA000);
  Line2D.Float l = new Line2D.Float(x1, y1, x2, y2);
  g2.draw(transform.createTransformedShape(l));
}

Color GetColor(color c)
{
  Integer ic = Integer.valueOf(c);  // New to 1.5! Cache values
  Color k = (Color) colorList.get(ic);
  if (k == null)
  {
    k = new Color(ic);
    colorList.put(ic, k);
  }
  return k;
}
void SetColor(color c)
{
  Color k = GetColor(c);
  g2.setPaint(k);
}

Java can do that out of the box. Some time ago, I explored this field.

I used Processing for easy/fast graphic rendering, but the code to access font information is pure Java. So maybe my little program can be of interest for you. I just pasted it in Pastebin

/**
Get a font and display glyphs with additional information
(bounding boxes, anchor & control points...)

by Philippe Lhoste <PhiLho(a)GMX.net> http://Phi.Lho.free.fr & http://PhiLho.deviantART.com
*/
/* File/Project history:
 1.01.000 -- 2008/08/28 (PL) -- Some improvements.
 1.00.000 -- 2008/08/28 (PL) -- Creation.
*/
/* Copyright notice: For details, see the following file:
http://Phi.Lho.free.fr/softwares/PhiLhoSoft/PhiLhoSoftLicence.txt
This program is distributed under the zlib/libpng license.
Copyright (c) 2008 Philippe Lhoste / PhiLhoSoft
*/

import java.awt.font.*;
import java.awt.geom.*;

final static int DEMO_ID = 2;
final static String FONT_NAME = "Times New Roman";
final static String STRING = "p@";

int FONT_SIZE, SCALE;
Graphics2D g2;
AffineTransform transform;
HashMap colorList = new HashMap();

void setup()
{
  size(1000, 700);

  smooth();
  noLoop();
  background(150);
  noFill();

  g2 = ((PGraphicsJava2D) g).g2;

  // Move drawing to a convenient place
  transform = new AffineTransform();
  if (DEMO_ID == 1)
  {
    FONT_SIZE = 1;
    SCALE = 300;
    transform.translate(40, 320);
    transform.scale(SCALE, SCALE);
  }
  else if (DEMO_ID == 2)
  {
    FONT_SIZE = 2;
    SCALE = 300;
    transform.translate(50, 500);
    transform.scale(SCALE, SCALE);
  }
  else if (DEMO_ID == 3)
  {
    FONT_SIZE = 2;
    SCALE = 300;
    transform.translate(50, 500);
    transform.scale(SCALE, SCALE);
  }
//  g2.setTransform(transform);

  // And show the origin (and scale) of drawing
  SetColor(#FF0000); // Update g2
  strokeWeight(5);
  Line2D.Double line = new Line2D.Double(-0.1, -0.1, 0.1, 0.1);
  g2.draw(transform.createTransformedShape(line));
  line = new Line2D.Double(-0.1, 0.1, 0.1, -0.1);
  g2.draw(transform.createTransformedShape(line));
  strokeWeight(2);

  // Now, we get the font
  Font font = new Font(FONT_NAME, Font.PLAIN, FONT_SIZE);
  g2.setFont(font);
  FontRenderContext frc = g2.getFontRenderContext();

  if (DEMO_ID == 1)
  {
//~     String str = "%&@";
    GlyphVector glyphVector = font.createGlyphVector(frc, STRING);
    for (int i = 0; i < STRING.length(); i++)
    {
      strokeWeight(2);

      SetColor(#00FF00);
      Shape vbs = glyphVector.getGlyphVisualBounds(i);
      g2.draw(transform.createTransformedShape(vbs));

      SetColor(#005000);
      Rectangle r = glyphVector.getGlyphPixelBounds(i, null, 0.0f, 0.0f);
      g2.draw(transform.createTransformedShape(r));

      SetColor(#A0A000);
      Shape lbs = glyphVector.getGlyphLogicalBounds(i);
      g2.draw(transform.createTransformedShape(lbs));

      SetColor(#0050F0);
      strokeWeight(5);

      Shape shape = glyphVector.getGlyphOutline(i);
      g2.draw(transform.createTransformedShape(shape));
    }

    // Draw the whole string at once
    SetColor(#F0A000);
    strokeWeight(1);

    Shape shape = glyphVector.getOutline();
    g2.draw(transform.createTransformedShape(shape));
  }
  else if (DEMO_ID == 2)
  {
    GlyphVector glyphVector = font.createGlyphVector(frc, STRING);
    for (int i = 0; i < STRING.length(); i++)
    {
      SetColor(#0050F0);
      strokeWeight(5);

      Shape shape = glyphVector.getGlyphOutline(i);
      g2.draw(transform.createTransformedShape(shape));

      HighlightPoints(shape);
    }
  }
  else if (DEMO_ID == 3) // To test the SEG_CUBICTO case!
  {
    SetColor(#0050F0);
    strokeWeight(5);

    GeneralPath shape = new GeneralPath();
    shape.moveTo(1.5, 0.0);
    shape.lineTo(1.8, 0.0);
    shape.quadTo(2.0, 0.3, 1.5, 0.3);
    shape.lineTo(1.1, 0.3);
    shape.curveTo(0.7, 0.3, 0.5, -0.5, 1.2, -0.2);
    shape.lineTo(1.5, -0.3);
    shape.closePath();

    g2.draw(transform.createTransformedShape(shape));

    HighlightPoints(shape);
  }
}

void HighlightPoints(Shape shape)
{
  strokeWeight(1);

  PathIterator iterator = shape.getPathIterator(null);
  float[] coords = new float[6];
  float px = 0, py = 0;
  while (!iterator.isDone())
  {
    int type = iterator.currentSegment(coords);
    switch (type)
    {
    case PathIterator.SEG_MOVETO: // One point
      print("Move ");
      px = coords[0];
      py = coords[1];
      DrawMovePoint(px, py);
      break;
    case PathIterator.SEG_LINETO:
      print("Line ");
      px = coords[0];
      py = coords[1];
      DrawLinePoint(px, py);
      break;
    case PathIterator.SEG_QUADTO: // Two points
      print("Quad ");
      DrawControlLine(coords[0], coords[1], px, py);
      px = coords[2];
      py = coords[3];
      DrawControlLine(coords[0], coords[1], px, py);
      DrawControlPoint(coords[0], coords[1]);
      DrawQuadPoint(px, py);
      break;
    case PathIterator.SEG_CUBICTO: // Three points
      print("Cubic "); // Not seen yet...
      DrawControlLine(coords[0], coords[1], px, py);  // Connect to last point
      px = coords[4];
      py = coords[5];
      DrawControlLine(coords[2], coords[3], px, py);
      DrawControlPoint(coords[0], coords[1]);
      DrawControlPoint(coords[2], coords[3]);
      DrawCubicPoint(px, py);
      break;
    case PathIterator.SEG_CLOSE:  // No points
    }
    iterator.next();
  }
}

void DrawMovePoint(float x, float y)
{
  float radius = 2.5 / SCALE;
  Ellipse2D.Float e = new Ellipse2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
  SetColor(#FF00A0);
  g2.fill(transform.createTransformedShape(e));
  SetColor(#FF00FF);
  g2.draw(transform.createTransformedShape(e));
}

void DrawLinePoint(float x, float y)
{
  float radius = 4.5 / SCALE;
  Ellipse2D.Float e = new Ellipse2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
  SetColor(#FF00FF);
  g2.draw(transform.createTransformedShape(e));
}

void DrawQuadPoint(float x, float y)
{
  SetColor(#80FF00);
  float radius = 3.0 / SCALE;
  Rectangle2D.Float r = new Rectangle2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
  g2.draw(transform.createTransformedShape(r));
}

void DrawCubicPoint(float x, float y)
{
  SetColor(#FF8000);
  float radius = 5.0 / SCALE;
  Rectangle2D.Float r = new Rectangle2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
  g2.draw(transform.createTransformedShape(r));
}

void DrawControlPoint(float x, float y)
{
  SetColor(#D0E000);
  float radius = 3.0 / SCALE;
  Ellipse2D.Float e = new Ellipse2D.Float(x - radius, y - radius, 2 * radius, 2 * radius);
  g2.draw(transform.createTransformedShape(e));
}

void DrawControlLine(float x1, float y1, float x2, float y2)
{
  SetColor(#FFA000);
  Line2D.Float l = new Line2D.Float(x1, y1, x2, y2);
  g2.draw(transform.createTransformedShape(l));
}

Color GetColor(color c)
{
  Integer ic = Integer.valueOf(c);  // New to 1.5! Cache values
  Color k = (Color) colorList.get(ic);
  if (k == null)
  {
    k = new Color(ic);
    colorList.put(ic, k);
  }
  return k;
}
void SetColor(color c)
{
  Color k = GetColor(c);
  g2.setPaint(k);
}
云朵有点甜 2024-07-21 05:42:47

您可以使用 Batik 解析 TrueType 字体并生成 SVG 文件(SVGFont)。 所以我打赌你可以使用这些库来获取向量。

You can use Batik to parse a TrueType Font and generate an SVG file (SVGFont). So I bet that you could use the libraries to get your vectors out.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文