SiteExperts.com Logo Home | Community | Developer's Paradise | Jobs
User Groups | Site Tools | Site Information | Search

Inside Technique : A Cure for our Printing Blues : Reformatting with the print events

Fixing the PRE formatting issue proved to be a challenge. We needed a solution that checks and automatically rewraps the PRE contents when the page is printing. We came up with a solution that works in Internet Explorer 5.0 only by taking advantage of the onbeforeprint and onafterprint events. Immediately before a page is printed, the onbeforeprint event occurs allowing you to do custom formatting of the page. We use this event to enumerate all the PRE elements and break up the longer lines. After printing, we reload the page restoring the original layout.

This task is easier said than done. We needed to take advantage of some of the advanced DHTML features added in Internet Explorer 5.0. To manipulate the contents of the PRE element, we use the TextRange object (also in Internet Explorer 4.0). This object is a very powerful and flexible way to manipulate text. Using this object and the new TextRange features in IE 5.0, we analyze each individual line within the PRE element, calculate its width, and reformat the line if it is too long. In addition, to keep the formatting looking nice, we maintain the same indention (assuming the indentention is composed of spaces) from line to line.

Below is the commented script used to format the PRE sections. The interested part of this script is where we break up long lines of text. Our algorithm is very simple - we walk backwards from the end of the line wherever there is a space until the line is smaller than 600 pixels. Since this approach can create a script that won't run when copied and pasted, we do not run this algorithm over pages when viewed on the screen.

function CountSpace(str) {
  // Used to count how many characters each line should indent
  var sSpace = ""
  while (str.charAt(0)==" ") {
    sSpace += " "
    str = str.substring(1)
  }
  return sSpace
}

function doLineBreak() {
  // Loop over all the PRE elements
  for (var i=document.all.tags("PRE").length-1;i>=0; i--) {
    // Get a text range over an individual PRE element
    var tr = document.body.createTextRange()
    tr.moveToElementText(document.all.tags("PRE")[i])

    // Store a copy of the PRE element
    var sLine = tr.duplicate()

    // Find the first line break
    sLine.findText("\r")
 
    // Move the textrange over the first line
    sLine.setEndPoint("StartToStart",tr)

    // Check that you are still within the PRE element
    while ((sLine.compareEndPoints("EndToEnd",tr)<=0)  && (sLine.compareEndPoints("StartToEnd",tr)<0) ) {
      // Count indent
      var sSpace = CountSpace(sLine.text)
 
      // Line is too big - break up into multiple lines
      if (sLine.boundingWidth>600) {
        // Make copy of the line
        var sDup = sLine.duplicate()
     
        // Shrink the line by every block of text that has a space in it.
        // If no spaces are in a long-line, the line is clipped.
       while ((sLine.boundingWidth>600) && (sLine.compareEndPoints("EndToStart",tr)>0) && (sLine.text.lastIndexOf(" ")!=0)) {
          sLine.moveEnd("character",-(sLine.text.length - sLine.text.lastIndexOf(" ") ))

        // Make sure you did not back up before the line
        if ((sLine.compareEndPoints("EndToStart",tr)>0) && (sLine.text.lastIndexOf(" ")!=0)) {
          // Insert a line break to split the line up
          sLine.collapse(false)
          sLine.text = " _\r" + sSpace + "  "
          sLine.moveEnd("character",-1)
        } else
          // Moved before the line (no spaces) - just leave line as is
          sLine = sDup
      }
      // Go find the next line break and continue
      sLine.collapse(false)
      sLine.setEndPoint("EndToEnd",tr)
      tr.setEndPoint("StartToStart",sLine)
      sLine.findText("\r")
      sLine.setEndPoint("StartToStart",tr)
    }
  }
}


function doCleanup() {
  // Reload the page 
  // Done on a short timeout b/c we saw problems with 
  // immediate reloading the page (sometimes the page did not print)
  setTimeout("window.location.reload()",0)
}

window.onbeforeprint= doLineBreak  // Before Printing
window.onafterprint = doCleanup    // After Printing

We have included this script on all our article pages for Internet Explorer 5.0 users. While we have done our best to test this script, it is possible that we missed a boundary condition that could cause problems in IE5 when you try and print a page. If you experience any problems printing with Internet Explorer 5.0, please send us mail and include the URL of the page you tried to print.

This fix does not work for Netscape browsers. Netscape does not support media style sheets nor is there a way to access and modify the contents of any of the HTML elements in the document. Instead, we hope to fix this problem for all browsers when we finish our work on reformatting our backend article store.

Our plan is to convert all our articles to a XML. From this XML file, we will use XSL to generate different representations of the page (eg., print version, HTML 3.2 version, CSS enhanced, etc).

Conclusion

We believe the real value of this article is that it demonstrates a creative short-term solution to a tough problem. Instead of forcing you to wait months before you can reasonably print an article, we explored ways of shifting the formatting burden to the client. While not ideal, we feel this is a perfect short-term solution until we can finish the tedious reformatting of our pages.

Discuss and Rate this Article

Page 1:A Cure for our Printing Blues
Page 2:Reformatting with the print events