1 17 18 19 20 package org.apache.fop.render.afp; 21 22 import java.awt.Color ; 23 import java.awt.Rectangle ; 24 import java.awt.geom.Rectangle2D ; 25 import java.awt.image.BufferedImage ; 26 import java.io.IOException ; 27 import java.io.OutputStream ; 28 import java.io.UnsupportedEncodingException ; 29 import java.util.ArrayList ; 30 import java.util.HashMap ; 31 import java.util.HashSet ; 32 import java.util.Iterator ; 33 import java.util.List ; 34 import java.util.Map ; 35 36 import org.apache.avalon.framework.configuration.Configuration; 37 import org.apache.avalon.framework.configuration.ConfigurationException; 38 import org.apache.commons.io.output.ByteArrayOutputStream; 39 import org.apache.fop.apps.FOUserAgent; 40 import org.apache.fop.apps.MimeConstants; 41 import org.apache.fop.area.Block; 42 import org.apache.fop.area.BlockViewport; 43 import org.apache.fop.area.BodyRegion; 44 import org.apache.fop.area.CTM; 45 import org.apache.fop.area.OffDocumentItem; 46 import org.apache.fop.area.PageViewport; 47 import org.apache.fop.area.RegionReference; 48 import org.apache.fop.area.RegionViewport; 49 import org.apache.fop.area.Trait; 50 import org.apache.fop.area.inline.Leader; 51 import org.apache.fop.area.inline.Image; 52 import org.apache.fop.area.inline.SpaceArea; 53 import org.apache.fop.area.inline.TextArea; 54 import org.apache.fop.area.inline.WordArea; 55 import org.apache.fop.fo.Constants; 56 import org.apache.fop.fo.extensions.ExtensionAttachment; 57 import org.apache.fop.fonts.FontInfo; 58 import org.apache.fop.fonts.FontMetrics; 59 import org.apache.fop.fonts.FontTriplet; 60 import org.apache.fop.fonts.FontUtil; 61 import org.apache.fop.fonts.Typeface; 62 import org.apache.fop.fonts.base14.Courier; 63 import org.apache.fop.fonts.base14.Helvetica; 64 import org.apache.fop.fonts.base14.TimesRoman; 65 import org.apache.fop.image.FopImage; 66 import org.apache.fop.image.ImageFactory; 67 import org.apache.fop.image.TIFFImage; 68 import org.apache.fop.image.XMLImage; 69 import org.apache.fop.render.AbstractPathOrientedRenderer; 70 import org.apache.fop.render.Graphics2DAdapter; 71 import org.apache.fop.render.RendererContext; 72 import org.apache.fop.render.afp.extensions.AFPElementMapping; 73 import org.apache.fop.render.afp.extensions.AFPPageSetup; 74 import org.apache.fop.render.afp.fonts.AFPFontInfo; 75 import org.apache.fop.render.afp.fonts.AFPFont; 76 import org.apache.fop.render.afp.fonts.CharacterSet; 77 import org.apache.fop.render.afp.fonts.FopCharacterSet; 78 import org.apache.fop.render.afp.fonts.OutlineFont; 79 import org.apache.fop.render.afp.fonts.RasterFont; 80 import org.apache.fop.render.afp.modca.AFPConstants; 81 import org.apache.fop.render.afp.modca.AFPDataStream; 82 import org.apache.fop.render.afp.modca.ImageObject; 83 import org.apache.fop.render.afp.modca.PageObject; 84 import org.w3c.dom.Document ; 85 86 87 141 public class AFPRenderer extends AbstractPathOrientedRenderer { 142 143 146 private static final int DPI_CONVERSION_FACTOR_240 = 300; 147 148 151 private AFPDataStream _afpDataStream = null; 152 153 156 private HashMap _rootExtensionMap = null; 157 158 161 private HashMap _pageSegmentsMap = null; 162 163 166 private HashMap _currentPageFonts = null; 167 168 171 private Color _currentColor = null; 172 173 176 private int _pageFontCounter = 0; 177 178 181 private String _currentFontFamily = ""; 182 183 186 private int _currentFontSize = 0; 187 188 191 private Map _afpOptions = null; 192 193 196 private int _pageWidth = 0; 197 198 201 private int _pageHeight = 0; 202 203 206 private String _pageSequenceId = null; 207 208 211 private int _portraitRotation = 0; 212 213 216 private int _landscapeRotation = 270; 217 218 221 private HashSet _lineCache = null; 222 223 226 private float _x; 227 228 231 private float _y; 232 233 236 private Map _pages = null; 237 238 241 private boolean colorImages = false; 242 243 246 private int bitsPerPixel = 8; 247 248 251 public AFPRenderer() { 252 super(); 253 } 254 255 260 public void setupFontInfo(FontInfo inFontInfo) { 261 this.fontInfo = inFontInfo; 262 int num = 1; 263 if (this.fontList != null && this.fontList.size() > 0) { 264 for (Iterator it = this.fontList.iterator(); it.hasNext(); ) { 265 AFPFontInfo afi = (AFPFontInfo)it.next(); 266 AFPFont bf = (AFPFont)afi.getAFPFont(); 267 for (Iterator it2 = afi.getFontTriplets().iterator(); it2.hasNext(); ) { 268 FontTriplet ft = (FontTriplet)it2.next(); 269 this.fontInfo.addFontProperties("F" + num, ft.getName() 270 , ft.getStyle(), ft.getWeight()); 271 this.fontInfo.addMetrics("F" + num, bf); 272 num++; 273 } 274 } 275 } else { 276 log.warn("No AFP fonts configured - using default setup"); 277 } 278 if (this.fontInfo.fontLookup("sans-serif", "normal", 400) == null) { 279 CharacterSet cs = new FopCharacterSet("T1V10500", "Cp500", "CZH200 ", 1, new Helvetica()); 280 AFPFont bf = new OutlineFont("Helvetica", cs); 281 this.fontInfo.addFontProperties("F" + num, "sans-serif", "normal", 400); 282 this.fontInfo.addMetrics("F" + num, bf); 283 num++; 284 } 285 if (this.fontInfo.fontLookup("serif", "normal", 400) == null) { 286 CharacterSet cs = new FopCharacterSet("T1V10500", "Cp500", "CZN200 ", 1, new TimesRoman()); 287 AFPFont bf = new OutlineFont("Helvetica", cs); 288 this.fontInfo.addFontProperties("F" + num, "serif", "normal", 400); 289 this.fontInfo.addMetrics("F" + num, bf); 290 num++; 291 } 292 if (this.fontInfo.fontLookup("monospace", "normal", 400) == null) { 293 CharacterSet cs = new FopCharacterSet("T1V10500", "Cp500", "CZ4200 ", 1, new Courier()); 294 AFPFont bf = new OutlineFont("Helvetica", cs); 295 this.fontInfo.addFontProperties("F" + num, "monospace", "normal", 400); 296 this.fontInfo.addMetrics("F" + num, bf); 297 num++; 298 } 299 if (this.fontInfo.fontLookup("any", "normal", 400) == null) { 300 FontTriplet ft = this.fontInfo.fontLookup("sans-serif", "normal", 400); 301 this.fontInfo.addFontProperties(this.fontInfo.getInternalFontKey(ft), "any", "normal", 400); 302 } 303 } 304 305 307 private AFPFontInfo buildFont(Configuration fontCfg, String _path) 308 throws ConfigurationException { 309 310 Configuration[] triple = fontCfg.getChildren("font-triplet"); 311 List tripleList = new java.util.ArrayList (); 312 if (triple.length == 0) { 313 log.error("Mandatory font configuration element '<font-triplet...' is missing"); 314 return null; 315 } 316 for (int j = 0; j < triple.length; j++) { 317 int weight = FontUtil.parseCSS2FontWeight(triple[j].getAttribute("weight")); 318 tripleList.add(new FontTriplet(triple[j].getAttribute("name"), 319 triple[j].getAttribute("style"), 320 weight)); 321 } 322 323 Configuration afpFontCfg = fontCfg.getChild("afp-font"); 325 if (afpFontCfg == null) { 326 log.error("Mandatory font configuration element '<afp-font...' is missing"); 327 return null; 328 } 329 String path = afpFontCfg.getAttribute("path", _path); 330 String type = afpFontCfg.getAttribute("type"); 331 if (type == null) { 332 log.error("Mandatory afp-font configuration attribute 'type=' is missing"); 333 return null; 334 } 335 String codepage = afpFontCfg.getAttribute("codepage"); 336 if (codepage == null) { 337 log.error("Mandatory afp-font configuration attribute 'code=' is missing"); 338 return null; 339 } 340 String encoding = afpFontCfg.getAttribute("encoding"); 341 if (encoding == null) { 342 log.error("Mandatory afp-font configuration attribute 'encoding=' is missing"); 343 return null; 344 } 345 346 if ("raster".equalsIgnoreCase(type)) { 347 348 String name = afpFontCfg.getAttribute("name", "Unknown"); 349 350 RasterFont font = new RasterFont(name); 352 353 Configuration[] rasters = afpFontCfg.getChildren("afp-raster-font"); 354 if (rasters.length == 0) { 355 log.error("Mandatory font configuration elements '<afp-raster-font...' are missing"); 356 return null; 357 } 358 for (int j = 0; j < rasters.length; j++) { 359 Configuration rasterCfg = rasters[j]; 360 361 String characterset = rasterCfg.getAttribute("characterset"); 362 if (characterset == null) { 363 log.error("Mandatory afp-raster-font configuration attribute 'characterset=' is missing"); 364 return null; 365 } 366 int size = rasterCfg.getAttributeAsInteger("size"); 367 String base14 = rasterCfg.getAttribute("base14-font", null); 368 369 if (base14 != null) { 370 try { 371 Class clazz = Class.forName("org.apache.fop.fonts.base14." 372 + base14); 373 try { 374 Typeface tf = (Typeface)clazz.newInstance(); 375 font.addCharacterSet(size, new FopCharacterSet( 376 codepage, encoding, characterset, size, tf)); 377 } catch (Exception ie) { 378 String msg = "The base 14 font class " + clazz.getName() 379 + " could not be instantiated"; 380 log.error(msg); 381 } 382 } catch (ClassNotFoundException cnfe) { 383 String msg = "The base 14 font class for " + characterset 384 + " could not be found"; 385 log.error(msg); 386 } 387 } else { 388 font.addCharacterSet(size, new CharacterSet( 389 codepage, encoding, characterset, path)); 390 } 391 } 392 return new AFPFontInfo(font, tripleList); 393 394 } else if ("outline".equalsIgnoreCase(type)) { 395 396 String characterset = afpFontCfg.getAttribute("characterset"); 397 if (characterset == null) { 398 log.error("Mandatory afp-font configuration attribute 'characterset=' is missing"); 399 return null; 400 } 401 String name = afpFontCfg.getAttribute("name", characterset); 402 403 CharacterSet characterSet = null; 404 405 String base14 = afpFontCfg.getAttribute("base14-font", null); 406 407 if (base14 != null) { 408 try { 409 Class clazz = Class.forName("org.apache.fop.fonts.base14." 410 + base14); 411 try { 412 Typeface tf = (Typeface)clazz.newInstance(); 413 characterSet = new FopCharacterSet( 414 codepage, encoding, characterset, 1, tf); 415 } catch (Exception ie) { 416 String msg = "The base 14 font class " + clazz.getName() 417 + " could not be instantiated"; 418 log.error(msg); 419 } 420 } catch (ClassNotFoundException cnfe) { 421 String msg = "The base 14 font class for " + characterset 422 + " could not be found"; 423 log.error(msg); 424 } 425 } else { 426 characterSet = new CharacterSet(codepage, encoding, characterset, path); 427 } 428 OutlineFont font = new OutlineFont(name, characterSet); 430 return new AFPFontInfo(font, tripleList); 431 } else { 432 log.error("No or incorrect type attribute"); 433 } 434 return null; 435 } 436 437 443 public List buildFontListFromConfiguration(Configuration cfg) 444 throws ConfigurationException { 445 List fontList = new java.util.ArrayList (); 446 Configuration[] font = cfg.getChild("fonts").getChildren("font"); 447 for (int i = 0; i < font.length; i++) { 448 AFPFontInfo afi = buildFont(font[i], null); 449 if (afi != null) { 450 if (log.isDebugEnabled()) { 451 log.debug("Adding font " + afi.getAFPFont().getFontName()); 452 for (int j = 0; j < afi.getFontTriplets().size(); ++j) { 453 FontTriplet triplet = (FontTriplet) afi.getFontTriplets().get(j); 454 log.debug("Font triplet " 455 + triplet.getName() + ", " 456 + triplet.getStyle() + ", " 457 + triplet.getWeight()); 458 } 459 } 460 461 fontList.add(afi); 462 } 463 } 464 return fontList; 465 } 466 467 472 public void configure(Configuration cfg) throws ConfigurationException { 473 this.fontList = buildFontListFromConfiguration(cfg); 475 Configuration images = cfg.getChild("images"); 476 if (!"color".equalsIgnoreCase(images.getAttribute("mode", "b+w"))) { 477 bitsPerPixel = images.getAttributeAsInteger("bits-per-pixel", 8); 478 switch (bitsPerPixel) { 479 case 1: 480 case 4: 481 case 8: 482 break; 483 default: 484 log.warn("Invalid bits_per_pixel value, must be 1, 4 or 8."); 485 bitsPerPixel = 8; 486 break; 487 } 488 } else { 489 colorImages = true; 490 } 491 492 } 493 494 497 public void setUserAgent(FOUserAgent agent) { 498 super.setUserAgent(agent); 499 } 500 501 504 public void startRenderer(OutputStream outputStream) throws IOException { 505 _currentPageFonts = new HashMap (); 506 _currentColor = new Color (255, 255, 255); 507 _afpDataStream = new AFPDataStream(); 508 _afpDataStream.setPortraitRotation(_portraitRotation); 509 _afpDataStream.setLandscapeRotation(_landscapeRotation); 510 _afpDataStream.startDocument(outputStream); 511 } 512 513 516 public void stopRenderer() throws IOException { 517 _afpDataStream.endDocument(); 518 } 519 520 523 public boolean supportsOutOfOrder() { 524 return true; 526 } 527 528 537 public void preparePage(PageViewport page) { 538 540 _currentFontFamily = ""; 541 _currentFontSize = 0; 542 _pageFontCounter = 0; 543 _currentPageFonts.clear(); 544 _lineCache = new HashSet (); 545 546 Rectangle2D bounds = page.getViewArea(); 547 548 _pageWidth = mpts2units(bounds.getWidth()); 549 _pageHeight = mpts2units(bounds.getHeight()); 550 551 553 _afpDataStream.startPage(_pageWidth, _pageHeight, 0); 554 555 renderPageObjectExtensions(page); 556 557 if (_pages == null) { 558 _pages = new HashMap (); 559 } 560 _pages.put(page, _afpDataStream.savePage()); 561 562 } 563 564 567 public void processOffDocumentItem(OffDocumentItem odi) { 568 } 570 571 572 public Graphics2DAdapter getGraphics2DAdapter() { 573 return new AFPGraphics2DAdapter(); 574 } 575 576 579 public void startVParea(CTM ctm, Rectangle2D clippingRect) { 580 } 582 583 586 public void endVParea() { 587 } 589 590 598 public void renderRegionViewport(RegionViewport port) { 599 if (port != null) { 600 Rectangle2D view = port.getViewArea(); 601 currentBPPosition = 0; 605 currentIPPosition = 0; 606 607 RegionReference regionReference = port.getRegionReference(); 608 handleRegionTraits(port); 609 610 617 618 pushViewPortPos(new ViewPortPos(view, regionReference.getCTM())); 619 620 if (regionReference.getRegionClass() == FO_REGION_BODY) { 621 renderBodyRegion((BodyRegion) regionReference); 622 } else { 623 renderRegion(regionReference); 624 } 625 628 popViewPortPos(); 629 } 630 } 631 632 635 protected void renderBlockViewport(BlockViewport bv, List children) { 636 638 int saveIP = currentIPPosition; 640 int saveBP = currentBPPosition; 641 643 CTM ctm = bv.getCTM(); 644 int borderPaddingStart = bv.getBorderAndPaddingWidthStart(); 645 int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore(); 646 float x, y; 647 x = (float)(bv.getXOffset() + containingIPPosition) / 1000f; 648 y = (float)(bv.getYOffset() + containingBPPosition) / 1000f; 649 float width = (float)bv.getIPD() / 1000f; 651 float height = (float)bv.getBPD() / 1000f; 652 653 654 if (bv.getPositioning() == Block.ABSOLUTE 655 || bv.getPositioning() == Block.FIXED) { 656 657 currentIPPosition = bv.getXOffset(); 658 currentBPPosition = bv.getYOffset(); 659 660 List breakOutList = null; 664 if (bv.getPositioning() == Block.FIXED) { 665 breakOutList = breakOutOfStateStack(); 666 } 667 668 CTM tempctm = new CTM(containingIPPosition, containingBPPosition); 669 ctm = tempctm.multiply(ctm); 670 671 x += bv.getSpaceStart() / 1000f; 673 currentIPPosition += bv.getSpaceStart(); 674 675 y += bv.getSpaceBefore() / 1000f; 676 currentBPPosition += bv.getSpaceBefore(); 677 678 float bpwidth = (borderPaddingStart + bv.getBorderAndPaddingWidthEnd()) / 1000f; 679 float bpheight = (borderPaddingBefore + bv.getBorderAndPaddingWidthAfter()) / 1000f; 680 681 drawBackAndBorders(bv, x, y, width + bpwidth, height + bpheight); 682 683 currentIPPosition += borderPaddingStart; 685 currentBPPosition += borderPaddingBefore; 686 687 Rectangle2D clippingRect = null; 688 clippingRect = new Rectangle (currentIPPosition, currentBPPosition, 689 bv.getIPD(), bv.getBPD()); 690 691 pushViewPortPos(new ViewPortPos(clippingRect, ctm)); 693 currentIPPosition = 0; 694 currentBPPosition = 0; 695 renderBlocks(bv, children); 696 popViewPortPos(); 698 699 if (breakOutList != null) { 700 restoreStateStackAfterBreakOut(breakOutList); 701 } 702 703 currentIPPosition = saveIP; 704 currentBPPosition = saveBP; 705 } else { 706 707 currentBPPosition += bv.getSpaceBefore(); 708 709 handleBlockTraits(bv); 711 712 currentIPPosition += bv.getStartIndent(); 714 715 CTM tempctm = new CTM(containingIPPosition, currentBPPosition); 716 ctm = tempctm.multiply(ctm); 717 718 currentBPPosition += borderPaddingBefore; 720 721 Rectangle2D clippingRect = null; 722 clippingRect = new Rectangle (currentIPPosition, currentBPPosition, 723 bv.getIPD(), bv.getBPD()); 724 725 pushViewPortPos(new ViewPortPos(clippingRect, ctm)); 727 728 currentIPPosition = 0; 729 currentBPPosition = 0; 730 renderBlocks(bv, children); 731 popViewPortPos(); 733 734 currentIPPosition = saveIP; 735 currentBPPosition = saveBP; 736 737 currentBPPosition += (int)(bv.getAllocBPD()); 738 } 739 } 741 742 745 public void renderPage(PageViewport page) { 746 747 749 _currentFontFamily = ""; 750 _currentFontSize = 0; 751 _pageFontCounter = 0; 752 _currentPageFonts.clear(); 753 _lineCache = new HashSet (); 754 755 Rectangle2D bounds = page.getViewArea(); 756 757 _pageWidth = mpts2units(bounds.getWidth()); 758 _pageHeight = mpts2units(bounds.getHeight()); 759 760 if (_pages != null && _pages.containsKey(page)) { 761 762 _afpDataStream.restorePage((PageObject)_pages.remove(page)); 763 764 } else { 765 767 _afpDataStream.startPage(_pageWidth, _pageHeight, 0); 768 769 renderPageObjectExtensions(page); 770 771 } 772 773 pushViewPortPos(new ViewPortPos()); 774 775 renderPageAreas(page.getPage()); 776 777 Iterator i = _currentPageFonts.values().iterator(); 778 while (i.hasNext()) { 779 AFPFontAttributes afpFontAttributes = (AFPFontAttributes) i.next(); 780 781 _afpDataStream.createFont( 782 afpFontAttributes.getFontReference(), 783 afpFontAttributes.getFont(), 784 afpFontAttributes.getPointSize()); 785 786 } 787 788 try { 789 _afpDataStream.endPage(); 790 } catch (IOException ioex) { 791 } 793 794 popViewPortPos(); 795 796 } 797 798 802 public void clip() { 803 } 805 806 810 public void clipRect(float x, float y, float width, float height) { 811 } 813 814 818 public void moveTo(float x, float y) { 819 } 821 822 827 public void lineTo(float x, float y) { 828 } 830 831 836 public void closePath() { 837 } 839 840 844 public void fillRect(float x, float y, float width, float height) { 845 855 _afpDataStream.createLine( 856 pts2units(x), 857 pts2units(y), 858 pts2units(x + width), 859 pts2units(y), 860 pts2units(height), 861 _currentColor); 862 } 863 864 869 public void drawBorderLine(float x1, float y1, float x2, float y2, 870 boolean horz, boolean startOrBefore, int style, Color col) { 871 float w = x2 - x1; 872 float h = y2 - y1; 873 if ((w < 0) || (h < 0)) { 874 log.error("Negative extent received. Border won't be painted."); 875 return; 876 } 877 switch (style) { 878 case Constants.EN_DOUBLE: 879 if (horz) { 880 float h3 = h / 3; 881 float ym1 = y1; 882 float ym2 = ym1 + h3 + h3; 883 _afpDataStream.createLine( 884 pts2units(x1), 885 pts2units(ym1), 886 pts2units(x2), 887 pts2units(ym1), 888 pts2units(h3), 889 col 890 ); 891 _afpDataStream.createLine( 892 pts2units(x1), 893 pts2units(ym2), 894 pts2units(x2), 895 pts2units(ym2), 896 pts2units(h3), 897 col 898 ); 899 } else { 900 float w3 = w / 3; 901 float xm1 = x1; 902 float xm2 = xm1 + w3 + w3; 903 _afpDataStream.createLine( 904 pts2units(xm1), 905 pts2units(y1), 906 pts2units(xm1), 907 pts2units(y2), 908 pts2units(w3), 909 col 910 ); 911 _afpDataStream.createLine( 912 pts2units(xm2), 913 pts2units(y1), 914 pts2units(xm2), 915 pts2units(y2), 916 pts2units(w3), 917 col 918 ); 919 } 920 break; 921 case Constants.EN_DASHED: 922 if (horz) { 923 float w2 = 2 * h; 924 while (x1 + w2 < x2) { 925 _afpDataStream.createLine( 926 pts2units(x1), 927 pts2units(y1), 928 pts2units(x1 + w2), 929 pts2units(y1), 930 pts2units(h), 931 col 932 ); 933 x1 += 2 * w2; 934 } 935 } else { 936 float h2 = 2 * w; 937 while (y1 + h2 < y2) { 938 _afpDataStream.createLine( 939 pts2units(x1), 940 pts2units(y1), 941 pts2units(x1), 942 pts2units(y1 + h2), 943 pts2units(w), 944 col 945 ); 946 y1 += 2 * h2; 947 } 948 } 949 break; 950 case Constants.EN_DOTTED: 951 if (horz) { 952 while (x1 + h < x2) { 953 _afpDataStream.createLine( 954 pts2units(x1), 955 pts2units(y1), 956 pts2units(x1 + h), 957 pts2units(y1), 958 pts2units(h), 959 col 960 ); 961 x1 += 2 * h; 962 } 963 } else { 964 while (y1 + w < y2) { 965 _afpDataStream.createLine( 966 pts2units(x1), 967 pts2units(y1), 968 pts2units(x1), 969 pts2units(y1 + w), 970 pts2units(w), 971 col 972 ); 973 y1 += 2 * w; 974 } 975 } 976 break; 977 case Constants.EN_GROOVE: 978 case Constants.EN_RIDGE: 979 { 980 float colFactor = (style == EN_GROOVE ? 0.4f : -0.4f); 981 if (horz) { 982 Color uppercol = lightenColor(col, -colFactor); 983 Color lowercol = lightenColor(col, colFactor); 984 float h3 = h / 3; 985 float ym1 = y1; 986 _afpDataStream.createLine( 987 pts2units(x1), 988 pts2units(ym1), 989 pts2units(x2), 990 pts2units(ym1), 991 pts2units(h3), 992 uppercol 993 ); 994 _afpDataStream.createLine( 995 pts2units(x1), 996 pts2units(ym1 + h3), 997 pts2units(x2), 998 pts2units(ym1 + h3), 999 pts2units(h3), 1000 col 1001 ); 1002 _afpDataStream.createLine( 1003 pts2units(x1), 1004 pts2units(ym1 + h3 + h3), 1005 pts2units(x2), 1006 pts2units(ym1 + h3 + h3), 1007 pts2units(h3), 1008 lowercol 1009 ); 1010 } else { 1011 Color leftcol = lightenColor(col, -colFactor); 1012 Color rightcol = lightenColor(col, colFactor); 1013 float w3 = w / 3; 1014 float xm1 = x1 + (w3 / 2); 1015 _afpDataStream.createLine( 1016 pts2units(xm1), 1017 pts2units(y1), 1018 pts2units(xm1), 1019 pts2units(y2), 1020 pts2units(w3), 1021 leftcol 1022 ); 1023 _afpDataStream.createLine( 1024 pts2units(xm1 + w3), 1025 pts2units(y1), 1026 pts2units(xm1 + w3), 1027 pts2units(y2), 1028 pts2units(w3), 1029 col 1030 ); 1031 _afpDataStream.createLine( 1032 pts2units(xm1 + w3 + w3), 1033 pts2units(y1), 1034 pts2units(xm1 + w3 + w3), 1035 pts2units(y2), 1036 pts2units(w3), 1037 rightcol 1038 ); 1039 } 1040 break; 1041 } 1042 case Constants.EN_HIDDEN: 1043 break; 1044 case Constants.EN_INSET: 1045 case Constants.EN_OUTSET: 1046 default: 1047 _afpDataStream.createLine( 1048 pts2units(x1), 1049 pts2units(y1), 1050 pts2units(horz ? x2 : x1), 1051 pts2units(horz ? y1 : y2), 1052 pts2units(Math.abs(horz ? (y2 - y1) : (x2 - x1))), 1053 col 1054 ); 1055 } 1056 } 1057 1058 1062 protected RendererContext createRendererContext(int x, int y, int width, int height, Map foreignAttributes) { 1063 RendererContext context; 1064 context = super.createRendererContext(x, y, width, height, foreignAttributes); 1065 context.setProperty(AFPRendererContextConstants.AFP_GRAYSCALE, 1066 new Boolean (!this.colorImages)); 1067 return context; 1068 } 1069 1070 1074 public void drawImage(String url, Rectangle2D pos, Map foreignAttributes) { 1075 String name = null; 1076 if (_pageSegmentsMap != null) { 1077 name = (String )_pageSegmentsMap.get(url); 1078 } 1079 if (name != null) { 1080 int x = mpts2units(pos.getX() + currentIPPosition); 1081 int y = mpts2units(pos.getY() + currentBPPosition); 1082 _afpDataStream.createIncludePageSegment(name, x, y); 1083 } else { 1084 url = ImageFactory.getURL(url); 1085 ImageFactory fact = userAgent.getFactory().getImageFactory(); 1086 FopImage fopimage = fact.getImage(url, userAgent); 1087 if (fopimage == null) { 1088 return; 1089 } 1090 if (!fopimage.load(FopImage.DIMENSIONS)) { 1091 return; 1092 } 1093 String mime = fopimage.getMimeType(); 1094 if ("text/xml".equals(mime) || MimeConstants.MIME_SVG.equals(mime)) { 1095 if (!fopimage.load(FopImage.ORIGINAL_DATA)) { 1096 return; 1097 } 1098 Document doc = ((XMLImage) fopimage).getDocument(); 1099 String ns = ((XMLImage) fopimage).getNameSpace(); 1100 1101 renderDocument(doc, ns, pos, foreignAttributes); 1102 } else if (MimeConstants.MIME_EPS.equals(mime)) { 1103 log.warn("EPS images are not supported by this renderer"); 1104 1127 } else if (MimeConstants.MIME_TIFF.equals(mime) 1128 && fopimage instanceof TIFFImage) { 1129 TIFFImage tiffImage = (TIFFImage) fopimage; 1130 int x = mpts2units(pos.getX() + currentIPPosition); 1131 int y = mpts2units(pos.getY() + currentBPPosition); 1132 int w = mpts2units(pos.getWidth()); 1133 int h = mpts2units(pos.getHeight()); 1134 ImageObject io = _afpDataStream.getImageObject(x, y, w, h); 1135 io.setImageParameters( 1136 (int)(fopimage.getHorizontalResolution() * 10), 1137 (int)(fopimage.getVerticalResolution() * 10), 1138 fopimage.getWidth(), 1139 fopimage.getHeight() 1140 ); 1141 if (tiffImage.getStripCount() == 1) { 1142 int comp = tiffImage.getCompression(); 1143 if (comp == 3) { 1144 if (!fopimage.load(FopImage.ORIGINAL_DATA)) { 1145 return; 1146 } 1147 io.setImageEncoding((byte)0x81); 1148 io.setImageData(fopimage.getRessourceBytes()); 1149 } else if (comp == 4) { 1150 if (!fopimage.load(FopImage.ORIGINAL_DATA)) { 1151 return; 1152 } 1153 io.setImageEncoding((byte)0x82); 1154 io.setImageData(fopimage.getRessourceBytes()); 1155 } else { 1156 if (!fopimage.load(FopImage.BITMAP)) { 1157 return; 1158 } 1159 convertToGrayScaleImage(io, fopimage.getBitmaps()); 1160 } 1161 } else { 1162 if (!fopimage.load(FopImage.BITMAP)) { 1163 return; 1164 } 1165 convertToGrayScaleImage(io, fopimage.getBitmaps()); 1166 } 1167 } else { 1168 if (!fopimage.load(FopImage.BITMAP)) { 1169 return; 1170 } 1171 fact.releaseImage(url, userAgent); 1172 1173 int x = mpts2units(pos.getX() + currentIPPosition); 1174 int y = mpts2units(pos.getY() + currentBPPosition); 1175 int w = mpts2units(pos.getWidth()); 1176 int h = mpts2units(pos.getHeight()); 1177 ImageObject io = _afpDataStream.getImageObject(x, y, w, h); 1178 io.setImageParameters( 1179 (int)(fopimage.getHorizontalResolution() * 10), 1180 (int)(fopimage.getVerticalResolution() * 10), 1181 fopimage.getWidth(), 1182 fopimage.getHeight() 1183 ); 1184 if (colorImages) { 1185 io.setImageIDESize((byte)24); 1186 io.setImageData(fopimage.getBitmaps()); 1187 } else { 1188 convertToGrayScaleImage(io, fopimage.getBitmaps()); 1189 } 1190 } 1191 } 1192 } 1193 1194 1200 public static void writeImage(BufferedImage img, OutputStream out) throws IOException { 1201 int w = img.getWidth(); 1202 int h = img.getHeight(); 1203 int[] tmpMap = img.getRGB(0, 0, w, h, null, 0, w); 1204 for (int i = 0; i < h; i++) { 1205 for (int j = 0; j < w; j++) { 1206 int p = tmpMap[i * w + j]; 1207 int r = (p >> 16) & 0xFF; 1208 int g = (p >> 8) & 0xFF; 1209 int b = (p) & 0xFF; 1210 out.write((byte)(r & 0xFF)); 1211 out.write((byte)(g & 0xFF)); 1212 out.write((byte)(b & 0xFF)); 1213 } 1214 } 1215 } 1216 1217 1226 public void drawBufferedImage(BufferedImage bi, int resolution, int x, int y, int w, int h) { 1227 int afpx = mpts2units(x); 1228 int afpy = mpts2units(y); 1229 int afpw = mpts2units(w); 1230 int afph = mpts2units(h); 1231 ByteArrayOutputStream baout = new ByteArrayOutputStream(); 1232 try { 1233 writeImage(bi, baout); 1235 byte[] buf = baout.toByteArray(); 1236 1237 ImageObject io = _afpDataStream.getImageObject(afpx, afpy, afpw, afph); 1239 io.setImageParameters( 1240 resolution, resolution, 1241 bi.getWidth(), 1242 bi.getHeight() 1243 ); 1244 if (colorImages) { 1245 io.setImageIDESize((byte)24); 1246 io.setImageData(buf); 1247 } else { 1248 convertToGrayScaleImage(io, buf); 1251 } 1252 } catch (IOException ioe) { 1253 log.error("Error while serializing bitmap: " + ioe.getMessage(), ioe); 1254 } 1255 } 1256 1257 1261 public void updateColor(Color col, boolean fill) { 1262 if (fill) { 1263 _currentColor = col; 1264 } 1265 } 1266 1267 1271 public void restoreStateStackAfterBreakOut(List breakOutList) { 1272 1273 } 1274 1275 1279 public List breakOutOfStateStack() { 1280 return null; 1281 } 1282 1283 1284 public void saveGraphicsState() { 1285 1286 } 1287 1288 1289 public void restoreGraphicsState() { 1290 1291 } 1292 1293 1294 public void beginTextObject() { 1295 1296 } 1297 1298 1299 public void endTextObject() { 1300 1301 } 1302 1303 1306 public void renderImage(Image image, Rectangle2D pos) { 1307 String url = image.getURL(); 1308 drawImage(url, pos); 1309 } 1310 1311 1314 public void renderText(TextArea text) { 1315 renderInlineAreaBackAndBorders(text); 1316 1317 String name = getInternalFontNameForArea(text); 1318 _currentFontSize = ((Integer ) text.getTrait(Trait.FONT_SIZE)).intValue(); 1319 AFPFont tf = (AFPFont) fontInfo.getFonts().get(name); 1320 1321 Color col = (Color ) text.getTrait(Trait.COLOR); 1322 1323 int vsci = mpts2units(tf.getWidth(' ', _currentFontSize) / 1000 1324 + text.getTextWordSpaceAdjust() 1325 + text.getTextLetterSpaceAdjust()); 1326 1327 int rx = currentIPPosition + text.getBorderAndPaddingWidthStart(); 1331 int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset(); 1332 1333 1336 String worddata = text.getText(); 1337 1338 AFPFontAttributes afpFontAttributes = 1340 new AFPFontAttributes(name, tf, _currentFontSize); 1341 1342 if (!_currentPageFonts.containsKey(afpFontAttributes.getFontKey())) { 1343 _pageFontCounter++; 1345 afpFontAttributes.setFontReference(_pageFontCounter); 1346 _currentPageFonts.put( 1347 afpFontAttributes.getFontKey(), 1348 afpFontAttributes); 1349 1350 } else { 1351 afpFontAttributes = 1353 (AFPFontAttributes) _currentPageFonts.get( 1354 afpFontAttributes.getFontKey()); 1355 } 1356 1357 String encoding = null; 1359 1360 try { 1361 encoding = tf.getCharacterSet(_currentFontSize).getEncoding(); 1362 } catch (Throwable ex) { 1363 encoding = AFPConstants.EBCIDIC_ENCODING; 1364 log.warn( 1365 "renderText():: Error getting encoding for font " 1366 + " - using default encoding " 1367 + encoding); 1368 } 1369 1370 try { 1371 _afpDataStream.createText( 1372 afpFontAttributes.getFontReference(), 1373 mpts2units(rx), 1374 mpts2units(bl), 1375 col, 1376 vsci, 1377 mpts2units(text.getTextLetterSpaceAdjust()), 1378 worddata.getBytes(encoding)); 1379 } catch (UnsupportedEncodingException usee) { 1380 log.error( 1381 "renderText:: Font " 1382 + afpFontAttributes.getFontKey() 1383 + " caused UnsupportedEncodingException"); 1384 } 1385 1386 super.renderText(text); 1387 1388 renderTextDecoration(tf, _currentFontSize, text, bl, rx); 1389 } 1390 1391 1394 public void renderWord(WordArea word) { 1395 String name = getInternalFontNameForArea(word.getParentArea()); 1396 int size = ((Integer ) word.getParentArea().getTrait(Trait.FONT_SIZE)).intValue(); 1397 AFPFont tf = (AFPFont) fontInfo.getFonts().get(name); 1398 1399 String s = word.getWord(); 1400 1401 FontMetrics metrics = fontInfo.getMetricsFor(name); 1402 1403 super.renderWord(word); 1404 } 1405 1406 1409 public void renderSpace(SpaceArea space) { 1410 String name = getInternalFontNameForArea(space.getParentArea()); 1411 int size = ((Integer ) space.getParentArea().getTrait(Trait.FONT_SIZE)).intValue(); 1412 AFPFont tf = (AFPFont) fontInfo.getFonts().get(name); 1413 1414 String s = space.getSpace(); 1415 1416 FontMetrics metrics = fontInfo.getMetricsFor(name); 1417 1418 super.renderSpace(space); 1419 } 1420 1421 1426 public void renderLeader(Leader area) { 1427 renderInlineAreaBackAndBorders(area); 1428 1429 int style = area.getRuleStyle(); 1430 float startx = (currentIPPosition + area.getBorderAndPaddingWidthStart()) / 1000f; 1431 float starty = (currentBPPosition + area.getOffset()) / 1000f; 1432 float endx = (currentIPPosition + area.getBorderAndPaddingWidthStart() 1433 + area.getIPD()) / 1000f; 1434 float ruleThickness = area.getRuleThickness() / 1000f; 1435 Color col = (Color )area.getTrait(Trait.COLOR); 1436 1437 switch (style) { 1438 case EN_SOLID: 1439 case EN_DASHED: 1440 case EN_DOUBLE: 1441 case EN_DOTTED: 1442 case EN_GROOVE: 1443 case EN_RIDGE: 1444 drawBorderLine(startx, starty, endx, starty + ruleThickness, 1445 true, true, style, col); 1446 break; 1447 default: 1448 throw new UnsupportedOperationException ("rule style not supported"); 1449 } 1450 super.renderLeader(area); 1451 } 1452 1453 1456 public void setOptions(Map options) { 1457 1458 _afpOptions = options; 1459 1460 } 1461 1462 1468 private int getOrientation(String orientationString) { 1469 1470 int orientation = 0; 1471 if (orientationString != null && orientationString.length() > 0) { 1472 try { 1473 orientation = Integer.parseInt(orientationString); 1474 } catch (NumberFormatException nfe) { 1475 log.error( 1476 "Cannot use orientation of " 1477 + orientation 1478 + " defaulting to zero."); 1479 orientation = 0; 1480 } 1481 } else { 1482 orientation = 0; 1483 } 1484 switch (orientation) { 1485 case 0 : 1486 break; 1487 case 90 : 1488 break; 1489 case 180 : 1490 break; 1491 case 270 : 1492 break; 1493 default : 1494 log.error( 1495 "Cannot use orientation of " 1496 + orientation 1497 + " defaulting to zero."); 1498 orientation = 0; 1499 break; 1500 } 1501 1502 return orientation; 1503 1504 } 1505 1506 1513 public void setPortraitRotation(int rotation) { 1514 1515 if (rotation == 0 1516 || rotation == 90 1517 || rotation == 180 1518 || rotation == 270) { 1519 _portraitRotation = rotation; 1520 } else { 1521 throw new IllegalArgumentException ("The portrait rotation must be one" 1522 + " of the values 0, 90, 180, 270"); 1523 1524 } 1525 1526 } 1527 1528 1535 public void setLandscapeRotation(int rotation) { 1536 1537 if (rotation == 0 1538 || rotation == 90 1539 || rotation == 180 1540 || rotation == 270) { 1541 _landscapeRotation = rotation; 1542 } else { 1543 throw new IllegalArgumentException ("The landscape rotation must be one" 1544 + " of the values 0, 90, 180, 270"); 1545 } 1546 1547 } 1548 1549 1554 public String getMimeType() { 1555 return MimeConstants.MIME_AFP; 1556 } 1557 1558 1565 private void renderPageObjectExtensions(PageViewport page) { 1566 1567 _pageSegmentsMap = null; 1568 if (page.getExtensionAttachments() != null 1569 && page.getExtensionAttachments().size() > 0) { 1570 Iterator i = page.getExtensionAttachments().iterator(); 1572 while (i.hasNext()) { 1573 ExtensionAttachment attachment = (ExtensionAttachment)i.next(); 1574 if (AFPPageSetup.CATEGORY.equals(attachment.getCategory())) { 1575 AFPPageSetup aps = (AFPPageSetup)attachment; 1576 String element = aps.getElementName(); 1577 if (AFPElementMapping.INCLUDE_PAGE_OVERLAY.equals(element)) { 1578 String overlay = aps.getName(); 1579 if (overlay != null) { 1580 _afpDataStream.createIncludePageOverlay(overlay); 1581 } 1582 } else if (AFPElementMapping.INCLUDE_PAGE_SEGMENT.equals(element)) { 1583 String name = aps.getName(); 1584 String source = aps.getValue(); 1585 if (_pageSegmentsMap == null) { 1586 _pageSegmentsMap = new HashMap (); 1587 } 1588 _pageSegmentsMap.put(source, name); 1589 } else if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(element)) { 1590 String name = aps.getName(); 1591 String value = aps.getValue(); 1592 if (_pageSegmentsMap == null) { 1593 _pageSegmentsMap = new HashMap (); 1594 } 1595 _afpDataStream.createTagLogicalElement(name, value); 1596 } 1597 } 1598 } 1599 } 1600 1601 } 1602 1603 1607 private int mpts2units(int mpt) { 1608 return mpts2units((double) mpt); 1609 } 1610 1611 1615 private int pts2units(float mpt) { 1616 return mpts2units(mpt * 1000d); 1617 } 1618 1619 1623 private int mpts2units(double mpt) { 1624 return (int)Math.round(mpt / DPI_CONVERSION_FACTOR_240); 1625 } 1626 1627 private void convertToGrayScaleImage(ImageObject io, byte raw[]) { 1628 int pixelsPerByte = 8 / bitsPerPixel; 1629 byte bw[] = new byte[raw.length / (3 * pixelsPerByte)]; 1630 int k = 0; 1631 for (int i = 0, j = 0; i < raw.length; i += 3, j++) { 1632 if (j == pixelsPerByte) { 1633 j = 0; 1634 k++; 1635 } 1636 double greyVal = 0.212671d * ((int) raw[i] & 0xff) 1638 + 0.715160d * ((int) raw[i + 1] & 0xff) 1639 + 0.072169d * ((int) raw[i + 2] & 0xff); 1640 switch (bitsPerPixel) { 1641 case 1: 1642 if (greyVal > 128) { 1643 bw[k] |= (byte)(1 << j); 1644 } 1645 break; 1646 case 4: 1647 greyVal /= 16; 1648 bw[k] |= (byte)((byte)greyVal << (j * 4)); 1649 break; 1650 case 8: 1651 bw[k] = (byte)greyVal; 1652 break; 1653 } 1654 } 1655 io.setImageIDESize((byte)bitsPerPixel); 1656 io.setImageData(bw); 1657 } 1658 1659 private class ViewPortPos { 1660 int x = 0; 1661 int y = 0; 1662 int rot = 0; 1663 1664 ViewPortPos() { 1665 } 1666 1667 ViewPortPos(Rectangle2D view, CTM ctm) { 1668 ViewPortPos currentVP = (ViewPortPos)viewPortPositions.get(viewPortPositions.size() - 1); 1669 int xOrigin; 1670 int yOrigin; 1671 int width; 1672 int height; 1673 switch (currentVP.rot) { 1674 case 90: 1675 width = mpts2units(view.getHeight()); 1676 height = mpts2units(view.getWidth()); 1677 xOrigin = _pageWidth - width - mpts2units(view.getY()) - currentVP.y; 1678 yOrigin = mpts2units(view.getX()) + currentVP.x; 1679 break; 1680 case 180: 1681 width = mpts2units(view.getWidth()); 1682 height = mpts2units(view.getHeight()); 1683 xOrigin = _pageWidth - width - mpts2units(view.getX()) - currentVP.x; 1684 yOrigin = _pageHeight - height - mpts2units(view.getY()) - currentVP.y; 1685 break; 1686 case 270: 1687 width = mpts2units(view.getHeight()); 1688 height = mpts2units(view.getWidth()); 1689 xOrigin = mpts2units(view.getY()) + currentVP.y; 1690 yOrigin = _pageHeight - height - mpts2units(view.getX()) - currentVP.x; 1691 break; 1692 default: 1693 xOrigin = mpts2units(view.getX()) + currentVP.x; 1694 yOrigin = mpts2units(view.getY()) + currentVP.y; 1695 width = mpts2units(view.getWidth()); 1696 height = mpts2units(view.getHeight()); 1697 break; 1698 } 1699 this.rot = currentVP.rot; 1700 double ctmf[] = ctm.toArray(); 1701 if (ctmf[0] == 0.0d && ctmf[1] == -1.0d && ctmf[2] == 1.0d && ctmf[3] == 0.d) { 1702 this.rot += 270; 1703 } else if (ctmf[0] == -1.0d && ctmf[1] == 0.0d && ctmf[2] == 0.0d && ctmf[3] == -1.0d) { 1704 this.rot += 180; 1705 } else if (ctmf[0] == 0.0d && ctmf[1] == 1.0d && ctmf[2] == -1.0d && ctmf[3] == 0.0d) { 1706 this.rot += 90; 1707 } 1708 this.rot %= 360; 1709 switch (this.rot) { 1710 1728 case 0: 1729 this.x = xOrigin; 1730 this.y = yOrigin; 1731 break; 1732 case 90: 1733 this.x = yOrigin; 1734 this.y = _pageWidth - width - xOrigin; 1735 break; 1736 case 180: 1737 this.x = _pageWidth - width - xOrigin; 1738 this.y = _pageHeight - height - yOrigin; 1739 break; 1740 case 270: 1741 this.x = _pageHeight - height - yOrigin; 1742 this.y = xOrigin; 1743 break; 1744 } 1745 } 1746 1747 public String toString() { 1748 return "x:" + x + " y:" + y + " rot:" + rot; 1749 } 1750 1751 } 1752 1753 private List viewPortPositions = new ArrayList (); 1754 1755 private void pushViewPortPos(ViewPortPos vpp) { 1756 viewPortPositions.add(vpp); 1757 _afpDataStream.setOffsets(vpp.x, vpp.y, vpp.rot); 1758 } 1759 1760 private void popViewPortPos() { 1761 viewPortPositions.remove(viewPortPositions.size() - 1); 1762 if (viewPortPositions.size() > 0) { 1763 ViewPortPos vpp = (ViewPortPos)viewPortPositions.get(viewPortPositions.size() - 1); 1764 _afpDataStream.setOffsets(vpp.x, vpp.y, vpp.rot); 1765 } 1766 } 1767 1768} 1769 1770 | Popular Tags |