C# 如何在现有PDF中编辑直线、曲线和矢量图形特性?

C# 如何在现有PDF中编辑直线、曲线和矢量图形特性?,c#,pdf,itext,pdf-generation,C#,Pdf,Itext,Pdf Generation,我收集了大量PDF,其中包含矢量图形,主要是直线和曲线,需要以某种方式进行批量编辑,以修改其基本属性,如角点类型和端点类型。这也适用于编辑厚度和颜色 我已经在使用iTextSharp编辑这些PDF,将图像插入每个文件的背景中,但是我没有太多关于曲线和直线的文档,也找不到编辑直线本身的方法。我也对其他库持开放态度,但我还没有找到一个库明确说明如何编辑现有曲线和直线,只绘制新的曲线和直线 using iTextSharp.text; using iTextSharp.text.pdf; // op

我收集了大量PDF,其中包含矢量图形,主要是直线和曲线,需要以某种方式进行批量编辑,以修改其基本属性,如角点类型和端点类型。这也适用于编辑厚度和颜色

我已经在使用iTextSharp编辑这些PDF,将图像插入每个文件的背景中,但是我没有太多关于曲线和直线的文档,也找不到编辑直线本身的方法。我也对其他库持开放态度,但我还没有找到一个库明确说明如何编辑现有曲线和直线,只绘制新的曲线和直线

using iTextSharp.text;
using iTextSharp.text.pdf;

// open the reader
PdfReader reader = new PdfReader(refPath);
Rectangle size = reader.GetPageSizeWithRotation(1);
Document document = new Document(size);

// open the writer
FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();

// the pdf content
PdfContentByte cb = writer.DirectContent;

//get an image to be inserted.
var screenshot = System.Drawing.Image.FromFile("somefile.png");

//Create iTextSharp image
Image bg = Image.GetInstance(screenshot, System.Drawing.Imaging.ImageFormat.Png);
bg.SetDpi(dpi, dpi);
bg.ScaleToFit(size);
bg.SetAbsolutePosition(0, 0);
bg.Alignment = Image.UNDERLYING;

cb.AddImage(bg);

/**
Get and edit linework properties in here somewhere???
**/

// create the new page and add it to the pdf
PdfImportedPage page = writer.GetImportedPage(reader, 1);
cb.AddTemplate(page, 0, 0);

// close the streams
document.Close();
fs.Close();
writer.Close();
reader.Close();
理想情况下,所有行的输出如下所示:


任何想法都很感激

通过一种让人感到舒服的方式,尝试往返这种事情是很有诱惑力的。因此,可能需要转换为SVG,然后进行操作,然后再返回到PDF

然而,我鼓励你们远离这种诱惑,因为这样的往返将不可避免地导致扭曲和损失

相反,我鼓励您直接使用原始PDF操作符流。一开始看起来有点吓人,但事实上,一旦你掌握了窍门,它就相当简单了。例如百分比表示评论

q  % save state
0 0 10 10 re % define rectangle path
s % stroke
Q % restore state
Adobe PDF规范将为您提供所有详细信息。它很大,但写得很好,很清楚。有关所有操作员的列表和相关章节的链接,请参见附录A

那么问题就变成了如何处理现有的内容流

解析这些东西是非常重要的,所以我建议您使用一个工具。例如,ABCpdf将允许您将流分解为原子,修改序列,然后将它们插回原始文档中。有关代码示例,请参见


就解析和操作而言,这是一种非常优雅和强大的机制。我相信还有其他工具可以实现类似的功能,但我知道ABCpdf-

您的图像显示您希望编辑所有路径中的线帽和线连接,使其成为圆形

不幸的是,您没有共享具有代表性的示例文件,因此我必须自己构建一个,混合使用不同的cap和join样式,并使用一个路径表单来提醒您:

我建议您的任务使用来自的通用PdfContentStreamEditor,因为它可以完成所有繁重的工作,我们可以专注于手头的任务

因此,我们的流编辑器实现必须做什么?它必须将封口和连接样式设置为圆角,并防止覆盖这些设置。查看PDF规范,我们发现cap和join样式是当前图形状态的参数,可以分别使用J和J指令直接设置,也可以通过图形状态参数字典中的LC和LJ条目设置

因此,我们可以实现我们的流编辑器,只需首先初始化cap和join样式进行取整,然后删除所有J和J指令,并在每个图形状态gs指令之后重新初始化cap和join样式

class PathMakeCapAndJoinRound : PdfContentStreamEditor
{
    protected override void Write(PdfContentStreamProcessor processor, PdfLiteral operatorLit, List<PdfObject> operands)
    {
        if (start)
        {
            initializeCapAndJoin(processor);
            start = false;
        }
        if (CAP_AND_JOIN_OPERATORS.Contains(operatorLit.ToString()))
        {
            return;
        }
        base.Write(processor, operatorLit, operands);
        if (GSTATE_OPERATOR == operatorLit.ToString())
        {
            initializeCapAndJoin(processor);
        }
    }

    void initializeCapAndJoin(PdfContentStreamProcessor processor)
    {
        PdfLiteral operatorLit = new PdfLiteral("J");
        List<PdfObject> operands = new List<PdfObject> { new PdfNumber(PdfContentByte.LINE_CAP_ROUND), operatorLit };
        base.Write(processor, operatorLit, operands);

        operatorLit = new PdfLiteral("j");
        operands = new List<PdfObject> { new PdfNumber(PdfContentByte.LINE_JOIN_ROUND), operatorLit };
        base.Write(processor, operatorLit, operands);
    }

    List<string> CAP_AND_JOIN_OPERATORS = new List<string> { "j", "J" };
    string GSTATE_OPERATOR = "gs";
    bool start = true;
}

您是否可以共享一个示例PDF,其中包含一组具有代表性的前样式曲线?我知道如何使用中介绍的PdfContentStreamEditor相当轻松地实现它。答案有用吗?还是还有悬而未决的问题?
using (PdfReader pdfReader = new PdfReader(testDocument))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(@"Paths-Rounded.pdf", FileMode.Create, FileAccess.Write), (char)0, true))
{
    pdfStamper.RotateContents = false;
    PdfContentStreamEditor editor = new PathMakeCapAndJoinRound();

    for (int i = 1; i <= pdfReader.NumberOfPages; i++)
    {
        editor.EditPage(pdfStamper, i);
    }
}
byte[] createMixedPathsPdf()
{
    using (MemoryStream memoryStream = new MemoryStream())
    {
        using (Document document = new Document())
        {
            PdfWriter writer = PdfWriter.GetInstance(document, memoryStream);
            document.Open();
            var canvas = writer.DirectContent;
            canvas.SetLineWidth(10);

            canvas.MoveTo(100, 700);
            canvas.CurveTo(180, 720, 180, 720, 200, 800);
            canvas.CurveTo(220, 720, 220, 720, 350, 700);
            canvas.MoveTo(350, 700);
            canvas.CurveTo(220, 680, 220, 680, 210, 650);
            canvas.Stroke();

            canvas.SetLineCap(PdfContentByte.LINE_CAP_BUTT);
            canvas.SetLineJoin(PdfContentByte.LINE_JOIN_BEVEL);
            canvas.SetGState(createGState(PdfContentByte.LINE_CAP_BUTT, PdfContentByte.LINE_JOIN_BEVEL));
            canvas.MoveTo(100, 500);
            canvas.CurveTo(180, 520, 180, 520, 200, 600);
            canvas.CurveTo(220, 520, 220, 520, 350, 500);
            canvas.MoveTo(350, 500);
            canvas.CurveTo(220, 480, 220, 480, 210, 450);
            canvas.Stroke();

            canvas.SetLineCap(PdfContentByte.LINE_CAP_PROJECTING_SQUARE);
            canvas.SetLineJoin(PdfContentByte.LINE_JOIN_MITER);
            canvas.SetGState(createGState(PdfContentByte.LINE_CAP_PROJECTING_SQUARE, PdfContentByte.LINE_JOIN_MITER));
            canvas.MoveTo(100, 300);
            canvas.CurveTo(180, 320, 180, 320, 200, 400);
            canvas.CurveTo(220, 320, 220, 320, 350, 300);
            canvas.MoveTo(350, 300);
            canvas.CurveTo(220, 280, 220, 280, 210, 250);
            canvas.Stroke();

            canvas.SetLineCap(PdfContentByte.LINE_CAP_ROUND);
            canvas.SetLineJoin(PdfContentByte.LINE_JOIN_ROUND);
            canvas.SetGState(createGState(PdfContentByte.LINE_CAP_ROUND, PdfContentByte.LINE_JOIN_ROUND));
            canvas.MoveTo(100, 100);
            canvas.CurveTo(180, 120, 180, 120, 200, 200);
            canvas.CurveTo(220, 120, 220, 120, 350, 100);
            canvas.MoveTo(350, 100);
            canvas.CurveTo(220, 080, 220, 080, 210, 050);
            canvas.Stroke();
        }
        return memoryStream.ToArray();
    }
}

PdfGState createGState(int lineCap, int lineJoin)
{
    PdfGState pdfGState = new PdfGState();
    pdfGState.Put(new PdfName("LC"), new PdfNumber(lineCap));
    pdfGState.Put(new PdfName("LJ"), new PdfNumber(lineJoin));
    return pdfGState;
}