<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Luke Lutman: Copy-Fitted Text</title>
    <link>http://lukelutman.com/articles/2006/10/15/copy-fitted-text</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Creative + Code</description>
    <item>
      <title>Copy-Fitted Text</title>
      <description>&lt;p&gt;I&amp;#8217;m nerdy enough that I write code just for fun, if the problem&amp;#8217;s interesting enough &amp;#8211; like this one: What&amp;#8217;s the best way to find an arbitrary value in a large range of numbers &amp;#8211; say the &lt;a href="/files/copyfit.html"&gt;maximum &lt;code&gt;font-size&lt;/code&gt; for a line of text without wrapping&lt;/a&gt;?&lt;/p&gt;

&lt;h2&gt;Brute Force&lt;/h2&gt;

&lt;p&gt;The obvious, brute force approach? Keep adding 1 to the font-size until it wraps, then subtract 1, and voila! If the line of text is short, and the window is very wide, we&amp;#8217;ll loop a few hundred times. What&amp;#8217;s that? You want to adjust the text when the window is resized? That&amp;#8217;s a few hundred more. You want to copy-fit two elements? Double. You want another one? Yes! Are you through? Not even close, bud!!&lt;/p&gt;

&lt;p&gt;Obviously, that&amp;#8217;s not going to work.&lt;/p&gt;

&lt;h2&gt;A Better Approach&lt;/h2&gt;

&lt;p&gt;What if I said we can find a magic number between 0 and 100,000 in less than 20 iterations &amp;#8211; doesn&amp;#8217;t matter what the number is, guaranteed? Yep, with a little brain-bending recursive goodness, we&amp;#8217;ll have that sucker in no time.&lt;/p&gt;

&lt;pre&gt;
function find_max( min, max, truth ) {
    var r = max - min;
    var n = min + Math.ceil(r / 2);
    if (r / 2 &lt; 1) {
        return truth(max) ? max : min;
    } else if(truth(n)) {
        return arguments.callee(n, max, truth);
    } else {
        return arguments.callee(min, n, truth);
    }
}

function truth (i) {
    return i &lt;= 66666;
}

find_max( 0, 100000, truth );
&lt;/pre&gt;

&lt;h2&gt;How It Works&lt;/h2&gt;

&lt;p&gt;With a &lt;code&gt;min&lt;/code&gt; of 0 and a &lt;code&gt;max&lt;/code&gt; of 100,000, we&amp;#8217;ll ask: does 50,000 work? If it does, then the number is between 50,000 and 100,000. If not, it&amp;#8217;s between 0 and 50,000. Either way, we&amp;#8217;ve just eliminated half the possibilities. Not bad for a single iteration&amp;#8230;&lt;/p&gt;

&lt;p&gt;In the (arbitrary) case above, 50,000 does work. So, on the next iteration, we have a &lt;code&gt;min&lt;/code&gt; of 50,000 and a &lt;code&gt;max&lt;/code&gt; of 100,000. Does 75,000 work? Nope. So, the magic number is between 50,000 and 75,000. And so on and so forth until half the difference between &lt;code&gt;min&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt; is less than one &amp;#8211; at which point  it must be one or the other. One more test and bingo! We&amp;#8217;re in business.&lt;/p&gt;

&lt;h2&gt;Making It Work&lt;/h2&gt;

&lt;p&gt;To copy-fit text, we need to do three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add some css to prevent the text from wrapping as the font-size increases, and give us the actual width of the text&lt;/li&gt;
&lt;li&gt;replace the arbitrary conditions in truth with code to change the font-size, and check if it&amp;#8217;s too wide.&lt;/li&gt;
&lt;li&gt;take the final result and use it as the copy-fitted font-size.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Markup&lt;/h2&gt;

&lt;pre&gt;
&amp;lt;h1 id="fitted"&gt;This text is copy-fitted.&amp;lt;/h1&gt;
&lt;/pre&gt;

&lt;h2&gt;CSS&lt;/h2&gt;

&lt;pre&gt;
#fitted {
    display: inline;
    white-space: nowrap; }
&lt;/pre&gt;

&lt;h2&gt;Script&lt;/h2&gt;

&lt;pre&gt;
function find_max( min, max, truth ) {
    var r = max - min;
    var n = min + Math.ceil(r / 2);
    if (r / 2 &lt; 1) {
        return truth(max) ? max : min;
    } else if(truth(n)) {
        return arguments.callee(n, max, truth);
    } else {
        return arguments.callee(min, n, truth);
    }
}

function copyFit(element, width) {
    element.style.fontSize = find_max(0, 500, function(i){
        element.style.fontSize = i + 'px';
        return element.offsetWidth &lt;= width;
    }) + 'px';
}

window.onload = window.onresize = function() {
    var c = document.body;
    var e = document.getElementById('fitted');
    e.style.fontSize = 0;               
    copyFit( e, c.offsetWidth );                
}
&lt;/pre&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;And &lt;a href="files/copyfit.html"&gt;there we go&lt;/a&gt;. A line of text that scales up or down to match the width of the body element. You could also use this in ActionScript, if you&amp;#8217;re so inclined to scale text to fit in a TextField.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; this is a proof of concept, so it&amp;#8217;ll need some bullet-proofing and cross-browser testing before being used in the wild.&lt;/p&gt;

&lt;h2&gt;Bonus&lt;/h2&gt;

&lt;p&gt;I haven&amp;#8217;t found a use for it yet, but here&amp;#8217;s a counter-part to &lt;code&gt;find_max&lt;/code&gt;: &lt;/p&gt;

&lt;pre&gt;
function find_min( min, max, truth ){   
    var r = max - min;
    var n = max - Math.ceil(r / 2);
    if (r / 2 &lt; 1) {
        return truth(max) ? max : min;
    } else if(truth(n)) {
        return arguments.callee(min, n, truth);
    } else {
        return arguments.callee(n, max, truth);
    }           
}
&lt;/pre&gt;</description>
      <pubDate>Sun, 15 Oct 2006 09:30:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:27b856ff-df4d-4003-9c08-df9d36073e1f</guid>
      <author>Luke Lutman</author>
      <link>http://lukelutman.com/articles/2006/10/15/copy-fitted-text</link>
      <category>(X)HTML</category>
      <category>JavaScript</category>
      <category>ActionScript</category>
      <category>CSS</category>
      <enclosure type="text/html" url="http://lukelutman.com/files/copyfit.html" length="2226"/>
    </item>
  </channel>
</rss>
