ABCpdf, render an HTML within a "template": How to add margin? - c#

I'm trying to render an HTML within a predefined PDF-template (e.g. within a frame.) The template/frame should reach the edges. But the HTML shouldn't do that. So I need some kind of margin for the HTML only. Here is my code so far:
var doc = new Doc();
doc.MediaBox.String = "A4";
doc.Rect.String = doc.MediaBox.String;
var id = doc.AddImageUrl(url.ToString());
doc.AddImageDoc("template.pdf", 1, doc.MediaBox);
while (doc.Chainable(id))
{
doc.Page = doc.AddPage();
id = doc.AddImageToChain(id);
doc.AddImageDoc("template.pdf", 1, doc.MediaBox);
}
for (var i = 1; i <= doc.PageCount; i++)
{
doc.PageNumber = i;
doc.Flatten();
}
I see, that there is a possibility to pass a Rect to #AddImageDoc. But I don't have this option for #AddImageUrl.

Here is how I could solve the problem:
First, I set the position and margins of the doc.Rect:
doc.Rect.Position(15, 15);
doc.Rect.Width = pageWidth - 2*15;
doc.Rect.Height = pageHeight - 2*15;
Then I filled the doc with the images from the parsed URL:
var id = doc.AddImageUrl(url.ToString());
while (doc.Chainable(id))
{
doc.Page = doc.AddPage();
id = doc.AddImageToChain(id);
}
After this, I reset the doc.Rect to the size of the actual paper (in ma case: A4):
doc.Rect.String = "A4";
Now I can loop over all pages and add the template to them:
for (var i = 1; i <= doc.PageCount; i++)
{
doc.PageNumber = i;
doc.AddImageDoc(template, 1, doc.Rect);
doc.Flatten();
}

Related

Create borderless table in iText 7

I am porting an application from iTextSharp 5 to iText 7. In the original application I added page numbers using a stamper that added a table to the top of the page. I have almost succeeded but I am unable to create a table with only a bottom border (internal, left, right and top is borderless. I tried the example from the iText building blocks book and have looked at itext 7 table borderless
But I still get top, internal and side borders. Here is my code:
private Table makepagetable(string doctitle, int curpage, int totalpage)
{
PdfFont font = PdfFontFactory.CreateFont(iText.IO.Font.Constants.StandardFonts.TIMES_ROMAN);
Text title = new Text(doctitle).SetFontSize(11f).SetFont(font);
Text pages = new Text("page " + curpage.ToString() + " of " + totalpage.ToString()).SetFont(font);
Paragraph p1 = new Paragraph(title).SetTextAlignment(TextAlignment.LEFT);
Paragraph p2 = new Paragraph(pages).SetTextAlignment(TextAlignment.RIGHT);
Table mytable = new Table(new float[] { 3, 1 })
.SetBorderTop(Border.NO_BORDER)
.SetBorderRight(Border.NO_BORDER)
.SetBorderLeft(Border.NO_BORDER)
.SetBorderBottom(new SolidBorder(2));
float pval = 500;
mytable.SetWidth(pval);
mytable.AddCell(new Cell().Add(p1)).SetBorder(Border.NO_BORDER);
mytable.AddCell(new Cell().Add(p2)).SetBorder(Border.NO_BORDER);
// SetBorderTop(Border.NO_BORDER).SetBorderLeft(Border.NO_BORDER).SetBorderRight(Border.NO_BORDER);
return mytable;
}
I have also tried mytable.AddCell(new Cell().Add(p2)).SetBorderTop(Border.NO_BORDER).SetBorderLeft(Border.NO_BORDER).SetBorderRight(Border.NO_BORDER); and various similar combinations on the table object, but I still get the borders.
For completion here is the routine that calls this function
public Byte[] AddPageNumbers(Byte[] firstpass, string doctitle) {
//firstpass = array of the original pdfdocument
// doctitle = text on left of table
int i;
float x = 30;
float y = 810;
MemoryStream ms2 = new MemoryStream(firstpass);
PdfReader reader = new PdfReader(ms2);
MemoryStream ms3 = new MemoryStream();
PdfDocument pdfDoc = new PdfDocument(reader, new PdfWriter(ms3));
Document document = new Document(pdfDoc, PageSize.A4, false);
int n = pdfDoc.GetNumberOfPages();
for (i = 3; i <= n; i++)
{
Paragraph header;
//PdfPage page = pdfDoc.GetPage(i);
//header = new Paragraph(String.Format(doctitle + " Page {0} of {1}", i, n));
Table table;
PdfPage page = pdfDoc.GetPage(i);
table = makepagetable(doctitle, i, n);
header = new Paragraph().Add(table);
document.ShowTextAligned(header,x,y, i, TextAlignment.JUSTIFIED, VerticalAlignment.MIDDLE, 0);
}
document.Close();
pdfDoc.Close();
return ms3.ToArray();
}
Borderless Table or Cell, for c# and iText7, you can use:
//Table with no border
table.SetBorder(Border.NO_BORDER);
//Cell with no border
cell.SetBorder(Border.NO_BORDER);
I review your code:
//your source code
mytable.AddCell(new Cell().Add(p1)).SetBorder(Border.NO_BORDER);
//changed code
mytable.AddCell(new Cell().Add(p1).SetBorder(Border.NO_BORDER));
Hope this is helpful.

c# itextsharp, locate words not chunks in page with their location for adding sticky notes

I already read all related StackOverflow and haven't find a decent solution to this. I want to open a PDF, get the text (words) and their coordinates then further, add a sticky note to some of them.
Seems to be mission impossible, I'm stucked.
How come this code will correctly find all words in a page (but not their coordinates)?
using (PdfReader reader = new PdfReader(path))
{
StringBuilder sb = new StringBuilder();
ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
for (int page = 5; page <= 5; page++)
{
string text = PdfTextExtractor.GetTextFromPage(reader, page, strategy);
Console.WriteLine(text);
}
//txt = sb.ToString();
}
But this one gets coordinates, but for "chunks" that cannot rely they are in proper order.
PdfReader reader = new PdfReader(path);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
LocationTextExtractionStrategyEx strategy;
for (int i = 5; i <= 5; i++) // reader.NumberOfPages
{
//strategy = parser.ProcessContent(i, new SimpleTextExtractionStrategy());
// new MyLocationTextExtractionStrategy("sample", System.Globalization.CompareOptions.None)
strategy = parser.ProcessContent(i, new LocationTextExtractionStrategyEx("MCU_MOSI", 0));
foreach (LocationTextExtractionStrategyEx.ExtendedTextChunk chunk in strategy.m_DocChunks)
{
if (chunk.m_text.Trim() == "MCU_MOSI")
Console.WriteLine("Bingo"); // <-- NEVER HIT
}
//Console.WriteLine(strategy.m_SearchResultsList.ToString()); // strategy.GetResultantText() +
}
This uses a class from this post (little modified by me)
Getting Coordinates of string using ITextExtractionStrategy and LocationTextExtractionStrategy in Itextsharp
But only finds useless "chunks".
So the question is can with iTextSharp really locate words in page so I can add some sticky notes nearby? Thank you.
It looks like the chunk.m_text only contains one letter at a time which is why it this will never be true:
if (chunk.m_text.Trim() == "MCU_MOSI")
What you could do instead is have each chunk text added to a string and see if it contains your text.
PdfReader reader = new PdfReader(path);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
LocationTextExtractionStrategyEx strategy;
string str = string.Empty;
for (int i = 5; i <= 5; i++) // reader.NumberOfPages
{
strategy = parser.ProcessContent(i, new LocationTextExtractionStrategyEx("MCU_MOSI", 0));
var x = strategy.m_SearchResultsList;
foreach (LocationTextExtractionStrategyEx.ExtendedTextChunk chunk in strategy.m_DocChunks)
{
str += chunk.m_text;
if (str.Contains("MCU_MOSI"))
{
str = string.Empty;
Vector location = chunk.m_endLocation;
Console.WriteLine("Bingo");
}
}
}
Note for the example of the location, I made m_endLocation public.

C# Array strings in a Group of Radio Buttons

My code is C# windows form
I have a file with data:
David One And Two/Three
Alex One Two Four And Five/Six
Amanda Two Seven/Ten
Micheal Seven/Nine
and trying to have them in array like
string[] Names = File.ReadAllLines("C:\Students\Name.txt", Encoding.Default);
and have them in Group of Radio Buttons
RadioButton[] n = new RadioButton[Names.Length];
for (int i = 0; i < Names.Length; i++)
{
n[i] = new RadioButton();
n[i].Text = Names[i];
n[i].Location = new Point(10, 10 + i * 20);
groupBox1.Controls.Add(n[i]);
}
But it shows as my attached image
I tried without Encoding.Default and Encoding.UTF8 but the same problem.
What is I'am doing wrong? Please see my image and help me. Thank you in advance!
I'm guessing your file contains empty lines at the end. You can try to remove them before creating a radio button:
for (int i = 0; i < Names.Length; i++)
{
var name = Names[i];
if(name.Trim() == string.Empty) continue;
n[i] = new RadioButton();
n[i].Text = Names[i];
n[i].Location = new Point(10, 10 + i * 20);
groupBox1.Controls.Add(n[i]);
}
If your text is trimmed you can try to increase the width of the control:
n[i].Width = 300; // Put a value which will show the entire text

How to Merge items within a List<> collection C#

I have a implememtation where i need to loop through a collection of documents and based on certain condition merge the documents .
The merge condition is very simple, if present document's doctype is same as later document's doctype, then copy all the pages from the later doctype and append it to the pages of present document's and remove the later document from the collection.
Note : Both response.documents and response.documents[].pages are List<> collections.
I was trying this but was getting following exception Once I remove the document.
collection was modified enumeration may not execute
Here is the code:
int docindex = 0;
foreach( var document in response.documents)
{
string presentDoctype = string.Empty;
string laterDoctype = string.Empty;
presentDoctype = response.documents[docindex].doctype;
laterDoctype = response.documents[docindex + 1].doctype;
if (laterDoctype == presentDoctype)
{
response.documents[docindex].pages.AddRange(response.documents[docindex + 1].pages);
response.documents.RemoveAt(docindex + 1);
}
docindex = docindex + 1;
}
Ex:
reponse.documents[0].doctype = "BankStatement" //page count = 1
reponse.documents[1].doctype = "BankStatement" //page count = 2
reponse.documents[2].doctype = "BankStatement" //page count = 2
reponse.documents[3].doctype = "BankStatement" //page count = 1
reponse.documents[4].doctype = "BankStatement" //page count = 4
Expected result:
response.documents[0].doctype = "BankStatement" //page count = 10
Please suggest.Appreciate your help.
I would recommend you to look at LINQ GroupBy and Distinct to process your response.documents
Example (as I cannot use your class, I give example using my own defined class):
Suppose you have DummyClass
public class DummyClass {
public int DummyInt;
public string DummyString;
public double DummyDouble;
public DummyClass() {
}
public DummyClass(int dummyInt, string dummyString, double dummyDouble) {
DummyInt = dummyInt;
DummyString = dummyString;
DummyDouble = dummyDouble;
}
}
Then doing GroupBy as shown,
DummyClass dc1 = new DummyClass(1, "This dummy", 2.0);
DummyClass dc2 = new DummyClass(2, "That dummy", 2.0);
DummyClass dc3 = new DummyClass(1, "These dummies", 2.0);
DummyClass dc4 = new DummyClass(2, "Those dummies", 2.0);
DummyClass dc5 = new DummyClass(3, "The dummies", 2.0);
List<DummyClass> dummyList = new List<DummyClass>() { dc1, dc2, dc3, dc4, dc5 };
var groupedDummy = dummyList.GroupBy(x => x.DummyInt).ToList();
Will create three groups, marked by DummyInt
Then to process the group you could do
for (int i = 0; i < groupedDummy.Count; ++i){
foreach (DummyClass dummy in groupedDummy[i]) { //this will process the (i-1)-th group
//do something on this group
//groupedDummy[0] will consists of "this" and "these", [1] "that" and "those", while [2] "the"
//Try it out!
}
}
In your case, you should create group based on doctype.
Once you create groups based on your doctype, everything else would be pretty "natural" for you to continue.
Another LINQ method which you might be interested in would be Distinct. But I think for this case, GroupBy would be the primary method you would like to use.
Use only "for loop" instead of "foreach".
foreach will hold the collection and cannot be modified while looping thru it.
Here is an example using groupBy, hope this help.
//mock a collection
ICollection<string> collection1 = new List<string>();
for (int i = 0; i < 10; i++)
{
collection1.Add("BankStatement");
}
for (int i = 0; i < 5; i++)
{
collection1.Add("BankStatement2");
}
for (int i = 0; i < 4; i++)
{
collection1.Add("BankStatement3");
}
//merge and get count
var result = collection1.GroupBy(c => c).Select(c => new { name = c.First(), count = c.Count().ToString() }).ToList();
foreach (var item in result)
{
Console.WriteLine(item.name + ": " + item.count);
}
Just use AddRange()
response.documents[0].pages.AddRange(response.documents[1].pages);
it will merge all pages of document[1] with the document[0] into document[0]

EPPlus: ExcelChartSerie.Header loads once but doesn't update when cell is changed

I am trying to create an EPPlus chart. I am having problems with series headers. Even when ExcelChartSerie.HeaderAddress is used, it seems to have no effect.
Each serie is initialized with the following piece of code
ExcelBarChartSerie serie = (OfficeOpenXml.Drawing.Chart.ExcelBarChartSerie)
chartClustered.Series.Add(ExcelRange.GetAddress(fromRow, fromCol, toRow, toCol),
ExcelRange.GetAddress(fromRow, fromColH, toRow, fromColH));
ExcelAddressBase headerAddr = new ExcelAddressBase(headRow, headCol, headRow, headCol);
serie.HeaderAddress = headerAddr;
serie.Header = (string)ws.Cells[headRow, headCol].Value;
Everything works great but I am having problems with the Header updating correctly with the rest of the graph. The serie.Header variable controls the legend, which is where I'm having the problem. I understand this is an awkwardly specific question, but perhaps someone out there can offer some insight. Here is some pictures to show you exactly where I am having my problem.
Initial plot before a change (correct):
Plot after label data changed (incorrect):
The problem is the you are creating a new Excel Address that is NOT attached to a worksheet when setting HeaderAddress. Its a very subtle but important difference because Excel would not know which sheet the address is actually associated with when looking for the header value (it would not assume the one the chart is on). Take a look at this:
[TestMethod]
public void ExcelChartSerie_Header()
{
//http://stackoverflow.com/questions/27866521/epplus-excelchartserie-header-loads-once-but-doesnt-update-when-cell-is-change
var existingFile = new FileInfo(#"c:\temp\temp.xlsx");
if (existingFile.Exists)
existingFile.Delete();
using (var package = new ExcelPackage(existingFile))
{
var workbook = package.Workbook;
var ws = workbook.Worksheets.Add("newsheet");
//Some data
ws.Cells["A1"].Value = "Stuff";
ws.Cells["A2"].Value = "bar1";ws.Cells["A3"].Value = "bar2";ws.Cells["A4"].Value = "bar3";ws.Cells["A5"].Value = "bar4";ws.Cells["A6"].Value = "bar5";
ws.Cells["C1"].Value = "Canadian";
ws.Cells["C2"].Value = 53;ws.Cells["C3"].Value = 36;ws.Cells["C4"].Value = 43;ws.Cells["C5"].Value = 86;ws.Cells["C6"].Value = 86;
ws.Cells["D1"].Value = "Saudi Arabia";
ws.Cells["D2"].Value = 53;ws.Cells["D3"].Value = 36;ws.Cells["D4"].Value = 43;ws.Cells["D5"].Value = 86;ws.Cells["D6"].Value = 86;
ws.Cells["E1"].Value = "Alaskan";
ws.Cells["E2"].Value = 53; ws.Cells["E3"].Value = 36; ws.Cells["E4"].Value = 43; ws.Cells["E5"].Value = 86; ws.Cells["E6"].Value = 86;
ws.Cells["F1"].Value = "Indian";
ws.Cells["F2"].Value = 53; ws.Cells["F3"].Value = 36; ws.Cells["F4"].Value = 43; ws.Cells["F5"].Value = 86; ws.Cells["F6"].Value = 86;
//Create the chart
var chart = (ExcelBarChart)ws.Drawings.AddChart("Chart1", eChartType.ColumnClustered);
chart.SetSize(400, 300);
chart.SetPosition(10, 400);
//Apply header
for (var i = 0; i < 4; i++)
{
var serie = (ExcelBarChartSerie) chart.Series.Add(
ExcelCellBase.GetAddress(2, i + 3, 6, i + 3),
ExcelCellBase.GetAddress(2, 1, 6, 1)
);
//var headerAddr = new ExcelAddressBase("C1"); //THIS IS NOT ASSOCIATED WITH WORKSHET 'ws'
var headerAddr = ws.Cells[1, i + 3]; //THIS IS
serie.HeaderAddress = headerAddr;
//serie.Header = (string) ws.Cells[1, i + 3].Value;
}
package.Save();
}
}

Categories

Resources