piaoyi's blog - 站在煩惱裡仰望幸福

http://www.javaworld.com.tw/roller/piaoyi/date/20080416 Wednesday April 16, 2008

HTML列印時,在分頁後如何重覆印出表格的表頭表尾

目錄

摘要

我們在HTML中常使用Table來呈現表格的資料,有時Table很大,如果利用Browser列印時,Table會被切成二頁,此時第二頁的Table由於沒有表頭資料,時常會不知哪一欄代表的意義是什麼,不過其實 CSS 是有定義與支援的,也就是利用 html tag : THEAD, TBODY, TFOOT這三個 tag 來 group 一個表格中的 header, body, footer 三塊,再利用 CSS 的設定,就可以在列印時,會在 Table 被切頁時,在第二頁會自動加入 Table header 與 footer。

See also : CSS對HTML列印的支援

設定方式

使用 THEAD, TBODY, TFOOT 來分類 Table 中的元素

HTML Table 中,底下第一層為TR,再來為TD,而TR代表一列,所以在分類中是以TR為主,看你要多少個TR為表頭,表身,或表尾都可以。
一般而言,表頭只會設一列,表尾則不常使用,表身則是主要表格內的內容,會有多列,而只要在寫TR前,先用THEAD,TBODY,TFOOT包裹即可。

THEAD, TBODY, TFOOT
<TABLE>
<THEAD>
  <TR>
    <TH noWrap>表頭 COL 1</TH>
    <TH noWrap>表頭 COL 2</TH>
    <TH>表頭 COL 3</TH>
    <TH noWrap>表頭 COL 4</TH>
    <TH>表頭 COL 5</TH>
    <TH noWrap>表頭 COL 6</TH>
    <TH noWrap>表頭 COL 7</TH>
  </TR>
</THEAD>
<TBODY>
  <TR>
    <TD>表身 ROW 1 COL 1</TD>
    <TD>表身 ROW 1 COL 2</TD>
    <TD>表身 ROW 1 COL 3</TD>
    <TD>表身 ROW 1 COL 4</TD>
    <TD>表身 ROW 1 COL 5</TD>
    <TD>表身 ROW 1 COL 6</TD>
    <TD>表身 ROW 1 COL 7</TD>
  </TR>
  <TR>
    <TD>表身 ROW 2 COL 1</TD>
    <TD>表身 ROW 2 COL 2</TD>
    <TD>表身 ROW 2 COL 3</TD>
    <TD>表身 ROW 2 COL 4</TD>
    <TD>表身 ROW 2 COL 5</TD>
    <TD>表身 ROW 2 COL 6</TD>
    <TD>表身 ROW 2 COL 7</TD>
  </TR>
  <TR>
    <TD>表身 ROW 3 COL 1</TD>
    <TD>表身 ROW 3 COL 2</TD>
    <TD>表身 ROW 3 COL 3</TD>
    <TD>表身 ROW 3 COL 4</TD>
    <TD>表身 ROW 3 COL 5</TD>
    <TD>表身 ROW 3 COL 6</TD>
    <TD>表身 ROW 3 COL 7</TD>
  </TR>
  <TR>
    <TD>表身 ROW 4 COL 1</TD>
    <TD>表身 ROW 4 COL 2</TD>
    <TD>表身 ROW 4 COL 3</TD>
    <TD>表身 ROW 4 COL 4</TD>
    <TD>表身 ROW 4 COL 5</TD>
    <TD>表身 ROW 4 COL 6</TD>
    <TD>表身 ROW 4 COL 7</TD>
  </TR>
  <TR>
    <TD>表身 ROW 5 COL 1</TD>
    <TD>表身 ROW 5 COL 2</TD>
    <TD>表身 ROW 5 COL 3</TD>
    <TD>表身 ROW 5 COL 4</TD>
    <TD>表身 ROW 5 COL 5</TD>
    <TD>表身 ROW 5 COL 6</TD>
    <TD>表身 ROW 5 COL 7</TD>
  </TR>
  <TR>
    <TD>表身 ROW 6 COL 1</TD>
    <TD>表身 ROW 6 COL 2</TD>
    <TD>表身 ROW 6 COL 3</TD>
    <TD>表身 ROW 6 COL 4</TD>
    <TD>表身 ROW 6 COL 5</TD>
    <TD>表身 ROW 6 COL 6</TD>
    <TD>表身 ROW 6 COL 7</TD>
  </TR>
  <TR>
    <TD>表身 ROW 7 COL 1</TD>
    <TD>表身 ROW 7 COL 2</TD>
    <TD>表身 ROW 7 COL 3</TD>
    <TD>表身 ROW 7 COL 4</TD>
    <TD>表身 ROW 7 COL 5</TD>
    <TD>表身 ROW 7 COL 6</TD>
    <TD>表身 ROW 7 COL 7</TD>
  </TR>
  <TR>
    <TD>表身 ROW 8 COL 1</TD>
    <TD>表身 ROW 8 COL 2</TD>
    <TD>表身 ROW 8 COL 3</TD>
    <TD>表身 ROW 8 COL 4</TD>
    <TD>表身 ROW 8 COL 5</TD>
    <TD>表身 ROW 8 COL 6</TD>
    <TD>表身 ROW 8 COL 7</TD>
  </TR>
</TBODY>
<TFOOT>
  <TR>
    <TH noWrap>表尾 COL 1</TH>
    <TH noWrap>表尾 COL 2</TH>
    <TH>表尾 COL 3</TH>
    <TH noWrap>表尾 COL 4</TH>
    <TH>表尾 COL 5</TH>
    <TH noWrap>表尾 COL 6</TH>
    <TH noWrap>表尾 COL 7</TH>
  </TR>
</TFOOT>
</TABLE>

使用 CSS 來設定列印時,重覆印出Table header 與 footer

使用THEAD, TBODY, TFOOT 分類後,對 Browser html 輸出並不會有影響,和沒有加的效果是一樣的,也就是Table就是一個,如果超過一頁則是用Scrollbar來拉,而列印時亦同,也沒有差異。
如果你希望列印時,當表格被分在二頁時,第二頁的表格會重覆印出表頭表尾,則你在該網頁中設定CSS來通知Browser列印時的處理。

與html中加入CSS設定
<HTML>
<HEAD>
<STYLE>
 THEAD {display: table-header-group;}
 TFOOT {display: table-footer-group;}
</STYLE>
</HEAD>
<BODY>
<table>
.......
</table>
</BODY>
</HTML>

原始網頁呈現

表頭 COL 1 表頭 COL 2 表頭 COL 3 表頭 COL 4 表頭 COL 5 表頭 COL 6 表頭 COL 7 表頭 COL 8
表身 ROW 1 COL 1 表身 ROW 1 COL 2 表身 ROW 1 COL 3 表身 ROW 1 COL 4 表身 ROW 1 COL 5 表身 ROW 1 COL 6 表身 ROW 1 COL 7 表身 ROW 1 COL 8
表身 ROW 2 COL 1 表身 ROW 2 COL 2 表身 ROW 2 COL 3 表身 ROW 2 COL 4 表身 ROW 2 COL 5 表身 ROW 2 COL 6 表身 ROW 2 COL 7 表身 ROW 2 COL 8
表身 ROW 3 COL 1 表身 ROW 3 COL 2 表身 ROW 3 COL 3 表身 ROW 3 COL 4 表身 ROW 3 COL 5 表身 ROW 3 COL 6 表身 ROW 3 COL 7 表身 ROW 3 COL 8
表身 ROW 4 COL 1 表身 ROW 4 COL 2 表身 ROW 4 COL 3 表身 ROW 4 COL 4 表身 ROW 4 COL 5 表身 ROW 4 COL 6 表身 ROW 4 COL 7 表身 ROW 4 COL 8
表身 ROW 5 COL 1 表身 ROW 5 COL 2 表身 ROW 5 COL 3 表身 ROW 5 COL 4 表身 ROW 5 COL 5 表身 ROW 5 COL 6 表身 ROW 5 COL 7 表身 ROW 5 COL 8
表身 ROW 6 COL 1 表身 ROW 6 COL 2 表身 ROW 6 COL 3 表身 ROW 6 COL 4 表身 ROW 6 COL 5 表身 ROW 6 COL 6 表身 ROW 6 COL 7 表身 ROW 6 COL 8
表身 ROW 7 COL 1 表身 ROW 7 COL 2 表身 ROW 7 COL 3 表身 ROW 7 COL 4 表身 ROW 7 COL 5 表身 ROW 7 COL 6 表身 ROW 7 COL 7 表身 ROW 7 COL 8
表身 ROW 8 COL 1 表身 ROW 8 COL 2 表身 ROW 8 COL 3 表身 ROW 8 COL 4 表身 ROW 8 COL 5 表身 ROW 8 COL 6 表身 ROW 8 COL 7 表身 ROW 8 COL 8
表尾 COL 1 表尾 COL 2 表尾 COL 3 表尾 COL 4 表尾 COL 5 表尾 COL 6 表尾 COL 7 表尾 COL 8

列印時的效果

假設在第 4 row 剛好分頁了。


Page 1

表頭 COL 1 表頭 COL 2 表頭 COL 3 表頭 COL 4 表頭 COL 5 表頭 COL 6 表頭 COL 7 表頭 COL 8
表身 ROW 1 COL 1 表身 ROW 1 COL 2 表身 ROW 1 COL 3 表身 ROW 1 COL 4 表身 ROW 1 COL 5 表身 ROW 1 COL 6 表身 ROW 1 COL 7 表身 ROW 1 COL 8
表身 ROW 2 COL 1 表身 ROW 2 COL 2 表身 ROW 2 COL 3 表身 ROW 2 COL 4 表身 ROW 2 COL 5 表身 ROW 2 COL 6 表身 ROW 2 COL 7 表身 ROW 2 COL 8
表身 ROW 3 COL 1 表身 ROW 3 COL 2 表身 ROW 3 COL 3 表身 ROW 3 COL 4 表身 ROW 3 COL 5 表身 ROW 3 COL 6 表身 ROW 3 COL 7 表身 ROW 3 COL 8
表身 ROW 4 COL 1 表身 ROW 4 COL 2 表身 ROW 4 COL 3 表身 ROW 4 COL 4 表身 ROW 4 COL 5 表身 ROW 4 COL 6 表身 ROW 4 COL 7 表身 ROW 4 COL 8
表尾 COL 1 表尾 COL 2 表尾 COL 3 表尾 COL 4 表尾 COL 5 表尾 COL 6 表尾 COL 7 表尾 COL 8

Page 2

表頭 COL 1 表頭 COL 2 表頭 COL 3 表頭 COL 4 表頭 COL 5 表頭 COL 6 表頭 COL 7 表頭 COL 8
表身 ROW 5 COL 1 表身 ROW 5 COL 2 表身 ROW 5 COL 3 表身 ROW 5 COL 4 表身 ROW 5 COL 5 表身 ROW 5 COL 6 表身 ROW 5 COL 7 表身 ROW 5 COL 8
表身 ROW 6 COL 1 表身 ROW 6 COL 2 表身 ROW 6 COL 3 表身 ROW 6 COL 4 表身 ROW 6 COL 5 表身 ROW 6 COL 6 表身 ROW 6 COL 7 表身 ROW 6 COL 8
表身 ROW 7 COL 1 表身 ROW 7 COL 2 表身 ROW 7 COL 3 表身 ROW 7 COL 4 表身 ROW 7 COL 5 表身 ROW 7 COL 6 表身 ROW 7 COL 7 表身 ROW 7 COL 8
表身 ROW 8 COL 1 表身 ROW 8 COL 2 表身 ROW 8 COL 3 表身 ROW 8 COL 4 表身 ROW 8 COL 5 表身 ROW 8 COL 6 表身 ROW 8 COL 7 表身 ROW 8 COL 8
表尾 COL 1 表尾 COL 2 表尾 COL 3 表尾 COL 4 表尾 COL 5 表尾 COL 6 表尾 COL 7 表尾 COL 8

Sample download

  1. Download [table_header_footer_sample.zip]
  2. 使用Browser 開啟 table_header_footer_sample.html,再點Browser 的[預覽列印],即可看到效果。

注意事項

  1. IE在表格分頁時,不會自動控制何處該切斷,也就是IE列印可能會把一個TD裡的內容截成二半,一半在上頁,一半在下頁,這時如果又有重覆押表頭,在第下頁看起來時會覺的有點奇怪,不過在 FireFox 與 Opera 則會以一個 TR 為切頁單位,輸出的感覺就會比較正常。因此,如果用 IE 列印,則可能需要自己用 javascript 計算,該在何處的 TR 加上 page-break-before 註記來完成以 TR 為單位的換頁。


CSS對HTML列印的支援

目錄

摘要

在此截錄一些CSS用在控制列印時的語法,但是並不是所有的Browser都支援。

@page 控制一頁的長,寬,邊界,直印,橫印等..

Page size: the 'size' property
'size' 
      Value:       <length>{1,2} | auto | portrait | landscape | inherit  
      Initial:     auto  
      Applies to:  the page context  
      Inherited:   N/A  
      Percentages: N/A  
      Media:       visual, paged
@page sample
@page { size 8.5in 11in; margin: 2cm }

@page {
  size: auto;   /* auto is the initial value */
  margin: 10%;
}

@page :left {
  margin-left: 4cm;
  margin-right: 3cm;
}

@page :right {
  margin-left: 3cm;
  margin-right: 4cm;
}

@page { margin: 2cm } /* All margins set to 2cm */

@page :first {
  margin-top: 10cm    /* Top margin on first page 10cm */
}

Reference : http://www.w3.org/TR/REC-CSS2/page.html

Page Break ,換頁控制

page break 是用來告訴 browser,在什麼情形下應該換頁,與應該不換頁,但這不是所有的Browser都能正確支援。

Reference : http://www.w3.org/TR/REC-CSS2/page.html#page-breaks

page-break-before

這個CSS代表在 Rander 某個 Tag 前,先換一頁。
這個屬性在IE 只支援 always 與 avoid 這二種屬性值,而且其中 avoid 是以空白代表。
這個屬性通常沒什麼用,除非我們明確的知道我要在哪要分頁,或我需要在哪要有切頁,這樣在我們產生HTML時,就可以在想要分頁的地方加入此設定。
例如我們可以自己用 javascript 計算每個 html 元素中的大小與位置,好決定在什麼時候要切頁,主要是用來在 Table 與 IMG 不要被截斷或可以截斷在好一點的位置。

page-break-before property
Value:   auto | always | avoid | left | right | inherit  
Initial:   auto  
Applies to:   block-level elements  
Inherited:   no  
Percentages:   N/A  
Media:   visual, paged

page-break-after

這個CSS代表在 Rander 某個 Tag 後,就換一頁。
這個屬性在IE 只支援 always 與 avoid 這二種屬性值,而且其中 avoid 是以空白代表。
這個屬性通常沒什麼用,除非我們明確的知道我要在哪要分頁,或我需要在哪要有切頁,這樣在我們產生HTML時,就可以在想要分頁的地方加入此設定。
例如我們可以自己用 javascript 計算每個 html 元素中的大小與位置,好決定在什麼時候要切頁,主要是用來在 Table 與 IMG 不要被截斷或可以截斷在好一點的位置。

page-break-after property
Value:   auto | always | avoid | left | right | inherit  
Initial:   auto  
Applies to:   block-level elements  
Inherited:   no  
Percentages:   N/A  
Media:   visual, paged

page-break-inside

這個屬性代表著,被 Apply 的 Tag 中不可以被切頁,而其實這個屬性最好用,因為我們通常都是希望某些元素在列印時不要被切斷,如 Table 中的 TR, TD ,IMG等,所以只要把這個 CSS apply 到 TR ,這樣就不會有 Table 被切頁時,會有一部份 TD 內容在上頁,一部份 TD 內容在下頁。
不幸的是,這個 CSS 支援的 Browser 太少了,只有一個Broser Opera有支援,而偏偏 IE 並不支援,或許可以看以後會不會支援而再來加以應用。所以目前只可以利用 javascript 自己計算位置在適當的位置加入 page-break-before or after。

page-break-inside property
Value:   avoid | auto | inherit  
Initial:   auto  
Applies to:   block-level elements  
Inherited:   yes  
Percentages:   N/A  
Media:   visual, paged

page-break value description

auto Neither force nor forbid a page break before (after, inside) the generated box.
always Always force a page break before (after) the generated box.
avoid Avoid a page break before (after, inside) the generated box.
left Force one or two page breaks before (after) the generated box so that the next page is formatted as a left page.
right Force one or two page breaks before (after) the generated box so that the next page is formatted as a right page.

page break sample

page-break-inside property
<STYLE>
<!-- 套用所有的 TR 中強迫不要被切頁-->
 TR { page-break-inside:avoid;} </STYLE>
</STYLE>



<!-- 或是只套用在某一個位置的Tag上-->
<!-- 強迫在印這一個row前,先換頁-->
<TR style="page-break-before:always">
    <TD>健康認知指導 (A) </TD>
    <TD >食道吻合手術前後須知 </TD>
    <TD >全不知</TD>
    <TD >2006/08/30 </TD>
    <TD >ssss</TD>
    <TD >示教 <BR>其他 :ffdd <BR></TD>
    <TD>家屬</TD>
</TR>

各家瀏覽器的支援程度

Reference : http://www.westciv.com/style_master/academy/browser_support/printing.html


http://www.javaworld.com.tw/roller/piaoyi/date/20080118 Friday January 18, 2008

淺談 XML Entity 與 Character escape 的關係

目錄

摘要

Character Escape - 大家都知道這是什麼意思,
但你們知道為什麼它會是 &lt; 代表 '<' ,&gt; 代表 '>' 嗎?


所以我這篇文章就是說明它的原理與定義。

XML Entity

XML Entity,是一個參照實體。

什麼是參照實體,丫咧,很抽象對吧;六年前我在看XML規格書時,也是霧煞煞,一堆機車的定義與機車的英文語法,讓人生不如 死,所以在這,我將用程式的角度來解釋它,會比較容易理解。

XML 中的 Entity ,以程式的角度來看,它就是一個 變數,又或者應該說它是一個 常數
因此,我們會在 XML 中定義一堆 Entity (常數),就是為了在後來的 XML 文件中,可以被取代,而減少文件中一堆多餘或重覆的資料。

讓我們來看一下Entity 的宣告方式,得知,
Entity 分 Internal Entities 與 External Entities 二種。

Internal Entities

直接在文件內容宣告 Entities.
例:

<!ENTITY vendor "piaoyi's blog">
<!ENTITY vendorAddr "250, Kuo Kuang Rd., Taichung 402, Taiwan R.O.C">

以 Java 角度來看,就會是:

public class ABean{
public static final String vendor = "piaoyi's blog";
public static final String vendorAddr = "250, Kuo Kuang Rd., Taichung 402, Taiwan R.O.C";
}

External Entities

在文件中引入外部定義的 Entities.
例:

<!ENTITY PiaoyiCopyright SYSTEM "http://www.javaworld.com.tw/roller/piaoyi/resource/w3c/entities.dtd">

Entity Reference

當我們宣告了一堆常數後,一定就是以後會用的到,那我們該怎麼用呢?
這時,該用法就會是此語言上之語法定義了。
所以,

以 Java 角度:

System.out.println("廠商:"+ABean.vendor);
System.out.println("住址:"+ABean.vendorAddr);

所以 Java 語法是直接引用此變數。

以 JSP 角度:

//忘了EL是不是一定要使用 getter,所以我假設 ABean 有 vendor getter,且有一個 instance : abeanObject。
${abeanObject.vendor}

所以 JSP EL 語法是以 '${' 變數名 '}' 來引用。

以 JSF 角度:

//忘了EL是不是一定要使用 getter,所以我假設 ABean 有 vendor getter,且有一個 instance : abeanObject。
#{abeanObject.vendor}

所以 JSF EL 語法是以 '#{' 變數名 '}' 來引用。

以 XML 角度:

&vendor;

由以上所舉的例子可知
XML 是以 '&' 變數名 ';' 來引用。
因此 '&' 符號是用來代表將引用一個 Entity 的 keyword,所以XML Parser 一遇到 '&', 就會認定是一個 Entity Reference,且會開始尋找結束符號 ';', 再將這二個筏號中間的字元當做變數名(Entity-Name)去尋找此 Entity 定義。所以如果此時你沒有結束符號';'出現,就會被認定為解析錯誤,而丟出 Exception。

Appendix:XML Entity Reference 語法定義

Entity Reference
[67] Reference ::= EntityRef | CharRef [68] EntityRef ::= '&' Name ';' [69] PEReference ::= '%' Name ';'

Predefined Entities

所以,為了XML 語法中的保留字,W3C 預先就定義了一些基本的 Entities

<!ENTITY lt   "&#38;#60;">
<!ENTITY gt   "&#62;">
<!ENTITY amp  "&#38;#38;">
<!ENTITY apos "&#39;">
<!ENTITY quot "&#34;">

結論

Escape XML 字元時,是一定必須是以 '&' 開頭,與 ';' 結尾,這樣才會是一個完整的 Escape;
而這個 Escape ,是透過 XML Entity 來達成的。

範例

entityTest.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [ <!ELEMENT root (item+)>
<!ELEMENT item (#PCDATA)>
<!ENTITY vendor "piaoyi's blog">
<!ENTITY vendorAddr "250, Kuo Kuang Rd., Taichung 402, Taiwan R.O.C">
]>
<root>
<item>1:vendor</item>
<item>2:&vendor;</item>
<item>3:vendorAddr</item>
<item>4:&vendorAddr;</item>
</root>

使用 IE 開啟結果:

Reference

http://www.w3.org/TR/xml/