解決依賴內嵌字體才能正常顯示的電子書亂碼問題 – 書伴

有一位名為“行云”的小伙伴留言反饋了一個問題,說他在網上找到一本 AZW3 格式的電子書,但是它使用了特殊的字體,只要轉換成其它格式就會亂碼,并且也不能正常復制字體,問如何解除這種限制。

一、問題表現

簡單實測了一下樣本電子書,發現在 Kindle 設備或 Calibre 內置閱讀器中閱讀一切正常:

但是在使用 Calibre 將其轉換成 MOBI 格式后,發現內容中大部分文字都變成了“亂碼”:

用 KindleUnpack 把樣本電子書拆解成源碼后,發現里面實際的文本內容確實是“亂碼”:

另外,還在源碼的 Fonts 文件夾中發現了一枚字體文件,在 CSS 文件中也可以看到強制指定該字體的相關屬性。經測試,電子書中的內容一旦應用該字體,內容就會變得正??潭?,否則就呈亂碼狀。

這看似是個很奇怪的問題,但是并不是太難解決,下面我們先來分析一下到底是怎么回事。

二、問題分析

一本 Kindle 電子書想要被正常閱讀,需要遵循亞馬遜為其制定的實現標準,這些實現標準都是公開的透明的(你可隨時可以通過亞馬遜持續維護著的《Kindle 電子書發布指南》來了解相關信息),因此不可能脫離這個實現標準對電子書做什么額外操作,除非是利用實現標準中所具備的特性搞一些障眼法。

這位小伙伴所提供的樣本電子書便利用了實現標準的一項本應是提升閱讀體驗的特性,有(惡)意地對電子書內容做了混淆,使得電子書必須依賴字體才能正常顯示,這一特性就是“嵌入字體”。

正常情況下“字符”與字體中的“字形”應該是一一對應的。如果一個字符是“人”,那么應用字體后的字形看起來也應該是“人”。而該樣本電子書卻沒這么做。假設有一句話是“人人為我,我為人人”,它就會將其故意寫成“????,????”,然后再通過修改字體文件,將里面的字符和字形的故意錯誤映射,如字符“?”映射到“人”字形、字符“?”映射到“為”字形等。這樣你就只能依賴這個字體文件才能看到那句話的正確顯示“人人為我,我為人人”,但實際文本的字符卻是“????,????”。

用字體編輯軟件 FontForge 打開在源代碼中的字體,就可以看到很多字符與字形的錯亂映射:

▲ 每個方框上方的小字是“字符”,下方的大字是與字符相對應的“字形”

在上圖中,按從左到右、從上到下的順序,可以看到“??????????”這十個字符,其對應字形卻是“提場展因大世意事沒年”。這就是為何電子書內容是亂碼,應用字體后卻是正常的原因。

該樣本電子書正是挑選了一些常用的高頻字符,將它們的字形根據制作者自己制定的規則逐一映射到“亂碼”字符上,然后再根據這個規則反向把電子書中的正常字符替換成“亂碼”字符。這樣,通過把字體嵌入到電子書中,并在 CSS 中為“亂碼”內容指定該字體,就可以讓亂碼內容正常顯示了。當時當你得到這樣一本經過處理的電子書后,卻無法對它進行編輯和格式轉換,更不能正常使用標注、查詞等功能。

三、解決方法

知道了問題所在,解決方法就顯而易見了,我們只需要將亂碼字符替換成他所對應字形的正確字符即可。大體思路為:先整理出字體文件中所有映射正常字形的亂碼字符,然后把這些亂碼字符所映射的字形抄下來(也就是將其轉化成字符),并使其與亂碼字符逐一配對,制成替換規則,最后用 Calibre 轉換功能中的“查找替換”功能把亂碼字符替換成正常字符。下面就以樣本電子書文件為例說一下具體步驟。

1、確定字符范圍

首先用 FontForge(也可使用其它字體編輯軟件)查看樣本電子書所使用的字體文件,借助 Unicode 十六進制編碼確認錯誤映射字形的亂碼字符范圍。不同字體文件其范圍會有所不同,個數可能比較多也可能比較少,分布可能比較集中也可能比較分散,但是只要是字符和字形不匹配就是需要篩選出來的。

比如在本例中,字符的分布比較集中,所以可以很方便的確定它們的范圍。如上圖所示,第一個出現錯誤映射字形的字符是“?”,選中它就可以在軟件界面上方看到 Unicode 編碼 0x3428,用同樣的方法找到最后一個字符的 Unicode 編碼 0x4dbc,這樣就可以確認這些亂碼字符的范圍是從 0x3428 到 0x4dbc。

2、導出字符編碼

確定好范圍后,接下來就要獲取這個范圍內所有字符的 Unicode 十六進制編碼,以便在查找替換時匹配它們。不過需要注意,并不是每一個字符都含有字形的,我們所需要的只是含有字形的字符。

為了避免重復的手工操作,可以使用一個名為 fonttools 可處理字體的三方 Python 庫來提高效率。如果你的電腦已裝有 Python 及其包管理器,可直接使用 pip 命令在命令行中運行以下指令安裝該庫。

pip install fonttools

安裝完成 fonttools 后,就可以在命令行上使用 ttx 命令執行下方的指令,來導出字體的 CMAP 表(即字符和字形的映射索引)以批量獲取字體文件中所有字符編碼,并且會自動忽略無字形的字符。

ttx -t cmap font.otf

* 注意!指令中的 font.otf 要換成你自己拆解出來的字體文件名

執行完命令后,可以在字體文件所在目錄看到命令生成名為 font.ttx 的 XML 文件,內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="OTTO" ttLibVersion="3.44">

  <cmap>
    <tableVersion version="0"/>
    <cmap_format_4 platformID="3" platEncID="1" language="0">
      <map code="0x20" name=""/><!-- SPACE -->
      <map code="0x21" name=""/><!-- EXCLAMATION MARK -->
      <map code="0x22" name=""/><!-- QUOTATION MARK -->
      <map code="0x23" name=""/><!-- NUMBER SIGN -->
      <map code="0x25" name=""/><!-- PERCENT SIGN -->
      <!-- 此處省略若干條 -->
      <map code="0xff1a" name=""/><!-- FULLWIDTH COLON -->
      <map code="0xff1b" name=""/><!-- FULLWIDTH SEMICOLON -->
      <map code="0xff1f" name=""/><!-- FULLWIDTH QUESTION MARK -->
      <map code="0xff5b" name=""/><!-- FULLWIDTH LEFT CURLY BRACKET -->
      <map code="0xff5d" name=""/><!-- FULLWIDTH RIGHT CURLY BRACKET -->
    </cmap_format_12>
  </cmap>

</ttFont>

這是字體文件中所有字符的 Unicode 編碼,接下來需要提取之前確定的范圍并轉制成替換規則。

3、轉制替換規則

本例中之前確定的字符范圍是 0x3428 到 0x4dbc,所以從 XML 中把這個范圍內的字符編碼提取出來。

<map code="0x3428" name=""/><!-- CJK UNIFIED IDEOGRAPH-3428 -->
<map code="0x343c" name=""/><!-- CJK UNIFIED IDEOGRAPH-343C -->
<map code="0x343d" name=""/><!-- CJK UNIFIED IDEOGRAPH-343D -->
<map code="0x3445" name=""/><!-- CJK UNIFIED IDEOGRAPH-3445 -->
<map code="0x344d" name=""/><!-- CJK UNIFIED IDEOGRAPH-344D -->
<!-- 此處省略若干條 -->
<map code="0x4d56" name=""/><!-- CJK UNIFIED IDEOGRAPH-4D56 -->
<map code="0x4d99" name=""/><!-- CJK UNIFIED IDEOGRAPH-4D99 -->
<map code="0x4dae" name=""/><!-- CJK UNIFIED IDEOGRAPH-4DAE -->
<map code="0x4db6" name=""/><!-- ???? -->
<map code="0x4dbc" name=""/><!-- ???? -->

想要把這些 XML 格式的字符編碼轉換成 Calibre 替換規則格式還需要處理一下??梢杂?Sublime Text 之類的代碼編輯器,開啟正則替換模式,查找 ^.*"?0x(.*?)".* 替換成 [\u$1]nn。替換完成后,這些字符的 Unicode 編碼就已經按照 Calibre 替換規則格式每間隔兩個空行排列好了,如下所示。

[u355f]


[u3561]


[u3563]


[u3564]


[u3572]


[u357f]


[u3590]


[u3593]


[u359a]


[u35ca]

* 提示:這里替換的目的是將字符的 Unicode 編碼轉換成匹配字符的正則表達式

當然現在這個替換規則文件只能匹配亂碼字符,下面還要為其填充替換內容?,F在先這些內容另存一下,文件名隨意,后綴名為 .csr(即 Calibre 的替換規則文件格式),如 pattern.csr。

4、填充替換規則

填充替換規則是個力氣活。仍以之前圖示顯示的“??????????”這十個字符為例。

你只需要按照 FontForge 中從左到右、從上到下的順序,將字符編碼對應字形輸入在其下方即可。注意,一個字符編碼及其對應字形所代表的真正字符為一組,每一組之間有一個空行,如下所示。

[u355f]
提

[u3561]
場

[u3563]
展

[u3564]
因

[u3572]
大

[u357f]
世

[u3590]
意

[u3593]
事

[u359a]
沒

[u35ca]
年

這些抄寫工作比較枯燥(本例有 400+ 個字符),不過對于打字熟練的人來說,輸入幾百個常用漢字應該花不了幾分鐘的時間。當然,如果你想要節省時間,或者說電子書制作者使得每本電子書的字體都不一樣,那可能就需要 OCR 相關的編程技術等來提高效率了,不過限于篇幅,這里不便展開討論。

編輯完成后保存一下?,F在你就得到了一個可以把電子書的“亂碼”恢復成正常字符的替換規則文件。

* 提示:這是本例最終編輯好的一份替換規則文件,可供參考:百度網盤【提取碼: gbvb】

5、使用替換規則

轉換電子書時,在轉換設置面板中,切換到【搜索替換】界面,點擊上面的【加載】按鈕,載入之前編輯好的 .csr 規則文件,然后點擊【確定】按鈕開始轉換,最終得到的電子書內容就恢復正常了。

總的說來,這個問題的解決方法并不復雜,但是操作起來還是要花費一些功夫的。如果能通過其它渠道找到替代文件,感覺沒必要這樣去做。不過,通過這件事真是見識了“盜版界”的奇技淫巧,本來感覺往電子書里插廣告就夠惡心的了,這種故意混淆字符硬生生退化電子書功能的做法更是刷新了下限。

未經允許不得轉載:螞蟻搬書 » 解決依賴內嵌字體才能正常顯示的電子書亂碼問題 – 書伴
微信公眾號:螞蟻搬書
關注我們,分享kindle電子書資源
12000人已關注
分享到:
贊(0) 打賞

評論搶沙發

  • 昵稱 (必填)
  • 郵箱 (必填)
  • 網址

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

微信掃一掃打賞

东京1.5分彩