From aadcde5198fa4c2d3defbc84d49a063593a2c2b8 Mon Sep 17 00:00:00 2001 From: Jamie Lemon Date: Wed, 25 Jun 2025 23:34:30 +0100 Subject: [PATCH 1/2] An example of rendering a multipage PDF using Story --- Examples/StoryRendering/StoryRender.sln | 22 + .../StoryRendering/StoryRender/Program.cs | 403 ++++++++++++++++++ .../StoryRender/StoryRender.csproj | 14 + 3 files changed, 439 insertions(+) create mode 100644 Examples/StoryRendering/StoryRender.sln create mode 100644 Examples/StoryRendering/StoryRender/Program.cs create mode 100644 Examples/StoryRendering/StoryRender/StoryRender.csproj diff --git a/Examples/StoryRendering/StoryRender.sln b/Examples/StoryRendering/StoryRender.sln new file mode 100644 index 0000000..86c38ac --- /dev/null +++ b/Examples/StoryRendering/StoryRender.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35506.116 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StoryRender", "StoryRender\StoryRender.csproj", "{8EB31A13-17AF-4B16-BE9A-23308BE0AF69}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8EB31A13-17AF-4B16-BE9A-23308BE0AF69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8EB31A13-17AF-4B16-BE9A-23308BE0AF69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8EB31A13-17AF-4B16-BE9A-23308BE0AF69}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8EB31A13-17AF-4B16-BE9A-23308BE0AF69}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Examples/StoryRendering/StoryRender/Program.cs b/Examples/StoryRendering/StoryRender/Program.cs new file mode 100644 index 0000000..eca3ebd --- /dev/null +++ b/Examples/StoryRendering/StoryRender/Program.cs @@ -0,0 +1,403 @@ +using MuPDF.NET; + +PDFRenderer renderer = new PDFRenderer(); +renderer.GeneratePDF(); + +public class PDFRenderer +{ + private readonly MuPDF.NET.Font _font = new("helv"); + private readonly MuPDF.NET.Font _fontBold = new("hebo"); + private readonly int _defaultFontSize = 8; + private readonly int _headerFontSize = 12; + + /* Margins */ + private readonly int _defaultMarginVertical = 12; + private readonly int _defaultMarginHorizontal = 12; + + private readonly MuPDF.NET.Point _pageStart = new(50, 72); + + private readonly string _HTML = "

生产准备:

1. 每日生产进行维护保养,请参照并填写Philips 自动螺丝起点检表《WI-Screw assembly-Makita DF010&Kilews

SKD-B512L-F01》

2 .扭力计UNIT选择‘lbf.in’,‘P-P’模式,每四小时检查一次,每次检查5组数据,只有合格才可以生产;并填写

力扭矩记录表,表单号 : F-EN-34 。

1.电动起子力矩: 5±1 in-lbs,电动螺丝起编号:5.0。

2.电动起子力矩:10±1 in-lbs,电动螺丝起编号:10.0。

3.电动起子力矩:12±1 in-lbs,电动螺丝起编号:12.0。

Colten - line break

生产准备:

1. 每日生产进行维护保养,请参照并填写Philips 自动螺丝起点检表《WI-Screw assembly-Makita DF010&Kilews

SKD-B512L-F01》

2 .扭力计UNIT选择‘lbf.in’,‘P-P’模式,每四小时检查一次,每次检查5组数据,只有合格才可以生产;并填写

力扭矩记录表,表单号 : F-EN-34 。

1.电动起子力矩: 5±1 in-lbs,电动螺丝起编号:5.0。

2.电动起子力矩:10±1 in-lbs,电动螺丝起编号:10.0。

3.电动起子力矩:12±1 in-lbs,电动螺丝起编号:12.0。

Colten - line break

生产准备:

1. 每日生产进行维护保养,请参照并填写Philips 自动螺丝起点检表《WI-Screw assembly-Makita DF010&Kilews

SKD-B512L-F01》

2 .扭力计UNIT选择‘lbf.in’,‘P-P’模式,每四小时检查一次,每次检查5组数据,只有合格才可以生产;并填写

力扭矩记录表,表单号 : F-EN-34 。

1.电动起子力矩: 5±1 in-lbs,电动螺丝起编号:5.0。

2.电动起子力矩:10±1 in-lbs,电动螺丝起编号:10.0。

3.电动起子力矩:12±1 in-lbs,电动螺丝起编号:12.0。

Colten - line break

生产准备:

1. 每日生产进行维护保养,请参照并填写Philips 自动螺丝起点检表《WI-Screw assembly-Makita DF010&Kilews

SKD-B512L-F01》

2 .扭力计UNIT选择‘lbf.in’,‘P-P’模式,每四小时检查一次,每次检查5组数据,只有合格才可以生产;并填写

力扭矩记录表,表单号 : F-EN-34 。

1.电动起子力矩: 5±1 in-lbs,电动螺丝起编号:5.0。

2.电动起子力矩:10±1 in-lbs,电动螺丝起编号:10.0。

3.电动起子力矩:12±1 in-lbs,电动螺丝起编号:12.0。

"; + + + struct TextBox + { + public required string Label; + public required float LabelLength; + public required string Content; + public required float ContentLength; + } + + /// + /// Describes which direction a textbox group should be rendered. + /// + enum TextboxGroupDirection + { + Horizontal, + Vertical + } + + /* Define colors. MuPDF expects rbg values to be unit values (i.e., 0-1).*/ + private readonly float[] _gray = ConvertToURGB(211, 211, 211); + private readonly float[] _pastelBlue = ConvertToURGB(167, 199, 231); + + /// + /// This method simply converts RGB values to Unit RGB. + /// + /// + /// + /// + /// + private static float[] ConvertToURGB(int red, int green, int blue) + => [red / (float)255, green / (float)255, blue / (float)255]; + + /// + /// Calculates the center of Rect Y axis including + /// an offset for the font height. + /// + /// + /// + /// + /// + private static float CenterFontY(float y0, float y1, float fontHeight) + => ((y0 + y1) / 2) + (fontHeight / 2f) - 2; + + private string ResizeContent(string txt, float maxWidth) + { + var ellipsis = "..."; + var ellipsisLen = _font.TextLength(ellipsis); + + var newContent = ""; + foreach (var c in txt) + { + var len = _font.TextLength(newContent, fontSize: _defaultFontSize); + if (len + ellipsisLen >= maxWidth) + { + newContent += ellipsis; + break; + } + + newContent += c; + } + + return newContent; + } + + + /// + /// Helper function to create a Textbox struct. + /// It will calculate the label length and content length. + /// + /// + /// + /// + private TextBox CreateTextbox(string lbl, string content) + { + var labelLen = _font.TextLength(lbl, fontSize: _defaultFontSize); + var contentLen = _font.TextLength(content, fontSize: _defaultFontSize); + return new TextBox + { + Label = lbl, + LabelLength = labelLen, + Content = content, + ContentLength = contentLen, + }; + } + + public void GeneratePDF() + { + Console.WriteLine("GeneratePDF"); + + Document doc = new Document(); // empty output PDF + Page page = doc.NewPage(-1); + MuPDF.NET.TextWriter writer = new MuPDF.NET.TextWriter(page.Rect); + + var point = DrawProgramInfoRect(page, writer); + + /* Now onto the steps */ + point.Y += (_headerFontSize * 1.2f); + + writer.Append(point, "Steps", _fontBold, fontSize: _headerFontSize); + + var html = _HTML; + Story descStory = new(html); + + var storyWidth = page.Rect.Width - point.X; + var fit = descStory.FitHeight(storyWidth, origin: new Point(0, 0)); + + bool overflow; + float filledHeight; + if (fit.Filled is Rect filled) + { + while (true) + { + (point, overflow, filledHeight) = DrawStepDescription(page, point, filled, descStory); + + if (!overflow) break; + + filled.Y1 -= filledHeight; + + /* Need a new page since we have overflow. Before + * making the new page, need to finish writing everything + * on the previous page. */ + writer.WriteText(page); + page = doc.NewPage(-1); + writer = new(page.Rect); + point.X = _pageStart.X; + point.Y = _pageStart.Y; + } + } + + point.X = _pageStart.X; + point.Y += _defaultMarginVertical; + + point.Y += _headerFontSize * 1.2f; + + writer.Append(point, "Components", _fontBold, _headerFontSize); + + point.Y += _defaultMarginVertical; + + + writer.WriteText(page); + + page = doc.NewPage(-1); + writer = new(page.Rect); + + point.X = _pageStart.X; + point.Y = _pageStart.Y; + + + doc.Save("from_html.pdf", garbage: 4, deflate: 1, useObjstms: 1, deflateImages: 1); + + + + } + + /// + /// Adds a list of textboxes to the page. They will be aligned either + /// vertically or horizontally depening on the direction. + /// + /// + /// + /// + /// + /// + /// IEnumerable> + private Point DrawTextboxGroup(Page page, MuPDF.NET.TextWriter writer, IEnumerable> textboxes, + Point start, TextboxGroupDirection dir, float textboxWidth, float maxLabelLen = -1) + { + var x0 = start.X; + var y0 = start.Y; + var fontHeight = _defaultFontSize * 1.2f; + if (maxLabelLen < 0) + { + maxLabelLen = textboxes.Max(l => l.Value.LabelLength); + } + + //MuPDF.NET.TextWriter writer = new(page.Rect); + + foreach (var tb in textboxes) + { + var centerP = new Point(x0, CenterFontY(y0, y0 + fontHeight, fontHeight)); + + writer.Append(centerP, tb.Value.Label, _font, _defaultFontSize); + + /* Give the textbox some breathing room. */ + var textboxMargin = 5; + var tbWidth = textboxWidth - textboxMargin; + x0 = x0 + maxLabelLen + textboxMargin; + + var textboxRect = new Rect(x0, y0, x0 + tbWidth, y0 + fontHeight); + page.DrawRect(textboxRect, color: _gray, fill: _gray, fillOpacity: .25f, dashes: "[5 .2] 0"); + + centerP.X = x0; + centerP.Y = CenterFontY(textboxRect.TopLeft.Y, textboxRect.BottomLeft.Y, fontHeight); + + /* Resize the content if it doesn't fit in the textbox */ + var content = tb.Value.Content; + if (tb.Value.ContentLength > tbWidth) + { + content = ResizeContent(tb.Value.Content, tbWidth); + } + + writer.Append(centerP, content, _font, _defaultFontSize); + + if (dir == TextboxGroupDirection.Horizontal) + { + x0 = textboxRect.TopRight.X + 5; + y0 = start.Y; + } + else + { + x0 = start.X; + y0 = textboxRect.BottomLeft.Y; + } + } + + return new Point(x0, y0); + } + + /// + /// Draw the Program Information box along with all related + /// program metadata. + /// + /// + /// + private Point DrawProgramInfoRect(Page page, MuPDF.NET.TextWriter writer) + { + var pageRect = page.Rect; + var fontHeight = _defaultFontSize * 1.2f; + + /* Define all labels before hand to determine + * start point of the textboxes. */ + Dictionary textboxes = new() + { + /* LHS */ + { + "Customer", + CreateTextbox("Customer", "program.Customer!.Name") + }, + { + "DC", + CreateTextbox("DC", "program.ProgramDict.DC") + }, + { + "OMD", + CreateTextbox("OMD", "program.ProgramDict.OMD") + }, + { + "Version", + CreateTextbox("Version", "V{program.ProgramDict.Version}") + }, + { + "LastChangedBy", + CreateTextbox("LastChangedBy", "program.ProgramDict.LastModifiedBy?.Username") + }, + { + "Submitted", + CreateTextbox("Submitted", "program.ProgramDict.Submitted?.ToString() ??") + }, + + /* RHS */ + { + "Assembly", + CreateTextbox("Assembly", "program.Assembly!.Name") + }, + { + "Previous DC", + CreateTextbox("PreviousDC", "program.ProgramDict.PreviousDC") + }, + { + "ReleaseStatus", + CreateTextbox("ReleaseStatus", "program.ProgramDict.ReleaseStatus.ToString()") + }, + { + "PreviousVersion", + CreateTextbox("PreviousVersion", "V{program.ProgramDict.PreviousVersion}") + }, + { + "LastChanged", + CreateTextbox("LastChanged", "program.ProgramDict.LastModified.ToString() ?? ") + }, + { + "Released", + CreateTextbox("Released", "program.ProgramDict.Released?.ToString() ?? ") + }, + }; + + writer.Append(new Point(50, 72), "l[ProgramInformation]", _fontBold, _headerFontSize); + + var infoY = 72 + fontHeight; + var programInfoRect = new Rect(50, infoY, pageRect.Width - 50, infoY + 80); + page.DrawRect(programInfoRect, color: _pastelBlue, dashes: "[5 .2] 0"); + + float textboxWidth = 125; + + float startX, x0, startY, y0; + x0 = startX = programInfoRect.X0 + 5; + y0 = startY = programInfoRect.Y0 + 5; + + var programNameDict = new Dictionary() + { + { + "ProgramName", + CreateTextbox("ProgramName", "program.ProgramDict.Name") + } + }; + + var point = new Point(x0, y0); + + var lhs = textboxes.Take(6); + var maxLabelLen = float.Max(lhs.Max(l => l.Value.LabelLength), programNameDict["ProgramName"].LabelLength); + + /* SPECIAL CASE: The ProgramName field needs to span the entire width + * of the programInfoRect. All the other fields will split into two columns.*/ + var programNameTextboxWidth = programInfoRect.X1 - 60 - maxLabelLen; + DrawTextboxGroup(page, writer, programNameDict, point, + TextboxGroupDirection.Vertical, programNameTextboxWidth, maxLabelLen); + point.Y = startY + fontHeight; + + DrawTextboxGroup(page, writer, lhs, point, + TextboxGroupDirection.Vertical, textboxWidth, maxLabelLen); + + var rhs = textboxes.Skip(6); + maxLabelLen = rhs.Max(l => l.Value.LabelLength); + + point.X = programInfoRect.X1 - maxLabelLen - textboxWidth - 5; + point.Y = startY + fontHeight; + + DrawTextboxGroup(page, writer, rhs, point, TextboxGroupDirection.Vertical, textboxWidth); + return programInfoRect.BottomLeft; + } + + private (Point point, bool overflow, float filledHeight) DrawStepDescription(Page page, Point start, Rect rect, Story story) + { + var pageRect = page.Rect; + + float x0 = start.X; + float y0 = start.Y; + + bool overflow = false; + + using var ms = new MemoryStream(); + var docWriter = new DocumentWriter(ms); + + Rect descRect = new(x0, y0, rect.X1, y0); + + // Check if rectangle is going to overflow page + var remHeight = pageRect.Height - (y0 + rect.Height) - 72; + + if (remHeight >= 0) // We have enough space on this page. + { + story.Place(rect); + var device = docWriter.BeginPage(rect); + story.Draw(device); + + descRect.Y1 += rect.Y1; + } + else // Not enough space on page, draw what we can + { + var fillHeight = pageRect.Height - y0 - 72; + Rect? filled = new(0, 0, rect.Width, fillHeight); + + story.Place(filled); + var device = docWriter.BeginPage(filled); + story.Draw(device); + + descRect.Y1 += fillHeight; + overflow = true; + } + + docWriter.EndPage(); + docWriter.Close(); + + var buf = ms.GetBuffer(); + var doc = new Document(stream: buf); + + page.ShowPdfPage(descRect, doc); + + page.DrawRect(descRect, color: _pastelBlue, dashes: "[5 .2] 0"); + + return (new Point(x0, descRect.Y1), overflow, descRect.Height); + } + +} diff --git a/Examples/StoryRendering/StoryRender/StoryRender.csproj b/Examples/StoryRendering/StoryRender/StoryRender.csproj new file mode 100644 index 0000000..5ab573c --- /dev/null +++ b/Examples/StoryRendering/StoryRender/StoryRender.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + From e8118e4eab33f76efe455291ec7f7e2aa3161c81 Mon Sep 17 00:00:00 2001 From: Jamie Lemon Date: Thu, 26 Jun 2025 15:52:37 +0100 Subject: [PATCH 2/2] Simplifies to just the basic principle --- .../StoryRendering/StoryRender/Program.cs | 392 +----------------- 1 file changed, 12 insertions(+), 380 deletions(-) diff --git a/Examples/StoryRendering/StoryRender/Program.cs b/Examples/StoryRendering/StoryRender/Program.cs index eca3ebd..037f8cb 100644 --- a/Examples/StoryRendering/StoryRender/Program.cs +++ b/Examples/StoryRendering/StoryRender/Program.cs @@ -5,399 +5,31 @@ public class PDFRenderer { - private readonly MuPDF.NET.Font _font = new("helv"); - private readonly MuPDF.NET.Font _fontBold = new("hebo"); - private readonly int _defaultFontSize = 8; - private readonly int _headerFontSize = 12; - - /* Margins */ - private readonly int _defaultMarginVertical = 12; - private readonly int _defaultMarginHorizontal = 12; - - private readonly MuPDF.NET.Point _pageStart = new(50, 72); - - private readonly string _HTML = "

生产准备:

1. 每日生产进行维护保养,请参照并填写Philips 自动螺丝起点检表《WI-Screw assembly-Makita DF010&Kilews

SKD-B512L-F01》

2 .扭力计UNIT选择‘lbf.in’,‘P-P’模式,每四小时检查一次,每次检查5组数据,只有合格才可以生产;并填写

力扭矩记录表,表单号 : F-EN-34 。

1.电动起子力矩: 5±1 in-lbs,电动螺丝起编号:5.0。

2.电动起子力矩:10±1 in-lbs,电动螺丝起编号:10.0。

3.电动起子力矩:12±1 in-lbs,电动螺丝起编号:12.0。

Colten - line break

生产准备:

1. 每日生产进行维护保养,请参照并填写Philips 自动螺丝起点检表《WI-Screw assembly-Makita DF010&Kilews

SKD-B512L-F01》

2 .扭力计UNIT选择‘lbf.in’,‘P-P’模式,每四小时检查一次,每次检查5组数据,只有合格才可以生产;并填写

力扭矩记录表,表单号 : F-EN-34 。

1.电动起子力矩: 5±1 in-lbs,电动螺丝起编号:5.0。

2.电动起子力矩:10±1 in-lbs,电动螺丝起编号:10.0。

3.电动起子力矩:12±1 in-lbs,电动螺丝起编号:12.0。

Colten - line break

生产准备:

1. 每日生产进行维护保养,请参照并填写Philips 自动螺丝起点检表《WI-Screw assembly-Makita DF010&Kilews

SKD-B512L-F01》

2 .扭力计UNIT选择‘lbf.in’,‘P-P’模式,每四小时检查一次,每次检查5组数据,只有合格才可以生产;并填写

力扭矩记录表,表单号 : F-EN-34 。

1.电动起子力矩: 5±1 in-lbs,电动螺丝起编号:5.0。

2.电动起子力矩:10±1 in-lbs,电动螺丝起编号:10.0。

3.电动起子力矩:12±1 in-lbs,电动螺丝起编号:12.0。

Colten - line break

生产准备:

1. 每日生产进行维护保养,请参照并填写Philips 自动螺丝起点检表《WI-Screw assembly-Makita DF010&Kilews

SKD-B512L-F01》

2 .扭力计UNIT选择‘lbf.in’,‘P-P’模式,每四小时检查一次,每次检查5组数据,只有合格才可以生产;并填写

力扭矩记录表,表单号 : F-EN-34 。

1.电动起子力矩: 5±1 in-lbs,电动螺丝起编号:5.0。

2.电动起子力矩:10±1 in-lbs,电动螺丝起编号:10.0。

3.电动起子力矩:12±1 in-lbs,电动螺丝起编号:12.0。

"; - - - struct TextBox - { - public required string Label; - public required float LabelLength; - public required string Content; - public required float ContentLength; - } - - /// - /// Describes which direction a textbox group should be rendered. - /// - enum TextboxGroupDirection - { - Horizontal, - Vertical - } - - /* Define colors. MuPDF expects rbg values to be unit values (i.e., 0-1).*/ - private readonly float[] _gray = ConvertToURGB(211, 211, 211); - private readonly float[] _pastelBlue = ConvertToURGB(167, 199, 231); - - /// - /// This method simply converts RGB values to Unit RGB. - /// - /// - /// - /// - /// - private static float[] ConvertToURGB(int red, int green, int blue) - => [red / (float)255, green / (float)255, blue / (float)255]; - - /// - /// Calculates the center of Rect Y axis including - /// an offset for the font height. - /// - /// - /// - /// - /// - private static float CenterFontY(float y0, float y1, float fontHeight) - => ((y0 + y1) / 2) + (fontHeight / 2f) - 2; - - private string ResizeContent(string txt, float maxWidth) - { - var ellipsis = "..."; - var ellipsisLen = _font.TextLength(ellipsis); - - var newContent = ""; - foreach (var c in txt) - { - var len = _font.TextLength(newContent, fontSize: _defaultFontSize); - if (len + ellipsisLen >= maxWidth) - { - newContent += ellipsis; - break; - } - - newContent += c; - } - - return newContent; - } - - - /// - /// Helper function to create a Textbox struct. - /// It will calculate the label length and content length. - /// - /// - /// - /// - private TextBox CreateTextbox(string lbl, string content) - { - var labelLen = _font.TextLength(lbl, fontSize: _defaultFontSize); - var contentLen = _font.TextLength(content, fontSize: _defaultFontSize); - return new TextBox - { - Label = lbl, - LabelLength = labelLen, - Content = content, - ContentLength = contentLen, - }; - } + private readonly string _HTML = "

The Start

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

Lorem Ipsum

The End

"; public void GeneratePDF() { Console.WriteLine("GeneratePDF"); - Document doc = new Document(); // empty output PDF - Page page = doc.NewPage(-1); - MuPDF.NET.TextWriter writer = new MuPDF.NET.TextWriter(page.Rect); - - var point = DrawProgramInfoRect(page, writer); - - /* Now onto the steps */ - point.Y += (_headerFontSize * 1.2f); - - writer.Append(point, "Steps", _fontBold, fontSize: _headerFontSize); + Rect mediaBox = MuPDF.NET.Utils.PaperRect("A4"); + Rect where = mediaBox + new Rect(36, 36, -36, -36); var html = _HTML; - Story descStory = new(html); - - var storyWidth = page.Rect.Width - point.X; - var fit = descStory.FitHeight(storyWidth, origin: new Point(0, 0)); - - bool overflow; - float filledHeight; - if (fit.Filled is Rect filled) - { - while (true) - { - (point, overflow, filledHeight) = DrawStepDescription(page, point, filled, descStory); - - if (!overflow) break; - - filled.Y1 -= filledHeight; - - /* Need a new page since we have overflow. Before - * making the new page, need to finish writing everything - * on the previous page. */ - writer.WriteText(page); - page = doc.NewPage(-1); - writer = new(page.Rect); - point.X = _pageStart.X; - point.Y = _pageStart.Y; - } - } - - point.X = _pageStart.X; - point.Y += _defaultMarginVertical; - - point.Y += _headerFontSize * 1.2f; - - writer.Append(point, "Components", _fontBold, _headerFontSize); - - point.Y += _defaultMarginVertical; - - - writer.WriteText(page); - - page = doc.NewPage(-1); - writer = new(page.Rect); - - point.X = _pageStart.X; - point.Y = _pageStart.Y; - - - doc.Save("from_html.pdf", garbage: 4, deflate: 1, useObjstms: 1, deflateImages: 1); - - - - } - - /// - /// Adds a list of textboxes to the page. They will be aligned either - /// vertically or horizontally depening on the direction. - /// - /// - /// - /// - /// - /// - /// IEnumerable> - private Point DrawTextboxGroup(Page page, MuPDF.NET.TextWriter writer, IEnumerable> textboxes, - Point start, TextboxGroupDirection dir, float textboxWidth, float maxLabelLen = -1) - { - var x0 = start.X; - var y0 = start.Y; - var fontHeight = _defaultFontSize * 1.2f; - if (maxLabelLen < 0) - { - maxLabelLen = textboxes.Max(l => l.Value.LabelLength); - } - - //MuPDF.NET.TextWriter writer = new(page.Rect); - - foreach (var tb in textboxes) - { - var centerP = new Point(x0, CenterFontY(y0, y0 + fontHeight, fontHeight)); - - writer.Append(centerP, tb.Value.Label, _font, _defaultFontSize); - - /* Give the textbox some breathing room. */ - var textboxMargin = 5; - var tbWidth = textboxWidth - textboxMargin; - x0 = x0 + maxLabelLen + textboxMargin; - - var textboxRect = new Rect(x0, y0, x0 + tbWidth, y0 + fontHeight); - page.DrawRect(textboxRect, color: _gray, fill: _gray, fillOpacity: .25f, dashes: "[5 .2] 0"); - - centerP.X = x0; - centerP.Y = CenterFontY(textboxRect.TopLeft.Y, textboxRect.BottomLeft.Y, fontHeight); - - /* Resize the content if it doesn't fit in the textbox */ - var content = tb.Value.Content; - if (tb.Value.ContentLength > tbWidth) - { - content = ResizeContent(tb.Value.Content, tbWidth); - } - - writer.Append(centerP, content, _font, _defaultFontSize); - - if (dir == TextboxGroupDirection.Horizontal) - { - x0 = textboxRect.TopRight.X + 5; - y0 = start.Y; - } - else - { - x0 = start.X; - y0 = textboxRect.BottomLeft.Y; - } - } + Story story = new(html); + var docWriter = new DocumentWriter("my-writer-pdf.pdf"); - return new Point(x0, y0); - } - - /// - /// Draw the Program Information box along with all related - /// program metadata. - /// - /// - /// - private Point DrawProgramInfoRect(Page page, MuPDF.NET.TextWriter writer) - { - var pageRect = page.Rect; - var fontHeight = _defaultFontSize * 1.2f; + bool more = true; - /* Define all labels before hand to determine - * start point of the textboxes. */ - Dictionary textboxes = new() + while (more) { - /* LHS */ - { - "Customer", - CreateTextbox("Customer", "program.Customer!.Name") - }, - { - "DC", - CreateTextbox("DC", "program.ProgramDict.DC") - }, - { - "OMD", - CreateTextbox("OMD", "program.ProgramDict.OMD") - }, - { - "Version", - CreateTextbox("Version", "V{program.ProgramDict.Version}") - }, - { - "LastChangedBy", - CreateTextbox("LastChangedBy", "program.ProgramDict.LastModifiedBy?.Username") - }, - { - "Submitted", - CreateTextbox("Submitted", "program.ProgramDict.Submitted?.ToString() ??") - }, - - /* RHS */ - { - "Assembly", - CreateTextbox("Assembly", "program.Assembly!.Name") - }, - { - "Previous DC", - CreateTextbox("PreviousDC", "program.ProgramDict.PreviousDC") - }, - { - "ReleaseStatus", - CreateTextbox("ReleaseStatus", "program.ProgramDict.ReleaseStatus.ToString()") - }, - { - "PreviousVersion", - CreateTextbox("PreviousVersion", "V{program.ProgramDict.PreviousVersion}") - }, - { - "LastChanged", - CreateTextbox("LastChanged", "program.ProgramDict.LastModified.ToString() ?? ") - }, - { - "Released", - CreateTextbox("Released", "program.ProgramDict.Released?.ToString() ?? ") - }, - }; - - writer.Append(new Point(50, 72), "l[ProgramInformation]", _fontBold, _headerFontSize); - - var infoY = 72 + fontHeight; - var programInfoRect = new Rect(50, infoY, pageRect.Width - 50, infoY + 80); - page.DrawRect(programInfoRect, color: _pastelBlue, dashes: "[5 .2] 0"); - - float textboxWidth = 125; - - float startX, x0, startY, y0; - x0 = startX = programInfoRect.X0 + 5; - y0 = startY = programInfoRect.Y0 + 5; - - var programNameDict = new Dictionary() - { - { - "ProgramName", - CreateTextbox("ProgramName", "program.ProgramDict.Name") - } - }; - - var point = new Point(x0, y0); - - var lhs = textboxes.Take(6); - var maxLabelLen = float.Max(lhs.Max(l => l.Value.LabelLength), programNameDict["ProgramName"].LabelLength); - - /* SPECIAL CASE: The ProgramName field needs to span the entire width - * of the programInfoRect. All the other fields will split into two columns.*/ - var programNameTextboxWidth = programInfoRect.X1 - 60 - maxLabelLen; - DrawTextboxGroup(page, writer, programNameDict, point, - TextboxGroupDirection.Vertical, programNameTextboxWidth, maxLabelLen); - point.Y = startY + fontHeight; - - DrawTextboxGroup(page, writer, lhs, point, - TextboxGroupDirection.Vertical, textboxWidth, maxLabelLen); - - var rhs = textboxes.Skip(6); - maxLabelLen = rhs.Max(l => l.Value.LabelLength); - - point.X = programInfoRect.X1 - maxLabelLen - textboxWidth - 5; - point.Y = startY + fontHeight; - - DrawTextboxGroup(page, writer, rhs, point, TextboxGroupDirection.Vertical, textboxWidth); - return programInfoRect.BottomLeft; - } - - private (Point point, bool overflow, float filledHeight) DrawStepDescription(Page page, Point start, Rect rect, Story story) - { - var pageRect = page.Rect; - - float x0 = start.X; - float y0 = start.Y; - - bool overflow = false; - - using var ms = new MemoryStream(); - var docWriter = new DocumentWriter(ms); - - Rect descRect = new(x0, y0, rect.X1, y0); - - // Check if rectangle is going to overflow page - var remHeight = pageRect.Height - (y0 + rect.Height) - 72; - - if (remHeight >= 0) // We have enough space on this page. - { - story.Place(rect); - var device = docWriter.BeginPage(rect); - story.Draw(device); - - descRect.Y1 += rect.Y1; - } - else // Not enough space on page, draw what we can - { - var fillHeight = pageRect.Height - y0 - 72; - Rect? filled = new(0, 0, rect.Width, fillHeight); - - story.Place(filled); - var device = docWriter.BeginPage(filled); + DeviceWrapper device = docWriter.BeginPage(mediaBox); + (bool, Rect) moreFilled = story.Place(where); + more = moreFilled.Item1; story.Draw(device); - - descRect.Y1 += fillHeight; - overflow = true; + docWriter.EndPage(); + Console.WriteLine("more" + more); } - - docWriter.EndPage(); docWriter.Close(); - - var buf = ms.GetBuffer(); - var doc = new Document(stream: buf); - - page.ShowPdfPage(descRect, doc); - - page.DrawRect(descRect, color: _pastelBlue, dashes: "[5 .2] 0"); - - return (new Point(x0, descRect.Y1), overflow, descRect.Height); } }