PDF Text Markup Annotations

This demo illustrates how you can find text and add markup to it with the help of the PDF Document API.

Select a file
Maximum file size 10Mb.
Options
Text to Find
Markup Type
Find and Markup
Download
@model AspNetCoreDemos.OfficeFileAPI.PdfPreviewModel

<img id="previewImage" src="@Url.Action(Model.PreviewDocumentAction, Model.ControllerName)" class="preview-image-bordered @(Model.IsViewWithoutSidePanel ? "preview-image-without-side-panel" : "preview-image-with-side-panel")" />

<script type="text/javascript">
    PdfPreview = {
        basePath: '@Url.Action(Model.PreviewDocumentAction, Model.ControllerName)',
        Update: function (param) {
            var iframeElement = document.getElementById("previewImage");
            if (!iframeElement)
                return;
            var additionalParams = "&" + new Date().valueOf();
            if (param)
                additionalParams = param;
            iframeElement.src = this.basePath + "?" + additionalParams;
        }
    };
</script>
@model AspNetCoreDemos.OfficeFileAPI.PdfTextMarkupAnnotationsModel
@using DevExtreme.AspNet.Mvc

@{ Html.BeginForm("PdfTextMarkupAnnotationsUploadFiles", "ContentManipulation", FormMethod.Post); }
@Html.HiddenFor(model => model.DocumentUrl)

<script type="text/javascript">
    function FindAndMarkup() {
        $.ajax({
            type: "POST",
            url: '@Url.Action("PdfFindAndMarkup", "ContentManipulation")',
            data: {
                text: $("#Find").dxTextBox('instance').option('value'),
                markupType: $("#MarkupType").dxSelectBox('instance').option('value')
            },
            success: function () { PdfPreview.Update(); }
        });
    }

    function fileUploader_valueChanged(e) {
        var files = e.value;
        if (files.length > 0) {
            $("#selected-files .selected-item").remove();

            $.each(files, function (i, file) {
                var $selectedItem = $("<div />").addClass("selected-item");
                $selectedItem.append(file.name);
                $selectedItem.appendTo($("#selected-files"));
            });
            $("#selected-files").show();

            $('#DocumentUrl').val(files[0].name);
        }
        else
            $("#selected-files").hide();
    }

    function UpdatePreview() {
        var params = "DocumentUrl=" + $('#DocumentUrl').val();
        PdfPreview.Update(params);
    }
</script>

<div class="demo-view-container">
    <div style="margin-left: -8px">
        @(Html.DevExtreme().FileUploader()
                           .ID("file-uploader")
                           .Name("myFile")
                           .Multiple(false)
                           .ShowFileList(false)
                           .Accept(".pdf")
                           .AllowedFileExtensions(new List<string> { ".pdf" })
                           .MaxFileSize(10485760)
                           .LabelText("Maximum file size 10Mb.")
                           .UploadMode(FileUploadMode.Instantly)
                           .UploadUrl(Url.Action("DocumentUpload", "ContentManipulation"))
                           .OnValueChanged("fileUploader_valueChanged")
                           .OnUploaded("UpdatePreview")
        )
    </div>
    <div class="demo-preview-border">
        @(Html.DevExtreme().ScrollView()
                           .ID("scrollview")
                           .ScrollByContent(true)
                           .ScrollByThumb(true)
                           .ShowScrollbar(ShowScrollbarMode.OnHover)
                           .Direction(ScrollDirection.Both)
                           .Height("580px")
                           .Content(@<text>
                                <div id="scrollview-content">
                                    @await Html.PartialAsync("PdfFileView", Model.PreviewModel)
                                </div>
                           </text>)
        )
    </div>
</div>

<div class="options">
    <div class="caption">Options</div>
    <div class="option">
        <div class="label">Text to Find</div>
        @(Html.DevExtreme().TextBox().ID("Find"))
    </div>
    <div class="option">
        <div class="label">Markup Type</div>
        @(Html.DevExtreme().SelectBox()
                           .ID("MarkupType")
                           .DataSource(Html.GetEnumSelectList<AspNetCoreDemos.OfficeFileAPI.MarkupType>()
                           .Select(i => new { Value = int.Parse(i.Value), Text = i.Text }))
                           .ValueExpr("Value")
                           .DisplayExpr("Text")
                           .Value(0)
        )
    </div>
    <div class="option-buttons">
        @(Html.DevExtreme().Button()
                           .Text("Find and Markup")
                           .Type(ButtonType.Default)
                           .StylingMode(ButtonStylingMode.Contained)
                           .OnClick("FindAndMarkup")
        )
        @(Html.DevExtreme().Button()
                           .Text("Download")
                           .Type(ButtonType.Default)
                           .StylingMode(ButtonStylingMode.Contained)
                           .UseSubmitBehavior(true)
                           .ElementAttr("style", "float: right")
        )
    </div>
</div>

@{ Html.EndForm(); }
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;

namespace AspNetCoreDemos.OfficeFileAPI {
    public partial class ContentManipulationController : DocumentProcessingController {
        public ContentManipulationController(ILogger<ContentManipulationController> logger, IWebHostEnvironment hostingEnvironment)
            : base(logger, hostingEnvironment) {
        }
    }
}
using System.IO;
using Microsoft.AspNetCore.Mvc;
using DevExpress.Pdf;

namespace AspNetCoreDemos.OfficeFileAPI {
    public partial class ContentManipulationController {
        const string textMarkupDefaultFile = "/Documents/Pdf/TextMarkup.pdf";
        protected override string SessionKey => "TextMarkupAnnotationsFileKey";

        public IActionResult PdfTextMarkupAnnotations() {
            return GetDemoView<PdfTextMarkupAnnotationsModel>("PdfTextMarkupAnnotations", SessionKey, HostingEnvironment.ContentRootPath + textMarkupDefaultFile);
        }

        public IActionResult DocumentViewPartial(PdfTextMarkupAnnotationsModel textMarkupModel) {
            return DocumentViewPartialAnotherDocument(textMarkupModel);
        }

        public IActionResult PdfTextMarkupAnnotationsUploadFiles() {
            using(PdfDocumentProcessor processor = new PdfDocumentProcessor()) {
                byte[] data;
                if(!HttpContext.Session.TryGetValue(SessionKey, out data))
                    return new EmptyResult();
                using(MemoryStream inputStream = new MemoryStream(data)) {
                    processor.LoadDocument(inputStream);
                    MemoryStream resultStream = new MemoryStream();
                    processor.SaveDocument(resultStream);
                    return CreateFileStreamResult(resultStream, "application/pdf", "pdf", "Result");
                }
            }
        }

        public IActionResult PdfFindAndMarkup(string text, int markupType) {
            using(PdfTextMarkupAnnotationsModel model = new PdfTextMarkupAnnotationsModel()) {
                byte[] data;
                if(HttpContext.Session.TryGetValue(SessionKey, out data)) {
                    model.LoadDocument(data);
                    HttpContext.Session.Set(SessionKey, model.FindAndMarkup(text, (PdfTextMarkupAnnotationType)markupType));
                }
                return new EmptyResult();
            }
        }

        public IActionResult DownloadResult() {
            byte[] data;
            if(HttpContext.Session.TryGetValue(SessionKey, out data))
                return CreateFileStreamResult(new MemoryStream(data), "application/pdf", "pdf", "Result");
            return new BadRequestResult();
        }
    }
}
using System;
using System.Collections.Generic;
using System.IO;
using DevExpress.Pdf;

namespace AspNetCoreDemos.OfficeFileAPI {
    public class PdfModelBase : IDisposable {
        readonly PdfDocumentProcessor processor;
        MemoryStream stream;
        List<PdfPageModel> items = new List<PdfPageModel>();

        public MemoryStream Stream { get { return stream; } }
        protected PdfDocumentProcessor Processor { get { return processor; } }
        protected PdfDocument Document { get { return processor.Document; } }
        public List<PdfPageModel> Items { get { return items; } }

        public string DocumentUrl { get; set; }
        public int PageIndex { get; set; }
        internal virtual string SessionKey { get; }
        public PdfPreviewModel PreviewModel { get; internal set; }

        public PdfModelBase() {
            processor = new PdfDocumentProcessor();
            PreviewModel = new PdfPreviewModel();
        }

        protected void CreateEmptyDocument() {
            stream = new MemoryStream();
            Processor.CreateEmptyDocument(stream);
            Document.Creator = "PDF Document Processor Demo";
            Document.Producer = "Developer Express Inc., " + AssemblyInfo.Version;
            Document.Author = "DevExpress Inc.";
        }

        public virtual void LoadDocument(byte[] data) {
            using(MemoryStream stream = new MemoryStream(data))
                LoadDocument(stream);
        }

        protected void LoadDocument(Stream stream) {
            processor.LoadDocument(stream, true);
            for(int pageNumber = 1; pageNumber <= processor.Document.Pages.Count; pageNumber++)
                Items.Add(new PdfPageModel(processor, pageNumber));
        }

        public void Dispose() {
            if(processor != null)
                processor.Dispose();
            GC.SuppressFinalize(this);
        }
    }
}
using System.ComponentModel.DataAnnotations;
using System.IO;
using DevExpress.Pdf;

namespace AspNetCoreDemos.OfficeFileAPI {
    public enum MarkupType {
        Highlight = 0,
        Underline = 1,
        [Display(Name = "Squiggly underline")]
        Squiggly = 2,
        Strikeout = 3
    }

    public class PdfTextMarkupAnnotationsModel : PdfModelBase {

        public PdfTextMarkupAnnotationsModel() {
            PreviewModel.PreviewDocumentAction = "DocumentViewPartial";
            PreviewModel.ControllerName = "ContentManipulation";
        }

        internal override string SessionKey { get { return "TextMarkupAnnotationsFileKey"; } }

        public byte[] FindAndMarkup(string textToFind, PdfTextMarkupAnnotationType markupType) {
            PdfDocumentProcessor processor = Processor;
            PdfTextSearchResults searchResults = processor.FindText(textToFind);
            if(searchResults.Status == PdfTextSearchStatus.Found) {
                while(searchResults.Status == PdfTextSearchStatus.Found) {
                    processor.AddTextMarkupAnnotation(searchResults.PageNumber, searchResults.Rectangles, markupType);
                    searchResults = processor.FindText(textToFind);
                }
            }
            using (MemoryStream stream = new MemoryStream()) {
                processor.SaveDocument(stream);
                return stream.ToArray();
            }
        }
    }
}