Rendering vertical Japanese text using a layout with horizontal columns

PDF TIFF SVG JPG C# VB
VerticalTextJP.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 System.Collections.Generic;
  9. using GrapeCity.Documents.Drawing;
  10. using GrapeCity.Documents.Pdf;
  11. using GrapeCity.Documents.Text;
  12. using GCTEXT = GrapeCity.Documents.Text;
  13. using GCDRAW = GrapeCity.Documents.Drawing;
  14.  
  15. namespace DsPdfWeb.Demos.Basics
  16. {
  17. // Draws vertical right-to-left Japanese text using a layout with horizontal columns.
  18. // See also ArabicColumns, MultiLang and VerticalText.
  19. public class VerticalTextJP
  20. {
  21. const string text = "日本語(にほんご、にっぽんご)は、主として、日本列島で使用されてきた言語である。日本手話を母語とする者などを除いて、ほぼ全ての日本在住者は日本語を第一言語とする。日本国は法令上、公用語を明記していないが、事実上の公用語となっており、学校教育の「国語」で教えられる。使用者は、日本国内を主として約\uFF11億\uFF13千万人。日本語の文法体系や音韻体系を反映する手話として日本語対応手話がある。";
  22.  
  23. public int CreatePDF(Stream stream)
  24. {
  25. using var clouds = GCDRAW.Image.FromFile(Path.Combine("Resources", "Images", "clouds.jpg"));
  26. using var firth = GCDRAW.Image.FromFile(Path.Combine("Resources", "Images", "firth.jpg"));
  27. using var lavender = GCDRAW.Image.FromFile(Path.Combine("Resources", "Images", "lavender.jpg"));
  28.  
  29. var fnt = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "NotoSansJP-Regular.ttf"));
  30. var ia = new ImageAlign(ImageAlignHorz.Left, ImageAlignVert.Top, true, true, true, false, false);
  31. var doc = new GcPdfDocument();
  32.  
  33. // The TextLayout that will hold and render the text:
  34. var tl = new TextLayout(72);
  35. tl.FirstLineIndent = 18;
  36. tl.ParagraphSpacing = 6;
  37. tl.FlowDirection = FlowDirection.VerticalRightToLeft;
  38. tl.TextAlignment = TextAlignment.Justified;
  39. var tf = new TextFormat() { Font = fnt, FontSize = 12 };
  40. // Repeat test text to fill a few pages:
  41. for (int i = 0; i < 25; ++i)
  42. {
  43. tl.Append(text, tf);
  44. tl.AppendLine();
  45. }
  46.  
  47. // Layout text in 4 horizontal columns:
  48. // (The logic/code in this sample is identical to ArabicColumns):
  49. const int NCOLS = 4;
  50. var margin = 36f;
  51. var gap = 18f;
  52. var page = doc.NewPage();
  53. page.Landscape = true;
  54. var colHeight = (page.Size.Height - margin * 2 - gap * (NCOLS - 1)) / NCOLS;
  55. tl.MaxWidth = page.Size.Width;
  56. tl.MaxHeight = page.Size.Height;
  57. tl.MarginLeft = tl.MarginRight = margin;
  58. tl.MarginTop = margin;
  59. tl.MarginBottom = margin + (colHeight + gap) * (NCOLS - 1);
  60. // We can specify arbitrary rectangles for the text to flow around.
  61. // In this case, we add 3 areas to draw some images:
  62. tl.ObjectRects = new List<ObjectRect>()
  63. {
  64. new ObjectRect(page.Size.Width - margin - 267, margin, 267, 200),
  65. new ObjectRect(margin + 100, margin + 60, 133, 100),
  66. new ObjectRect(margin, page.Size.Height - margin - 301, 200, 301),
  67. };
  68. // Convert object rects to image areas, adjust to provide nice looking padding:
  69. var rClouds = tl.ObjectRects[0].ToRectangleF();
  70. rClouds.Inflate(-4, -3);
  71. var rFirth = tl.ObjectRects[1].ToRectangleF();
  72. rFirth.Inflate(-4, -3);
  73. var rLavender = tl.ObjectRects[2].ToRectangleF();
  74. rLavender.Inflate(-4, -3);
  75. page.Graphics.DrawImage(clouds, rClouds, null, ia);
  76. page.Graphics.DrawImage(firth, rFirth, null, ia);
  77. page.Graphics.DrawImage(lavender, rLavender, null, ia);
  78.  
  79. // THE call: it calculates the glyphs needed to draw the text, and lays it out:
  80. tl.PerformLayout(true);
  81.  
  82. // Loop while there is still text to render:
  83. bool done = false;
  84. while (!done)
  85. {
  86. for (int col = 0; col < NCOLS; ++col)
  87. {
  88. int nextcol = (col < NCOLS - 1) ? col + 1 : 0;
  89. // TextSplitOptions tell TextLayout.Split() how to layout the remaining text.
  90. // In this case we advance from column to column by updating top and bottom margins:
  91. var tso = new TextSplitOptions(tl)
  92. {
  93. RestMarginTop = margin + (colHeight + gap) * nextcol,
  94. RestMarginBottom = margin + (colHeight + gap) * (NCOLS - 1 - nextcol)
  95. };
  96. var split = tl.Split(tso, out TextLayout rest);
  97. page.Graphics.DrawTextLayout(tl, PointF.Empty);
  98. if (split != SplitResult.Split)
  99. {
  100. done = true;
  101. break;
  102. }
  103. tl = rest;
  104. }
  105. if (!done)
  106. {
  107. page = doc.NewPage();
  108. page.Landscape = true;
  109. // We only want to render images on the first page, so clear ObjectRect:
  110. if (tl.ObjectRects != null)
  111. {
  112. tl.ObjectRects = null;
  113. // We need to redo the layout, but no need to recalculate the glyphs:
  114. tl.PerformLayout(false);
  115. }
  116. }
  117. }
  118. // Done:
  119. doc.Save(stream);
  120. return doc.Pages.Count;
  121. }
  122. }
  123. }
  124.