blob: 9ccb958aa36bcb20286c45311aa045864465d872 [file] [log] [blame]
#region Copyright Notice
// This file is part of PowerPoint LaTeX.
//
// Copyright (C) 2008/2009 Andreas Kirsch
//
// PowerPoint LaTeX is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PowerPoint LaTeX is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Interop.PowerPoint;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;
namespace PowerPointLaTeX
{
static class LaTeXRendering
{
// renders all formulas at a higher resolution than necessary to allow for zooming
public const int RenderDPISetting = 300;
public static string LastLog {
get;
private set;
}
static public Shape GetPictureShapeFromLaTeXCode(Slide currentSlide, string latexCode, float fontSize)
{
// check the cache first
int baselineOffset;
float wantedPixelsPerEmHeight = DPIHelper.FontSizeToPixelsPerEmHeight(fontSize, DPIHelper.WindowsDPISetting);
float actualPixelsPerEmHeight = DPIHelper.FontSizeToPixelsPerEmHeight(fontSize, RenderDPISetting);
CacheTags presentationCache = LaTeXTool.ActivePresentation.CacheTags();
Image image = GetImageForLaTeXCode(presentationCache, latexCode, ref actualPixelsPerEmHeight, out baselineOffset);
if (image == null)
{
return null;
}
Shape picture = CreatePictureShapeFromImage(currentSlide, image);
if (picture == null)
{
return null;
}
// make white the transparent color
picture.PictureFormat.TransparencyColor = ~0;
picture.PictureFormat.TransparentBackground = Microsoft.Office.Core.MsoTriState.msoCTrue;
picture.AlternativeText = latexCode;
// prescale the image to the wanted em height here
float scaleRatio = wantedPixelsPerEmHeight / actualPixelsPerEmHeight;
picture.LockAspectRatio = Microsoft.Office.Core.MsoTriState.msoFalse;
picture.Height *= scaleRatio;
picture.Width *= scaleRatio;
// store the baseline offset as percentage value instead of pixels to support rescaling the image
picture.LaTeXTags().BaseLineOffset.value = (float)baselineOffset / image.Height;
string shortenedName;
if (latexCode.Length > 32)
{
shortenedName = latexCode.Substring(0, 32) + "..";
}
else
{
shortenedName = latexCode;
}
picture.Name = "LaTeX: " + shortenedName;
return picture;
}
/// <returns>A valid picture shape from the data or null if creation failed</returns>
static private Shape CreatePictureShapeFromImage(Slide slide, Image image)
{
IDataObject oldClipboardContent = null;
try
{
oldClipboardContent = Clipboard.GetDataObject();
}
catch { Debug.Assert(false, "Retrieving the current clipboard contents failed!"); }
Clipboard.SetImage(image);
ShapeRange pictureRange = slide.Shapes.Paste();
if (oldClipboardContent != null)
Clipboard.SetDataObject(oldClipboardContent);
if (pictureRange == null)
{
return null;
}
Trace.Assert(pictureRange.Count == 1);
return pictureRange[1];
}
static public Image GetImageForLaTeXCode(ICacheStorage cacheStorage, string latexCode, ref float pixelsPerEmHeight, out int baselineOffset)
{
byte[] imageData = GetImageDataForLaTeXCode(cacheStorage, latexCode, ref pixelsPerEmHeight, out baselineOffset);
return GetImageFromImageData(imageData);
}
/// <summary>
/// Get the raw image data for some latex code or null if the compilation failed.
/// It seems to be possible to receive a byte[0] array for some reason.
/// </summary>
/// <param name="latexCode"></param>
/// <returns></returns>
static private byte[] GetImageDataForLaTeXCode(ICacheStorage cacheStorage, string latexCode, ref float pixelsPerEmHeight, out int baselineOffset)
{
CacheEntry? cacheEntry = Cache.Query(cacheStorage, latexCode);
// TODO: rewrite the cache system to work even if the main thread is blocked [8/4/2009 Andreas]
if (cacheEntry.HasValue)
{
// don't return a malformed cache entry
// TODO: move this check somewhere else? [12/11/2010 Andreas]
if (cacheEntry.Value.Content == null || cacheEntry.Value.Content.Length == 0)
{
cacheEntry = null;
}
}
CacheEntry entry = new CacheEntry();
// convert to int to avoid floating point issues [5/24/2010 Andreas]
if (cacheEntry.HasValue && (int)cacheEntry.Value.PixelsPerEmHeight >= (int)pixelsPerEmHeight)
{
// we can use the cached formula
entry = cacheEntry.Value;
}
else
{
// try to render the formula using our LaTeX service
// TODO: needs further refactoring [10/1/2010 Andreas]
LaTeXCompilationTask task;
task.code = latexCode;
task.pixelsPerEmHeight = pixelsPerEmHeight;
LaTeXCompilationResult result = Globals.ThisAddIn.LaTeXRenderingServices.Service.RenderLaTeXCode(task);
LastLog = result.report;
entry.Content = result.imageData;
entry.BaselineOffset = (int) result.baselineOffset;
entry.PixelsPerEmHeight = result.pixelsPerEmHeight;
if (entry.Content != null && entry.Content.Length > 0)
{
// looks good, so cache it
Cache.Store(cacheStorage, latexCode, entry);
}
else
{
// if this failed, use the result from the cache, can't be off worse
entry = cacheEntry.GetValueOrDefault();
}
}
// TODO: change the parameters to return a CacheEntry instead?
pixelsPerEmHeight = entry.PixelsPerEmHeight;
baselineOffset = entry.BaselineOffset;
return entry.Content;
}
static private Image GetImageFromImageData(byte[] imageData)
{
if (imageData == null)
{
return null;
}
MemoryStream stream = new MemoryStream(imageData);
Image image;
try
{
image = Image.FromStream(stream);
}
catch
{
return null;
}
return image;
}
}
}