/******************************************************************************* * Copyright (c) 2006, 2015 Innoopract Informationssysteme GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Innoopract Informationssysteme GmbH - initial API and implementation * EclipseSource - ongoing development ******************************************************************************/ package org.eclipse.rap.rwt.internal.textsize; import static org.eclipse.rap.rwt.internal.util.EncodingUtil.splitNewLines; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.internal.graphics.FontUtil; public final class TextSizeEstimation { /** * Estimates the size of a given text. Line breaks are not respected. * @param font the font to perform the estimation for * @param string the text whose size to estimate * @return the estimated size */ static Point stringExtent( Font font, String string ) { int width = getLineWidth( string, font ); int height = getCharHeight( font ) + 2; return new Point( width, height ); } /** * Estimates the size of a given text, respecting line breaks and wrapping at * a given width. * @param font the font to perform the estimation for * @param string the text whose size to estimate * @param wrapWidth the width to wrap at in pixels, 0 or negative stands for no wrapping * * @return the estimated size */ static Point textExtent( Font font, String string, int wrapWidth ) { int lineCount = 0; int maxWidth = 0; for( String line : splitNewLines( string ) ) { lineCount++; int width = getLineWidth( line, font ); if( wrapWidth > 0 ) { boolean done = false; while( !done ) { int index = getLongestMatch( line, wrapWidth, font ); if( index == 0 || index == line.length() ) { // line fits or cannot be wrapped done = true; } else { // wrap line String substr = line.substring( 0, index ); width = getLineWidth( substr, font ); maxWidth = Math.max( maxWidth, width ); line = line.substring( index, line.length() ); lineCount++; } } } maxWidth = Math.max( maxWidth, width ); } int height = Math.round( getCharHeight( font ) * 1.25f * lineCount ); return new Point( maxWidth, height ); } /** * Estimates the size of a given markup text, respecting wrapping at a given width. * Line break tag is replaced with a new line character. All other tags are removed. * @param font the font to perform the estimation for * @param markup the markup text whose size to estimate * @param wrapWidth the width to wrap at in pixels, 0 or negative stands for no wrapping * * @return the estimated size */ static Point markupExtent( Font font, String markup, int wrapWidth ) { return textExtent( font, removeAllTags( markup ), wrapWidth ); } /** * Returns the character height in pixels. The returned value is only a rough * estimation. * * @param font the font to perform the estimation for * @return the estimated character height in pixels */ static int getCharHeight( Font font ) { // at 72 dpi, 1 pt == 1 px return FontUtil.getData( font ).getHeight(); } /** * Returns the average character weight in pixels. The returned value is only * a rough estimation that does not take the font family into account. * * @param font the font to perform the estimation for * @return the estimated average character width in pixels */ static float getAvgCharWidth( Font font ) { float result; FontData fontData = FontUtil.getData( font ); ProbeResultStore probeStore = ProbeResultStore.getInstance(); if( probeStore.containsProbeResult( fontData ) ) { // we can improve char width estimations in case that we already have the // specified font probed. result = probeStore.getProbeResult( fontData ).getAvgCharWidth(); } else { result = fontData.getHeight() * 0.48f; if( ( fontData.getStyle() & SWT.BOLD ) != 0 ) { result *= 1.45; } } return result; } static String removeAllTags( String markup ) { StringBuilder result = new StringBuilder(); StringBuilder tag = new StringBuilder(); boolean inTag = false; int length = markup.length(); for( int i = 0; i < length; i++ ) { char ch = markup.charAt( i ); if( ch == '<' ) { inTag = true; } else if( ch == '>' ) { if( tag.toString().equalsIgnoreCase( "br" ) ) { result.append( "\n" ); } tag.setLength( 0 ); inTag = false; } else if( inTag ) { if( Character.isLetterOrDigit( ch ) ) { tag.append( ch ); } } else { result.append( ch ); } } return result.toString(); } /** * Returns the length of the longest substring, whose width is smaller or * equal to wrapWidth. If there is no such substring, zero is returned. The * result is never negative. */ private static int getLongestMatch( String string, int wrapWidth, Font font ) { int result = 0; if( getLineWidth( string, font ) < wrapWidth ) { result = string.length(); } else { String subStr = nextSubLine( string, 0 ); boolean done = false; while( !done && getLineWidth( subStr, font ) <= wrapWidth ) { result = subStr.length(); // loop prevention (see bug 182754) if( subStr.length() == string.length() ) { done = true; } else { subStr = nextSubLine( string, subStr.length() + 1 ); } } } return result; } /** * Returns the next substring that can be wrapped. */ private static String nextSubLine( String line, int startIndex ) { String result = line; int index = line.indexOf( ' ', startIndex ); if( index != -1 ) { result = line.substring( 0, index ); } return result; } /** * Returns the width of a given string in pixels. Line breaks are ignored. */ private static int getLineWidth( String line, Font font ) { return Math.round( getAvgCharWidth( font ) * line.length() ); } }