1 17 18 19 20 package org.apache.fop.render.pcl; 21 22 import java.awt.Color ; 23 import java.awt.Dimension ; 24 import java.awt.Graphics2D ; 25 import java.awt.color.ColorSpace ; 26 import java.awt.geom.AffineTransform ; 27 import java.awt.image.BufferedImage ; 28 import java.awt.image.BufferedImageOp ; 29 import java.awt.image.ByteLookupTable ; 30 import java.awt.image.ColorConvertOp ; 31 import java.awt.image.ColorModel ; 32 import java.awt.image.DataBuffer ; 33 import java.awt.image.IndexColorModel ; 34 import java.awt.image.LookupOp ; 35 import java.awt.image.Raster ; 36 import java.awt.image.RenderedImage ; 37 import java.awt.image.WritableRaster ; 38 import java.io.DataOutputStream ; 39 import java.io.IOException ; 40 import java.io.OutputStream ; 41 import java.text.DecimalFormat ; 42 import java.text.DecimalFormatSymbols ; 43 import java.util.Locale ; 44 45 import org.apache.commons.io.output.ByteArrayOutputStream; 46 import org.apache.fop.util.UnitConv; 47 import org.apache.xmlgraphics.image.GraphicsUtil; 48 49 52 public class PCLGenerator { 53 54 55 public static final char ESC = '\033'; 56 57 58 public static final int[] PCL_RESOLUTIONS = new int[] {75, 100, 150, 200, 300, 600}; 59 60 61 public static final int DITHER_MATRIX_4X4 = 4; 62 63 public static final int DITHER_MATRIX_8X8 = 8; 64 65 private final DecimalFormatSymbols symbols = new DecimalFormatSymbols (Locale.US); 66 private final DecimalFormat df2 = new DecimalFormat ("0.##", symbols); 67 private final DecimalFormat df4 = new DecimalFormat ("0.####", symbols); 68 69 private OutputStream out; 70 71 private boolean currentSourceTransparency = true; 72 private boolean currentPatternTransparency = true; 73 74 private int maxBitmapResolution = PCL_RESOLUTIONS[PCL_RESOLUTIONS.length - 1]; 75 76 80 private boolean usePCLShades = false; 81 82 86 public PCLGenerator(OutputStream out) { 87 this.out = out; 88 } 89 90 95 public PCLGenerator(OutputStream out, int maxResolution) { 96 this(out); 97 boolean found = false; 98 for (int i = 0; i < PCL_RESOLUTIONS.length; i++) { 99 if (PCL_RESOLUTIONS[i] == maxResolution) { 100 found = true; 101 break; 102 } 103 } 104 if (!found) { 105 throw new IllegalArgumentException ("Illegal value for maximum resolution!"); 106 } 107 this.maxBitmapResolution = maxResolution; 108 } 109 110 111 public OutputStream getOutputStream() { 112 return this.out; 113 } 114 115 116 public int getMaximumBitmapResolution() { 117 return this.maxBitmapResolution; 118 } 119 120 125 public void writeCommand(String cmd) throws IOException { 126 out.write(27); out.write(cmd.getBytes("US-ASCII")); 128 } 129 130 135 public void writeText(String s) throws IOException { 136 out.write(s.getBytes("ISO-8859-1")); 137 } 138 139 145 public final String formatDouble2(double value) { 146 return df2.format(value); 147 } 148 149 155 public final String formatDouble4(double value) { 156 return df4.format(value); 157 } 158 159 163 public void universalEndOfLanguage() throws IOException { 164 writeCommand("%-12345X"); 165 } 166 167 171 public void resetPrinter() throws IOException { 172 writeCommand("E"); 173 } 174 175 179 public void separateJobs() throws IOException { 180 writeCommand("&l1T"); 181 } 182 183 187 public void formFeed() throws IOException { 188 out.write(12); } 190 191 196 public void setUnitOfMeasure(int value) throws IOException { 197 writeCommand("&u" + value + "D"); 198 } 199 200 205 public void setRasterGraphicsResolution(int value) throws IOException { 206 writeCommand("*t" + value + "R"); 207 } 208 209 214 public void selectPageSize(int selector) throws IOException { 215 writeCommand("&l" + selector + "A"); 216 } 217 218 226 public void selectPaperSource(int selector) throws IOException { 227 writeCommand("&l" + selector + "H"); 228 } 229 230 234 public void clearHorizontalMargins() throws IOException { 235 writeCommand("9"); 236 } 237 238 244 public void setTopMargin(int numberOfLines) throws IOException { 245 writeCommand("&l" + numberOfLines + "E"); 246 } 247 248 254 public void setTextLength(int numberOfLines) throws IOException { 255 writeCommand("&l" + numberOfLines + "F"); 256 } 257 258 263 public void setVMI(double value) throws IOException { 264 writeCommand("&l" + formatDouble4(value) + "C"); 265 } 266 267 273 public void setCursorPos(double x, double y) throws IOException { 274 if (x < 0) { 275 writeCommand("&a0h" + formatDouble2(x / 100) + "h" + formatDouble2(y / 100) + "V"); 279 } else { 280 writeCommand("&a" + formatDouble2(x / 100) + "h" + formatDouble2(y / 100) + "V"); 281 } 282 } 283 284 288 public void pushCursorPos() throws IOException { 289 writeCommand("&f0S"); 290 } 291 292 296 public void popCursorPos() throws IOException { 297 writeCommand("&f1S"); 298 } 299 300 305 public void changePrintDirection(int rotate) throws IOException { 306 writeCommand("&a" + rotate + "P"); 307 } 308 309 315 public void enterHPGL2Mode(boolean restorePreviousHPGL2Cursor) throws IOException { 316 if (restorePreviousHPGL2Cursor) { 317 writeCommand("%0B"); 318 } else { 319 writeCommand("%1B"); 320 } 321 } 322 323 329 public void enterPCLMode(boolean restorePreviousPCLCursor) throws IOException { 330 if (restorePreviousPCLCursor) { 331 writeCommand("%0A"); 332 } else { 333 writeCommand("%1A"); 334 } 335 } 336 337 345 protected void fillRect(int w, int h, Color col) throws IOException { 346 if ((w == 0) || (h == 0)) { 347 return; 348 } 349 if (h < 0) { 350 h *= -1; 351 } else { 352 } 354 setPatternTransparencyMode(false); 355 if (usePCLShades 356 || Color.black.equals(col) 357 || Color.white.equals(col)) { 358 writeCommand("*c" + formatDouble4(w / 100) + "h" 359 + formatDouble4(h / 100) + "V"); 360 int lineshade = convertToPCLShade(col); 361 writeCommand("*c" + lineshade + "G"); 362 writeCommand("*c2P"); } else { 364 defineGrayscalePattern(col, 32, DITHER_MATRIX_4X4); 365 366 writeCommand("*c" + formatDouble4(w / 100) + "h" 367 + formatDouble4(h / 100) + "V"); 368 writeCommand("*c32G"); 369 writeCommand("*c4P"); } 371 setPatternTransparencyMode(true); 373 } 374 375 private static final int[] BAYER_D2 = new int[] {0, 2, 3, 1}; 377 private static final int[] BAYER_D4; 378 private static final int[] BAYER_D8; 379 380 static { 381 BAYER_D4 = deriveBayerMatrix(BAYER_D2); 382 BAYER_D8 = deriveBayerMatrix(BAYER_D4); 383 } 384 385 private static void setValueInMatrix(int[] dn, int half, int part, int idx, int value) { 386 int xoff = (part & 1) * half; 387 int yoff = (part & 2) * half * half; 388 int matrixIndex = yoff + ((int)(idx / half) * half * 2) + (idx % half) + xoff; 389 dn[matrixIndex] = value; 390 } 391 392 private static int[] deriveBayerMatrix(int[] d) { 393 int[] dn = new int[d.length * 4]; 394 int half = (int)Math.sqrt(d.length); 395 for (int part = 0; part < 4; part++) { 396 for (int i = 0, c = d.length; i < c; i++) { 397 setValueInMatrix(dn, half, part, i, d[i] * 4 + BAYER_D2[part]); 398 } 399 } 400 return dn; 401 } 402 403 411 public void defineGrayscalePattern(Color col, int patternID, int ditherMatrixSize) 412 throws IOException { 413 ByteArrayOutputStream baout = new ByteArrayOutputStream(); 414 DataOutputStream data = new DataOutputStream (baout); 415 data.writeByte(0); data.writeByte(0); data.writeByte(1); data.writeByte(0); data.writeShort(8); data.writeShort(8); int gray255 = convertToGray(col.getRed(), col.getGreen(), col.getBlue()); 424 425 byte[] pattern; 426 if (ditherMatrixSize == 8) { 427 int gray65 = gray255 * 65 / 255; 428 429 pattern = new byte[BAYER_D8.length / 8]; 430 431 for (int i = 0, c = BAYER_D8.length; i < c; i++) { 432 boolean dot = !(BAYER_D8[i] < gray65 - 1); 433 if (dot) { 434 int byteIdx = i / 8; 435 pattern[byteIdx] |= 1 << (i % 8); 436 } 437 } 438 } else { 439 int gray17 = gray255 * 17 / 255; 440 441 pattern = new byte[BAYER_D4.length / 8 * 4]; 445 446 for (int i = 0, c = BAYER_D4.length; i < c; i++) { 447 boolean dot = !(BAYER_D4[i] < gray17 - 1); 448 if (dot) { 449 int byteIdx = i / 4; 450 pattern[byteIdx] |= 1 << (i % 4); 451 pattern[byteIdx] |= 1 << ((i % 4) + 4); 452 pattern[byteIdx + 4] |= 1 << (i % 4); 453 pattern[byteIdx + 4] |= 1 << ((i % 4) + 4); 454 } 455 } 456 } 457 data.write(pattern); 458 if ((baout.size() % 2) > 0) { 459 baout.write(0); 460 } 461 writeCommand("*c" + patternID + "G"); 462 writeCommand("*c" + baout.size() + "W"); 463 baout.writeTo(this.out); 464 writeCommand("*c4Q"); } 466 467 472 public void setSourceTransparencyMode(boolean transparent) throws IOException { 473 setTransparencyMode(transparent, currentPatternTransparency); 474 } 475 476 481 public void setPatternTransparencyMode(boolean transparent) throws IOException { 482 setTransparencyMode(currentSourceTransparency, transparent); 483 } 484 485 491 public void setTransparencyMode(boolean source, boolean pattern) throws IOException { 492 if (source != currentSourceTransparency && pattern != currentPatternTransparency) { 493 writeCommand("*v" + (source ? '0' : '1') + "n" + (pattern ? '0' : '1') + "O"); 494 } else if (source != currentSourceTransparency) { 495 writeCommand("*v" + (source ? '0' : '1') + "N"); 496 } else if (pattern != currentPatternTransparency) { 497 writeCommand("*v" + (pattern ? '0' : '1') + "O"); 498 } 499 this.currentSourceTransparency = source; 500 this.currentPatternTransparency = pattern; 501 } 502 503 510 public final int convertToGray(int r, int g, int b) { 511 return (r * 30 + g * 59 + b * 11) / 100; 512 } 513 514 519 public final int convertToPCLShade(Color col) { 520 float gray = convertToGray(col.getRed(), col.getGreen(), col.getBlue()) / 255f; 521 return (int)(100 - (gray * 100f)); 522 } 523 524 529 public void selectGrayscale(Color col) throws IOException { 530 if (Color.black.equals(col)) { 531 selectCurrentPattern(0, 0); } else if (Color.white.equals(col)) { 533 selectCurrentPattern(0, 1); } else { 535 if (usePCLShades) { 536 selectCurrentPattern(convertToPCLShade(col), 2); 537 } else { 538 defineGrayscalePattern(col, 32, DITHER_MATRIX_4X4); 539 selectCurrentPattern(32, 4); 540 } 541 } 542 } 543 544 550 public void selectCurrentPattern(int patternID, int pattern) throws IOException { 551 if (pattern > 1) { 552 writeCommand("*c" + patternID + "G"); 553 } 554 writeCommand("*v" + pattern + "T"); 555 } 556 557 562 public static boolean isMonochromeImage(RenderedImage img) { 563 ColorModel cm = img.getColorModel(); 564 if (cm instanceof IndexColorModel ) { 565 IndexColorModel icm = (IndexColorModel )cm; 566 return icm.getMapSize() == 2; 567 } else { 568 return false; 569 } 570 } 571 572 577 public static boolean isGrayscaleImage(RenderedImage img) { 578 return (img.getColorModel().getColorSpace().getNumComponents() == 1); 579 } 580 581 private MonochromeBitmapConverter createMonochromeBitmapConverter() { 582 MonochromeBitmapConverter converter = null; 583 try { 584 String clName = "org.apache.fop.render.pcl.JAIMonochromeBitmapConverter"; 585 Class clazz = Class.forName(clName); 586 converter = (MonochromeBitmapConverter)clazz.newInstance(); 587 } catch (ClassNotFoundException cnfe) { 588 } catch (LinkageError le) { 590 } catch (InstantiationException e) { 596 } catch (IllegalAccessException e) { 598 } 600 if (converter == null) { 601 converter = new DefaultMonochromeBitmapConverter(); 602 } 603 return converter; 604 } 605 606 private int calculatePCLResolution(int resolution) { 607 return calculatePCLResolution(resolution, false); 608 } 609 610 618 private int calculatePCLResolution(int resolution, boolean increased) { 619 int choice = -1; 620 for (int i = PCL_RESOLUTIONS.length - 2; i >= 0; i--) { 621 if (resolution > PCL_RESOLUTIONS[i]) { 622 int idx = i + 1; 623 if (idx < PCL_RESOLUTIONS.length - 2) { 624 idx += increased ? 2 : 0; 625 } else if (idx < PCL_RESOLUTIONS.length - 1) { 626 idx += increased ? 1 : 0; 627 } 628 choice = idx; 629 break; 630 } 632 } 633 if (choice < 0) { 634 choice = (increased ? 2 : 0); 635 } 636 while (choice > 0 && PCL_RESOLUTIONS[choice] > getMaximumBitmapResolution()) { 637 choice--; 638 } 639 return PCL_RESOLUTIONS[choice]; 640 } 641 642 private boolean isValidPCLResolution(int resolution) { 643 return resolution == calculatePCLResolution(resolution); 644 } 645 646 private Dimension getAdjustedDimension(Dimension orgDim, double orgResolution, 647 int pclResolution) { 648 if (orgResolution == pclResolution) { 649 return orgDim; 650 } else { 651 Dimension result = new Dimension (); 652 result.width = (int)Math.round((double)orgDim.width * pclResolution / orgResolution); 653 result.height = (int)Math.round((double)orgDim.height * pclResolution / orgResolution); 654 return result; 655 } 656 } 657 658 private static final byte[] THRESHOLD_TABLE = new byte[256]; 660 static { for (int i = 0; i < 256; i++) { 662 THRESHOLD_TABLE[i] = (byte) ((i < 240) ? 255 : 0); 663 } 664 } 665 666 private RenderedImage getMask(RenderedImage img, Dimension targetDim) { 667 ColorModel cm = img.getColorModel(); 668 if (cm.hasAlpha()) { 669 BufferedImage alpha = new BufferedImage (img.getWidth(), img.getHeight(), 670 BufferedImage.TYPE_BYTE_GRAY); 671 Raster raster = img.getData(); 672 GraphicsUtil.copyBand(raster, cm.getNumColorComponents(), alpha.getRaster(), 0); 673 674 BufferedImageOp op1 = new LookupOp (new ByteLookupTable (0, THRESHOLD_TABLE), null); 675 BufferedImage alphat = op1.filter(alpha, null); 676 677 BufferedImage mask; 678 if (true) { 679 mask = new BufferedImage (targetDim.width, targetDim.height, 680 BufferedImage.TYPE_BYTE_BINARY); 681 } else { 682 byte[] arr = {(byte)0, (byte)0xff}; 683 ColorModel colorModel = new IndexColorModel (1, 2, arr, arr, arr); 684 WritableRaster wraster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, 685 targetDim.width, targetDim.height, 1, 1, null); 686 mask = new BufferedImage (colorModel, wraster, false, null); 687 } 688 689 Graphics2D g2d = mask.createGraphics(); 690 try { 691 AffineTransform at = new AffineTransform (); 692 double sx = targetDim.getWidth() / img.getWidth(); 693 double sy = targetDim.getHeight() / img.getHeight(); 694 at.scale(sx, sy); 695 g2d.drawRenderedImage(alphat, at); 696 } finally { 697 g2d.dispose(); 698 } 699 706 return mask; 707 } else { 708 return null; 709 } 710 } 711 712 720 public void paintBitmap(RenderedImage img, Dimension targetDim, boolean sourceTransparency) 721 throws IOException { 722 double targetResolution = img.getWidth() / UnitConv.mpt2in(targetDim.width); 723 int resolution = (int)Math.round(targetResolution); 724 int effResolution = calculatePCLResolution(resolution, true); 725 Dimension orgDim = new Dimension (img.getWidth(), img.getHeight()); 726 Dimension effDim = getAdjustedDimension(orgDim, targetResolution, effResolution); 727 boolean scaled = !orgDim.equals(effDim); 728 729 boolean monochrome = isMonochromeImage(img); 730 if (!monochrome) { 731 final boolean transparencyDisabled = true; 733 RenderedImage mask = (transparencyDisabled ? null : getMask(img, effDim)); 734 if (mask != null) { 735 pushCursorPos(); 736 selectCurrentPattern(0, 1); setTransparencyMode(true, true); 738 paintMonochromeBitmap(mask, effResolution); 739 popCursorPos(); 740 } 741 742 BufferedImage src = null; 743 if (img instanceof BufferedImage && !scaled) { 744 if (!isGrayscaleImage(img) || img.getColorModel().hasAlpha()) { 745 src = new BufferedImage (effDim.width, effDim.height, 746 BufferedImage.TYPE_BYTE_GRAY); 747 ColorConvertOp op = new ColorConvertOp ( 748 ColorSpace.getInstance(ColorSpace.CS_GRAY), null); 749 op.filter((BufferedImage )img, src); 750 } else { 751 src = (BufferedImage )img; 752 } 753 } 754 if (src == null) { 755 src = new BufferedImage (effDim.width, effDim.height, 756 BufferedImage.TYPE_BYTE_GRAY); 757 Graphics2D g2d = src.createGraphics(); 758 try { 759 AffineTransform at = new AffineTransform (); 760 double sx = effDim.getWidth() / orgDim.getWidth(); 761 double sy = effDim.getHeight() / orgDim.getHeight(); 762 at.scale(sx, sy); 763 g2d.drawRenderedImage(img, at); 764 } finally { 765 g2d.dispose(); 766 } 767 } 768 MonochromeBitmapConverter converter = createMonochromeBitmapConverter(); 769 converter.setHint("quality", "false"); 770 771 BufferedImage buf = (BufferedImage )converter.convertToMonochrome(src); 772 773 RenderedImage red = buf; 774 selectCurrentPattern(0, 0); setTransparencyMode(sourceTransparency || mask != null, true); 776 paintMonochromeBitmap(red, effResolution); 777 } else { 778 RenderedImage effImg = img; 780 if (scaled) { 781 BufferedImage buf = new BufferedImage (effDim.width, effDim.height, 782 BufferedImage.TYPE_BYTE_BINARY); 783 Graphics2D g2d = buf.createGraphics(); 784 try { 785 AffineTransform at = new AffineTransform (); 786 double sx = effDim.getWidth() / orgDim.getWidth(); 787 double sy = effDim.getHeight() / orgDim.getHeight(); 788 at.scale(sx, sy); 789 g2d.drawRenderedImage(img, at); 790 } finally { 791 g2d.dispose(); 792 } 793 effImg = buf; 794 } 795 setSourceTransparencyMode(sourceTransparency); 796 selectCurrentPattern(0, 0); paintMonochromeBitmap(effImg, effResolution); 798 } 799 } 800 801 808 public void paintMonochromeBitmap(RenderedImage img, int resolution) throws IOException { 809 if (!isValidPCLResolution(resolution)) { 810 throw new IllegalArgumentException ("Invalid PCL resolution: " + resolution); 811 } 812 setRasterGraphicsResolution(resolution); 813 writeCommand("*r0f" + img.getHeight() + "t" + img.getWidth() + "s1A"); 814 Raster raster = img.getData(); 815 boolean monochrome = isMonochromeImage(img); 816 if (!monochrome) { 817 throw new IllegalArgumentException ("img must be a monochrome image"); 818 } 819 820 int x = 0; 821 int y = 0; 822 int imgw = img.getWidth(); 823 int imgh = img.getHeight(); 824 int bytewidth = (imgw / 8); 825 if ((imgw % 8) != 0) { 826 bytewidth++; 827 } 828 byte ib; 829 byte[] rle = new byte[bytewidth * 2]; byte[] uncompressed = new byte[bytewidth]; int lastcount = -1; 832 byte lastbyte = 0; 833 int rlewidth = 0; 834 835 for (y = 0; y < imgh; y++) { 837 ib = 0; 838 for (x = 0; x < imgw; x++) { 839 int sample = raster.getSample(x, y, 0); 840 if ((sample == 0)) { 842 ib |= (1 << (7 - (x % 8))); 843 } 844 845 if ((x % 8) == 7 || ((x + 1) == imgw)) { 847 if (rlewidth < bytewidth) { 848 if (lastcount >= 0) { 849 if (ib == lastbyte) { 850 lastcount++; 851 } else { 852 rle[rlewidth++] = (byte)(lastcount & 0xFF); 853 rle[rlewidth++] = lastbyte; 854 lastbyte = ib; 855 lastcount = 0; 856 } 857 } else { 858 lastbyte = ib; 859 lastcount = 0; 860 } 861 if (lastcount == 255 || ((x + 1) == imgw)) { 862 rle[rlewidth++] = (byte)(lastcount & 0xFF); 863 rle[rlewidth++] = lastbyte; 864 lastbyte = 0; 865 lastcount = -1; 866 } 867 } 868 uncompressed[x / 8] = ib; 869 ib = 0; 870 } 871 } 872 if (rlewidth < bytewidth) { 873 writeCommand("*b1m" + rlewidth + "W"); 874 this.out.write(rle, 0, rlewidth); 875 } else { 876 writeCommand("*b0m" + bytewidth + "W"); 877 this.out.write(uncompressed); 878 } 879 lastcount = -1; 880 rlewidth = 0; 881 } 882 883 writeCommand("*rB"); 885 } 886 887 } 888 | Popular Tags |