使用 .NET 将可滚动面板另存为 PDF

发布于 2024-11-02 16:15:19 字数 4754 浏览 1 评论 0原文

我有一个面板,里面有很多控件供用户填充。其中包括文本框、复选框、单选按钮等。这是一个需要填写的长表单,因此控件位于可滚动面板中。我需要的是将整个面板另存为pdf。我认为 PDFsharp 是一个很好的库,能够将任何文本或图像保存为 pdf 文件,但我不想为面板内的每个控件编写代码。我曾经编写过一个类来从 Control 对象创建 pdf 文件。它迭代给定控件的所有内部控件(及其内部控件,直到没有剩余内部控件),并使用其 Location 和 Size 属性将其 Text 属性(对于可检查控件是/否)写入 pdf。我现在找不到它,但我记得它与我使用的一些 DevExpress 控件有问题,所以我没有费心再次编写它。 (编辑:我必须这样做,你可以在下面找到它。)我认为截取屏幕截图并将该图像另存为 pdf 会很好,但我不知道如何实现它。 这个问题似乎是这样,但没有令人满意的答案。

所以,无论是否截图,我都愿意寻求任何建议。在很多情况下,用户必须填写长表单并能够将其保存为 pdf。同样,任何建议或解决方法将不胜感激。 (我考虑使用 html 创建表单,在 WebBrowser 控件中显示它并使用 html 到 pdf 库,但我真的更喜欢使用现有的表单)

非常感谢。


编辑: 我必须编写一些东西来迭代容器控件(如面板)的内部控件,并使用其位置、大小和字体属性将每个内部控件写入 pdf,但我不建议使用它(至少是这样)因为这些:

  1. 它将页面的大小设置为给定控件的大小,并且仅使用一页(通常是巨大的)pdf 页面。如果需要,您可以添加逻辑将其拆分为页面。 (我没有,但我想你可能需要你的 pdf 更适合打印)。
  2. 对于这样的任务,Cheeso 的方法(使用 FlowDocument)是一种更“合法”的方法。我更喜欢使用它而不是这个,但在这种情况下我没有选择。

我在这段代码中使用了 PDFsharp。您可以在它的主页这是 CodePlex 页面

PdfReport 类:

    private PdfDocument Document;
    public Control Control { get; private set; }        

    public PdfReport(Control control) { Control = control; }

    public PdfDocument CreatePdf(PdfDocument document = null)
    {
        Document = document != null ? document : new PdfDocument();
        PdfPage page = Document.AddPage();
        page.Height = Control.Height;
        page.Width = Control.Width;

        XGraphics gfx = XGraphics.FromPdfPage(page);
        foreach (PdfItem item in CreatePdf(new Point(0, 0), Control.Controls))
        {
            XStringFormat format = item.IsContainer ? XStringFormats.TopLeft : item.TextAlign == ContentAlignment.BottomCenter ? XStringFormats.BottomCenter : item.TextAlign == ContentAlignment.TopLeft ? XStringFormats.TopLeft : item.TextAlign == ContentAlignment.TopCenter ? XStringFormats.TopCenter : XStringFormats.Center;
            gfx.DrawString(item.Text, item.Font, item.Brush, new XRect(item.Location, item.Size), format);
        }
        return Document;
    }
    private IEnumerable<PdfItem> CreatePdf(Point location, Control.ControlCollection controls)
    {
        List<PdfItem> items = new List<PdfItem>();
        foreach (Control control in controls)
        {
            if (control.Controls.Count > 0)
                items.AddRange(CreatePdf(control.Location, control.Controls));
            items.Add(new PdfItem(control, location));
        }
        return items;
    }

    public void SaveAsPdf(string path, bool open = false)
    {
        CreatePdf().Save(path);
        if (open)
            Process.Start(path);
    }

PdfItem 类:

    public string Text { get; set; }
    public Point Location { get; set; }
    public Size Size { get; set; }
    public Font Font { get; set; }
    public bool IsContainer { get; set; }
    public ContentAlignment TextAlign { get; set; }
    public Color ForeColor { get; set; }
    public XBrush Brush { get { return new SolidBrush(ForeColor); } }

    public PdfItem() { }
    public PdfItem(string text, Point location, Font font, Color foreColor, Size size, bool isContainer = false, ContentAlignment alignment = ContentAlignment.MiddleCenter)
    {
        Text = text;
        Location = location;
        Size = size;
        Font = new Font(font.FontFamily, font.Size, font.Style, GraphicsUnit.World);
        TextAlign = alignment;
        ForeColor = foreColor;
        IsContainer = isContainer;
    }
    public PdfItem(string text, Point location, Size size)
        : this(text, location, new Font("Calibri", 12), Color.Black, size) { }
    public PdfItem(Control control, Point parentLocation)
        : this(control.Text, control.Location, control.Font, control.ForeColor, control.Size, control.Controls.Count > 0)
    {
        Location = new Point(Location.X + parentLocation.X, Location.Y + parentLocation.Y);
        IEnumerable<PropertyInfo> properties = control.GetType().GetProperties();
        if (properties.FirstOrDefault(p => p.Name == "TextAlign" && p.PropertyType == typeof(ContentAlignment)) != null)
            TextAlign = (control as dynamic).TextAlign;
        if (properties.FirstOrDefault(p => p.Name == "Checked" && p.PropertyType == typeof(bool)) != null)
        {
            string title = control.Text != null && control.Text.Length > 0 ? string.Format("{0}: ", control.Text) : string.Empty;
            Text = string.Format("{0}{1}", title, (control as dynamic).Checked ? "Yes" : "No");
        }
    }

I have a Panel filled with a lot of controls for users to fill. These include textboxes, checkboxes, radiobuttons etc. It is a long form to fill so the controls are in a scrollable panel. What I need is to save the whole panel as pdf. I think PDFsharp is a good library to be able to save any text or image as a pdf file but I don't want to write code for every single control inside the panel. I once wrote a class to create a pdf file from a Control object. It was iterating all inner controls (and their inner controls until no inner control is left) of the given control and write their Text property (yes/no for chekable controls) to pdf using their Location and Size properties. I could not find it now but I remember it was having issues with some of the DevExpress controls I use so I didn't bother writing it again. (Edit: I had to, you can find it below.) I think taking a screenshot and save that image as pdf would be nice but I couldn't find out how to achieve it. This question seems like it but there is no satisfying answer to that.

So, screenshot or not I'm open for any advice. There should be many occasions where users must fill long forms and be able to keep it as pdf. Again, any advice or workaround would be appreciated. (I think about creating the form using html, displaying it in a WebBrowser control and using an html to pdf library but I really prefer using my existent form)

Many Thanks.


Edit:
I had to write something iterates inner controls of a container control (like a panel) and writes every inner control to a pdf using their Location, Size and Font properties though, I don't recommend to use it (at least as it is) because of these:

  1. It sets the page's size to given control's size and use only one (usually huge) pdf page. You can add a logic to split it to pages if you need to. (I didn't, but I guess you'll probably need your pdf more printer friendly).
  2. Cheeso's method (using a FlowDocument) is a much more "legitimate" way for a task like this. I prefer using that over this but I didn't have a choice in this instance.

I used PDFsharp in this code. You can find it in it's hompage or it's CodePlex page.

PdfReport class:

    private PdfDocument Document;
    public Control Control { get; private set; }        

    public PdfReport(Control control) { Control = control; }

    public PdfDocument CreatePdf(PdfDocument document = null)
    {
        Document = document != null ? document : new PdfDocument();
        PdfPage page = Document.AddPage();
        page.Height = Control.Height;
        page.Width = Control.Width;

        XGraphics gfx = XGraphics.FromPdfPage(page);
        foreach (PdfItem item in CreatePdf(new Point(0, 0), Control.Controls))
        {
            XStringFormat format = item.IsContainer ? XStringFormats.TopLeft : item.TextAlign == ContentAlignment.BottomCenter ? XStringFormats.BottomCenter : item.TextAlign == ContentAlignment.TopLeft ? XStringFormats.TopLeft : item.TextAlign == ContentAlignment.TopCenter ? XStringFormats.TopCenter : XStringFormats.Center;
            gfx.DrawString(item.Text, item.Font, item.Brush, new XRect(item.Location, item.Size), format);
        }
        return Document;
    }
    private IEnumerable<PdfItem> CreatePdf(Point location, Control.ControlCollection controls)
    {
        List<PdfItem> items = new List<PdfItem>();
        foreach (Control control in controls)
        {
            if (control.Controls.Count > 0)
                items.AddRange(CreatePdf(control.Location, control.Controls));
            items.Add(new PdfItem(control, location));
        }
        return items;
    }

    public void SaveAsPdf(string path, bool open = false)
    {
        CreatePdf().Save(path);
        if (open)
            Process.Start(path);
    }

PdfItem class:

    public string Text { get; set; }
    public Point Location { get; set; }
    public Size Size { get; set; }
    public Font Font { get; set; }
    public bool IsContainer { get; set; }
    public ContentAlignment TextAlign { get; set; }
    public Color ForeColor { get; set; }
    public XBrush Brush { get { return new SolidBrush(ForeColor); } }

    public PdfItem() { }
    public PdfItem(string text, Point location, Font font, Color foreColor, Size size, bool isContainer = false, ContentAlignment alignment = ContentAlignment.MiddleCenter)
    {
        Text = text;
        Location = location;
        Size = size;
        Font = new Font(font.FontFamily, font.Size, font.Style, GraphicsUnit.World);
        TextAlign = alignment;
        ForeColor = foreColor;
        IsContainer = isContainer;
    }
    public PdfItem(string text, Point location, Size size)
        : this(text, location, new Font("Calibri", 12), Color.Black, size) { }
    public PdfItem(Control control, Point parentLocation)
        : this(control.Text, control.Location, control.Font, control.ForeColor, control.Size, control.Controls.Count > 0)
    {
        Location = new Point(Location.X + parentLocation.X, Location.Y + parentLocation.Y);
        IEnumerable<PropertyInfo> properties = control.GetType().GetProperties();
        if (properties.FirstOrDefault(p => p.Name == "TextAlign" && p.PropertyType == typeof(ContentAlignment)) != null)
            TextAlign = (control as dynamic).TextAlign;
        if (properties.FirstOrDefault(p => p.Name == "Checked" && p.PropertyType == typeof(bool)) != null)
        {
            string title = control.Text != null && control.Text.Length > 0 ? string.Format("{0}: ", control.Text) : string.Empty;
            Text = string.Format("{0}{1}", title, (control as dynamic).Checked ? "Yes" : "No");
        }
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

时光病人 2024-11-09 16:15:19

关于

。我认为截取屏幕截图并将该图像另存为 pdf 会很好,但我不知道如何实现它。

codeplex.com 上有一个名为“cropper”的工具。它被设计用作可以截取屏幕截图的用户工具。它是托管代码,开源。

我可以想象将一些裁剪器魔法嵌入到您的应用程序中,以便您可以截取屏幕截图。我还可以想象这对于在出现问题时收集屏幕的诊断图像很有用。

另一方面...如果您有兴趣生成在屏幕上重现内容的打印表单,那么我认为您应该使用 WPF,在这种情况下做您想做的事情非常容易。例如,这个问题描述了如何对 FlowDocument 进行打印预览。从那时起,您的用户可以打印到 PDF(如果安装了 PDF 打印机)或打印到 XPS,或打印到物理输出设备,等等。

Regarding

. I think taking a screenshot and save that image as pdf would be nice but I couldn't find out how to achieve it.

There is a tool called "cropper" available on codeplex.com. It is designed to be used as a user tool that can take screenshots. It is managed code, open source.

I can imagine embedding some of the cropper magic into your app so that you could take that screenshot. I can also imagine this would be useful for collecting a diagnostic image of the screen at the time of a problem.

On the other hand... if you are interested in producing a printed form that reproduces the content on the screen, then I think you should be using WPF, in which case doing what you want is pretty easy. For example, this question describes how to do a print-preview for a FlowDocument. From that point your user can print to PDF (if he has a PDF printer installed) or print to XPS, or print to a physical output device, and so on.

百思不得你姐 2024-11-09 16:15:19

我不知道这是否对您有帮助,但是 DocRaptor.com 的 pdf api 可以内置,这样就可以了无论用户输入什么,它都会为您服务。它使用基本的 html。

I don't know if this would help you or not, but DocRaptor.com's pdf api could be built in so it would do it for you, no matter what the user inputs. It uses basic html.

爱*していゐ 2024-11-09 16:15:19

正如您可以使用下面的:)

YourPanel.AutoSize = true;


        int width = YourPanel.Size.Width;
        int height = YourPanel.Size.Height;

        Bitmap bm = new Bitmap(width, height);
        YourPanel.DrawToBitmap(bm, new Rectangle(0, 0, width, height));

        string outputFileName = @"C:\YourDirectory/myimage.bmp";
        using (MemoryStream memory = new MemoryStream())
        {
            using (FileStream fs = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite))
            {
                bm.Save(memory, ImageFormat.Bmp);
                Clipboard.SetImage(bm);
                byte[] bytes = memory.ToArray();
                fs.Write(bytes, 0, bytes.Length);
            }
        }
        YourPanel.AutoSize = false;

Clipboard.SetImage 会将您的 bm 发送到剪贴板,以便您可以将它们粘贴到 pdf 表单或任何文档中。

如果您愿意,它还有一个内置示例,可以将其保存为图像。

这里的技巧是面板的自动调整大小。它需要设置为 true,以便面板将其自身调整为整个可见区域的大小,然后在完成工作后,您可以将其大小调整为 false,以便它再次为用户屏幕使用滚动条(您可能会看到它闪烁半秒,但这段代码确实有效。

我个人更喜欢将其写为剪贴板,或者您可以编写字节,但 ITextSharp 是一个很棒的扩展库,

我真的希望这会有所帮助。

As you can use the below :)

YourPanel.AutoSize = true;


        int width = YourPanel.Size.Width;
        int height = YourPanel.Size.Height;

        Bitmap bm = new Bitmap(width, height);
        YourPanel.DrawToBitmap(bm, new Rectangle(0, 0, width, height));

        string outputFileName = @"C:\YourDirectory/myimage.bmp";
        using (MemoryStream memory = new MemoryStream())
        {
            using (FileStream fs = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite))
            {
                bm.Save(memory, ImageFormat.Bmp);
                Clipboard.SetImage(bm);
                byte[] bytes = memory.ToArray();
                fs.Write(bytes, 0, bytes.Length);
            }
        }
        YourPanel.AutoSize = false;

The Clipboard.SetImage will send you bm to the clipboard so you can paste them to your pdf form or whatever document

This also has an example built in that saves it as a image for you if you want.

The trick here is Autosize for your panel. It needs to be set to true so the panel resizes itself as a whole area visible, then right after you do your work you can resize it to false so it uses scrollbars again for the users screen (you may see it flash for half a second, but this code does work.

Saving it in a PDF I personally just prefer to write it there as my clipboard or you can write byte. But ITextSharp is a great library for the extension to work with!

I Really hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文