LuaTeX/PdfTeX:重叠字形的字体透明度不正确 - 在文本模式下实现类似于 Adob​​e InDesign/Tikz 的效果

LuaTeX/PdfTeX:重叠字形的字体透明度不正确 - 在文本模式下实现类似于 Adob​​e InDesign/Tikz 的效果

更新-1:此问题会影响所有假定字形重叠的字体。受影响的三大类字体为:1) 所有非拉丁文脚本的字体,这些字体本身需要字形重叠;2) 拉丁文脚本的手写(草书)字体;3) 装饰字体,旨在通过重复部分重叠的字形图案来构建复杂的装饰(如装饰边框/框架)。

更新-2:该问题尚未得到解答。大卫·普顿发布下面的这个“相关答案”,正确的理解是 tikz“透明度组”也会将分组透明度应用于节点文本。我的 tikz 代码中的错误是我调用了透明度分组,但在不同的范围级别指定了不透明度。我已经更新了有问题的原始代码,tikz 输出看起来不错。这意味着文本模式(tikz 之外)中透明度分组所需的代码可以从 tikz 代码库改编,也可以由了解的人编写PDF XObjects。即使透明度仅应用于 latex 的牢不可破的框(如包textpos的环境texblockminipage,或纯 tex 的框(vboxhbox),这也是没问题的;这本身就是一个巨大的飞跃。了解宏的人\pdfxform),这也是没问题的;这本身就是一个巨大的飞跃。了解宏的pdftex 用户手册第 8.8 节也许能有所帮助。

在某些脚本中,几乎不可能有不重叠的字形组件。这要么是因为一些复杂的字形本身由多个(可能重叠的)组件字形组成,要么是设计上相邻的字形需要重叠才能形成一个单词。这两种情况对于天城文都是正确的,后者是因为单词中字形的“上划线”必须重叠才能形成一个单词(这是除单词间距之外的脚本功能)。当文本设置为纯色时,这些重叠(理所当然地)不可见,而当文本设置为透明时,这些重叠可见。

为此类脚本设置fontspec不透明度会产生丑陋的结果(使其无法使用),例如,请参见下图,这是使用不透明度设置的印地语父级(पिताजी)的单词。如您所见,不透明度设置会导致重叠区域出现较暗的斑点。这是因为在应用透明度之前未对文本进行分组(不透明度设置在单个字形级别应用透明度)。如果使用其隐式或显式透明度分组,Tikz 不会出现此问题(此处未显示 tikz 输出,可以通过编译此问题中的代码来查看)。以下字体规格Opacity输出是 Adob​​e InDesign 2020 的输出(幸运的是我仍然可以访问 InDesign 的试用版),看起来很棒(重叠处没有黑点,结果看起来很统一)。我测试了 Tikz 和 Adob​​e InDesign 的文本透明度分组的输出,文本在分组后不会转换为路径(文本是可复制/可搜索的,这很棒)。从大卫·普顿分析Tikz 和 Adob​​e InDesign 生成类似的 PDF 代码。

LuaLaTeX 和fontspec输出Opacity<1LuaLaTeX 输出:字形重叠处有较暗的区域

Adobe InDesign 2020 输出: Adobe InDesign 输出:字形重叠处没有较暗的区域 理想的解决方案是执行分组透明度文本模式(如果它只适用于 HarfBuzz,那就没问题了),就像“透明组”对 tikz 文本和图形的工作方式一样。文本仍然应该像 pdf 中的文本一样,它应该是可搜索和可复制的,并且不应该在文本模式下添加任何限制(例如透明文本可以跨越段落等)。如果它使实现更容易,解决方案可以限制自己将分组透明度应用于包的textpos环境textblock,或者minipage使该环境中的所有对象都成为透明度组的一部分(可能带有混合模式的参数,并且可能可以像 tikz 透明度组和颜色命令一样嵌套组\color/\textcolor[尽管嵌套是一个额外的好处,但不是最重要的])。 附注:我并不是在寻找一个 tikz 解决方案(其作用类似于具有分组透明度的 textpos 或 minipage),但欢迎您在此处为更广泛的社区发布一个解决方案(第一行明确以粗体说明“这不是这个问题的解决方案,在这里发布只是为了展示用 tikz 可以实现什么。”)。[1]

[1] 这可能听起来太严格了,但在我看来,一个似乎部分解决问题的错误答案可能不会引发进一步的探究/推动找到正确的解决方案。

作为参考,以下截图来自asppdf 手册第 17.3.1 节可以引导我们找到解决方案。看来,这个问题的解决方案需要实现一个透明度分组,就像屏幕截图左下角的图一样。四个透明度分组图像上方的段落很好地解释了事情。当前的透明度实现功能类似于右上图像(没有分组的透明度,导致字形相互合成)。

显示不同透明度分组选项的图像

这是重现重叠处暗区问题的测试代码。您可能需要放大才能在屏幕上看到问题,但在打印的文档中它会清晰可见且令人分心。最后,这是不是字体问题,并可以用任何现有的天城体字体重新创建。

% >> lualatex opacityoverlap.tex
\documentclass{article}
\usepackage{fontspec}
\usepackage{tikz}

\newfontfamily{\devanagarifamtext}{Noto Sans Devanagari}[Script=Devanagari, Scale=1, Renderer=HarfBuzz, Color=FF0000, Opacity=0.25]

\newfontfamily{\devanagarifamtikz}{Noto Sans Devanagari}[Script=Devanagari, Scale=1, Renderer=HarfBuzz, Color=FF0000]

\begin{document}

Text mode: {\devanagarifamtext एक गांव में पिताजी}

Tikz mode: \tikz[baseline,blend group=hue,opacity=0.25]{\node[inner sep=0pt,minimum width=0pt,outer sep=0pt,anchor=base] () {\devanagarifamtikz एक गांव में पिताजी};}

\end{document}

答案1

这不是这个问题的解决方案,发布在这里只是为了展示使用 tikz/pgf 可以实现什么(按照问题指示)。

更新:仅限 PGF 答案

这是一个仅使用 的优化解决方案pgf。它比使用接口的开销要小tikz。经过长时间的研究,我认为这是迄今为止最好的解决方案。它实现起来简单得多,可以与其他软件包配合使用,并且对速度的影响最小。

除非您非常小心地布置文本,否则混合模式在各个查看器中似乎都不可靠,pgfpicture所以我认为使用它们没有任何好处。

%! TeX Program = lualatex

\documentclass{article}

\usepackage{xparse}
\usepackage{pgf}

\ExplSyntaxOn

\NewDocumentEnvironment { transparencygroup }
  { m +b }
  {
    \par
    \dim_set_eq:NN \l_tmpa_dim \prevdepth
    \noindent
    \pgfrememberpicturepositiononpagefalse
    \begin { pgfpicture }
      \pgfsetfillopacity { #1 }
      \begin { pgftransparencygroup } [ isolated=false ]
        \pgfpathmoveto { \pgfpointorigin }
        \pgftext [ base ]
          {
            \vbox:n
              {
                \dim_set_eq:NN \prevdepth \l_tmpa_dim
                #2
              }
          }
      \end { pgftransparencygroup }
    \end { pgfpicture }
  }
  { }

\NewDocumentCommand \texttransparencygroup { m m }
  {
    \mode_leave_vertical:
    \hbox:n
      {
        \pgfrememberpicturepositiononpagefalse
        \begin { pgfpicture }
          \pgfsetfillopacity { #1 }
          \begin { pgftransparencygroup } [ isolated=true ]
            \pgfpathmoveto { \pgfpointorigin }
            \pgftext [ base ] { #2 }
          \end { pgftransparencygroup }
        \end { pgfpicture }
      }
  }

\ExplSyntaxOff

\usepackage{fontspec}

\newfontfamily{\devanagari}{Noto Sans Devanagari}[Script=Devanagari, Renderer=HarfBuzz]

\begin{document}

\Huge
English {\devanagari \texttransparencygroup{0.4}{एक गांव में पिताजी}} English

\devanagari\color{red}
\begin{transparencygroup}{0.5}
  एक गांव में पिताजी
\end{transparencygroup}

\vskip-8mm
\color{green}
\begin{transparencygroup}{0.25}
  एक गांव में पिताजी
\end{transparencygroup}

\end{document}

输出


TIKZ 答案

InDesign 中的 PDF 本质上使用与 tikz 相同的方法。它创建一个带有透明度组的 Form XObject 并将文本放入其中。然后它将此 Form XObject 插入到页面流中。即使您不使用 tikz,您仍然必须将内容装入 XObject 中。透明度组不仅仅是您可以在流中打开和关闭的图形标志。

诀窍tikz似乎是将文本放入透明度组中:

    \documentclass{article}
    \usepackage{l3pdf}
    \ExplSyntaxOn
    \pdf_uncompress:
    \ExplSyntaxOff
    \usepackage{fontspec}
    \usepackage{tikz}
    \newfontfamily{\devanagari}{Noto Sans Devanagari}[Script=Devanagari, Scale=1, Renderer=HarfBuzz]
    \begin{document}
    \begin{tikzpicture}[opacity=0.5]
      \fill[cyan] (0,0) circle [radius=10pt];
      \begin{scope}[transparency group]
        \node[text=red, font=\devanagari] {एक गांव में पिताजी};
      \end{scope}
    \end{tikzpicture}
    \end{document}

输出

这是一个更完整的示例,展示了如何将几个不同颜色的段落放在透明组中。它还允许您指定混合模式。但是,任何背景图形都必须位于混合组中。如果您只想要透明度,则这不是限制。

\documentclass{article}
\usepackage{l3pdf}
\ExplSyntaxOn
\pdf_uncompress:
\ExplSyntaxOff
\usepackage{fontspec}
\usepackage{tikz}
\usepackage{lipsum}
\newfontfamily{\devanagari}{Noto Sans Devanagari}[Script=Devanagari, Scale=1, Renderer=HarfBuzz]
\newcommand*{\tgopacity}{0.5}
\newcommand*{\tgblendmode}{normal}
\newcommand*{\tggraphics}{}
% \begin{transparentgroup}{opacity}{blend mode}{graphics within blend group}
\newenvironment{transparentgroup}[3]{%
  \renewcommand*{\tgopacity}{#1}%
  \renewcommand*{\tgblendmode}{#2}%
  \renewcommand{\tggraphics}{#3}%
  \setbox0=\vbox\bgroup
}{%
  \egroup
  \noindent\begin{tikzpicture}[
      inner sep=0pt, outer sep=0pt, blend group=\tgblendmode
    ]
    \tggraphics
    \pgfresetboundingbox
    \begin{scope}[transparency group, opacity=\tgopacity]
      \node [anchor=north west] {\box0};
    \end{scope}
  \end{tikzpicture}}
\begin{document}
\noindent\tikz[remember picture, overlay] \fill[green, opacity=0.5]
  ([yshift=7cm]current page.center) circle [radius=20pt];%
\begin{transparentgroup}{0.5}{lighten}{
    \fill[green, opacity=0.5] (10mm,-5mm) circle [radius=20pt];
  }
  \textcolor{red}{\devanagari एक गांव में पिताजी}

  \lipsum[1]
\end{transparentgroup}

\textcolor{red}{\devanagari एक गांव में पिताजी}

\lipsum[1]

\end{document}

输出

答案2

您可以做您想做的事情,但这很麻烦。下面的示例有一个简单的界面来设置材质,\vbox并允许您设置不透明度和混合模式。

用法是:

\begin{transparencygroup}[
    opacity = < Value between 0 and 1 >,
    blend mode = < One of the standard PDF blend modes >
  ]
\end{transparencygroup}

由于页面资源的接口较差,其他向页面资源添加内容的包可能会出现问题。

此示例代码并未尝试与其他软件包一起使用,因此可能无法正常工作如果您加载了其他会产生干扰的软件包/ExtGState(例如tikz,、、、的不透明度功能等)。提供了一个可在您需要使用时使用的钩子。colorspacetransparentfontspectikztikz

%! TeX Program = lualatex

\documentclass{article}
\pagestyle{empty}

% grouped transparency implementation

% Limitations:
%
% 1. Because of the poor interface for page resources, there can be problems
%    with other packages that add things to the /ExtGState dictionary (e.g.,
%    tikz, colorspace, transparent, etc.).
%
% 2. You can't use the Opacity feature of fontspec as this puts the
%    transparency code inside the transparency group, and you'll still get the
%    overlapping glyphs.
%
% 3. Only LuaLaTeX is supported.

\usepackage{l3pdf}
\usepackage{xparse}
\usepackage{everyshi}

\ExplSyntaxOn

\pdf_uncompress:


% l3pdf extensions (lualatex only)

\cs_generate_variant:Nn \pdf_object_new:nn { xn }
\cs_generate_variant:Nn \pdf_object_write:nn { xx }
\cs_generate_variant:Nn \pdf_object_ref:n { e }

\cs_new_protected:Nn \__reportaman_pdf_xform_now:Nnn
  {
    \tex_immediate:D \tex_pdfxform:D
      attr { #2 }
      resources { #3 }
      #1
  }
\cs_generate_variant:Nn \__reportaman_pdf_xform_now:Nnn { Nxx }

\cs_new_protected:Nx \__reportaman_pdf_xform_last:
  {
    \exp_not:N \int_value:w
    \exp_not:N \tex_pdflastxform:D
    \c_space_tl 0 ~ R
  }

\cs_new_protected:Nn \__reportaman_pdf_refxform_last:
  {
    \tex_pdfrefxform:D \tex_pdflastxform:D
  }

\cs_new_protected:Nn \__reportaman_pdf_pageresources_gput_right:nn
  {
    \tex_global:D
    \tex_pdfvariable:D pageresources
    \exp_after:wN
      {
        \tex_the:D \tex_pdfvariable:D pageresources / #1 ~ #2
      }
  }
\cs_generate_variant:Nn \__reportaman_pdf_pageresources_gput_right:nn { nx }

\cs_new_protected:Nn \__reportaman_pdf_literal_direct:n
  {
    \tex_pdfextension:D literal direct
      {
        #1
      }
  }
\cs_generate_variant:Nn \__reportaman_pdf_literal_direct:n { x }

\cs_new_protected:Nn \__reportaman_pdf_save_gs:
  {
    \__reportaman_pdf_literal_direct:n { q }
  }

\cs_new_protected:Nn \__reportaman_pdf_restore_gs:
  {
    \__reportaman_pdf_literal_direct:n { Q }
  }


% grouped transparency back end

\clist_new:N \g__reportaman_ca_clist
\clist_new:N \g__reportaman_bm_clist
\int_new:N \g__reportaman_extgstate_int

\cs_new_protected:Nn \__reportaman_set_pdf_page_resources:
  {
    \clist_remove_duplicates:N \g__reportaman_ca_clist
    \clist_remove_duplicates:N \g__reportaman_bm_clist
    \tl_clear:N \l_tmpa_tl
    \clist_map_inline:Nn \g__reportaman_ca_clist
      {
        \tl_put_right:Nn \l_tmpa_tl
          {
            /reportaman_CA_##1 ~ << ~ /CA ~ ##1 ~ >> ~
            /reportaman_ca_##1 ~ << ~ /ca ~ ##1 ~ >> ~
          }
      }
    \clist_map_inline:Nn \g__reportaman_bm_clist
      {
        \tl_put_right:Nn \l_tmpa_tl
          {
            /reportaman_bm_##1 ~ << ~ /BM ~ [ ~ /##1 ~ ] ~ >> ~
          }
      }
    \clist_gclear:N \g__reportaman_ca_clist
    \clist_gclear:N \g__reportaman_bm_clist
    \exp_args:Nx \pdf_object_if_exist:nT
      {
        reportaman_extgstate_ \int_use:N \g__reportaman_extgstate_int
      }
      {
        \pdf_object_write:xx
          { reportaman_extgstate_ \int_use:N \g__reportaman_extgstate_int }
          { \l_tmpa_tl }
        \__reportaman_pdf_pageresources_gput_right:nx
          { ExtGState }
          { 
            \pdf_object_ref:e
              {
                reportaman_extgstate_ \int_use:N \g__reportaman_extgstate_int
              }
          }
        \int_gincr:N \g__reportaman_extgstate_int
      }
  }

\EveryShipout { \__reportaman_set_pdf_page_resources: }

\keys_define:nn { reportaman }
{
  opacity      .tl_set:N           = \l__reportaman_opacity_tl,
  opacity      .value_required:n   = true,
  blend ~ mode .choice:,
  blend ~ mode .choices:nn         =
    { Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge,
      ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation,
      Color, Luminosity }
    { \tl_set_eq:NN \l__reportaman_blend_mode_tl \l_keys_choice_tl },
  blend ~ mode .value_required:n   = true,
}


% grouped transparency front end
% \begin{transparencygroup}[
%   opacity = < Value between 0 and 1 >,
%   blend mode = < One of the standard PDF blend modes >
% ]
% \end{transparencygroup}
\NewDocumentEnvironment { transparentgroup }
  { o +b }
  {
    \keys_set:nn { reportaman }
      {
        opacity = { 1 },
        blend ~ mode = { Normal }
      }
    \IfValueT { #1 }
      {
        \keys_set:nn { reportaman } { #1 }
      }
    \clist_gput_right:Nx \g__reportaman_ca_clist { \l__reportaman_opacity_tl }
    \clist_gput_right:Nx \g__reportaman_bm_clist { \l__reportaman_blend_mode_tl }
    \vbox_set:Nn \l_tmpa_box
      {
        \__reportaman_pdf_literal_direct:x
          {
            /reportaman_bm_\l__reportaman_blend_mode_tl \c_space_tl gs
          }
        #2
      }
    \exp_args:Nx \pdf_object_if_exist:nF
      {
        reportaman_extgstate_ \int_use:N \g__reportaman_extgstate_int
      }
      {
        \pdf_object_new:xn
          { reportaman_extgstate_ \int_use:N \g__reportaman_extgstate_int }
          { dict }
      }
    \__reportaman_pdf_xform_now:Nxx
      \l_tmpa_box
      { /Group ~ << ~ /S ~ /Transparency ~ /I ~ true ~ /K ~ false ~ >> }
      { /ExtGState ~
        \pdf_object_ref:e
        {
          reportaman_extgstate_ \int_use:N \g__reportaman_extgstate_int
        }
      }
    \__reportaman_pdf_save_gs:
    \__reportaman_pdf_literal_direct:x
      {
        /reportaman_CA_\l__reportaman_opacity_tl \c_space_tl gs \iow_newline:
        /reportaman_ca_\l__reportaman_opacity_tl \c_space_tl gs
      }
    \__reportaman_pdf_refxform_last:
    \__reportaman_pdf_restore_gs:
  }
  {
  }

\ExplSyntaxOff


% Usage example

\usepackage{xcolor}
\usepackage{fontspec}
\newfontfamily{\devanagari}{Noto Sans Devanagari}[Script=Devanagari, Scale=1, Renderer=HarfBuzz]

\begin{document}
\begin{transparentgroup}[opacity=0.25, blend mode=Hue]
  \devanagari\Huge
  \color{red}एक गांव में पिताजी

  \vspace{-7mm}\quad
  \color{green}एक गांव में पिताजी
\end{transparentgroup}

\begin{transparentgroup}[opacity=0.5]
  \devanagari\Huge
  \color{red}एक गांव में पिताजी

  \vspace{-7mm}\quad
  \color{blue}एक गांव में पिताजी
\end{transparentgroup}

\begin{transparentgroup}
  \devanagari\Huge
  \color{red}एक गांव में पिताजी

  \vspace{-7mm}\quad
  \color{yellow}एक गांव में पिताजी
\end{transparentgroup}
\end{document}

输出

相关内容