Copy a visio page to a new document - c#

What I want to accomplish:
I want to copy the active page in my Visio application to a new document and save it (and make it a byte[] for the db), I am already doing this but in a slightly "wrong" way as there is too much interaction with the Visio application.
Method to copy page to byte array:
private static byte[] VisioPageToBytes()
{
//Make a new invisible app to dump the shapes in
var app = new InvisibleApp();
Page page = MainForm.IVisioApplication.ActivePage;
app.AlertResponse = 2;
//Selact all shapes and copy, then deselect
MainForm.IVisioApplication.ActiveWindow.SelectAll();
MainForm.IVisioApplication.ActiveWindow.Selection.Copy();
MainForm.IVisioApplication.ActiveWindow.DeselectAll();
//Add empty document to invisible app and dump shapes
app.Documents.Add( string.Empty );
app.ActivePage.Paste();
//Save document and convert to byte[]
app.ActiveDocument.SaveAs( Application.UserAppDataPath + #"/LastStored.vsd" );
app.ActiveDocument.Close();
app.Quit();
app.AlertResponse = 0;
var bytes = File.ReadAllBytes( Application.UserAppDataPath + #"/LastStored.vsd" );
Clipboard.Clear();
return bytes;
}
Why it's wrong:
This code makes selections in the visio page and has to open an invisible window to store the page. I'm looking for a way with less interaction with the Visio application (as its unstable). The opening of the 2nd (invisible) Visio application occasionally makes my main Visio application crash.
I would like to do something like:
Page page = MainForm.IVisioApplication.ActivePage;
Document doc;
doc.Pages.Add( page ); //Pages.Add has no parameters so this doesn't work
doc.SaveAs(Application.UserAppDataPath + #"/LastStored.vsd");
If this is not possible in a way with less interaction (by "building" the document), please comment to let me know.
TL;DR;
I wan't to make a new Visio document without opening Visio and copy (the content of) 1 page to it.

If you want to create a copy page then you might find the Duplicate method on Page handy, but by the sounds of it just save the existing doc should work:
void Main()
{
var vApp = MyExtensions.GetRunningVisio();
var sourcePage = vApp.ActivePage;
var sourcePageNameU = sourcePage.NameU;
var vDoc = sourcePage.Document;
vDoc.Save(); //to retain original
var origFileName = vDoc.FullName;
var newFileName = Path.Combine(vDoc.Path, $"LastStored{Path.GetExtension(origFileName)}");
vDoc.SaveAs(newFileName);
//Remove all other pages
for (short i = vDoc.Pages.Count; i > 0; i--)
{
if (vDoc.Pages[i].NameU != sourcePageNameU)
{
vDoc.Pages[i].Delete(0);
}
}
//Save single page state
vDoc.Save();
//Close copy and reopen original
vDoc.Close();
vDoc = vApp.Documents.Open(origFileName);
}
GetRunningVisio is my extension method for using with LinqPad:
http://visualsignals.typepad.co.uk/vislog/2015/12/getting-started-with-c-in-linqpad-with-visio.html
...but you've already got a reference to your app so you can use that instead.
Update based on comments:
Ok, so how about this modification of your original code? Note that I'm creating a new Selection object from the page but not changing the Window one, so this shouldn't interfere with what the user sees or change the source doc at all.
void Main()
{
var vApp = MyExtensions.GetRunningVisio();
var sourcePage = vApp.ActivePage;
var sourceDoc = sourcePage.Document;
var vSel = sourcePage.CreateSelection(Visio.VisSelectionTypes.visSelTypeAll);
vSel.Copy(Visio.VisCutCopyPasteCodes.visCopyPasteNoTranslate);
var copyDoc = vApp.Documents.AddEx(string.Empty,
Visio.VisMeasurementSystem.visMSDefault,
(int)Visio.VisOpenSaveArgs.visAddHidden);
copyDoc.Pages[1].Paste(Visio.VisCutCopyPasteCodes.visCopyPasteNoTranslate);
var origFileName = sourceDoc.FullName;
var newFileName = Path.Combine(sourceDoc.Path, $"LastStored{Path.GetExtension(origFileName)}");
copyDoc.SaveAs(newFileName);
copyDoc.Close();
}
Note that this will only create a default page so you might want to include copying over page cells such as PageWidth, PageHeight, PageScale and DrawingScale etc. prior to pasting.

Related

WebBrowser.Document.ExecCommand with "Copy" parameter is not working in C# windows application

I am trying to convert HTML text into RTF in C# windows application.
For that,
I have created one sample windows application in C#.
Used Web Browser control.
Load HTML text into it.
Called web browser's document's object ExecCommand method with "Select" and "Copy" parameter one after the other.
Select command selects the text but Copy command does not copy selected text to the clipboard.
Following is the code that I have used:
//Load HTML text
System.Windows.Forms.WebBrowser webBrowser = new System.Windows.Forms.WebBrowser();
webBrowser.IsWebBrowserContextMenuEnabled = true;
webBrowser.Navigate("about:blank");
webBrowser.Document.Write(htmlText);//htmlText = Valid HTML text
//Copy formatted text from web browser
webBrowser.Document.ExecCommand("SelectAll", false, null);
webBrowser.Document.ExecCommand("Copy", false, null); // NOT WORKING
//Paste copied text from clipboard to Rich Text Box control
using (System.Windows.Forms.RichTextBox objRichTextBox = new System.Windows.Forms.RichTextBox())
{
objRichTextBox.SelectAll();
objRichTextBox.Paste();
string rtfTrxt = objRichTextBox.Rtf;
}
Notes:
I have also marked Main method as a STAThreadAttribute
This is not work on the client system (Windows Server 2019)
Works fine on my system (Windows 7 32 bits)
Browser version is same on my system and client ststem i.e. IE 11
We don't want to use any paid tool like SautinSoft.
I had the same problem.
This helped:
webBrowser.DocumentText = value;
while (webBrowser.DocumentText != value) Application.DoEvents();
webBrowser.Document.ExecCommand("SelectAll", false, null);
webBrowser.Document.ExecCommand("Copy", false, null);
richTextBoxActually.Text = "";
richTextBoxActually.Paste();
Probably it takes several iterations for wb to draw text that can be copied then.
I've used this solution for our task:
// The conversion process will be done completely in memory.
string inpFile = #"..\..\..\example.html";
string outFile = #"ResultStream.rtf";
byte[] inpData = File.ReadAllBytes(inpFile);
byte[] outData = null;
using (MemoryStream msInp = new MemoryStream(inpData))
{
// Load a document.
DocumentCore dc = DocumentCore.Load(msInp, new HtmlLoadOptions());
// Save the document to RTF format.
using (MemoryStream outMs = new MemoryStream())
{
dc.Save(outMs, new RtfSaveOptions() );
outData = outMs.ToArray();
}
// Show the result for demonstration purposes.
if (outData != null)
{
File.WriteAllBytes(outFile, outData);
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(outFile) { UseShellExecute = true });
}

Hash files are different after taking same screen shot with Selenium

I'm trying to learn about automated tests, using Selenium. I'm working alone, and so only have the docs, Google and then you guys.
Using Selenium in VS-2105, I save a screen shot of my website as an image to a file location, and then stop debugging at that point. This file then becomes the 'expected' result.
I then comment that line out, run it again, taking a screen shot but saving to a different location. The files, although of the same size, have different hash values.
To my eyes they are identical.
Is there something wrong with my approach?
This is the code which I run to create my 'master'
_webDriver.Navigate().GoToUrl(url);
var accept = _webDriver.SwitchTo().Alert();
accept.Accept();
IWebElement menu = _webDriver.FindElement(By.Id("link"));
menu.Click();
System.Threading.Thread.Sleep(500);
var screenshot = _webDriver.GetScreenshot();
var fileName = "expandMenuInPlan.png";
var origFile = _testImagesPersistentPath + fileName;
screenshot.SaveAsFile(origFile, OpenQA.Selenium.ScreenshotImageFormat.Png);
And this is the code I use to compare
_webDriver.Navigate().GoToUrl(url);
var accept = _webDriver.SwitchTo().Alert();
accept.Accept();
IWebElement menu = _webDriver.FindElement(By.Id("link"));
menu.Click();
System.Threading.Thread.Sleep(500);
var screenshot = _webDriver.GetScreenshot();
var fileName = "expandMenuInPlan.png";
var origFile = _testImagesPersistentPath + fileName;
//screenshot.SaveAsFile(origFile, OpenQA.Selenium.ScreenshotImageFormat.Png); COMMENTED OUT
//The above is identical
var newFile = _testImagesTempForTestRunPath + fileName;
screenshot.SaveAsFile(newFile, OpenQA.Selenium.ScreenshotImageFormat.Png);
string hashOrig = GetBytes(origFile);
string hashNew = GetBytes(newFile);
if (hashOrig != hashNew)
{
SaveFailedImage(origFile, newFile, fileName);
}
And the GetBytes method
private string GetBytes(string file)
{
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
{
var img = new Bitmap(file);
ImageConverter converter = new ImageConverter();
var bytes = (byte[])converter.ConvertTo(img, typeof(byte[]));
return Convert.ToBase64String(sha1.ComputeHash(bytes));
}
}
Is using screenshots in this manner just not reliable or is there something wrong with my code?
Hashing a image file and comparing is never the right approach. Even a single pixel off will change the hash. So you don't want to to make your tests brittle
You can use the C# method ImageComparer.Compare Method (Image, Image, ColorDifference, Image)
https://msdn.microsoft.com/en-in/library/hh191601.aspx?f=255&MSPPError=-2147217396
Or you can use external tools like https://applitools.com/ which allow you to do compare images which are smart in nature
PS: I have no association with applitools and it is just one example, there might be other services available as well.

Xamarin Android print PDF file

I am trying to print a PDF file I created using PrintManager. I have already written my code to generate PDF File using ITextSharp in my activity. Below is my generate PDF method:
private byte[] generatePdf()
{
using (var baos = new MemoryStream())
{
using (var ps = new StreamWriter(baos))
{
Document doc = new Document(PageSize.A4, 72, 72, 72, 72);
PdfWriter writer = PdfWriter.GetInstance(doc, ps.BaseStream);
if (!doc.IsOpen())
doc.Open();
doc.AddTitle("Sales Order " + GlobalVars.selectedSales.DocumentNo);
var TitleFont = new Font(Font.FontFamily.TIMES_ROMAN, 24, Font.BOLD);
var SubTitleFont = new Font(Font.FontFamily.TIMES_ROMAN, 20);
var HeadingFont = new Font(Font.FontFamily.TIMES_ROMAN, 18, Font.BOLD);
var BoldNormalFont = new Font(Font.FontFamily.TIMES_ROMAN, 14, Font.BOLD);
var NormalFont = new Font(Font.FontFamily.TIMES_ROMAN, 14);
doc.Add(new Paragraph("SALES ORDER", TitleFont));
... //other data
doc.Close();
writer.Close();
}
var bytes = baos.ToArray();
return bytes;
}
}
In short, the code above will return byte array containing the PDF file generated because I do not want to save the PDF anywhere in the Android device.
After generating this PDF, I am lost to use PrintManager, which takes only PrintDocumentAdapter argument to generate the file and not using my generated PDF. I am trying to generate CustomPrintAdapter which inherits from PrintDocumentAdapter, but have no idea how could I reuse the PDF that I created above from my activity because it uses PrintedPdfDocument data type.
Could you guys point me into the right direction here? Or is there any other easier approach to achieve printing custom PDF file?
UPDATE 1
Turns out Xamarin Android does not support System.Drawing.dll based on this article. I think ITextSharp library uses System.Drawing library because when I tried to build it, it raise an exception:
Exception while loading assemblies: System.IO.FileNotFoundException: Could not load assembly 'System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
Perhaps it doesn't exist in the Mono for Android profile?
And when I removed it, the build is success and no exception raised. Note that I have tried closing solution, delete all contents from Packages folder, reopen solution, and build; it did not work in this case.
Therefore, I will try generate the PDF using Android.Graphics.Canvas as seen on the Android Developer site.
UPDATE 2
I got no success generating PDF with Android.Graphics.Canvas like the way Android developer site does. The reason is I have no idea how to calculate the page and change the page as the content is dynamic. There was also a method like getPrintItemCount() in the site, which I have no idea what is that as the site does not include the explanation there.
Therefore, the workaround that I could think of now is printing WebView which is handled by my web service endpoint. I am creating a HTML layout for the page that I would like to print, then the WebView will open my web service endpoint. Luckily, printing with this approach can be done well. And of course, this approach has limitation which is the web service should be reachable to do printing.
To do this, first, I need to create a custom class that extends WebViewClient to get the event when page is finished loading using OnPageFinished method. I, then use the print method from my activity, which I pass it using the constructor. To make it clearer, I will attach my codes.
public class CustomWebViewClient : WebViewClient
{
private SalesDetailView myActivity;
public bool shouldOverrideUrlLoading(WebView view, string url)
{
return false;
}
public override void OnPageFinished(WebView view, string url)
{
Log.Debug("PRINT", "page finished loading " + url);
myActivity.doWebViewPrint(url);
base.OnPageFinished(view, url);
}
public CustomWebViewClient(SalesDetailView activity)
{
myActivity = activity;
}
}
Here is the doWebPrint method from my activity
public async void doWebViewPrint(string URL)
{
if (await vm.printCheck())
{
PrintDocumentAdapter adapter;
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Lollipop)
adapter = webView.CreatePrintDocumentAdapter("test");
else
adapter = webView.CreatePrintDocumentAdapter();
var printMgr = (PrintManager)GetSystemService(PrintService);
printMgr.Print("printTest", adapter, null);
}
}
I then create the WebView, load the website whenever the user clicks print button.
Button btnPrintSO = FindViewById<Button>(Resource.Id.btnPrintSO);
btnPrintSO.Click += delegate
{
string URL = ServerDatabaseApi.printSOEndpoint + GlobalVars.selectedSales.DocumentNo;
webView = new WebView(this);
CustomWebViewClient client = new CustomWebViewClient(this);
webView.SetWebViewClient(client);
webView.Settings.JavaScriptEnabled = true;
webView.LoadUrl(URL);
webView.Settings.UseWideViewPort = true;
webView.Settings.LoadWithOverviewMode = true;
};
Hope these updates could help anyone in the future.

c# Novacode.Picture to System.Drawing.Image

I'm reading in a .docx file using the Novacode API, and am unable to create or display any images within the file to a WinForm app due to not being able to convert from a Novacode Picture (pic) or Image to a system image. I've noticed that there's very little info inside the pic itself, with no way to get any pixel data that I can see. So I have been unable to utilize any of the usual conversion ideas.
I've also looked up how Word saves images inside the files as well as Novacode source for any hints and I've come up with nothing.
My question then is is there a way to convert a Novacode Picture to a system one, or should I use something different to gather the image data like OpenXML? If so, would Novacode and OpenXML conflict in any way?
There's also this answer that might be another place to start.
Any help is much appreciated.
Okay. This is what I ended up doing. Thanks to gattsbr for the advice. This only works if you can grab all the images in order, and have descending names for all the images.
using System.IO.Compression; // Had to add an assembly for this
using Novacode;
// Have to specify to remove ambiguous error from Novacode
Dictionary<string, System.Drawing.Image> images = new Dictionary<string, System.Drawing.Image>();
void LoadTree()
{
// In case of previous exception
if(File.Exists("Images.zip")) { File.Delete("Images.zip"); }
// Allow the file to be open while parsing
using(FileStream stream = File.Open("Images.docx", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using(DocX doc = DocX.Load(stream))
{
// Work rest of document
// Still parse here to get the names of the images
// Might have to drag and drop images into the file, rather than insert through Word
foreach(Picture pic in doc.Pictures)
{
string name = pic.Description;
if(null == name) { continue; }
name = name.Substring(name.LastIndexOf("\\") + 1);
name = name.Substring(0, name.Length - 4);
images[name] = null;
}
// Save while still open
doc.SaveAs("Images.zip");
}
}
// Use temp zip directory to extract images
using(ZipArchive zip = ZipFile.OpenRead("Images.zip"))
{
// Gather all image names, in order
// They're retrieved from the bottom up, so reverse
string[] keys = images.Keys.OrderByDescending(o => o).Reverse().ToArray();
for(int i = 1; ; i++)
{
// Also had to add an assembly for ZipArchiveEntry
ZipArchiveEntry entry = zip.GetEntry(String.Format("word/media/image{0}.png", i));
if(null == entry) { break; }
Stream stream = entry.Open();
images[keys[i - 1]] = new Bitmap(stream);
}
}
// Remove temp directory
File.Delete("Images.zip");
}

Remove outer print marks on PDF iTextSharp

I have a pdf file with a cover that looks like the following:
Now, I need to remove the so-called 'galley marks' around the edges of the cover. I am using iTextSharp with C# and I need code using iTextSharp to create a new document with only the intended cover or use PdfStamper to remove that. Or any other solution using iTextSharp that would deliver the results.
I have been unable to find any good code samples in my search to this point.
Do you have to actually remove them or can you just crop them out? If you can just crop them out then the code below will work. If you have to actually remove them from the file then to the best of my knowledge there isn't a simple way to do that. Those objects aren't explicitly marked as meta-objects to the best of my knowledge. The only way I can think of to remove them would be to inspect everything and see if it fits into the document's active area.
Below is sample code that reads each page in the input file and finds the various boxes that might exist, trim, art and bleed. (See this page.)
As long as it finds at least one it sets the page's crop box to the first item in the list. In your case you might actually have to perform some logic to find the "smallest" of all of those items or you might be able to just know that "art" will always work for you. See the code for additional comments. This targets iTextSharp 5.4.0.0.
//Sample input file
var inputFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Binder1.pdf");
//Sample output file
var outputFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Cropped.pdf");
//Bind a reader to our input file
using (var r = new PdfReader(inputFile)) {
//Get the number of pages
var pageCount = r.NumberOfPages;
//See this for a list: http://api.itextpdf.com/itext/com/itextpdf/text/pdf/PdfReader.html#getBoxSize(int, java.lang.String)
var boxNames = new string[] { "trim", "art", "bleed" };
//We'll create a list of all possible boxes to pick from later
List<iTextSharp.text.Rectangle> boxes;
//Loop through each page
for (var i = 1; i <= pageCount; i++) {
//Initialize our list for this page
boxes = new List<iTextSharp.text.Rectangle>();
//Loop through the list of known boxes
for (var j = 0; j < boxNames.Length; j++) {
//If the box exists
if(r.GetBoxSize(i, boxNames[j]) != null){
//Add it to our collection
boxes.Add(r.GetBoxSize(i, boxNames[j]));
}
}
//If we found at least one box
if (boxes.Count > 0) {
//Get the page's entire dictionary
var dict = r.GetPageN(i);
//At this point we might want to apply some logic to find the "inner most" box if our trim/bleed/art aren't all the same
//I'm just hard-coding the first item in the list for demonstration purposes
//Set the page's crop box to the specified box
dict.Put(PdfName.CROPBOX, new PdfRectangle(boxes[0]));
}
}
//Create our output file
using (var fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
//Bind a stamper to our reader and output file
using(var stamper = new PdfStamper(r,fs)){
//We did all of our PDF manipulation above so we don't actually have to do anything here
}
}
}

Categories