有一位名為「行雲」的小夥伴留言反饋了一個問題,說他在網上找到一本電子書,但是裡面的內容使用了特殊的字體,只要轉換成其它格式就會亂碼,並且也不能正常複製字體,問如何解除這種限制。
一、問題表現
簡單實測了一下樣本電子書,發現在 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 命令在命令行中運行以下指令安裝該庫。
安裝完成 fonttools 後,就可以在命令行上使用 ttx 命令執行下方的指令,來導出字體的 CMAP 表(即字符和字形的映射索引)以批量獲取字體文件中所有字符編碼,並且會自動忽略無字形的字符。
* 注意!指令中的 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=""/> <map code="0x21" name=""/> <map code="0x22" name=""/> <map code="0x23" name=""/> <map code="0x25" name=""/> <map code="0xff1a" name=""/> <map code="0xff1b" name=""/> <map code="0xff1f" name=""/> <map code="0xff5b" name=""/> <map code="0xff5d" name=""/> </cmap_format_12> </cmap></ttFont>這是字體文件中所有字符的 Unicode 編碼,接下來需要提取之前確定的範圍並轉製成替換規則。
3、轉制替換規則
本例中之前確定的字符範圍是 0x3428 到 0x4dbc,所以從 XML 中把這個範圍內的字符編碼提取出來。
<map code="0x3428" name=""/><map code="0x343c" name=""/><map code="0x343d" name=""/><map code="0x3445" name=""/><map code="0x344d" name=""/><map code="0x4d56" name=""/><map code="0x4d99" name=""/><map code="0x4dae" name=""/><map code="0x4db6" name=""/><map code="0x4dbc" name=""/>想要把這些 XML 格式的字符編碼轉換成 Calibre 替換規則格式還需要處理一下。可以用 Sublime Text 之類的代碼編輯器,開啟正則替換模式,查找 ^.*"?0x(.*?)".* 替換成 [\\u$1]\n\n。替換完成後,這些字符的 Unicode 編碼就已經按照 Calibre 替換規則格式每間隔兩個空行排列好了,如下所示。
[\u355f]
[\u3561]
[\u3563]
[\u3564]
[\u3572]
[\u357f]
[\u3590]
[\u3593]
[\u359a]
[\u35ca]* 提示:這裡替換的目的是將字符的 Unicode 編碼轉換成匹配字符的正則表達式
當然現在這個替換規則文件只能匹配亂碼字符,下面還要為其填充替換內容。現在先這些內容另存一下,文件名隨意,後綴名為 .csr(即 Calibre 的替換規則文件格式),如 pattern.csr。
4、填充替換規則
填充替換規則是個力氣活。仍以之前圖示顯示的「㕟㕡㕣㕤㕿㖂㖐㖓㖚㗊」這十個字符為例。
你只需要按照 FontForge 中從左到右、從上到下的順序,將字符編碼對應字形輸入在其下方即可。注意,一個字符編碼及其對應字形所代表的真正字符為一組,每一組之間有一個空行,如下所示。
[\u355f]提
[\u3561]場
[\u3563]展
[\u3564]因
[\u3572]大
[\u357f]世
[\u3590]意
[\u3593]事
[\u359a]沒
[\u35ca]年這些抄寫工作比較枯燥(本例有 400+ 個字符),不過對於打字熟練的人來說,輸入幾百個常用漢字應該花不了幾分鐘的時間。當然,如果你想要節省時間,或者說電子書製作者使得每本電子書的字體都不一樣,那可能就需要 OCR 相關的編程技術等來提高效率了,不過限於篇幅,這裡不便展開討論。
編輯完成後保存一下。現在你就得到了一個可以把電子書的「亂碼」恢復成正常字符的替換規則文件。
5、使用替換規則
轉換電子書時,在轉換設置面板中,切換到【搜索替換】界面,點擊上面的【加載】按鈕,載入之前編輯好的 .csr 規則文件,然後點擊【確定】按鈕開始轉換,最終得到的電子書內容就恢復正常了。
總的說來,這個問題的解決方法並不複雜,但是操作起來還是要花費一些功夫的。如果能通過其它渠道找到替代文件,感覺沒必要這樣去做。不過,通過這件事真是見識了「盜版界」的奇技淫巧,本來感覺往電子書裡插廣告就夠噁心的了,這種故意混淆字符硬生生退化電子書功能的做法更是刷新了下限。
---- · END · ----
【推廣】書伴推送服務:push.bookfere.com
微信ID:kindle-fere「書伴」為靜心閱讀而