Magazine style, multi-column page layout with balanced columns and document outline

PDF TIFF SVG JPG C# VB
BalancedColumns.cs
  1. //
  2. // This code is part of Document Solutions for PDF demos.
  3. // Copyright (c) MESCIUS inc. All rights reserved.
  4. //
  5. using System;
  6. using System.IO;
  7. using System.Drawing;
  8. using GrapeCity.Documents.Pdf;
  9. using GrapeCity.Documents.Text;
  10.  
  11. namespace DsPdfWeb.Demos.Basics
  12. {
  13. // Creates a multi-column text layout with balanced columns.
  14. // The heart of this sample is the TextLayout.SplitAndBalance() method
  15. // which allows splitting a text between multiple columns,
  16. // AND balance those columns so that their heights are similar,
  17. // thus allowing to produce magazine- and newspaper-like text layouts.
  18. public class BalancedColumns
  19. {
  20. public int CreatePDF(Stream stream)
  21. {
  22. var doc = new GcPdfDocument();
  23. var font = StandardFonts.Times;
  24. var fontSize = 12;
  25. // 1/2" margins all around (72 dpi is the default resolution used by DsPdf):
  26. var margin = 72 / 2;
  27. var pageWidth = doc.PageSize.Width;
  28. var pageHeight = doc.PageSize.Height;
  29. var cW = pageWidth - margin * 2;
  30. // Text format for the chapter titles:
  31. var tlCaption = new TextLayout(72);
  32. tlCaption.DefaultFormat.Font = font;
  33. tlCaption.DefaultFormat.FontSize = fontSize + 4;
  34. tlCaption.DefaultFormat.Underline = true;
  35. tlCaption.MaxWidth = pageWidth;
  36. tlCaption.MaxHeight = pageHeight;
  37. tlCaption.MarginLeft = tlCaption.MarginTop = tlCaption.MarginRight = tlCaption.MarginBottom = margin;
  38. tlCaption.TextAlignment = TextAlignment.Center;
  39. // Height of chapter caption (use a const for simplicity):
  40. const float captionH = 24;
  41. // Text layout for main document body (default DsPdf resolution is 72dpi):
  42. var tl = new TextLayout(72);
  43. tl.DefaultFormat.Font = font;
  44. tl.DefaultFormat.FontSize = fontSize;
  45. tl.FirstLineIndent = 72 / 2;
  46. tl.MaxWidth = pageWidth;
  47. tl.MaxHeight = pageHeight;
  48. tl.MarginLeft = tl.MarginRight = tl.MarginBottom = margin;
  49. tl.MarginTop = margin + captionH;
  50. tl.ColumnWidth = cW * 0.3f;
  51. tl.TextAlignment = TextAlignment.Justified;
  52. // Array of PageSplitArea's which control additional columns (1st column is controlled by
  53. // the 'main' TextLayout, for each additional one a PageSplitArea must be provided -
  54. // it will create and return a TextLayout that can then be used to render the column):
  55. var psas = new PageSplitArea[]
  56. {
  57. new PageSplitArea(tl) { MarginLeft = tl.MarginLeft + (cW * 0.35f) },
  58. new PageSplitArea(tl) { ColumnWidth = -cW * 0.3f }
  59. };
  60. // Split options to control splitting text between pages:
  61. var tso = new TextSplitOptions(tl)
  62. {
  63. RestMarginTop = margin,
  64. MinLinesInFirstParagraph = 2,
  65. MinLinesInLastParagraph = 2
  66. };
  67. // Generate a number of "chapters", provide outline entry for each:
  68. const int NChapters = 20;
  69. doc.Pages.Add();
  70. for (int i = 0; i < NChapters; ++i)
  71. {
  72. // Print chapter header across all columns:
  73. string chapter = $"Chapter {i + 1}";
  74. tlCaption.Clear();
  75. tlCaption.Append(chapter);
  76. tlCaption.PerformLayout(true);
  77. doc.Pages.Last.Graphics.DrawTextLayout(tlCaption, PointF.Empty);
  78. // Add outline node for the chapter:
  79. doc.Outlines.Add(new OutlineNode(chapter, new DestinationFitV(doc.Pages.Last, null)));
  80. //
  81. // Clear last chapter's text and add new chapter:
  82. tl.FirstLineIsStartOfParagraph = true;
  83. tl.LastLineIsEndOfParagraph = true;
  84. tl.Clear();
  85. tl.Append(Common.Util.LoremIpsum(5, 7, 9, 15, 25));
  86. tl.PerformLayout(true);
  87. // Variable to hold last chapter end's bottom coord:
  88. float contentBottom = 0f;
  89. // Print the chapter:
  90. var tls = new TextLayoutSplitter(tl);
  91. while (true)
  92. {
  93. var tlCol0 = tls.SplitAndBalance(psas, tso);
  94. var g = doc.Pages.Last.Graphics;
  95. g.DrawTextLayout(tlCol0, PointF.Empty);
  96. g.DrawTextLayout(psas[0].TextLayout, PointF.Empty);
  97. g.DrawTextLayout(psas[1].TextLayout, PointF.Empty);
  98. if (tls.SplitResult != SplitResult.Split)
  99. {
  100. // End of chapter, find out how much height left on page for next chapter:
  101. contentBottom = tl.ContentY + tl.ContentHeight;
  102. contentBottom = Math.Max(contentBottom, psas[0].TextLayout.ContentRectangle.Bottom);
  103. contentBottom = Math.Max(contentBottom, psas[1].TextLayout.ContentRectangle.Bottom);
  104. // Done printing chapter:
  105. break;
  106. }
  107. // Continue printing chapter on new page:
  108. psas[0].MarginTop = psas[1].MarginTop = margin;
  109. doc.Pages.Add();
  110. }
  111. // Next chapter - find out if we have enough space left on current page to start new chapter:
  112. if (contentBottom + captionH < pageHeight * 0.8f)
  113. {
  114. // Start new chapter on current page:
  115. contentBottom += pageHeight * 0.05f;
  116. tlCaption.MarginTop = contentBottom;
  117. tl.MarginTop = psas[0].MarginTop = psas[1].MarginTop = contentBottom + captionH;
  118. }
  119. else if (i < NChapters - 1)
  120. {
  121. // Start new chapter on new page:
  122. tlCaption.MarginTop = margin;
  123. tl.MarginTop = psas[0].MarginTop = psas[1].MarginTop = margin + captionH;
  124. doc.Pages.Add();
  125. }
  126. }
  127. // Done:
  128. doc.Save(stream);
  129. return doc.Pages.Count;
  130. }
  131. }
  132. }
  133.