GroupDocs.Viewer for .NET 18.4 Release Notes

Major Features

There are 18 new features, improvements, and fixes in this regular monthly release. The most notable are:

  • Added support for following file formats
    • POTX (PowerPoint Open XML Presentation Template)
    • PPTM (PowerPoint Open XML Macro-Enabled Presentation)
    • EPS (Encapsulated PostScript)
  • Implemented simple file storage interface
  • Improved minification process
  • Added settings to include or exclude hidden content in Excel documents
  • Added option for rendering Print Area in Excel documents

Full List of Issues Covering all Changes in this Release

KeySummaryCategory
VIEWERNET-1552Feature for AutoFitting column width depending on content for rendering into HTMLNew Feature
VIEWERNET-1544Implement simple file storage interfaceNew Feature
VIEWERNET-1518Settings to include/exclude hidden content in Excel documentsNew Feature
VIEWERNET-1517Rendering only Print Area in Excel documentsNew Feature
VIEWERNET-1478Add POTX file format supportNew Feature
VIEWERNET-1476Add PPTM file format supportNew Feature
VIEWERNET-526Add EPS (Encapsulated PostScript) file format supportNew Feature
VIEWERNET-1537Add prefix for CSS classes when rendering Email messagesImprovement
VIEWERNET-1501Minify CSS content when rendering into HTML with EnableMinification is trueImprovement
VIEWERNET-1481Improve rendering comments from Presentation documentsImprovement
VIEWERNET-1428Support JpegQuality option when rendering Microsoft Project documentsImprovement
VIEWERNET-1549Extend support for DefaultFontName setting to PDF documents when rendering into HTMLImprovement
VIEWERNET-512Responsive HTML output required in the case of HTML representationImprovement
VIEWERNET-1526Invalid PDF when rendering Excel document with multiple pages per sheetBug
VIEWERNET-1511DefaultFontName setting is not working for rendering Text documents into PDF and imageBug
VIEWERNET-1494Incorrect rendering of the content in header and footer of Word documentBug
WEB-2106Local links are ignored when rendering PDF to HTMLBug
WEB-1153Discrepancy when rendering as JPEG and HTMLBug

Public API and Backward Incompatible Changes

Managing Text Overflow when Rendering Cells Documents to HTML

When cells documents are rendered into HTML, overflowed text inside the cell overlays subsequent cells until it meets non-empty cell. To expand the cell width to fit the overflowed text, set CellsOptions.TextOverflowMode of HtmlOptions, ImageOptions or PdfOptions to TextOverflowMode.AutoFitColumn.

Setting overflowed text to be hidden when rendering Cells documents

// Setup GroupDocs.Viewer config
ViewerConfig config = new ViewerConfig();
config.StoragePath = @"C:\storage";
  
// Create html handler
ViewerHtmlHandler htmlHandler = new ViewerHtmlHandler(config);
string guid = "document.xlsx";
  
// Set Cells options to hide overflowing text
HtmlOptions options = new HtmlOptions();
options.CellsOptions.TextOverflowMode = TextOverflowMode.AutoFitColumn;
 
// Get pages 
List<PageHtml> pages = htmlHandler.GetPages(guid, options);
  
foreach (PageHtml page in pages)
{
    Console.WriteLine("Page number: {0}", page.PageNumber);
    Console.WriteLine("Html content: {0}", page.HtmlContent);
}

Improved Minification Process

Since the version 18.4, embedded CSS content is minified when the EnableMinification setting is on.

Details behind minification

Here is the list of technics that lay behind minification process:

CSS minification:

  • Remove all insignificant whitespace.
  • Remove all comments.
  • Remove all unnecessary semicolon separators.
  • Reduce color values.
  • Reduce integer representations by removing leading and trailing zeros.
  • Remove unit specifiers from numeric zero values.

Rendering Print Area in Cells Documents

Starting from 18.4 GroupDocs.Viewer provides new option RenderPrintAreaOnly in GroupDocs.Viewer.Converter.Options.CellsOptions class which enables rendering sections of the worksheet(s) defined as print area. GroupDocs.Viewer renders each print area in a worksheet as a separate page.

Rendering print area. This example shows how to render print area(s) when rendering to HTML.

 // Setup GroupDocs.Viewer config
ViewerConfig config = new ViewerConfig();
config.StoragePath = @"C:\storage";
 
// Create html handler
ViewerHtmlHandler htmlHandler = new ViewerHtmlHandler(config);
string guid = "document.xlsx";
 
// Enable redering of print area
HtmlOptions options = new HtmlOptions();
options.CellsOptions.RenderPrintAreaOnly = true;
 
// Get pages 
List<PageHtml> pages = htmlHandler.GetPages(guid, options);
 
foreach (PageHtml page in pages)
{
    Console.WriteLine("Page number: {0}", page.PageNumber);
    Console.WriteLine("Html content: {0}", page.HtmlContent);
}

Rendering print area.

Next example shows how to render print area(s) when rendering to image.

// Setup GroupDocs.Viewer config
ViewerConfig config = new ViewerConfig();
config.StoragePath = @"C:\storage";
 
// Create image handler
ViewerImageHandler imageHandler = new ViewerImageHandler(config);
string guid = "document.xlsx";
 
// Enable redering of print area
ImageOptions options = new ImageOptions();
options.CellsOptions.RenderPrintAreaOnly = true;
 
// Get pages 
List<PageImage> pages = imageHandler.GetPages(guid, options);
 
foreach (PageImage page in pages)
{
    Console.WriteLine("Page number: {0}", page.PageNumber);
    //Save image by accessing page.Stream
}

Rendering print area.

Next example shows how to render print area(s) when rendering as PDF.

// Setup GroupDocs.Viewer config
ViewerConfig config = new ViewerConfig();
config.StoragePath = @"C:\storage";
 
 
// Create image handler
ViewerImageHandler imageHandler = new ViewerImageHandler(config);
string guid = "document.xlsx";
 
 
// Enable redering of print area
PdfFileOptions options = new PdfFileOptions();
options.CellsOptions.RenderPrintAreaOnly = true;
 
// Get PDF file 
FileContainer container = imageHandler.GetPdfFile(guid, options);
 
 
//Save PDF file by accessing container.Stream

Rendering Hidden Rows and Columns

Starting from 18.4 GroupDocs.Viewer provides two new options ShowHiddenRows and ShowHiddenColumns in GroupDocs.Viewer.Converter.Options.CellsOptions class which enables rendering hidden rows and columns. By default, GroupDocs.Viewer doesn’t render hidden rows and columns so this options should be enabled in case you would like to render the hidden content.

Note: Next example shows how to render hidden rows and columns when rendering as HTML.

Rendering hidden content

// Setup GroupDocs.Viewer config
ViewerConfig config = new ViewerConfig();
config.StoragePath = @"C:\storage";
 
// Create html handler
ViewerHtmlHandler htmlHandler = new ViewerHtmlHandler(config);
string guid = "document.xlsx";
 
// Enable redering of hidden rows and columns
HtmlOptions options = new HtmlOptions();
options.CellsOptions.ShowHiddenRows = true;
options.CellsOptions.ShowHiddenColumns = true;
 
// Get pages 
List<PageHtml> pages = htmlHandler.GetPages(guid, options);
 
foreach (PageHtml page in pages)
{
    Console.WriteLine("Page number: {0}", page.PageNumber);
    Console.WriteLine("Html content: {0}", page.HtmlContent);
}

Note: Next example shows how to render hidden rows and columns when rendering as the image.

Rendering hidden content

// Setup GroupDocs.Viewer config
ViewerConfig config = new ViewerConfig();
config.StoragePath = @"C:\storage";
 
// Create image handler
ViewerImageHandler imageHandler = new ViewerImageHandler(config);
string guid = "document.xlsx";
 
// Enable redering of hidden rows and columns
ImageOptions options = new ImageOptions();
options.CellsOptions.ShowHiddenRows = true;
options.CellsOptions.ShowHiddenColumns = true;
 
// Get pages 
List<PageImage> pages = imageHandler.GetPages(guid, options);
 
foreach (PageImage page in pages)
{
    Console.WriteLine("Page number: {0}", page.PageNumber);
    //Save image by accessing page.Stream
}

Note: Next example shows how to render hidden rows and columns when rendering as PDF.

Rendering hidden content

// Setup GroupDocs.Viewer config
ViewerConfig config = new ViewerConfig();
config.StoragePath = @"C:\storage";
 
// Create image handler
ViewerImageHandler imageHandler = new ViewerImageHandler(config);
string guid = "document.xlsx";
 
// Enable redering of hidden rows and columns
PdfFileOptions options = new PdfFileOptions();
options.CellsOptions.ShowHiddenRows = true;
options.CellsOptions.ShowHiddenColumns = true;
 
// Get PDF file 
FileContainer container = imageHandler.GetPdfFile(guid, options);
 
//Save PDF file by accessing container.Stream

Implementation of Simple File Storage Interface

Starting from v18.4 GroupDocs.Viewer provides a simple interface to implement custom file storage called IFileStorage.

Amazon S3 File Storage

The next example is based on IFileStorage interface which was added in v18.4 and required AWSSDK.S3 NuGet package.

Custom file storage implementation

Amazon S3 File Storage (C#)

public class AmazonS3FileStorage : IFileStorage, IDisposable
{
    private AmazonS3Client _client;
    private readonly string _bucketName;
    private char PathDelimiter
    {
        get { return '/'; }
    }
 
    public AmazonS3FileStorage(AmazonS3Client client, string bucketName)
    {
        _client = client;
        _bucketName = bucketName;
    }
 
    public bool FileExists(string path)
    {
        try
        {
            string key = GetKey(path);
            GetObjectMetadataRequest request = new GetObjectMetadataRequest
            {
                BucketName = _bucketName,
                Key = key
            };
            _client.GetObjectMetadata(request);
            return true;
        }
        catch (AmazonS3Exception)
        {
            return false;
        }
    }
 
    public Stream GetFile(string path)
    {
        string key = GetKey(path);
        GetObjectRequest request = new GetObjectRequest
        {
            Key = key,
            BucketName = _bucketName
        };
        using (GetObjectResponse response = _client.GetObject(request))
        {
            MemoryStream stream = new MemoryStream();
            response.ResponseStream.CopyTo(stream);
            stream.Position = 0;
            return stream;
        }
    }
 
    public void SaveFile(string path, Stream content)
    {
        string key = GetKey(path);
        PutObjectRequest request = new PutObjectRequest
        {
            Key = key,
            BucketName = _bucketName,
            InputStream = content
        };
        _client.PutObject(request);
    }
    public void DeleteDirectory(string path)
    {
        string key = GetKey(path);
        S3DirectoryInfo directory = new S3DirectoryInfo(_client, _bucketName, key);
        directory.Delete(true);
    }
 
    public IFileInfo GetFileInfo(string path)
    {
        string key = GetKey(path);
        GetObjectMetadataRequest request = new GetObjectMetadataRequest
        {
            BucketName = _bucketName,
            Key = key
        };
        GetObjectMetadataResponse response = _client.GetObjectMetadata(request);
        IFileInfo file = new GroupDocs.Viewer.Storage.FileInfo();
        file.Path = path;
        file.Size = response.ContentLength;
        file.LastModified = response.LastModified;
        file.IsDirectory = false;
        return file;
    }
 
    public IEnumerable<IFileInfo> GetFilesInfo(string path)
    {
        string key = GetKey(path);
        ListObjectsRequest request = new ListObjectsRequest
        {
            BucketName = _bucketName,
            Prefix = key.Length > 1 ? key : string.Empty,
            Delimiter = PathDelimiter.ToString()
        };
        ListObjectsResponse response = _client.ListObjects(request);
        List<IFileInfo> files = new List<IFileInfo>();
         
        // add directories 
        foreach (string directory in response.CommonPrefixes)
        {
            IFileInfo file = new GroupDocs.Viewer.Storage.FileInfo();
            file.Path = directory;
            file.IsDirectory = true;
            files.Add(file);
        }
         
        // add files
        foreach (S3Object entry in response.S3Objects)
        {
            IFileInfo fileDescription = new GroupDocs.Viewer.Storage.FileInfo
            {
                Path = entry.Key,
                IsDirectory = false,
                LastModified = entry.LastModified,
                Size = entry.Size
            };
            files.Add(fileDescription);
        }
        return files;
    }
 
    private string GetKey(string path)
    {
        return Regex.Replace(path, @"\\+", PathDelimiter.ToString())
            .Trim(PathDelimiter);
    }
 
    public void Dispose()
    {
        if (_client != null)
        {
            _client.Dispose();
            _client = null;
        }
    }
}

Local File Storage

The next example demonstrates example implementation of local file storage.

Local File Storage (C#)

/// <summary>
/// Local file storage
/// </summary>
public class LocalFileStorage : IFileStorage
{
    /// <summary>
    /// Checks if file exists
    /// </summary>
    /// <param name="path">File path.</param>
    /// <returns><c>true</c> when file exists, otherwise <c>false</c></returns>
    public bool FileExists(string path)
    {
        return File.Exists(path);
    }
    /// <summary>
    /// Retrieves file content
    /// </summary>
    /// <param name="path">File path.</param>
    /// <returns>Stream</returns>
    public Stream GetFile(string path)
    {
        return File.OpenRead(path);
    }
    /// <summary>
    /// Saves file
    /// </summary>
    /// <param name="path">File path.</param>
    /// <param name="content">File content.</param>
    public void SaveFile(string path, Stream content)
    {
        string directory = Path.GetDirectoryName(path);
        if (directory != null)
        {
            if (!Directory.Exists(directory))
                Directory.CreateDirectory(directory);
        }
        using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
        {
            if (content.Position != 0)
                content.Position = 0;
            CopyStream(content, fileStream);
        }
    }
    /// <summary>
    /// Removes directory
    /// </summary>
    /// <param name="path">Directory path.</param>
    public void DeleteDirectory(string path)
    {
        if (Directory.Exists(path))
        {
            try
            {
                Directory.Delete(path, true);
            }
            catch (IOException)
            {
                //NOTE: ignore
            }
        }
    }
    /// <summary>
    /// Retrieves file information
    /// </summary>
    /// <param name="path">File path.</param>
    /// <returns>File information.</returns>
    public IFileInfo GetFileInfo(string path)
    {
        System.IO.FileInfo info = new System.IO.FileInfo(path);
        FileInfo fileInfo = new FileInfo();
        if (info.Exists)
        {
            fileInfo.Path = path;
            fileInfo.LastModified = info.LastWriteTime;
            fileInfo.Size = info.Length;
            fileInfo.IsDirectory = false;
        }
        return fileInfo;
    }
    /// <summary>
    /// Retrieves list of files and folders
    /// </summary>
    /// <param name="path">Directory path.</param>
    /// <returns>Files and folders.</returns>
    public IEnumerable<IFileInfo> GetFilesInfo(string path)
    {
        List<IFileInfo> filesInfo = new List<IFileInfo>();
        foreach (string directory in Directory.GetDirectories(path))
        {
            DirectoryInfo directoryInfo = new DirectoryInfo(directory);
            IFileInfo info = new FileInfo();
            info.IsDirectory = true;
            info.Path = directoryInfo.FullName;
            info.LastModified = directoryInfo.LastWriteTime;
            filesInfo.Add(info);
        }
        foreach (string file in Directory.GetFiles(path))
        {
            System.IO.FileInfo fileInfo = new System.IO.FileInfo(file);
            IFileInfo info = new FileInfo();
            info.IsDirectory = false;
            info.Path = fileInfo.FullName;
            info.LastModified = fileInfo.LastWriteTime;
            info.Size = fileInfo.Length;
            filesInfo.Add(info);
        }
        return filesInfo;
    }
    private void CopyStream(Stream src, Stream dst)
    {
        const int bufferSize = 81920; //NOTE: taken from System.IO
        byte[] buffer = new byte[bufferSize];
        int read;
        while ((read = src.Read(buffer, 0, buffer.Length)) != 0)
            dst.Write(buffer, 0, read);
    }
}

IFileStorage Interface

This interface is an alternative to complex and overloaded ICacheDataHandler and IInputDataHandler interfaces.

IFileStorage interface (C#)

/// <summary>
/// File storage interface
/// </summary>
public interface IFileStorage
{
    /// <summary>
    /// Checks if file exists
    /// </summary>
    /// <param name="path">File path.</param>
    /// <returns><c>true</c> when file exists, otherwise <c>false</c></returns>
    bool FileExists(string path);
 
    /// <summary>
    /// Retrieves file content
    /// </summary>
    /// <param name="path">File path.</param>
    /// <returns>Stream</returns>
    Stream GetFile(string path);
 
    /// <summary>
    /// Saves file
    /// </summary>
    /// <param name="path">File path.</param>
    /// <param name="content">File content.</param>
    void SaveFile(string path, Stream content);
 
    /// <summary>
    /// Removes directory
    /// </summary>
    /// <param name="path">Directory path.</param>
    void DeleteDirectory(string path);
 
    /// <summary>
    /// Retrieves file information
    /// </summary>
    /// <param name="path">File path.</param>
    /// <returns>File information.</returns>
    IFileInfo GetFileInfo(string path);
 
    /// <summary>
    /// Retrieves list of files and folders
    /// </summary>
    /// <param name="path">Directory path.</param>
    /// <returns>Files and folders.</returns>
    IEnumerable<IFileInfo> GetFilesInfo(string path);
}

IFileInfo interface (C#)

/// <summary>
/// File information
/// </summary>
public struct FileInfo : IFileInfo
{
    /// <summary>
    /// File or directory path
    /// </summary>
    public string Path { get; set; }
     
    /// <summary>
    /// File size in bytes
    /// </summary>
    public long Size { get; set; }
     
    /// <summary>
    /// Last modification date
    /// </summary>
    public DateTime LastModified { get; set; }
     
    /// <summary>
    /// Indicates if file is directory
    /// </summary>
    public bool IsDirectory { get; set; }
}

List of Changes in v18.4

GroupDocs.Viewer.Config.ViewerConfig

public string DefaultFontName property compilation is set to fail

Please use DefaultFontName property of the ImageOptions, HtmlOptions, DocumentInfoOptions or PdfFileOptions class instead

GroupDocs.Viewer.Converter.Options.CellsOptions

public bool RenderPrintAreaOnly { get; set; } property has been added.

This option enables rendering section(s) of a worksheet defined as a print area.

//Init viewer config
ViewerConfig viewerConfig = new ViewerConfig();
viewerConfig.StoragePath = "c:\\storage";
 
// Init viewer html or image handler
ViewerHtmlHandler viewerHtmlHandler = new ViewerHtmlHandler(viewerConfig);

// Set the guid of the document you want to render
string guid = "with-print-area.xlsx";
 
// Enable rendering print area
HtmlOptions htmlOptions = new HtmlOptions();
htmlOptions.CellsOptions.RenderPrintAreaOnly = true;
//Render document with specified options
List<PageHtml> pages = viewerHtmlHandler.GetPages(guid, htmlOptions);
public bool ShowHiddenRows { get; set; } property has been added.

This option enables rendering of hidden rows.

//Init viewer config
ViewerConfig viewerConfig = new ViewerConfig();
viewerConfig.StoragePath = "c:\\storage";
 
// Init viewer html or image handler
ViewerHtmlHandler viewerHtmlHandler = new ViewerHtmlHandler(viewerConfig);

// Set the guid of the document you want to render
string guid = "with-hidden-rows.xlsx";
 
// Enable rendering hidden rows
HtmlOptions htmlOptions = new HtmlOptions();
htmlOptions.CellsOptions.ShowHiddenRows= true;
//Render document with specified options
List<PageHtml> pages = viewerHtmlHandler.GetPages(guid, htmlOptions);
public bool ShowHiddenColumns { get; set; } property has been added.

This option enables rendering of hidden columns.

//Init viewer config
ViewerConfig viewerConfig = new ViewerConfig();
viewerConfig.StoragePath = "c:\\storage";
 
// Init viewer html or image handler
ViewerHtmlHandler viewerHtmlHandler = new ViewerHtmlHandler(viewerConfig);

// Set the guid of the document you want to render
string guid = "with-hidden-columns.xlsx";
 
// Enable rendering hidden columns
HtmlOptions htmlOptions = new HtmlOptions();
htmlOptions.CellsOptions.ShowHiddenColumns= true;
//Render document with specified options
List<PageHtml> pages = viewerHtmlHandler.GetPages(guid, htmlOptions);

GroupDocs.Viewer.Domain.FileData

Public int MaxHeight property has been removed.

To get maximal height, loop through the list of pages.

Public int MaxWidth property has been removed.

To get the maximal width, loop through the list of pages.

Public int PageCount property has been removed.

To get the number of pages, use Count property of pages list

GroupDocs.Viewer.Exception.CacheFileNotFoundException

GroupDocs.Viewer.Exception.CacheFileNotFoundException class has been set as obsolete

This exception is obsolete. GroupDocs.Viewer will start throwing System.IO.FileNotFoundException instead of CacheFileNotFoundException in version v18.7 and next versions.

GroupDocs.Viewer.Exception.GuidNotSpecifiedException

GroupDocs.Viewer.Exception.GuidNotSpecifiedException class has been set as obsolete

This exception is obsolete. GroupDocs.Viewer will start throwing System.ArgumentNullException instead of GuidNotSpecifiedException in version v18.7 and next versions.

GroupDocs.Viewer.Exception.StoragePathNotSpecifiedException

GroupDocs.Viewer.Exception.StoragePathNotSpecifiedException class has been set as obsolete

This exception is obsolete and will be removed in v18.7.

GroupDocs.Viewer.Handler.Cache.ICacheDataHandler

void ClearCache(TimeSpan olderThan) method has been set as obsolete

This method is obsolete and will be removed in v18.7.

GroupDocs.Viewer.Handler.ViewerHtmlHandler

public ViewerImageHandler(IFileStorage fileStorage) constructor has been added

This constructor can be used to pass custom file storage implementation.

public ViewerImageHandler(IFileStorage fileStorage, CultureInfo cultureInfo) constructor has been added

This constructor can be used to pass custom file storage implementation along with culture information.

public ViewerImageHandler(ViewerConfig viewerConfig, IFileStorage fileStorage) constructor has been added

This constructor can be used to pass custom file storage implementation along with Viewer configuration.

public ViewerImageHandler(ViewerConfig viewerConfig, IFileStorage fileStorage, CultureInfo cultureInfo) constructor has been added

This constructor can be used to pass custom file storage implementation along with Viewer configuration and culture information.

public void ClearCache(TimeSpan olderThan) method has been set as obsolete

Please use public void ClearCache() or *public void ClearCache(string guid) * methods instead.

C# (since the v18.4)

// Setup GroupDocs.Viewer config
ViewerConfig config = new ViewerConfig();
config.StoragePath = @"C:\storage";
config.UseCache = true;
 
string guid = "document.docx";
ViewerHtmlHandler handler = new ViewerHtmlHandler(config);

// Create cache by calling GetPages method
List<PageHtml> pages = handler.GetPages(guid);

// Clear cache files for specified file 
handler.ClearCache(guid);
 
// Clear all cache files
handler.ClearCache();

C# (before v18.1)

// Setup GroupDocs.Viewer config
ViewerConfig config = new ViewerConfig();
config.StoragePath = @"C:\storage";
config.UseCache = true;
 
string guid = "document.docx";
ViewerHtmlHandler handler = new ViewerHtmlHandler(config);

// Create cache by calling GetPages method
List<PageHtml> pages = handler.GetPages(guid);

// Clear cache filies created before specified time span
handler.ClearCache(TimeSpan.FromMinutes(1));

GroupDocs.Viewer.Handler.ViewerImageHandler

public ViewerImageHandler(IFileStorage fileStorage) constructor has been added

This constructor can be used to pass custom file storage implementation.

public ViewerImageHandler(IFileStorage fileStorage, CultureInfo cultureInfo) constructor has been added

This constructor can be used to pass custom file storage implementation along with culture information.

public ViewerImageHandler(ViewerConfig viewerConfig, IFileStorage fileStorage) constructor has been added

This constructor can be used to pass custom file storage implementation along with Viewer configuration.

public ViewerImageHandler(ViewerConfig viewerConfig, IFileStorage fileStorage, CultureInfo cultureInfo) constructor has been added

This constructor can be used to pass custom file storage implementation along with Viewer configuration and culture information.

public void ClearCache(TimeSpan olderThan) method has been set as obsolete

Please use public void ClearCache() or public void ClearCache(string guid) methods instead.

C# (since the v18.4)

// Setup GroupDocs.Viewer config
ViewerConfig config = new ViewerConfig();
config.StoragePath = @"C:\storage";
config.UseCache = true;
 
string guid = "document.docx";
ViewerImageHandler handler = new ViewerImageHandler(config);

// Create cache by calling GetPages method
List<PageImage> pages = handler.GetPages(guid);

// Clear cache files for specified file 
handler.ClearCache(guid);
 
// Clear all cache files
handler.ClearCache();

C# (before v18.1)

// Setup GroupDocs.Viewer config
ViewerConfig config = new ViewerConfig();
config.StoragePath = @"C:\storage";
config.UseCache = true;
 
string guid = "document.docx";
ViewerImageHandler handler = new ViewerImageHandler(config);

// Create cache by calling GetPages method
List<PageImage> pages = handler.GetPages(guid);

// Clear cache filies created before specified time span
handler.ClearCache(TimeSpan.FromMinutes(1));

GroupDocs.Viewer.Storage.IFileStorage

public interface IFileStorage interface has been added.

This interface is an alternative to complex and overloaded ICacheDataHandler and IInputDataHandler interfaces.

Custom file storage based on IFileStorage interface

IFileStorage interface (C#)

/// <summary>
/// File storage interface
/// </summary>
public interface IFileStorage
{
    /// <summary>
    /// Checks if file exists
    /// </summary>
    /// <param name="path">File path.</param>
    /// <returns><c>true</c> when file exists, otherwise <c>false</c></returns>
    bool FileExists(string path);

    /// <summary>
    /// Retrieves file content
    /// </summary>
    /// <param name="path">File path.</param>
    /// <returns>Stream</returns>
    Stream GetFile(string path);

    /// <summary>
    /// Saves file
    /// </summary>
    /// <param name="path">File path.</param>
    /// <param name="content">File content.</param>
    void SaveFile(string path, Stream content);

    /// <summary>
    /// Removes directory
    /// </summary>
    /// <param name="path">Directory path.</param>
    void DeleteDirectory(string path);

    /// <summary>
    /// Retrieves file information
    /// </summary>
    /// <param name="path">File path.</param>
    /// <returns>File information.</returns>
    IFileInfo GetFileInfo(string path);

    /// <summary>
    /// Retrieves list of files and folders
    /// </summary>
    /// <param name="path">Directory path.</param>
    /// <returns>Files and folders.</returns>
    IEnumerable<IFileInfo> GetFilesInfo(string path);
}

IFileInfo interface (C#)

/// <summary>
/// File information
/// </summary>
public struct FileInfo : IFileInfo
{
    /// <summary>
    /// File or directory path
    /// </summary>
    public string Path { get; set; }
    
    /// <summary>
    /// File size in bytes
    /// </summary>
    public long Size { get; set; }
    
    /// <summary>
    /// Last modification date
    /// </summary>
    public DateTime LastModified { get; set; }
    
    /// <summary>
    /// Indicates if file is directory
    /// </summary>
    public bool IsDirectory { get; set; }
}

Amazon S3 File Storage (C#)

public class AmazonS3FileStorage : IFileStorage, IDisposable
{
    private AmazonS3Client _client;
    private readonly string _bucketName;
    private char PathDelimiter
    {
        get { return '/'; }
    }

    public AmazonS3FileStorage(AmazonS3Client client, string bucketName)
    {
        _client = client;
        _bucketName = bucketName;
    }

    public bool FileExists(string path)
    {
        try
        {
            string key = GetKey(path);
            GetObjectMetadataRequest request = new GetObjectMetadataRequest
            {
                BucketName = _bucketName,
                Key = key
            };
            _client.GetObjectMetadata(request);
            return true;
        }
        catch (AmazonS3Exception)
        {
            return false;
        }
    }

    public Stream GetFile(string path)
    {
        string key = GetKey(path);
        GetObjectRequest request = new GetObjectRequest
        {
            Key = key,
            BucketName = _bucketName
        };
        using (GetObjectResponse response = _client.GetObject(request))
        {
            MemoryStream stream = new MemoryStream();
            response.ResponseStream.CopyTo(stream);
            stream.Position = 0;
            return stream;
        }
    }

    public void SaveFile(string path, Stream content)
    {
        string key = GetKey(path);
        PutObjectRequest request = new PutObjectRequest
        {
            Key = key,
            BucketName = _bucketName,
            InputStream = content
        };
        _client.PutObject(request);
    }
    public void DeleteDirectory(string path)
    {
        string key = GetKey(path);
        S3DirectoryInfo directory = new S3DirectoryInfo(_client, _bucketName, key);
        directory.Delete(true);
    }

    public IFileInfo GetFileInfo(string path)
    {
        string key = GetKey(path);
        GetObjectMetadataRequest request = new GetObjectMetadataRequest
        {
            BucketName = _bucketName,
            Key = key
        };
        GetObjectMetadataResponse response = _client.GetObjectMetadata(request);
        IFileInfo file = new GroupDocs.Viewer.Storage.FileInfo();
        file.Path = path;
        file.Size = response.ContentLength;
        file.LastModified = response.LastModified;
        file.IsDirectory = false;
        return file;
    }

    public IEnumerable<IFileInfo> GetFilesInfo(string path)
    {
        string key = GetKey(path);
        ListObjectsRequest request = new ListObjectsRequest
        {
            BucketName = _bucketName,
            Prefix = key.Length > 1 ? key : string.Empty,
            Delimiter = PathDelimiter.ToString()
        };
        ListObjectsResponse response = _client.ListObjects(request);
        List<IFileInfo> files = new List<IFileInfo>();
        
		// add directories 
        foreach (string directory in response.CommonPrefixes)
        {
            IFileInfo file = new GroupDocs.Viewer.Storage.FileInfo();
            file.Path = directory;
            file.IsDirectory = true;
            files.Add(file);
        }
        
		// add files
        foreach (S3Object entry in response.S3Objects)
        {
            IFileInfo fileDescription = new GroupDocs.Viewer.Storage.FileInfo
            {
                Path = entry.Key,
                IsDirectory = false,
                LastModified = entry.LastModified,
                Size = entry.Size
            };
            files.Add(fileDescription);
        }
        return files;
    }

    private string GetKey(string path)
    {
        return Regex.Replace(path, @"\\+", PathDelimiter.ToString())
            .Trim(PathDelimiter);
    }

    public void Dispose()
    {
        if (_client != null)
        {
            _client.Dispose();
            _client = null;
        }
    }
}

GroupDocs.Viewer.Storage.LocalFileStorage

public class LocalFileStorage class has been added.

This class is an example of how IFileStorage interface that can be implemented to work with local files.

Custom file storage based on IFileStorage interface

IFileStorage interface (C#)

/// <summary>
/// Local file storage
/// </summary>
public class LocalFileStorage : IFileStorage
{
    /// <summary>
    /// Checks if file exists
    /// </summary>
    /// <param name="path">File path.</param>
    /// <returns><c>true</c> when file exists, otherwise <c>false</c></returns>
    public bool FileExists(string path)
    {
        return File.Exists(path);
    }

    /// <summary>
    /// Retrieves file content
    /// </summary>
    /// <param name="path">File path.</param>
    /// <returns>Stream</returns>
    public Stream GetFile(string path)
    {
        return File.OpenRead(path);
    }

    /// <summary>
    /// Saves file
    /// </summary>
    /// <param name="path">File path.</param>
    /// <param name="content">File content.</param>
    public void SaveFile(string path, Stream content)
    {
        string directory = Path.GetDirectoryName(path);
        if (directory != null)
        {
            if (!Directory.Exists(directory))
               Directory.CreateDirectory(directory);
        }

        using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
        {
            if (content.Position != 0)
            	content.Position = 0;
            
			CopyStream(content, fileStream);
        }
    }
    
	/// <summary>
    /// Removes directory
    /// </summary>
    /// <param name="path">Directory path.</param>
    public void DeleteDirectory(string path)
    {
        if (Directory.Exists(path))
        {
            try
            {
                Directory.Delete(path, true);
            }
            catch (IOException)
            {
                //NOTE: ignore
            }
        }
    }
    
	/// <summary>
    /// Retrieves file information
    /// </summary>
    /// <param name="path">File path.</param>
    /// <returns>File information.</returns>
    public IFileInfo GetFileInfo(string path)
    {
        System.IO.FileInfo info = new System.IO.FileInfo(path);
        FileInfo fileInfo = new FileInfo();
        if (info.Exists)
        {
            fileInfo.Path = path;
            fileInfo.LastModified = info.LastWriteTime;
            fileInfo.Size = info.Length;
            fileInfo.IsDirectory = false;
         }
         return fileInfo;
    }
    
	/// <summary>
    /// Retrieves list of files and folders
    /// </summary>
    /// <param name="path">Directory path.</param>
    /// <returns>Files and folders.</returns>
    public IEnumerable<IFileInfo> GetFilesInfo(string path)
    {
        List<IFileInfo> filesInfo = new List<IFileInfo>();
        foreach (string directory in Directory.GetDirectories(path))
        {
            DirectoryInfo directoryInfo = new DirectoryInfo(directory);
            IFileInfo info = new FileInfo();
            info.IsDirectory = true;
            info.Path = directoryInfo.FullName;
            info.LastModified = directoryInfo.LastWriteTime;
            filesInfo.Add(info);
        }

        foreach (string file in Directory.GetFiles(path))
        {
            System.IO.FileInfo fileInfo = new System.IO.FileInfo(file);
            IFileInfo info = new FileInfo();
            info.IsDirectory = false;
            info.Path = fileInfo.FullName;
            info.LastModified = fileInfo.LastWriteTime;
            info.Size = fileInfo.Length;
            filesInfo.Add(info);
        }
        return filesInfo;
    }

    private void CopyStream(Stream src, Stream dst)
    {
        const int bufferSize = 81920;
        byte[] buffer = new byte[bufferSize];
        int read;
        while ((read = src.Read(buffer, 0, buffer.Length)) != 0)
            dst.Write(buffer, 0, read);
    }
}