[英]How to get page content height using pdfbox
是否可以使用pdfbox獲取頁面內容的高度? 我想我嘗試了所有方法,但是每個(PDRectangle)返回頁面的完整高度:842。首先,我認為這是因為頁碼位於頁面底部,但是當我在Illustrator中打開pdf時,整個內容都在里面復合元素,並且不會擴展到整個頁面高度。 因此,如果插畫家可以將其視為單獨的元素並計算其高度,我想這在pdfbox中也應該可行。
樣本頁面:
PDF規范允許PDF提供許多頁面邊界,請參見此答案 。 除了它們,內容邊界只能從頁面內容中得出,例如
表格XObjects:
XObject表單是PDF內容流,它是對圖形對象(包括路徑對象,文本對象和采樣圖像)任何序列的獨立描述。 表單XObject可以多次繪制(在多個頁面上或在同一頁面上的多個位置),並且每次生成相同的結果,但僅在調用它時處於圖形狀態。
剪切路徑:
圖形狀態應包含當前的剪切路徑 ,該路徑限制了受繪畫操作員影響的頁面區域。 該路徑的封閉子路徑應定義可以繪制的區域。 落在該區域內的標記應粘貼到頁面上; 那些不在它外面的人不會。
...
要找到它們之一,必須解析頁面內容,尋找適當的操作,然后計算結果邊界。
您的每個樣本PDF都僅明確定義了一個頁面邊界MediaBox 。 因此,所有其他的PDF頁面的邊界( 裁剪框 , 出血框 , 裁切框 , 作品框 ),默認情況下它的。 因此,難怪您嘗試
每個(PDRectangle)返回頁面的完整高度:842
它們都不包含XObjects形式,但是都使用剪切路徑。
如果是test-pdf4.pdf:
Start at: 28.31999969482422, 813.6799926757812 Line to: 565.9199829101562, 813.6799926757812 Line to: 565.9199829101562, 660.2196655273438 Line to: 28.31999969482422, 660.2196655273438 Line to: 28.31999969482422, 813.6799926757812
(這可能與您的問題中的草圖匹配。)
如果是test-pdf5.pdf:
Start at: 23.0, 34.0 Line to: 572.0, 34.0 Line to: 572.0, -751.0 Line to: 23.0, -751.0 Line to: 23.0, 34.0
和
Start at: 23.0, 819.0 Line to: 572.0, 819.0 Line to: 572.0, 34.0 Line to: 23.0, 34.0 Line to: 23.0, 819.0
由於與草圖的匹配,我假設Illustrator會在有效的非平凡剪切路徑生效時考慮所有繪制的內容,這是一個以剪切路徑為邊界的復合元素 。
我使用PDFBox查找上面提到的剪切路徑。 我使用了目前正在開發的2.0.0版本的當前SNAPSHOT,因為與當前版本1.8.8相比,所需的API已有了很大的改進。
我將PDFGraphicsStreamEngine
擴展到ClipPathFinder
類:
public class ClipPathFinder extends PDFGraphicsStreamEngine implements Iterable<Path>
{
public ClipPathFinder(PDPage page)
{
super(page);
}
//
// PDFGraphicsStreamEngine overrides
//
public void findClipPaths() throws IOException
{
processPage(getPage());
}
@Override
public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException
{
startPathIfNecessary();
currentPath.appendRectangle(toFloat(p0), toFloat(p1), toFloat(p2), toFloat(p3));
}
@Override
public void drawImage(PDImage pdImage) throws IOException { }
@Override
public void clip(int windingRule) throws IOException
{
currentPath.complete(windingRule);
paths.add(currentPath);
currentPath = null;
}
@Override
public void moveTo(float x, float y) throws IOException
{
startPathIfNecessary();
currentPath.moveTo(x, y);
}
@Override
public void lineTo(float x, float y) throws IOException
{
currentPath.lineTo(x, y);
}
@Override
public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException
{
currentPath.curveTo(x1, y1, x2, y2, x3, y3);
}
@Override
public Point2D.Float getCurrentPoint() throws IOException
{
return currentPath.getCurrentPoint();
}
@Override
public void closePath() throws IOException
{
currentPath.closePath();
}
@Override
public void endPath() throws IOException
{
currentPath = null;
}
@Override
public void strokePath() throws IOException
{
currentPath = null;
}
@Override
public void fillPath(int windingRule) throws IOException
{
currentPath = null;
}
@Override
public void fillAndStrokePath(int windingRule) throws IOException
{
currentPath = null;
}
@Override
public void shadingFill(COSName shadingName) throws IOException
{
currentPath = null;
}
void startPathIfNecessary()
{
if (currentPath == null)
currentPath = new Path();
}
Point2D.Float toFloat(Point2D p)
{
if (p == null || (p instanceof Point2D.Float))
{
return (Point2D.Float)p;
}
return new Point2D.Float((float)p.getX(), (float)p.getY());
}
//
// Iterable<Path> implementation
//
public Iterator<Path> iterator()
{
return paths.iterator();
}
Path currentPath = null;
final List<Path> paths = new ArrayList<Path>();
}
它使用此幫助器類來表示路徑:
public class Path implements Iterable<Path.SubPath>
{
public static class Segment
{
Segment(Point2D.Float start, Point2D.Float end)
{
this.start = start;
this.end = end;
}
public Point2D.Float getStart()
{
return start;
}
public Point2D.Float getEnd()
{
return end;
}
final Point2D.Float start, end;
}
public class SubPath implements Iterable<Segment>
{
public class Line extends Segment
{
Line(Point2D.Float start, Point2D.Float end)
{
super(start, end);
}
//
// Object override
//
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append(" Line to: ")
.append(end.getX())
.append(", ")
.append(end.getY())
.append('\n');
return builder.toString();
}
}
public class Curve extends Segment
{
Curve(Point2D.Float start, Point2D.Float control1, Point2D.Float control2, Point2D.Float end)
{
super(start, end);
this.control1 = control1;
this.control2 = control2;
}
public Point2D getControl1()
{
return control1;
}
public Point2D getControl2()
{
return control2;
}
//
// Object override
//
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append(" Curve to: ")
.append(end.getX())
.append(", ")
.append(end.getY())
.append(" with Control1: ")
.append(control1.getX())
.append(", ")
.append(control1.getY())
.append(" and Control2: ")
.append(control2.getX())
.append(", ")
.append(control2.getY())
.append('\n');
return builder.toString();
}
final Point2D control1, control2;
}
SubPath(Point2D.Float start)
{
this.start = start;
currentPoint = start;
}
public Point2D getStart()
{
return start;
}
void lineTo(float x, float y)
{
Point2D.Float end = new Point2D.Float(x, y);
segments.add(new Line(currentPoint, end));
currentPoint = end;
}
void curveTo(float x1, float y1, float x2, float y2, float x3, float y3)
{
Point2D.Float control1 = new Point2D.Float(x1, y1);
Point2D.Float control2 = new Point2D.Float(x2, y2);
Point2D.Float end = new Point2D.Float(x3, y3);
segments.add(new Curve(currentPoint, control1, control2, end));
currentPoint = end;
}
void closePath()
{
closed = true;
currentPoint = start;
}
//
// Iterable<Segment> implementation
//
public Iterator<Segment> iterator()
{
return segments.iterator();
}
//
// Object override
//
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append(" {\n Start at: ")
.append(start.getX())
.append(", ")
.append(start.getY())
.append('\n');
for (Segment segment : segments)
builder.append(segment);
if (closed)
builder.append(" Closed\n");
builder.append(" }\n");
return builder.toString();
}
boolean closed = false;
final Point2D.Float start;
final List<Segment> segments = new ArrayList<Path.Segment>();
}
public class Rectangle extends SubPath
{
Rectangle(Point2D.Float p0, Point2D.Float p1, Point2D.Float p2, Point2D.Float p3)
{
super(p0);
lineTo((float)p1.getX(), (float)p1.getY());
lineTo((float)p2.getX(), (float)p2.getY());
lineTo((float)p3.getX(), (float)p3.getY());
closePath();
}
//
// Object override
//
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append(" {\n Rectangle\n Start at: ")
.append(start.getX())
.append(", ")
.append(start.getY())
.append('\n');
for (Segment segment : segments)
builder.append(segment);
if (closed)
builder.append(" Closed\n");
builder.append(" }\n");
return builder.toString();
}
}
public int getWindingRule()
{
return windingRule;
}
void complete(int windingRule)
{
finishSubPath();
this.windingRule = windingRule;
}
void appendRectangle(Point2D.Float p0, Point2D.Float p1, Point2D.Float p2, Point2D.Float p3) throws IOException
{
finishSubPath();
currentSubPath = new Rectangle(p0, p1, p2, p3);
finishSubPath();
}
void moveTo(float x, float y) throws IOException
{
finishSubPath();
currentSubPath = new SubPath(new Point2D.Float(x, y));
}
void lineTo(float x, float y) throws IOException
{
currentSubPath.lineTo(x, y);
}
void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException
{
currentSubPath.curveTo(x1, y1, x2, y2, x3, y3);
}
Point2D.Float getCurrentPoint() throws IOException
{
return currentPoint;
}
void closePath() throws IOException
{
currentSubPath.closePath();
finishSubPath();
}
void finishSubPath()
{
if (currentSubPath != null)
{
subPaths.add(currentSubPath);
currentSubPath = null;
}
}
//
// Iterable<Path.SubPath> implementation
//
public Iterator<SubPath> iterator()
{
return subPaths.iterator();
}
//
// Object override
//
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("{\n Winding: ")
.append(windingRule)
.append('\n');
for (SubPath subPath : subPaths)
builder.append(subPath);
builder.append("}\n");
return builder.toString();
}
Point2D.Float currentPoint = null;
SubPath currentSubPath = null;
int windingRule = -1;
final List<SubPath> subPaths = new ArrayList<Path.SubPath>();
}
類ClipPathFinder
的用法如下:
PDDocument document = PDDocument.load(PDFRESOURCE, null);
PDPage page = document.getPage(PAGENUMBER);
ClipPathFinder finder = new ClipPathFinder(page);
finder.findClipPaths();
for (Path path : finder)
{
System.out.println(path);
}
document.close();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.