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
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
351 * needs to be recalculated in the opposite direction.
352 */
353 if (angle != 0.0) {
354 angle = 360.0 - angle;
355 }
356 return (int)Math.round(angle * 10.0);
357 }
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);
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
|
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.FontManagerFactory;
69 import sun.font.FontUtilities;
70 import sun.font.PhysicalFont;
71 import sun.font.TrueTypeFont;
72
73 import sun.print.PathGraphics;
74 import sun.print.ProxyGraphics2D;
75
76 class WPathGraphics extends PathGraphics {
77
78 /**
79 * For a drawing application the initial user space
80 * resolution is 72dpi.
81 */
82 private static final int DEFAULT_USER_RES = 72;
83
84 private static final float MIN_DEVICE_LINEWIDTH = 1.2f;
85 private static final float MAX_THINLINE_INCHES = 0.014f;
86
87 /* Note that preferGDITextLayout implies useGDITextLayout.
88 * "prefer" is used to override cases where would otherwise
89 * choose not to use it. Note that non-layout factors may
279 fontTransform.concatenate(getFont().getTransform());
280 int transformType = fontTransform.getType();
281
282 /* Test if GDI can handle the transform */
283 boolean directToGDI = ((transformType !=
284 AffineTransform.TYPE_GENERAL_TRANSFORM)
285 && ((transformType & AffineTransform.TYPE_FLIP)
286 == 0));
287
288 if (!directToGDI) {
289 return 0;
290 }
291
292 /* Since all windows fonts are available, and the JRE fonts
293 * are also registered. Only the Font.createFont() case is presently
294 * unknown to GDI. Those can be registered too, although that
295 * code does not exist yet, it can be added too, so we should not
296 * fail that case. Just do a quick check whether its a TrueTypeFont
297 * - ie not a Type1 font etc, and let drawString() resolve the rest.
298 */
299 Font2D font2D = FontUtilities.getFont2D(font);
300 if (font2D instanceof CompositeFont ||
301 font2D instanceof TrueTypeFont) {
302 return 1;
303 } else {
304 return 0;
305 }
306 }
307
308 private static boolean isXP() {
309 String osVersion = System.getProperty("os.version");
310 if (osVersion != null) {
311 Float version = Float.valueOf(osVersion);
312 return (version.floatValue() >= 5.1f);
313 } else {
314 return false;
315 }
316 }
317
318 /* In case GDI doesn't handle shaping or BIDI consistently with
319 * 2D's TextLayout, we can detect these cases and redelegate up to
320 * be drawn via TextLayout, which in is rendered as runs of
321 * GlyphVectors, to which we can assign positions for each glyph.
322 */
323 private boolean strNeedsTextLayout(String str, Font font) {
324 char[] chars = str.toCharArray();
325 boolean isComplex = FontUtilities.isComplexText(chars, 0, chars.length);
326 if (!isComplex) {
327 return false;
328 } else if (!useGDITextLayout) {
329 return true;
330 } else {
331 if (preferGDITextLayout ||
332 (isXP() && textLayoutIsCompatible(font))) {
333 return false;
334 } else {
335 return true;
336 }
337 }
338 }
339
340 /**
341 * Used by windows printing to assess if a font is likely to
342 * be layout compatible with JDK
343 * TrueType fonts should be, but if they have no GPOS table,
344 * but do have a GSUB table, then they are probably older
345 * fonts GDI handles differently.
346 */
347 private boolean textLayoutIsCompatible(Font font) {
348
349 Font2D font2D = FontUtilities.getFont2D(font);
350 if (font2D instanceof TrueTypeFont) {
351 TrueTypeFont ttf = (TrueTypeFont)font2D;
352 return
353 ttf.getDirectoryEntry(TrueTypeFont.GSUBTag) == null ||
354 ttf.getDirectoryEntry(TrueTypeFont.GPOSTag) != null;
355 } else {
356 return false;
357 }
358 }
359
360 private int getAngle(Point2D.Double pt) {
361 /* Get the rotation in 1/10'ths degree (as needed by Windows)
362 * so that GDI can draw the text rotated.
363 * This calculation is only valid for a uniform scale, no shearing.
364 */
365 double angle = Math.toDegrees(Math.atan2(pt.y, pt.x));
366 if (angle < 0.0) {
367 angle+= 360.0;
368 }
369 /* Windows specifies the rotation anti-clockwise from the x-axis
370 * of the device, 2D specifies +ve rotation towards the y-axis
371 * Since the 2D y-axis runs from top-to-bottom, windows angle of
372 * rotation here is opposite than 2D's, so the rotation needed
373 * needs to be recalculated in the opposite direction.
374 */
375 if (angle != 0.0) {
376 angle = 360.0 - angle;
377 }
378 return (int)Math.round(angle * 10.0);
379 }
503 * Although we have already tested that there is no shear,
504 * there may be a non-uniform scale, so the width of the font
505 * does not scale equally with the height. That is handled
506 * by specifying an 'average width' scale to GDI.
507 */
508 float fontSize = font.getSize2D();
509
510 Point2D.Double pty = new Point2D.Double(0.0, 1.0);
511 fontTransform.deltaTransform(pty, pty);
512 double scaleFactorY = Math.sqrt(pty.x*pty.x+pty.y*pty.y);
513 float scaledFontSizeY = (float)(fontSize * scaleFactorY);
514
515 Point2D.Double ptx = new Point2D.Double(1.0, 0.0);
516 fontTransform.deltaTransform(ptx, ptx);
517 double scaleFactorX = Math.sqrt(ptx.x*ptx.x+ptx.y*ptx.y);
518 float scaledFontSizeX = (float)(fontSize * scaleFactorX);
519
520 float awScale = getAwScale(scaleFactorX, scaleFactorY);
521 int iangle = getAngle(ptx);
522
523 Font2D font2D = FontUtilities.getFont2D(font);
524 if (font2D instanceof TrueTypeFont) {
525 textOut(str, font, (TrueTypeFont)font2D, frc,
526 scaledFontSizeY, iangle, awScale,
527 deviceTransform, scaleFactorX,
528 x, y, devpos.x, devpos.y, targetW);
529 } else if (font2D instanceof CompositeFont) {
530 /* Composite fonts are made up of multiple fonts and each
531 * substring that uses a particular component font needs to
532 * be separately sent to GDI.
533 * This works for standard composite fonts, alternate ones,
534 * Fonts that are a physical font backed by a standard composite,
535 * and with fallback fonts.
536 */
537 CompositeFont compFont = (CompositeFont)font2D;
538 float userx = x, usery = y;
539 float devx = devpos.x, devy = devpos.y;
540 char[] chars = str.toCharArray();
541 int len = chars.length;
542 int[] glyphs = new int[len];
543 compFont.getMapper().charsToGlyphs(len, chars, glyphs);
698 * When we specify the advances, they are in device space, so
699 * we don't want any further interpretation applied by GDI, but
700 * since as noted the advances are interpreted in the HFONT's
701 * coordinate space, our advances would be rotated again.
702 * We don't have any way to tell GDI to rotate only the glyphs and
703 * not the advances, so we need to account for this in the advances
704 * we supply, by supplying unrotated advances.
705 * Note that "iangle" is in the opposite direction to 2D's normal
706 * direction of rotation, so this rotation inverts the
707 * rotation element of the deviceTransform.
708 */
709 AffineTransform advanceTransform =
710 new AffineTransform(deviceTransform);
711 advanceTransform.rotate(iangle*Math.PI/1800.0);
712 float[] glyphAdvPos = new float[glyphPos.length];
713
714 advanceTransform.transform(glyphPos, 0, //source
715 glyphAdvPos, 0, //destination
716 glyphPos.length/2); //num points
717
718 Font2D font2D = FontUtilities.getFont2D(font);
719 if (font2D instanceof TrueTypeFont) {
720 String family = font2D.getFamilyName(null);
721 int style = font.getStyle() | font2D.getStyle();
722 if (!wPrinterJob.setFont(family, scaledFontSizeY, style,
723 iangle, awScale)) {
724 return false;
725 }
726 wPrinterJob.glyphsOut(glyphCodes, devpos.x, devpos.y, glyphAdvPos);
727
728 } else if (font2D instanceof CompositeFont) {
729 /* Composite fonts are made up of multiple fonts and each
730 * substring that uses a particular component font needs to
731 * be separately sent to GDI.
732 * This works for standard composite fonts, alternate ones,
733 * Fonts that are a physical font backed by a standard composite,
734 * and with fallback fonts.
735 */
736 CompositeFont compFont = (CompositeFont)font2D;
737 float userx = x, usery = y;
738 float devx = devpos.x, devy = devpos.y;
797 boolean setFont = wPrinterJob.setFont(family, deviceSize, style,
798 rotation, awScale);
799 if (!setFont) {
800 super.drawString(str, userx, usery, font, frc, targetW);
801 return;
802 }
803
804 float[] glyphPos = null;
805 if (!okGDIMetrics(str, font, frc, scaleFactorX)) {
806 /* If there is a 1:1 char->glyph mapping then char positions
807 * are the same as glyph positions and we can tell GDI
808 * where to place the glyphs.
809 * On drawing we remove control chars so these need to be
810 * removed now so the string and positions are the same length.
811 * For other cases we need to pass glyph codes to GDI.
812 */
813 str = wPrinterJob.removeControlChars(str);
814 char[] chars = str.toCharArray();
815 int len = chars.length;
816 GlyphVector gv = null;
817 if (!FontUtilities.isComplexText(chars, 0, len)) {
818 gv = font.createGlyphVector(frc, str);
819 }
820 if (gv == null) {
821 super.drawString(str, userx, usery, font, frc, targetW);
822 return;
823 }
824 glyphPos = gv.getGlyphPositions(0, len, null);
825 Point2D gvAdvPt = gv.getGlyphPosition(gv.getNumGlyphs());
826
827 /* GDI advances must not include device space rotation.
828 * See earlier comment in printGlyphVector() for details.
829 */
830 AffineTransform advanceTransform =
831 new AffineTransform(deviceTransform);
832 advanceTransform.rotate(rotation*Math.PI/1800.0);
833 float[] glyphAdvPos = new float[glyphPos.length];
834
835 advanceTransform.transform(glyphPos, 0, //source
836 glyphAdvPos, 0, //destination
837 glyphPos.length/2); //num points
|