iText(Sharp) - how to avoid creating a blank page? - itext

I'm generating a PDF document using iTextSharp version 5.5.7, using their "streaming" mode - by which I mean I'm not specifying the location of every piece of text, I'm just adding Paragraphs to the Document and letting iTextSharp figure out where to draw them. The text I'm outputting is the result of a report generator, so it is different every time.
The problem I'm running into is this: imagine that, given the page size and the selected font, I can fit 40 lines of text on a page. I output 40 Paragraphs, then I output a blank Paragraph (contents=" "), then an image that fills an entire page. iTextSharp does exactly what I tell it - I end up with one page full of text, a blank page, and then a page containing my image.
But now my document looks funny - there is this unexpectedly blank page in the middle of everything.
I can't just say "don't output any blank lines" because of course that blank line might show up after only 20 lines of text, in which case it has to be there. I need some way to either tell iTextSharp "include this paragraph only if it's not the only thing on a page" or else somehow detect that the page is blank in OnEndPage() and suppress its output (without screwing up my page numbers).
Any suggestions on how I can do this?
ADDED LATER
Output from the report generator:
<html>
<p>Information header</p>
<p>Detail</p>
<p>Detail</p>
<p>Detail</p>
<p></p> <!-- Blank line inserted by report generator for clarity -->
<p>Information header</p>
<p>Detail</p>
<p>Detail</p>
<p>Detail</p>
...
<p>Detail</p> <!-- just by random happenstance this is the last line that will fit on the first page -->
<p></p> <!-- This line happens to be blank, I have no control over it -->
<img src="blah blah"></image>
My (pseudo) code:
foreach (HtmlNode node in htmlFromReportGenerator)
{
if (node is text)
pdfDoc.Add(new Paragraph(node.text));
else if (node is image)
pdfDoc.Add(new Image(node.image));
}

Following Bruno's suggestion, my (pseudo)code now looks like this:
Paragraph lastParagraph = null;
foreach (HtmlNode node in htmlFromReportGenerator)
{
if (node is text)
{
Paragraph parg = new Paragraph(node.text);
if ( (lastParagraph != null) && (text.Trim().Length == 0) )
lastParagraph.SpacingAfter += parg.Leading;
else
{
pdfDoc.Add(parg);
lastParagraph = parg;
}
}
else if (node is image)
{
pdfDoc.Add(new Image(node.image));
lastParagraph = null;
}
}

Related

How can I add text to a document footer using OpenXML?

I have a document template that I want to append some text to the footer. I've seen ways of deleting the footer to replace it, and ways to replace text in the footer, but I want to keep the footer as is from the template and just add to it. I have code to add text to the main doc., but unfortunately, main doc. parts are not setup the same way as footer parts. This is easily accomplished in Interop by a range.InsertAfter(text), but end users need this to work sans Word.
FooterPart footer = _doc.MainDocumentPart.FooterParts.ElementAtOrDefault(0);
string rid = _doc.MainDocumentPart.GetIdOfPart(footer);
footer = _doc.MainDocumentPart.AddNewPart<FooterPart>(rid);
Paragraph para = footer.AddPart(new Paragraph(), rid);
Run run = para.AppendChild(new Run());
// get the last footer of the document
FooterPart footerPart = _doc.MainDocumentPart.FooterParts.LastOrDefault();
// create your paragraph. i created simple, but you will need to add style properties to the run or text class
Paragraph pr = new Paragraph(new Run(new Text("hello")));
// Insert the Paragraph to the end of the footer in footerPart.Footer
footerPart.Footer.AppendChild(pr);
Other way as you said would be putting a text to replace like "txttoreplace" and then you will find it and replace
IEnumerable<FooterPart> footerPartsList = Documento.MainDocumentPart.FooterParts.ToList();
foreach (FooterPart hp in footerPartsList)
foreach (Text text in hp.RootElement.Descendants<Text>())
{
if (text.Text.Contains("txttoreplace"))
{
text.Text = text.Text.Replace("txttoreplace", "new text");
}
}
And another way and the hardest would be, you inser the whole footer, with open xml productivity tools you get the c# code of the footer.xml, and then you delete the footer of the document and you insert.

TYPO3: Trying to add link to images

On our site, other admins add images via the "Resources" tab of the main page. These images are displayed as Banners in a Slider on the main page. However, now they want the ability to add links to specific images.
My first thought on this (after receiving some help on making a loop for images to be added to the page) was to perhaps let them be able to add the link to either the "Title" or "Caption" spot I saw there. And later, on the slider "create" function, pull the said data from the image and make <a> wrap around the image before the slider finished building. I've already tested the slider plugin with this functionality, and that would work fine, however, I can't seem to pull anything from the "Title" or "Caption" and add it to the image in any way.
My other thought would be, is there a way to extend the back end to give them an actualy spot to paste links on images so that I may pull that and wrap the image via the typoscript, or can i pull from caption and wrap image in <a> "if" the link is available.
In other words, does typoscript have a type of "if" statement? What I ahve so far, thanks to maholtz is as follows:
#BANNER IMAGES LOOP BEGIN
page.10.marks.topimage = TEXT
page.10.marks.topimage {
# retrieve data
data = levelmedia: -1, "slide"
override.field = media
# we have some filenames in a list, let us split the list
# and create images one by one
# if there are five images selected, the CARRAY "1" will be executed
# five times where current is loaded with only one filename
split {
# the images are separated via ","
token = ,
# you can do funny stuff with options split, f.e. if you want to give first
# and last image a different class... but thats another topic;)
# we just say, render every splitted object via CARRAY "1"
cObjNum = 1
1 {
# just render the single image,
# now there should be one filename in current only
10 = IMAGE
10 {
file.import.wrap = fileadmin/user_upload/|
file.import.current = 1
border = 0
file.height = 670
file.width = 1800
altText = Banner
titleText = Banner
# attempt to add link to image if available
caption.1.typolink.parameter.field = image_link
caption.1.typolink.parameter.listNum.stdWrap.data = register:IMAGE_NUM_CURRENT
}
}
}
wrap = <div id="slides">|</div>
}
#BANNER IMAGES LOOP END
I was thinking perhaps I could do something like:
10 {
file.import.wrap = fileadmin/user_upload/|
file.import.current = 1
border = 0
file.height = 670
file.width = 1800
altText = Banner
titleText = Banner
# attempt to add link to image if available
caption.1.typolink.parameter.field = ???
caption.1.typolink.parameter.listNum.stdWrap.data = register:IMAGE_NUM_CURRENT
}
But as you can see, I'm stumped on how that might even work right. Can anyone help point me the right way.
As before mentioned, perhaps I could do ONE of two things:
Pull link from "Title" or "Caption" and add it to the IMAGE Date on output so that I can use that client side to wrap the image in appropriate a tag, |OR|
Pull link from there and use typoscript to wrap the image in a tags
When accessing the ressources via levelmedia = slide you're not directly accessing the FAL table. Therefore you have to load it again to access the fields you want. We solved exactly the problem you have with the following code. Insert it inside your 1 after 10 = IMAGE.
typolink{
parameter{
cObject = RECORDS
cObject{
source.current = 1
tables = sys_file_reference
conf.sys_file_reference = TEXT
conf.sys_file_reference.field = #title or description
}
}
}

Get tag name of start container in range

I'm an absolute newb at this so forgive the simplicity of this question. I have a contenteditable div. All the text in this div are wrapped in link tags.
If the user makes a selection that spans 2 or more of these link nodes, I'd like to identify the name of the link tag at the startContainer and also at the endContainer.
Unfortunately, more often than not, the startContainer node is a formatting node such a paragraph or a bold tag as seen in the example html below.
<div id="myarea" onmouseup="getSelectionHtml();" contenteditable="true">
<a id="1" href=#>text1 <b>text1 text1 </b></a>
<a id="2" href=#>text2 <b>text2 text2 </b></a>
<a id="3" href=#>text3 <b>text3 text3 </b></a>
</div>
So I figure my approach should be to first find the nameTag of the startContainer. If it is not a link tag, then query for it's parent node. If that is not a link tag, query again for the next node up the hierarchy until I find the link tag and can get it's id.
As pitifully short as it is, this is all the code that I have so far. I wish to find tagName of the startContainer, but I'm getting an alert of "undefined". I've been reading as much documentation on the range object as I can but it's all scattered and a little difficult for me to comprehend.
function getSelectionHtml() {
var userSelection;
if (window.getSelection) {
userSelection = window.getSelection();
var selRange = userSelection.getRangeAt(0);
alert(selRange.startContainer.tagName);
}
By the way, if anyone has a better conceptual solution for grabbing the link tag of the beginning and end of a contentEditable selection, I'd be much obliged.
tia
A range's startContainer and endContainer properties may be references to either text nodes or elements. When you're getting an undefined tagName property, it's because you've got a text node.
Here's a simple function for getting hold of the <a> element containing a node:
function getAncestorWithTagName(node, tagName) {
tagName = tagName.toUpperCase();
while (node) {
if (node.nodeType == 1 && node.tagName.toUpperCase() == tagName) {
return node;
}
node = node.parentNode;
}
return null;
}
Example:
var link = getAncestorWithTagName(selRange.startContainer);
if (link) {
alert("Start is inside link with ID " + link.id);
}

Change text in a textbox in Powerpoint slide

I have a Powerpoint presentation which contains 3 slides. Each slide has a Textbox which is a place holder. I would like to replace the Textbox contents on one slide.
I need to know how to do this using C# and OpenXML
Thanks a ton
Do this for each slide, you want to change:
ODP.ShapeTree tree = slide.Slide.CommonSlideData.ShapeTree;
foreach (ODP.Shape shape in tree.Elements<ODP.Shape>())
{
// Run through all the paragraphs in the document
foreach (ODD.Paragraph paragraph in shape.Descendants().OfType<ODD.Paragraph>())
{
foreach (ODD.Run run in paragraph.Elements<ODD.Run>())
{
if (run.Text.InnerText.Contains("PLACEHOLDER"))
{
run.Text = new ODD.Text("Your new text");
}
}
}
}
Keep in mind, that if your template's placeholders contain spaces, this may create two individual run elements. So instead of one run element with run.Text of "Place holder", you might get one run with run.text of "Place" and another one with run.Text "holder".

Weird KRL foreach behavior

I've been getting some odd behavior using a foreach today. I have a dataset that's pulling in a JSON document. Part of it is an array, which I pick() out and send to the foreach. Here's my global block:
global {
dataset appserver <- "http://imaj-app.lddi.org:8010/list/popular" cachable for 1 hour;
popular = appserver.pick("$..images")
}
There's one rule first that sets up the page. It looks like this:
rule setup {
select when web pageview "www\.google\.com"
pre {
imagelist = <<
<div id="462popular" style="margin-left:auto;margin-right:auto;width:450px">
<p>Popular images from the CS 462 Image Project</p>
<span class="image"></span>
</div>
>>;
}
prepend('#footer', imagelist);
}
And here's the rule that's not working:
rule images {
select when web pageview "www\.google\.com"
foreach popular setting (image)
pre {
thumburl = image.pick("$..thumburl");
viewurl = "http://imaj-web.lddi.org/view?imagekey=" + image.pick("$..imagekey");
html = <<
<span class="image"><img src="#{thumburl}" style="border:none"/></span>
>>;
}
after('#462popular .image', html);
}
I get something like this (notice how small the scrollbar thumb is):
Any ideas what's going on here?
You have a recursion problem with your html structure and your after selector to insert new content.
Your selector for inserting new content is
#462popular .image
which means that the contents of html will be inserted after every element with the class of image inside an element with the id of #462popular.
Inside the html that you are inserting you have an element with the class name of image which means you are multiplying the number of elements with the class of image inside #462popular every time you go through the loop.
: )