I am trying to replicate the following method in C# Word interop (NetOffice)
Selection.PasteSpecial Link:=True, DataType:=wdPasteMetafilePicture, _
Placement:=wdInLine, DisplayAsIcon:=False
I am saying "replicate" since I don't want to actually use the PasteSpecial method, since I need to update many fields of type link in one go, and don't like to communicate with excel by relying on copy and paste.
I already have the following code that does mostly what I want but has some issues that I am not able to overcome.
Selection.InlineShapes.AddOLEObject ("Excel.Sheet.12", fileName + $"!{label}", true)
This method fails with an error message
Word is unable to create a link to the object you specified. Please insert the object directly into your file without creating a link
I am only getting this error message, if and only if I use a network drive or unc path with a label.
eg.
T:\TestFile.xlsx!Sheet1!TestRange
T:\TestFile.xlsx!Sheet1!R1C1:R2C2
//notice that T is a network drive and not a local drive
\\TestShare\TestFile.xlsx!Sheet1!TestRange
\\TestShare\TestFile.xlsx!Sheet1!R1C1:R2C2
If I dont use the label
eg.
T:\TestFile.xlsx
T:\TestFile.xlsx
//notice that T is a network drive and not a local drive
\\TestShare\TestFile.xlsx
\\TestShare\TestFile.xlsx
or a local drive eg. C: (with or without label)
C:\TestFile.xlsx
C:\TestFile.xlsx
C:\TestFile.xlsx!Sheet1!TestRange
C:\TestFile.xlsx!Sheet1!R1C1:R2C2
everything works fine.
So, only the combination network drive or unc + a label produces that error message.
I thought, okay no problem I already know how to change the path and label of the produced field and wrote the following code to include the label in a second step.
var inlineShape = Selection.InlineShapes.AddOLEObject("Excel.Sheet.12", fileName, true);
field = inlineShape.Field;
if (!string.IsNullOrEmpty(label))
{
var fieldCode = field.Code.Text;
//replace the produced empty label "" with the label I desire eg. "Sheet1!R1C1:R2C2"
fieldCode = fieldCode.Replace(" \"\" ", $" \"{label}\"");
//write the updated fieldcode back to the field
field.Code.Text = fieldCode;
//update the field
field.Update();
}
BUT this solution, changes the underlying format of the image from WMF (Windows Meta File) to EMF (Enhanced Meta File), but I need it to be stored in the WMF format, since Office 2010 doesn't render EMF correctly.
To get the underlying file, I rename my docx to zip and extract it to look into the word\media directory.
PasteSpecial doesn't throw that error, and produces that desired WMF format, but I can't seem to 100% replicate the routine that happens while PasteSpecial.
As I side note, I don't want to have png or bmp either, since those are generated with a blur around the font, so the best result delivers wmf for Office 2010.
For Office 2013 and higher, I am using the following code to get emf right from the start and don't face issue about network drive/unc + label error.
var field = range.Fields.Add(range, WdEnums.WdFieldType.wdFieldLink);
field.LinkFormat.SourceFullName = fileName + $"!{label}";
Edit:
Here a screenshot of the main problem that I try to solve, as I reminder this is how it looks in Office 2010 in Office 2013 and higher both look decent:
For both I am using paste special
https://support.office.com/en-us/article/paste-special-e03db6c7-8295-4529-957d-16ac8a778719
The first table in the word document produces an EMF File, while using the paste special format "Picture (Enhanced Metafile)", macro recording produces this code:
Selection.PasteSpecial Link:=False, DataType:=wdPasteEnhancedMetafile, _
Placement:=wdInLine, DisplayAsIcon:=False
The second table in the word document produces an WMF File, while using the paste special format "Picture (Windows Metafile)", macro recording produces this code:
Selection.PasteSpecial Link:=True, DataType:=wdPasteMetafilePicture, _
Placement:=wdInLine, DisplayAsIcon:=False
On the right side, you can see the excel file I did copy to paste in the left word document.
As you can clearly see, Office 2010 renders/draws the WMF file (the second table in the word document) alot nicer, so that would be my desired output, but as I mentioned I have struggle replicating the PasteSpecial and keeping the WMF file that renders/draws better.
Edit2:
I recorded my screen to show the error, pls excuse that my environment is in german.
https://imgur.com/a/50wcBGQ
The code I have used is this:
Sub NetworkAndLabel()
'this will fail
Dim fileName As String
Dim label As String
'T is a mapped drive for the share \\NAS1\Ablage
fileName = "T:\rfa\TestFile.xlsx"
'label is in german, in english it would be Sheet1!R1C1:R1C2
label = "Tabelle1!Z1S1:Z2S2"
Selection.InlineShapes.AddOLEObject "Excel.Sheet.12", fileName & "!" & label, True
End Sub
Sub Network()
'this will run successfully
Dim fileName As String
Dim label As String
'T is a mapped drive for the share \\NAS1\Ablage
fileName = "T:\rfa\TestFile.xlsx"
Selection.InlineShapes.AddOLEObject "Excel.Sheet.12", fileName, True
End Sub
Sub LocalAndLabel()
'this will run successfully
Dim fileName As String
Dim label As String
'E is a local drive (second partition, not a seperate drive)
'up till now, I only tested with C and wanted to see if a simple second partition works or not
fileName = "E:\rfa\TestFile.xlsx"
'label is in german, in english it would be Sheet1!R1C1:R1C2
label = "Tabelle1!Z1S1:Z2S2"
Selection.InlineShapes.AddOLEObject "Excel.Sheet.12", fileName & "!" & label, True
End Sub
Sub Local_()
'this will run successfully
Dim fileName As String
Dim label As String
'E is a local drive (second partition, not a seperate drive)
'up till now, I only tested with C and wanted to see if a simple second partition works or not
fileName = "E:\rfa\TestFile.xlsx"
Selection.InlineShapes.AddOLEObject "Excel.Sheet.12", fileName, True
End Sub
I wrote "UsedRange" in C5 of the excel file, to show the differnt output in word if a label is or isn't in use, if I don't specify the label, it uses the UsedRange of the first sheet.
FYI, I have written the code in simple vba to demonstrate that it has nothing to do with C# and can nativly reproduced without anything special. In the end the solution, if there is one, will be included in my C# application.
Edit3:
Just to clarify, if someone knows a way to make Office 2010 render/draw the EMF file better, that would also be a valid solution, for my problem, because at the end of the day, I simply want to have a decent looking linked excel file in my word document. I don't really care if EMF or WMF, I am just trying to work with WMF because it looks better, and have the issues that I described, telling Office 2010 to stay on the WMF format.
Because the only way, I know of, to overcome the error message, is by manipulating the Field, as shown above, but once I call field.Update() the WMF file gets replaced with EMF, and I end up with the bad looking render/draw of the EMF file.
Edit4:
As requested, I had a look into the internal structure of the document and had a look at the differences. The actual difference is basically 0, only had stuff that should be irrelevent, like revisionnumber, characters count and stuff like this.
But I noticed the following, even though this code
Selection.PasteSpecial Link:=True, DataType:=wdPasteMetafilePicture, _
Placement:=wdInLine, DisplayAsIcon:=False
generates a WMF file, which I can see either by looking into the word\media folder and seeing there the produced image1.wmf, or by looking at the file word\_rels\document.xml.rels which has the following entry
<Relationship Id="rId7" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image1.wmf"/>
The actual XML for the OLE Object has the following in word\document.xml
<o:OLEObject Type="Link" ProgID="Excel.Sheet.12" ShapeID="_x0000_i1025" DrawAspect="Content" r:id="rId8" UpdateMode="Always"><o:LinkType>EnhancedMetaFile</o:LinkType><o:LockedField>false</o:LockedField></o:OLEObject>
As you can see the LinkType is defined as EnhancedMetaFile (EMF).
Since the OLE Object is defined like this, I tried if a simple call to field.Update would trigger the change from WMF to EMF, and sadly thats the case, so even without me modifing the field's code, a simple call like this
Selection.InlineShapes(1).Field.Update
will already trigger the conversion from WMF to EMF since the OLE Object is just simply defined like this, the conversion can also be achieved without any custom code, just select the inline shape and hit F9 or right click and select Update Links (translated from my german environment)
Notice that for what ever reason, this code
Selection.InlineShapes.AddOLEObject("Excel.Sheet.12", "E:\rfa\TestFile.xlsx", True).Field.Update
Will not trigger the conversion, but if you save the word document and open it again, than you can call field.Update to trigger the conversion.
So, to have the conversion in one go, without the necessity of saving/closing the word document, you need to atleast call this
With Selection.InlineShapes.AddOLEObject("Excel.Sheet.12", "E:\rfa\TestFile.xlsx!Tabelle1!Z1S1:Z2S2", True).Field
.Code.Text = Replace(.Code.Text, "Z1S1:Z2S2", "Z2S2:Z3S3")
.Update
End With
I'm currently working on a project where I need to create a "dashboard" which can be exported as pdf. I wanted to use Rotativa but as our application uses .NET framework 4.0 it's not possible. So I found the NReco PdfGenerator.
Now that's the code how I create the PDF result:
var ViewAsString = RenderViewAsString("~/Views/QMetrics/StandardDashboard.cshtml", viewModel);
var htmlToPdf = new NReco.PdfGenerator.HtmlToPdfConverter();
htmlToPdf.PageWidth = 1600;
htmlToPdf.PageHeight = 900;
var pdfBytes = htmlToPdf.GeneratePdf(ViewAsString);
FileResult FileResult = new FileContentResult(pdfBytes, "application/pdf");
FileResult.FileDownloadName = "Dashboard-" + viewModel.ProjectName + "-" +
DateTime.Now.ToString() + "-.pdf";
return FileResult;
It successfully creates the PDF page with all the content that comes from the backend (Project information, and so on) but the page looks very ugly. On the original page I have 2 columns and on the PDF page it puts everything in one column. I tried a few different page sizes and I also changed the layout to be non-responsive but nothing has changed.
My first suggesstion was that the referenced CSS and JS files are not included when the PDF get's created, so I copied all the stuff that comes from external files (bootstrap, Chart.js) and pasted it directly in the .cshtml file. But nothing changed at all. My Chart is not rendering/loading and the missing CSS stuff is still not there.
On the NReco PDFGenerator website they say that it supports complex CSS code and also javascript code so I don't really understand why this is not working.
Has anyone here experiences with NReco or can someone recommend something else that works for .NET 4.0?
NReco PdfGenerator internally uses wkhtmltopdf tool, so you can check it and its options.
Regarding 2 columns: if you don't use flex/grid layout everything should work fine. Possibly you need to disable wkhtmltopdf smart shrinking logic (enabled by default) and define web page 'window' size explicitely (with "--viewport-size 1600" option).
Regarding CSS and charts: you need to check that CSS files could be accessed by wkhtmltopdf, simplest way to do that is running wkhtmltopdf.exe from the command line and check console log output (or, handle PdfGenerator's "LogReceived" event in C#). For Chart.js ensure that chart container div has explicit width (not in %), and that there are no js errors (you can get them in console by specifying "--debug-javascript" option). If your js code uses 'bind' method you have to include polyfill as WebKit engine version used in wkhtmltopdf doesn't support 'bind'.
When I try to remove Creator, Author, Subject, Title and Keywords from a PDF using Ghostscript.NET, I get the error -100.
Here are the switches I am using:
var switches = new List<string>
{
"-dBATCH",
"-dNOPAUSE",
"-sDEVICE=pdfwrite",
"-c",
"\"[/Creator()/Author()/Subject()/Title()/Keywords() /DOCINFO pdfmark\"",
$"-sOutputFile={pdfOutput}",
pdfInput
};
THANKS
You should read the back channel output to see the error messages and report those.
I can see several problems here, the first being that you have introduced PostScript using the -c switch, but not terminated it with the -f switch. This means that everything following the -c is interpreted as PostScript, -sOutputFile=... is not valid PostScript and will throw a syntax error.
You have created the PDF file, then set its DOCINFO to (), then interpreted a PDF file. The act of interpreting the PDF file will set the DOCINFO from the input file Info dictionary, so handily overwriting your empty strings. You need to set the Info dictionary entries after you have interpreted the PDF file, not before. So the -c "..../DOCINFO pdfmark" -f should come after the input file, not before it.
I tried to read a DICOM file using GDCM library using this code :
gdcm.ImageReader imagereader = new gdcm.ImageReader();
imagereader.SetFileName(#"E:\sample_success.dcm");
if (!imagereader.Read()) throw new Exception("Cannot read dicom file!");
For "sample_success.dcm" file, I can read the file just fine (sample_success.png).
But using "sample_failed.dcm" file, GDCM throws exception because it couldn't read it. I tried to open the file using other DICOM viewer such as Radiant and it worked. Is there something wrong with my GDCM build? Why it cannot read it?
I use GDCM 2.6.5. Please find both samples here.
You're file contains garbage (bunch of binary 0) after offset 0x1480aa (somewhere in the Pixel Data attribute). What did you expect from a toolkit if not report properly an error ?
By design GDCM will still load whatever it can until the error. So if you remove your new Exception in your code, you can decide (for example) to pass the imagereader.GetFile() to a gdcm::Writer and rewrite the file as clean DICOM.
As a side note I do not have access to Radiant software but I find it very odd that it does not indicate an error in that case.
I've checked with DCMTK and dicom3tools they all report a parsing issue.
Using the gdcm command line tool you can almost rewrite the file clean using:
$ gdcmconv -I sample_failed.dcm sample_failed_correct.dcm
Because your input dataset is invalid, GDCM (falsely) believe to see an attribute, you can remove it using:
$ gdcmanon --dumb --remove 0,0 sample_failed_correct.dcm sample_failed_correct_clean.dcm
And then:
$ gdcminfo sample_failed_correct.dcm
MediaStorage is 1.2.840.10008.5.1.4.1.1.3.1 [Ultrasound Multi-frame Image Storage]
TransferSyntax is 1.2.840.10008.1.2.4.50 [JPEG Baseline (Process 1): Default Transfer Syntax for Lossy JPEG 8 Bit Image Compression]
NumberOfDimensions: 3
Dimensions: (800,600,21)
SamplesPerPixel :3
BitsAllocated :8
BitsStored :8
HighBit :7
PixelRepresentation:0
ScalarType found :UINT8
PhotometricInterpretation: YBR_FULL_422
PlanarConfiguration: 0
TransferSyntax: 1.2.840.10008.1.2.4.50
Origin: (0,0,0)
Spacing: (0.0106324,0.0106324,1)
DirectionCosines: (1,0,0,0,1,0)
Rescale Intercept/Slope: (0,1)
Orientation Label: AXIAL
Which is valid with the number of Fragments in the Pixel Data:
$ gdcmdump sample_failed_correct.dcm | grep Item | grep "ff.d8" | wc
21 126 2856
In my software I make 2 PDF files from 1 input file using iTextSharp. I'd like to convert these files into 2 different PNG images using GS, but something strange happens. I use this code for the conversion:
GhostscriptRasterizer rasterizer = new GhostscriptRasterizer();
rasterizer.Open(newFilePath1, gsInfo, false);
Image image = rasterizer.GetPage(300, 300, 1);
image.Save(subDirPath + serCod + "_S1.png");
rasterizer.Close();
rasterizer.Open(newFilePath2, gsInfo, false);
image = rasterizer.GetPage(300, 300, 1);
image.Save(subDirPath + serCod + "_S2.png");
rasterizer.Close();
When I save the first image it shows like a blank page and the file name is the same of newFilePath1 without .pdf but with .png.
When I look at the second image with the same file name as newFilePath2 with .png instead of .pdf, it has the image of the newFilePath1 pdf file.
How can I solve this problem?
I'd suggest you try the same operation using Ghostscript from the command line (instead of through Ghostscript.NET). If you get the same result then you can open a bug report at bugs.ghostscript.com and someone can look at it (remember to include the PDF file(s) and command lines).
Otherwise you'll have to contact jhabjan (the author of Ghostscript.NET) and have him investigate it.