//
// This code is part of Document Solutions for PDF demos.
// Copyright (c) MESCIUS inc. All rights reserved.
//
using System;
using System.IO;
using System.Drawing;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Pdf.Structure;
using GrapeCity.Documents.Pdf.MarkedContent;
namespace DsPdfWeb.Demos.Basics
{
// This sample shows how to create tagged (structured) PDF and attach
// tags to individual paragraphs in a TextLayout that is used to render
// them together, splitting between pages.
// The code generating the document is similar to that used in PaginatedText,
// but adds tags.
// To see/explore the tags, open the document in Adobe Acrobat Pro and go to
// View | Navigation Panels | Tags.
public class TagTextLayout
{
public int CreatePDF(Stream stream)
{
var doc = new GcPdfDocument();
// Create a Part element, it will contain P (paragraph) elements:
var sePart = new StructElement("Part");
doc.StructTreeRoot.Children.Add(sePart);
// Create and set up a TextLayout to render paragraphs:
var tl = new TextLayout(72);
tl.DefaultFormat.Font = StandardFonts.Times;
tl.DefaultFormat.FontSize = 12;
tl.FirstLineIndent = 72 / 2;
tl.MaxWidth = doc.PageSize.Width;
tl.MaxHeight = doc.PageSize.Height;
tl.MarginAll = tl.Resolution;
//
// Append the text (20 paragraphs so they would not fit on a single page)
// (note that TextLayout interprets "\r\n" as paragraph delimiter):
//
// Get the text (20 paragraphs):
var text = Common.Util.LoremIpsum(20);
// In order to tag the individual paragraphs, we need to split the text into paragraphs,
// and use each paragraph format's Tag property (which is not related to PDF tags,
// it is just an arbitrary data that can be associated with a TextFormat) to add the
// paragraph's index to the paragraph:
var pars = text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < pars.Length; ++i)
{
var tf = new TextFormat(tl.DefaultFormat) { Tag = i };
tl.AppendLine(pars[i], tf);
}
// Layout the text:
tl.PerformLayout(true);
// Use split options to provide widow/orphan control:
var to = new TextSplitOptions(tl)
{
MinLinesInFirstParagraph = 2,
MinLinesInLastParagraph = 2,
};
// TextLayoutHandler implements ITextLayoutHandler, which
// allows tagging the text as it is rendered:
var tlh = new TextLayoutHandler() { ParentElement = sePart };
// In a loop, split and render the text:
while (true)
{
// 'rest' will accept the text that did not fit:
var splitResult = tl.Split(to, out TextLayout rest);
var page = doc.Pages.Add();
var g = page.Graphics;
// Tell the TextLayoutHandler which page we're on:
tlh.Page = page;
// ..and associate it with the graphics:
g.TextLayoutHandler = tlh;
// Draw the text that fits on the current page, and advance to next page unless we're done:
g.DrawTextLayout(tl, PointF.Empty);
if (splitResult != SplitResult.Split)
break;
tl = rest;
}
// Mark document as tagged:
doc.MarkInfo.Marked = true;
// Done:
doc.Save(stream);
return doc.Pages.Count;
}
// Custom class that allows tagging content as it is rendered by TextLayout:
private class TextLayoutHandler : ITextLayoutHandler
{
private int _tagIndex;
private int _currentParagraphIndex = -1;
private StructElement _currentparagraphElement;
public StructElement ParentElement;
public Page Page;
public void TextTagBegin(GcPdfGraphics graphics, TextLayout textLayout, object tag)
{
int paragraphIndex;
if (tag is int)
paragraphIndex = (int)tag;
else
paragraphIndex = -1;
StructElement paragraphElement;
if (_currentParagraphIndex == paragraphIndex)
{
paragraphElement = _currentparagraphElement;
}
else
{
if (paragraphIndex >= 0)
{
paragraphElement = new StructElement("P");
ParentElement.Children.Add(paragraphElement);
_currentparagraphElement = paragraphElement;
_currentParagraphIndex = paragraphIndex;
}
else
{
paragraphElement = null;
_currentparagraphElement = paragraphElement;
_currentParagraphIndex = paragraphIndex;
}
}
//
if (paragraphElement != null)
{
graphics.BeginMarkedContent(new TagMcid("P", _tagIndex));
var mcil = new McrContentItemLink();
mcil.MCID = _tagIndex;
mcil.Page = Page;
paragraphElement.ContentItems.Add(mcil);
_tagIndex++;
}
}
public void TextTagEnd(GcPdfGraphics graphics, TextLayout textLayout, object tag)
{
if (_currentparagraphElement != null)
graphics.EndMarkedContent();
}
public void AddTextArea(RectangleF bounds)
{
}
}
}
}