英文:
How to use PDF4NET to convert PDFPathVisualObject to System.Windows.Shapes.Path for wpf
问题
我正在尝试使用PDF4NET从PDF文件中提取路径元素并在WPF中显示它们。
我参考了PDF4NET的示例,并通过以下代码获取了PDFPathVisualObject。
PDFFixedDocument document = new PDFFixedDocument("path.pdf");
PDFContentExtractor ce = new PDFContentExtractor(document.Pages[0]);
PDFVisualObjectCollection voc = ce.ExtractVisualObjects(false);
PDFPathVisualObject pvo = voc[0] as PDFPathVisualObject;
然而,在将PDFPathVisualObject转换为System.Windows.Shapes.Path时,PathItems中显示的路径信息似乎与实际路径信息不一致,如下图所示(我随机找到了一个包含路径信息的PDF)。
我想知道如何将PDFPathVisualObject转换为System.Windows.Shapes.Path。
英文:
I am trying to extract path elements from PDF files using PDF4NET and display them in WPF
I referenced the example of PDF4NET and obtained the PDFPathVisualObject through the following code.
PDFFixedDocument document = new PDFFixedDocument("path.pdf");
PDFContentExtractor ce = new PDFContentExtractor(document.Pages[0]);
PDFVisualObjectCollection voc = ce.ExtractVisualObjects(false);
PDFPathVisualObject pvo = voc[0] as PDFPathVisualObject
However, I encountered a problem when converting the PDFPathVisualObject to System.Windows.Shapes.Path. The path information displayed in the PathItems in PDFPathVisualObject seems to be inconsistent with the actual path information, As shown in the following figure (I randomly found a PDF with path information)
I would like to know how to convert PDFPathVisualObject to System. Windows. Shapes. Path
答案1
得分: 2
以下是翻译好的内容:
第1张截图(PDFXplorer应用程序)中的值是标准PDF坐标系统中的原始路径点。
第2张截图中的点是显示坐标系统中的路径点,相对于可见页面区域的左上角。原始路径点通过在绘制路径时处于活动状态的当前变换矩阵进行变换,然后从标准PDF坐标系统转换为显示坐标系统,考虑页面mediabox/cropbox和页面旋转。
您可以直接使用第2张截图中的路径点来创建您的WPF路径,因为它们使用相同的坐标系统。
下面的代码提供了一个起点,它展示了如何将PDFPathVisualObject
转换为XAML路径:
private void ConvertPDFPathsToXAML()
{
PDFFixedDocument document = new PDFFixedDocument("paths.pdf");
PDFContentExtractor ce = new PDFContentExtractor(document.Pages[0]);
PDFVisualObjectCollection voc = ce.ExtractVisualObjects(false, false);
// ...(此处省略了代码的其余部分)
}
private string PdfPathToXamlPathGeometry(PDFPathVisualObject path, bool isFilled, PDFFillMode fillMode)
{
// ...(此处省略了代码的其余部分)
}
被转换为以下XAML:
<Canvas xmlns="http://schemas.microsoft.com/xps/2005/06" Width="612" Height="792" Background="White">
<Canvas.Clip>
<RectangleGeometry Rect="0,0,612,792"/>
</Canvas.Clip>
<Path Data="M 0 792 L 612 0 " Stroke="#FF0080" StrokeThickness="16"/>
<Path Data="M 0 396 L 612 396 " Stroke="#000080" StrokeThickness="16"/>
</Canvas>
注:我为开发PDF4NET的公司工作。
英文:
The values from the 1st screenshot (PDFXplorer application) are raw path points in standard PDF coordinate system.
The points in 2nd screenshot are the path points in display coordinate system relative to top left corner of the visible page area. The raw path points are transformed through the current transformation matrix that is active when the path is painted and then converted from standard PDF coordinate system to display coordinate system considering the page mediabox/cropbox and page rotation.
You can use directly the path points in the 2nd screenshot to create your WPF paths as they use the same coordinate system.
The code below provides a start, it shows how to convert the PDFPathVisualObject
to XAML paths:
private void ConvertPDFPathsToXAML()
{
PDFFixedDocument document = new PDFFixedDocument("paths.pdf");
PDFContentExtractor ce = new PDFContentExtractor(document.Pages[0]);
PDFVisualObjectCollection voc = ce.ExtractVisualObjects(false, false);
MemoryStream xamlStream = new MemoryStream();
XmlTextWriter xamlWriter = new XmlTextWriter(xamlStream, Encoding.UTF8);
xamlWriter.WriteStartElement("Canvas");
xamlWriter.WriteAttributeString("xmlns", "http://schemas.microsoft.com/xps/2005/06");
xamlWriter.WriteAttributeString("Width", string.Format(CultureInfo.InvariantCulture, "{0:0.####}", document.Pages[0].Width));
xamlWriter.WriteAttributeString("Height", string.Format(CultureInfo.InvariantCulture, "{0:0.####}", document.Pages[0].Height));
xamlWriter.WriteAttributeString("Background", "White");
xamlWriter.WriteStartElement("Canvas.Clip");
xamlWriter.WriteStartElement("RectangleGeometry");
xamlWriter.WriteAttributeString("Rect", string.Format(CultureInfo.InvariantCulture, "0,0,{0:0.#####},{1:0.#####}", document.Pages[0].Width, document.Pages[0].Height));
xamlWriter.WriteEndElement();
xamlWriter.WriteEndElement();
for (int i = 0; i < voc.Count; i++)
{
if (voc[i].Type == PDFVisualObjectType.Path)
{
PDFPathVisualObject path = (PDFPathVisualObject)voc[i];
string pathGeometry = PdfPathToXamlPathGeometry(path, path.Brush != null, path.FillMode);
xamlWriter.WriteStartElement("Path");
xamlWriter.WriteAttributeString("Data", pathGeometry);
if (path.Pen != null)
{
PDFRgbColor rgb = path.Pen.Color.ToRgbColor();
xamlWriter.WriteAttributeString("Stroke", string.Format("#{0:X02}{1:X02}{2:X02}", rgb.R, rgb.G, rgb.B));
xamlWriter.WriteAttributeString("StrokeThickness", string.Format("{0:0.####}", path.Pen.Width));
}
if (path.Brush != null)
{
PDFRgbColor rgb = path.Brush.Color.ToRgbColor();
xamlWriter.WriteAttributeString("Fill", string.Format("#{0:X02}{1:X02}{2:X02}", rgb.R, rgb.G, rgb.B));
}
xamlWriter.WriteEndElement();
}
}
xamlWriter.WriteEndElement();
xamlWriter.Flush();
xamlStream.Position = 0;
Canvas canvas = (Canvas)XamlReader.Load(xamlStream);
mainGrid.Children.Add(canvas);
}
private string PdfPathToXamlPathGeometry(PDFPathVisualObject path, bool isFilled, PDFFillMode fillMode)
{
double crtX = 0, crtY = 0;
double x1, y1, x2, y2, x3, y3;
StringBuilder pathGeometry = new StringBuilder();
if (isFilled)
{
pathGeometry.Append(fillMode == PDFFillMode.EvenOdd ? "F0 " : "F1 ");
}
for (int i = 0; i < path.PathItems.Count; i++)
{
switch (path.PathItems[i].Type)
{
case PDFPathItemType.MoveTo: // m
x1 = path.PathItems[i].Points[0].X;
y1 = path.PathItems[i].Points[0].Y;
pathGeometry.AppendFormat(CultureInfo.InvariantCulture, "M {0:0.######} {1:0.######} ", x1, y1);
crtX = x1;
crtY = y1;
break;
case PDFPathItemType.LineTo: // l
x1 = path.PathItems[i].Points[0].X;
y1 = path.PathItems[i].Points[0].Y;
pathGeometry.AppendFormat(CultureInfo.InvariantCulture, "L {0:0.######} {1:0.######} ", x1, y1);
crtX = x1;
crtY = y1;
break;
case PDFPathItemType.CCurveTo: // c
x1 = path.PathItems[i].Points[0].X;
y1 = path.PathItems[i].Points[0].Y;
x2 = path.PathItems[i].Points[1].X;
y2 = path.PathItems[i].Points[1].Y;
x3 = path.PathItems[i].Points[2].X;
y3 = path.PathItems[i].Points[2].Y;
pathGeometry.AppendFormat(CultureInfo.InvariantCulture,
"C {0:0.######} {1:0.######} {2:0.######} {3:0.######} {4:0.######} {5:0.######} ", x1, y1, x2, y2, x3, y3);
crtX = x3;
crtY = y3;
break;
case PDFPathItemType.VCurveTo: // v
x1 = path.PathItems[i].Points[0].X;
y1 = path.PathItems[i].Points[0].Y;
x2 = path.PathItems[i].Points[1].X;
y2 = path.PathItems[i].Points[1].Y;
pathGeometry.AppendFormat(CultureInfo.InvariantCulture,
"C {0:0.######} {1:0.######} {2:0.######} {3:0.######} {4:0.######} {5:0.######} ", crtX, crtY, x1, y1, x2, y2);
crtX = x2;
crtY = y2;
break;
case PDFPathItemType.YCurveTo: // y
x1 = path.PathItems[i].Points[0].X;
y1 = path.PathItems[i].Points[0].Y;
x2 = path.PathItems[i].Points[1].X;
y2 = path.PathItems[i].Points[1].Y;
pathGeometry.AppendFormat(CultureInfo.InvariantCulture,
"C {0:0.######} {1:0.######} {2:0.######} {3:0.######} {4:0.######} {5:0.######} ", x1, y1, x2, y2, x2, y2);
crtX = x2;
crtY = y2;
break;
case PDFPathItemType.CloseSubpath: // h
pathGeometry.Append("z ");
break;
}
}
return pathGeometry.ToString();
}
is converted to this XAML:
<Canvas xmlns="http://schemas.microsoft.com/xps/2005/06" Width="612" Height="792" Background="White">
<Canvas.Clip>
<RectangleGeometry Rect="0,0,612,792"/>
</Canvas.Clip>
<Path Data="M 0 792 L 612 0 " Stroke="#FF0080" StrokeThickness="16"/>
<Path Data="M 0 396 L 612 396 " Stroke="#000080" StrokeThickness="16"/>
</Canvas>
Note: I work for the company that develops PDF4NET.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论