Input: Pairs of images along with a title and description and coordinates of a shape that should be drawn over each image.
The size of the first image is fixed, and a rectangle should be drawn over it.
The size of the second image is variable, and an elipse should be drawn over it.
Example:The first image in each pair is a scaled down high resolution image of a tree, with a rectangle roi (region-of-interest) marked over it.
The second image is the cliped roi in high resolution, with an ellipse marking a bird or insect that is on the tree.
The problemI would like to use MigraDoc in order to format the document so that for each pair of images, their title and description will be in the same pdf page as the images.
In case multiple pairs re displayed in the same page, I would like to control the gap between them.
On the other hand I would like to use PdfSharp for drawing the marks over the images, and since the drawing requires abolute coordinates, I need to convert the relative coordinates of the marks (relative to the image) to absolute page coordinatesm, by retrieving and adding the absolute coordinates of each image.
The solution1) Displaying the images in a 1-row x 2-columns table. This way the top-left of the first image will be the same as the top-left of the table, and the top-left of the second image will be the top-left of the table shifted by the column width to the right.
Note: The using of a table is a workaround I used since I didn't find a way to retrived the rendering coordinates of the images. This workaround woudn't have worked if the size of the first images wasn't fixed.
2) In order to ensure that the title, description and images are displayed on the same page they are added to paragraphs with a
KeepWithNext style.
The table of images is followed by a paragraph with a
KeepWithNext = false , and
SpaceAfter = 4 style, thus ensuring the gap between different data displayed on the same page.
Note:Since there are no
KeepWithPrev styles, I don't know of a way to ensure that text followng the images will be displaed in the same page as the images.
The codeNote: The code below is a simplification of my runtime code. I compiled it with no errors, but I didn't run or debug it.
Code:
using System;
using MigraDoc.DocumentObjectModel;
using MigraDoc.Rendering;
using MigraDoc.DocumentObjectModel.Shapes;
using MigraDoc.DocumentObjectModel.Tables;
using PdfSharp.Pdf;
using PdfSharp.Drawing;
namespace ReporterNs
{
class ReportCreatorDemo
{
public class Ellipse
{
public double X; // center X
public double Y; // center Y
public double L; // long radius
public double S; // short radius
public double Phi; // angle in radians between the X axis and the long radius.
}
public class Data
{
public string title;
public string description;
public string image1Path;
public XRect image1Mark;
public string image2Path;
public Ellipse image2Mark;
}
Document m_document = null;
PdfDocument m_pdfDocument = null;
// The following variables should be initialized before calling CreateReport()
public Unit m_pageWidth = 0;
public Unit m_pageHeight = 0;
public Unit m_Image1Width = 0;
public Unit m_GapBetweenImages = 0;
public Unit m_MaxImage2Width = 0;
protected void CreateReport(Data[] data, string pdfPath)
{
m_document = new Document();
DefineStyles();
AddData(data);
Render(m_pageWidth, m_pageHeight);
m_pdfDocument.Save(pdfPath);
}
void AddData(Data[] data)
{
Section section = m_document.LastSection;
for (int i = 0; i < data.Length; ++i )
{
Data cur = data[i];
Paragraph paragraph = section.AddParagraph(cur.title, "DataTitle");
paragraph = section.AddParagraph(cur.description, "DataDescription");
section.AddParagraph("", "BeforeImages");
Table table = section.AddTable();
table.AddColumn(m_Image1Width + m_GapBetweenImages);
table.AddColumn(m_MaxImage2Width);
table.Columns[0].LeftPadding = table.Columns[0].RightPadding = table.Columns[1].LeftPadding = table.Columns[1].RightPadding = 0;
Row row = table.AddRow();
Image image1 = new Image(cur.image1Path);
image1.Tag = cur.image1Mark;
image1.LockAspectRatio = true;
row.Cells[0].Add(image1);
Image image2 = new Image(cur.image2Path);
image2.Tag = cur.image2Mark;
image2.LockAspectRatio = true;
row.Cells[1].Add(image2);
section.AddParagraph("", "AfterImages"); // Setting KeepWithNext to false
}
}
protected void Render(double pageWidth_mm, double pageHeight_mm)
{
DocumentRenderer documentRenderer = new DocumentRenderer(m_document);
PdfDocument m_pdfDocument = new PdfDocument();
documentRenderer.PrepareDocument();
int pageCount = documentRenderer.FormattedDocument.PageCount;
XRect pageRect = new XRect(0, 0, m_pageWidth, m_pageHeight);
for (int pageNumber = 1; pageNumber <= pageCount; pageNumber++) // Note that page numbers start with 1.
{
PdfPage pdfPage = m_pdfDocument.AddPage();
XGraphics xGraphics = XGraphics.FromPdfPage(pdfPage);
XGraphicsContainer container = xGraphics.BeginContainer(pageRect, pageRect, XGraphicsUnit.Point);
documentRenderer.RenderPage(xGraphics, pageNumber);
PostRenderPage(documentRenderer, xGraphics, pageNumber);
xGraphics.EndContainer(container); // Pop the previous graphical state
}
}
protected void PostRenderPage(DocumentRenderer documentRenderer, XGraphics xGraphics, int pageNumber)
{
RenderInfo[] renderInfo = documentRenderer.GetRenderInfoFromPage(pageNumber);
for ( int i = 0; i < renderInfo.Length; ++i )
{
if ( renderInfo[i].GetType() != typeof(TableRenderInfo) )
continue;
TableRenderInfo tableRenderInfo = (TableRenderInfo)renderInfo[i];
Table table = (Table)tableRenderInfo.DocumentObject;
if ( table.IsNull() || table.Columns.IsNull() || table.Rows.IsNull() || table.Columns.Count < 2 )
continue;
if ( table.Rows[0].Cells[0].Elements[0].GetType() != typeof(Image) || table.Rows[0].Cells[1].Elements[0].GetType() != typeof(Image) )
continue;
Area a = tableRenderInfo.LayoutInfo.ContentArea;
Image image1 = (Image)System.Convert.ChangeType(table.Rows[0].Cells[0].Elements[0], typeof(Image));
Image image2 = (Image)System.Convert.ChangeType(table.Rows[0].Cells[1].Elements[0], typeof(Image));
XRect rect = (XRect)image1.Tag;
rect.X += a.X;
rect.Y += a.Y;
xGraphics.DrawRectangle(new XPen(XColor.FromArgb(0,0,255), 2), rect);
Ellipse ellipse = (Ellipse)image2.Tag;
double centerX = a.X + table.Columns[0].Width + ellipse.X;
double centerY = a.Y + ellipse.Y;
float phiDegrees = -1 * (float)(180 * ellipse.Phi / Math.PI);
XGraphicsState gfxState = xGraphics.Save();
xGraphics.RotateAtTransform(phiDegrees, new XPoint(centerX , centerY));
xGraphics.DrawEllipse(new XPen(XColor.FromArgb(255,0,0), 2), centerX - ellipse.L , centerY - ellipse.S, 2*ellipse.L, 2*ellipse.S);
xGraphics.Restore(gfxState);
}
}
public void DefineStyles()
{
Style style = m_document.Styles["Normal"]; // Get the predefined style Normal.
style = m_document.Styles.AddStyle("DataTitle", "Normal");
style.Font.Bold = true;
style.ParagraphFormat.KeepWithNext = true;
style = m_document.Styles.AddStyle("DataDescription", "Normal");
style.ParagraphFormat.KeepWithNext = true;
style = m_document.Styles.AddStyle("AfterImages", "Normal");
style.ParagraphFormat.SpaceAfter = Unit.FromMillimeter(3.0);
style.ParagraphFormat.KeepWithNext = false;
}
} // class ReportCreatorDemo
} // namespace ReporterNs