How can I get my PdfPTable's border and background color to display (iTextSharp)? - pdf-generation

As you can see, I've got a table which sports borders and a background color (Section 1):
...but section 3 lacks these sartorial refinements, and I don't know why.
Here is the pertinent code for section 1 (which displays as it should):
PdfPTable tblHeadings = new PdfPTable(3);
tblHeadings.WidthPercentage = 100;
tblHeadings.SpacingBefore = 10f;
float[] headingRowWidths = new float[] { 550f, 50f, 400f };
tblHeadings.SetWidths(headingRowWidths);
tblHeadings.HorizontalAlignment = Element.ALIGN_LEFT;
Phrase phrasesec1Heading = new Phrase("Section 1: Payment Information", timesRoman9BoldFont);
PdfPCell cellSec1Heading GetCellForBorderedTable(phrasesec1Heading, Element.ALIGN_LEFT, ucscgold);
tblHeadings.AddCell(cellSec1Heading);
. . .
doc.Add(tblHeadings);
...and here for section 3 (which doesn't display as it should):
// Section 3
PdfPTable tblSection3 = new PdfPTable(1);
tblSection3.WidthPercentage = 100;
tblSection3.SpacingBefore = 10f;
//tblSection3.HorizontalAlignment = Element.ALIGN_LEFT;
Chunk sec3PayeeStatus = new Chunk("Section 3: Payee Status", timesRoman9BoldFont);
Chunk requiredFields = new Chunk(" * Required Fields", timesRoman9BoldRedFont);
Paragraph parSection3 = new Paragraph();
parSection3.Add(sec3PayeeStatus);
parSection3.Add(requiredFields);
//Phrase phrasesec1Heading = new Phrase("Section 1: Payment Information", timesRoman9BoldFont);
PdfPCell cellSec3Heading = GetCellForBorderedTable(parSection3, Element.ALIGN_LEFT, ucscgold);
tblSection3.AddCell(cellSec3Heading);
doc.Add(parSection3);
What am I missing or forgetting? The difference in the table creation and setup is in the number of cells/columns, and related declaration (float array) and property (setWidths()). The code adding to the PdfPCell differs in that a Phrase
is used for Section 1 (which displays as I want it to), and a Paragraph is used for section 3 (which doesn't), but I tried that:
// try using a Phrase instead of a Paragraph
Chunk sec3PayeeStatus = new Chunk("Section 3 PayeeStatus",
timesRoman9BoldFont);
Chunk requiredFields = new Chunk(" * Require Fields",
timesRoman9BoldRedFont);
Phrase phraseSection3 = new Phrase();
phraseSection3.Add(sec3PayeeStatus);
phraseSection3.Add(requiredFields);
PdfPCell cellSec3Heading GetCellForBorderedTable(phraseSection3,
Element.ALIGN_LEFT, ucscgold);
tblSection3.AddCell(cellSec3Heading);
doc.Add(phraseSection3);
...but that did no better, in fact was a little worse, because the "SpacingBefore" didn't seem to "take"
Finally (so far), I tried using Phrases instead of Chunks, like so:
Phrase sec3PayeeStatus = new Phrase("Section 3: Payee Status", timesRoman9BoldFont);
Phrase requiredFields = new Phrase(" * Required Fields", timesRoman9BoldRedFont);
Paragraph parSection3 = new Paragraph();
parSection3.Add(sec3PayeeStatus);
parSection3.Add(requiredFields);
PdfPCell cellSec3Heading = GetCellForBorderedTable(parSection3, Element.ALIGN_LEFT, ucscgold);
tblSection3.AddCell(cellSec3Heading);
doc.Add(parSection3);
...but that is no better, worse, or different than the first attempt (at least I get the vertical space back, though, separating sections 2 and 3).
What do I have to do to display the cell borders and background color?
UPDATE
Here's GetCellForBorderedTable():
private static PdfPCell GetCellForBorderedTable(Phrase phrase, int align, BaseColor color)
{
PdfPCell cell = new PdfPCell(phrase);
cell.HorizontalAlignment = align;
cell.PaddingBottom = 2f;
cell.PaddingTop = 0f;
cell.BackgroundColor = color;
cell.VerticalAlignment = PdfPCell.ALIGN_CENTER;
return cell;
}

The problem was (not "visible" to me until I compared the code in a code comparison utility, namely KDiff3) that I was adding the Paragraph, not the table, to the doc:
doc.Add(parSection3);
Now that I've changed that to:
doc.Add(tblSection3);
...it works.

Related

Why I am unable to change the QR Code's size in iText7?

How to change QR Code size?
using (iText.Kernel.Pdf.PdfReader _pdf_reader =
new iText.Kernel.Pdf.PdfReader("tmp/example.pdf"))
{
using (iText.Kernel.Pdf.PdfDocument pdfDoc = new iText.Kernel.Pdf.PdfDocument(_pdf_reader, new iText.Kernel.Pdf.PdfWriter("tmp/output.pdf").SetSmartMode(true)))
{
BarcodeQRCode qrc = new BarcodeQRCode("https://google.com");
PdfFormXObject xObject = qrc.CreateFormXObject(ColorConstants.BLACK, pdfDoc);
float _w = pdfDoc.GetPage(1).GetPageSize().GetWidth();
float _h = pdfDoc.GetPage(1).GetPageSize().GetHeight();
PdfCanvas canvas = new PdfCanvas(pdfDoc.GetPage(1));
canvas.SaveState();
canvas.SetFillColor(ColorConstants.LIGHT_GRAY);
//canvas.Rectangle(_w - 90, _h - 90, 100, 100);
canvas.Fill();
canvas.RestoreState();
canvas.AddXObject(xObject, _w - qrc.GetBarcodeSize().GetWidth(), _h - qrc.GetBarcodeSize().GetHeight());
}
}
I try:
qrc.GetBarcodeSize().GetHeight();
qrc.GetBarcodeSize().GetWidth();
it returns 33
I try to set Height & Width to 100 like below:
qrc.GetBarcodeSize().SetHeight(100);
qrc.GetBarcodeSize().SetWidth(100);
and then check the size again, but it keeps returning 33, is it a bug? or Did I miss something?
please help
thanks
Don
I try to set Height & Width to 100 like below:
Actually, you can`t change the QrCode side this way.
In fact, QRcode is an n*n grid where n depends on some parameters as a QR code version and the error correction level.
When generating, iText uses the smallest version that can fit the content. This is version 4 (33*33) in your case.
The easiest way to change the size of QrCode in a document is by using the version of the createFormXObject method which accepts the moduleSide parameter.
float moduleSize = 100/qrc.GetBarcodeSize().GetHeight();
qrc.createFormXObject(foreground, moduleSize, document)
Module size here is size of the barcode`s grid cell (1 by default).
iTxt 7 Qrcode size effected by three parameter in hints, example example for your reference.
//C# code
//Prepare all necessary properties to create the qrcode
IDictionary<EncodeHintType, Object> hints = new Dictionary<EncodeHintType, object>();
//default character set (ISO-8859-1)
hints[EncodeHintType.CHARACTER_SET] = "UTF-8";
//Qrcode Error correction level L,M,Q,H
//default ErrorCorrectionLevel.L
hints[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.L;
//Qrcode minimal version level
//default 4
hints[EncodeHintType.MIN_VERSION_NR] = 6;
string code = "Qrcode content here";
BarcodeQRCode qrcode = new BarcodeQRCode(code, hints);

Something strange with positioning text with custom font in Itext7

I'm working on a program that creates several pdf docs and puts different text in the same location in them.
Text should be placed in a particular area and if it doesn't fit it in width it should wrap. It also has a custom font and may be differently aligned in that area. It should be Vertically aligned to Top because when the area is laid out for three lines and I has only one, it should appear on the top. Finally, I need to preserve leading on the level of font-size.
It is important to be precise in text positioning (e.g. I need an upper left corner of "H" from "Hello world" to appear definitely at 0, 100).
Now, I'm using
canvas.showTextAligned(paragraph, 0, 300,
TextAlignment.valueOf(alignment),
VerticalAlignment.TOP);
However, when I try to implement it with different fonts it has a various offset from desired y = 300. Moreover, offset differ from font to font. For Helvetica (everywhere 50 fontSize is used) offset is about 13 px, for Oswald about 17 px and for SedgwickAveDisplay it is massive 90 px.
I added borders to paragraph for debugging purpose and things become more strange.
Helvetica:
SedgwickAveDisplay:
The full snippet of my code to create pdf is below:
public byte[] createBadgeInMemory(int i) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PdfDocument newPdf = new PdfDocument(new PdfWriter(out));
srcPdf.copyPagesTo(1,1,newPdf);
PdfPage page = newPdf.getFirstPage();
PdfCanvas pdfCanvas = new PdfCanvas(page);
Canvas canvas = new Canvas(pdfCanvas, newPdf, pageSize);
File defaultFont = new File("src/main/resources/fonts/Helvetica.otf");
PdfFont font = PdfFontFactory
.createFont(fontPath == null ? defaultFont.getAbsolutePath() : fontPath,
PdfEncodings.IDENTITY_H, true);
String value = "Example word";
Paragraph paragraph = new Paragraph(value);
float textWidth = font.getWidth("Example", 50);
paragraph.setWidth(textWidth);
switch (alignment) {
case("CENTER"):
textWidth /= 2;
break;
case("RIGHT"):
break;
default:
textWidth = 0;
break;
}
paragraph.setFont(font)
.setFontSize(fontSize)
.setFixedLeading(fontSize)
.setFontColor(new DeviceRgb(red, green, blue))
.setMargin(0)
.setPadding(0);
paragraph.setBorderTop(new DashedBorder(Color.BLACK, 0.5f))
.setBorderBottom(new DashedBorder(Color.BLACK, 0.5f))
.setBorderRight(new DashedBorder(Color.BLACK, 0.5f));
paragraph.setHyphenation(new HyphenationConfig(0,
"Example".length()));
canvas.showTextAligned(paragraph,
0 + textWidth,
300,
TextAlignment.valueOf(alignment),
VerticalAlignment.TOP);
newPdf.close();
return out.toByteArray();
}
I also tried variant from here, but for some reason text inside rectangle cuts out at some point (for instance, if I have area width 100px and text snippet I put in that I know occupies exactly 100 px (with the help of font.getWidth(value)), I have my text cut at nearly 80 px).
I also haven't found a way to align text inside a rectangle.
This is the result with Rectangle. A solid border is Rectangle border. As you can see it cuts letter "t" in "Redundant". It also should contain "word" on the second line, but it doesn't.
I copied code from an example.
I need your help. What am I doing wrong or may be there is another way to layout text in particular area with alignment and font?
Thank you in advance.
Update 21.09.17
Also tried variant from this question with SedgwickAveDisplay:
paragraph.setHorizontalAlignment(HorizontalAlignment.LEFT);
paragraph.setVerticalAlignment(VerticalAlignment.TOP);
paragraph.setFixedPosition( 0, 300 - textHeight, "Example".length());
doc.add(paragraph);
The result is the same as on the second screenshot.
This is a font-specific problem. iText guesses information about font glyphs, namely the bboxes incorrectly.
There is a quick and dirty method to adjust this behavior. You can create a custom renderer for text and adjust the calculated positions in it. An example of such a class would be as follows:
class CustomTextRenderer extends TextRenderer {
private CustomTextRenderer(Text text) {
super(text);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
Rectangle oldBbox = this.occupiedArea.getBBox().clone();
// you can also make the height more than font size or less if needed
this.occupiedArea.setBBox(oldBbox.moveUp(oldBbox.getHeight() - fontSize).setHeight(fontSize));
yLineOffset = fontSize * 0.8f; // here you config the proportion of the ascender
return result;
}
#Override
public IRenderer getNextRenderer() {
return new CustomTextRenderer((Text) modelElement);
}
}
In order that new rendering logic to be applied, you have to use it in the following way:
Text text = new Text(value);
text.setNextRenderer(new CustomTextRenderer(text));
Paragraph paragraph = new Paragraph(text);
Please note that you have to be very careful with this kind of low-level layouting, be aware of you are doing and use this as seldom as possible.
Finally, I created a variant that worked for me.
pdfCanvas.beginText()
.setFontAndSize(font, fontSize)
.setLeading(fontSize)
.moveText(0, 300);
numberOfLines = 0;
sumOfShifts = 0;
float maxWidth = computeStringWidth("Exaxple");
String[] words = value.split("\\s");
StringBuilder line = new StringBuilder();
line.append(words[0]);
float spaceWidth = computeStringWidth(" ") ;
float lineWidth;
for (int index = 1; index < words.length; index++) {
String word = words[index];
float wordWidth = computeStringWidth(word) ;
lineWidth = computeStringWidth(line.toString()) ;
if (lineWidth + spaceWidth + wordWidth <= maxWidth) {
line.append(" ").append(word);
} else {
showTextAligned(alignment, pdfCanvas, line.toString(), lineWidth, maxWidth);
line.delete(0, line.length());
line.append(word);
}
}
if(line.length() != 0) {
lineWidth = computeStringWidth(line.toString()) ;
showTextAligned(alignment, pdfCanvas, line.toString(), lineWidth, maxWidth);
}
pdfCanvas.endText();
As computeStringWidth(String str) I used
Toolkit.getToolkit().getFontLoader().computeStringWidth(String str, Font font);
from import com.sun.javafx.tk.Toolkit with Font from javafx.scene.text.Font. I've chosen it because I use it in other parts of my app.
showTextAligned(...) is my own method that looks this way:
private void showTextAligned(String alignment,
PdfCanvas pdfCanvas,
String line,
float lineWidth,
float maxWidth) {
switch (alignment) {
case "CENTER": {
float shift = (maxWidth - lineWidth) / 2 - sumOfShifts;
pdfCanvas.moveText(shift, 0);
if(numberOfLines == 0) pdfCanvas.showText(line);
else pdfCanvas.newlineShowText(line);
numberOfLines++;
sumOfShifts += shift;
break;
}
case "RIGHT": {
float shift = maxWidth - lineWidth - sumOfShifts;
pdfCanvas.moveText(shift, 0);
if(numberOfLines == 0) pdfCanvas.showText(line);
else pdfCanvas.newlineShowText(line);
numberOfLines++;
sumOfShifts += shift;
break;
}
default:
pdfCanvas.moveText(0, 0);
if(numberOfLines == 0) pdfCanvas.showText(line);
else pdfCanvas.newlineShowText(line);
numberOfLines++;
break;
}
}
In my project, I used my variant, because it gives me an opportunity to work with hyphenation deeper (for instance, I can in future add functionality to avoid putting prepositions as a last word in the line).

iText header: add logo img on the left side and text on the right side

I´m new to iText.
I would like to make a header that will be the same on each page.
The page header will look something like this:
logo.jpg some text
How can I do that?
I have read this link:
http://developers.itextpdf.com/question/how-generate-report-dynamic-header-pdf-using-itextsharp
and I have a problem because I cant add an image to pharse.
Image image = Image.GetInstance(Server.MapPath(mclLogo));
phrase.Add(image);
Throws the Error:
Insertion of illegal Element: 32
Edit: I have tried with paragraph and glue:
Paragraph paragraph = new Paragraph();
paragraph.Add(image);
paragraph.Add(new Chunk(glue));
paragraph.Add("text on the right");
and the output is 2 lines.
somthing link this:
logo.jpg
some text
edit 2:
I have read this:
http://developers.itextpdf.com/content/itext-7-jump-start-tutorial/chapter-1-introducing-basic-building-blocks
espacially the part:
Image fox = new Image(ImageDataFactory.create(FOX));
Image dog = new Image(ImageDataFactory.create(DOG));
Paragraph p = new Paragraph("The quick brown ")
.add(fox)
.add(" jumps over the lazy ")
.add(dog);
document.add(p);
but I use iText5 and I dont find any way how to make a picture and text in the same line.
Try below code, which works with two different Header plot which plotted at right end with image and Left end one with text.
public override void OnStartPage(PdfWriter writer, iTextSharp.text.Document document)
{
PdfPTable HeaderPlot = new PdfPTable(new float[] { 10F });//Header plot 1
PdfPTable HeaderPlot2 = new PdfPTable(new float[] { 10F });//Header plot 2
PdfPCell cell;//cell 1
PdfPCell cell2;// cell 2
HeaderPlot.TotalWidth = 570F; //width for Header plot 1
HeaderPlot2.TotalWidth = 570F;//width for Header plot 2
cell = new PdfPCell();
cell2 = new PdfPCell();
string path = headerpath;
FileInfo f2 = new FileInfo(path);
FileStream fs = new FileStream(f2.FullName,
FileMode.Open, FileAccess.Read);
BinaryReader rdr = new BinaryReader(fs);
byte[] fileData = rdr.ReadBytes((int)fs.Length);
Image image = Image.GetInstance(fileData);
image.ScaleAbsolute(80, 40); //adjusting image size
image.Alignment = Element.ALIGN_CENTER;
cell = new PdfPCell(image)
{
Border = 0,
HorizontalAlignment = Element.ALIGN_TOP,
VerticalAlignment = Element.ALIGN_TOP
};//Header image position
Font font = FontFactory.GetFont("Calibri Light", 8f, Font.NORMAL,
iTextSharp.text.BaseColor.BLACK);//Initializing font
cell2 = new PdfPCell(new Phrase("Text", font))
{
Border = 0,
HorizontalAlignment = Element.ALIGN_LEFT,
VerticalAlignment = Element.ALIGN_TOP
};//Header Text position
HeaderPlot.AddCell(cell);//adding cell 1 to Headerplot 1
HeaderPlot.WriteSelectedRows(0, -1, 480, 835,
writer.DirectContent);//Position of the header on right end, coordinates on right end(480,835)
HeaderPlot2.AddCell(cell2);//adding cell 2 to Headerplot 2
HeaderPlot2.WriteSelectedRows(0, -1, 40, 835,
writer.DirectContent);//Position of the header on left end, coordinates on left end(40,835)
}

NPOI XWPF how can I place text on a single line that is both left & right justified?

I'm new to using NPOI XWPF and trying to create my first document, so far it's going well. The only issue I have left is trying to place text on the same line that is both left and right justified, I want it to look like:
Area: 1(Left Jstfd) Grade Level/Course: 10th Grade Reading (Right Jstfd)
Below is the code snippet I'm using, it's just pushing all the text together on the left side of the page...blah
XWPFParagraph p2 = doc.CreateParagraph();
p2.Alignment = ParagraphAlignment.LEFT;
XWPFRun r3 = p2.CreateRun();
r3.SetBold(true);
r3.FontFamily = "Times New Roman";
r3.FontSize = 12;
r3.SetText("Area: " + ah.schoolArea);
XWPFRun r4 = p2.CreateRun();
r4.SetBold(true);
r4.FontFamily = "Times New Roman";
r4.FontSize = 12;
r4.SetText("Grade Level/Course: " + ah.filterParm);
Before trying to accomplish a task in (N)POI, it's always good to realize how said task is accomplished in Microsoft Word itself. You can't simply split a paragraph half-way a line, what you do is
Add a tab stop at the end of the line
Set it to right-aligned.
Type text on the left, hit tab, type text on the right
Unfortunately, it doesn't seem XWPFParagraph exposes tabstop functionality at this point. However, XWPFParagraph is a wrapper around the CT_P class, which maps 1:1 onto the underlying Office XML format. Using reflection, we can access this private field and use it to directly add the tabstop.
Sample code:
var paragraph = document.CreateParagraph();
var memberInfo = typeof(XWPFParagraph).GetField("paragraph", BindingFlags.NonPublic | BindingFlags.Instance);
if (memberInfo == null)
{
throw new Exception("Could not retrieve CT_P from XWPFParagraph");
}
var internalParagraph = (CT_P) memberInfo.GetValue(paragraph);
CT_PPr pPr = internalParagraph.AddNewPPr();
CT_Tabs tabs = pPr.AddNewTabs();
CT_TabStop tab = tabs.AddNewTab();
tab.pos = "9000";
tab.val = ST_TabJc.right;
var run = paragraph.CreateRun();
run.SetText("Left aligned");
run.AddTab();
run = paragraph.CreateRun();
run.SetText("Right aligned");
Result:

Windows Forms chart with two aligned/overlapping areas don't use the entire control

A chart on a form I created has two overlapping areas. The overlapping part works just fine. The problem is that visible graph only takes up half the height of the chart control:
The bottom half of the control is left empty (presumably because that's where the second area would have gone were the two areas not aligned?). I can't figure out how to get the chart to use the entire control. The code is below:
chart1.Dock = DockStyle.Fill;
chart1.Legends.Add(new Legend { Name = "Legend1" });
chart1.Location = new Point(435, 3);
chart1.Name = "chart1";
chart1.Size = new Size(426, 287);
chart1.TabIndex = 2;
chart1.Text = "chart1";
var firstArea = chart1.ChartAreas.Add("First Area");
var seriesFirst = chart1.Series.Add("First Series");
seriesFirst.ChartType = SeriesChartType.Line;
seriesFirst.Points.Add(new DataPoint(10, 55));
seriesFirst.Points.Add(new DataPoint(11, 56));
seriesFirst.Points.Add(new DataPoint(12, 59));
var secondArea = chart1.ChartAreas.Add("Second Area");
secondArea.BackColor = Color.Transparent;
secondArea.AlignmentOrientation = AreaAlignmentOrientations.All;
secondArea.AlignmentStyle = AreaAlignmentStyles.All;
secondArea.AlignWithChartArea = firstArea.Name;
secondArea.AxisY.LabelStyle.Enabled = false;
secondArea.AxisX.LabelStyle.Enabled = false;
var seriesSecond = chart1.Series.Add("Second Series");
seriesSecond.ChartType = SeriesChartType.Line;
seriesSecond.ChartArea = secondArea.Name;
seriesSecond.Points.Add(new DataPoint(10, 1001));
seriesSecond.Points.Add(new DataPoint(11, 1015));
seriesSecond.Points.Add(new DataPoint(12, 1016));
This is some old code I've dug out and modified to suit your example. The problem is the InnerPlotPosition.Auto and Position.Auto status of the ChartAreas, thats why after you add the second chart the first charts auto position jumps up and then the second chart aligns with the new InnerPlotPosition.Auto values.
You can try turning this off but I think its easier to just position the first chart manually and then allow the second to align with the new manual position. It produces the below image (minus your legend you can work the values needed yourself)
Bit of pain in the ass solution but hopefully it helps
Dim chart1 As New Chart
Me.Controls.Add(chart1)
chart1.Location = New Point(435, 3)
chart1.Name = "chart1"
chart1.Size = New Size(426, 287)
chart1.TabIndex = 2
chart1.Text = "chart1"
Dim firstArea As ChartArea = chart1.ChartAreas.Add("First Area")
Dim seriesFirst = chart1.Series.Add("First Series")
seriesFirst.ChartType = SeriesChartType.Line
seriesFirst.Points.Add(New DataPoint(10, 55))
seriesFirst.Points.Add(New DataPoint(11, 56))
seriesFirst.Points.Add(New DataPoint(12, 59))
Dim secondArea As ChartArea = chart1.ChartAreas.Add("Second Area")
secondArea.BackColor = Color.Transparent
secondArea.AlignmentOrientation = AreaAlignmentOrientations.All
secondArea.AlignmentStyle = AreaAlignmentStyles.All
secondArea.AlignWithChartArea = firstArea.Name
secondArea.AxisY.LabelStyle.Enabled = False
secondArea.AxisX.LabelStyle.Enabled = False
Dim seriesSecond = chart1.Series.Add("Second Series")
seriesSecond.ChartType = SeriesChartType.Line
seriesSecond.ChartArea = secondArea.Name
seriesSecond.Points.Add(New DataPoint(10, 1001))
seriesSecond.Points.Add(New DataPoint(11, 1015))
seriesSecond.Points.Add(New DataPoint(12, 1016))
' *** Set locational values here for your first chart***
Dim heightAboveChartArea As Integer = 20
Dim heightBelowChartArea As Integer = 20
Dim axisLabelHeight As Integer = 40
Dim widthLeftOfChartArea As Integer = 20
Dim widthRightOfChartArea As Integer = 20
Dim heightPerBar As Integer = 20
Dim numberOfPoints As Integer = chart1.Series(0).Points.Count
' *** The following code should not normally be modified ***
chart1.Height = heightAboveChartArea + heightBelowChartArea + axisLabelHeight + (numberOfPoints * heightPerBar)
chart1.ChartAreas(0).Position.X = widthLeftOfChartArea / chart1.Width * 100
chart1.ChartAreas(0).Position.Width = 100 - (widthRightOfChartArea / chart1.Width * 100) - chart1.ChartAreas(0).Position.X
chart1.ChartAreas(0).Position.Y = (heightAboveChartArea / chart1.Height * 100)
chart1.ChartAreas(0).Position.Height = 100 - (heightBelowChartArea / chart1.Height * 100) - chart1.ChartAreas(0).Position.Y
I thought about monkeying with the position, but I'd have to take into account borders and the legend and other chart components and assumed I'd never get it as good as the auto-positioning provided by the chart - and it would drive me nuts. However, the suggestion by TylerDurden led me to the idea of simply delaying the addition of the second series/area until after the chart had rendered at least once and had calculated the position. This turned out to be non-trivial, since for most of the chart's initialization the X, Y, Height and Width are still zero. The best way I found was to add the second series in the Form's Shown event:
private void OnShown(object sender, EventArgs eventArgs)
{
Application.DoEvents();
var f = chart1.ChartAreas[0].Position.ToRectangleF();
chart1.ChartAreas[0].Position.Auto = false;
chart1.ChartAreas[0].Position.X = f.X;
chart1.ChartAreas[0].Position.Y = f.Y;
chart1.ChartAreas[0].Position.Height = f.Height;
chart1.ChartAreas[0].Position.Width = f.Width;
// add second area/series here
The call to Application.DoEvents() is required to force the chart to render and calculate the Position. Since Position is a percentage, both chart areas will always occupy the full height and width of the parent Chart.

Resources