具有图形文本效果的文本元素
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,但它导致更少的业务逻辑复制粘贴,并且可能更容易维护。
我想创建 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,但它导致更少的业务逻辑复制粘贴,并且可能更容易维护。