Changeset 3855

Show
Ignore:
Timestamp:
10/06/11 12:05:15 (8 months ago)
Author:
sbrunner
Message:

Add the possibility to have a legend on multi columns (closes #646)

Location:
print/trunk
Files:
3 modified

Legend:

Unmodified
Added
Removed
  • print/trunk/docs/configuration.txt

    r3831 r3855  
    274274  ?         fontSize: 12 
    275275  ?         fontEncoding: Cp1252 
    276   ?          fontColor: black 
     276  ?         fontColor: black 
    277277  ?         spacingAfter: 0 
    278278  ?         align: left 
     
    515515 
    516516          - !legends 
     517  ?         maxHeight: 0 
     518  ?         maxWidth: 0 
    517519  ?         maxIconWidth: 8  
    518520  ?         maxIconHeight: 8 
     
    528530  ?         classFontSize: 8 
    529531  ?         fontEncoding: Cp1252 
     532  ?         columnMargin: 3 
    530533 
    531534**maxIconWidth**, **maxIconHeight**, **defaultScale** with value of 0 indicates that the value will be ignore. 
     535 
     536**maxHeight** with value of 0 indicates that the multi column is disable. 
     537 
     538if **inline** is true the multi column is disable. 
     539 
     540if **maxWidth** is set that mean that in the multi column mode the column next to the maxWidth will not be add. 
    532541 
    533542if **defaultScale** is non null it means that the legend image will be scaled so it doesn't take the full space. 
  • print/trunk/src/main/java/org/mapfish/print/PDFUtils.java

    r3832 r3855  
    2020package org.mapfish.print; 
    2121 
    22 import com.lowagie.text.*; 
    23 import com.lowagie.text.pdf.BaseFont; 
    24 import com.lowagie.text.pdf.PdfContentByte; 
    25 import com.lowagie.text.pdf.PdfPCell; 
    26 import com.lowagie.text.pdf.PdfPTable; 
    27 import com.lowagie.text.pdf.PdfTemplate; 
     22import java.awt.Graphics2D; 
     23import java.io.File; 
     24import java.io.IOException; 
     25import java.net.MalformedURLException; 
     26import java.net.URI; 
     27import java.net.URL; 
     28import java.text.SimpleDateFormat; 
     29import java.util.Date; 
     30import java.util.List; 
     31import java.util.Map; 
     32import java.util.regex.Matcher; 
     33import java.util.regex.Pattern; 
    2834 
    2935import org.apache.batik.bridge.BridgeContext; 
     
    4046import org.apache.commons.httpclient.methods.GetMethod; 
    4147import org.apache.log4j.Logger; 
    42 import org.mapfish.print.config.layout.*; 
     48import org.mapfish.print.config.layout.Block; 
     49import org.mapfish.print.config.layout.HorizontalAlign; 
     50import org.mapfish.print.config.layout.MapBlock; 
     51import org.mapfish.print.config.layout.ScalebarBlock; 
     52import org.mapfish.print.config.layout.TableConfig; 
    4353import org.mapfish.print.utils.PJsonObject; 
    4454import org.w3c.dom.svg.SVGDocument; 
    4555 
    46 import java.awt.Graphics2D; 
    47 import java.io.File; 
    48 import java.io.IOException; 
    49 import java.net.MalformedURLException; 
    50 import java.net.URI; 
    51 import java.net.URL; 
    52 import java.text.SimpleDateFormat; 
    53 import java.util.Date; 
    54 import java.util.List; 
    55 import java.util.Map; 
    56 import java.util.regex.Matcher; 
    57 import java.util.regex.Pattern; 
     56import com.lowagie.text.BadElementException; 
     57import com.lowagie.text.Chunk; 
     58import com.lowagie.text.DocumentException; 
     59import com.lowagie.text.Element; 
     60import com.lowagie.text.Font; 
     61import com.lowagie.text.Image; 
     62import com.lowagie.text.Phrase; 
     63import com.lowagie.text.pdf.BaseFont; 
     64import com.lowagie.text.pdf.PdfContentByte; 
     65import com.lowagie.text.pdf.PdfPCell; 
     66import com.lowagie.text.pdf.PdfPTable; 
     67import com.lowagie.text.pdf.PdfTemplate; 
    5868 
    5969/** 
  • print/trunk/src/main/java/org/mapfish/print/config/layout/LegendsBlock.java

    r3852 r3855  
    2020package org.mapfish.print.config.layout; 
    2121 
     22import java.io.IOException; 
     23import java.net.URI; 
     24import java.util.ArrayList; 
     25 
     26import org.apache.log4j.Logger; 
     27import org.mapfish.print.InvalidValueException; 
     28import org.mapfish.print.PDFUtils; 
     29import org.mapfish.print.RenderingContext; 
     30import org.mapfish.print.utils.PJsonArray; 
     31import org.mapfish.print.utils.PJsonObject; 
     32 
     33import com.lowagie.text.Chunk; 
    2234import com.lowagie.text.DocumentException; 
    2335import com.lowagie.text.Font; 
     
    2840import com.lowagie.text.pdf.PdfPTable; 
    2941 
    30 import org.apache.log4j.Logger; 
    31 import org.mapfish.print.PDFUtils; 
    32 import org.mapfish.print.RenderingContext; 
    33 import org.mapfish.print.InvalidValueException; 
    34 import org.mapfish.print.utils.PJsonArray; 
    35 import org.mapfish.print.utils.PJsonObject; 
    36  
    37 import java.io.IOException; 
    38 import java.net.URI; 
    39  
    4042/** 
    4143 * Bean to configure a !legends block. 
     
    4648    public static final Logger LOGGER = Logger.getLogger(LegendsBlock.class); 
    4749 
     50    private double maxHeight = 0; // 0 mean multi column disable 
     51    private double maxWidth = 0; 
     52 
    4853    private double maxIconWidth = 0; // 0 mean disable 
    4954    private double maxIconHeight = 8; // 0 mean disable 
     
    5964    protected double classFontSize = 8; 
    6065    private String fontEncoding = BaseFont.WINANSI; 
    61  
     66     
     67    private double columnMargin = 3; 
     68     
     69    /** 
     70     * Render the legends block 
     71     * @see org.mapfish.print.config.layout.Block#render(org.mapfish.print.utils.PJsonObject, org.mapfish.print.config.layout.Block.PdfElement, org.mapfish.print.RenderingContext) 
     72     */ 
    6273    public void render(PJsonObject params, PdfElement target, RenderingContext context) throws DocumentException { 
    63         PdfPTable table = new PdfPTable(1); 
    64         table.setWidthPercentage(100f); 
    65  
    66         Font layerPdfFont = getLayerPdfFont(); 
    67         Font classPdfFont = getClassPdfFont(); 
    68  
    69         PJsonArray legends = context.getGlobalParams().optJSONArray("legends"); 
    70         if (legends != null && legends.size() > 0) { 
    71             for (int i = 0; i < legends.size(); ++i) { 
    72                 PJsonObject layer = legends.getJSONObject(i); 
    73                 createLine(context, 0.0, layer, layerPdfFont, params, table, i == 0 ? layerSpace : 0); 
    74  
    75                 PJsonArray classes = layer.getJSONArray("classes"); 
    76                 for (int j = 0; j < classes.size(); ++j) { 
    77                     PJsonObject clazz = classes.getJSONObject(j); 
    78                     createLine(context, classIndentation, clazz, classPdfFont, params, table, classSpace); 
    79                 } 
    80             } 
     74        Renderer renderer = new Renderer(params, context); 
     75        renderer.render(target); 
     76    } 
     77     
     78    /** 
     79     * A renderer to render the legend block 
     80     * @author Stéphane Brunner 
     81     */ 
     82    private class Renderer { 
     83        private PJsonObject params; 
     84        private RenderingContext context; 
     85         
     86        // all the pdf columns 
     87        private ArrayList<PdfPTable> columns = new ArrayList<PdfPTable>(); 
     88        // all the columns width 
     89        private ArrayList<Float> columnsWidth = new ArrayList<Float>(); 
     90        // the current cell width 
     91        private float cellWidth = 0; 
     92        // the current column 
     93        private PdfPTable column = new PdfPTable(1); 
     94        // the curent title 
     95        private PdfPCell title; 
     96        // the current cell height 
     97        private double currentCellHeight = 0; 
     98        // the current column height  
     99        private double currentColumnHeight = 0; 
     100 
     101        /** 
     102         * Construct 
     103         * @param params the params 
     104         * @param context the context 
     105         */ 
     106        public Renderer(PJsonObject params, RenderingContext context) { 
     107            column.setWidthPercentage(100f); 
     108            columns.add(column); 
     109            currentCellHeight = 0; 
     110            columnsWidth.add(0f); 
     111            this.params = params; 
     112            this.context = context; 
    81113        } 
    82         table.setSpacingAfter((float) spacingAfter); 
    83         target.add(table); 
    84     } 
    85  
    86     private void createLine(RenderingContext context, double indent, PJsonObject node, Font pdfFont, PJsonObject params, 
    87             PdfPTable table, float lineSpace) throws DocumentException { 
    88         final String name = node.getString("name"); 
    89         final String icon = node.optString("icon"); 
    90         final PJsonArray icons = node.optJSONArray("icons"); 
    91  
    92         Paragraph result = new Paragraph(); 
    93         if (icon != null) { 
     114         
     115        /** 
     116         * Render 
     117         * @param target the target element 
     118         * @throws DocumentException 
     119         */ 
     120        public void render(PdfElement target) throws DocumentException { 
     121 
     122            Font layerPdfFont = getLayerPdfFont(); 
     123            Font classPdfFont = getClassPdfFont(); 
     124 
     125            // create the legend 
     126            PJsonArray legends = context.getGlobalParams().optJSONArray("legends"); 
     127            if (legends != null && legends.size() > 0) { 
     128                for (int i = 0; i < legends.size(); ++i) { 
     129                    PJsonObject layer = legends.getJSONObject(i); 
     130                    createLine(0.0, layer, layerPdfFont, i == 0 ? 0 : layerSpace, true); 
     131     
     132                    PJsonArray classes = layer.getJSONArray("classes"); 
     133                    for (int j = 0; j < classes.size(); ++j) { 
     134                        PJsonObject clazz = classes.getJSONObject(j); 
     135                        createLine(classIndentation, clazz, classPdfFont, classSpace, false); 
     136                    } 
     137                } 
     138            } 
     139             
     140            // complete the width of the last column 
     141            {  
     142                int index = columnsWidth.size() - 1; 
     143                columnsWidth.set(index, Math.max(columnsWidth.get(index), cellWidth)); 
     144            } 
     145            if (title != null) { 
     146                column.addCell(title); 
     147            } 
     148             
     149            // calculate the fullWidth (sum of width of visible column with margin) 
     150            int len = columns.size(); 
     151            float fullWidth = 0; 
     152            for (int i = 0 ; i < len ; i++) { 
     153                float width = columnsWidth.get(i); 
     154                if (fullWidth + width < maxWidth) { 
     155                    fullWidth += width + columnMargin; 
     156                } 
     157                else { 
     158                    len = i + 1; 
     159                } 
     160            } 
     161             
     162            // create the column with array for the table 
     163            float[] pdfWidths = new float[len + 1]; 
     164            for (int i = 0 ; i < len ; i++) { 
     165                pdfWidths[i] = columnsWidth.get(i); 
     166            } 
     167            // for empty column 
     168            pdfWidths[len] = Math.max(0f, (float)maxWidth - fullWidth); 
     169             
     170            // table used for column 
     171            PdfPTable table = new PdfPTable(pdfWidths); 
     172            table.setWidthPercentage(100f); 
     173            table.getDefaultCell().setBorder(PdfPCell.NO_BORDER); 
     174            table.getDefaultCell().setPadding(0f); 
     175            table.setSpacingAfter((float) spacingAfter); 
     176     
     177            // add columns 
     178            for (int i = 0 ; i < len ; i++) { 
     179                table.addCell(columns.get(i)); 
     180            } 
     181            // create empty column 
     182            table.addCell(""); 
     183             
     184            // add to result  
     185            target.add(table); 
     186        } 
     187     
     188        /** 
     189         * Display a legend or class block (name and icon(s) ). 
     190         * In not inline mode it's a line.  
     191         * @param indent left indentation 
     192         * @param node the json node 
     193         * @param pdfFont the font used for the title 
     194         * @param lineSpace the top indent for the icons 
     195         * @param escapeOrphanTitle don't have orphan title 
     196         * @throws DocumentException 
     197         */ 
     198        private void createLine(double indent, PJsonObject node, Font pdfFont, 
     199                float lineSpace, boolean escapeOrphanTitle) throws DocumentException { 
     200            final String name = node.getString("name"); 
     201            final String icon = node.optString("icon"); 
     202            final PJsonArray icons = node.optJSONArray("icons"); 
     203     
     204            Paragraph result = new Paragraph(); 
     205            if (icon != null) { 
     206                result = createIcon(indent, lineSpace, icon, result); 
     207            } 
     208            if (icons != null) { 
     209                for (int i = 0; i < icons.size(); ++i) { 
     210                    String iconItem = icons.getString(i); 
     211                    result = createIcon(indent, i == 0 ? lineSpace : 0, 
     212                            iconItem, result); 
     213                } 
     214            } 
     215     
     216            if (title != null) { 
     217                column.addCell(title); 
     218            } 
     219            title = new PdfPCell(result); 
     220            title.setBorder(PdfPCell.NO_BORDER); 
     221            title.setPadding(0f); 
     222            title.setPaddingLeft((float)indent); 
     223            if (inline) { 
     224                title.setPaddingTop(lineSpace); 
     225            } 
     226     
     227            result.setFont(pdfFont); 
     228            result.add(name); 
     229            if (name.trim().length() > 0) { 
     230                float width = pdfFont.getBaseFont().getWidthPoint(name, pdfFont.getSize()); 
     231                if (escapeOrphanTitle) { 
     232                    currentCellHeight += pdfFont.getSize(); 
     233                    cellWidth = Math.max(cellWidth, width); 
     234                } 
     235                else { 
     236                    currentColumnHeight += pdfFont.getSize(); 
     237                    int index = columnsWidth.size() - 1; 
     238                    columnsWidth.set(index, Math.max(columnsWidth.get(index), width)); 
     239                } 
     240            } 
     241     
     242            if (getBackgroundColorVal(context, params) != null) { 
     243                title.setBackgroundColor(getBackgroundColorVal(context, params)); 
     244            } 
     245            if (!escapeOrphanTitle) { 
     246                column.addCell(title); 
     247                title = null; 
     248            } 
     249        } 
     250 
     251        /** 
     252         * Creates a legend icon 
     253         * @param indent left indentation 
     254         * @param lineSpace the top indent for the icons 
     255         * @param icon the icon to display 
     256         * @param result the element to add to 
     257         * @return the result for the next elements 
     258         * @throws DocumentException 
     259         */ 
     260        private Paragraph createIcon(double indent, float lineSpace, 
     261                final String icon, Paragraph result) throws DocumentException { 
    94262            try { 
     263                Chunk iconChunk = null; 
    95264                if (icon.indexOf("image%2Fsvg%2Bxml") != -1) { // TODO: make this cleaner 
    96                     result.add(PDFUtils.createImageChunkFromSVG(context, icon, maxIconWidth, maxIconHeight)); 
     265                    iconChunk = PDFUtils.createImageChunkFromSVG(context, icon, maxIconWidth, maxIconHeight); 
    97266                } else { 
    98                     result.add(PDFUtils.createImageChunk(context, maxIconWidth, maxIconHeight, URI.create(icon), 0f)); 
    99                 } 
     267                    iconChunk = PDFUtils.createImageChunk(context, maxIconWidth, maxIconHeight, scale,  
     268                            URI.create(icon), 0f); 
     269                } 
     270                result.add(iconChunk); 
    100271                if (!inline) { 
    101                     addCell(context, indent, params, table, lineSpace, result); 
     272                    currentCellHeight += iconChunk.getImage().getPlainHeight() + lineSpace; 
     273                    cellWidth = Math.max(cellWidth, iconChunk.getImage().getPlainWidth()); 
     274                    addCell(indent, lineSpace, result); 
    102275                    result = new Paragraph(); 
    103                     result.setFont(pdfFont); 
    104276                } 
    105277                else { 
     
    111283                LOGGER.warn("Failed to create image chunk: " + e.getMessage()); 
    112284            } 
     285            return result; 
    113286        } 
    114         if (icons != null) { 
    115             for (int i = 0; i < icons.size(); ++i) { 
    116                 String iconItem = icons.getString(i); 
    117                 try { 
    118                     if (iconItem.indexOf("image%2Fsvg%2Bxml") != -1) { // TODO: make this cleaner 
    119                         result.add(PDFUtils.createImageChunkFromSVG(context, iconItem, maxIconWidth, maxIconHeight)); 
    120                     } else { 
    121                         result.add(PDFUtils.createImageChunk(context, maxIconWidth, maxIconHeight, scale, URI.create(iconItem), 0f)); 
    122                     } 
    123                     if (!inline) { 
    124                         addCell(context, indent, params, table, 0, result); 
    125                         result = new Paragraph(); 
    126                         result.setFont(pdfFont); 
    127                     } 
    128                     else { 
    129                         result.add(" "); 
    130                     } 
    131                 } catch (IOException ioe) { 
    132                     LOGGER.warn("Failed to load " + iconItem + " with " + ioe.getMessage()); 
    133                 } catch (InvalidValueException e) { 
    134                     LOGGER.warn("Failed to create image chunk: " + e.getMessage()); 
    135                 } 
    136             } 
     287 
     288        /** 
     289         * Add an icon in the column as a cell. 
     290         * @param indent left indentation 
     291         * @param lineSpace the top indent for the icons 
     292         * @param icon the icon element to add 
     293         */ 
     294        private void addCell(double indent, float lineSpace, final Paragraph icon) { 
     295            final PdfPCell cell = new PdfPCell(icon); 
     296            cell.setBorder(PdfPCell.NO_BORDER); 
     297            cell.setPadding(0f); 
     298            cell.setPaddingLeft((float) indent); 
     299     
     300            if (getBackgroundColorVal(context, params) != null) { 
     301                cell.setBackgroundColor(getBackgroundColorVal(context, params)); 
     302            } 
     303     
     304            cell.setPaddingTop(lineSpace); 
     305            currentCellHeight += lineSpace; 
     306            if (!inline && maxHeight != 0 && (currentColumnHeight + currentCellHeight > maxHeight)) { 
     307                column = new PdfPTable(1); 
     308                column.setWidthPercentage(100f); 
     309                columns.add(column); 
     310                currentColumnHeight = 0; 
     311                columnsWidth.add(cellWidth); 
     312            } 
     313            else { 
     314                int index = columnsWidth.size() - 1; 
     315                columnsWidth.set(index, Math.max(columnsWidth.get(index), cellWidth)); 
     316            } 
     317            cellWidth = 0; 
     318            currentColumnHeight += currentCellHeight; 
     319            currentCellHeight = 0; 
     320            if (title != null) { 
     321                column.addCell(title); 
     322                title = null; 
     323            } 
     324            column.addCell(cell); 
    137325        } 
    138         result.add(name); 
    139  
    140         final PdfPCell cell = new PdfPCell(result); 
    141         cell.setBorder(PdfPCell.NO_BORDER); 
    142         cell.setPadding(0f); 
    143         cell.setPaddingLeft((float) indent); 
    144  
    145         result.setFont(pdfFont); 
    146         result.add(name); 
    147  
    148         if (getBackgroundColorVal(context, params) != null) { 
    149             cell.setBackgroundColor(getBackgroundColorVal(context, params)); 
    150         } 
    151         table.addCell(cell); 
    152     } 
    153  
    154     private void addCell(RenderingContext context, double indent, PJsonObject params, PdfPTable table, 
    155             float lineSpace, final Paragraph result) { 
    156         final PdfPCell cell = new PdfPCell(result); 
    157         cell.setBorder(PdfPCell.NO_BORDER); 
    158         cell.setPadding(0f); 
    159         cell.setPaddingLeft((float) indent); 
    160  
    161         if (getBackgroundColorVal(context, params) != null) { 
    162             cell.setBackgroundColor(getBackgroundColorVal(context, params)); 
    163         } 
    164  
    165         cell.setPaddingTop(lineSpace); 
    166         table.addCell(cell); 
     326    } 
     327 
     328    public void setMaxWidth(double maxWidth) { 
     329        this.maxWidth = maxWidth; 
     330        if (maxWidth < 0.0) throw new InvalidValueException("maxWidth", maxWidth); 
     331    } 
     332 
     333    public void setMaxHeight(double maxHeight) { 
     334        this.maxHeight = maxHeight; 
     335        if (maxHeight < 0.0) throw new InvalidValueException("maxHeight", maxHeight); 
    167336    } 
    168337 
     
    174343    public void setInline(String inline) { 
    175344        this.inline = "true".equalsIgnoreCase(inline); 
    176         if (!inline.equalsIgnoreCase("true") || !inline.equalsIgnoreCase("false")) throw new InvalidValueException("inline", inline); 
     345        if (!(inline.equalsIgnoreCase("true") || inline.equalsIgnoreCase("false"))) throw new InvalidValueException("inline", inline); 
    177346    } 
    178347     
     
    235404        this.fontEncoding = fontEncoding; 
    236405    } 
     406     
     407    public void setColumnMargin(double columnMargin) { 
     408        this.columnMargin = columnMargin; 
     409    } 
    237410}