【问题标题】:How can lines, curves, and vector graphic properties be edited in an existing PDF?如何在现有 PDF 中编辑线条、曲线和矢量图形属性?
【发布时间】:2019-02-14 22:10:29
【问题描述】:

我收集了大量带有矢量图形(主要是直线和曲线)的 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();

理想情况下,所有行的输出如下所示:

感谢任何想法!

【问题讨论】:

  • 您能否分享一个示例 PDF,其中包含一组具有代表性的 Before 样式曲线?我知道如何使用PdfContentStreamEditor(如this answer 中所述)相当容易地实现它。
  • 这些答案有帮助吗?还是还有未解决的问题?

标签: c# pdf itext pdf-generation


【解决方案1】:

尝试通过一种人们熟悉的格式来回往返这种事情是很诱人的。所以也许是转换为 SVG,然后进行操作,然后返回 PDF。

但是我建议你远离这种诱惑,因为这样的往返不可避免地会导致失真和损失。

相反,我鼓励您直接使用原始 PDF 运算符流。起初它看起来有点令人生畏,但实际上一旦你掌握了它就非常简单。例如(百分比表示 cmets),

q  % save state
0 0 10 10 re % define rectangle path
s % stroke
Q % restore state

Adobe PDF 规范将为您提供所有详细信息。它很大,但写得很好,很清楚。有关所有运算符的列表以及相关部分的链接,请参见附录 A。

https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf

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

解析这些东西并非易事,因此我建议您使用工具。例如 ABCpdf 将允许您将流分解为原子,修改序列,然后将它们重新插入原始文档。有关代码示例,请参阅,

https://www.websupergoo.com/helppdfnet/default.htm?page=source%2F7-abcpdf.atoms%2Fopatom%2F1-methods%2Ffind.htm

在解析和操作方面,这是一个非常优雅和强大的机制。我敢肯定还有其他工具可以做类似的事情,但我知道 ABCpdf。 :-)

【讨论】:

    【解决方案2】:

    您的图像显示您想要编辑所有路径中的线帽和线连接以使其成为圆形。

    很遗憾,您没有分享具有代表性的示例文件,所以我不得不自己构建一个混合了不同的 cap 和 join 样式以及提醒您的路径形式的文件:

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

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

    因此,我们可以简单地通过首先初始化 cap 和 join 样式来实现我们的流编辑器,然后删除所有 Jj 指令并重新初始化 cap 和 join每个图形状态 gs 指令之后的样式。

    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 中

    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);
        }
    }
    

    我们得到结果:


    请注意,引用答案的限制仍然存在。特别是这个编辑器只编辑页面内容流。要获得完整的解决方案,您还必须编辑所有表单 XObject 和 Pattern 流,并处理注释。


    为了允许复制,这是我创建测试文档的方式:

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-30
      • 1970-01-01
      相关资源
      最近更新 更多