Search code examples
asp.net-mvcadobe-illustratorimageresizer

Render .ai with PdfiumRenderer


I'm trying to generate thumbnails of .ai files (Adobe Illustrator) with ImageResizer. Since the official Adobe Acrobat Reader can actually open these files, I assumed that the PdfiumRenderer would also be able to do this.

In fact, I have tested this by renaming a .ai file to .pdf and the thumbnail would appear just fine. Obviously it doesn't work without the renaming since no plugin is registered for that file extension. And renaming client-provided files in this way doesn't seem like a good solution either.

I have tried to write a minimal custom plugin that inherits from PdfiumRenderer but adds the .ai file extension to the list of supported file types. ImageResizer will then catch the .ai requests alright but displays an error.

HTTP/1.1 500 Internal Server Error 
Cache-Control: public 
Content-Type: image/jpeg; charset=utf-8 Server: Microsoft-IIS/10.0 
X-AspNet-Version: 4.0.30319 
X-Powered-By: ASP.NET 
Content-Length: 7472

[ArgumentException]: Parameter is not valid.
   at System.Drawing.Bitmap..ctor(Stream stream, Boolean useIcm)
   at ImageResizer.ImageBuilder.DecodeStream(Stream s, ResizeSettings settings, String optionalPath)
   at ImageResizer.ImageBuilder.LoadImage(Object source, ResizeSettings settings, Boolean restoreStreamPos)
[ImageCorruptedException]: File may be corrupted, empty, or may contain a PNG image with a single dimension greater than 65,535 pixels.
   at ImageResizer.ImageBuilder.LoadImage(Object source, ResizeSettings settings, Boolean restoreStreamPos)
   at ImageResizer.ImageBuilder.BuildJob(ImageJob job)
   at ImageResizer.ImageBuilder.Build(ImageJob job)
   at ImageResizer.ImageBuilder.Build(Object source, Object dest, ResizeSettings settings, Boolean disposeSource, Boolean addFileExtension)
   at ImageResizer.ImageBuilder.Build(Object source, Object dest, ResizeSettings settings, Boolean disposeSource)
   at ImageResizer.ImageBuilder.Build(Object source, Object dest, ResizeSettings settings)
   at ImageResizer.InterceptModule.<>c__DisplayClass5_0.<HandleRequest>b__1(Stream stream)
   at ImageResizer.Plugins.DiskCache.CustomDiskCache.<>c__DisplayClass29_0.<TryWriteFile>b__0()
   at ImageResizer.Plugins.DiskCache.LockProvider.TryExecute(String key, Int32 timeoutMs, LockCallback success)
   at ImageResizer.Plugins.DiskCache.CustomDiskCache.TryWriteFile(CacheResult result, String physicalPath, String relativePath, ResizeImageDelegate writeCallback, Int32 timeoutMs, Boolean recheckFS)
   at ImageResizer.Plugins.DiskCache.CustomDiskCache.GetCachedFile(String keyBasis, String extension, ResizeImageDelegate writeCallback, Int32 timeoutMs, Boolean asynchronous)
   at ImageResizer.Plugins.DiskCache.DiskCache.Process(IResponseArgs e)
   at ImageResizer.Plugins.DiskCache.DiskCache.Process(HttpContext context, IResponseArgs e)
   at ImageResizer.InterceptModule.HandleRequest(HttpContext context, HttpModuleRequestAssistant ra, IVirtualFile vf)
   at ImageResizer.InterceptModule.CheckRequest_PostAuthorizeRequest(Object sender, EventArgs e)
   at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

If someone with a little more experience on custom plugins or someone from Imazen could help me on this I would be grateful.


Solution

  • All right, I finally got it working and my project now supports PDF and AI thumbnails, including support for whitespace trimming, azure storage, disk cache etc.!

    Nathanael didn't directly provide the solution but pointed me in the right direction (thanks!). The DecodeStream method is provided with an optional path string and apparently the PdfiumRenderer freaks out if that path is not ending with .pdf. The trick here was to hide this path and pass null instead.

    I did have to write a little more code than before though since the DecodeStream method cannot be overriden when inheriting from PdfiumRendererPlugin. Here's the full code:

    public class IllustratorPlugin : BuilderExtension, IPlugin, IFileExtensionPlugin, IIssueProvider, IQuerystringPlugin {
    
        private readonly PdfiumRendererPlugin BasePlugin;
    
        public IllustratorPlugin() : base() {
    
            BasePlugin = new PdfiumRendererPlugin();
    
        }
    
        public IEnumerable<IIssue> GetIssues()
            => BasePlugin.GetIssues();
    
        public IEnumerable<string> GetSupportedFileExtensions()
            => new string[] { ".ai" };
    
        public IEnumerable<string> GetSupportedQuerystringKeys()
            => BasePlugin.GetSupportedQuerystringKeys();
    
        public IPlugin Install(ImageResizer.Configuration.Config c) {
    
            BasePlugin.Install(c);
            c.Plugins.add_plugin(this);
    
            return this;
    
        }
    
        public bool Uninstall(ImageResizer.Configuration.Config c) {
    
            c.Plugins.remove_plugin(this);
            return BasePlugin.Uninstall(c);
    
        }
    
        public int MaxHeight {
            get => BasePlugin.MaxHeight;
            set => BasePlugin.MaxHeight = value;
        }
    
        public int MaxWidth {
            get => BasePlugin.MaxWidth;
            set => BasePlugin.MaxWidth = value;
        }
    
        public int DefaultHeight {
            get => BasePlugin.DefaultHeight;
            set => BasePlugin.DefaultHeight = value;
        }
    
        public int DefaultWidth {
            get => BasePlugin.DefaultWidth;
            set => BasePlugin.DefaultWidth = value;
        }
    
        public override Bitmap DecodeStream(Stream s, ResizeSettings settings, string optionalPath)
            => BasePlugin.DecodeStream(s, settings, null);
    
    }
    

    In global.asax I have just added this line:

    new IllustratorPlugin().Install(ImageResizer.Configuration.Config.Current);
    

    The PdfiumRenderer plugin can then be removed from the web.config.