Excel列寬像素值計算方法詳解

字數不多,全是乾貨;
心血凝結,全網唯一;
如有幫助,萬望點贊;
未經許可,禁止轉載!

前兩年基於Apache POI做了個Excel轉Html的程序。爲了讓HTML中列寬的顯示尺寸精確吻合Excel原生顯示尺寸,做了大量的研究和試算,不同字號不同字體測試了N多種組合,最終完美還原了Excel列寬像素計算方法。今天有空,爲了知識不被遺忘,寫個博記錄一下,全網獨一份哦!

首先,要知道每個Excel文件都有一個缺省字體定義,包含了字體、字號信息。這個缺省字體是在Excel選項中指定的,當新建一個工作簿時就被作爲內置屬性在文件中固化下來。如下圖所示:
Excel文件的默認字體
該字體的寬度被作爲一個基本度量單位用於與列寬相關的計算。POI源碼中涉及像素、缺省列寬的計算是大多是錯誤或者說不完善的,其中最主要的一點就是其對Excel的缺省字體寬度沒有正確獲取,在3.x版本中(4.x沒看過)直接硬編碼了一個數字8(好像是)作爲字體寬度,且對單元格padding部分的處理不正確,因此後續相關的像素計算都得不到正確結果。我在做Excel轉Html的過程中,不得不自行研究計算,最終能生成像素級一致的HTML,效果非常好。

下面開始講解Excel是如何進行像素計算的。具體來說是這樣的:

  • 對Excel來說默認字體有一個缺省寬度,其定義是0-9這10個字符中最寬的字符的像素值。以下,我簡稱該值爲DFW(Default Font Width)。
  • Excel的列寬總是以DFW爲單位,而與當前列或單元格中的實際字體無關。當在Excel中設置列寬時,對話框中輸入的數字即是DFW的倍數,其含義是該列能顯示多少個缺省字體字符。下圖是缺省字體爲"宋體,48"時缺省列的寬度。

列寬數值是缺省字體寬度的倍數

  • 列寬的精確像素值由所能容納的字符總寬度、兩端的空白(padding)和1px的margin(網格線)組成,其中padding是DFW除以4後的向上取整值。整體計算步驟略微複雜,下面通過實例來講解。

首先,請把Excel的默認字體設置爲"宋體,48",然後新建一個文件,查看任意一列的列寬一定爲8.22(參見上圖)。這個8.22是怎麼來的呢,和列的像素寬度有什麼關係?敲黑板,Excel是按照下面的步驟計算列寬的:

  1. 48號的宋體,字符寬度是32像素(別問我是怎麼知道的,不信你量一下)。Excel默認列寬要求必須完整顯示8個字符,所以字符總寬度爲8*32=256px;
  2. padding = 向上取整(DFW/4) = 8px,兩邊總共16px,再加上1px的網格線,現在的寬度=256+16+1=273px。
  3. Excel規定每列的像素寬度必須是8的整數倍(爲了在滾動時提高渲染性能),故把273px向上取整爲8的整數倍,得到280px,這就是該列最終在屏幕上佔據的實際像素寬度,不信拿尺子量。

現在我們已經計算出默認列寬爲280像素,再來算一下究竟能顯示多少個缺省字符。很簡單,去掉第2步中的padding和margin共17px再除以DFW,即(280-17)/32 = 8.22,與Excel中實際看到的完全一致。這就是缺省列寬爲8.22的來歷。如果給Excel指定不同的默認字體,你會發現缺省列寬無規律地變來變去(但肯定大於8),這都是上述算法造成的結果。

在上述過程中,最核心的數值是DFW。它是怎麼得到的呢?換句話說,在Java中如何得到任意一種字體的0-9數字的最大寬度?我試過stackoverflow或apache論壇等各種渠道找到的計算方法,比如用java.awt.font.TextLayout,都無法得到完全準確的結果,有時候正確,有時候會差一兩個像素。最終我是用了一個很low很粗暴的方法在有限條件下解決,這裏就不說了。如果看官有辦法精確計算字體寬度,請不吝賜教!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章