1 32 33 package it.businesslogic.ireport; 34 import it.businesslogic.ireport.gui.*; 35 import it.businesslogic.ireport.util.*; 36 import java.awt.*; 37 import java.awt.image.*; 38 import java.awt.geom.*; 39 import java.io.File ; 40 import java.net.MalformedURLException ; 41 import java.net.URL ; 42 43 import net.sf.jasperreports.engine.JRException; 44 import net.sf.jasperreports.engine.util.JRImageLoader; 45 46 public class ImageReportElement extends GraphicReportElement implements ImageObserver, HyperLinkableReportElement, BoxElement 47 { 48 49 static public final String SCALE = "SCALE"; 50 static public final String VERTICAL_ALIGN = "VERTICAL_ALIGN"; 51 static public final String HORIZONTAL_ALIGN = "HORIZONTAL_ALIGN"; 52 static public final String USING_CACHE = "USING_CACHE"; 53 54 static public final String DEFAULT_SCALE = "FillFrame"; 55 static public final String DEFAULT_VERTICAL_ALIGN = "Top"; 56 static public final String DEFAULT_HORIZONTAL_ALIGN = "Left"; 57 static public final boolean DEFAULT_USING_CACHE = false; 58 59 private java.util.List linkParameters = new java.util.ArrayList (); 60 61 private Image img=null; 62 static private Image defimg=null; 63 private String imageExpression; 64 private String imageClass; 65 private String hyperlinkType="None"; 67 private String evaluationTime="Now"; 68 private String evaluationGroup=""; 69 70 73 private String anchorNameExpression=""; 74 private String hyperlinkReferenceExpression=""; 75 private String hyperlinkAnchorExpression=""; 76 private String hyperlinkPageExpression=""; 77 78 private String onErrorType = "Error"; 79 80 private int bookmarkLevel = 0; 81 82 private boolean isLazy=false; 84 private File reportDirectory = null; 85 private boolean triedToLoadImg = false; 86 87 private Box box = null; 88 89 public ImageReportElement(int x, int y, int width, int height) 90 { 91 super(x,y, Math.abs(width), Math.abs(height)); 92 setKey("image"); 96 97 if (defimg == null) 98 { 99 defimg = Misc.loadImageFromResources("it/businesslogic/ireport/icons/layout/img.gif"); 101 } 102 103 img = null; 104 imageExpression=""; 106 imageClass="java.lang.String"; 107 this.hyperlinkType = "None"; 110 this.anchorNameExpression = ""; 111 box = new Box(); 112 } 113 114 115 116 141 142 public void drawObject(Graphics2D g, double zoom_factor, int x_shift_origin, int y_shift_origin) 143 { 144 145 position.x += -10 + box.getLeftPadding(); 146 position.y += -10 + box.getTopPadding(); 147 width = width - box.getLeftPadding() - box.getRightPadding(); 148 height = height - box.getTopPadding() - box.getBottomPadding(); 149 150 x_shift_origin -= 10; 151 y_shift_origin -= 10; 152 153 this.zoom_factor = zoom_factor; 154 Image imgx = img; 155 156 if (imgx==null) 157 { 158 160 String fname = imageExpression; 161 162 if(!triedToLoadImg && imageClass.equals("java.lang.String") && fname.length()>3 && fname.charAt(0)=='\"' && 163 fname.charAt(fname.length()-1 ) == '\"'){ 164 165 fname = Misc.string_replace("\\","\\\\",fname); 167 fname = Misc.string_replace("","\"",fname); 168 169 File file = new File (fname); 171 if(!file.isAbsolute() && reportDirectory != null){ 172 file = new File (reportDirectory, fname); 173 } 174 if(file.exists()){ 175 img = Misc.loadImageFromFile(file.getAbsolutePath()); 176 }else{ 177 178 try{ 179 180 img = JRImageLoader.loadImage(JRImageLoader.loadImageDataFromURL( MainFrame.getMainInstance().getReportClassLoader().getResource( fname ))); 181 }catch(Exception jrex){ 182 } 183 } 184 185 imgx = img; 186 triedToLoadImg = true; } 188 189 if (imgx==null) 190 imgx=defimg; 191 } 192 193 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 194 g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); 195 196 if (imgx != null) 197 { 198 int imageWidth = imgx.getWidth(this); 199 int imageHeight = imgx.getHeight(this); 200 206 207 209 Java2DUtil.setClip(g,getZoomedDim(position.x) - x_shift_origin, 210 getZoomedDim(position.y) - y_shift_origin, 211 getZoomedDim(width), getZoomedDim(height)); 212 213 214 if (getScaleImage().equalsIgnoreCase("RetainShape")) 215 { 216 if (imageWidth <= width && imageHeight<= height) 217 { 218 Rectangle destination = new Rectangle(-x_shift_origin+ getZoomedDim( position.x) ,-y_shift_origin+getZoomedDim(position.y), getZoomedDim( imageWidth), getZoomedDim( imageHeight)); 219 Rectangle source = new Rectangle(0,0,imageWidth,imageHeight); 220 int elem_height = getZoomedDim(this.height); 222 elem_height -= getZoomedDim( imageHeight); 223 if (getVerticalAlignment().equals("Middle")) destination.y += elem_height/2; 224 else if (getVerticalAlignment().equals("Bottom")) destination.y += elem_height; 225 226 int elem_width = getZoomedDim(this.width); 228 elem_width -= getZoomedDim( imageWidth); 229 if (getHorizontalAlignment().equals("Center")) destination.x += elem_width/2; 230 else if (getHorizontalAlignment().equals("Right")) destination.x += elem_width; 231 232 233 g.drawImage( imgx,destination.x, destination.y, destination.x+destination.width, destination.y+destination.height, 234 source.x,source.y,source.width,source.height, 235 null,this); 236 237 } 238 else if (width>0 && height>0) { 240 if ((double)((double)imageWidth/(double)width)> (double)((double)imageHeight/(double)height)) 241 { 242 243 Rectangle source = new Rectangle(0,0,imageWidth,imageHeight); 244 Rectangle destination = new Rectangle(-x_shift_origin+ getZoomedDim( position.x) ,-y_shift_origin+getZoomedDim(position.y),getZoomedDim(width) ,getZoomedDim(Math.min( (imageHeight*width)/imageWidth, height)) ); 245 246 int elem_height = getZoomedDim(this.height); 248 elem_height -= getZoomedDim(Math.min( (imageHeight*width)/imageWidth, height)); 249 if (getVerticalAlignment().equals("Middle")) destination.y += elem_height/2; 250 else if (getVerticalAlignment().equals("Bottom")) destination.y += elem_height; 251 252 253 g.drawImage( imgx,destination.x, destination.y, destination.x+destination.width, destination.y+destination.height, 254 source.x,source.y,source.width,source.height, 255 null,this); 256 257 } 258 else 259 { 260 Rectangle source = new Rectangle(0,0,imageWidth,imageHeight); 261 Rectangle destination = new Rectangle(-x_shift_origin+ getZoomedDim( position.x) ,-y_shift_origin+getZoomedDim(position.y), getZoomedDim( Math.min( (imageWidth*height)/imageHeight, width)) ,getZoomedDim( height) ); 262 263 int elem_width = getZoomedDim(this.width); 265 elem_width -= getZoomedDim( Math.min( (imageWidth*height)/imageHeight, width)); 266 if (getHorizontalAlignment().equals("Center")) destination.x += elem_width/2; 267 else if (getHorizontalAlignment().equals("Right")) destination.x += elem_width; 268 269 270 g.drawImage( imgx,destination.x, destination.y, destination.x+destination.width, destination.y+destination.height, 271 source.x,source.y,source.width,source.height, 272 null,this); 273 274 } 275 } 276 277 } 278 else if (getScaleImage().equalsIgnoreCase("FillFrame")) 279 { 280 281 g.drawImage(imgx, getZoomedDim(position.x)-x_shift_origin, 282 getZoomedDim(position.y)-y_shift_origin, 283 getZoomedDim(position.x+width)-x_shift_origin, 284 getZoomedDim(position.y+height)-y_shift_origin, 285 0,0,imageWidth,imageHeight,null,this); 286 287 289 290 } 291 else 292 { 293 Rectangle destination = new Rectangle(-x_shift_origin+ getZoomedDim( position.x) ,-y_shift_origin+getZoomedDim(position.y),getZoomedDim( Math.min(imageWidth, width)),getZoomedDim( Math.min(imageHeight, height))); 294 Rectangle source = new Rectangle(0,0,Math.min( imageWidth, width) ,Math.min(imageHeight, height)); 295 296 int elem_height = getZoomedDim( height); 298 elem_height -= getZoomedDim( imageHeight ); 299 if (getVerticalAlignment().equals("Middle")) { 300 destination.y += elem_height/2; 301 if (destination.y<--y_shift_origin+getZoomedDim(position.y)+10 ) 302 { 303 source.y += -y_shift_origin+getZoomedDim(position.y)+10-destination.y; 304 destination.y = -y_shift_origin+getZoomedDim(position.y)+10; 305 } 306 } 307 else if (getVerticalAlignment().equals("Bottom")) { 308 destination.y += elem_height; 309 if (destination.y<-y_shift_origin+getZoomedDim(position.y)+10 ) 310 { 311 source.y += -y_shift_origin+getZoomedDim(position.y)+10-destination.y; 312 destination.y = -y_shift_origin+getZoomedDim(position.y)+10; 313 } 314 } 315 316 int elem_width = getZoomedDim( width); 318 elem_width -= getZoomedDim( imageWidth ); 319 if (getHorizontalAlignment().equals("Center")) { 320 destination.x += elem_width/2; 321 if (destination.x<-x_shift_origin+ getZoomedDim( position.x-10)+10 ) 322 { 323 source.x += -x_shift_origin+ getZoomedDim( position.x-10)+10-destination.x; 324 destination.x = -x_shift_origin+ getZoomedDim( position.x-10)+10; 325 } 326 } 327 else if (getHorizontalAlignment().equals("Right")) { 328 destination.x += elem_width; 329 if (destination.x<-x_shift_origin+ getZoomedDim( position.x-10)+10 ) 330 { 331 source.x += -x_shift_origin+ getZoomedDim( position.x-10)+10-destination.x; 332 destination.x = -x_shift_origin+ getZoomedDim( position.x-10)+10; 333 } 334 } 335 336 g.drawImage( imgx,destination.x, destination.y, destination.x+destination.width, destination.y+destination.height, 337 source.x,source.y,source.width,source.height, 338 null,this); 339 } 340 341 Java2DUtil.resetClip(g); 342 } 343 344 345 if (it.businesslogic.ireport.gui.MainFrame.getMainInstance().getProperties().getProperty("Antialias","true").equals("false")) 346 { 347 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); 348 g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE); 349 } 350 353 position.x += 10 - box.getLeftPadding(); 354 position.y += 10 - box.getTopPadding(); 355 width = width + box.getLeftPadding() + box.getRightPadding(); 356 height = height + box.getTopPadding() + box.getBottomPadding(); 357 358 359 x_shift_origin += 10; 360 y_shift_origin += 10; 361 362 super.drawBorder((Graphics2D)g, zoom_factor, x_shift_origin,y_shift_origin); 363 drawGraphicsElement((Graphics2D)g,this.getGraphicElementPen(), zoom_factor, x_shift_origin,y_shift_origin); 364 drawBorder( g, zoom_factor, x_shift_origin,y_shift_origin,getBox()); 365 } 366 367 368 public ReportElement cloneMe() 369 { 370 ImageReportElement newReportElement = new ImageReportElement(position.x, position.y, width, height); 371 copyBaseReportElement(newReportElement, this); 372 return newReportElement; 373 } 374 375 public void copyBaseReportElement(ReportElement destination, ReportElement source) 376 { 377 super.copyBaseReportElement(destination, source); 378 379 if (destination instanceof ImageReportElement && 380 source instanceof ImageReportElement ) 381 { 382 destination.setPropertyValue(SCALE, source.getPropertyValue(SCALE)); 383 destination.setPropertyValue(VERTICAL_ALIGN, source.getPropertyValue(VERTICAL_ALIGN)); 384 destination.setPropertyValue(HORIZONTAL_ALIGN, source.getPropertyValue(HORIZONTAL_ALIGN)); 385 destination.setPropertyValue(USING_CACHE, source.getPropertyValue(USING_CACHE)); 386 387 ((ImageReportElement)destination).setEvaluationGroup( new String ( ((ImageReportElement)source).getEvaluationGroup() )); 388 ((ImageReportElement)destination).setEvaluationTime( new String ( ((ImageReportElement)source).getEvaluationTime() )); 389 ((ImageReportElement)destination).setImageClass( new String ( ((ImageReportElement)source).getImageClass() )); 391 ((ImageReportElement)destination).setImageExpression( new String ( ((ImageReportElement)source).getImageExpression() )); 392 ((ImageReportElement)destination).setReportDirectory( ((ImageReportElement)source).getReportDirectory() ); 396 397 ((HyperLinkableReportElement)destination).setAnchorNameExpression(new String ( ((HyperLinkableReportElement)source).getAnchorNameExpression() )); 398 ((HyperLinkableReportElement)destination).setHyperlinkAnchorExpression(new String ( ((HyperLinkableReportElement)source).getHyperlinkAnchorExpression() )); 399 ((HyperLinkableReportElement)destination).setHyperlinkPageExpression(new String ( ((HyperLinkableReportElement)source).getHyperlinkPageExpression() )); 400 ((HyperLinkableReportElement)destination).setHyperlinkReferenceExpression(new String ( ((HyperLinkableReportElement)source).getHyperlinkReferenceExpression() )); 401 ((HyperLinkableReportElement)destination).setHyperlinkType(new String ( ((HyperLinkableReportElement)source).getHyperlinkType() )); 402 } 403 } 404 405 public void drawGraphicsElement(Graphics2D g, String pen, double zoom_factor, int x_shift_origin, int y_shift_origin) 406 { 407 408 Stroke stroke = getPenStroke( pen,zoom_factor ); 409 g.setColor( getFgcolor()); 410 411 this.zoom_factor = zoom_factor; 412 if (stroke==null || pen.equalsIgnoreCase("None")) return; 413 414 position.x -= 10; 415 position.y -= 10; 416 x_shift_origin -= 10; 417 y_shift_origin -= 10; 418 419 Stroke oldStroke = g.getStroke(); 420 g.setStroke(stroke); 421 g.drawRect( 422 getZoomedDim(position.x)-x_shift_origin, 423 getZoomedDim(position.y)-y_shift_origin, 424 getZoomedDim(width),getZoomedDim(height)); 425 426 position.x += 10; 427 position.y += 10; 428 429 g.setStroke(oldStroke); 430 } 431 432 436 public java.lang.String getHorizontalAlignment() { 437 if (getPropertyValue( HORIZONTAL_ALIGN) == null) 438 { 439 if (getStyle() != null) 441 { 442 return getStyle().getAttributeString( getStyle().ATTRIBUTE_hAlign, DEFAULT_HORIZONTAL_ALIGN, true); 443 } 444 } 445 return getStringValue( HORIZONTAL_ALIGN, DEFAULT_HORIZONTAL_ALIGN ); 446 } 447 448 452 public void setHorizontalAlignment(java.lang.String horizontalAlignment) { 453 setPropertyValue( HORIZONTAL_ALIGN, horizontalAlignment ); 454 } 455 456 460 public java.lang.String getImageClass() { 461 return imageClass; 462 } 463 464 468 public void setImageClass(java.lang.String imageClass) { 469 this.imageClass = imageClass; 470 } 471 472 476 public java.lang.String getVerticalAlignment() { 477 if (getPropertyValue( VERTICAL_ALIGN) == null) 478 { 479 if (getStyle() != null) 481 { 482 return getStyle().getAttributeString( getStyle().ATTRIBUTE_vAlign, DEFAULT_VERTICAL_ALIGN, true); 483 } 484 } 485 return getStringValue( VERTICAL_ALIGN, DEFAULT_VERTICAL_ALIGN ); 486 } 487 488 492 public void setVerticalAlignment(java.lang.String verticalAlignment) { 493 setPropertyValue( VERTICAL_ALIGN, verticalAlignment ); 494 } 495 496 500 public java.lang.String getEvaluationGroup() { 501 return evaluationGroup; 502 } 503 504 508 public void setEvaluationGroup(java.lang.String evaluationGroup) { 509 this.evaluationGroup = evaluationGroup; 510 } 511 512 516 public java.lang.String getImageExpression() { 517 return imageExpression; 518 } 519 520 524 public void setImageExpression(java.lang.String imageExpression) { 525 this.imageExpression = imageExpression; 526 this.triedToLoadImg = false; 527 } 528 529 533 public java.lang.String getEvaluationTime() { 534 return evaluationTime; 535 } 536 537 541 public void setEvaluationTime(java.lang.String evaluationTime) { 542 this.evaluationTime = evaluationTime; 543 } 544 545 549 public java.awt.Image getImg() { 550 return img; 551 } 552 553 public java.awt.Image getImgDef() { 554 return defimg; 555 } 556 557 561 public void setImg(java.awt.Image img) { 562 this.img = img; 563 } 564 565 569 public java.lang.String getHyperlinkType() { 570 return hyperlinkType; 571 } 572 573 577 public void setHyperlinkType(java.lang.String hyperlinkType) { 578 this.hyperlinkType = hyperlinkType; 579 } 580 581 585 public java.lang.String getAnchorNameExpression() { 586 return anchorNameExpression; 587 } 588 589 593 public void setAnchorNameExpression(java.lang.String anchorNameExpression) { 594 this.anchorNameExpression = anchorNameExpression; 595 } 596 597 601 public java.lang.String getScaleImage() { 602 if (getPropertyValue( SCALE) == null) 603 { 604 if (getStyle() != null) 606 { 607 return getStyle().getAttributeString( getStyle().ATTRIBUTE_scaleImage, DEFAULT_SCALE, true); 608 } 609 } 610 return getStringValue( SCALE, DEFAULT_SCALE ); 611 } 612 613 617 public void setScaleImage(java.lang.String scaleImage) { 618 setPropertyValue( SCALE, scaleImage ); 619 } 620 621 625 public java.lang.String getHyperlinkReferenceExpression() { 626 return hyperlinkReferenceExpression; 627 } 628 629 633 public void setHyperlinkReferenceExpression(java.lang.String hyperlinkReferenceExpression) { 634 this.hyperlinkReferenceExpression = hyperlinkReferenceExpression; 635 } 636 637 641 public java.lang.String getHyperlinkAnchorExpression() { 642 return hyperlinkAnchorExpression; 643 } 644 645 649 public void setHyperlinkAnchorExpression(java.lang.String hyperlinkAnchorExpression) { 650 this.hyperlinkAnchorExpression = hyperlinkAnchorExpression; 651 } 652 653 657 public java.lang.String getHyperlinkPageExpression() { 658 return hyperlinkPageExpression; 659 } 660 661 665 public void setHyperlinkPageExpression(java.lang.String hyperlinkPageExpression) { 666 this.hyperlinkPageExpression = hyperlinkPageExpression; 667 } 668 669 720 public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { 721 return true; 722 } 723 724 public static void setImgDef(Image defImg) 725 { 726 defimg = defImg; 727 } 728 729 733 public boolean isIsUsingCache() { 734 735 return getBooleanValue( USING_CACHE, DEFAULT_USING_CACHE ); 736 } 737 738 742 public void setIsUsingCache(boolean isUsingCache) { 743 setPropertyValue( USING_CACHE, new Boolean ( isUsingCache ) ); 744 } 745 746 749 public File getReportDirectory() { 750 return reportDirectory; 751 } 752 753 756 public void setReportDirectory(File directory) { 757 reportDirectory = directory; 758 } 759 760 protected String hyperlinkTarget = "Self"; 761 765 public java.lang.String getHyperlinkTarget(){ return hyperlinkTarget; } 766 767 771 public void setHyperlinkTarget(java.lang.String hyperlinkTarget) { this.hyperlinkTarget = hyperlinkTarget; } 772 773 public Box getBox() { 774 return box; 775 } 776 777 public void setBox(Box box) { 778 this.box = box; 779 } 780 781 public boolean isIsLazy() { 782 return isLazy; 783 } 784 785 public void setIsLazy(boolean isLazy) { 786 this.isLazy = isLazy; 787 } 788 789 public int getBookmarkLevel() { 790 return bookmarkLevel; 791 } 792 793 public void setBookmarkLevel(int bookmarkLevel) { 794 this.bookmarkLevel = bookmarkLevel; 795 } 796 797 public String getOnErrorType() { 798 return onErrorType; 799 } 800 801 public void setOnErrorType(String onErrorType) { 802 this.onErrorType = onErrorType; 803 } 804 805 806 public void setStyle(Style style) { 807 808 super.setStyle(style); 809 810 if (style != null) 811 { 812 815 817 if (style.getAttributeString(style.ATTRIBUTE_border, null, true) != null) 819 this.getBox().setBorder( style.getAttributeString(style.ATTRIBUTE_border, null, true) ); 820 if (style.getAttributeColor(style.ATTRIBUTE_borderColor, null, true) != null) 821 this.getBox().setBorderColor( style.getAttributeColor(style.ATTRIBUTE_borderColor, null, true)); 822 if (style.getAttributeString(style.ATTRIBUTE_padding, null, true) != null) 823 this.getBox().setPadding( Integer.parseInt( style.getAttributeString(style.ATTRIBUTE_padding, null, true) )); 824 825 if (style.getAttributeString(style.ATTRIBUTE_topBorder, null, true) != null) 826 this.getBox().setTopBorder( style.getAttributeString(style.ATTRIBUTE_topBorder, null, true) ); 827 if (style.getAttributeColor(style.ATTRIBUTE_topBorderColor, null, true) != null) 828 this.getBox().setTopBorderColor( style.getAttributeColor(style.ATTRIBUTE_topBorderColor, null, true)); 829 if (style.getAttributeString(style.ATTRIBUTE_topPadding, null, true) != null) 830 this.getBox().setTopPadding( Integer.parseInt( style.getAttributeString(style.ATTRIBUTE_topPadding, null, true) )); 831 832 if (style.getAttributeString(style.ATTRIBUTE_leftBorder, null, true) != null) 833 this.getBox().setLeftBorder( style.getAttributeString(style.ATTRIBUTE_leftBorder, null, true) ); 834 if (style.getAttributeColor(style.ATTRIBUTE_leftBorderColor, null, true) != null) 835 this.getBox().setLeftBorderColor( style.getAttributeColor(style.ATTRIBUTE_leftBorderColor, null, true)); 836 if (style.getAttributeString(style.ATTRIBUTE_leftPadding, null, true) != null) 837 this.getBox().setLeftPadding( Integer.parseInt( style.getAttributeString(style.ATTRIBUTE_leftPadding, null, true) )); 838 839 if (style.getAttributeString(style.ATTRIBUTE_rightBorder, null, true) != null) 840 this.getBox().setRightBorder( style.getAttributeString(style.ATTRIBUTE_rightBorder, null, true) ); 841 if (style.getAttributeColor(style.ATTRIBUTE_rightBorderColor, null, true) != null) 842 this.getBox().setRightBorderColor( style.getAttributeColor(style.ATTRIBUTE_rightBorderColor, null, true)); 843 if (style.getAttributeString(style.ATTRIBUTE_rightPadding, null, true) != null) 844 this.getBox().setRightPadding( Integer.parseInt( style.getAttributeString(style.ATTRIBUTE_rightPadding, null, true) )); 845 846 if (style.getAttributeString(style.ATTRIBUTE_bottomBorder, null, true) != null) 847 this.getBox().setBottomBorder( style.getAttributeString(style.ATTRIBUTE_bottomBorder, null, true) ); 848 if (style.getAttributeColor(style.ATTRIBUTE_bottomBorderColor, null, true) != null) 849 this.getBox().setBottomBorderColor( style.getAttributeColor(style.ATTRIBUTE_bottomBorderColor, null, true)); 850 if (style.getAttributeString(style.ATTRIBUTE_bottomPadding, null, true) != null) 851 this.getBox().setBottomPadding( Integer.parseInt( style.getAttributeString(style.ATTRIBUTE_bottomPadding, null, true) )); 852 853 } 854 } 855 856 public java.util.List getLinkParameters() { 857 return linkParameters; 858 } 859 860 public void setLinkParameters(java.util.List linkParameters) { 861 this.linkParameters = linkParameters; 862 } 863 864 } 865 | Popular Tags |