All Downloads are FREE. Search and download functionalities are using the official Maven repository.

playn.java.JavaTextLayout Maven / Gradle / Ivy

/**
 * Copyright 2013 The PlayN Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package playn.java;

import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.List;

import pythagoras.f.Rectangle;

import playn.core.TextFormat;
import playn.core.TextWrap;

class JavaTextLayout extends playn.core.TextLayout {

  public static JavaTextLayout layoutText (JavaGraphics gfx, String text, TextFormat format) {
    // we do some fiddling to work around the fact that TextLayout chokes on the empty string
    AttributedString astring = new AttributedString(text.length() == 0 ? " " : text);
    if (format.font != null) {
      astring.addAttribute(TextAttribute.FONT, gfx.resolveFont(format.font));
    }
    FontRenderContext frc = format.antialias ? gfx.aaFontContext() : gfx.aFontContext();
    return new JavaTextLayout(text, format, new TextLayout(astring.getIterator(), frc));
  }

  public static JavaTextLayout[] layoutText (JavaGraphics gfx, String text, TextFormat format,
                                             TextWrap wrap) {
    // normalize newlines in the text (Windows: CRLF -> LF, Mac OS pre-X: CR -> LF)
    text = normalizeEOL(text);

    // we do some fiddling to work around the fact that TextLayout chokes on the empty string
    String ltext = text.length() == 0 ? " " : text;

    // set up an attributed character iterator so that we can measure the text
    AttributedString astring = new AttributedString(ltext);
    if (format.font != null) {
      astring.addAttribute(TextAttribute.FONT, gfx.resolveFont(format.font));
    }

    List layouts = new ArrayList();
    FontRenderContext frc = format.antialias ? gfx.aaFontContext() : gfx.aFontContext();
    LineBreakMeasurer measurer = new LineBreakMeasurer(astring.getIterator(), frc);
    int lastPos = ltext.length(), curPos = 0;
    char eol = '\n';
    while (curPos < lastPos) {
      int nextRet = ltext.indexOf(eol, measurer.getPosition()+1);
      if (nextRet == -1) {
        nextRet = lastPos;
      }
      TextLayout layout = measurer.nextLayout(wrap.width, nextRet, false);
      int endPos = measurer.getPosition();
      while (curPos < endPos && ltext.charAt(curPos) == eol)
        curPos += 1; // skip over EOLs
      layouts.add(new JavaTextLayout(ltext.substring(curPos, endPos), format, layout));
      curPos = endPos;
    }
    return layouts.toArray(new JavaTextLayout[layouts.size()]);
  }

  private final TextLayout layout;

  JavaTextLayout(String text, TextFormat format, TextLayout layout) {
    super(text, format, computeBounds(layout), layout.getAscent()+layout.getDescent());
    this.layout = layout;
  }

  @Override
  public float ascent () {
    return layout.getAscent();
  }

  @Override
  public float descent () {
    return layout.getDescent();
  }

  @Override
  public float leading () {
    return layout.getLeading();
  }

  void stroke(Graphics2D gfx, float x, float y) {
    paint(gfx, x, y, true);
  }

  void fill(Graphics2D gfx, float x, float y) {
    paint(gfx, x, y, false);
  }

  void paint(Graphics2D gfx, float x, float y, boolean stroke) {
    Object ohint = gfx.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
    try {
      gfx.setRenderingHint(RenderingHints.KEY_ANTIALIASING, format.antialias ?
                           RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);

      float yoff = y + layout.getAscent();
      if (stroke) {
        gfx.translate(x, yoff);
        gfx.draw(layout.getOutline(null));
        gfx.translate(-x, -yoff);
      } else {
        layout.draw(gfx, x, yoff);
      }

    } finally {
      gfx.setRenderingHint(RenderingHints.KEY_ANTIALIASING, ohint);
    }
  }

  private static Rectangle computeBounds (TextLayout layout) {
    Rectangle2D bounds = layout.getBounds();
    // the y position of the bounds includes a negative ascent, but we don't want that showing up
    // in our bounds since we render from 0 rather than from the baseline
    return new Rectangle((float)bounds.getX(), (float)bounds.getY() + layout.getAscent(),
                         (float)bounds.getWidth(), (float)bounds.getHeight());
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy