I try to write a pdf file with a header, logo and table using iText7 in c#.
I never used iText7 before and therefore I don't know how to write text in a paragraph to a fixed position.
Right now I am just using tabstops as anchors for my text. But the problem here is, when the string is too long everything following in the line will be shifted by a tabstop and the "columns" in the header aren't aligned anymore.
The following picture is what I want too achieve:
This picture shows what happens if a string gets too long (in this example I used a long username):
Here is a code snippet I use to write one line of the header:
// generate 8 tabstops to split pdf in equal sections
List<TabStop> tabStops = new List<TabStop>();
for (uint i = 0; i < 8; i++)
{
float tabSize = pageSize.GetWidth() / 8;
tabStops.Add(new TabStop(tabSize, TabAlignment.LEFT));
}
Paragraph p = new Paragraph();
p.SetFontSize(10);
// add tabstops to paragraph for text alignment
p.AddTabStops(tabStops);
// add title of header
p.Add(title1).Add("\n");
// write line one of header
p.Add("Serie: ").Add(new Tab()).Add(info.serial.ToString())
.Add(new Tab()).Add(new Tab())
.Add("Input CSV: ").Add(new Tab()).Add(info.inputFileName)
.Add(new Tab()).Add(new Tab()).Add("Out-Series: ")
.Add(info.serial.ToString()).Add("\n");
// line 2...
p.Add("User: ").Add(new Tab()).Add(info.username)
.Add(new Tab()).Add(new Tab()).Add(new Tab())
.Add("qPCR-Datei: ").Add(new Tab()).Add(info.qpcr1FileName)
.Add(new Tab()).Add(new Tab()).Add(new Tab())
.Add("STR-Out: ").Add(strFileName).Add("\n");
I hope someone can help me show me a better way of text alignment or has information where to look at.
Another nice tip would be how I can keep linebreaks in the same tab stop section. for example if a file name gets too long (s. "STR-Out: " in picture) the linebreak will be executed but the part of the filename in the new line should stay at the tab stop behind "STR-OUT: "
Instead of Tab/Tabspace use Tables and Cells so that alignment will be proper.
Create table of column 8 size (Label, Value, space , Label, Value, Space, Label, Value)
Use this sample Code.
PdfPTable table = new PdfPTable(8);
PdfPCell cell;
cell = new PdfPCell();
cell.setRowspan(2); //only if spanning needed
table.addCell(cell);
for(int aw=0;aw<8;aw++){
table.addCell("hi");
}
Thanks #shihabudheenk for pointing me in the right direction with the idea of using a table.
Just had to adjust some code to iText7.
First thing is that
Table headerTable = new Table().SetBorder(Border.NO_BORDER);
has no effect in iText7, you have to set the option for each cell individually like:
Cell cell = new Cell().SetBorder(Border.NO_BORDER);
but here is the problem that
cell.Add()
in iText7 only accepts IBlockElement as parameter so i have too use it like this:
cell.Add(new Paragraph("text");
which is pretty annoying doing that for every cell over and over again. Therefore i used a removeBorder function as suggested here
So the final code I use to build the header looks like this:
// initialize table with fixed column sizes
Table headerTable = new Table(UnitValue.CreatePercentArray(
new[] { 1f, 1.2f, 1f, 1.8f, 0.7f, 2.5f })).SetFixedLayout();
// write headerline 1
headerTable.AddCell("Serie: ").AddCell(info.serial.ToString())
.AddCell("Input CSV: ")
.AddCell(info.inputFileName)
// write remaining lines...
....
// remove boarder from all cells
removeBorder(headerTable);
private static void removeBorder(Table table)
{
foreach (IElement iElement in table.GetChildren())
{
((Cell)iElement).SetBorder(Border.NO_BORDER);
}
}
Related
[EDIT due to misunderstanding of the answer]
I'm doing a simple program in C# with PDF file creation with iText7.
In this PDF i'm adding a table whose first cell starts at a certain position in the file.
I don't know if I set the position correctly, but everytime I add another cell with tab.StartNewRow() the resulting new table is repositioned taking THAT last cell as position reference, putting the previously added cells from that point up, while I want to add the cells from that point down.
Which method should I use? That's my code:
Previously I set the position of the first table cell using tab1.SetFixedPosition(20, heigh, width);
and then, in order to add the other cells:
if (mylistbox.Items.Count > 0)
{
tab1.AddCell("FIRST CELL");
tab1.StartNewRow();
for (int i = 0; i < mylistbox.Items.Count; i++)
{
tab1.AddCell(mylistbox.Items[i].ToString());
tab1.StartNewRow();
}
doc.Add(tab1);
}
[EDIT #2] in order to explain my issue better
I have to put 5 tables, which have to grow from a certain point DOWN, positioned at equal distances, same height and width in the doc. This image explains how it should result:
In a WPF application, I have a ListBox with 5 items, numbered 1 through 5. This should be very similar to WinForms.
The CreatePercentArray takes a size which is equal to the amount of columns in a row.
An interesting article about tables: link
private void CreateListBoxTable(Document pdfDoc)
{
// Create an array where each item has an equal width, and use the entire pdf width
// The CreatePercentArray takes a size which is equal to the amount of columns in a row
// By using percentages, they will automatically adapt
// Use CreatePointArray for exacter measurements
var table = new Table(UnitValue.CreatePercentArray(2)).UseAllAvailableWidth();
if (!MyListBox.Items.IsEmpty)
{
foreach (var listBoxItem in MyListBox.Items)
{
table.AddCell(((ListBoxItem) listBoxItem).Content.ToString());
}
}
// Adds table to document
pdfDoc.Add(table);
// Closes document
pdfDoc.Close();
}
I'm working wth .NET 4.7.2, Windowsform.
I have a datagridview and I manage to generate a powerpoint file pptx.
I made a first ppt slide and I'd like to add the datagridview content into the second ppt slide given that I need to have the option to change the data within the PPt slide.
Microsoft.Office.Interop.PowerPoint.Application pptApp = new Microsoft.Office.Interop.PowerPoint.Application();
pptApp.Visible = Microsoft.Office.Core.MsoTriState.msoTrue;
Microsoft.Office.Interop.PowerPoint.Slides slides;
Microsoft.Office.Interop.PowerPoint._Slide slide;
Microsoft.Office.Interop.PowerPoint._Slide slide2;
Microsoft.Office.Interop.PowerPoint.TextRange objText;
// Create File
Presentation pptPresentation = pptApp.Presentations.Add(Microsoft.Office.Core.MsoTriState.msoTrue);
CustomLayout customLayout = pptPresentation.SlideMaster.CustomLayouts[PpSlideLayout.ppLayoutText];
// new Slide
slides = pptPresentation.Slides;
slide = slides.AddSlide(1, customLayout);
slide2 = slides.AddSlide(1, customLayout);
// title
objText = slide.Shapes[1].TextFrame.TextRange;
objText.Text = "Bonds Screner Report";
objText.Font.Name = "Haboro Contrast Ext Light";
objText.Font.Size = 32;
Shape shape1 = slide.Shapes[2];
slide.Shapes.AddPicture("C:\\mylogo.png", Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoTrue, shape1.Left, shape1.Top, shape1.Width, shape1.Height);
slide.NotesPage.Shapes[2].TextFrame.TextRange.Text = "Disclaimer";
dataGridViewBonds.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText;
dataGridViewBonds.SelectAll();
DataObject obj = dataGridViewBonds.GetClipboardContent();
Clipboard.SetDataObject(obj, true);
Shape shapegrid = slide2.Shapes[2];
I know I'm not so far by now but I miss smething. Any help would be appreciated !
I am familiar with Excel interop and have used it many times and most likely have become numb to the awkward ways in which interop works. Using PowerPoint interop can be very frustrating for numerous reasons, however, the biggest I feel is the lack of documentation and the differences between the different MS versions.
In addition, I looked for a third-party PowerPoint library and “Aspose” looked like the only option, unfortunately it is not a “free” option. I will assume there is a free third-party option and I just did not look in the right place… Or there may be a totally different way to do this possibly with XML. I am confident I am preaching to the choir.
Therefore, what I have been able to put together may work for you. For starters, looking at your current posted code, there is one part missing that you need to get the “copied” grid cells into the slide…
slide.Shapes.Paste();
This will paste the “copied” cells from the grid into an “unformatted” table into the slide. This will copy the “row header” if it is displayed in the grid in addition to the “new row” if the grids AllowUserToAddRows is set to true. If this “unformatted paste” works for you, then you are good to go.
If you prefer to have at least a minimally formatted table and ignore the row headers and last empty row… It may be easier to simply “create” a new Table in the slide with the size we want along with the correct number of rows and columns. Granted, this may be more work, however, using the paste is going require this anyway “IF” you want the table formatted.
The method (below) takes a power point _Slide and a DataGridView. The code “creates” a new Table in the slide based on the number of rows and columns in the given grid. With this approach, the table will be “formatted” using the default “Table Style” in the presentation. So, this may give you the formatting you want by simply “creating” the table as opposed to “pasting” the table.
I have tried to “apply” one of the existing “Table Styles” in power point, however, the examples I saw used something like…
table.ApplyStyle("{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}");
Which uses a GUID id to identify “which” style to use. I am not sure why MS decided on this GUID approach… this is beyond me, and it worked for “some” styles but not all.
Also, more common-sense solutions that showed something like…
table.StylePreset = TableStylePreset.MediumStyle2Accent2;
Unfortunately using my 2019 version of Office PowerPoint, this property does not exist. I have abandoned further research on this as it appears to be version dependent. Very annoying!
Given this, it may be easier if we format the cells individually as we want. We will need to add the cells text from the grid into the individual cells anyway, so we could also format the individual cells at the same time. Again, I am confident there is a better way, however, I could not find one.
Below the InsertTableIntoSlide(_Slide slide, DataGridView dgv) method takes a slide and a grid as parameters. It will add a table to the slide with data from the given grid. A brief code trace is below.
First a check is made to get the number of total rows in the grid (not including the headers) totRows. If the grids AllowUsersToAddRows is true, then the total rows variable is decremented by 1 to ignore this new row. Next the number of columns in the grid is set to the variable totCols. The top left X and Y point is defined topLeftX and topLeftY to position the table in the slide along with the tables width and height.
ADDED NOTE: Using the AllowUserToAddRows property to determine the number of rows … may NOT work as described above and will “miss” the last row… “IF” AllowUserToAddRows is true (default) AND the grid is data bound to a data source that does NOT allow new rows to be added. In that case you do NOT want to decrement the totRows variable.
Next a “Table” “Shape” is added to the slide using the previous variables to define the base table dimensions. Next are two loops. The first loop adds the header cells to the first row in the table. Then a second loop to add the data from the cells in the grid… to the table cells in the slide.
The commented-out code is left as an example such that you want to do some specific formatting for the individual cells. This was not need in my case since the “default” table style was close to the formatting I wanted.
Also, a note that “ForeColor” is the “Back ground” color of the cell/shape. Strange!
I hope this helps and again, sympathize more about having to use PowerPoint interop… I could not.
private void InsertTableIntoSlide(_Slide slide, DataGridView dgv) {
try {
int totRows;
if (dgv.AllowUserToAddRows) {
totRows = dgv.Rows.Count - 1;
}
else {
totRows = dgv.Rows.Count;
}
int totCols = dgv.Columns.Count;
int topLeftX = 10;
int topLeftY = 10;
int width = 400;
int height = 100;
// add extra row for header row
Shape shape = slide.Shapes.AddTable(totRows + 1, totCols, topLeftX, topLeftY, width, height);
Table table = shape.Table;
for (int i = 0; i < dgv.Columns.Count; i++) {
table.Cell(1, i+1).Shape.TextFrame.TextRange.Text = dgv.Columns[i].HeaderText;
//table.Cell(1, i+1).Shape.Fill.ForeColor.RGB = ColorTranslator.ToOle(Color.Blue);
//table.Cell(1, i+1).Shape.TextFrame.TextRange.Font.Bold = Microsoft.Office.Core.MsoTriState.msoTrue;
//table.Cell(1, i+1).Shape.TextFrame.TextRange.Font.Color.RGB = ColorTranslator.ToOle(Color.White);
}
int curRow = 2;
for (int i = 0; i < totRows; i++) {
for (int j = 0; j < totCols; j++) {
if (dgv.Rows[i].Cells[j].Value != null) {
table.Cell(curRow, j + 1).Shape.TextFrame.TextRange.Text = dgv.Rows[i].Cells[j].Value.ToString();
//table.Cell(curRow, j + 1).Shape.Fill.ForeColor.RGB = ColorTranslator.ToOle(Color.LightGreen);
//table.Cell(curRow, j + 1).Shape.TextFrame.TextRange.Font.Bold = Microsoft.Office.Core.MsoTriState.msoTrue;
//table.Cell(curRow, j + 1).Shape.TextFrame.TextRange.Font.Color.RGB = ColorTranslator.ToOle(Color.Black);
}
}
curRow++;
}
}
catch (Exception ex) {
MessageBox.Show("Error: " + ex.Message);
}
}
I want to draw a visual distinction between rows based on their content in my iText table.
I found a tutorial here:, but it's in Java instead of C#, and I don't know what version of iText it is for.
I am using iText 7 and it is apparently radically different than previous versions.
I tried using this code from that article:
if ((pod.Doc1Count > 0) && (pod.Doc2Count > 0))
{
Cell cell = new Cell();
cell.SetBackgroundColor(Color.DARK_GRAY);
}
...but it won't even compile (the Color class has no definition for DARK_GRAY).
I would much prefer to work with a row at a time, if that's possible, but I don't think it is.
Does anybody know how to change the background color of a row, either all at once, or a cell at a time, using iText 7?
UPDATE
I tried to convert this Java example to C#, but so far no go. This is what I have:
Color headerBg = new DeviceRgb(0xA6, 0xCB, 0x0B);
Color lineColor = new DeviceRgb(0xCC, 0xCC, 0xCC);
. . .
foreach (PairODocs pod in lstPairODocs.OrderByDescending(a => a.Doc1Prcntg).ThenByDescending(a => a.Doc2Prcntg))
{
if ((pod.Doc1Count > 0) && (pod.Doc2Count > 0))
{
Cell cell = new Cell();
cell.Add(new Paragraph(pod.Whirred));
cell.SetBackgroundColor(Color.headerBg);
table.AddCell(cell);
// TODO: If get it to work, add for the other four vals below, too...
}
else // these work, but are plain jane
{
table.AddCell(pod.Whirred);
table.AddCell(pod.Doc1Count.ToString());
table.AddCell(pod.Doc1Prcntg.ToString());
table.AddCell(pod.Doc2Count.ToString());
table.AddCell(pod.Doc2Prcntg.ToString());
}
}
...and the compiler says, "Error CS0117 'Color' does not contain a definition for 'headerBg'" on the cell.SetBackgroundColor(Color.headerBg); line.
UPDATE 2
I had an extraneous "Color." preceding "headerBg" which was causing it to not compile. After removing it, it compiles with this code:
if ((pod.Doc1Count > 0) && (pod.Doc2Count > 0))
{
Cell cell = new Cell();
cell.Add(new Paragraph(pod.Whirred));
cell.SetBackgroundColor(headerBg);
table.AddCell(cell);
cell = new Cell();
cell.Add(new Paragraph(pod.Doc1Count.ToString()));
cell.SetBackgroundColor(headerBg);
table.AddCell(cell);
... // etc.
}
iText.Kernel.Colors.Color bgColour = new DeviceRgb(169, 169, 169); //Create Darkgray color
cell.SetBackgroundColor(bgColour); // Set the color we created to the cell
https://www.rapidtables.com/web/color/gray-color.html
Here you can find multiple colors, just choose the one you like and edit the '169, 169, 169' in the example above.
Actually, if you're willing to stick with some basic colors, it's far simpler to use the iText.Kernel.Color class' ColorConstants class like so:
cell.SetBackgroundColor(ColorConstants.YELLOW);
I'm trying to fill the remaining space of the last line of a paragraph using iText7 with C#:
var par = new Paragraph(text);
par.Add(c);
document.Add(par);
How can i add - char to fill the space left by the line? Something like LineSeparator(new DashedLine() but from the beginning on the last character of my paragraph instead of new line.
You can use the concept of tabs and tab stops for it. This concept is not iText-specific.
Roughly speaking you can define points (tab stops) and adding a tab would "jump" to the next point. In your case the tab stop is the end of the line and you only need one tab.
Here is a complete example that uses small dashes on the baseline as the filling. You can implement ILineDrawer yourself to customize the behavior or subclass/configure an existing implementation. The code is in Java, but to convert it to C# you basically need to do some capitalization and that's it.
Document doc = ....;
Paragraph p = new Paragraph("Hello world").add(new Tab());
ILineDrawer filling = new DashedLine();
PageSize pageSize = doc.getPdfDocument().getDefaultPageSize();
Rectangle effectivePageSize = doc.getPageEffectiveArea(pageSize);
float rightTabStopPoint = effectivePageSize.getWidth();
TabStop tabStop = new TabStop(rightTabStopPoint, TabAlignment.LEFT, filling);
p.addTabStops(tabStop);
doc.add(p);
Result looks as follows:
I am trying to make the application generate an Excel file from a list of string type and a list of image type. I have the user enter a line then makes them take a screenshot and so on until G is pressed and then it should generate an Excel file with the format as so:
Row 1- Title-0-
Row 2- Header-1-
Row 3- Image-0-
Row 4- Header-2-
Row 5- Image-1-
Row 6- Header-3-
Row 7- Image-2-
Row 8- Header-4-
Row 9- Image-3-
Row 10- Header-5-
Row 11- Image-4-
...and so on until its done all in the collections.
I have created the List and List and I know that they both contain Strings and Images before I hit G as I have looked inspected the collections debug mode.
This is the code I have so far and the excel file looks right except there are no Images to be seen however it is re-sizing the rows to the pictures heights. I have never worked with Images before so think I could be missing something important but not sure what.
The Collections are passed into this method from a calling method
String collection is named "withHeadersList",
Image collection is named "withImgList".
Generate Excel Method:
public static bool GenerateTestPlan(List<String> withHeadersList, List<Image> withImgList, string stringOutputPath)
{
ExcelPackage newExcelPackage = CreateExcelPackage(withHeadersList[0]);
ExcelWorksheet newExcelWorksheet = CreateWorkSheet(newExcelPackage, "Sheet1");
SetCellValue(newExcelWorksheet, 1, 1, withHeadersList[0]); //Title
newExcelWorksheet.Row(1).Style.Font.Size = 35;
newExcelWorksheet.Row(1).Style.Font.Bold = true;
int pRowIndex = 3;
int hRowIndex = 2;
for (int i = 1; i < withHeadersList.Count; i++)
{
SetCellValue(newExcelWorksheet, hRowIndex, 1, withHeadersList[i]);
newExcelWorksheet.Row(hRowIndex).Style.Font.Size = 20;
newExcelWorksheet.Row(pRowIndex).Height = withImgList[i - 1].Height; //Set row height to height of screenshot
var img = newExcelWorksheet.Drawings.AddPicture(withHeadersList[i], withImgList[i - 1]); //Add Images (THINK THIS LAST PARAMETER IS THE PROBLEM)
img.SetPosition(pRowIndex, Pixel2MTU(2), 1, Pixel2MTU(2));
img.SetSize(withImgList[i - 1].Width, withImgList[i - 1].Height);
hRowIndex += 2;
pRowIndex += 2;
}
SaveExcelPackage(newExcelPackage, stringOutputPath);
return true;
}
Excel File here
As you see it's like the images are just not being rendered.
Your issue is most certainly with this line:
img.SetPosition(pRowIndex, Pixel2MTU(2), 1, Pixel2MTU(2));
I'm not sure why you are converting pixels to anything considering SetPosition is looking for the offset in pixels. From the metadata:
// Summary:
// Set the top left corner of a drawing. Note that resizing columns / rows after
// using this function will effect the position of the drawing
//
// Parameters:
// Row:
// Start row
//
// RowOffsetPixels:
// Offset in pixels
//
// Column:
// Start Column
//
// ColumnOffsetPixels:
// Offset in pixels
public void SetPosition(int Row, int RowOffsetPixels, int Column, int ColumnOffsetPixels);
I would recommend just passing through small values, such as 2, for the RowOffestPixels and ColumnOffsetPixels parameters:
img.SetPosition(pRowIndex, 2, 1, 2);
I found a method called Pixel2MTU(int pixels) on codeproject from a quick google search. The method is as follows:
public int Pixel2MTU(int pixels)
{
int mtus = pixels * 9525;
return mtus;
}
If this is the same method you are using, your images might be at the very far bottom right of your excel document.