【原创】修复lazport用lrPDFExport导出pdf中文变为框框的Bug(2024-10-22更新)

秋·风 / 2024-10-23 / 原文

按《lazreport的pdf导出插件lrPDFExport的DoMemoView(View: TfrMemoView)功能缺失》修复后英文显示正常,但中文显示为框框的问题。
前几天经网友“安全生产监管”提醒我,使用freepascal自带的fpPDF创建的pdf可以正常显示中文,字体名称不能用中文。lazreport的lrPDFExport就是使用fpPDF来创建pdf的。

引起这个问题的根源:
由于TfrMemoView的字体名称是中文的,如宋体等,但fpPDF使用字体本身的名称,如仿宋对应的FangSong,设置PDF字体时默认直接使用TfrMemoView的中文字体名称,所以生成pdf时找不到对应的字体。还有个问题,设置字体时还要注意字体名称的大小写,错了也不能正常显示。

lrPDFExport导出含中文的pdf中文变为框框:

修复步骤:
打开lazreport/source/addons/lrFclPDFExport/lr_e_fclpdf.pas
1、修改348行的function DefFontName:string; 
增加缺省字体:宋体/仿宋/楷体/和黑体等字体对名称,修改后如果系统如已安装以下字体之一就可以正常显示中文,但所有字体都会用缺省字体显示。

function DefFontName:string;
const
  DefFontNames : array [1..7] of string =
     ('STSong','FangSong','KaiTi','SimHei','Liberation Sans', 'Arial', 'FreeSans');
var
  i: Integer;
begin
  for i:=1 to 7 do
    if Assigned(gTTFontCache.Find(DefFontNames[i], false, false)) then
    begin
      Result:=DefFontNames[i];
      exit;
    end;
  raise Exception.Create('Not found Sans font');
end;

2、将memoview的中文字体名称转为系统对应字体名称,修改后就可以显示对应的中文字体,你也可以根据自己的需要添加相应的字体(233行)。

function TExportFonts.AddItem(AFontName: string; AFontStyle: TFontStyles
  ): TExportFontItem;
var
  S1, S2, S3: String;
begin
  if AFontName='仿宋' Then AFontName:='FangSong';
  if AFontName='楷体' Then AFontName:='KaiTi';
  if AFontName='幼圆' Then AFontName:='YouYuan';
  if AFontName='黑体' Then AFontName:='SimHei';
  if AFontName='宋体' Then AFontName:='STSong';

  Result:=FindItem(AFontName, AFontStyle);
  if Assigned(Result) then exit;

  if Assigned(gTTFontCache.Find(AFontName, Graphics.fsBold in AFontStyle, Graphics.fsItalic in AFontStyle)) then
  begin
    Result:=TExportFontItem.Create(Self, AFontName, AFontStyle);
    S1:=ExtractFileDir(Result.FTTFFontInfo.FileName);
    S2:=ExtractFileName(Result.FTTFFontInfo.FileName);
    S3:=AFontName;
    FOwner.FPDFDocument.FontDirectory:=S1;
    Result.FPdfFont:=FOwner.FPDFDocument.AddFont(S2, S3);
  end
  else
    Result:=FDefaultFontNormal;
end;

3、在linux,字体可能安装到用户目录的.local/share/fonts,还要修改下383行的procedure CreateFontDirList,添加1行。如果自行安装的字体不在这些目录,只需添加对应的目录即可。

gTTFontCache.SearchPath.Add(ExpandFileName('~/')+'.local/share/fonts');

修改后:

procedure CreateFontDirList;
{$IFDEF WINDOWS}
var
  s: String;
{$ENDIF}
begin
  {$IFDEF WINDOWS}
  s := SHGetFolderPathUTF8(20); // CSIDL_FONTS = 20
  if s <> '' then
    gTTFontCache.SearchPath.Add(s);
  {$ENDIF}
  {$IFDEF linux}
  //tested on Fedora 24
  gTTFontCache.SearchPath.Add('/usr/share/cups/fonts/');
  gTTFontCache.SearchPath.Add('/usr/share/fonts/');
  gTTFontCache.SearchPath.Add('/usr/share/wine/fonts/');
  gTTFontCache.SearchPath.Add('/usr/local/lib/X11/fonts/');
  gTTFontCache.SearchPath.Add(ExpandFileName('~/')+'.local/share/fonts');//linuxs要添加这行
  gTTFontCache.SearchPath.Add(GetUserDir + '.fonts/');
  {$ENDIF}

end;

2024-10-22更新:
发现新的procedure TlrPdfExportFilter.InitFonts更改了部分代码,gTTFontCache.ReadStandardFonts只读取的fonts.conf不包含我的字体目录(~/.local/share/fonts),在linux不执行CreateFontDirList,造成字体设置失败,生成只pdf只使用默认字体,添加字体目录就可以【

gTTFontCache.SearchPath.Add(GetUserDir+'.local/share/fonts');】。

procedure TlrPdfExportFilter.InitFonts;
procedure CreateFontDirList;
{$IFDEF WINDOWS}
var
  s: String;
{$ENDIF}
begin
  {$IFDEF WINDOWS}
  s := SHGetFolderPathUTF8(20); // CSIDL_FONTS = 20
  if s <> '' then
    gTTFontCache.SearchPath.Add(s);
  {$ENDIF}
  {$IFDEF linux}
  //tested on Fedora 24
  gTTFontCache.SearchPath.Add('/usr/share/cups/fonts/');
  gTTFontCache.SearchPath.Add('/usr/share/fonts/');
  gTTFontCache.SearchPath.Add('/usr/share/wine/fonts/');
  gTTFontCache.SearchPath.Add('/usr/local/lib/X11/fonts/');
  gTTFontCache.SearchPath.Add(GetUserDir + '.fonts/');
  {$ENDIF}

end;
begin
  if gTTFontCache.Count = 0 then
  begin
    gTTFontCache.BuildFontCacheIgnoresErrors:=true;
    {$IFDEF WINDOWS}
    CreateFontDirList;
    {$ELSE}
    gTTFontCache.ReadStandardFonts;
    gTTFontCache.SearchPath.Add(GetUserDir+'.local/share/fonts');//linuxs要添加这行
    {$ENDIF}
    gTTFontCache.BuildFontCache;
  end;
end;

修改后重新安装lrPDFExport后就可以.

如果按以上方面修改后还不能正常显示中文,请安装相关字体。

以上方法在linux已验证,上述截图是在银河麒麟 aarch64运行lazreport导出的。

lrPDFExport中文导出的效果比lazreportpdfexport强太多了,不会出现字间距的Bug。