blob: 08a249100ae0b00eb57dcdb6f0d4c3a712bfe705 [file] [log] [blame]
Andreas30063e32009-03-13 18:33:58 +00001#region Copyright Notice
2// This file is part of PowerPoint LaTeX.
3//
4// Copyright (C) 2008/2009 Andreas Kirsch
5//
6// PowerPoint LaTeX is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// PowerPoint LaTeX is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <http://www.gnu.org/licenses/>.
18#endregion
19
20using System;
Andrease06dbcc2009-01-04 23:21:44 +000021using System.Collections.Generic;
22using System.Linq;
23using System.Text;
24using Microsoft.Office.Interop.PowerPoint;
25using System.IO;
26using System.Drawing;
27using System.Diagnostics;
28using System.Windows.Forms;
Andreas8ce307a2009-02-25 20:01:45 +000029using System.Threading;
Andrease06dbcc2009-01-04 23:21:44 +000030
31namespace PowerPointLaTeX
32{
33 public enum EquationType
34 {
35 None,
36 // has inline LaTeX codes (but not compiled)
37 HasInlines,
38 // has compiled LaTeX codes
39 HasCompiledInlines,
blackhc461abfe2010-09-22 18:46:23 +000040 // a compiled LaTeX code element (picture)
Andrease06dbcc2009-01-04 23:21:44 +000041 Inline,
blackhc9fb503b2010-09-13 12:46:32 +000042 // an equation (picture)
Andreas9d4c7a32009-03-04 12:50:51 +000043 Equation,
Andrease06dbcc2009-01-04 23:21:44 +000044 }
45
Andreas436b33a2009-03-23 14:04:54 +000046 static class EquationTypeShapeExtension
Andrease06dbcc2009-01-04 23:21:44 +000047 {
blackhc461abfe2010-09-22 18:46:23 +000048 public static bool IsEquation(this Shape shape)
Andreas78ded792009-03-25 13:07:38 +000049 {
Andreas436b33a2009-03-23 14:04:54 +000050 return shape.LaTeXTags().Type == EquationType.Equation;
51 }
Andrease06dbcc2009-01-04 23:21:44 +000052 }
53
54 /// <summary>
55 /// Contains all the important methods, etc.
56 /// Instantiated by the add-in
57 /// </summary>
blackhc461abfe2010-09-22 18:46:23 +000058 class LaTeXTool : PowerPointLaTeX.ILaTeXTool
Andrease06dbcc2009-01-04 23:21:44 +000059 {
blackhc0b4037c2010-05-28 01:21:41 +000060 private const char NoneBreakingSpace = (char) 8201;
blackhce0739142010-05-22 19:13:40 +000061 private const int InitialEquationFontSize = 44;
Andrease06dbcc2009-01-04 23:21:44 +000062
63 private Microsoft.Office.Interop.PowerPoint.Application Application
64 {
65 get
66 {
67 return Globals.ThisAddIn.Application;
68 }
69 }
70
71 internal Presentation ActivePresentation
72 {
73 get { return Application.ActivePresentation; }
74 }
75
76 internal Slide ActiveSlide
77 {
78 get { return Application.ActiveWindow.View.Slide as Slide; }
79 }
80
blackhc9fb503b2010-09-13 12:46:32 +000081 /// <summary>
82 /// returns whether the addin is enabled in the current context (ie presentation)
83 /// but is also affected by the global addin settings, of course.
84 /// </summary>
85 internal bool AddInEnabled
Andreas8ce307a2009-02-25 20:01:45 +000086 {
Andrease06dbcc2009-01-04 23:21:44 +000087 get
88 {
blackhc9fb503b2010-09-13 12:46:32 +000089 return !ActivePresentation.Final && Properties.Settings.Default.EnableAddIn && Compatibility.IsSupportedPresentation( ActivePresentation );
Andrease06dbcc2009-01-04 23:21:44 +000090 }
91 }
92
Andreas9d4c7a32009-03-04 12:50:51 +000093 /// <summary>
94 /// Compile latexCode into an inline shape
95 /// </summary>
96 /// <param name="slide"></param>
97 /// <param name="textShape"></param>
98 /// <param name="latexCode"></param>
99 /// <param name="codeRange"></param>
100 /// <returns></returns>
101 private Shape CompileInlineLaTeXCode(Slide slide, Shape textShape, string latexCode, TextRange codeRange)
102 {
blackhc363e8362010-09-22 21:35:03 +0000103 Shape picture = LaTeXRendering.GetPictureShapeFromLaTeXCode( slide, latexCode, codeRange.Font.Size );
Andreas8ce307a2009-02-25 20:01:45 +0000104 if (picture == null)
105 {
106 return null;
107 }
Andrease06dbcc2009-01-04 23:21:44 +0000108
Andrease06dbcc2009-01-04 23:21:44 +0000109 // add tags to the picture
110 picture.LaTeXTags().Code.value = latexCode;
111 picture.LaTeXTags().Type.value = EquationType.Inline;
Andreasc7ba65c2009-03-23 13:28:28 +0000112 picture.LaTeXTags().LinkID.value = textShape.Id;
blackhccc549012009-05-01 22:56:24 +0000113
blackhc112e3c02010-05-21 18:45:51 +0000114 float baselineOffset = picture.LaTeXTags().BaseLineOffset;
blackhc3eac6902010-09-22 20:36:01 +0000115 float heightInPts = DPIHelper.PixelsPerEmHeightToFontSize(picture.Height);
blackhc112e3c02010-05-21 18:45:51 +0000116
blackhc461abfe2010-09-22 18:46:23 +0000117 FontFamily fontFamily;
118 try
119 {
120 fontFamily = new FontFamily(codeRange.Font.Name);
121 }
122 catch( Exception exception ) {
123 // TODO: add message box and inform the user about it [9/20/2010 Andreas]
124 MessageBox.Show("Failed to load font information (using Times New Roman as substitute). Error: " + exception, "PowerPoint LaTeX");
125 fontFamily = new FontFamily("Times New Roman");
126 }
127
blackhc8bae69f2010-05-23 02:13:52 +0000128 // from top to baseline
129 float ascentHeight = (float) (codeRange.Font.Size * ((float) fontFamily.GetCellAscent( FontStyle.Regular ) / fontFamily.GetEmHeight( FontStyle.Regular )));
blackhc93c00792010-05-24 21:29:01 +0000130 float descentRatio = (float) fontFamily.GetCellDescent( FontStyle.Regular ) / fontFamily.GetEmHeight( FontStyle.Regular );
131 float descentHeight = (float) (codeRange.Font.Size * descentRatio);
blackhc8bae69f2010-05-23 02:13:52 +0000132
133 float ascentSize = Math.Max( 0, 1 - baselineOffset ) * heightInPts;
134 float descentSize = Math.Max( 0, baselineOffset ) * heightInPts;
blackhc66d22092010-05-27 00:31:37 +0000135
blackhc8bae69f2010-05-23 02:13:52 +0000136 float factor = 1.0f;
137 if( ascentSize > 0 ) {
138 factor = Math.Max( factor, ascentSize / ascentHeight );
139 }
140 if( descentSize > 0 ) {
141 factor = Math.Max( factor, descentSize / descentHeight );
142 }
143
blackhc9335fbe2010-05-23 19:04:10 +0000144 // dont let it get too big (starts to look ridiculous otherwise)
145 if( factor <= 1.5f ) {
146 codeRange.Font.Size *= factor;
147 }
148 else {
blackhc9335fbe2010-05-23 19:04:10 +0000149 // keep linespacing intact (assuming that the line spacing scales with the font height)
150 float lineSpacing = (float) (codeRange.Font.Size * ((float) fontFamily.GetLineSpacing( FontStyle.Regular ) / fontFamily.GetEmHeight( FontStyle.Regular )));
blackhc66d22092010-05-27 00:31:37 +0000151 // additional line spacing
152 codeRange.Font.Size *= (lineSpacing + 5.0f - codeRange.Font.Size + heightInPts) / lineSpacing;
blackhc93c00792010-05-24 21:29:01 +0000153 // just ignore the baseline offset
154 picture.LaTeXTags().BaseLineOffset.value = descentRatio;
blackhc9335fbe2010-05-23 19:04:10 +0000155 }
156
blackhc8bae69f2010-05-23 02:13:52 +0000157
158 /*
blackhc112e3c02010-05-21 18:45:51 +0000159 if( Math.Abs(baselineOffset) > 1 ) {
blackhc8bae69f2010-05-23 02:13:52 +0000160 if( baselineOffset < -1 ) {
161 codeRange.Font.Size = heightInPts * (1 - baselineOffset); // ie 1 + abs( baselineOffset)
162 codeRange.Font.BaselineOffset = 1;
163 }
164 else / * baselineOffset > 1 * / {
165 codeRange.Font.Size = heightInPts * baselineOffset;
166 codeRange.Font.BaselineOffset = -1;
167 }
168 }
169 else {
170 // change the font size to keep the formula from overlapping with regular text (nifty :))
171 codeRange.Font.Size = heightInPts;
172
173 // BaseLineOffset > for subscript but PPT uses negative values for this
174 codeRange.Font.BaselineOffset = -baselineOffset;
175 }*/
176
blackhccc549012009-05-01 22:56:24 +0000177
Andreasf79ef362009-04-17 14:10:22 +0000178 // disable word wrap
Andreas8ce307a2009-02-25 20:01:45 +0000179 codeRange.ParagraphFormat.WordWrap = Microsoft.Office.Core.MsoTriState.msoFalse;
Andreasf79ef362009-04-17 14:10:22 +0000180 // fill the text up with none breaking space to make it "wrap around" the formula
181 FillTextRange(codeRange, NoneBreakingSpace, picture.Width);
Andreas8ce307a2009-02-25 20:01:45 +0000182
blackhc9335fbe2010-05-23 19:04:10 +0000183 CopyInlineEffects( slide, textShape, codeRange, picture );
184
185 return picture;
186 }
187
blackhc47bcee52010-09-24 07:42:57 +0000188 private static int GetSafeEffectParagraph( Effect effect ) {
blackhc66d22092010-05-27 00:31:37 +0000189 try {
190 return effect.Paragraph;
191 }
192 catch {
193 return 1;
194 }
195 }
Andrease06dbcc2009-01-04 23:21:44 +0000196
blackhc66d22092010-05-27 00:31:37 +0000197 private void CopyInlineEffects( Slide slide, Shape textShape, TextRange codeRange, Shape picture ) {
198 try {
199 // copy animations from the parent textShape
200 Sequence sequence = slide.TimeLine.MainSequence;
201 var effects =
202 from Effect effect in sequence
203 where effect.Shape.SafeThis() != null && effect.Shape == textShape &&
204 ((effect.EffectInformation.TextUnitEffect == MsoAnimTextUnitEffect.msoAnimTextUnitEffectByParagraph &&
blackhcc5e71772010-09-23 15:55:15 +0000205 GeneralHelpers.ParagraphContainsRange( textShape, GetSafeEffectParagraph( effect ), codeRange ))
blackhc66d22092010-05-27 00:31:37 +0000206 || effect.EffectInformation.BuildByLevelEffect == MsoAnimateByLevel.msoAnimateLevelNone)
207 select effect;
208
blackhc47bcee52010-09-24 07:42:57 +0000209 picture.AddEffects(effects, true, sequence);
blackhc66d22092010-05-27 00:31:37 +0000210 }
211 catch {
212 Debug.Fail( "CopyInlineEffects failed!" );
213 }
Andreasa159b6e2009-03-07 21:07:13 +0000214 }
215
Andreas8ce307a2009-02-25 20:01:45 +0000216 private static void FillTextRange(TextRange range, char character, float minWidth)
217 {
blackhc0b4037c2010-05-28 01:21:41 +0000218 range.Text = character.ToString() + ( (char) 160 ).ToString(); // ;
Andreasa84d9a12009-01-05 12:03:40 +0000219
blackhc0b4037c2010-05-28 01:21:41 +0000220 // line-breaks are futile, so break if one happens
Andreasf79ef362009-04-17 14:10:22 +0000221 float oldHeight = range.BoundHeight;
222 while (range.BoundWidth < minWidth && oldHeight == range.BoundHeight)
Andreasa84d9a12009-01-05 12:03:40 +0000223 {
blackhc0b4037c2010-05-28 01:21:41 +0000224 range.Text += character.ToString() + ((char) 160).ToString();
Andreasa84d9a12009-01-05 12:03:40 +0000225 }
Andreasf79ef362009-04-17 14:10:22 +0000226 if( oldHeight != range.BoundHeight ) {
blackhc0b4037c2010-05-28 01:21:41 +0000227 range.Text = range.Text.Remove( range.Text.Length - 2, 2 );
228 range.Text += ((char)8232).ToString(); // new line that doesnt begin new paragraph
Andreasf79ef362009-04-17 14:10:22 +0000229 }
Andreas8ce307a2009-02-25 20:01:45 +0000230 }
231
232 private static void AlignFormulaWithText(TextRange codeRange, Shape picture)
233 {
Andreasf79ef362009-04-17 14:10:22 +0000234 // interesting fact: text filled with (at most one line of none-breaking) spaces -> BoundHeight == EmSize
235 //codeRange.Text = " ";
Andrease06dbcc2009-01-04 23:21:44 +0000236 float fontHeight = codeRange.BoundHeight;
237 FontFamily fontFamily = new FontFamily(codeRange.Font.Name);
blackhc549a4d12010-05-21 18:11:51 +0000238 // from top to baseline
Andrease06dbcc2009-01-04 23:21:44 +0000239 float baselineHeight = (float) (fontHeight * ((float) fontFamily.GetCellAscent(FontStyle.Regular) / fontFamily.GetLineSpacing(FontStyle.Regular)));
240
Andrease06dbcc2009-01-04 23:21:44 +0000241 picture.Left = codeRange.BoundLeft;
Andreasa84d9a12009-01-05 12:03:40 +0000242
blackhc549a4d12010-05-21 18:11:51 +0000243 // DISABLED to try baseline feature from the miktex service [5/21/2010 Andreas]
244 /*
Andrease06dbcc2009-01-04 23:21:44 +0000245 if (baselineHeight >= picture.Height)
blackhc549a4d12010-05-21 18:11:51 +0000246 {
247 // baseline: center (assume that its a one-line codeRange)
248 picture.Top = codeRange.BoundTop + (baselineHeight - picture.Height) * 0.5f;
249 }
250 else
251 {
252 // center the picture directly
253 picture.Top = codeRange.BoundTop + (fontHeight - picture.Height) * 0.5f;
254 }*/
255 picture.Top = codeRange.BoundTop + baselineHeight - (1.0f - picture.LaTeXTags().BaseLineOffset) * picture.Height;
256
Andrease06dbcc2009-01-04 23:21:44 +0000257 }
258
Andreas9d4c7a32009-03-04 12:50:51 +0000259 private void CompileInlineTextRange(Slide slide, Shape shape, TextRange range)
Andrease06dbcc2009-01-04 23:21:44 +0000260 {
261 int startIndex = 0;
262
263 int codeCount = 0;
Andreas8ce307a2009-02-25 20:01:45 +0000264
265 List<TextRange> pictureRanges = new List<TextRange>();
266 List<Shape> pictures = new List<Shape>();
267
blackhc9335fbe2010-05-23 19:04:10 +0000268 while (true)
Andrease06dbcc2009-01-04 23:21:44 +0000269 {
blackhc9335fbe2010-05-23 19:04:10 +0000270 bool inlineMode;
271 int latexCodeStartIndex;
272 int latexCodeEndIndex;
273 int endIndex;
Andrease06dbcc2009-01-04 23:21:44 +0000274
blackhc9335fbe2010-05-23 19:04:10 +0000275 int inlineStartIndex, displaystyleStartIndex;
276 inlineStartIndex = range.Text.IndexOf("$$", startIndex);
277 displaystyleStartIndex = range.Text.IndexOf( "$$[", startIndex );
278 if( displaystyleStartIndex != -1 && displaystyleStartIndex <= inlineStartIndex ) {
279 inlineMode = false;
280
281 startIndex = displaystyleStartIndex;
282 latexCodeStartIndex = startIndex + 3;
283 latexCodeEndIndex = range.Text.IndexOf( "]$$", latexCodeStartIndex );
284 endIndex = latexCodeEndIndex + 3;
285 } else if( inlineStartIndex != -1 ) {
286 inlineMode = true;
287
288 startIndex = inlineStartIndex;
289 latexCodeStartIndex = startIndex + 2;
290 latexCodeEndIndex = range.Text.IndexOf( "$$", latexCodeStartIndex );
291 endIndex = latexCodeEndIndex + 2;
292 }
293 else {
294 break;
295 }
296
297 if( latexCodeEndIndex == -1 )
Andrease06dbcc2009-01-04 23:21:44 +0000298 {
299 break;
300 }
301
302 int length = endIndex - startIndex;
blackhc9335fbe2010-05-23 19:04:10 +0000303
304 int latexCodeLength = latexCodeEndIndex - latexCodeStartIndex;
305 string latexCode = range.Text.Substring( latexCodeStartIndex, latexCodeLength );
Andreas8ce307a2009-02-25 20:01:45 +0000306 // TODO: move this into its own function [1/5/2009 Andreas]
Andreas9d4c7a32009-03-04 12:50:51 +0000307 latexCode = latexCode.Replace((char) 8217, '\'');
blackhc93c00792010-05-24 21:29:01 +0000308 // replace weird unicode - (hypens) with minus
309 latexCode = latexCode.Replace( (char) 8208, '-' );
310 latexCode = latexCode.Replace( (char) 8211, '-' );
311 latexCode = latexCode.Replace( (char) 8212, '-' );
312 latexCode = latexCode.Replace( (char) 8722, '-' );
313 latexCode = latexCode.Replace( (char) 8209, '-' );
314 latexCode = latexCode.Replace( (char) 8259, '-' );
315 // replace ellipses with ...
316 latexCode = latexCode.Replace( ((char) 8230).ToString(), "..." );
Andrease06dbcc2009-01-04 23:21:44 +0000317
blackhc9335fbe2010-05-23 19:04:10 +0000318 // must be [[ then
319 if( !inlineMode ) {
320 latexCode = @"\displaystyle{" + latexCode + "}";
321 }
322
Andrease06dbcc2009-01-04 23:21:44 +0000323 LaTeXEntry tagEntry = shape.LaTeXTags().Entries[codeCount];
324 tagEntry.Code.value = latexCode;
blackhccc549012009-05-01 22:56:24 +0000325 // TODO: cohesion? [5/2/2009 Andreas]
326 // save the font size because it might be changed later
blackhc9335fbe2010-05-23 19:04:10 +0000327 // +1 because IndexOf is base 0, but Characters uses base 1
328 tagEntry.FontSize.value = range.Characters( latexCodeStartIndex + 1, latexCodeLength ).Font.Size;
Andrease06dbcc2009-01-04 23:21:44 +0000329
330 // escape $$!$$
blackhc9335fbe2010-05-23 19:04:10 +0000331 // +1 because IndexOf is base 0, but Characters uses base 1
332 TextRange codeRange = range.Characters(startIndex + 1, length);
blackhcc5e71772010-09-23 15:55:15 +0000333 if (!Helpers.IsEscapeCode(latexCode))
Andrease06dbcc2009-01-04 23:21:44 +0000334 {
Andreas9d4c7a32009-03-04 12:50:51 +0000335 Shape picture = CompileInlineLaTeXCode(slide, shape, latexCode, codeRange);
Andreas8ce307a2009-02-25 20:01:45 +0000336 if (picture != null)
337 {
338 tagEntry.ShapeId.value = picture.Id;
339
Andreas8ce307a2009-02-25 20:01:45 +0000340 pictures.Add(picture);
341 pictureRanges.Add(codeRange);
342 }
343 else
344 {
345 codeRange.Text = "$Formula Error$";
346 }
Andrease06dbcc2009-01-04 23:21:44 +0000347 }
348 else
349 {
350 codeRange.Text = "$$";
351 }
352
353 tagEntry.StartIndex.value = codeRange.Start;
354 tagEntry.Length.value = codeRange.Length;
355
blackhc3d141cb2010-05-23 19:06:22 +0000356 // NOTE: endIndex isnt valid anymore since we've removed some text [5/24/2010 Andreas
357 // IndexOf uses base0, codeRange base1 => -1
358 startIndex = codeRange.Start + codeRange.Length - 1;
Andrease06dbcc2009-01-04 23:21:44 +0000359 codeCount++;
360 }
361
Andreas9d4c7a32009-03-04 12:50:51 +0000362 // TODO: this doesn't work - simply disable autofit instead.. [1/5/2009 Andreas]
363 // TODO: can we automate this? [2/26/2009 Andreas]
Andreas8ce307a2009-02-25 20:01:45 +0000364 /*
365 (new Thread( delegate() {
366 Thread.Sleep(100);
367 for (int i = 0; i < pictures.Count; i++)
368 {
369 TextRange codeRange = pictureRanges[i];
370 AlignFormulaWithText(codeRange, pictures[i]);
371 }
372 } )).Start();*/
373
Andreasf79ef362009-04-17 14:10:22 +0000374 // now that everything has been converted we can position the formulas (pictures) in the text area
Andreas8ce307a2009-02-25 20:01:45 +0000375 for (int i = 0; i < pictures.Count; i++)
376 {
377 TextRange codeRange = pictureRanges[i];
378 AlignFormulaWithText(codeRange, pictures[i]);
Andreas9d4c7a32009-03-04 12:50:51 +0000379 }
380
381 // update the type, too
Andrease06dbcc2009-01-04 23:21:44 +0000382 shape.LaTeXTags().Type.value = codeCount > 0 ? EquationType.HasCompiledInlines : EquationType.None;
383 }
384
Andrease06dbcc2009-01-04 23:21:44 +0000385 private void DecompileTextRange(Slide slide, Shape shape, TextRange range)
386 {
387 // make sure this is always valid, otherwise the code will do stupid things
388 Debug.Assert(shape.LaTeXTags().Type == EquationType.HasCompiledInlines);
389
390 LaTeXEntries entries = shape.LaTeXTags().Entries;
391 int length = entries.Length;
392 for (int i = length - 1; i >= 0; i--)
393 {
394 LaTeXEntry entry = entries[i];
395 string latexCode = entry.Code;
396
blackhcc5e71772010-09-23 15:55:15 +0000397 if (!Helpers.IsEscapeCode(latexCode))
Andrease06dbcc2009-01-04 23:21:44 +0000398 {
399 int shapeID = entry.ShapeId;
400 // find the shape
401 Shape picture = slide.Shapes.FindById(shapeID);
402
Andreas8ce307a2009-02-25 20:01:45 +0000403 //Debug.Assert(picture != null);
Andrease06dbcc2009-01-04 23:21:44 +0000404 // fail gracefully
405 if (picture != null)
406 {
Andreas8ce307a2009-02-25 20:01:45 +0000407 Debug.Assert(picture.LaTeXTags().Type == EquationType.Inline);
Andrease06dbcc2009-01-04 23:21:44 +0000408 picture.Delete();
409 }
410
411 // release the cache entry, too
blackhcffefb6c2010-05-22 18:11:47 +0000412 ActivePresentation.CacheTags()[latexCode].Release();
Andrease06dbcc2009-01-04 23:21:44 +0000413 }
414
415 // add back the latex code
416 TextRange codeRange = range.Characters(entry.StartIndex, entry.Length);
blackhc9335fbe2010-05-23 19:04:10 +0000417 if( latexCode.StartsWith( @"\displaystyle{" ) && latexCode.EndsWith( "}" ) ) {
418 codeRange.Text = "$$[" + latexCode.Substring( @"\displayStyle{".Length, latexCode.Length - 1 - @"\displayStyle{".Length ) + "]$$";
419 }
420 else {
421 codeRange.Text = "$$" + latexCode + "$$";
422 }
blackhc9491d452009-05-03 23:06:13 +0000423 if (entry.FontSize != 0) {
424 codeRange.Font.Size = entry.FontSize;
425 }
blackhca88b6ad2009-05-02 11:29:20 +0000426 codeRange.Font.BaselineOffset = 0.0f;
Andrease06dbcc2009-01-04 23:21:44 +0000427 }
428
429 entries.Clear();
430 shape.LaTeXTags().Type.value = EquationType.HasInlines;
431 }
432
blackhc47bcee52010-09-24 07:42:57 +0000433 public void CompileShape(Slide slide, Shape shape)
434 {
435 // we don't need to compile already compiled shapes (its also sensible to avoid destroying escape sequences or overwrite entries, etc)
436 // don't try to compile equations (or their sources) either
437 EquationType type = shape.LaTeXTags().Type;
438 if (type == EquationType.HasCompiledInlines || type == EquationType.Equation)
439 {
440 return;
441 }
442
443 if (shape.HasTextFrame == Microsoft.Office.Core.MsoTriState.msoTrue)
444 {
445 TextFrame textFrame = shape.TextFrame;
446 if (textFrame.HasText == Microsoft.Office.Core.MsoTriState.msoTrue)
447 {
448 CompileInlineTextRange(slide, shape, textFrame.TextRange);
449 }
450 }
451 }
452
Andrease06dbcc2009-01-04 23:21:44 +0000453 public void DecompileShape(Slide slide, Shape shape)
454 {
455 // we don't need to decompile already shapes that aren't compiled
456 if (shape.LaTeXTags().Type != EquationType.HasCompiledInlines)
457 {
458 return;
459 }
460
461 if (shape.HasTextFrame == Microsoft.Office.Core.MsoTriState.msoTrue)
462 {
463 TextFrame textFrame = shape.TextFrame;
464 if (textFrame.HasText == Microsoft.Office.Core.MsoTriState.msoTrue)
465 {
466 DecompileTextRange(slide, shape, textFrame.TextRange);
467 }
468 }
469 }
470
471 /*
472 private bool presentationNeedsCompile;
473 // TODO: use an exception, etc. to early-out [1/2/2009 Andreas]
474 private void ShapeNeedsCompileWalker(Slide slide, Shape shape) {
475 EquationType type = shape.LaTeXTags().Type;
476 if( type == EquationType.HasInlines ) {
477 presentationNeedsCompile = true;
478 }
479 if( type == EquationType.None ) {
480 // check it and assign a type if necessary
481 if (shape.HasTextFrame == Microsoft.Office.Core.MsoTriState.msoTrue)
482 {
483 TextFrame textFrame = shape.TextFrame;
484 if (textFrame.HasText == Microsoft.Office.Core.MsoTriState.msoTrue)
485 {
486 // search for a $$ - if there is one occurrence, it needs to be compiled
487 if(textFrame.TextRange.Text.IndexOf("$$") != -1) {
488 presentationNeedsCompile = true;
489 // update the type, too
490 shape.LaTeXTags().Type.value = EquationType.HasInlines;
491 } else {
492 shape.LaTeXTags().Type.value = EquationType.None;
493 }
494 }
495 }
496 }
497 }
498
499 public bool NeedsCompile(Presentation presentation) {
500 presentationNeedsCompile = false;
501 WalkPresentation(presentation, ShapeNeedsCompileWalker);
502 return presentationNeedsCompile;
503 }*/
504
505
506 private void FinalizeShape(Slide slide, Shape shape)
507 {
508 CompileShape(slide, shape);
509 shape.LaTeXTags().Clear();
510 }
511
Andrease06dbcc2009-01-04 23:21:44 +0000512 public void CompileSlide(Slide slide)
513 {
blackhc093923f2010-09-07 16:56:48 +0000514 ShapeWalker.WalkSlide(slide, CompileShape);
Andrease06dbcc2009-01-04 23:21:44 +0000515 }
516
517 public void DecompileSlide(Slide slide)
518 {
blackhc093923f2010-09-07 16:56:48 +0000519 ShapeWalker.WalkSlide(slide, DecompileShape);
Andrease06dbcc2009-01-04 23:21:44 +0000520 }
521
522 public void CompilePresentation(Presentation presentation)
523 {
blackhc093923f2010-09-07 16:56:48 +0000524 ShapeWalker.WalkPresentation(presentation, CompileShape);
Andrease06dbcc2009-01-04 23:21:44 +0000525 }
526
527 /// <summary>
528 /// Removes all tags and all pictures that belong to inline formulas
529 /// </summary>
530 /// <param name="slide"></param>
531 public void FinalizePresentation(Presentation presentation)
532 {
blackhc093923f2010-09-07 16:56:48 +0000533 ShapeWalker.WalkPresentation(presentation, FinalizeShape);
Andrease06dbcc2009-01-04 23:21:44 +0000534 // purge the cache, too
535 presentation.CacheTags().PurgeAll();
536 presentation.SettingsTags().Clear();
537 }
538
Andreas9d4c7a32009-03-04 12:50:51 +0000539 public Shape CreateEmptyEquation()
540 {
blackhc69c5d322009-08-04 17:05:17 +0000541 const float width = 100, height = 60;
542
543 Shape shape = ActiveSlide.Shapes.AddShape(Microsoft.Office.Core.MsoAutoShapeType.msoShapeRectangle, 100, 100, width, height);
Andreasa159b6e2009-03-07 21:07:13 +0000544 shape.Fill.ForeColor.ObjectThemeColor = Microsoft.Office.Core.MsoThemeColorIndex.msoThemeColorBackground1;
545 shape.Fill.Solid();
546
547 shape.Line.Visible = Microsoft.Office.Core.MsoTriState.msoFalse;
Andreas9d4c7a32009-03-04 12:50:51 +0000548
549 LaTeXTags tags = shape.LaTeXTags();
550 tags.Type.value = EquationType.Equation;
blackhc69c5d322009-08-04 17:05:17 +0000551 tags.OriginalWidth.value = width;
552 tags.OriginalHeight.value = height;
blackhc9fb503b2010-09-13 12:46:32 +0000553 tags.FontSize.value = InitialEquationFontSize;
Andreas9d4c7a32009-03-04 12:50:51 +0000554
555 return shape;
556 }
557
blackhce0739142010-05-22 19:13:40 +0000558 public Shape EditEquation( Shape equation, out bool cancelled ) {
blackhc67aa8c82010-05-22 19:29:58 +0000559 EquationEditor editor = new EquationEditor( equation.LaTeXTags().Code, equation.LaTeXTags().FontSize );
blackhc802790d2009-08-03 23:03:27 +0000560 DialogResult result = editor.ShowDialog();
561 if( result == DialogResult.Cancel ) {
blackhce0739142010-05-22 19:13:40 +0000562 cancelled = true;
blackhc69c5d322009-08-04 17:05:17 +0000563 // don't change anything
blackhc802790d2009-08-03 23:03:27 +0000564 return equation;
565 }
blackhce0739142010-05-22 19:13:40 +0000566 else {
567 cancelled = false;
568 }
blackhc802790d2009-08-03 23:03:27 +0000569
Andreas9d4c7a32009-03-04 12:50:51 +0000570 // recompile the code
571 //equation.TextFrame.TextRange.Text = equationSource.TextFrame.TextRange.Text;
blackhc802790d2009-08-03 23:03:27 +0000572 string latexCode = editor.LaTeXCode;
Andreasa159b6e2009-03-07 21:07:13 +0000573
blackhc802790d2009-08-03 23:03:27 +0000574 Slide slide = equation.GetSlide();
575 if (slide == null) {
576 // TODO: what do we do in this case? [3/3/2009 Andreas]
577 return equation;
Andreas9d4c7a32009-03-04 12:50:51 +0000578 }
579
blackhc802790d2009-08-03 23:03:27 +0000580 Shape newEquation = null;
581 if (latexCode.Trim() != "") {
blackhc363e8362010-09-22 21:35:03 +0000582 newEquation = LaTeXRendering.GetPictureShapeFromLaTeXCode( slide, latexCode, editor.FontSize );
Andreas9d4c7a32009-03-04 12:50:51 +0000583 }
Andreasa159b6e2009-03-07 21:07:13 +0000584
blackhc802790d2009-08-03 23:03:27 +0000585 if (newEquation != null) {
586 LaTeXTags tags = newEquation.LaTeXTags();
Andreas9d4c7a32009-03-04 12:50:51 +0000587
blackhc802790d2009-08-03 23:03:27 +0000588 tags.OriginalWidth.value = newEquation.Width;
589 tags.OriginalHeight.value = newEquation.Height;
blackhc9fb503b2010-09-13 12:46:32 +0000590 tags.FontSize.value = editor.FontSize;
blackhc67aa8c82010-05-22 19:29:58 +0000591
Andreasa159b6e2009-03-07 21:07:13 +0000592 tags.Type.value = EquationType.Equation;
593 }
blackhc802790d2009-08-03 23:03:27 +0000594 else {
595 newEquation = CreateEmptyEquation();
Andreas9d4c7a32009-03-04 12:50:51 +0000596 }
597
blackhc802790d2009-08-03 23:03:27 +0000598 newEquation.LaTeXTags().Code.value = latexCode;
Andreas9d4c7a32009-03-04 12:50:51 +0000599
blackhc802790d2009-08-03 23:03:27 +0000600 newEquation.Top = equation.Top;
601 newEquation.Left = equation.Left;
Andreas9d4c7a32009-03-04 12:50:51 +0000602
603 // keep the equation's scale
604 // TODO: this scales everything twice if we are not careful [3/4/2009 Andreas]
blackhc802790d2009-08-03 23:03:27 +0000605 float widthScale = equation.Width / equation.LaTeXTags().OriginalWidth;
606 float heightScale = equation.Height / equation.LaTeXTags().OriginalHeight;
607 newEquation.LockAspectRatio = Microsoft.Office.Core.MsoTriState.msoFalse;
blackhce14d7a92010-05-22 19:56:04 +0000608 newEquation.Width *= widthScale;
609 newEquation.Height *= heightScale;
Andreas9d4c7a32009-03-04 12:50:51 +0000610
Andreasa159b6e2009-03-07 21:07:13 +0000611 // copy animations over from the old equation
612 Sequence sequence = slide.TimeLine.MainSequence;
613 var effects =
614 from Effect effect in sequence
blackhc802790d2009-08-03 23:03:27 +0000615 where effect.Shape == equation
Andreasa159b6e2009-03-07 21:07:13 +0000616 select effect;
617
blackhc47bcee52010-09-24 07:42:57 +0000618 newEquation.AddEffects(effects, false, sequence);
Andreasa159b6e2009-03-07 21:07:13 +0000619
620 // delete the old equation
blackhc802790d2009-08-03 23:03:27 +0000621 equation.Delete();
622
623 // return the new equation
624 return newEquation;
Andreas9d4c7a32009-03-04 12:50:51 +0000625 }
Andrease06dbcc2009-01-04 23:21:44 +0000626 }
627}