Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Suggestion】Support grid layout for easier PDF document creation. #156

Open
soul-soft opened this issue Aug 22, 2024 · 3 comments
Open
Labels
enhancement New feature or request forum Please use Discussions or the Support forum to ask Support questions - https://forum.pdfsharp.net/

Comments

@soul-soft
Copy link

soul-soft commented Aug 22, 2024

extensions

public static class PdfDocumentExtensions
{
    public static void DrawPage(this PdfDocument document,Action<PdfPage,XGraphics> configure)
    {
        var page = document.AddPage();
        using (var graphics = XGraphics.FromPdfPage(page))
        {
            configure(page, graphics);
        }
    }
}

public static class PdfSharpExtensions
{
    public static void DrawGrid(this XGraphics graphics, XFont font, XBrush brush, double y, Action<XGrid> conguire)
    {
        var yIndex = y;
        var grid = new XGrid();
        conguire(grid);
        var pageWidth = graphics.PageSize.Width;
        foreach (var row in grid.Rows)
        {
            var xIndex = row.Margin;
            var rowHeight = row.Height;
            foreach (var cell in row.Cells)
            {
                if (cell is XGridTextCell textCell)
                {
                    var cellSize = graphics.MeasureString(textCell.Text, font);
                    var cellWidth = cell.Width;
                    var cellHelight = cellSize.Height > rowHeight ? cellSize.Height : rowHeight;
                    //计算单元格的宽度
                    if (cellSize.Width > cellWidth)
                    {
                        cellWidth = cellSize.Width;
                    }
                    if (row.Cells.Last() == cell)
                    {
                        cellWidth = pageWidth - (xIndex + row.Margin);
                    }
                    if (!textCell.Warp)
                    {
                        graphics.DrawString(textCell.Text, font, brush, new XRect(xIndex, yIndex, cellWidth, 0), textCell.Format);
                    }
                    else
                    {
                        var lines = graphics.GetLines(font, textCell.Text, cellWidth);
                        var tempY = yIndex;
                        foreach (var item in lines)
                        {
                            graphics.DrawString(item, font, brush, new XRect(xIndex, tempY, cellWidth, 0), textCell.Format);
                            tempY += cellHelight;
                        }
                        cellHelight = lines.Count * cellHelight;
                        rowHeight = cellHelight > rowHeight ? cellHelight : rowHeight;
                    }
                    xIndex += cellWidth;
                }
            }
            yIndex += rowHeight;
        }
    }
    public static List<string> GetLines(this XGraphics graphics, XFont font, string text, double lineWidth)
    {
        var lines = new List<string>();
        var sb = new StringBuilder();
        foreach (var item in text)
        {
            sb.Append(item);
            var size = graphics.MeasureString(sb.ToString(), font);
            if (size.Width >= lineWidth)
            {
                lines.Add(sb.ToString());
                sb.Clear();
            }
        }
        lines.Add(sb.ToString());
        return lines;
    }
}

public class XGrid
{
    public List<XGridRow> Rows { get; set; } = new List<XGridRow>();

    public void DrawRow(Action<XGridRow> configure)
    {
        var row = new XGridRow();
        configure(row);
        Rows.Add(row);
    }
}

public class XGridRow
{
    /// <summary>
    /// 垂直间距
    /// </summary>
    public double Height { get; set; }
    /// <summary>
    /// 水平间距
    /// </summary>
    public double Margin { get; set; }

    public List<XGridCell> Cells { get; set; } = new List<XGridCell>();

    public void DrawTextCell(Action<XGridTextCell> configure)
    {
        var cell = new XGridTextCell();
        configure(cell);
        Cells.Add(cell);
    }
}

public class XGridCell
{
    public double Width { get; set; }
}

public class XGridTextCell : XGridCell
{
    public string Text { get; set; }
    public bool Warp { get; set; } = false;
    public XStringFormat Format { get; set; } = XStringFormats.Default;
}

Usage

class Program
{
    static void Main(string[] args)
    {
        GlobalFontSettings.FontResolver = new WindowsFontResolver();
        GlobalFontSettings.DefaultFontEncoding = PdfFontEncoding.Unicode;
        // 创建新的 PDF 文档
        PdfDocument document = new PdfDocument();
        document.Info.Title = "工程造价咨询报告书";

        // 创建一个页面
        document.DrawPage((page, gfx) =>
        {
            page.Size = PdfSharp.PageSize.A4;
            // 设置字体
            var titleFont = new XFont("STSONG.TTF", 30, XFontStyleEx.Bold);
            var footerFont = new XFont("STSONG.TTF", 18, XFontStyleEx.Bold);
            var bodyFont = new XFont("STSONG.TTF", 12, XFontStyleEx.Bold);
            gfx.DrawGrid(titleFont, XBrushes.Black, 100, grid =>
            {
                grid.DrawRow(row =>
                {
                    row.Margin = 100;
                    row.Height = 20;
                    row.DrawTextCell(cell =>
                    {
                        cell.Text = "工程造价咨询报告书";
                        cell.Format = XStringFormats.Center;
                    });
                });
            });
            gfx.DrawGrid(bodyFont, XBrushes.Black, 350, grid =>
            {
                grid.DrawRow(row =>
                {
                    row.Margin = 100;
                    row.Height = 20;
                    row.DrawTextCell(cell =>
                    {
                        cell.Text = "咨询项目全称: ";
                    });
                    row.DrawTextCell(cell =>
                    {
                        cell.Text = "伊犁新天煤化工有限责任公司年产20亿Nm3煤制天然气项目2020年煤气水膨胀入气柜施工合同";
                        cell.Warp = true;
                    });
                });
                grid.DrawRow(row =>
                {
                    row.Margin = 100;
                    row.Height = 20;
                    row.DrawTextCell(cell =>
                    {
                        cell.Text = "咨询业务类别: ";
                    });
                    row.DrawTextCell(cell =>
                    {
                        cell.Text = "工程结算审核";
                        cell.Format = XStringFormats.Center;
                    });
                });
                grid.DrawRow(row =>
                {
                    row.Margin = 100;
                    row.Height = 20;
                    row.DrawTextCell(cell =>
                    {
                        cell.Text = "咨询报告日期: ";
                    });
                    row.DrawTextCell(cell =>
                    {
                        cell.Text = "二○二三年四月二十四日";
                        cell.Format = XStringFormats.Center;
                    });
                });
            });
            gfx.DrawGrid(footerFont, XBrushes.Black, page.Height.Value - 100, grid =>
            {
                grid.DrawRow(row =>
                {
                    row.Margin = 100;
                    row.Height = 20;
                    row.DrawTextCell(cell =>
                    {
                        cell.Text = "万邦工程管理咨询有限公司";
                        cell.Format = XStringFormats.Center;
                    });
                });
            });
        });
        document.DrawPage((page, gfx) =>
        {
            gfx.DrawRectangle(XBrushes.Black, new XRect(50, 50, page.Width.Value - 100, page.Height.Value - 100));
        });
        // 保存文档
        string filename = "工程造价咨询报告书.pdf";
        document.Save(filename);
        // 打开生成的 PDF 文件
        Process.Start(new ProcessStartInfo(filename) { UseShellExecute = true });
    }


    internal class WindowsFontResolver : IFontResolver
    {
        public byte[] GetFont(string faceName)
        {
            return File.ReadAllBytes($@".\{faceName}");
        }

        public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic)
        {
            return new FontResolverInfo(familyName, isBold, isItalic);
        }
    }
}
@ThomasHoevel ThomasHoevel added enhancement New feature or request forum Please use Discussions or the Support forum to ask Support questions - https://forum.pdfsharp.net/ labels Aug 22, 2024
@soul-soft
Copy link
Author

soul-soft commented Aug 22, 2024

Topic

I have implemented most of the logic, including the Grid layout. The rows support: margins, Y-axis positioning, and row height. The cells support: border lines, alignment, automatic wrapping, margins, and fluid layout.

It can easily achieve the requirements for rendering paragraphs, tables, and more. However, I do not have enough resources to support images and other features, and I hope the author will continue the development.

Source

https://github.com/soul-soft/Soul.PDFsharp.Extensions

Nuget

dotnet add package Soul.PDFsharp.Extensions --version 1.0.0

@soul-soft
Copy link
Author

soul-soft commented Aug 22, 2024

image

@soul-soft
Copy link
Author

I have already implemented most of the requirements, including internal and external margins, alignment in all directions, the box model, and text and image rendering, among other things.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request forum Please use Discussions or the Support forum to ask Support questions - https://forum.pdfsharp.net/
Projects
None yet
Development

No branches or pull requests

2 participants