001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.gwt.client.util; 029 030import java.util.HashMap; 031import java.util.Map; 032 033import com.google.gwt.dom.client.Element; 034import com.google.gwt.dom.client.Style; 035import com.google.gwt.user.client.DOM; 036import com.google.gwt.user.client.ui.RootPanel; 037 038/** 039 * Provides precise pixel measurements for blocks of text so that you can 040 * determine exactly how high and wide, in pixels, a given block of text will 041 * be.<p> 042 * 043 * Normal usage would be: 044 * <pre> 045 * for(Element e: elements) { 046 * CmsTextMetrics tm = CmsTextMetrics.get(e, "TextMetricsKey"); 047 * 048 * // measure text 049 * if (r.getWidth(text) > 500) { 050 * // do something 051 * } 052 * // release 053 * tm.release(); 054 * } 055 * </pre> 056 * 057 * Based on <a href="http://code.google.com/p/my-gwt/source/browse/trunk/user/src/net/mygwt/ui/client/util/TextMetrics.java">my-gwt TextMetrics</a>.<p> 058 * 059 * @since 8.0.0 060 */ 061public final class CmsTextMetrics { 062 063 /** Default attributes to bind. */ 064 private static final CmsDomUtil.Style[] ATTRIBUTES = new CmsDomUtil.Style[] { 065 CmsDomUtil.Style.fontSize, 066 CmsDomUtil.Style.fontSizeAdjust, 067 CmsDomUtil.Style.fontFamily, 068 CmsDomUtil.Style.fontStretch, 069 CmsDomUtil.Style.fontStyle, 070 CmsDomUtil.Style.fontVariant, 071 CmsDomUtil.Style.fontWeight, 072 CmsDomUtil.Style.letterSpacing, 073 CmsDomUtil.Style.textAlign, 074 CmsDomUtil.Style.textDecoration, 075 CmsDomUtil.Style.textIndent, 076 CmsDomUtil.Style.textShadow, 077 CmsDomUtil.Style.textTransform, 078 CmsDomUtil.Style.lineHeight, 079 CmsDomUtil.Style.whiteSpace, 080 CmsDomUtil.Style.wordSpacing, 081 CmsDomUtil.Style.wordWrap, 082 CmsDomUtil.Style.padding}; 083 084 /** The map containing the instances. */ 085 private static Map<String, CmsTextMetrics> m_instances = new HashMap<String, CmsTextMetrics>(); 086 087 /** The playground. */ 088 private Element m_elem; 089 090 /** The text metrics key. */ 091 private String m_key; 092 093 /** 094 * Internal constructor for creating a text metrics object with a given key.<p> 095 * 096 * @param key the key identifying the text metrics. 097 */ 098 private CmsTextMetrics(String key) { 099 100 m_key = key; 101 } 102 103 /** 104 * Gets the text metrics object for a given DOM element and key.<p> 105 * 106 * If the key is null, or the method has been never called with the same key 107 * before, a new text metrics object will be created, with its style taken from 108 * the element parameter. Otherwise, the text metrics object for the given key will 109 * be returned, and the element parameter will be ignored. 110 * 111 * @param element the element from which to take the style 112 * @param key the text metrics key 113 * 114 * @return a text metrics object 115 */ 116 public static CmsTextMetrics get(Element element, String key) { 117 118 CmsTextMetrics instance = null; 119 if (key == null) { 120 instance = new CmsTextMetrics(key); 121 instance.bind(element); 122 } else { 123 instance = m_instances.get(key); 124 if (instance == null) { 125 instance = new CmsTextMetrics(key); 126 instance.bind(element); 127 m_instances.put(key, instance); 128 } 129 } 130 return instance; 131 } 132 133 /** 134 * Returns the measured height of the specified text. For multiline text, be 135 * sure to call {@link #setFixedWidth} if necessary.<p> 136 * 137 * @param text the text to be measured 138 * @return the height in pixels 139 */ 140 public int getHeight(String text) { 141 142 m_elem.setInnerText(text); 143 return CmsDomUtil.getCurrentStyleInt(m_elem, CmsDomUtil.Style.height); 144 } 145 146 /** 147 * Returns the measured width of the specified text.<p> 148 * 149 * @param text the text to measure 150 * @return the width in pixels 151 */ 152 public int getWidth(String text) { 153 154 m_elem.setInnerText(text); 155 return CmsDomUtil.getCurrentStyleInt(m_elem, CmsDomUtil.Style.width); 156 } 157 158 /** 159 * Should be called, when finished measuring, to release the playground.<p> 160 */ 161 public void release() { 162 163 if (m_key == null) { 164 m_elem.removeFromParent(); 165 m_elem = null; 166 } 167 // if we have a key, we do nothing so that the instance can be reused later 168 169 } 170 171 /** 172 * Sets a fixed width on the internal measurement element. If the text will be 173 * multiline, you have to set a fixed width in order to accurately measure the 174 * text height.<p> 175 * 176 * @param width the width to set on the element 177 */ 178 public void setFixedWidth(int width) { 179 180 m_elem.getStyle().setWidth(width, Style.Unit.PX); 181 } 182 183 /** 184 * Binds this text metrics instance to an element from which to copy existing 185 * CSS styles that can affect the size of the rendered text.<p> 186 * 187 * @param element the element 188 */ 189 protected void bind(Element element) { 190 191 bind(element, ATTRIBUTES); 192 } 193 194 /** 195 * Binds this text metrics instance to an element from which to copy existing 196 * CSS styles that can affect the size of the rendered text.<p> 197 * 198 * @param element the element 199 * @param attributes the attributes to bind 200 */ 201 protected void bind(Element element, CmsDomUtil.Style... attributes) { 202 203 if (m_elem == null) { 204 // create playground 205 m_elem = DOM.createDiv(); 206 Style style = m_elem.getStyle(); 207 style.setVisibility(Style.Visibility.HIDDEN); 208 style.setPosition(Style.Position.ABSOLUTE); 209 style.setLeft(-5000, Style.Unit.PX); 210 style.setTop(-5000, Style.Unit.PX); 211 } 212 // copy all relevant CSS properties 213 Style style = m_elem.getStyle(); 214 for (CmsDomUtil.Style attr : attributes) { 215 String attrName = attr.toString(); 216 style.setProperty(attrName, CmsDomUtil.getCurrentStyle(element, attr)); 217 } 218 // append playground 219 RootPanel.getBodyElement().appendChild(m_elem); 220 } 221}