Ejemplo n.º 1
0
 BufferedImage drawText(boolean doGV) {
   int w = 400;
   int h = 50;
   BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
   Graphics2D g = bi.createGraphics();
   g.setColor(Color.white);
   g.fillRect(0, 0, w, h);
   g.setColor(Color.black);
   Font f = helvFont.deriveFont(Font.PLAIN, 40);
   g.setFont(f);
   int x = 5;
   int y = h - 10;
   if (doGV) {
     FontRenderContext frc = new FontRenderContext(null, true, true);
     GlyphVector gv = f.createGlyphVector(frc, codes);
     g.drawGlyphVector(gv, 5, y);
   } else {
     g.setRenderingHint(
         RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
     g.setRenderingHint(
         RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
     g.drawString(str, x, y);
   }
   return bi;
 }
Ejemplo n.º 2
0
 private static LegendTitle createLegend(String legend1Text, String legend2Text) {
   final LegendItemCollection legendItems = new LegendItemCollection();
   FontRenderContext frc = new FontRenderContext(null, true, true);
   Font legenfont = new Font(Font.SANS_SERIF, Font.BOLD, 12);
   GlyphVector gv = legenfont.createGlyphVector(frc, new char[] {'X', 'X'});
   Shape shape = gv.getVisualBounds();
   Rectangle2D bounds = shape.getBounds2D();
   HatchedRectangle hatchShape =
       new HatchedRectangle(
           bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), 5, 5);
   {
     LegendItem li = new LegendItem(legend1Text, null, null, null, hatchShape, Color.black);
     li.setLabelFont(legenfont);
     legendItems.add(li);
   }
   {
     LegendItem li = new LegendItem(legend2Text, null, null, null, shape, Color.black);
     li.setLabelFont(legenfont);
     legendItems.add(li);
   }
   LegendTitle legend =
       new LegendTitle(
           new LegendItemSource() {
             @Override
             public LegendItemCollection getLegendItems() {
               return legendItems;
             }
           });
   legend.setPosition(RectangleEdge.BOTTOM);
   legend.setMargin(new RectangleInsets(0, 30, 0, 0));
   legend.setPadding(RectangleInsets.ZERO_INSETS);
   legend.setLegendItemGraphicPadding(new RectangleInsets(0, 20, 0, 0));
   return legend;
 }
Ejemplo n.º 3
0
 private boolean canDisplayImpl(char c) {
   if (USE_ALTERNATIVE_CAN_DISPLAY_PROCEDURE) {
     return myFont.createGlyphVector(DUMMY_CONTEXT, new char[] {c}).getGlyphCode(0) > 0;
   } else {
     return myFont.canDisplay(c);
   }
 }
Ejemplo n.º 4
0
  /**
   * Returns a GeometryArray of a glyph in this Font3D.
   *
   * @param c character from which to generate a tessellated glyph.
   * @return a GeometryArray
   * @since Java 3D 1.4
   */
  public GeometryArray getGlyphGeometry(char c) {
    char code[] = {c};
    GlyphVector gv = font.createGlyphVector(frc, code);

    // triangulate the glyph
    GeometryArrayRetained glyph_gar = triangulateGlyphs(gv, code[0]);

    // Assume that triangulateGlyphs returns a triangle array with only coords & normals
    // (and without by-ref, interleaved, etc.)
    assert glyph_gar instanceof TriangleArrayRetained
        : "Font3D: GeometryArray is not an instance of TrangleArray";
    assert glyph_gar.getVertexFormat() == (GeometryArray.COORDINATES | GeometryArray.NORMALS)
        : "Font3D: Illegal GeometryArray format -- only coordinates and normals expected";

    // create a correctly sized TriangleArray
    TriangleArray ga = new TriangleArray(glyph_gar.getVertexCount(), glyph_gar.getVertexFormat());

    // temp storage for coords, normals
    float tmp[] = new float[3];

    int vertexCount = ga.getVertexCount();
    for (int i = 0; i < vertexCount; i++) {
      // copy the glyph geometry to the TriangleArray
      glyph_gar.getCoordinate(i, tmp);
      ga.setCoordinate(i, tmp);

      glyph_gar.getNormal(i, tmp);
      ga.setNormal(i, tmp);
    }

    return ga;
  }
Ejemplo n.º 5
0
  @Override
  protected void paintIcon(
      java.awt.Component c,
      java.awt.Graphics2D g2,
      int width,
      int height,
      java.awt.Paint fillPaint,
      java.awt.Paint drawPaint) {
    java.awt.Font prevFont = g2.getFont();
    g2.setFont(font);

    String text = "A";
    java.awt.FontMetrics fm = g2.getFontMetrics();
    int messageWidth = fm.stringWidth(text);
    int ascent = fm.getMaxAscent();
    int descent = fm.getMaxDescent();
    int x = (width / 2) - (messageWidth / 2);
    int y = ((height / 2) + (ascent / 2)) - (descent / 2);

    java.awt.font.GlyphVector glyphVector = font.createGlyphVector(g2.getFontRenderContext(), text);
    java.awt.Shape outline = glyphVector.getOutline(x, y);
    g2.setPaint(drawPaint);
    g2.draw(outline);

    g2.setPaint(fillPaint);
    g2.fill(outline);
    //		g2.drawString( text, x, y );

    g2.setFont(prevFont);
  }
  /**
   * @param c
   * @param font
   * @return GlyphVector using a default rendering context
   */
  private SVGGlyph createGlyph(int c, Font font) {
    GlyphVector glyphVector =
        font.createGlyphVector(
            // flipping is done by SVGGlyph
            new FontRenderContext(null, true, true),
            // unicode to char
            String.valueOf((char) c));

    // create and store the SVG Glyph
    return new SVGGlyph(glyphVector.getGlyphOutline(0), c, glyphVector.getGlyphMetrics(0));
  }
Ejemplo n.º 7
0
 /**
  * This deals with a bug/peculiarity for the default Mac font: several pixels of the ascent are
  * actually empty. This screws up certain measurements which assume the font is actually a few
  * pixels taller than it really is.
  */
 private static int getUnusedAscent(FontMetrics fm, Font font) {
   Integer value = ascentTable.get(font);
   if (value == null) {
     int recordedAscent = fm.getAscent();
     FontRenderContext frc = new FontRenderContext(new AffineTransform(), false, false);
     GlyphVector gv = font.createGlyphVector(frc, "XYZ");
     Rectangle2D bounds = ShapeBounds.getBounds(gv.getOutline());
     int observedAscent = (int) (Math.ceil(bounds.getHeight()) + .5);
     value = new Integer(recordedAscent - observedAscent);
     ascentTable.put(font, value);
   }
   return value.intValue();
 }
Ejemplo n.º 8
0
  public static Shape generateShapeFromText(Font font, String string) {
    BufferedImage img = GraphicsUtilities.createCompatibleTranslucentImage(1, 1);
    Graphics2D g2 = img.createGraphics();

    try {
      GlyphVector vect = font.createGlyphVector(g2.getFontRenderContext(), string);
      Shape shape = vect.getOutline(0f, (float) -vect.getVisualBounds().getY());

      return shape;
    } finally {
      g2.dispose();
    }
  }
Ejemplo n.º 9
0
    /** Method to repaint this ThreeDSideView. */
    @Override
    public void paint(Graphics g) {
      Dimension dim = getSize();
      g.setColor(Color.WHITE);
      g.fillRect(0, 0, dim.width, dim.height);
      g.setColor(Color.BLACK);
      g.drawLine(0, 0, 0, dim.height - 1);
      g.drawLine(0, dim.height - 1, dim.width - 1, dim.height - 1);
      g.drawLine(dim.width - 1, dim.height - 1, dim.width - 1, 0);
      g.drawLine(dim.width - 1, 0, 0, 0);

      String layerName = (String) dialog.threeDLayerList.getSelectedValue();
      Layer selectedLayer = dialog.curTech.findLayer(layerName);
      for (Iterator<Layer> it = dialog.curTech.getLayers(); it.hasNext(); ) {
        Layer layer = it.next();
        if (layer.isPseudoLayer()) continue;
        // if (!layer.isVisible()) continue;
        if (layer == selectedLayer) g.setColor(Color.RED);
        else g.setColor(Color.BLACK);
        GenMath.MutableDouble thickness = dialog.threeDThicknessMap.get(layer);
        GenMath.MutableDouble distance = dialog.threeDDistanceMap.get(layer);
        double dis = distance.doubleValue() + thickness.doubleValue() / 2;
        int yValue =
            dim.height - (int) ((dis - lowHeight) / (highHeight - lowHeight) * dim.height + 0.5);
        int yHeight = (int) (thickness.doubleValue() / (highHeight - lowHeight) * dim.height + 0.5);
        if (yHeight == 0) {
          g.drawLine(0, yValue, dim.width / 3, yValue);
        } else {
          // yHeight -= 4;
          int firstPart = dim.width / 6;
          int pointPos = dim.width / 4;
          g.drawLine(0, yValue - yHeight / 2, firstPart, yValue - yHeight / 2);
          g.drawLine(0, yValue + yHeight / 2, firstPart, yValue + yHeight / 2);
          g.drawLine(firstPart, yValue - yHeight / 2, pointPos, yValue);
          g.drawLine(firstPart, yValue + yHeight / 2, pointPos, yValue);
          g.drawLine(pointPos, yValue, dim.width / 3, yValue);
        }
        String string = layer.getName();
        Font font = new Font(User.getDefaultFont(), Font.PLAIN, 9);
        g.setFont(font);
        FontRenderContext frc = new FontRenderContext(null, true, true);
        GlyphVector gv = font.createGlyphVector(frc, string);
        LineMetrics lm = font.getLineMetrics(string, frc);
        double txtHeight = lm.getHeight();
        Graphics2D g2 = (Graphics2D) g;
        g2.drawGlyphVector(
            gv, dim.width / 3 + 1, (float) (yValue + txtHeight / 2) - lm.getDescent());
      }
    }
Ejemplo n.º 10
0
  private Shape generateShapeFromText() {
    Font font = new Font(fontFamily, Font.PLAIN, fontSize);
    BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2 = img.createGraphics();

    try {
      GlyphVector vect = font.createGlyphVector(g2.getFontRenderContext(), str);
      float dispX = x;
      float dispY = (float) (y - vect.getVisualBounds().getY());
      Shape shape = vect.getOutline(dispX, dispY);

      return shape;
    } finally {
      g2.dispose();
    }
  }
Ejemplo n.º 11
0
 public void paintComponent(Graphics g) {
   super.paintComponent(g);
   Graphics2D g2 = (Graphics2D) g;
   FontRenderContext frc = new FontRenderContext(null, true, true);
   Font f = helvFont.deriveFont(Font.PLAIN, 40);
   System.out.println("font = " + f.getFontName());
   GlyphVector gv = f.createGlyphVector(frc, codes);
   g.setFont(f);
   g.setColor(Color.white);
   g.fillRect(0, 0, 400, 400);
   g.setColor(Color.black);
   g2.drawGlyphVector(gv, 5, 200);
   g2.setRenderingHint(
       RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
   g2.setRenderingHint(
       RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
   g2.drawString(str, 5, 250);
 }
Ejemplo n.º 12
0
  /**
   * Returns the 3D bounding box of the specified glyph code.
   *
   * @param glyphCode the glyphCode from the original 2D Font
   * @param bounds the 3D glyph's bounds
   */
  public void getBoundingBox(int glyphCode, BoundingBox bounds) {
    int[] gCodes = {glyphCode};
    GlyphVector gVec = font.createGlyphVector(frc, gCodes);
    Rectangle2D.Float bounds2d =
        (Rectangle2D.Float) (((GlyphMetrics) (gVec.getGlyphMetrics(0))).getBounds2D());

    Point3d lower = new Point3d(bounds2d.x, bounds2d.y, 0.0);
    Point3d upper;
    if (fontExtrusion != null) {
      upper =
          new Point3d(
              bounds2d.x + bounds2d.width, bounds2d.y + bounds2d.height, fontExtrusion.length);
    } else {
      upper = new Point3d(bounds2d.x + bounds2d.width, bounds2d.y + bounds2d.height, 0.0);
    }
    bounds.setLower(lower);
    bounds.setUpper(upper);
  }
Ejemplo n.º 13
0
    private static BufferedImage createImage(String s, boolean valid) {
      FontRenderContext frc = new FontRenderContext(null, true, true);
      Font font = new Font("dialog", Font.BOLD, 12);

      GlyphVector glyphs = font.createGlyphVector(frc, s);
      Shape shape = glyphs.getOutline();
      Rectangle bounds = shape.getBounds();

      int imageW = bounds.width;
      int imageH = bounds.height;
      BufferedImage image = new BufferedImage(imageW, imageH, BufferedImage.TYPE_INT_ARGB);
      Graphics2D g = (Graphics2D) image.getGraphics();

      g.setColor(valid ? Color.blue : Color.red);
      g.translate(bounds.x, -bounds.y);
      g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
      g.fill(shape);

      return image;
    }
Ejemplo n.º 14
0
 private void parseProblemGlyphs() {
   myCheckedForProblemGlyphs = true;
   BufferedImage buffer = UIUtil.createImage(20, 20, BufferedImage.TYPE_INT_RGB);
   final Graphics graphics = buffer.getGraphics();
   if (!(graphics instanceof Graphics2D)) {
     return;
   }
   final FontRenderContext context = ((Graphics2D) graphics).getFontRenderContext();
   char[] charBuffer = new char[1];
   for (char c = 0; c < 128; c++) {
     if (!myFont.canDisplay(c)) {
       continue;
     }
     charBuffer[0] = c;
     final GlyphVector vector = myFont.createGlyphVector(context, charBuffer);
     final float y = vector.getGlyphMetrics(0).getAdvanceY();
     if (Math.round(y) != 0) {
       mySymbolsToBreakDrawingIteration.add(c);
     }
   }
   myHasGlyphsToBreakDrawingIteration = !mySymbolsToBreakDrawingIteration.isEmpty();
 }
Ejemplo n.º 15
0
  /**
   * Draws the string at (x, y). If TEXT_AS_SHAPES is set {@link
   * #drawGlyphVector(java.awt.font.GlyphVector, float, float)} is used, otherwise {@link
   * #writeString(String, double, double)} for a more direct output of the string.
   *
   * @param string
   * @param x
   * @param y
   */
  public void drawString(String string, double x, double y) {
    // something to draw?
    if (string == null || string.equals("")) { // $NON-NLS-1$
      return;
    }

    // draw strings directly?
    if (isProperty(TEXT_AS_SHAPES)) {

      Font font = getFont();

      // NOTE, see FVG-199, createGlyphVector does not seem to create the
      // proper glyphcodes
      // for either ZapfDingbats or Symbol. We use our own encoding which
      // seems to work...
      String fontName = font.getName();
      if (fontName.equals("Symbol")
          || fontName.equals("ZapfDingbats")) { // $NON-NLS-1$ //$NON-NLS-2$
        string = FontEncoder.getEncodedString(string, fontName);
        // use a standard font, not Symbol.
        font = new Font("Serif", font.getStyle(), font.getSize()); // $NON-NLS-1$
      }

      // create glyph
      GlyphVector gv = font.createGlyphVector(getFontRenderContext(), string);

      // draw it
      drawGlyphVector(gv, (float) x, (float) y);
    } else {
      // write string directly
      try {
        writeString(string, x, y);
      } catch (IOException e) {
        handleException(e);
      }
    }
  }
Ejemplo n.º 16
0
  public void paintComponent(Graphics g) {
    super.paintComponent(g); // paint background
    Graphics2D g2 = (Graphics2D) g;

    // Turn on antialiasing
    Map m = new HashMap();
    m.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.addRenderingHints(m);

    // Figure out how large the maze is
    Point p = maze.getSize();

    double llx = 0.0, lly = 0.0, width = 0.0, height = 0.0, cellwidth = 0.0, cellheight = 0.0;
    // Do we need to update our cache?
    if ((panelSize == null) || (panelSize != getSize())) {
      panelSize = getSize();
      llx = (double) panelSize.width * 0.025;
      lly = (double) panelSize.height * 0.025;
      width = (double) panelSize.width - (double) panelSize.width * 0.05;
      height = (double) panelSize.height - (double) panelSize.height * 0.05;
      cellwidth = width / (double) p.getX();
      cellheight = height / (double) p.getY();
      buildWalls(llx, lly, cellwidth, cellheight);
      double diameter = java.lang.Math.min(cellwidth, cellheight) * 0.75;
      player =
          new Arc2D.Double(
              new Rectangle2D.Double(-diameter / 2.0, -diameter / 2.0, diameter, diameter),
              30.0,
              300.0,
              Arc2D.PIE);
      diameter = java.lang.Math.min(cellwidth, cellheight) * 0.30;
      projectile =
          new Arc2D.Double(
              new Rectangle2D.Double(-diameter / 2.0, -diameter / 2.0, diameter, diameter),
              0.0,
              360.0,
              Arc2D.PIE);
    }

    // Flip coordinate system
    g2.translate(0.0, (double) panelSize.height);
    g2.scale(1.0, -1.0);

    // Draw the maze walls
    g2.setStroke(new BasicStroke(2.0f));
    g2.setColor(Color.black);
    g2.draw(new Rectangle2D.Double(llx, lly, width, height));

    Iterator it = wallList.iterator();
    while (it.hasNext()) {
      Object o = it.next();
      if (o instanceof Shape) {
        g2.draw((Shape) o);
      } else {
        throw new Error();
      }
    }

    Font font = new Font("Arial", Font.PLAIN, 9);
    FontRenderContext frc = g2.getFontRenderContext();

    // Obtain the location of the distinguished client
    Point cp = maze.getClientPoint(client);

    for (int i = 0; i < p.getY(); i++) {
      for (int j = 0; j < p.getX(); j++) {
        boolean cellVisible = true;
        Line2D visLine =
            new Line2D.Double(
                llx + (cp.getX() + 0.5) * cellwidth,
                lly + (cp.getY() + 0.5) * cellheight,
                llx + (j + 0.5) * cellwidth,
                lly + (i + 0.5) * cellheight);

        /* Visibility testing */
        /* Iterator visIt = wallList.iterator();
        while(visIt.hasNext()) {
                Object o = visIt.next();
                if(o instanceof Line2D) {
                        Line2D l = (Line2D)o;
                        if(l.intersectsLine(visLine)) {
                                cellVisible = false;
                        }
                } else {
                        throw new Error();
                }

        } */
        if (cellVisible) {
          Cell cell = maze.getCell(new Point(j, i));
          Object o = cell.getContents();
          if (o != null) {
            if (o instanceof Client) {
              Client c = (Client) o;
              if (c instanceof GUIClient) {
                g2.setColor(Color.green);
              } else if (c instanceof RobotClient) {
                g2.setColor(Color.red);
              } else if (c instanceof RemoteClient) {
                g2.setColor(Color.magenta);
              }

              double xoffset = llx + j * cellwidth + (cellwidth / 2.0);
              double yoffset = lly + i * cellheight + (cellheight / 2.0);
              Direction orient = c.getOrientation();
              g2.translate(xoffset, yoffset);
              double rotation = 0.0;
              if (orient.equals(Direction.South)) {
                rotation = -java.lang.Math.PI / 2.0;
              } else if (orient.equals(Direction.North)) {
                rotation = java.lang.Math.PI / 2.0;
              } else if (orient.equals(Direction.West)) {
                rotation = java.lang.Math.PI;
              }
              g2.rotate(rotation);
              g2.fill(player);
              g2.rotate(-rotation);

              GlyphVector name = font.createGlyphVector(frc, c.getName());
              g2.scale(1.0, -1.0);
              g2.setColor(Color.black);
              g2.drawGlyphVector(name, 0.0f, 0.0f);
              g2.scale(1.0, -1.0);
              g2.translate(-xoffset, -yoffset);

            } else {
              if (o instanceof Projectile) {
                g2.setColor(Color.yellow);
                double xoffset = llx + j * cellwidth + (cellwidth / 2.0);
                double yoffset = lly + i * cellheight + (cellheight / 2.0);
                g2.translate(xoffset, yoffset);
                g2.fill(projectile);
                g2.translate(-xoffset, -yoffset);
              }
            }
          }
        }
      }
    }
  }
Ejemplo n.º 17
0
  private TessOutput tesselateString(String s) {
    GlyphVector gv = _font.createGlyphVector(_frc, s);

    Shape shape = gv.getOutline();
    //
    AffineTransform at = new AffineTransform();
    at.scale(1, -1);
    PathIterator pIt = shape.getPathIterator(at, _font.getSize() / 200.0);

    // Create a GLU tesselator
    GLUtessellator tess = GLU.gluNewTess();
    CharTesselator tessAdapt = new CharTesselator();

    GLU.gluTessCallback(tess, GLU.GLU_TESS_VERTEX, tessAdapt);
    GLU.gluTessCallback(tess, GLU.GLU_TESS_BEGIN, tessAdapt);
    GLU.gluTessCallback(tess, GLU.GLU_TESS_END, tessAdapt);
    GLU.gluTessCallback(tess, GLU.GLU_TESS_COMBINE, tessAdapt);
    GLU.gluTessCallback(tess, GLU.GLU_TESS_ERROR, tessAdapt);

    int winding = pIt.getWindingRule();

    if (winding == PathIterator.WIND_EVEN_ODD)
      GLU.gluTessProperty(tess, GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_ODD);
    else if (winding == PathIterator.WIND_NON_ZERO)
      GLU.gluTessProperty(tess, GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_NONZERO);
    else assert (false); // PathIterator should only return these two winding rules

    GLU.gluBeginPolygon(tess);
    GLU.gluTessNormal(tess, 0, 0, 1);
    double[] first = null;
    double[] v;
    while (!pIt.isDone()) {
      v = new double[3];
      int type = pIt.currentSegment(v);
      v[2] = 0.0;
      if (type == PathIterator.SEG_MOVETO) {
        first = v;
        GLU.gluNextContour(tess, GLU.GLU_UNKNOWN);
        GLU.gluTessVertex(tess, v, 0, v);
      } else if (type == PathIterator.SEG_LINETO) {
        GLU.gluTessVertex(tess, v, 0, v);
      } else if (type == PathIterator.SEG_CLOSE) {
        assert (first != null); // If this is true, there is an error in the AWT path iterator
        GLU.gluTessVertex(tess, first, 0, first);
        first = null;
      } else {
        assert (false); // The path itertor should not return other path types here
      }
      pIt.next();
    }
    GLU.gluEndPolygon(tess);

    int numVerts = tessAdapt.getVerts().size();
    double[] verts = new double[numVerts];
    int count = 0;
    for (double d : tessAdapt.getVerts()) {
      verts[count++] = d;
    }

    TessOutput ret = new TessOutput();
    ret.verts = verts;
    ret.bounds = gv.getVisualBounds();

    ret.advances = new double[s.length()];
    for (int i = 0; i < s.length(); ++i) {
      ret.advances[i] = gv.getGlyphMetrics(i).getAdvance();
    }
    return ret;
  }
Ejemplo n.º 18
0
  public int print(Graphics g, PageFormat pf, int page) throws PrinterException {
    if (context instanceof TextWindow) {
      TextWindow tw = (TextWindow) context;
      if (page == 0) {
        allStrings = tw.convertToStrings();
        printFont = new Font("Helvetica", Font.PLAIN, 10);
        FontMetrics fm = g.getFontMetrics(printFont);
        pageHeight = (int) pf.getImageableHeight();
        fontHeight = fm.getHeight();
        yOffset = fm.getAscent();
        linesPerPage = pageHeight / fontHeight;
        startLine = startChar = 0;
        startPageNumber = 0;
        startLineAtPage = startLine;
        startCharAtPage = startChar;
        frc = new FontRenderContext(null, true, true);
      }
      if (page == startPageNumber) {
        startLine = startLineAtPage;
        startChar = startCharAtPage;
      } else {
        startPageNumber = page;
        startLineAtPage = startLine;
        startCharAtPage = startChar;
      }
      if (startLine < allStrings.length) {
        g.setColor(Color.WHITE);
        g.fillRect(
            (int) pf.getImageableX(),
            (int) pf.getImageableY(),
            (int) pf.getImageableWidth(),
            (int) pf.getImageableHeight());
        g.setFont(printFont);
        g.setColor(Color.BLACK);
        for (int i = 0; i < linesPerPage; i++) {
          if (startLine >= allStrings.length) break;
          String fullLine = allStrings[startLine];

          // figure bounding box of text
          int endChar = fullLine.length();
          String theLine = null;
          for (; ; ) {
            theLine = fullLine.substring(startChar, endChar);
            GlyphVector gv = printFont.createGlyphVector(frc, theLine);
            Rectangle2D rasRect = gv.getLogicalBounds();
            if (rasRect.getWidth() <= pf.getImageableWidth()) break;
            endChar--;
            if (endChar <= startChar) break;
          }

          g.drawString(
              theLine,
              (int) pf.getImageableX(),
              (int) pf.getImageableY() + yOffset + (i * fontHeight));
          if (endChar < fullLine.length()) {
            // only did partial line
            startChar = endChar;
          } else {
            // completed the line
            startLine++;
            startChar = 0;
          }
        }
        return Printable.PAGE_EXISTS;
      }
    } else if (context instanceof EditWindow) {
      // only 1 page
      if (page == 0) {
        graphics = g;
        pageFormat = pf;
        if (context.getPrintImage(this) != null) return Printable.PAGE_EXISTS;
      }
    }
    return Printable.NO_SUCH_PAGE;
  }