2017-06-18 32 views
7

完全披露:我正在研究libui GUI框架的文本API。這包括Windows上的DirectWrite,OS X上的Core Text以及其他Unix上的Pango(使用HarfBuzz進行OpenType整形)。我想指定的一種文本格式屬性是要使用的OpenType功能的集合,這三個屬性都提供了這些功能; DirectWrite的是IDWriteTypography如何使用DirectWrite平衡面向腳本的OpenType功能與其他OpenType功能?

現在,當您使用這些庫繪製一些文本時,默認情況下會啓用一些有用的OpenType功能,例如標準連字(liga),如f + i連字。我認爲這是字體特定的,但事實證明這是特定於正在形成的文本的腳本。 Microsoft provides guidelines for all the scripts supported by OpenType(在「特定腳本開發」下),我可以看到相當複雜的邏輯,在HarfBuzz中完成這一切,以確認它。

在Core Text和Pango上,如果啓用其他屬性,它們將被添加到這些默認值之上。但隨着DirectWrite的,特別是IDWriteTextLayout::SetTypography(),這樣做移除默認

DirectWrite removes default OpenType features if you explicitly specify a IDWriteTypography object

產生該輸出可以發現here程序。

很顯然,我的第一個選擇是詢問如何獲取DirectWrite上的默認功能。 Someone did so already on this site, though, and the answer seems to be "no".

我在猜測,DirectWrite允許我完全控制要應用於某些文本的功能列表。這很好,除非我不能用其他API來做到這一點,除非我明確禁用默認功能!當然,我不知道這個列表是否會改變,所以對它進行硬編碼可能不是最好的主意。根據(我認爲)的版本兼容性(例如緬甸),腳本有多種可能的整形器,但是, 。

那麼,爲什麼不使用HarfBuzz的列表重新創建DirectWrite的默認功能列表呢?無論如何,它似乎想要對其他塑造者是準確的,所以這應該工作,對吧?那麼我需要做兩件事:找出要使用的腳本,並找出哪些屬性用於腳本中哪些字符在字中的位置很重要。

DirectWrite提供了一個接口IDWriteTextAnalyzer,它提供了執行整形的工具。我可以使用它,但看起來腳本數據是在DWRITE_SCRIPT_ANALYSIS structure中返回的,並且腳本ID的描述是「寫入系統腳本的從零開始的索引表示。」。

這沒有幫助,所以我寫了a program to just dump the script numbers for text I type in。運行它輸入字符串

لللللللللللللاااااااااالا abcd محمد ابن بطوطة‎‎ Отложения датского яруса 

產生輸出

0 - 26 script 3 shapes 0 
26 - 5 script 49 shapes 0 
31 - 14 script 3 shapes 0 
45 - 2 script 1 shapes 1 
47 - 25 script 22 shapes 0 

我不能在任何Windows頭的這些腳本數字匹配任何東西:如果有阿拉伯文,拉丁文,或限定數目西里爾在任何API,他們不匹配這些。即使我確實得到了腳本和腳本編號之間的映射關係,但仍然無法爲我提供應用單詞內部特徵的數據。

Uniscribe呢?那麼,the equivalent SCRIPT_ANALYSIS type的文檔說,它的腳本ID是一個「[opaque]值」,它的「這個成員的值是未定義的,應用程序不應該依賴於它的值從一個版本到下一個版本是相同的」。雖然我可以通過獲得一個語言代碼來識別腳本,但對於「西方」(拉丁語?)腳本,除LANG_ENGLISH之外,還沒有定義值。 DirectWrite值是否與Uniscribe相同?看起來我至少可以通過查看fLinkBeforefLinkAfter字段來查看單詞的初始狀態和最終狀態,但這足以正確應用每個腳本的屬性嗎?

HarfBuzz確實有一個實驗性的DirectWrite後端isn't intended to be used by real programs;我還不確定它是否具有上述我指定的相同功能。如果我發現,我會在這裏更新這部分。

最後,如果我的東西輸入以下等效試驗情況下的第一個像上面這樣kaxaml:

<Page 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
    <Grid> 
    <FlowDocumentPageViewer> 
    <FlowDocument FontFamily="Constantia" FontSize="48"> 
    <Paragraph> 
    afford afire aflight 1/4<LineBreak/> 
    <Run Typography.Fraction="1">afford afire aflight 1/4</Run> 
    </Paragraph> 
    </FlowDocument> 
    </FlowDocumentPageViewer> 
    </Grid> 
</Page> 

我看到正在正確應用的繃帶,即使在後一種情況下:

kaxaml shows what I want works fine

(最後小數部分只是爲了證明屬性正在被應用。)如果我假設XAML使用DirectWrite,那麼這證明了我的第一個選項(簡單地說就是ove在默認情況下拖拽我的自定義屬性)應該是可能的......(我做出這個假設的基礎是XAML爲繪製2D圖形提供了非常類似於Direct2D的API,並且在其中填充了很多空洞我不得不手動編寫大量的膠水代碼來與vanilla Direct2D做同樣的事情,所以我認爲XAML中可能的任何事情都可以通過Direct2D來實現,並且由於它們在技術上一起被引入到DirectWrite中,因此可以推廣...)

在這一點上,我完全失去了。我想至少可以跨平臺進行預測,而且我不確定程序應該如何,更不用說直接使用OpenType功能,或者不管怎樣。我對文本佈局API有不好的期望嗎?如果我想要這樣做,我將不得不放棄IDWriteTextLayout並完成所有的文本整形和佈局工作嗎?

或者我必須放棄對Windows 7的支持並升級到Platform Update DirectWrite功能集?甚至完全是Windows 7?

+0

如果你只想要這個UI文本,可以說使用系統默認值是「更好」,而不是擺弄功能。我不記得我是否用DirectWrite測試過這個功能,但是完全控制功能列表看起來毫無用處,因爲您必須知道每個腳本的功能集,禁用強制功能並沒有什麼用處。 – bunglehead

+0

如果您確實需要它們,腳本ID在Windows版本中保持穩定,則會附加新腳本以保持順序。要獲得完全的映射,可以使用GetScriptProperties()方法,通過使用增加的腳本ID調用它,直到失敗。受支持的腳本數量取決於系統版本。 Uniscribe腳本ID同樣適用,並且與DirectWrite腳本不兼容。 – bunglehead

+0

@bunglehead謝謝。我不得不通過進一步的討論和調查結果來修改這篇文章,但事實證明,無論如何,你都不能禁用強制性功能,所以真正的問題是如何獲得(或重新獲取)所有可選功能。對劇本的東西感到羞恥; 'GetScriptProperties()'在IDWriteTextAnalyzer1中是新的,我可以用它來猜測哪些腳本得到了什麼,但我可以使用Uniscribe的'ScriptItemizeOpenType()'並獲得OpenType腳本標記而不是ISO腳本代碼,這可能會使這一點更容易...我會在明天更新的問題中寫更多。 – andlabs

回答

3

經過與Peter Sikking和Ebrahim Byagowi的一些討論後,我去調試了一個更快速構建的通用程序來測試事情,並且我弄清楚了內部發生了什麼。

但是,首先我會說這適用於Uniscribe和DirectWrite同樣

事實證明,DirectWrite總是提供一組默認的OpenType功能,而不管我使用的是什麼功能集!情況是,提供的默認功能列表根據我是否加載自己的功能而不同,並取決於整形引擎而有所不同。對於水平書寫模式和英文版的latn腳本,這是通過「通用引擎」完成的。

如果我不提供任何功能,通用引擎將加載腳本特定的功能。對於水平latn,這個名單是

locl 
ccmp 
rlig 
rclt 
calt 
liga 
clig 

如果我提供的功能,通用引擎將使用相同的默認列表中的所有腳本:

locl 
ccmp 
rclt 
rlig 
mark 
mkmk 
dist 

所以我不知道該怎麼辦這個。我可能只是在libui代碼中提供liga和其他幾個我自己(當然標記爲HACK),但這仍然很奇怪。我不確定動機是什麼。無論哪種方式,這解釋了我看到的行爲。

1

假設你的問題一般是關於編程或者至少關注編程,我會嘗試給你一些疑問句的答案。

如果我希望能夠在默認值之上添加印刷功能,我是否必須完全在代碼中放棄使用IDWriteTextLayout?

這取決於。如果一個IDWriteTextLayout接口以各種方式適合您的項目任務,除了DirectWrite默認印刷功能的易變性,請了解您應該如何處理印刷術並創建一個適合您需要的IDWriteTypography實例。爲程序開發自定義文本佈局可能需要大量時間和精力,尤其是在程序應呈現雙向文本,複雜腳本,內聯對象等情況下。

可能會發生項目的任務需要開發除了控制渲染文本中使用的印刷特徵以外,還有一個文本佈局引擎。例如,您的經理/客戶可能會要求實施定製的換行機會或字形提前調整算法。在這種情況下,您將實現一個IDWriteTextAnalizer :: GetGlyphs方法。此方法具有參數DWRITE_TYPOGRAPHIC_FEATURES ** features,const UINT32 * featureRangeLengths,UINT32 featureRanges,並且此參數使您能夠取代要呈現的文本範圍的一組「默認」排版功能(請參閱我對其他問題的回答What are the default typography settings used by IDWriteTextLayout? )。只有受影響的功能纔會改變;其他功能具有其「默認」值。此外,如果您在下一個文本範圍的GetGlyphs調用中忽略此參數(例如,使用NULL,NULL,0值),則前一次GetGlyphs調用中更改的功能將不會因下一個範圍的調用而更改。

爲等效SCRIPT_ANALYSIS類型的文件說,它的腳本ID是「[不透明]值」,其「此成員值是未定義的,應用程序不應該從一個釋放到依靠它的值是相同的下一個」。儘管我可以通過語言代碼來識別腳本,但對於「西方」(拉丁語?)腳本,除LANG_ENGLISH外,還沒有定義值。

嚴格地說,這不是一個疑問性的陳述,但我想你對這些Unicode腳本ID是如何定義以及如何使用具有如此模糊定義的結構和常量的API不滿意。

它可能是脫離主題,但我冒險推測「Unicode腳本ID」值的來源。截至2010年7月17日,Unicode,Inc.發佈了Unicode 6.0版本。該標準包含文件 http://www.unicode.org/Public/6.0.0/ucd/PropertyValueAliases.txt,包含一個包含腳本列表的部分。清單如下:

# Script (sc) 

    sc ; Arab  ; Arabic 
    sc ; Armi  ; Imperial_Aramaic 
    etc. 

阿拉伯文腳本是#1,西里爾文腳本是#20,拉丁腳本是在這個列表中#47。此外,在其他地方,我看到這個列表以腳本Common和Inherited開頭。它將阿拉伯文字放到第3位,西里爾文放到第22位,拉丁文放到第49位。這些序言對你來說很熟悉,不是嗎?

幸運的是,我們不需要依賴「Unicode腳本ID」值;我們需要腳本屬性,而不是腳本ID或縮寫。當我們向GetScriptProperties方法傳遞從AnalyzeScript調用派生的數字時,該API是自我一致的,因爲它爲文本範圍提供了實際的腳本屬性。