Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/nwalsh/saxon/Table.java


1   // Verbatim.java - Saxon extensions supporting DocBook verbatim environments
2   
3   package com.nwalsh.saxon;
4   
5   import java.util.Hashtable;
6   import org.xml.sax.*;
7   import org.w3c.dom.*;
8   import javax.xml.transform.TransformerException;
9   import com.icl.saxon.Controller;
10  import com.icl.saxon.expr.*;
11  import com.icl.saxon.om.*;
12  import com.icl.saxon.pattern.*;
13  import com.icl.saxon.Context;
14  import com.icl.saxon.tree.*;
15  import com.icl.saxon.functions.Extensions;
16  
17  /**
18   * <p>Saxon extensions supporting Tables</p>
19   *
20   *
21   * <p>Copyright (C) 2000 Norman Walsh.</p>
22   *
23   * <p>This class provides a
24   * <a href="http://users.iclway.co.uk/mhkay/saxon/">Saxon</a>
25   * implementation of some code to adjust CALS Tables to HTML
26   * Tables.</p>
27   *
28   * <p><b>Column Widths</b></p>
29   * <p>The <tt>adjustColumnWidths</tt> method takes a result tree
30   * fragment (assumed to contain the colgroup of an HTML Table)
31   * and returns the result tree fragment with the column widths
32   * adjusted to HTML terms.</p>
33   *
34   * <p><b>Convert Lengths</b></p>
35   * <p>The <tt>convertLength</tt> method takes a length specification
36   * of the form 9999.99xx (where "xx" is a unit) and returns that length
37   * as an integral number of pixels. For convenience, percentage lengths
38   * are returned unchanged.</p>
39   * <p>The recognized units are: inches (in), centimeters (cm),
40   * millimeters (mm), picas (pc, 1pc=12pt), points (pt), and pixels (px).
41   * A number with no units is assumed to be pixels.</p>
42   *
43   * <p><b>Change Log:</b></p>
44   * <dl>
45   * <dt>1.0</dt>
46   * <dd><p>Initial release.</p></dd>
47   * </dl>
48   *
49   * @author Norman Walsh
50   * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
51   *
52   *
53   */
54  public class Table {
55    /** The number of pixels per inch */
56    private static int pixelsPerInch = 96;
57  
58    /** The nominal table width (6in by default). */
59    private static int nominalWidth = 6 * pixelsPerInch;
60  
61    /** The default table width (100% by default). */
62    private static String tableWidth = "100%";
63  
64    /** Is this an FO stylesheet? */
65    private static boolean foStylesheet = false;
66  
67    /** The hash used to associate units with a length in pixels. */
68    protected static Hashtable unitHash = null;
69  
70    /**
71     * <p>Constructor for Verbatim</p>
72     *
73     * <p>All of the methods are static, so the constructor does nothing.</p>
74     */
75    public Table() {
76    }
77  
78    /** Initialize the internal hash table with proper values. */
79    protected static void initializeHash() {
80      unitHash = new Hashtable();
81      unitHash.put("in", new Float(pixelsPerInch));
82      unitHash.put("cm", new Float(pixelsPerInch / 2.54));
83      unitHash.put("mm", new Float(pixelsPerInch / 25.4));
84      unitHash.put("pc", new Float((pixelsPerInch / 72) * 12));
85      unitHash.put("pt", new Float(pixelsPerInch / 72));
86      unitHash.put("px", new Float(1));
87    }
88  
89    /** Set the pixels-per-inch value. Only positive values are legal. */
90    public static void setPixelsPerInch(int value) {
91      if (value > 0) {
92        pixelsPerInch = value;
93        initializeHash();
94      }
95    }
96  
97    /** Return the current pixels-per-inch value. */
98    public int getPixelsPerInch() {
99      return pixelsPerInch;
100   }
101 
102   /**
103    * <p>Convert a length specification to a number of pixels.</p>
104    *
105    * <p>The specified length should be of the form [+/-]999.99xx,
106    * where xx is a valid unit.</p>
107    */
108   public static int convertLength(String length) {
109     // The format of length should be 999.999xx
110     int sign = 1;
111     String digits = "";
112     String units = "";
113     char lench[] = length.toCharArray();
114     float flength = 0;
115     boolean done = false;
116     int pos = 0;
117     float factor = 1;
118     int pixels = 0;
119 
120     if (unitHash == null) {
121       initializeHash();
122     }
123 
124     if (lench[pos] == '+' || lench[pos] == '-') {
125       if (lench[pos] == '-') {
126   sign = -1;
127       }
128       pos++;
129     }
130 
131     while (!done) {
132       if (pos >= lench.length) {
133   done = true;
134       } else {
135   if ((lench[pos] > '9' || lench[pos] < '0') && lench[pos] != '.') {
136     done = true;
137     units = length.substring(pos);
138   } else {
139     digits += lench[pos++];
140   }
141       }
142     }
143 
144     try {
145       flength = Float.parseFloat(digits);
146     } catch (NumberFormatException e) {
147       System.out.println(digits + " is not a number; 1 used instead.");
148       flength = 1;
149     }
150 
151     Float f = null;
152 
153     if (!units.equals("")) {
154       f = (Float) unitHash.get(units);
155       if (f == null) {
156   System.out.println(units + " is not a known unit; 1 used instead.");
157   factor = 1;
158       } else {
159   factor = f.floatValue();
160       }
161     } else {
162       factor = 1;
163     }
164 
165     f = new Float(flength * factor);
166 
167     pixels = f.intValue() * sign;
168 
169     return pixels;
170   }
171 
172   /**
173    * <p>Find the string value of a stylesheet variable or parameter</p>
174    *
175    * <p>Returns the string value of <code>varName</code> in the current
176    * <code>context</code>. Returns the empty string if the variable is
177    * not defined.</p>
178    *
179    * @param context The current stylesheet context
180    * @param varName The name of the variable (without the dollar sign)
181    *
182    * @return The string value of the variable
183    */
184   protected static String getVariable(Context context, String varName) 
185     throws TransformerException {
186     Value variable = null;
187     String varString = null;
188 
189     try {
190       variable = Extensions.evaluate(context, "$" + varName);
191       varString = variable.asString();
192       return varString;
193     } catch (IllegalArgumentException e) {
194       System.out.println("Undefined variable: " + varName);
195       return "";
196     }
197   }
198 
199   /**
200    * <p>Setup the parameters associated with column width calculations</p>
201    *
202    * <p>This method queries the stylesheet for the variables
203    * associated with table column widths. It is called automatically before
204    * column widths are adjusted. The context is used to retrieve the values,
205    * this allows templates to redefine these variables.</p>
206    *
207    * <p>The following variables are queried. If the variables do not
208    * exist, builtin defaults will be used (but you may also get a bunch
209    * of messages from the Java interpreter).</p>
210    *
211    * <dl>
212    * <dt><code>nominal.table.width</code></dt>
213    * <dd>The "normal" width for tables. This must be an absolute length.</dd>
214    * <dt><code>table.width</code></dt>
215    * <dd>The width for tables. This may be either an absolute
216    * length or a percentage.</dd>
217    * </dl>
218    *
219    * @param context The current stylesheet context
220    *
221    */
222   private static void setupColumnWidths(Context context) {
223     // Hardcoded defaults
224     nominalWidth = 6 * pixelsPerInch;
225     tableWidth = "100%";
226 
227     String varString = null;
228 
229     try {
230       // Get the stylesheet type
231       varString = getVariable(context, "stylesheet.result.type");
232       foStylesheet = varString.equals("fo");
233 
234       // Get the nominal table width
235       varString = getVariable(context, "nominal.table.width");
236       nominalWidth = convertLength(varString);
237 
238       // Get the table width
239       varString = getVariable(context, "table.width");
240       tableWidth = varString;
241     } catch (TransformerException e) {
242       //nop, can't happen
243     }
244   }
245 
246   /**
247    * <p>Adjust column widths in an HTML table.</p>
248    *
249    * <p>The specification of column widths in CALS (a relative width
250    * plus an optional absolute width) are incompatible with HTML column
251    * widths. This method adjusts CALS column width specifiers in an
252    * attempt to produce equivalent HTML specifiers.</p>
253    *
254    * <p>In order for this method to work, the CALS width specifications
255    * should be placed in the "width" attribute of the &lt;col>s within
256    * a &lt;colgroup>. Then the colgroup result tree fragment is passed
257    * to this method.</p>
258    *
259    * <p>This method makes use of two parameters from the XSL stylesheet
260    * that calls it: <code>nominal.table.width</code> and
261    * <code>table.width</code>. The value of <code>nominal.table.width</code>
262    * must be an absolute distance. The value of <code>table.width</code>
263    * can be either absolute or relative.</p>
264    *
265    * <p>Presented with a mixture of relative and
266    * absolute lengths, the table width is used to calculate
267    * appropriate values. If the <code>table.width</code> is relative,
268    * the nominal width is used for this calculation.</p>
269    *
270    * <p>There are three possible combinations of values:</p>
271    *
272    * <ol>
273    * <li>There are no relative widths; in this case the absolute widths
274    * are used in the HTML table.</li>
275    * <li>There are no absolute widths; in this case the relative widths
276    * are used in the HTML table.</li>
277    * <li>There are a mixture of absolute and relative widths:
278    *   <ol>
279    *     <li>If the table width is absolute, all widths become absolute.</li>
280    *     <li>If the table width is relative, make all the widths absolute
281    *         relative to the nominal table width then turn them all
282    *         back into relative widths.</li>
283    *   </ol>
284    * </li>
285    * </ol>
286    *
287    * @param context The stylesheet context; supplied automatically by Saxon
288    * @param rtf The result tree fragment containing the colgroup.
289    *
290    * @return The result tree fragment containing the adjusted colgroup.
291    *
292    */
293   public static NodeSetValue adjustColumnWidths (Context context,
294              NodeSetValue rtf_ns) {
295 
296     FragmentValue rtf = (FragmentValue) rtf_ns;
297 
298     setupColumnWidths(context);
299 
300     try {
301       Controller controller = context.getController();
302       NamePool namePool = controller.getNamePool();
303 
304       ColumnScanEmitter csEmitter = new ColumnScanEmitter(namePool);
305       rtf.replay(csEmitter);
306 
307       int numColumns = csEmitter.columnCount();
308       String widths[] = csEmitter.columnWidths();
309 
310       float relTotal = 0;
311       float relParts[] = new float[numColumns];
312 
313       float absTotal = 0;
314       float absParts[] = new float[numColumns];
315 
316       for (int count = 0; count < numColumns; count++) {
317   String width = widths[count];
318 
319   int pos = width.indexOf("*");
320   if (pos >= 0) {
321     String relPart = width.substring(0, pos);
322     String absPart = width.substring(pos+1);
323 
324     try {
325       float rel = Float.parseFloat(relPart);
326       relTotal += rel;
327       relParts[count] = rel;
328     } catch (NumberFormatException e) {
329       System.out.println(relPart + " is not a valid relative unit.");
330     }
331 
332     int pixels = 0;
333     if (absPart != null && !absPart.equals("")) {
334       pixels = convertLength(absPart);
335     }
336 
337     absTotal += pixels;
338     absParts[count] = pixels;
339   } else {
340     relParts[count] = 0;
341 
342     int pixels = 0;
343     if (width != null && !width.equals("")) {
344       pixels = convertLength(width);
345     }
346 
347     absTotal += pixels;
348     absParts[count] = pixels;
349   }
350       }
351 
352       // Ok, now we have the relative widths and absolute widths in
353       // two parallel arrays.
354       //
355       // - If there are no relative widths, output the absolute widths
356       // - If there are no absolute widths, output the relative widths
357       // - If there are a mixture of relative and absolute widths,
358       //   - If the table width is absolute, turn these all into absolute
359       //     widths.
360       //   - If the table width is relative, turn these all into absolute
361       //     widths in the nominalWidth and then turn them back into
362       //     percentages.
363 
364       if (relTotal == 0) {
365   for (int count = 0; count < numColumns; count++) {
366     Float f = new Float(absParts[count]);
367     if (foStylesheet) {
368       int pixels = f.intValue();
369       float inches = (float) pixels / pixelsPerInch;
370       widths[count] = inches + "in";
371     } else {
372       widths[count] = Integer.toString(f.intValue());
373     }
374   }
375       } else if (absTotal == 0) {
376   for (int count = 0; count < numColumns; count++) {
377     float rel = relParts[count] / relTotal * 100;
378     Float f = new Float(rel);
379     widths[count] = Integer.toString(f.intValue()) + "%";
380   }
381       } else {
382   int pixelWidth = nominalWidth;
383 
384   if (tableWidth.indexOf("%") <= 0) {
385     pixelWidth = convertLength(tableWidth);
386   }
387 
388   if (pixelWidth <= absTotal) {
389     System.out.println("Table is wider than table width.");
390   } else {
391     pixelWidth -= absTotal;
392   }
393 
394   absTotal = 0;
395   for (int count = 0; count < numColumns; count++) {
396     float rel = relParts[count] / relTotal * pixelWidth;
397     relParts[count] = rel + absParts[count];
398     absTotal += rel + absParts[count];
399   }
400 
401   if (tableWidth.indexOf("%") <= 0) {
402     for (int count = 0; count < numColumns; count++) {
403       Float f = new Float(relParts[count]);
404       if (foStylesheet) {
405         int pixels = f.intValue();
406         float inches = (float) pixels / pixelsPerInch;
407         widths[count] = inches + "in";
408       } else {
409         widths[count] = Integer.toString(f.intValue());
410       }
411     }
412   } else {
413     for (int count = 0; count < numColumns; count++) {
414       float rel = relParts[count] / absTotal * 100;
415       Float f = new Float(rel);
416       widths[count] = Integer.toString(f.intValue()) + "%";
417     }
418   }
419       }
420 
421       ColumnUpdateEmitter cuEmitter = new ColumnUpdateEmitter(controller,
422                     namePool,
423                     widths);
424 
425       rtf.replay(cuEmitter);
426       return cuEmitter.getResultTreeFragment();
427     } catch (TransformerException e) {
428       // This "can't" happen.
429       System.out.println("Transformer Exception in adjustColumnWidths");
430       return rtf;
431     }
432   }
433 }