TagTextLayout.vb
  1. ''
  2. '' This code is part of Document Solutions for PDF demos.
  3. '' Copyright (c) MESCIUS inc. All rights reserved.
  4. ''
  5. Imports System.IO
  6. Imports System.Drawing
  7. Imports GrapeCity.Documents.Pdf
  8. Imports GrapeCity.Documents.Text
  9. Imports GrapeCity.Documents.Drawing
  10. Imports GrapeCity.Documents.Pdf.Structure
  11. Imports GrapeCity.Documents.Pdf.MarkedContent
  12.  
  13. '' This sample shows how To create tagged (structured) PDF And attach
  14. '' tags to individual paragraphs in a TextLayout that is used to render
  15. '' them together, splitting between pages.
  16. '' The code generating the document is similar to that used in PaginatedText,
  17. '' but adds tags.
  18. '' To see/explore the tags, open the document in Adobe Acrobat Pro and go to
  19. '' View | Navigation Panels | Tags.
  20. Public Class TagTextLayout
  21. Function CreatePDF(ByVal stream As Stream) As Integer
  22. Dim doc = New GcPdfDocument()
  23.  
  24. '' Create a Part element, it will contain P (paragraph) elements:
  25. Dim sePart = New StructElement("Part")
  26. doc.StructTreeRoot.Children.Add(sePart)
  27.  
  28. '' Create and set up a TextLayout to render paragraphs:
  29. Dim tl = New TextLayout(72)
  30. tl.DefaultFormat.Font = StandardFonts.Times
  31. tl.DefaultFormat.FontSize = 12
  32. tl.FirstLineIndent = 72 / 2
  33. tl.MaxWidth = doc.PageSize.Width
  34. tl.MaxHeight = doc.PageSize.Height
  35. tl.MarginAll = tl.Resolution
  36. ''
  37. '' Append the text (20 paragraphs so they would not fit on a single page)
  38. '' (note that TextLayout interprets vbCrLf as paragraph delimiter):
  39. ''
  40. '' Get the text (20 paragraphs):
  41. Dim text = Util.LoremIpsum(20)
  42. '' In order to tag the individual paragraphs, we need to split the text into paragraphs,
  43. '' and use each paragraph format's Tag property (which is not related to PDF tags,
  44. '' it is just an arbitrary data that can be associated with a TextFormat) to add the
  45. '' paragraph's index to the paragraph:
  46. Dim pars = text.Split(New Char() {vbCr, vbLf}, StringSplitOptions.RemoveEmptyEntries)
  47. For i = 0 To pars.Length - 1
  48. Dim tf = New TextFormat(tl.DefaultFormat) With {.Tag = i}
  49. tl.AppendLine(pars(i), tf)
  50. Next
  51.  
  52. '' Layout the text:
  53. tl.PerformLayout(True)
  54. '' Use split options to provide widow/orphan control:
  55. Dim tso = New TextSplitOptions(tl) With {
  56. .MinLinesInFirstParagraph = 2,
  57. .MinLinesInLastParagraph = 2
  58. }
  59. '' TextLayoutHandler implements ITextLayoutHandler, which
  60. '' allows tagging the text as it is rendered:
  61. Dim tlh = New TextLayoutHandler() With {.ParentElement = sePart}
  62.  
  63. '' In a loop, split and render the text:
  64. While True
  65. '' 'rest' will accept the text that did not fit:
  66. Dim rest As TextLayout = Nothing
  67. Dim splitResult = tl.Split(tso, rest)
  68. Dim page = doc.Pages.Add()
  69. Dim g = page.Graphics
  70. '' Tell the TextLayoutHandler which page we're on:
  71. tlh.Page = page
  72. '' ..and associate it with the graphics:
  73. g.TextLayoutHandler = tlh
  74. '' Draw the text that fits on the current page, and advance to next page unless we're done:
  75. g.DrawTextLayout(tl, PointF.Empty)
  76. If splitResult <> SplitResult.Split Then
  77. Exit While
  78. End If
  79. tl = rest
  80. End While
  81. '' Mark document as tagged:
  82. doc.MarkInfo.Marked = True
  83. ''
  84. '' Done:
  85. doc.Save(stream)
  86. Return doc.Pages.Count
  87. End Function
  88.  
  89. '' Custom class that allows tagging content as it is rendered by TextLayout:
  90. Private Class TextLayoutHandler : Implements ITextLayoutHandler
  91. Private _tagIndex As Integer
  92. Private _currentParagraphIndex As Integer = -1
  93. Private _currentparagraphElement As StructElement
  94. Public Property ParentElement As StructElement
  95. Public Property Page As Page
  96.  
  97. Public Sub TextTagBegin(ByVal graphics As GcPdfGraphics, ByVal textLayout As TextLayout, ByVal tag As Object) Implements ITextLayoutHandler.TextTagBegin
  98. Dim paragraphIndex As Integer
  99. If TypeOf tag Is Integer Then
  100. paragraphIndex = CInt(tag)
  101. Else
  102. paragraphIndex = -1
  103. End If
  104.  
  105. Dim paragraphElement As StructElement
  106. If _currentParagraphIndex = paragraphIndex Then
  107. paragraphElement = _currentparagraphElement
  108. Else
  109. paragraphElement = New StructElement("P")
  110. ParentElement.Children.Add(paragraphElement)
  111. _currentparagraphElement = paragraphElement
  112. _currentParagraphIndex = paragraphIndex
  113. End If
  114. ''
  115. graphics.BeginMarkedContent(New TagMcid("P", _tagIndex))
  116. Dim mcil = New McrContentItemLink()
  117. mcil.MCID = _tagIndex
  118. mcil.Page = Page
  119. paragraphElement.ContentItems.Add(mcil)
  120. _tagIndex += 1
  121. End Sub
  122.  
  123. Public Sub TextTagEnd(ByVal graphics As GcPdfGraphics, ByVal textLayout As TextLayout, ByVal tag As Object) Implements ITextLayoutHandler.TextTagEnd
  124. graphics.EndMarkedContent()
  125. End Sub
  126.  
  127. Public Sub AddTextArea(ByVal bounds As RectangleF) Implements ITextLayoutHandler.AddTextArea
  128. End Sub
  129. End Class
  130. End Class
  131.