Bookmark to specific page using iTextSharp 4.1.6 - c#

I want to add a bookmark pointing to a specific page within a document. Adding bookmarks from other PDF files I'm merging with code similar to that below works fine, but when I copied it to add custom bookmarks to non-bookmarked items it fails:
var bookmarks = new ArrayList();
var writer = new PdfCopy(document, memorystream);
// ...
var uni = new Hashtable();
uni.Add("Action", "GoTo");
uni.Add("Title", "Awesome Unicorn pic");
uni.Add("Page", "8 XYZ 0 0 0");
bookmarks.Add(uni);
// ...
writer.Outlines = bookmarks;
But apparently ("Page", "8 XYZ 0 0 0") does not reference Page 8 but rather Section 8 or something like that. Is there an alternative Action I could use to point to an arbitrary page? Or some other method?

Looks like the PDF coordinate system messed with my feeble human brain. Turns out that ("Page", "8 XYZ 0 0 0"); actually does reference page 8, but "XYZ 0 0 0" does not reference the top left point on a page, but rather the bottom left point. So when clicked, a bookmark like this unexpectedly takes you to page two. Awesome.
The code below works as expected, because it gets the height of the first page and uses that to link to the top of the page. The code is gathered from different places around my source, so it's not very "together" but still, it works.
var bookmarks = new ArrayList();
var rdr = new PdfReader(first);
var doc = new Document(rdr.GetPageSizeWithRotation(1));
var wri = new PdfCopy(doc, memorystream);
var temp = wri.GetImportedPage(rdr, 1); // get 1st page
var h = temp.Height; // get height of 1st page
// Add first item to bookmarks.
var test = new Hashtable();
test.Add("Action", "GoTo");
test.Add("Title", "Page1 0 H 0");
test.Add("Page", "1 XYZ 0 "+h+" 0"); // use height of 1st page
bookmarks.Add(test);
// Do your worst and afterwards set the bookmarks to Outline. So yeah.
wri.Outlines = bookmarks;

Related

How to fill remaining space of a line in iText7

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:

Calculate new ScrollPercent - after ViewSize changed

First of all, basically I believe I just need help in figuring out how to calculate it, math isnt really my strong side.
FYI, the MS-Word is most likely not relevant to solve my problem, I basically just mention it so you know the context I am in, but I believe it should be solvable also for people who have no knowledge about MS-Word.
I have the problem that Word has the property VerticalPercentScrolled which is an integer.
Which isnt as accurate as I need it to be, consider a Word Document with 300 pages and the scrollbar can only have a integer between 0 - 100 (percent), so for example 25 percent of a 300 page document is a rather big range.
I have now gone and read the correct value, with the help of UI Automation library - TestStack.White - and can successfully set it to its old value, if some un-desired scrolling happens.
Like this:
var hWnd = (IntPtr)WordUtils.GetHwnd();
var processId = Process.GetCurrentProcess().Id;
var uiApp = TestStack.White.Application.Attach((int)processId);
var uiWindow = uiApp.GetWindow(WindowHandling.GetWindowText(hWnd), InitializeOption.NoCache);
var wf = (IUIItemContainer)uiWindow.Get(SearchCriteria.ByClassName("_WwF"));
var wbs= wf.GetMultiple(SearchCriteria.ByClassName("_WwB"));
if (wbs.Length != 1)
return;
var wb= (IUIItemContainer)wbs.First();
var wgs = wb.GetMultiple(SearchCriteria.ByClassName("_WwG"));
if (wgs.Length != 1)
return;
var wg = wgs.First();
var element = wg.AutomationElement;
var oldVerticalPercent = (double)element.GetCurrentPropertyValue(ScrollPattern.VerticalScrollPercentProperty);
with this code I get a percent value of lets say 9.442248572683356 instead of 9.
To reset the value I use the following code:
object scrollPattern;
if (element.TryGetCurrentPattern(ScrollPattern.Pattern, out scrollPattern))
((ScrollPattern)scrollPattern).SetScrollPercent(ScrollPattern.NoScroll, oldVerticalPercent);
This works like a charm.
Now my problem is what if my document gets larger/smaller during the time I stored the oldValue and want to reapply it, my oldValue needs to be adjusted.
I can read the following values (at least those I found so far):
ScrollPattern.VerticalScrollPercentProperty
ScrollPattern.VerticalViewSizeProperty
I can also go and look for the scrollbar it self and read the following values:
RangeValuePattern.LargeChangeProperty
RangeValuePattern.MaximumProperty
RangeValuePattern.MinimumProperty
RangeValuePattern.SmallChangeProperty
RangeValuePattern.ValueProperty
To look for the scrollbar, I use the following code:
var hWnd = (IntPtr)WordUtils.GetHwnd();
var processId = Process.GetCurrentProcess().Id;
var uiApp = Ts.Application.Attach((int)processId);
var uiWindow = uiApp.GetWindow(WindowHandling.GetWindowText(hWnd), InitializeOption.NoCache);
var wf = (IUIItemContainer)uiWindow.Get(SearchCriteria.ByClassName("_WwF"));
var wbs = wf.GetMultiple(SearchCriteria.ByClassName("_WwB"));
if (wbs.Length != 1)
return;
var wb = (IUIItemContainer)wbs.First();
var wgs= wb.GetMultiple(SearchCriteria.ByClassName("_WwG"));
if (wgs.Length != 1)
return;
var nUiScrollBars = wgs.GetMultiple(SearchCriteria.ByClassName("NUIScrollbar"));
if (scrollBar.Length != 1)
return;
var nUiScrollBar = (IUIItemContainer)nUiScrollBars.First();
var nUiHwndElements = nUiScrollBar.GetMultiple(SearchCriteria.ByClassName("NetUIHWNDElement"));
if (nUiHwndElements.Length != 1)
return;
var nUiHwndElement = (IUIItemContainer)nUiHwndElements.First();
var netUiScrollBar = nUiHwndElement.GetElement(SearchCriteria.ByClassName("NetUIScrollBar"));
var scrollBarValue = (double)netUiScrollBar.GetCurrentPropertyValue(RangeValuePattern.ValueProperty);
You may ask yourself, why I dont set the RangeValuePattern.ValueProperty when I am able to access it, the problem there is that Word doesnt update the document when I change this property, the scroll thumb does move but the document doesnt move an inch.
I have to set the ScrollPattern.VerticalScrollPercentProperty for it to work.
So my question, is how do I calculate the new ScrollPattern.VerticalScrollPercentProperty based on the oldValue, where the document could shrink or grow in size in between?
Edit:
Here is a testscenario:
scrollPercent 64.86486486486487
scrollViewSize 74.394463667820062
scrollBarLargeChange 37197
scrollBarMaximum 12803
scrollBarMinimum 0
scrollBarSmallChange 1
scrollBarValue 8304
After inserting 5 new pages
scrollPercent 87.890366182251867 <-- undesired scrolling occured (see desired values below)
scrollViewSize 9.442248572683356
scrollBarLargeChange 4721
scrollBarMaximum 45279
scrollBarMinimum 0
scrollBarSmallChange 1
scrollBarValue 39795 <-- undesired scrolling occured (see desired values below)
And as said, I need to adjust the scrollPercent - after inserting those pages - so that it is again on the old position.
I can tell you that in this testscenario, I would need the new value to be
scrollPercent 2.3278413357480452
after changing it to the desired percentValue the scrollBarValue is the only value getting updated and it is than at
scrollBarValue 1054
So, it looks like your scrollPercent is calculated like this:
scrollPercent = 100.0 * scrollBarValue / (scrollBarMaximum - 1);
So the math should work out like this (assuming floating-point division):
scrollPercentOld = 100.0 * scrollBarValueOld / (scrollBarMaximumOld - 1)
scrollPercentNew = 100.0 * scrollBarValueOld / (scrollBarMaximumNew - 1)
From that, you have:
scrollBarValueOld = scrollPercentOld * (scrollBarMaximumOld - 1) / 100.0
scrollBarValueOld = scrollPercentNew * (scrollBarMaximumNew - 1) / 100.0
When you equate the two, you get:
scrollPercentOld * (scrollBarMaximumOld - 1) = scrollPercentNew * (scrollBarMaximumNew - 1)
And then finally:
scrollPercentNew = scrollPercentOld * (scrollBarMaximumOld - 1) / (scrollBarMaximumNew - 1)

Bold a single word within a sentence with iTextSharp

Is it possible to bold a single word within a sentence with iTextSharp? I am trying to bold several individual words without having to break the string into individual phrases.
I want to this type of out put
Eg:REASON(S) FOR CANCELLATION: See Statutory reason(s) designated by Code No(s) 1 on the reverse side hereof.
My actual output is below
Eg:REASON(S) FOR CANCELLATION: See Statutory reason(s) designated by Code No(s) 1 on the reverse side hereof.
Code
pdftb4 = new PdfPTable(1);
pdftb4.WidthPercentage = 100;
width = new float[1];
width[0] = 0.7F;
pdftb4.SetWidths(width);
pdfcel4 = new PdfPCell(new Phrase("\n REASON(S) FOR CANCELLATION: See Statutoryreason(s) designated by Code No(s) 1 on the reverse side hereof", docBlackFont10));
pdfcel4.Border = 0;
pdfcel4.HorizontalAlignment = Element.ALIGN_LEFT;
pdftb4.AddCell(pdfcel4);
objDocument.Add(pdftb4);
somebody please help me
The way to accomplish what you are trying is with Chunks. A simple example is:
var normalFont = FontFactory.GetFont(FontFactory.HELVETICA, 12);
var boldFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 12);
var phrase = new Phrase();
phrase.Add(new Chunk("REASON(S) FOR CANCELLATION:", boldFont));
phrase.Add(new Chunk(" See Statutoryreason(s) designated by Code No(s) 1 on the reverse side hereof", normalFont));
Can also create font like
Font verdanaBold = FontFactory.GetFont("Verdana", 7f, Font.BOLD);
Maybe this link Bolding with Rich Text Values in iTextSharp will help?
Not sure if it fits your scenario completely but might get you where you need to go.

M-dialog - show number of char that are left when the user typing?

Only in the System.Console do I get the result, it print the number of characters that are left, and it update it
So whats wrong, how do i update it in the Element?
Hope you guys can help me with this.
Console:
Characters typed(left): in Value it should write the number of characters that are left.
var root = new RootElement ("Send Message");
var messageElement = new MultilineEntryElement ("", "0123456789")
{
Editable = true,
Height = 120
};
var messageSection = new Section ();
int leangtOfChar = 200 - messageElement.Value.Length;
var lengthElement = new StringElement ("characters typed:", leangtOfChar.ToString());
messageElement.Changed += delegate {
System.Console.WriteLine (leangtOfChar.ToString ());
//lengthElement.Value = (leangtOfChar.ToString());
};
root.Add(messageSection);
The problem is that when you update a normal StringElement it doesn't automatically update the attached cell.
To force an update for just that cell, I would recommend either:
calling root.Reload(lengthElement, UITableViewRowAnimation.Fade) after you change the value
or using a custom element/cell type instead of StringElement - basically you could model this on the simple code in BooleanElement - but use a UILabel as the accessory instead of a UISwitch.
If you wanted to go further - to actually allow StringElement to be updated without a reload - then you can do this by modifying the Element class to track whether a cell instance is currently attached. I've done exactly that in a databinding branch of monotouch.dialog - https://github.com/slodge/MvvmCross/blob/master/Cirrious/Cirrious.MvvmCross.Dialog/Dialog/Elements/Element.cs - but this is almost certainly overkill for what you are trying to do right now.

Generate table of content using itextsharp

What I am doing is to generate a pdf booklet from database. I need go generate a content table with page numbers. E.g there are two chapters with page number like:
=============================
Content table
Chapter 1 ----- 3
Chapter 2 ----- 17
=============================
The text "Chapter 1 ----- " is normal paragraph. But the page number "3" has to be produced using PdfTemplate because it can only be known later. But the pdfTemplate is absolutely positioned. How can I know where to position the PdfTemplate? Am I right on this ? How could I figure this out or should I use other methods?
I've extracted a bit of code to get you on your way.. This code allows you to place text anywhere on a page using an x and y. You may actually want to use iTextSharp's built in paragraph and margin support, but this will be useful, just needs converting to C#
Dim stamper As PdfStamper
Dim templateReader As PdfReader = New PdfReader(yourFileName)
Dim currentPage As PdfImportedPage = stamper.GetImportedPage(templateReader, 1)
stamper.InsertPage(1, PageSize.A4)
Dim cb As PdfContentByte = stamper.GetOverContent(1)
cb.AddTemplate(currentPage, 0, 0)
Look this next bit with each element you want to add..
cb.BeginText()
cb.SetFontAndSize(bf, 12)
cb.SetColorFill(color) 'create a color object to represent the colour you want
cb.ShowTextAligned(1, "Content Table", x, y, 0) 'pass in the x & y of the element
cb.EndText()

Categories