簡體   English   中英

Java / pdf文本渲染

[英]java / pdf text rendering

我正在自己的pdf文件中使用Java生成lib,並且在字體/文本呈現方面遇到了一些麻煩。 Java中顯示的文本(字體,單詞間距,字符間距等)不同於PDF中顯示的文本。

在下面的示例中,我使用的是“ Time New Roman”字體,它是PDF基本字體之一(因此,我不必計算所有字體度量並將其輸出到pdf)。

因此,具體來說,在生成的PDF中,我有以下內容:

BT
/F5 16 Tf
849 921 Td
(Normal Return Distribution) Tj
ET

字體F5由對象29 0 R定義,它是(僅基本字體,因此未指定文本度量):

29 0 obj <</Type /Font /Subtype /Type1 /BaseFont /Times-Roman>>
endobj

在Java中,我正在使用:

g2d.setFont(new Font("TimesRoman", Font.PLAIN, 16));
g2d.drawString("Normal Return Distribution", 849, 921);

我已將文本繪制成與文本邊界匹配的矩形,並且在Java中就可以了(我已經在Java中計算了字符串邊界),但是在Adobe Acrobat Reader中,文本大於矩形。

這是一個屏幕截圖(我是通過獲取顯示我的PDF的Adobe Acrobat Reader屏幕截圖,並獲取顯示該緩沖圖像的程序的屏幕截圖來構建的;然后將PDF屏幕截圖的一部分復制/粘貼到我的矩形下方)程序截圖進入MSPaint。要具有相同的矩形大小,我必須在Adobe中以原始大小的65.5%顯示pdf):

Java vs PDF文本輸出

因此,我們可以看到java en adobe中用於顯示文本的字體是相同的。 但是文本似乎在Adobe中更大了。 實際上,如果我疊加兩個單詞(一個來自java的單詞,一個來自adobe的單詞),則單詞間距也一樣,字母間距也一樣,但是有些字母的像素寬度差異為1。

為什么? 我該怎么做才能解決這個問題? 我試過用字符間距(Tc運算符),字間距(Tw運算符),水平縮放(Tz運算符)播放(以pdf格式); 我認為它可以“起作用”; 但是為什么兩個程序中的縮放/間距/ ...不一樣? 這些(默認)參數不是Font文件的一部分(這是一種真正的類型)嗎? 以及如何正確檢索它們(無需手動將參數放入我的Java代碼中)?

謝謝

編輯

因此,正如您倆所解釋的,我正在研究不要使用pdf基本字體,以確保Java和Adobe Reader使用相同的字體(ttf文件)。 但是我仍然有一個問題(一樣嗎?)。

在PDF輸出中,我正在生成如下字體:

31 0 obj <<
/Type /Font
/FirstChar 0
/LastChar 255
/Widths[1298 ... 646]
/Name /F7
/Encoding /WinAnsiEncoding
/Subtype /TrueType /BaseFont /Tahoma /FontDescriptor 32 0 R
>>
endobj

32 0 obj <<
/Type /FontDescriptor
/Ascent 1299
/CapHeight 1298
/Descent -269
/Flags 32
/FontBBox [0 -269 2012 1299]
/FontName /Tahoma
/ItalicAngle 0
/StemV 126
/XHeight 1298
>>
endobj

如果我正確理解了規范,則所有數字(寬度,上升,下降等)都是相對於字形單位(基於1em?),其中1em = 1000(1em是M字符的寬度)。

因此,要從Java生成所有這些參數,我首先嘗試找到正確的Java字體大小,以使M字符的寬度等於1000(因為Java不允許訪問Font類或其他類中的這些參數;並且即使這些信息已放入ttf文件中,PDF也需要它?)。

float size = 1f;
while (true) {
    font = font.deriveFont(size);
    fm = g2d.getFontMetrics(font);
    int em = fm.charWidth('M');
    if (em >= 1000)
        break ;
    size += 1;
}

然后,我可以生成所有必需的參數。 例如,對於Widths數組(這是每個字符的寬度):

String pdfWidths = "";
for (int i = 0; i <= 255; ++i) {
    int width = fm.charWidth(i);
    pdfWidths += width + " ";
}

但是這樣做,我仍然使我的文本與Adobe Viewer中的矩形重疊。 因此,我必須將Tahoma字體的EM限制(設置為蠻力循環)設置為780; 到850(對於Verdana字體); ...顯示類似的文本(不完全相同,但這可能是由於抗鋸齒算法造成的?)(請參見下面的屏幕截圖)。 因此,這不是恆定的“極限”(理論上必須等於1000),而是可變的極限...對嗎? (我認為不是)如果是,如何找到這個極限? 如果沒有,那是什么問題?

Java與PDF文本輸出-EM

再次感謝。

編輯

只需將字體大小設置為1000即可,而無需通過強求來找到EM /行高大小,pdf中的結果實際上就是Java。

font = font.deriveFont(1000f);
fm = g2d.getFontMetrics(font);
//Retrieve Widths attribute
_pdfWidths = "";
for (int i = _firstChar; i <= _lastChar; ++i) {
    int width = fm.charWidth(i);
    _pdfWidths += width + " ";
}

但是仍然存在一些差異,可能是由於文本繪圖算法所致(字距調整可能與Java和Adobe Reader不同?)。 參見下圖,通過Verdana,我們可以看到pdf中的文本(寬度)比java中的文本小一些。

Java vs PDF文本輸出-1000

這個答案本質上是我的評論的總結。

首次嘗試使用 PDF字體和Java的“ TimesRoman” 字體“ Time New Roman” (實際上是Times-Roman字體,它是PDF的基本字體之一(不計算所有字體度量並將其輸出到pdf中) 。 AWT,導致

嘗試使用標准的14次羅馬時代

本質上:您的應用使用Java AWT在16pt時認為TimesRoman普通的內容,以自己的方式應用字體指標; 您的PDF查看器以16個用戶空間單位使用它認為的Times-Roman並應用PDF規范中指定的字體指標。 您可以期望的只是一些相似性(否則,其中的一個上下文將是一個非常糟糕的選擇),但一點也不相同。

大衛實際上在回答中在第1項(不同的字體)和第3項(不同的字距調整和替換)中進行了更詳細的解釋。

此外,

順便說一句:從PDF 1.5開始,不贊成對標准14字體進行特殊處理。 ISO 32000-1中的9.6.2.1節)。 因此,通過不將字體指標明確包含在PDF中,您可以執行許多年來不推薦使用的操作。

下一個嘗試涉及不使用pdf基本字體來確保Java和Adobe Reader使用相同的字體(ttf文件) ,這需要計算要嵌入PDF的字符寬度。 在這種情況下,假設所有數字(寬度,上升,下降等)都相對於字形單位(基於1em?),其中1em = 1000(1em是M字符的寬度)。 因此,嘗試找到正確的Java字體大小以使M字符的寬度等於1000 ,然后從該字體生成所有必需的參數

不,不是基於em的,而是: 一種字體以一種標准大小定義字形。 排列此標准的目的是使緊密排列的文本行的標稱高度為1單位。 因此,1000個字形空間單位是該標稱線的高度。

這就引出了一個問題,即“名義線”到底是什么。 幸運的是,采用另一種方法比較容易:按定義,大小為1的字體是指其“標稱行”的高度為1的字體。

寬度數組是否不應該填充1000 * fm.charWidth(i) ,其中fm是1號字體的度量? 或者,當AWT使用int寬度時,請使用fm.charWidth(i) ,其中fm是大小為1000的字體的度量?

考慮到這一點, 只需將字體大小設置為1000即可,而無需通過蠻力來找到EM /行高大小,pdf中的結果實際上就是java。 但是仍然存在一些差異,可能是由於文本繪圖算法所致(字距調整可能與Java和Adobe Reader不同?)。 參見下圖,通過Verdana,我們可以看到pdf中的文本(寬度)比java中的文本小一些。

嘗試使用嵌入字體和正確的字符寬度

看一下FontMetrics.charWidth方法的注釋: 請注意,字符串的前進不一定是其字符的前進總和。 AWT還額外應用了字距調整等,導致輕微的偏差。 但是,在PDF中,使用單個Tj操作,這些進步的確加起來了。

如果要在PDF中使用字距調整,則必須明確地寫出與標准寬度的偏差。 在這里, TJ運算符非常方便,允許將字符串和偏移量的混合數組作為參數。

如果要用連字代替某些字符,則還必須自己做

有許多可能的解釋,所有這些都導致使用PDF中定義的標准14字體可能是合法的,但通常不明智。 它介紹了您遇到的歧義。 PDF通常旨在避免此類歧義。 從這種意義上講,允許使用非嵌入式和未正確指定的字體是一個壞主意。

  • 如果您仔細查看文本中的字符形狀,我可能會敢說您實際上正在查看不同的字體。 相似,但不同。 例如看“ i”,在一種情況下,“ i”上的點高多少。 這樣做的原因可能是Adobe Reader擁有自己的字體集,並且不使用系統字體(例如Java可能使用的字體)。 考慮一下-無論Adobe Reader運行在哪個系統上,它還能如何始終正確顯示這些字體?

  • 實際上可能更糟。 如果我通過安裝Adobe Reader搜索我沒有找到Times字體(不是“宋體”,比如你說的,這是一個不同的字體)。 因此,很有可能是Adobe Reader使用不同的字體來模仿Times(以及其他一些基本的14種字體)。 我不太確定這一點,但我不認為Acrobat和Reader曾經使用MultiMaster字體來模擬非嵌入式字體。

  • 此外,在PDF中呈現文本的方式不使用字符間字距調整,而Java可能很聰明,可以應用一些其他字距調整或使用字符替換(例如使用一個字形表示組合“ ffl”而不是三個單獨的字符)。 PDF能夠使用字距調整和那些特殊的字形,但是您必須完成工作以確保已使用它們。

如果要絕對確保PDF看起來與Java渲染完全相同,請弄清楚Java中的字符位置。 然后以使每個字符都位於完全相同的位置的方式編寫PDF文件...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM