Hiding Data from Powerpoint Charts using the MS Office Interop - c#

I'm updating software that cuts up and stitches PowerPoint slides together for end users. The slides contain charts. I need to find a way of hiding the raw chart data from the users that receive the files.
Is there anyway of doing this natively within the PowerPoint interop?
I've tried Read-Only but the user can still get at the data.

Ok. Here is my final answer to my question.
This shows the completed code that can perfectly replicate the slide while keeping the charts from being edited i.e. buy turning it into a .png.
This also solves the previous problem of the placeholders being left.
Hopefully some of this code is helpful to someone trawling the internet like I did.
//Open the slides presentation
var pres = _application.Presentations.Open2007(item.PresentationPath,
Microsoft.Office.Core.MsoTriState.msoFalse,
Microsoft.Office.Core.MsoTriState.msoFalse,
Microsoft.Office.Core.MsoTriState.msoFalse,
Microsoft.Office.Core.MsoTriState.msoFalse);
//For slide ranges
foreach (var i in range)
{
//Get the old slide
var oldSlide = pres.Slides[i];
oldSlide.Copy();
//Paste the slide into the new presentation
var newSlide = newPresentation.Slides.Paste(totalSlides + 1);
newSlide.Design = oldSlide.Design;
newSlide.ColorScheme = oldSlide.ColorScheme;
/* The shapes haven't retained their content because we are not in slide...
view because there is no window. */
//Delete all the shapes that were just pasted in because of the slide paste.
for (int k = newSlide.Shapes.Count; k > 0; k--)
{
newSlide.Shapes[k].Delete();
}
//Put in our shapes
//Loop forwards, because we arn't editing the list and forward is required to
//maintain the zorder we want on some slides.
for (int j = 1; j <= oldSlide.Shapes.Count; j++)
{
var oldShape = oldSlide.Shapes[j];
oldShape.Copy();
//Paste Put it where it should be on the page
/* This is a special case where the client have put textboxes in the
Powerpoint and rotated throwing off the position, so we need treat as rotated
shapes to make it right. */
if (oldShape.HasTextFrame == MsoTriState.msoTrue && Math.Abs(oldShape.Rotation) > 0)
{
//Paste as a shape because it's a more complex object
//set ALL THE PROPERTIES just in case.
var newShape = newSlide.Shapes.PasteSpecial(PpPasteDataType.ppPasteShape);
newShape.Rotation = oldShape.Rotation;
newShape.Top = oldShape.Top;
newShape.Left = oldShape.Left;
newShape.TextFrame.Orientation = oldShape.TextFrame.Orientation;
newShape.TextFrame.WordWrap = oldShape.TextFrame.WordWrap;
newShape.TextFrame.VerticalAnchor = oldShape.TextFrame.VerticalAnchor;
}
else // Act normally
{
//Paste the old shape into the new slide as an image to ENSURE FORMATTING
var newShape = newSlide.Shapes.PasteSpecial(PpPasteDataType.ppPastePNG);
newShape.Top = oldShape.Top;
newShape.Left = oldShape.Left;
}
}
totalSlides += ((item.EndIndex - item.StartIndex) + 1);
pres.Close();
}
After the new presentation has been compiled, you must delete all placeholders.
//Loop through all slides
foreach (Slide exportSlide in newPresentation.Slides)
{
//Delete the placeholders
for (int i = exportSlide.Shapes.Placeholders.Count; i > 0; i--)
{
exportSlide.Shapes.Placeholders[i].Delete();
}
}

VBA to ungroup a chart (in order to remove any connection to the original data)
Sub UngroupAChart()
Dim oSh As Shape
Dim oNewSh As Shape
Dim oNewShapes As ShapeRange
Dim oSl As Slide
' for demo purposes, use the currently selected
' shape; up to tester to select a chart
Set oSh = ActiveWindow.Selection.ShapeRange(1)
' Get a reference to the shape's parent slide
' We'll need it later
Set oSl = oSh.Parent
' put the shape on the clipboard
oSh.Copy
Set oNewSh = oSl.Shapes.PasteSpecial(ppPasteEnhancedMetafile)(1)
oNewSh.Ungroup
' once you're done testing, delete the original chart
'oSh.Delete
End Sub

Using the principals of Steve's code I've created a bespoke one for my use. As I am interacting with PowerPoint as a Component Service, I don't have an ActiveWindow or anything.
//Loop through all slides
foreach (Slide exportSlide in newPresentation.Slides)
{
//Loop through all shapes
foreach (Shape shape in exportSlide.Shapes)
{
//If the shape is a chart
if (shape.HasChart == MsoTriState.msoTrue)
{
//Copy to clipboard
shape.Copy();
//Paste as an image
var newShape = exportSlide.Shapes.PasteSpecial(PpPasteDataType.ppPastePNG);
//Move back to chart position
newShape.Left = shape.Left;
newShape.Top = shape.Top;
//Delete the original shape
shape.Delete();
}
}
}
This works fine for replacing the charts with images (no data at all). The only issue now is that because the slides where created with a layout containing chart, when you open the slide in Powerpoint the GUI displays an "Insert Chart" box. I've tried:
exportSlide.Layout = PpSlideLayout.ppLayoutBlank;
However because of my clients custom templates this is not blank, so it's not usable.

Related

Resize Page Height and Width with image inside c#

I use Aspose.Word. When you try to resize the page, everything changes. BUT the images go beyond the boundaries of the text space.
There are several images in the document and I have no idea how to fix it.
`
var input = #"d:\1.docx";
var output = #"d:\2.docx";
Document doc = new Document(input);
DocumentBuilder builder = new DocumentBuilder(doc);
if (project.Variables["flagsize"].Value=="69")
{
builder.PageSetup.PageWidth = ConvertUtil.MillimeterToPoint(152.4);
builder.PageSetup.PageHeight = ConvertUtil.MillimeterToPoint(228.6);
Node[] runs = doc.GetChildNodes(NodeType.Run, true).ToArray();
for (int j = 0; j < runs.Length; j++)
{ Run run = (Run)runs[j];
run.Font.Size = 18;
}
}
foreach (Section section in doc)
{
section.PageSetup.PaperSize = Aspose.Words.PaperSize.Custom;
section.PageSetup.LeftMargin= ConvertUtil.MillimeterToPoint(22);
section.PageSetup.RightMargin= ConvertUtil.MillimeterToPoint(22);
}
doc.Save(output);
`
Try to find correct method of word.
Expecting all images at doc will be right dimensions
I think this code i need:
foreach (Aspose.Words.Drawing.Shape shape in doc)
{
shape.Width ...
}
But i have error :
Не удалось привести тип объекта "Aspose.Words.Section" к типу "Aspose.Words.Drawing.Shape".
To get all shapes in the document, you can use Document.GetChildNodes method passing the appropriate NodeType as a parameter. For example the following code returns all shapes in the document:
NodeCollection shapes = doc.GetChildNodes(NodeType.Shape, true);
You can use LINQ to filter the collection, for example the following code returns shapes that has an image:
List<Shape> shapes = doc.GetChildNodes(NodeType.Shape, true)
.Cast<Shape>().Where(s => s.HasImage).ToList();
It looks like your requirement is to fit the image to image size. I think the example provided here might be useful for you. In the provided example an image is instead into the document and page is adjusted to the actual image size. Then the result document is converted to PDF.
NodeCollection shapes = doc.GetChildNodes(NodeType.Shape, true);
PageSetup page_Setup = doc.FirstSection.PageSetup;
foreach (Shape shape in shapes)
{
shape.HorizontalAlignment = HorizontalAlignment.Center;
shape.Width = page_Setup.PageWidth - page_Setup.LeftMargin - page_Setup.RightMargin;
}

I want to make my header left as well as right aligned

I am trying to put some image in header of Word document through Microsoft. Office.Interop.Word.
I want to place the picture stretched to both boundaries of document but I am unable to do so; whenever I set right and left indent it only changes the left indent like image is only stretched on the left side but not on the right side, Any type of help will be very precious to me. This is the snippet I am trying with:
foreach (Microsoft.Office.Interop.Word.Section wordSection in docWord.Sections)
{
wordSection.PageSetup.HeaderDistance = 0;
Microsoft.Office.Interop.Word.Range footerRange = wordSection.Footers[Microsoft.Office.Interop.Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;
//footerRange.Font.ColorIndex = Microsoft.Office.Interop.Word.WdColorIndex.wdDarkRed;
//footerRange.ParagraphFormat.LeftIndent = -(docWord.Application.CentimetersToPoints(3));
footerRange.InlineShapes.AddPicture(#"C:\\test\\footer.png");
Microsoft.Office.Interop.Word.Range headerRange = wordSection.Headers[Microsoft.Office.Interop.Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;
//headerRange.Font.ColorIndex = Microsoft.Office.Interop.Word.WdColorIndex.wdDarkRed;
MessageBox.Show((headerRange.ParagraphFormat.RightIndent - (docWord.Application.CentimetersToPoints(72))).ToString());
//headerRange.ParagraphFormat.RightIndent = (docWord.Application.InchesToPoints(-1));
//headerRange.ParagraphFormat.LeftIndent = -(docWord.Application.CentimetersToPoints(3));
headerRange.ParagraphFormat.FirstLineIndent = -(docWord.Application.CentimetersToPoints(3));
//MessageBox.Show(headerRange.ParagraphFormat.RightIndent.ToString());
//headerRange.ParagraphFormat.SpaceBefore =0 ;
headerRange.InlineShapes.AddPicture(#"C:\\test\\header.png");
}
I have worked with Microsoft.interope Library. You are doing it correctly. Unfortunately, Microsoft.interope does not provide support for stretch header.

ArcObjects 10.3 Add Transparent Polygon to Map

This is an issue I have been trying to tackle for a while and decided to reach out for help. I am creating an ESRI ArcGIS Desktop Add-In that allows the user to draw a polygon and then have it added to the map. I am able to capture the polygon and add it to the map, the issue is the transparency. Currently and by default it is 100% opacity and solid. I want to make it around 50% opacity so the user can see the data behind it.
Here is the code I have so far:
public static void AddPolygonToMap(IActiveView ActiveViewInstance, IGeometry NewGeo)
{
//Local Variable Declaration
var fillShapeElement = default(IFillShapeElement);
var element = default(IElement);
var graphicsContainer = default(IGraphicsContainer);
var simpleFilleSymbol = default(ISimpleFillSymbol);
var newRgbColor = default(IRgbColor);
var lineSymbol = default(ILineSymbol);
//Use the IElement interface to set the Envelope Element's geo
element = new PolygonElement();
element.Geometry = NewGeo;
//QI for the IFillShapeElement interface so that the symbol property can be set
fillShapeElement = element as IFillShapeElement;
//Create a new fill symbol
simpleFilleSymbol = new SimpleFillSymbol();
//Create a new color marker symbol of the color black;
newRgbColor = new RgbColor();
newRgbColor.Red = 0;
newRgbColor.Green = 0;
newRgbColor.Blue = 0;
//Create a new line symbol so that we can set the width outline
lineSymbol = new SimpleLineSymbol();
lineSymbol.Color = newRgbColor;
lineSymbol.Width = 2;
//Setup the Simple Fill Symbol
simpleFilleSymbol.Color = newRgbColor;
simpleFilleSymbol.Style = esriSimpleFillStyle.esriSFSHollow;
simpleFilleSymbol.Outline = lineSymbol;
fillShapeElement.Symbol = simpleFilleSymbol;
//QI for the graphics container from the active view allows access to basic graphics layer
graphicsContainer = ActiveViewInstance as IGraphicsContainer;
//Add the new element at Z order 0
graphicsContainer.AddElement((IElement)fillShapeElement, 0);
//Show the new graphic
ActiveViewInstance.Refresh();
}
I know that this is possible somehow and I am sure it's just a line or two missing but any help would be much appreciated.
V/r,
Josh
This looks to be a graphic element that you are creating. Graphic elements do not support transparency other than 100% transparent or 0% transparent. This is outlined in the following documentation:
IColor.Transparency Property
http://help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp/index.html#//001w000000nt000000
For graphic elements, 0 for transparent and 255 for opaque are the only supported values.
I hope this helps!

How to add a custom slide before and after the first slide in PowerPoint

I have created an Office add-in in which I need to add a slide to the presentation on click of a custom button.
Now, if the user clicks at the top(before first slide) in slide preview pane(i.e pane in the left hand side), the new custom slide should be added at the first position. If however, the user selects in between any two slides, the new custom slide should be added in between.
I am trying the below code:
if (insertNextSlideHere == 0)
{
slide = Globals.ThisAddIn.Application.ActivePresentation.Slides.Add(1, Microsoft.Office.Interop.PowerPoint.PpSlideLayout.ppLayoutBlank);
}
else if (Globals.ThisAddIn.sldIndexVal == 0 && Globals.ThisAddIn.Application.ActivePresentation.Windows[1].Selection.SlideRange[1].SlideIndex == 1)
{
slide = Globals.ThisAddIn.Application.ActivePresentation.Slides.Add(Globals.ThisAddIn.Application.ActivePresentation.Windows[1].Selection.SlideRange[1].SlideIndex, Microsoft.Office.Interop.PowerPoint.PpSlideLayout.ppLayoutBlank);
slide.MoveTo(1);
}
else if (Globals.ThisAddIn.sldIndexVal == 0 && Globals.ThisAddIn.Application.ActivePresentation.Windows[1].Selection.SlideRange[1].SlideIndex > 1)
{
MessageBox.Show("loop1");
slide = Globals.ThisAddIn.Application.ActivePresentation.Slides.Add(Globals.ThisAddIn.Application.ActivePresentation.Windows[1].Selection.SlideRange[1].SlideIndex + 1, Microsoft.Office.Interop.PowerPoint.PpSlideLayout.ppLayoutBlank);
}
else if (Globals.ThisAddIn.sldIndexVal == 0)
{
MessageBox.Show("loop2");
slide = Globals.ThisAddIn.Application.ActivePresentation.Slides.Add(Globals.ThisAddIn.Application.ActivePresentation.Windows[1].Selection.SlideRange[1].SlideIndex, Microsoft.Office.Interop.PowerPoint.PpSlideLayout.ppLayoutBlank);
slide.MoveTo(2);
}
else
{
MessageBox.Show("loop3");
slide = Globals.ThisAddIn.Application.ActivePresentation.Slides.Add(Globals.ThisAddIn.Application.ActivePresentation.Windows[1].Selection.SlideRange[1].SlideIndex +1,
Microsoft.Office.Interop.PowerPoint.PpSlideLayout.ppLayoutBlank);
}
However, I am basically not able to figure out how to distinguish between when the mouse is clicked at the top and when in between two slides.
Please refer to the image attached and help.)
Thanks!
Change the view then change back. If the cursor was between, say, slides 2 and 3, when you return to the original view, it'll be on 2.
For example, in VBA you'd:
Dim lCurrentView As Long
' save the current view
lCurrentView = ActiveWindow.ViewType
' switch views
ActiveWindow.ViewType = ppViewNotesPage
' switch back
ActiveWindow.ViewType = lCurrentView
' and now add a slide after the current slide
Another trick that seems to work (only if you're in Normal view):
For x = 1 To ActiveWindow.Panes.Count
Debug.Print ActiveWindow.Panes(x).ViewType
ActiveWindow.Panes(x).Activate
Next
Problem: if the cursor is sitting before the first slide or between slides 1 and 2, both methods will select the first slide. That will make it difficult to tell whether the cursor was between slides 1 and 2 or before slide 1.
I'm not sure how to solve that one, other than maybe by sending arrowkeys to the window. Ugly.

ASP.NET Charting - MapAreas null when returning chart as binary data

My objective: return coordinates and shapes so that I can roll my own custom hover text to a .NET Charting image. This would be no problem if I were using the ASP.NET control (which renders both an image tag and an HTML <map> tag; however, I'm in the MVC world so I'm returning the chart as a binary image. Here's the gist:
public virtual FileStreamResult Chart()
{
//Set up chart
Chart Chart1 = new Chart();
Chart1.RenderType = RenderType.ImageTag;
Chart1.ChartAreas.Add(new ChartArea("First"));
//Add some lovely data
Series s = new Series();
s.Name = "Tasks";
s.Points.AddXY("Task 1", 5, 8);
s.ChartArea = "First";
s.ChartType = SeriesChartType.RangeBar;
//Add a tooltip - This **should** make the MapAreas collection populate.
s.ToolTip = "Hello World";
Chart1.Series.Add(s);
if (Chart1.MapAreas.Count == 0)
CryRiver(); //Always executed. :*(
//Output image as FileStreamResult
//...
}
The Problem: No matter what I do, I cannot get the MapAreas collection to contain anything. I believe this to be because the coordinates don't get populated until the image actually renders.
How do I get at the coordinates of the map areas for the charted data when rendering an image as binary?
Thanks!
Found an answer; not sure if it's the best one.
One must call Chart1.RenderControl(); before the Chart1.MapAreas gets populated.

Categories

Resources