47 import java.awt.geom.Line2D;
48
49 import java.awt.image.BufferedImage;
50 import java.awt.image.ColorModel;
51 import java.awt.image.DataBuffer;
52 import java.awt.image.IndexColorModel;
53 import java.awt.image.WritableRaster;
54 import sun.awt.image.ByteComponentRaster;
55 import sun.awt.image.BytePackedRaster;
56
57 import java.awt.print.PageFormat;
58 import java.awt.print.Printable;
59 import java.awt.print.PrinterException;
60 import java.awt.print.PrinterJob;
61
62 import java.util.Arrays;
63
64 import sun.font.CharToGlyphMapper;
65 import sun.font.CompositeFont;
66 import sun.font.Font2D;
67 import sun.font.FontManager;
68 import sun.font.PhysicalFont;
69 import sun.font.TrueTypeFont;
70
71 import sun.print.PathGraphics;
72 import sun.print.ProxyGraphics2D;
73
74 class WPathGraphics extends PathGraphics {
75
76 /**
77 * For a drawing application the initial user space
78 * resolution is 72dpi.
79 */
80 private static final int DEFAULT_USER_RES = 72;
81
82 private static final float MIN_DEVICE_LINEWIDTH = 1.2f;
83 private static final float MAX_THINLINE_INCHES = 0.014f;
84
85 /* Note that preferGDITextLayout implies useGDITextLayout.
86 * "prefer" is used to override cases where would otherwise
87 * choose not to use it. Note that non-layout factors may
102 useGDITextLayout = true;
103 preferGDITextLayout = true;
104 }
105 }
106 }
107 }
108
109 WPathGraphics(Graphics2D graphics, PrinterJob printerJob,
110 Printable painter, PageFormat pageFormat, int pageIndex,
111 boolean canRedraw) {
112 super(graphics, printerJob, painter, pageFormat, pageIndex, canRedraw);
113 }
114
115 /**
116 * Creates a new <code>Graphics</code> object that is
117 * a copy of this <code>Graphics</code> object.
118 * @return a new graphics context that is a copy of
119 * this graphics context.
120 * @since JDK1.0
121 */
122 public Graphics create() {
123
124 return new WPathGraphics((Graphics2D) getDelegate().create(),
125 getPrinterJob(),
126 getPrintable(),
127 getPageFormat(),
128 getPageIndex(),
129 canDoRedraws());
130 }
131
132 /**
133 * Strokes the outline of a Shape using the settings of the current
134 * graphics state. The rendering attributes applied include the
135 * clip, transform, paint or color, composite and stroke attributes.
136 * @param s The shape to be drawn.
137 * @see #setStroke
138 * @see #setPaint
139 * @see java.awt.Graphics#setColor
140 * @see #transform
141 * @see #setTransform
142 * @see #clip
143 * @see #setClip
144 * @see #setComposite
145 */
146 public void draw(Shape s) {
147
148 Stroke stroke = getStroke();
149
150 /* If the line being drawn is thinner than can be
151 * rendered, then change the line width, stroke
152 * the shape, and then set the line width back.
153 * We can only do this for BasicStroke's.
154 */
155 if (stroke instanceof BasicStroke) {
156 BasicStroke lineStroke;
157 BasicStroke minLineStroke = null;
158 float deviceLineWidth;
159 float lineWidth;
160 AffineTransform deviceTransform;
161 Point2D.Float penSize;
162
163 /* Get the requested line width in user space.
164 */
165 lineStroke = (BasicStroke) stroke;
233 /* The stroke in effect was not a BasicStroke so we
234 * will not try to enforce a minimum line width.
235 */
236 } else {
237 super.draw(s);
238 }
239 }
240
241 /**
242 * Draws the text given by the specified string, using this
243 * graphics context's current font and color. The baseline of the
244 * first character is at position (<i>x</i>, <i>y</i>) in this
245 * graphics context's coordinate system.
246 * @param str the string to be drawn.
247 * @param x the <i>x</i> coordinate.
248 * @param y the <i>y</i> coordinate.
249 * @see java.awt.Graphics#drawBytes
250 * @see java.awt.Graphics#drawChars
251 * @since JDK1.0
252 */
253 public void drawString(String str, int x, int y) {
254 drawString(str, (float) x, (float) y);
255 }
256
257 public void drawString(String str, float x, float y) {
258 drawString(str, x, y, getFont(), getFontRenderContext(), 0f);
259 }
260
261 /* A return value of 0 would mean font not available to GDI, or the
262 * it can't be used for this string.
263 * A return of 1 means it is suitable, including for composites.
264 * We check that the transform in effect is doable with GDI, and that
265 * this is a composite font AWT can handle, or a physical font GDI
266 * can handle directly. Its possible that some strings may ultimately
267 * fail the more stringent tests in drawString but this is rare and
268 * also that method will always succeed, as if the font isn't available
269 * it will use outlines via a superclass call. Also it is only called for
270 * the default render context (as canDrawStringToWidth() will return
271 * false. That is why it ignores the frc and width arguments.
272 */
273 protected int platformFontCount(Font font, String str) {
274
275 AffineTransform deviceTransform = getTransform();
276 AffineTransform fontTransform = new AffineTransform(deviceTransform);
277 fontTransform.concatenate(getFont().getTransform());
278 int transformType = fontTransform.getType();
279
280 /* Test if GDI can handle the transform */
281 boolean directToGDI = ((transformType !=
282 AffineTransform.TYPE_GENERAL_TRANSFORM)
283 && ((transformType & AffineTransform.TYPE_FLIP)
284 == 0));
285
286 if (!directToGDI) {
287 return 0;
288 }
289
290 /* Since all windows fonts are available, and the JRE fonts
291 * are also registered. Only the Font.createFont() case is presently
292 * unknown to GDI. Those can be registered too, although that
293 * code does not exist yet, it can be added too, so we should not
294 * fail that case. Just do a quick check whether its a TrueTypeFont
295 * - ie not a Type1 font etc, and let drawString() resolve the rest.
296 */
297 Font2D font2D = FontManager.getFont2D(font);
298 if (font2D instanceof CompositeFont ||
299 font2D instanceof TrueTypeFont) {
300 return 1;
301 } else {
302 return 0;
303 }
304 }
305
306 private static boolean isXP() {
307 String osVersion = System.getProperty("os.version");
308 if (osVersion != null) {
309 Float version = Float.valueOf(osVersion);
310 return (version.floatValue() >= 5.1f);
311 } else {
312 return false;
313 }
314 }
315
316 /* In case GDI doesn't handle shaping or BIDI consistently with
317 * 2D's TextLayout, we can detect these cases and redelegate up to
318 * be drawn via TextLayout, which in is rendered as runs of
319 * GlyphVectors, to which we can assign positions for each glyph.
320 */
321 private boolean strNeedsTextLayout(String str, Font font) {
322 char[] chars = str.toCharArray();
323 boolean isComplex = FontManager.isComplexText(chars, 0, chars.length);
324 if (!isComplex) {
325 return false;
326 } else if (!useGDITextLayout) {
327 return true;
328 } else {
329 if (preferGDITextLayout ||
330 (isXP() && FontManager.textLayoutIsCompatible(font))) {
331 return false;
332 } else {
333 return true;
334 }
335 }
336 }
337
338 private int getAngle(Point2D.Double pt) {
339 /* Get the rotation in 1/10'ths degree (as needed by Windows)
340 * so that GDI can draw the text rotated.
341 * This calculation is only valid for a uniform scale, no shearing.
342 */
343 double angle = Math.toDegrees(Math.atan2(pt.y, pt.x));
344 if (angle < 0.0) {
345 angle+= 360.0;
346 }
347 /* Windows specifies the rotation anti-clockwise from the x-axis
348 * of the device, 2D specifies +ve rotation towards the y-axis
349 * Since the 2D y-axis runs from top-to-bottom, windows angle of
350 * rotation here is opposite than 2D's, so the rotation needed
371 * using the current <code>Font</code> and <code>Paint</code> attributes
372 * in the <code>Graphics2D</code> context.
373 * The baseline of the first character is at position
374 * (<i>x</i>, <i>y</i>) in the User Space.
375 * The rendering attributes applied include the <code>Clip</code>,
376 * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
377 * <code>Composite</code> attributes. For characters in script systems
378 * such as Hebrew and Arabic, the glyphs can be rendered from right to
379 * left, in which case the coordinate supplied is the location of the
380 * leftmost character on the baseline.
381 * @param s the <code>String</code> to be rendered
382 * @param x, y the coordinates where the <code>String</code>
383 * should be rendered
384 * @see #setPaint
385 * @see java.awt.Graphics#setColor
386 * @see java.awt.Graphics#setFont
387 * @see #setTransform
388 * @see #setComposite
389 * @see #setClip
390 */
391 public void drawString(String str, float x, float y,
392 Font font, FontRenderContext frc, float targetW) {
393 if (str.length() == 0) {
394 return;
395 }
396
397 if (WPrinterJob.shapeTextProp) {
398 super.drawString(str, x, y, font, frc, targetW);
399 return;
400 }
401
402 /* If the Font has layout attributes we need to delegate to TextLayout.
403 * TextLayout renders text as GlyphVectors. We try to print those
404 * using printer fonts - ie using Postscript text operators so
405 * we may be reinvoked. In that case the "!printingGlyphVector" test
406 * prevents us recursing and instead sends us into the body of the
407 * method where we can safely ignore layout attributes as those
408 * are already handled by TextLayout.
409 * Similarly if layout is needed based on the text, then we
410 * delegate to TextLayout if possible, or failing that we delegate
481 * Although we have already tested that there is no shear,
482 * there may be a non-uniform scale, so the width of the font
483 * does not scale equally with the height. That is handled
484 * by specifying an 'average width' scale to GDI.
485 */
486 float fontSize = font.getSize2D();
487
488 Point2D.Double pty = new Point2D.Double(0.0, 1.0);
489 fontTransform.deltaTransform(pty, pty);
490 double scaleFactorY = Math.sqrt(pty.x*pty.x+pty.y*pty.y);
491 float scaledFontSizeY = (float)(fontSize * scaleFactorY);
492
493 Point2D.Double ptx = new Point2D.Double(1.0, 0.0);
494 fontTransform.deltaTransform(ptx, ptx);
495 double scaleFactorX = Math.sqrt(ptx.x*ptx.x+ptx.y*ptx.y);
496 float scaledFontSizeX = (float)(fontSize * scaleFactorX);
497
498 float awScale = getAwScale(scaleFactorX, scaleFactorY);
499 int iangle = getAngle(ptx);
500
501 Font2D font2D = FontManager.getFont2D(font);
502 if (font2D instanceof TrueTypeFont) {
503 textOut(str, font, (TrueTypeFont)font2D, frc,
504 scaledFontSizeY, iangle, awScale,
505 deviceTransform, scaleFactorX,
506 x, y, devpos.x, devpos.y, targetW);
507 } else if (font2D instanceof CompositeFont) {
508 /* Composite fonts are made up of multiple fonts and each
509 * substring that uses a particular component font needs to
510 * be separately sent to GDI.
511 * This works for standard composite fonts, alternate ones,
512 * Fonts that are a physical font backed by a standard composite,
513 * and with fallback fonts.
514 */
515 CompositeFont compFont = (CompositeFont)font2D;
516 float userx = x, usery = y;
517 float devx = devpos.x, devy = devpos.y;
518 char[] chars = str.toCharArray();
519 int len = chars.length;
520 int[] glyphs = new int[len];
521 compFont.getMapper().charsToGlyphs(len, chars, glyphs);
532 String substr = new String(chars, startChar,endChar-startChar);
533 PhysicalFont slotFont = compFont.getSlotFont(slot);
534 textOut(substr, font, slotFont, frc,
535 scaledFontSizeY, iangle, awScale,
536 deviceTransform, scaleFactorX,
537 userx, usery, devx, devy, 0f);
538 Rectangle2D bds = font.getStringBounds(substr, frc);
539 float xAdvance = (float)bds.getWidth();
540 userx += xAdvance;
541 userpos.x += xAdvance;
542 deviceTransform.transform(userpos, devpos);
543 }
544 } else {
545 super.drawString(str, x, y, font, frc, targetW);
546 }
547 }
548
549 /** return true if the Graphics instance can directly print
550 * this glyphvector
551 */
552 protected boolean printGlyphVector(GlyphVector gv, float x, float y) {
553 /* We don't want to try to handle per-glyph transforms. GDI can't
554 * handle per-glyph rotations, etc. There's no way to express it
555 * in a single call, so just bail for this uncommon case.
556 */
557 if ((gv.getLayoutFlags() & GlyphVector.FLAG_HAS_TRANSFORMS) != 0) {
558 return false;
559 }
560
561 AffineTransform deviceTransform = getTransform();
562 AffineTransform fontTransform = new AffineTransform(deviceTransform);
563 Font font = gv.getFont();
564 fontTransform.concatenate(font.getTransform());
565 int transformType = fontTransform.getType();
566
567 /* Use GDI for the text if the graphics transform is something
568 * for which we can obtain a suitable GDI font.
569 * A flip or shearing transform on the graphics or a transform
570 * on the font force us to decompose the text into a shape.
571 */
676 * When we specify the advances, they are in device space, so
677 * we don't want any further interpretation applied by GDI, but
678 * since as noted the advances are interpreted in the HFONT's
679 * coordinate space, our advances would be rotated again.
680 * We don't have any way to tell GDI to rotate only the glyphs and
681 * not the advances, so we need to account for this in the advances
682 * we supply, by supplying unrotated advances.
683 * Note that "iangle" is in the opposite direction to 2D's normal
684 * direction of rotation, so this rotation inverts the
685 * rotation element of the deviceTransform.
686 */
687 AffineTransform advanceTransform =
688 new AffineTransform(deviceTransform);
689 advanceTransform.rotate(iangle*Math.PI/1800.0);
690 float[] glyphAdvPos = new float[glyphPos.length];
691
692 advanceTransform.transform(glyphPos, 0, //source
693 glyphAdvPos, 0, //destination
694 glyphPos.length/2); //num points
695
696 Font2D font2D = FontManager.getFont2D(font);
697 if (font2D instanceof TrueTypeFont) {
698 String family = font2D.getFamilyName(null);
699 int style = font.getStyle() | font2D.getStyle();
700 if (!wPrinterJob.setFont(family, scaledFontSizeY, style,
701 iangle, awScale)) {
702 return false;
703 }
704 wPrinterJob.glyphsOut(glyphCodes, devpos.x, devpos.y, glyphAdvPos);
705
706 } else if (font2D instanceof CompositeFont) {
707 /* Composite fonts are made up of multiple fonts and each
708 * substring that uses a particular component font needs to
709 * be separately sent to GDI.
710 * This works for standard composite fonts, alternate ones,
711 * Fonts that are a physical font backed by a standard composite,
712 * and with fallback fonts.
713 */
714 CompositeFont compFont = (CompositeFont)font2D;
715 float userx = x, usery = y;
716 float devx = devpos.x, devy = devpos.y;
775 boolean setFont = wPrinterJob.setFont(family, deviceSize, style,
776 rotation, awScale);
777 if (!setFont) {
778 super.drawString(str, userx, usery, font, frc, targetW);
779 return;
780 }
781
782 float[] glyphPos = null;
783 if (!okGDIMetrics(str, font, frc, scaleFactorX)) {
784 /* If there is a 1:1 char->glyph mapping then char positions
785 * are the same as glyph positions and we can tell GDI
786 * where to place the glyphs.
787 * On drawing we remove control chars so these need to be
788 * removed now so the string and positions are the same length.
789 * For other cases we need to pass glyph codes to GDI.
790 */
791 str = wPrinterJob.removeControlChars(str);
792 char[] chars = str.toCharArray();
793 int len = chars.length;
794 GlyphVector gv = null;
795 if (!FontManager.isComplexText(chars, 0, len)) {
796 gv = font.createGlyphVector(frc, str);
797 }
798 if (gv == null) {
799 super.drawString(str, userx, usery, font, frc, targetW);
800 return;
801 }
802 glyphPos = gv.getGlyphPositions(0, len, null);
803 Point2D gvAdvPt = gv.getGlyphPosition(gv.getNumGlyphs());
804
805 /* GDI advances must not include device space rotation.
806 * See earlier comment in printGlyphVector() for details.
807 */
808 AffineTransform advanceTransform =
809 new AffineTransform(deviceTransform);
810 advanceTransform.rotate(rotation*Math.PI/1800.0);
811 float[] glyphAdvPos = new float[glyphPos.length];
812
813 advanceTransform.transform(glyphPos, 0, //source
814 glyphAdvPos, 0, //destination
815 glyphPos.length/2); //num points
|
47 import java.awt.geom.Line2D;
48
49 import java.awt.image.BufferedImage;
50 import java.awt.image.ColorModel;
51 import java.awt.image.DataBuffer;
52 import java.awt.image.IndexColorModel;
53 import java.awt.image.WritableRaster;
54 import sun.awt.image.ByteComponentRaster;
55 import sun.awt.image.BytePackedRaster;
56
57 import java.awt.print.PageFormat;
58 import java.awt.print.Printable;
59 import java.awt.print.PrinterException;
60 import java.awt.print.PrinterJob;
61
62 import java.util.Arrays;
63
64 import sun.font.CharToGlyphMapper;
65 import sun.font.CompositeFont;
66 import sun.font.Font2D;
67 import sun.font.FontUtilities;
68 import sun.font.PhysicalFont;
69 import sun.font.TrueTypeFont;
70
71 import sun.print.PathGraphics;
72 import sun.print.ProxyGraphics2D;
73
74 class WPathGraphics extends PathGraphics {
75
76 /**
77 * For a drawing application the initial user space
78 * resolution is 72dpi.
79 */
80 private static final int DEFAULT_USER_RES = 72;
81
82 private static final float MIN_DEVICE_LINEWIDTH = 1.2f;
83 private static final float MAX_THINLINE_INCHES = 0.014f;
84
85 /* Note that preferGDITextLayout implies useGDITextLayout.
86 * "prefer" is used to override cases where would otherwise
87 * choose not to use it. Note that non-layout factors may
102 useGDITextLayout = true;
103 preferGDITextLayout = true;
104 }
105 }
106 }
107 }
108
109 WPathGraphics(Graphics2D graphics, PrinterJob printerJob,
110 Printable painter, PageFormat pageFormat, int pageIndex,
111 boolean canRedraw) {
112 super(graphics, printerJob, painter, pageFormat, pageIndex, canRedraw);
113 }
114
115 /**
116 * Creates a new <code>Graphics</code> object that is
117 * a copy of this <code>Graphics</code> object.
118 * @return a new graphics context that is a copy of
119 * this graphics context.
120 * @since JDK1.0
121 */
122 @Override
123 public Graphics create() {
124
125 return new WPathGraphics((Graphics2D) getDelegate().create(),
126 getPrinterJob(),
127 getPrintable(),
128 getPageFormat(),
129 getPageIndex(),
130 canDoRedraws());
131 }
132
133 /**
134 * Strokes the outline of a Shape using the settings of the current
135 * graphics state. The rendering attributes applied include the
136 * clip, transform, paint or color, composite and stroke attributes.
137 * @param s The shape to be drawn.
138 * @see #setStroke
139 * @see #setPaint
140 * @see java.awt.Graphics#setColor
141 * @see #transform
142 * @see #setTransform
143 * @see #clip
144 * @see #setClip
145 * @see #setComposite
146 */
147 @Override
148 public void draw(Shape s) {
149
150 Stroke stroke = getStroke();
151
152 /* If the line being drawn is thinner than can be
153 * rendered, then change the line width, stroke
154 * the shape, and then set the line width back.
155 * We can only do this for BasicStroke's.
156 */
157 if (stroke instanceof BasicStroke) {
158 BasicStroke lineStroke;
159 BasicStroke minLineStroke = null;
160 float deviceLineWidth;
161 float lineWidth;
162 AffineTransform deviceTransform;
163 Point2D.Float penSize;
164
165 /* Get the requested line width in user space.
166 */
167 lineStroke = (BasicStroke) stroke;
235 /* The stroke in effect was not a BasicStroke so we
236 * will not try to enforce a minimum line width.
237 */
238 } else {
239 super.draw(s);
240 }
241 }
242
243 /**
244 * Draws the text given by the specified string, using this
245 * graphics context's current font and color. The baseline of the
246 * first character is at position (<i>x</i>, <i>y</i>) in this
247 * graphics context's coordinate system.
248 * @param str the string to be drawn.
249 * @param x the <i>x</i> coordinate.
250 * @param y the <i>y</i> coordinate.
251 * @see java.awt.Graphics#drawBytes
252 * @see java.awt.Graphics#drawChars
253 * @since JDK1.0
254 */
255 @Override
256 public void drawString(String str, int x, int y) {
257 drawString(str, (float) x, (float) y);
258 }
259
260 @Override
261 public void drawString(String str, float x, float y) {
262 drawString(str, x, y, getFont(), getFontRenderContext(), 0f);
263 }
264
265 /* A return value of 0 would mean font not available to GDI, or the
266 * it can't be used for this string.
267 * A return of 1 means it is suitable, including for composites.
268 * We check that the transform in effect is doable with GDI, and that
269 * this is a composite font AWT can handle, or a physical font GDI
270 * can handle directly. Its possible that some strings may ultimately
271 * fail the more stringent tests in drawString but this is rare and
272 * also that method will always succeed, as if the font isn't available
273 * it will use outlines via a superclass call. Also it is only called for
274 * the default render context (as canDrawStringToWidth() will return
275 * false. That is why it ignores the frc and width arguments.
276 */
277 @Override
278 protected int platformFontCount(Font font, String str) {
279
280 AffineTransform deviceTransform = getTransform();
281 AffineTransform fontTransform = new AffineTransform(deviceTransform);
282 fontTransform.concatenate(getFont().getTransform());
283 int transformType = fontTransform.getType();
284
285 /* Test if GDI can handle the transform */
286 boolean directToGDI = ((transformType !=
287 AffineTransform.TYPE_GENERAL_TRANSFORM)
288 && ((transformType & AffineTransform.TYPE_FLIP)
289 == 0));
290
291 if (!directToGDI) {
292 return 0;
293 }
294
295 /* Since all windows fonts are available, and the JRE fonts
296 * are also registered. Only the Font.createFont() case is presently
297 * unknown to GDI. Those can be registered too, although that
298 * code does not exist yet, it can be added too, so we should not
299 * fail that case. Just do a quick check whether its a TrueTypeFont
300 * - ie not a Type1 font etc, and let drawString() resolve the rest.
301 */
302 Font2D font2D = FontUtilities.getFont2D(font);
303 if (font2D instanceof CompositeFont ||
304 font2D instanceof TrueTypeFont) {
305 return 1;
306 } else {
307 return 0;
308 }
309 }
310
311 private static boolean isXP() {
312 String osVersion = System.getProperty("os.version");
313 if (osVersion != null) {
314 Float version = Float.valueOf(osVersion);
315 return (version.floatValue() >= 5.1f);
316 } else {
317 return false;
318 }
319 }
320
321 /* In case GDI doesn't handle shaping or BIDI consistently with
322 * 2D's TextLayout, we can detect these cases and redelegate up to
323 * be drawn via TextLayout, which in is rendered as runs of
324 * GlyphVectors, to which we can assign positions for each glyph.
325 */
326 private boolean strNeedsTextLayout(String str, Font font) {
327 char[] chars = str.toCharArray();
328 boolean isComplex = FontUtilities.isComplexText(chars, 0, chars.length);
329 if (!isComplex) {
330 return false;
331 } else if (!useGDITextLayout) {
332 return true;
333 } else {
334 if (preferGDITextLayout ||
335 (isXP() && FontUtilities.textLayoutIsCompatible(font))) {
336 return false;
337 } else {
338 return true;
339 }
340 }
341 }
342
343 private int getAngle(Point2D.Double pt) {
344 /* Get the rotation in 1/10'ths degree (as needed by Windows)
345 * so that GDI can draw the text rotated.
346 * This calculation is only valid for a uniform scale, no shearing.
347 */
348 double angle = Math.toDegrees(Math.atan2(pt.y, pt.x));
349 if (angle < 0.0) {
350 angle+= 360.0;
351 }
352 /* Windows specifies the rotation anti-clockwise from the x-axis
353 * of the device, 2D specifies +ve rotation towards the y-axis
354 * Since the 2D y-axis runs from top-to-bottom, windows angle of
355 * rotation here is opposite than 2D's, so the rotation needed
376 * using the current <code>Font</code> and <code>Paint</code> attributes
377 * in the <code>Graphics2D</code> context.
378 * The baseline of the first character is at position
379 * (<i>x</i>, <i>y</i>) in the User Space.
380 * The rendering attributes applied include the <code>Clip</code>,
381 * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
382 * <code>Composite</code> attributes. For characters in script systems
383 * such as Hebrew and Arabic, the glyphs can be rendered from right to
384 * left, in which case the coordinate supplied is the location of the
385 * leftmost character on the baseline.
386 * @param s the <code>String</code> to be rendered
387 * @param x, y the coordinates where the <code>String</code>
388 * should be rendered
389 * @see #setPaint
390 * @see java.awt.Graphics#setColor
391 * @see java.awt.Graphics#setFont
392 * @see #setTransform
393 * @see #setComposite
394 * @see #setClip
395 */
396 @Override
397 public void drawString(String str, float x, float y,
398 Font font, FontRenderContext frc, float targetW) {
399 if (str.length() == 0) {
400 return;
401 }
402
403 if (WPrinterJob.shapeTextProp) {
404 super.drawString(str, x, y, font, frc, targetW);
405 return;
406 }
407
408 /* If the Font has layout attributes we need to delegate to TextLayout.
409 * TextLayout renders text as GlyphVectors. We try to print those
410 * using printer fonts - ie using Postscript text operators so
411 * we may be reinvoked. In that case the "!printingGlyphVector" test
412 * prevents us recursing and instead sends us into the body of the
413 * method where we can safely ignore layout attributes as those
414 * are already handled by TextLayout.
415 * Similarly if layout is needed based on the text, then we
416 * delegate to TextLayout if possible, or failing that we delegate
487 * Although we have already tested that there is no shear,
488 * there may be a non-uniform scale, so the width of the font
489 * does not scale equally with the height. That is handled
490 * by specifying an 'average width' scale to GDI.
491 */
492 float fontSize = font.getSize2D();
493
494 Point2D.Double pty = new Point2D.Double(0.0, 1.0);
495 fontTransform.deltaTransform(pty, pty);
496 double scaleFactorY = Math.sqrt(pty.x*pty.x+pty.y*pty.y);
497 float scaledFontSizeY = (float)(fontSize * scaleFactorY);
498
499 Point2D.Double ptx = new Point2D.Double(1.0, 0.0);
500 fontTransform.deltaTransform(ptx, ptx);
501 double scaleFactorX = Math.sqrt(ptx.x*ptx.x+ptx.y*ptx.y);
502 float scaledFontSizeX = (float)(fontSize * scaleFactorX);
503
504 float awScale = getAwScale(scaleFactorX, scaleFactorY);
505 int iangle = getAngle(ptx);
506
507 Font2D font2D = FontUtilities.getFont2D(font);
508 if (font2D instanceof TrueTypeFont) {
509 textOut(str, font, (TrueTypeFont)font2D, frc,
510 scaledFontSizeY, iangle, awScale,
511 deviceTransform, scaleFactorX,
512 x, y, devpos.x, devpos.y, targetW);
513 } else if (font2D instanceof CompositeFont) {
514 /* Composite fonts are made up of multiple fonts and each
515 * substring that uses a particular component font needs to
516 * be separately sent to GDI.
517 * This works for standard composite fonts, alternate ones,
518 * Fonts that are a physical font backed by a standard composite,
519 * and with fallback fonts.
520 */
521 CompositeFont compFont = (CompositeFont)font2D;
522 float userx = x, usery = y;
523 float devx = devpos.x, devy = devpos.y;
524 char[] chars = str.toCharArray();
525 int len = chars.length;
526 int[] glyphs = new int[len];
527 compFont.getMapper().charsToGlyphs(len, chars, glyphs);
538 String substr = new String(chars, startChar,endChar-startChar);
539 PhysicalFont slotFont = compFont.getSlotFont(slot);
540 textOut(substr, font, slotFont, frc,
541 scaledFontSizeY, iangle, awScale,
542 deviceTransform, scaleFactorX,
543 userx, usery, devx, devy, 0f);
544 Rectangle2D bds = font.getStringBounds(substr, frc);
545 float xAdvance = (float)bds.getWidth();
546 userx += xAdvance;
547 userpos.x += xAdvance;
548 deviceTransform.transform(userpos, devpos);
549 }
550 } else {
551 super.drawString(str, x, y, font, frc, targetW);
552 }
553 }
554
555 /** return true if the Graphics instance can directly print
556 * this glyphvector
557 */
558 @Override
559 protected boolean printGlyphVector(GlyphVector gv, float x, float y) {
560 /* We don't want to try to handle per-glyph transforms. GDI can't
561 * handle per-glyph rotations, etc. There's no way to express it
562 * in a single call, so just bail for this uncommon case.
563 */
564 if ((gv.getLayoutFlags() & GlyphVector.FLAG_HAS_TRANSFORMS) != 0) {
565 return false;
566 }
567
568 AffineTransform deviceTransform = getTransform();
569 AffineTransform fontTransform = new AffineTransform(deviceTransform);
570 Font font = gv.getFont();
571 fontTransform.concatenate(font.getTransform());
572 int transformType = fontTransform.getType();
573
574 /* Use GDI for the text if the graphics transform is something
575 * for which we can obtain a suitable GDI font.
576 * A flip or shearing transform on the graphics or a transform
577 * on the font force us to decompose the text into a shape.
578 */
683 * When we specify the advances, they are in device space, so
684 * we don't want any further interpretation applied by GDI, but
685 * since as noted the advances are interpreted in the HFONT's
686 * coordinate space, our advances would be rotated again.
687 * We don't have any way to tell GDI to rotate only the glyphs and
688 * not the advances, so we need to account for this in the advances
689 * we supply, by supplying unrotated advances.
690 * Note that "iangle" is in the opposite direction to 2D's normal
691 * direction of rotation, so this rotation inverts the
692 * rotation element of the deviceTransform.
693 */
694 AffineTransform advanceTransform =
695 new AffineTransform(deviceTransform);
696 advanceTransform.rotate(iangle*Math.PI/1800.0);
697 float[] glyphAdvPos = new float[glyphPos.length];
698
699 advanceTransform.transform(glyphPos, 0, //source
700 glyphAdvPos, 0, //destination
701 glyphPos.length/2); //num points
702
703 Font2D font2D = FontUtilities.getFont2D(font);
704 if (font2D instanceof TrueTypeFont) {
705 String family = font2D.getFamilyName(null);
706 int style = font.getStyle() | font2D.getStyle();
707 if (!wPrinterJob.setFont(family, scaledFontSizeY, style,
708 iangle, awScale)) {
709 return false;
710 }
711 wPrinterJob.glyphsOut(glyphCodes, devpos.x, devpos.y, glyphAdvPos);
712
713 } else if (font2D instanceof CompositeFont) {
714 /* Composite fonts are made up of multiple fonts and each
715 * substring that uses a particular component font needs to
716 * be separately sent to GDI.
717 * This works for standard composite fonts, alternate ones,
718 * Fonts that are a physical font backed by a standard composite,
719 * and with fallback fonts.
720 */
721 CompositeFont compFont = (CompositeFont)font2D;
722 float userx = x, usery = y;
723 float devx = devpos.x, devy = devpos.y;
782 boolean setFont = wPrinterJob.setFont(family, deviceSize, style,
783 rotation, awScale);
784 if (!setFont) {
785 super.drawString(str, userx, usery, font, frc, targetW);
786 return;
787 }
788
789 float[] glyphPos = null;
790 if (!okGDIMetrics(str, font, frc, scaleFactorX)) {
791 /* If there is a 1:1 char->glyph mapping then char positions
792 * are the same as glyph positions and we can tell GDI
793 * where to place the glyphs.
794 * On drawing we remove control chars so these need to be
795 * removed now so the string and positions are the same length.
796 * For other cases we need to pass glyph codes to GDI.
797 */
798 str = wPrinterJob.removeControlChars(str);
799 char[] chars = str.toCharArray();
800 int len = chars.length;
801 GlyphVector gv = null;
802 if (!FontUtilities.isComplexText(chars, 0, len)) {
803 gv = font.createGlyphVector(frc, str);
804 }
805 if (gv == null) {
806 super.drawString(str, userx, usery, font, frc, targetW);
807 return;
808 }
809 glyphPos = gv.getGlyphPositions(0, len, null);
810 Point2D gvAdvPt = gv.getGlyphPosition(gv.getNumGlyphs());
811
812 /* GDI advances must not include device space rotation.
813 * See earlier comment in printGlyphVector() for details.
814 */
815 AffineTransform advanceTransform =
816 new AffineTransform(deviceTransform);
817 advanceTransform.rotate(rotation*Math.PI/1800.0);
818 float[] glyphAdvPos = new float[glyphPos.length];
819
820 advanceTransform.transform(glyphPos, 0, //source
821 glyphAdvPos, 0, //destination
822 glyphPos.length/2); //num points
|