如何使用TallComponents PDFKit获取PDF中形状的绝对位置

本文关键字:位置 TallComponents 何使用 PDFKit 获取 PDF | 更新日期: 2023-09-27 18:25:59

我正试图使用TallComponents PDFKit 4来识别PDF中特定形状的位置,这样我就可以相对于它们定位其他形状。(即,识别图像下方10 pt,右侧40 pt)

PDFKit中的每个Shape都具有XY坐标属性,但坐标系的原点似乎会根据嵌套的形状而变化。

如何使用TallComponents PDFKit获取PDF中形状的绝对位置

以下代码将第一页上的所有图形提取为形状:

Stream stream;
... // initialize stream
Document document = new Document(stream);
ShapeCollection shapes = document.Pages[0].CreateShapes();

集合中的形状都继承自类ContentShape。专业包括TextshapeFreeHandShapeImagesShapeShapeCollection。后者允许嵌套。每个形状的坐标系由包含的ShapeCollection的坐标系乘以其自身的ContentShape.Transform属性来定义。

因此,为了计算绝对坐标(或设备坐标),您需要将从根到实际形状的所有变换连接起来。

以下代码将路径的坐标写入输出:

Matrix transform = new Matrix(); // start with identity
write(shapes, transform);
void write(Shape shape, Matrix transform)
{
   if (shape is TallComponents.PDF.Shapes.ContentShape)
   {
      ContentShape contentshape = (ContentShape)shape;
      // calculate the transformation matrix of the user space
      Matrix newTransform = contentshape.Transform.CreateGdiMatrix();
      newTransform.Multiply(transform, MatrixOrder.Append);
      if (shape is FreeHandShape)
      {
         write((FreeHandShape)shape, newTransform);
      }
      else if (shape is ImageShape)
      {
         write((ImageShape)shape, newTransform);
      }
      else if (shape is ShapeCollection)
      {
        // recurse
        ShapeCollection shapes = (ShapeCollection)shape;
        foreach (Shape s in shapes)
           write(s, newTransform);
      }
      // ignore other shapes
   }
}

以下方法使用绝对坐标以类似SVG的格式将FreeHandShape的所有点写入输出:

void write(FreeHandShape freeHandShape, Matrix transform)
{
   write(freeHandShape.Paths, transform);
}
void write(FreeHandPathCollection paths, Matrix transform)
{
   foreach (var path in paths)
      write(path, transform);
}
void write(FreeHandPath path, Matrix transform)
{
   foreach (var segment in path.Segments)
   {
      if (segment is FreeHandStartSegment)
      {
         var s = (FreeHandStartSegment)segment;
         Console.Write("M {0} ", write(s.X, s.Y, transform));
      }
      else if (segment is FreeHandLineSegment)
      {
         var s = (FreeHandLineSegment)segment;
         Console.Write("L {0} ", write(s.X1, s.Y1, transform));
      }
      else if (segment is FreeHandBezierSegment)
      {
         var s = (FreeHandBezierSegment)segment;
         Console.Write("C {0} {1} {2} ",
            write(s.X1, s.Y1, transform),
            write(s.X2, s.Y2, transform),
            write(s.X3, s.Y3, transform));
         }
      }
      if (path.Closed)
         Console.Write("Z ");
   }
}
string write(double x, double y, Matrix transform)
{
   PointF[] points = { new PointF((float)x, (float)y) };
   transform.TransformPoints(points);
   return String.Format("{0} {1}", points[0].X, points[0].Y);
}

更新:

以下方法使用绝对坐标以类似SVG的格式将ImageShape写入输出。请注意,它将图像绘制为红色矩形,因为它只显示角的位置。还要注意,它将(0, 0)转换为左下角,而不是(X, Y)。原因是属性ContentShape.XContentShape.Y是只读属性,它们返回转换后的位置。因此,如果我们也对(X, Y)进行变换,它将被变换两次。

  private void writeImageShape(ImageShape image, Matrix transform)
  {
     Console.Write("M {0} ", writePoint(0, 0, transform));
     Console.Write("L {0} ", writePoint(0, image.Height, transform));
     Console.Write("L {0} ", writePoint(image.Width, image.Height, transform));
     Console.Write("L {0} ", writePoint(image.Width, 0, transform));
     Console.Write("Z");
  }

为了简化起见,此代码不考虑定义初始转换的页面的"旋转"answers"裁剪框"属性。下面是一个工作代码示例,它使用绝对(设备)坐标将所有路径和图像(作为矩形)转换为SVG。