具有图形文本效果的文本元素

Text element with graphical text effects

我想创建 Text 个元素 (com.itextpdf.layout.element.Text) 具有不同类型的附加图形效果,例如投影或在字形上添加某种纹理。 (参见 DropShadow some 3D Effect) 实现此目标的最佳方法是什么?

到目前为止,我的最佳想法是使用裁剪文本渲染模式。 (在 PDF 32000-1 9.3.6 中定义;com.itextpdf.kernel.pdf.canvas.PdfCanvasConstants.TextRenderingMode)。将文本绘制为剪裁边界并应用某种纹理或绘制额外的阴影 "layer"。但是剪切路径恢复到canvas.restoreState()绘制文字前的状态,最后在com.itextpdf.layout.renderer.TextRender#draw中调用。将其扩展到自定义 TextRenderer 可以工作,但绘图函数很大,需要调用 TextRenderer.

的私有函数

对其他可能的方法有什么建议吗?

我认为一般来说,该级别的自定义无论如何都需要相当多的代码。完全覆盖 draw 可能确实行不通,因为一些私有实现细节没有暴露给 public。一种选择当然是将这些实现细节复制到您的自定义渲染器中。

另一个想法是插入执行低级绘图的 PdfCanvas。您可以像下面这样创建自己的包装器,并将所有操作委托给您包装的 PdfCanvas 实例,除了几个 "interesting" 操作,您将在其中自定义逻辑并应用一些样式:

private static class PdfCanvasWrapper extends PdfCanvas {
    private PdfCanvas delegate;
    public PdfCanvasWrapper(PdfCanvas wrapped) {
        super(wrapped.getContentStream(), wrapped.getResources(), wrapped.getDocument());
        this.delegate = wrapped;
    }

    // "Interesting" methods
    @Override
    public PdfCanvas endText() {
        delegate.endText();
        delegate.setFillColor(ColorConstants.BLACK);
        delegate.rectangle(10, 10, 300, 300);
        delegate.fill();
        return this;
    }

    // "Boring" methods - just delegate the implementation to the wrapped instance
    @Override
    public PdfCanvas beginVariableText() {
        delegate.beginVariableText();
        return this;
    }

    @Override
    public PdfCanvas endVariableText() {
        delegate.endVariableText();
        return this;
    }

    // Override all other members like above
}

在这种情况下,您的自定义文本渲染器将只插入正确的 DrawContext 但使用默认的 draw 操作:

private static class CustomTextRenderer extends TextRenderer {
    public CustomTextRenderer(Text textElement) {
        super(textElement);
    }

    @Override
    public void draw(DrawContext drawContext) {
        DrawContext newContext = new DrawContext(drawContext.getDocument(), new PdfCanvasWrapper(drawContext.getCanvas()));
        super.draw(newContext);
    }

    @Override
    public CustomTextRenderer getNextRenderer() {
        return new CustomTextRenderer((Text) modelElement);
    }
}

主要内容可能如下所示:

Paragraph p = new Paragraph();
Text text = new Text("Hello");
text.setTextRenderingMode(TextRenderingMode.CLIP);
text.setNextRenderer(new CustomTextRenderer(text));
p.add(text);

总的来说,这种方法也很老套,当然取决于实现细节,就像您建议的初始方法一样。您建议的方法是一种更稳定的方法,但在更新到新版本的库时需要更多代码并且可能需要更多调整。我上面描述的方法更 hacky,但它导致更少的业务逻辑复制粘贴,并且可能更容易维护。