Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ScopedAlgorithms/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/4.0.3/EPPlus-4.0.3/Drawing/ExcelDrawings.cs @ 14728

Last change on this file since 14728 was 12074, checked in by sraggl, 10 years ago

#2341: Added EPPlus-4.0.3 to ExtLibs

File size: 24.0 KB
Line 
1/*******************************************************************************
2 * You may amend and distribute as you like, but don't remove this header!
3 *
4 * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
5 * See http://www.codeplex.com/EPPlus for details.
6 *
7 * Copyright (C) 2011  Jan Källman
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
17 * See the GNU Lesser General Public License for more details.
18 *
19 * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
20 * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
21 *
22 * All code and executables are provided "as is" with no warranty either express or implied.
23 * The author accepts no liability for any damage or loss of business that this product may cause.
24 *
25 * Code change notes:
26 *
27 * Author             Change            Date
28 * ******************************************************************************
29 * Jan Källman                    Initial Release           2009-12-22
30 * Jan Källman    License changed GPL-->LGPL 2011-12-16
31 *******************************************************************************/
32using System;
33using System.Collections.Generic;
34using System.Text;
35using System.Xml;
36using System.Collections;
37using System.IO;
38using System.Drawing;
39using System.Linq;
40using OfficeOpenXml.Drawing.Chart;
41using OfficeOpenXml.Table.PivotTable;
42using OfficeOpenXml.Utils;
43namespace OfficeOpenXml.Drawing
44{
45    /// <summary>
46    /// Collection for Drawing objects.
47    /// </summary>
48    public class ExcelDrawings : IEnumerable<ExcelDrawing>, IDisposable
49    {
50        private XmlDocument _drawingsXml=new XmlDocument();
51        private Dictionary<string, int> _drawingNames;
52        private List<ExcelDrawing> _drawings;
53        internal class ImageCompare
54        {
55            internal byte[] image { get; set; }
56            internal string relID { get; set; }
57
58            internal bool Comparer(byte[] compareImg)
59            {
60                if (compareImg.Length != image.Length)
61                {
62                    return false;
63                }
64
65                for (int i = 0; i < image.Length; i++)
66                {
67                    if (image[i] != compareImg[i])
68                    {
69                        return false;
70                    }
71                }
72                return true; //Equal
73            }
74        }
75        //internal List<ImageCompare> _pics = new List<ImageCompare>();
76        internal Dictionary<string, string> _hashes = new Dictionary<string, string>();
77        internal ExcelPackage _package;
78        internal Packaging.ZipPackageRelationship _drawingRelation = null;
79        internal ExcelDrawings(ExcelPackage xlPackage, ExcelWorksheet sheet)
80        {
81                _drawingsXml = new XmlDocument();               
82                _drawingsXml.PreserveWhitespace = false;
83                _drawings = new List<ExcelDrawing>();
84                _drawingNames = new Dictionary<string,int>(StringComparer.InvariantCultureIgnoreCase);
85                _package = xlPackage;
86                Worksheet = sheet;
87                XmlNode node = sheet.WorksheetXml.SelectSingleNode("//d:drawing", sheet.NameSpaceManager);
88                CreateNSM();
89                if (node != null)
90                {
91                    _drawingRelation = sheet.Part.GetRelationship(node.Attributes["r:id"].Value);
92                    _uriDrawing = UriHelper.ResolvePartUri(sheet.WorksheetUri, _drawingRelation.TargetUri);
93
94                    _part = xlPackage.Package.GetPart(_uriDrawing);
95                    XmlHelper.LoadXmlSafe(_drawingsXml, _part.GetStream());
96
97                    AddDrawings();
98                }
99         }
100        internal ExcelWorksheet Worksheet { get; set; }
101        /// <summary>
102        /// A reference to the drawing xml document
103        /// </summary>
104        public XmlDocument DrawingXml
105        {
106            get
107            {
108                return _drawingsXml;
109            }
110        }
111        private void AddDrawings()
112        {
113            // Look inside all children for the drawings because they could be inside
114            // Markup Compatibility AlternativeContent/Choice or AlternativeContent/Fallback nodes.
115            // The code below currently pretends that loading all Choice alternative drawings doesn't cause a problem
116            // elsewhere. This seems to be ok for the time being as encountered drawing files so far only seem to have
117            // one Choice node (and no Fallback) underneath the AlternativeContent node. (Excel 2013 that is.)
118            // This change prevents CodePlex issue #15028 from occurring.
119            // (the drawing xml part (that ONLY contained AlternativeContent nodes) was incorrectly being garbage collected when the package was saved)
120            XmlNodeList list = _drawingsXml.SelectNodes("//*[self::xdr:twoCellAnchor or self::xdr:oneCellAnchor or self::xdr:absoluteAnchor]", NameSpaceManager);
121
122            foreach (XmlNode node in list)
123            {
124               
125                ExcelDrawing dr;
126                switch(node.LocalName)
127                {
128                    case "oneCellAnchor":
129                        dr = new ExcelDrawing(this, node, "xdr:sp/xdr:nvSpPr/xdr:cNvPr/@name");
130                        break;
131                    case "twoCellAnchor":
132                        dr = ExcelDrawing.GetDrawing(this, node);
133                        break;
134                    case "absoluteAnchor":
135                        dr = ExcelDrawing.GetDrawing(this, node);
136                        break;
137                    default: //"absoluteCellAnchor":
138                        dr = null;
139                        break;
140                }
141                if (dr != null)
142                {
143                    _drawings.Add(dr);
144                    if (!_drawingNames.ContainsKey(dr.Name))
145                    {
146                        _drawingNames.Add(dr.Name, _drawings.Count - 1);
147                    }
148                }
149            }
150        }
151
152
153        #region NamespaceManager
154        /// <summary>
155        /// Creates the NamespaceManager.
156        /// </summary>
157        private void CreateNSM()
158        {
159            NameTable nt = new NameTable();
160            _nsManager = new XmlNamespaceManager(nt);
161            _nsManager.AddNamespace("a", ExcelPackage.schemaDrawings);
162            _nsManager.AddNamespace("xdr", ExcelPackage.schemaSheetDrawings);
163            _nsManager.AddNamespace("c", ExcelPackage.schemaChart);
164            _nsManager.AddNamespace("r", ExcelPackage.schemaRelationships);
165        }
166        /// <summary>
167        /// Provides access to a namespace manager instance to allow XPath searching
168        /// </summary>
169        XmlNamespaceManager _nsManager=null;
170        public XmlNamespaceManager NameSpaceManager
171        {
172            get
173            {
174                return _nsManager;
175            }
176        }
177        #endregion
178        #region IEnumerable Members
179
180        public IEnumerator GetEnumerator()
181        {
182            return (_drawings.GetEnumerator());
183        }
184        #region IEnumerable<ExcelDrawing> Members
185
186        IEnumerator<ExcelDrawing> IEnumerable<ExcelDrawing>.GetEnumerator()
187        {
188            return (_drawings.GetEnumerator());
189        }
190
191        #endregion
192
193        /// <summary>
194        /// Returns the drawing at the specified position. 
195        /// </summary>
196        /// <param name="PositionID">The position of the drawing. 0-base</param>
197        /// <returns></returns>
198        public ExcelDrawing this[int PositionID]
199        {
200            get
201            {
202                return (_drawings[PositionID]);
203            }
204        }
205
206        /// <summary>
207        /// Returns the drawing matching the specified name
208        /// </summary>
209        /// <param name="Name">The name of the worksheet</param>
210        /// <returns></returns>
211        public ExcelDrawing this[string Name]
212        {
213            get
214            {
215                if (_drawingNames.ContainsKey(Name))
216                {
217                    return _drawings[_drawingNames[Name]];
218                }
219                else
220                {
221                    return null;
222                }
223            }
224        }
225        public int Count
226        {
227            get
228            {
229                if (_drawings == null)
230                {
231                    return 0;
232                }
233                else
234                {
235                    return _drawings.Count;
236                }
237            }
238        }
239        Packaging.ZipPackagePart _part=null;
240        internal Packaging.ZipPackagePart Part
241        {
242            get
243            {
244                return _part;
245            }       
246        }
247        Uri _uriDrawing=null;
248        public Uri UriDrawing
249        {
250            get
251            {
252                return _uriDrawing;
253            }
254        }
255        #endregion
256        #region Add functions
257            /// <summary>
258            /// Add a new chart to the worksheet.
259            /// Do not support Bubble-, Radar-, Stock- or Surface charts.
260            /// </summary>
261            /// <param name="Name"></param>
262            /// <param name="ChartType">Type of chart</param>
263            /// <param name="PivotTableSource">The pivottable source for a pivotchart</param>   
264            /// <returns>The chart</returns>
265            public ExcelChart AddChart(string Name, eChartType ChartType, ExcelPivotTable PivotTableSource)
266            {
267                if(_drawingNames.ContainsKey(Name))
268                {
269                    throw new Exception("Name already exists in the drawings collection");
270                }
271
272                if (ChartType == eChartType.StockHLC ||
273                    ChartType == eChartType.StockOHLC ||
274                    ChartType == eChartType.StockVOHLC)
275                {
276                    throw(new NotImplementedException("Chart type is not supported in the current version"));
277                }
278                if (Worksheet is ExcelChartsheet && _drawings.Count > 0)
279                {
280                    throw new InvalidOperationException("Chart Worksheets can't have more than one chart");               
281                }
282                XmlElement drawNode = CreateDrawingXml();
283
284                ExcelChart chart = ExcelChart.GetNewChart(this, drawNode, ChartType, null, PivotTableSource);
285                chart.Name = Name;
286                _drawings.Add(chart);
287                _drawingNames.Add(Name, _drawings.Count - 1);
288                return chart;
289            }
290            /// <summary>
291            /// Add a new chart to the worksheet.
292            /// Do not support Bubble-, Radar-, Stock- or Surface charts.
293            /// </summary>
294            /// <param name="Name"></param>
295            /// <param name="ChartType">Type of chart</param>
296            /// <returns>The chart</returns>
297            public ExcelChart AddChart(string Name, eChartType ChartType)
298            {
299                return AddChart(Name, ChartType, null);
300            }
301            /// <summary>
302            /// Add a picure to the worksheet
303            /// </summary>
304            /// <param name="Name"></param>
305            /// <param name="image">An image. Allways saved in then JPeg format</param>
306            /// <returns></returns>
307            public ExcelPicture AddPicture(string Name, Image image)
308            {
309               return AddPicture(Name, image, null);
310            }
311            /// <summary>
312            /// Add a picure to the worksheet
313            /// </summary>
314            /// <param name="Name"></param>
315            /// <param name="image">An image. Allways saved in then JPeg format</param>
316            /// <param name="Hyperlink">Picture Hyperlink</param>
317            /// <returns></returns>
318            public ExcelPicture AddPicture(string Name, Image image, Uri Hyperlink)
319            {
320                if (image != null)
321                {
322                    if (_drawingNames.ContainsKey(Name))
323                    {
324                        throw new Exception("Name already exists in the drawings collection");
325                    }
326                    XmlElement drawNode = CreateDrawingXml();
327                    drawNode.SetAttribute("editAs", "oneCell");
328                    ExcelPicture pic = new ExcelPicture(this, drawNode, image, Hyperlink);
329                    pic.Name = Name;
330                    _drawings.Add(pic);
331                    _drawingNames.Add(Name, _drawings.Count - 1);
332                    return pic;
333                }
334                throw (new Exception("AddPicture: Image can't be null"));
335            }
336            /// <summary>
337            /// Add a picure to the worksheet
338            /// </summary>
339            /// <param name="Name"></param>
340            /// <param name="ImageFile">The image file</param>
341            /// <returns></returns>
342            public ExcelPicture AddPicture(string Name, FileInfo ImageFile)
343            {
344               return AddPicture(Name, ImageFile, null);
345            }
346            /// <summary>
347            /// Add a picure to the worksheet
348            /// </summary>
349            /// <param name="Name"></param>
350            /// <param name="ImageFile">The image file</param>
351            /// <param name="Hyperlink">Picture Hyperlink</param>
352            /// <returns></returns>
353            public ExcelPicture AddPicture(string Name, FileInfo ImageFile, Uri Hyperlink)
354            {
355                if (Worksheet is ExcelChartsheet && _drawings.Count > 0)
356                {
357                    throw new InvalidOperationException("Chart worksheets can't have more than one drawing");
358                }
359                if (ImageFile != null)
360               {
361                  if (_drawingNames.ContainsKey(Name))
362                  {
363                     throw new Exception("Name already exists in the drawings collection");
364                  }
365                  XmlElement drawNode = CreateDrawingXml();
366                  drawNode.SetAttribute("editAs", "oneCell");
367                  ExcelPicture pic = new ExcelPicture(this, drawNode, ImageFile, Hyperlink);
368                  pic.Name = Name;
369                  _drawings.Add(pic);
370                  _drawingNames.Add(Name, _drawings.Count - 1);
371                  return pic;
372               }
373               throw (new Exception("AddPicture: ImageFile can't be null"));
374            }
375
376        /// <summary>
377        /// Add a new shape to the worksheet
378        /// </summary>
379        /// <param name="Name">Name</param>
380        /// <param name="Style">Shape style</param>
381        /// <returns>The shape object</returns>
382   
383        public ExcelShape AddShape(string Name, eShapeStyle Style)
384            {
385                if (Worksheet is ExcelChartsheet && _drawings.Count > 0)
386                {
387                    throw new InvalidOperationException("Chart worksheets can't have more than one drawing");
388                }
389                if (_drawingNames.ContainsKey(Name))
390                {
391                    throw new Exception("Name already exists in the drawings collection");
392                }
393                XmlElement drawNode = CreateDrawingXml();
394                ExcelShape shape = new ExcelShape(this, drawNode, Style);
395                shape.Name = Name;
396                shape.Style = Style;
397                _drawings.Add(shape);
398                _drawingNames.Add(Name, _drawings.Count - 1);
399                return shape;
400            }
401            private XmlElement CreateDrawingXml()
402            {
403                if (DrawingXml.OuterXml == "")
404                {
405                    DrawingXml.LoadXml(string.Format("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><xdr:wsDr xmlns:xdr=\"{0}\" xmlns:a=\"{1}\" />", ExcelPackage.schemaSheetDrawings, ExcelPackage.schemaDrawings));
406                    _uriDrawing = new Uri(string.Format("/xl/drawings/drawing{0}.xml", Worksheet.SheetID),UriKind.Relative);
407
408                    Packaging.ZipPackage package = Worksheet._package.Package;
409                    _part = package.CreatePart(_uriDrawing, "application/vnd.openxmlformats-officedocument.drawing+xml", _package.Compression);
410
411                    StreamWriter streamChart = new StreamWriter(_part.GetStream(FileMode.Create, FileAccess.Write));
412                    DrawingXml.Save(streamChart);
413                    streamChart.Close();
414                    package.Flush();
415
416                    _drawingRelation = Worksheet.Part.CreateRelationship(UriHelper.GetRelativeUri(Worksheet.WorksheetUri, _uriDrawing), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/drawing");
417                    XmlElement e = Worksheet.WorksheetXml.CreateElement("drawing", ExcelPackage.schemaMain);
418                    e.SetAttribute("id",ExcelPackage.schemaRelationships, _drawingRelation.Id);
419
420                    Worksheet.WorksheetXml.DocumentElement.AppendChild(e);
421                    package.Flush();                   
422                }
423                XmlNode colNode = _drawingsXml.SelectSingleNode("//xdr:wsDr", NameSpaceManager);
424                XmlElement drawNode;
425                if (this.Worksheet is ExcelChartsheet)
426                {
427                    drawNode = _drawingsXml.CreateElement("xdr", "absoluteAnchor", ExcelPackage.schemaSheetDrawings);
428                    XmlElement posNode = _drawingsXml.CreateElement("xdr", "pos", ExcelPackage.schemaSheetDrawings);
429                    posNode.SetAttribute("y", "0");
430                    posNode.SetAttribute("x", "0");
431                    drawNode.AppendChild(posNode);
432                    XmlElement extNode = _drawingsXml.CreateElement("xdr", "ext", ExcelPackage.schemaSheetDrawings);
433                    extNode.SetAttribute("cy", "6072876");
434                    extNode.SetAttribute("cx", "9299263");
435                    drawNode.AppendChild(extNode);
436                    colNode.AppendChild(drawNode);
437                }
438                else
439                {
440                    drawNode = _drawingsXml.CreateElement("xdr", "twoCellAnchor", ExcelPackage.schemaSheetDrawings);
441                    colNode.AppendChild(drawNode);
442                    //Add from position Element;
443                    XmlElement fromNode = _drawingsXml.CreateElement("xdr", "from", ExcelPackage.schemaSheetDrawings);
444                    drawNode.AppendChild(fromNode);
445                    fromNode.InnerXml = "<xdr:col>0</xdr:col><xdr:colOff>0</xdr:colOff><xdr:row>0</xdr:row><xdr:rowOff>0</xdr:rowOff>";
446
447                    //Add to position Element;
448                    XmlElement toNode = _drawingsXml.CreateElement("xdr", "to", ExcelPackage.schemaSheetDrawings);
449                    drawNode.AppendChild(toNode);
450                    toNode.InnerXml = "<xdr:col>10</xdr:col><xdr:colOff>0</xdr:colOff><xdr:row>10</xdr:row><xdr:rowOff>0</xdr:rowOff>";
451                }
452
453                return drawNode;
454            }
455        #endregion
456        #region Remove methods
457            /// <summary>
458            /// Removes a drawing.
459            /// </summary>
460            /// <param name="Index">The index of the drawing</param>
461        public void Remove(int Index)
462        {
463            if (Worksheet is ExcelChartsheet && _drawings.Count > 0)
464            {
465                throw new InvalidOperationException("Can' remove charts from chart worksheets");
466            }
467            RemoveDrawing(Index);
468        }
469
470        internal void RemoveDrawing(int Index)
471        {
472            var draw = _drawings[Index];
473            draw.DeleteMe();
474            for (int i = Index + 1; i < _drawings.Count; i++)
475            {
476                _drawingNames[_drawings[i].Name]--;
477            }
478            _drawingNames.Remove(draw.Name);
479            _drawings.Remove(draw);
480        }
481        /// <summary>
482        /// Removes a drawing.
483        /// </summary>
484        /// <param name="Drawing">The drawing</param>
485        public void Remove(ExcelDrawing Drawing)
486        {
487            Remove(_drawingNames[Drawing.Name]);
488        }
489        /// <summary>
490        /// Removes a drawing.
491        /// </summary>
492        /// <param name="Name">The name of the drawing</param>
493        public void Remove(string Name)
494        {
495            Remove(_drawingNames[Name]);
496        }
497        /// <summary>
498        /// Removes all drawings from the collection
499        /// </summary>
500        public void Clear()
501        {
502            if (Worksheet is ExcelChartsheet && _drawings.Count > 0)
503            {
504                throw new InvalidOperationException("Can' remove charts from chart worksheets");
505            }
506            ClearDrawings();
507        }
508
509        internal void ClearDrawings()
510        {
511            while (Count > 0)
512            {
513                RemoveDrawing(0);
514            }
515        }
516        #endregion
517            internal void AdjustWidth(int[,] pos)
518            {
519                var ix = 0;
520                //Now set the size for all drawings depending on the editAs property.
521                foreach (OfficeOpenXml.Drawing.ExcelDrawing d in this)
522                {
523                    if (d.EditAs != Drawing.eEditAs.TwoCell)
524                    {
525                        if (d.EditAs == Drawing.eEditAs.Absolute)
526                        {
527                            d.SetPixelLeft(pos[ix, 0]);
528                        }
529                        d.SetPixelWidth(pos[ix, 1]);
530                       
531                    }
532                    ix++;
533                }
534            }
535            internal void AdjustHeight(int[,] pos)
536            {
537                var ix = 0;
538                //Now set the size for all drawings depending on the editAs property.
539                foreach (OfficeOpenXml.Drawing.ExcelDrawing d in this)
540                {
541                    if (d.EditAs != Drawing.eEditAs.TwoCell)
542                    {
543                        if (d.EditAs == Drawing.eEditAs.Absolute)
544                        {
545                            d.SetPixelTop(pos[ix, 0]);
546                        }
547                        d.SetPixelHeight(pos[ix, 1]);
548
549                    }
550                    ix++;
551                }
552            }
553            internal int[,] GetDrawingWidths()
554            {
555                int[,] pos = new int[Count, 2];
556                int ix = 0;
557                //Save the size for all drawings
558                foreach (ExcelDrawing d in this)
559                {
560                    pos[ix, 0] = d.GetPixelLeft();
561                    pos[ix++, 1] = d.GetPixelWidth();
562                }
563                return pos;
564            }
565            internal int[,] GetDrawingHeight()
566            {
567                int[,] pos = new int[Count, 2];
568                int ix = 0;
569                //Save the size for all drawings
570                foreach (ExcelDrawing d in this)
571                {
572                    pos[ix, 0] = d.GetPixelTop();
573                    pos[ix++, 1] = d.GetPixelHeight();
574                }
575                return pos;
576            }
577
578            public void Dispose()
579            {
580                _drawingsXml = null;
581                _hashes.Clear();
582                _hashes = null;
583                _part = null;
584                _drawingNames.Clear();
585                _drawingNames = null;
586                _drawingRelation = null;
587                foreach (var d in _drawings)
588                {
589                    d.Dispose();
590                }
591                _drawings.Clear();
592                _drawings = null;
593            }
594    }
595}
Note: See TracBrowser for help on using the repository browser.