如何解决Itext 7 文本拟合的更好解决方案
我有一个使用 Itext 5 并按预期工作的项目。 程序必须将 userInput 放在段落内的某些“块”中。段落每行有不可移动的(块)词,并且 userInput 应该在为段落内的 userInput 保留的空间中缩放。 旧项目有以下代码(作为示例)
public class Oldway {
static final transient Font bold2 = FontFactory.getFont("Times-Roman",10.0f,1);
public static void main(String[] args) {
Document document = new Document();
document.setPageSize(PageSize.A4);
try {
PdfWriter writer = PdfWriter.getInstance(document,new FileOutputStream(new File("itext5.pdf")));
document.open();
Paragraph title = new Paragraph("Title of doc");
title.setAlignment(1);
document.add(title);
Paragraph dec= new Paragraph();
Chunk ch01 = new Chunk("Prev text ");
dec.add(ch01);
Chunk ch02 = new Chunk(getEmptySpace(42));
dec.add(ch02);
Chunk ch03 = new Chunk(" next Text");
dec.add(ch03);
document.add(dec);
float y = writer.getVerticalPosition(false);
float x2 = document.left() + ch01.getWidthPoint();
float x3 = x2 + ch02.getWidthPoint();
getPlainFillTest("Text to insert",document,y,x3,x2,writer,false);
document.close();
writer.flush();
} catch (FileNotFoundException | DocumentException e) {
// Todo Auto-generated catch block
e.printstacktrace();
}
}
public static Chunk getEmptySpace(int size) {
Chunk ch = new Chunk();
for(int i = 0;i<=size;i++) {
ch.append("\u00a0");
}
return new Chunk(ch);
}
public static void getPlainFillTest(String str,Document document,float y,float x1pos,float x2pos,PdfWriter writer,boolean withTab) {
if(str.isEmpty() || str.isBlank()) {
str = "________";
}
Rectangle rec2 = null;
if(!withTab)
rec2 = new Rectangle(x2pos,x1pos-2,y+10);
else {
rec2 = new Rectangle(x2pos+35,x1pos+33,y+10);
}
BaseFont bf = bold2.getBaseFont();
PdfContentByte cb = writer.getDirectContent();
float fontSize = getMaxFontSize(bf,str,(int)rec2.getWidth(),(int)rec2.getHeight());
Phrase phrase = new Phrase(str,new Font(bf,fontSize));
ColumnText.showtextAligned(cb,Element.ALIGN_CENTER,phrase,// center horizontally
(rec2.getLeft() + rec2.getRight()) / 2,// shift baseline based on descent
rec2.getBottom() - bf.getDescentPoint(str,fontSize),0);
cb.saveState();//patrulaterul albastru
cb.setColorstroke(Color.BLUE);
cb.rectangle(rec2.getLeft(),rec2.getBottom(),rec2.getWidth(),rec2.getHeight());
cb.stroke();
cb.restoreState();
}
//stackoverflow solution
private static float getMaxFontSize(BaseFont bf,String text,int width,int height){
// avoid infinite loop when text is empty
if(text.isEmpty()){
return 0.0f;
}
float fontSize = 0.1f;
while(bf.getWidthPoint(text,fontSize) < width){
fontSize += 0.1f;
}
float maxHeight = measureHeight(bf,text,fontSize);
while(maxHeight > height){
fontSize -= 0.1f;
maxHeight = measureHeight(bf,fontSize);
};
return fontSize;
}
public static float measureHeight(BaseFont baseFont,float fontSize)
{
float ascend = baseFont.getAscentPoint(text,fontSize);
float descend = baseFont.getDescentPoint(text,fontSize);
return ascend - descend;
}}
现在我正在尝试在 IText 7 中做同样的事情……这并不容易! 我设法创建了一个工作代码,但它很混乱,有些东西没有得到正确的坐标。 Itext7 代码(以示例为例):
public class Newway {
public static void main(String[] args) {
PdfWriter writer;
try {
writer = new PdfWriter(new File("test2.pdf"));
PdfDocument document = new PdfDocument(writer);
document.getDocumentInfo().addCreationDate();
document.getDocumentInfo().setTitle("Title");
document.setDefaultPageSize(PageSize.A4);
Document doc = new Document(document);
doc.setFontSize(12);
Paragraph par = new Paragraph();
Text ch01 = new Text("Prev Text ");
par.add(ch01);
Paragraph space = new Paragraph();
space.setMaxWidth(40);
for(int i=0;i<40;i++) {
par.add("\u00a0");
space.add("\u00a0");
}
Text ch02 = new Text(" next text");
par.add(ch02);
doc.add(par);
Paragraph linePara = new Paragraph().add("Test from UserInput")
.setTextAlignment(TextAlignment.CENTER).setBorder(new DottedBorder(1));
float width = doc.getPageEffectiveArea(PageSize.A4).getWidth();
float height = doc.getPageEffectiveArea(PageSize.A4).getHeight();
IRenderer primul = ch01.createRendererSubTree().setParent(doc.getRenderer());
IRenderer spaceR = space.createRendererSubTree().setParent(doc.getRenderer());
LayoutResult primulResult = primul.layout(new LayoutContext(new LayoutArea(1,new Rectangle(width,height))));
LayoutResult layoutResult = spaceR.layout(new LayoutContext(new LayoutArea(1,height))));
Rectangle primulBox = ((TextRenderer) primul).getInnerAreaBBox();
Rectangle rect = ((ParagraphRenderer) spaceR).getInnerAreaBBox();
float rwidth = rect.getWidth();
float rheight = rect.getHeight();
float x = primulBox.getWidth()+ doc.getLeftMargin();
float y = rect.getY()+(rheight*2.05f);//rect.getY() is never accurate,is always below the paragraph. WHY ??
Rectangle towr = new Rectangle(x,rwidth,rheight*1.12f);//rheight on default is way too small
PdfCanvas pdfcanvas = new PdfCanvas(document.getFirstPage());
Canvas canvas = new Canvas(pdfcanvas,towr);
//from theinternet
float fontSizeL = 1;
float fontSizeR = 14;
while (Math.abs(fontSizeL - fontSizeR) > 1e-1) {
float curFontSize = (fontSizeL + fontSizeR) / 2;
linePara.setFontSize(curFontSize);
// It is important to set parent for the current element renderer to a root renderer
IRenderer renderer = linePara.createRendererSubTree().setParent(canvas.getRenderer());
LayoutContext context = new LayoutContext(new LayoutArea(1,towr));
if (renderer.layout(context).getStatus() == LayoutResult.FULL) {
// we can fit all the text with curFontSize
fontSizeL = curFontSize;
} else {
fontSizeR = curFontSize;
}
}
canvas.add(linePara);
new PdfCanvas(document.getFirstPage()).rectangle(towr).setstrokeColor(ColorConstants.BLACK).stroke();
canvas.close();
doc.close();
writer.flush();
} catch (IOException e) {
// Todo Auto-generated catch block
e.printstacktrace();
}
}}
问题是
- 有没有更好、更优雅的方法来做到这一点?
- 为什么 rect.getY() 总是在段落下方?以及如何让 Y 与 Paragraph real Y 坐标相匹配?
- 为什么默认的“rheight”总是太小?但是 (rheight*1.1f) 有效吗?
- (可选)如何在 IText 7 中设置 tab() 空间大小?
解决方法
这种方式非常好,因为它考虑了所有可能的模型元素设置和布局过程的影响。您的 iText 5 替代方案对于基于拉丁语的文本的基本情况来说已经足够好了,而无需在视觉方面进行任何修改。您拥有的 iText 7 代码更加灵活,如果您使用更复杂的布局设置、复杂脚本等,它仍然可以工作。此外,我看到您的示例中 iText 5 代码为 105 行,而 iText 7 代码为 80 行。>
-
你在这里添加了一些魔法
+(rheight*2.05f);
而实际上你在这里缺少的是当你通过Canvas
绘制时你不再定义你的边距,所以你真正需要而不是rect.getY()+(rheight*2.05f);
是rect.getY() + doc.getBottomMargin()
-
问题在于您将
rheight
计算为renderer.getInnerAreaBBox()
而此计算并未考虑应用于段落的默认边距。边距包含在占用区域中,但不包含在内部区域 bbox 中。要解决此问题,请改用renderer.getOccupiedArea().getBBox()
。在这种情况下,无需再将rheight
乘以系数。
现在的视觉效果略有不同,但不再有魔法常数。根据您想要真正实现的目标,您可以进一步调整代码(在这里和那里添加一些边距等)。但是代码很好地适应了用户文本的变化。
之前的视觉效果:
视觉效果:
结果代码:
PdfWriter writer;
try {
writer = new PdfWriter(new File("test2.pdf"));
PdfDocument document = new PdfDocument(writer);
document.getDocumentInfo().addCreationDate();
document.getDocumentInfo().setTitle("Title");
document.setDefaultPageSize(PageSize.A4);
Document doc = new Document(document);
doc.setFontSize(12);
Paragraph par = new Paragraph();
Text ch01 = new Text("Prev Text ");
par.add(ch01);
Paragraph space = new Paragraph();
space.setMaxWidth(40);
for(int i=0;i<40;i++) {
par.add("\u00a0");
space.add("\u00a0");
}
Text ch02 = new Text(" next text");
par.add(ch02);
doc.add(par);
Paragraph linePara = new Paragraph().add("Test from UserInput")
.setTextAlignment(TextAlignment.CENTER).setBorder(new DottedBorder(1));
float width = doc.getPageEffectiveArea(PageSize.A4).getWidth();
float height = doc.getPageEffectiveArea(PageSize.A4).getHeight();
IRenderer primul = ch01.createRendererSubTree().setParent(doc.getRenderer());
IRenderer spaceR = space.createRendererSubTree().setParent(doc.getRenderer());
LayoutResult primulResult = primul.layout(new LayoutContext(new LayoutArea(1,new Rectangle(width,height))));
LayoutResult layoutResult = spaceR.layout(new LayoutContext(new LayoutArea(1,height))));
Rectangle primulBox = ((TextRenderer) primul).getInnerAreaBBox();
Rectangle rect = ((ParagraphRenderer) spaceR).getOccupiedArea().getBBox();
float rwidth = rect.getWidth();
float rheight = rect.getHeight();
float x = primulBox.getWidth()+ doc.getLeftMargin();
float y = rect.getY() + doc.getBottomMargin();
Rectangle towr = new Rectangle(x,y,rwidth,rheight);
PdfCanvas pdfcanvas = new PdfCanvas(document.getFirstPage());
Canvas canvas = new Canvas(pdfcanvas,towr);
//from theinternet
float fontSizeL = 1;
float fontSizeR = 14;
while (Math.abs(fontSizeL - fontSizeR) > 1e-1) {
float curFontSize = (fontSizeL + fontSizeR) / 2;
linePara.setFontSize(curFontSize);
// It is important to set parent for the current element renderer to a root renderer
IRenderer renderer = linePara.createRendererSubTree().setParent(canvas.getRenderer());
LayoutContext context = new LayoutContext(new LayoutArea(1,towr));
if (renderer.layout(context).getStatus() == LayoutResult.FULL) {
// we can fit all the text with curFontSize
fontSizeL = curFontSize;
} else {
fontSizeR = curFontSize;
}
}
canvas.add(linePara);
new PdfCanvas(document.getFirstPage()).rectangle(towr).setStrokeColor(ColorConstants.BLACK).stroke();
canvas.close();
doc.close();
writer.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。