I have a MigraDoc pdf I am generating and have had the requirement to have some 'inline' tables, I needed to get two tables on a line so after investigating this I found TextFrames worked perfectly... Until it encounters the end of the page. Turns out TextFrames do not recognize the end of the page:
(TextFrame are the red boxes)
So, I was wondering if anyone else had come up with a work around for this?
I had thought if I could find the position of the 'current location' I could calculate if the text frame is going to be too tall then manually inject a page break - but I can not seem to find the 'current position' - probably because it doesn't exist until post render??
Is there any way of getting the rendered height position of the 'current line' when coding or is there a better/easier way of doing what I am looking for?
I have found a piece of code that returns the current position using a kind of pre-render:
public double GetMigraHeightPosition()
{
MigraDoc.Rendering.DocumentRenderer docRenderer = new DocumentRenderer(this.document);
docRenderer.PrepareDocument();
RenderInfo[] RenderInfos = docRenderer.GetRenderInfoFromPage(docRenderer.FormattedDocument.PageCount);
RenderInfo r = RenderInfos[RenderInfos.Count() - 1];
return r.LayoutInfo.ContentArea.Y + r.LayoutInfo.ContentArea.Height;
}
So I am using this plus the height of the table to check if that is greater than the active page size (pagesize minus margins & header/footers). Seems to do the trick with regards to stopping the tables spilling into the footer... I still seem to be having the problem with the second table in the first row going onto the next page...?? Havent got a clue what is happening there?!
Object positions are determined when a document is being "prepared" before it is being rendered. If you make changes at that stage, you have to prepare the document again.
You can create tables with "invisible" cells. You can create tables within tables, but inner tables will not have pagebreaks within, so make sure each nested table fits on a single page.
See also:
https://stackoverflow.com/a/36304148/162529
Related
I am using MigraDoc to generate a PDF. I want to align some content to the bottom of the last page. This is boilerplate disclaimer information that my company always includes in these types of documents. The goal is to keep the content altogether and always have it appear just above the footer on the last page. If there isn't room on the last page of actual content to keep it together, there should be a page break and the boilerplate content will be on a page by itself.
I would like to do this without measuring the height of the content.
I get the impression that I can use TextFrame to assist with this in some way, but I can't find any documentation on it. The questions and examples I've found online refer to being able to "absolutely position" the TextFrame, but none of the examples seem to do that, exactly.
As a starting point, I am trying to position a paragraph of text in the way I described on an otherwise blank page. Here's my latest experiment:
static void AddBoilerPlate(Section section) {
var sectionWidth = PdfSharp.PageSizeConverter.ToSize(PageSize.Letter).Width - section.Document.DefaultPageSetup.LeftMargin.Point -
section.Document.DefaultPageSetup.RightMargin.Point;
section.AddPageBreak();
var frame = section.AddTextFrame();
frame.RelativeVertical = RelativeVertical.Page;
frame.Width = sectionWidth;
frame.Top = ShapePosition.Bottom;
frame.AddParagraph(DisclaimerText);
}
The text starts just above the footer (the blue line in this screenshot) and then runs over the footer:
Once I get a paragraph of text aligned, then I think I will be able to use a table with one column and one row instead, and then put all of my content in there.
Update: here is a screenshot showing my goal (faked using a page break and SpaceBefore):
Update: here are some related articles that don't quite get me where I'm trying to go:
Centering a table on a page using a TextFrame: http://forum.pdfsharp.net/viewtopic.php?p=753#p753
Seems to answer the same question, but doesn't provide information about how to absolutely position the TextFrame: http://forum.pdfsharp.com/viewtopic.php?f=2&t=587
Keeping table together on a page ("in one piece"). This will be useful once I figure out the issue with aligning relative to the bottom of the page: Keep table in one piece MigraDoc / PDFsharp
Update: Note that adding a height to the TextFrame does make the text align the way I need it to. It's not ideal, because I would rather not hard-code the height. But it does produce the desired effect:
static void AddBoilerPlate(Section section) {
var sectionWidth = PdfSharp.PageSizeConverter.ToSize(PageSize.Letter).Width - section.Document.DefaultPageSetup.LeftMargin.Point -
section.Document.DefaultPageSetup.RightMargin.Point;
section.AddPageBreak();
var frame = section.AddTextFrame();
frame.RelativeVertical = RelativeVertical.Page;
**frame.Height = new Unit(75, UnitType.Millimeter);**
frame.Width = sectionWidth;
frame.Top = ShapePosition.Bottom;
frame.AddParagraph(DisclaimerText);
}
Update: Note, though, that setting the TextFrame height doesn't resolve the ultimate issue. Content added before the boilerplate should push the boilerplate to the next page if there isn't enough space for it, and that's not happening.
Update: After trying it out myself (I've had the same Problem), add an invisible element with the Height you want to have just before the bottom element. In case the invisible Element doesn't fit it will seem that the page break occurred because of the bottom element.
Works like a charm.
(tip I added a border to both elements for testing so I was able to make sure the layout was proper)
Is there a way to set fixed height of an element? Could be a table, row or section. This element is dynamically generated from database and it can have a variable number of rows. I need to do that, because the section below needs to be in a fixed position for print out. I am using WPF v1.31. I know it is not the latest, but it's an addition to a quite old application.
You can set the height of a Paragraph or a table Row.
I think you cannot set the height of a Table - but that will be the sum of the heights of all rows. Automatic page breaks will make things complicated if a table does not fit a single page.
A Section always starts on a new page.
TextFrame can be used to place text at a fixed position. Depending on the requirements, this could be simple or complicated.
You can prepare a document to let MigraDoc determine sizes and positions. Then you code can decide whether the items with the fixed position will be on the same page or on a new page.
Here is sample code that shows how to show the progress while creating a PDF:
http://forum.pdfsharp.net/viewtopic.php?f=8&t=3172
The same technique can be used to stitch several MigraDoc documents together to create a single PDF. If I understand your requirements correctly, this could be the way to go - without setting the height of any elements.
I use a Header in my Migradoc documents. Right now I just add a Paragraph to the Header but it doesnt really fit my needs as I want to have lets say two texts that have a different Alignment. For example:
Middle-ALigned.Text Right-Aligned-Text
second line second line
Before I used two textframes and added them to the section (not as Header). That worked somehow but cant be the right choice as I want to print the text on every page and thats what headers are for.
So the question is how do I align the two texts differently in one paragraph or how do I get two paragraphs to appear on the same height in the header.
I hope someone can help with that.
Cheers
Keep it Simple: Use a Tab Stop
The best way to do this is the same as you would do in most word processing tools: with a right-aligned tab-stop, placed on the right margin of the page, with the "left" text, being center-aligned. This is pretty straight forward, but I couldn't find the "full" solution anywhere, so here's what you need:
// Grab the current section, and other settings
var section = documentWrapper.CurrentSection;
var footer = section.Footers.Primary;
var reportMeta = documentWrapper.AdminReport.ReportMeta;
// Format, then add the report date to the footer
var footerDate = string.Format("{0:MM/dd/yyyy}", reportMeta.ReportDate);
var footerP = footer.AddParagraph(footerDate);
// Add "Page X of Y" on the next tab stop.
footerP.AddTab();
footerP.AddText("Page ");
footerP.AddPageField();
footerP.AddText(" of ");
footerP.AddNumPagesField();
// The tab stop will need to be on the right edge of the page, just inside the margin
// We need to figure out where that is
var tabStopPosition =
documentWrapper.CurrentPageWidth
- section.PageSetup.LeftMargin
- section.PageSetup.RightMargin;
// Clear all existing tab stops, and add our calculated tab stop, on the right
footerP.Format.TabStops.ClearAll();
footerP.Format.TabStops.AddTabStop(tabStopPosition, TabAlignment.Right);
The hardest part of this, is figuring out what your tab stop position should be. Because I'm boring and really like encapsulation, I dynamically calculate the tab stop position, based on the page width, less the horizontal page margins. However, getting the current page width wasn't as easy as I'd thought it'd be, because I'm using PageFormat to set the page dimensions.
Next Challenge: Getting Your Page Width, Dynamically
I really hate having tightly coupled code (think: fan-in and fan-out), so even though I know at this point in time what my page width is, even to the point of hard-coding it, I still want to hard code it in only a single place, then refer to that one place everywhere else.
I keep a custom "has-a"/wrapper class to keep this stuff encapsulated into; That's documentWrapper in my code here. Additionally, I don't expose any of the PDFSharp/MigraDoc types to the rest of my application, so I'm using ReportMeta as a way to communicate settings.
Now for some code. When I setup the section, I'm using the MigraDoc PageFormat to define the size of my page for the current section:
// The tab stop will need to be on the right edge of the page, just inside the margin
// We need to figure out where that is
var tabStopPosition =
documentWrapper.CurrentPageWidth
- section.PageSetup.LeftMargin
- section.PageSetup.RightMargin;
// Clear all existing tab stops, and add our calculated tab stop, on the right
footerP.Format.TabStops.ClearAll();
footerP.Format.TabStops.AddTabStop(tabStopPosition / 2, TabAlignment.Center);
footerP.Format.TabStops.AddTabStop(tabStopPosition, TabAlignment.Right);
footerP.Format.Alignment = ParagraphAlignment.Center;
What's really important here, is that I'm storing the CurrentPageWidth, this becomes really important when setting up our tab stops. The CurrentPageWidth property, is simply a MigraDoc Unit type. I am able to determine what this is by using MigraDoc's PageSetup.GetPageSize with my chosen PageFormat.
Results
You can add TextFrames to the header - then they will appear on every page.
Or use one paragraph as header. Use TabStops to position the text (as in Word there are left-aligned, center-aligned, and right-aligned TabStops. With TabStops you have to deal with the linebreaks like this:
(TabStop)Middle Text(TabStop)RightText(LineBreak)(TabStop)MT Second Line(TabStop)RT Second Line.
See also:
https://stackoverflow.com/a/29250830/1015447
Here's something I've not been able to solve. We've moved over from FONET to Reporting to provide decent PDF exporting and printing. There's no (free) way to print generated PDF's we could find without using an outdated version of Adobe Reader, and we can't install a third party program everywhere.
We are making invoices this way, with a variable numbers of invoice lines (which are variable in height too), and an optional remark. This in a subreport. At the bottom of the last page, we want the totals without VAT, the total VAT and the totals including VAT. When there is no VAT in this invoice, these lines are hidden.
However, there is no way we found to anchor these totals to the bottom of the last page. We are already using the footer to show something on each page, and abusing the header to show the column headers for the invoice lines, as the row headers are not repeated on the next page.
What we've tried:
- We've tried adding a footer to the invoice lines subreport to show this, but this can't contain a Tablix and doesn't show.
- Adding whitespace between the invoice lines and totals only works with a few invoice lines. When more than 4, it places the totals on the beginning of the next page. Removing the whitespace places the totals right after the invoice lines, while we want it at the bottom of the last page.
- We could work around this in FONET, by using an absolute table, but we have not seen this possibility in Reporting.
- We would use another system if it's simply not possible. We need something which supports designing, tables which can be populated with DataSets or IEnumerables, anchoring, embedded images, direct printing and exporting to PDF. Localization (strings based on language using default resource files) support would be a huge plus.
Thanks in advance,
Nick
My answer is a year late, but maybe this will help someone
Had a similar problem while printing an envelope cover with a title at the bottom. I found questions like yours, but no answers. After banging my head for a couple of hours, i've got it.
Keep the controls with variable heights in a rectangle & make the rectangle as lengthy as you wish and keep the other controls (which you wish to print after a variable gap) below the rectangle - Check the image below
Now, if the 'CanGrow' controls inside the rectangle grows, it does so without pushing the controls outside the rectangle. But beware, if the controls grow beyond the size of the rectangle, then the rectangle will grow & push the rest down.
That's it. But Wait!!! I moved this report from VS 2008 to ReportBuilder 2 (& 3) and this did not work. After pulling my hair for another 2 hours - here's the issue
Select the Report (not the body) and set the 'ConsumeContainerWhitespace' property to True for this to work. Whitespaces are consumed both to the right & bottom within a container - I guess this is the default & only behavior in VS 2008 & VS 2005's report runtime.
-I've been developing on the .Net platform for the past 6 years, comfortable with almost everything, but this damned fine reporting component never fails to annoy me!
I generate a HTML-Page from C#. In the HTML-Page there are a lot of elements. All of them have a absolute position.
One of these elements is a table. This table represents a object that keeps a double[]. Every double value is a new cell in a new row.
I iterate over double[] and create my table:
for (int i = 0; i < dbl.Length; i++)
{
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Tr);
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Td);
htmlTextWriter.Write(dbl[i]);
htmlTextWriter.RenderEndTag(); // td
htmlTextWriter.RenderEndTag(); // tr
}
If the table has so much elements, that it cross an element that is below this table, I have to be responsive to this issue.
This means, I need to know how many pixels this table is long.
Of course I do know how many cells I generate and I also know BorderSize, Padding, Margin, etc.
But there are two problems. First although I know FontSize, FontFamily, FontWeight, I do not know how to include these information into a mathematical calculation.
Second I think in every browser the actual size is also different. I created a dummy table and recognized, that in one screen height I already have one cell difference between Opera and Firefox.
So I think in C# I am only able to approximate the actual height?!
The next idea I have is to include a JavaScript into my HTML. I've no experience with JavaScript, but my approach would be to find my tables and read out size. Then iterate over all elements and find all overlappings.
My questions are:
Are my consideration true or do I miss some aspects?
Are my approaches the right way (in C# I only will get an
approximated result?!, JavaScript I do not know if it is really
possible what I want to do)
Are there other possibilities I do not see right now?
Hint: Other script languages than JavaSript are not applicable for my solution. JavaScript I only use if really necessary.
I think javascript is the answer, not c#.
http://api.jquery.com/height/
You cannot calculate the height in C#. Please use javascript to do this.