I want to draw multiple images (with transparent color space like .png) on top of each other into one table cell. At the moment I'm doing it this way: I draw both pictures but then move the second picture up by the height of the first picture to place then over each other:
img1.SetRelativePosition(0, 0, 0, 0);
img2.SetRelativePosition(0, -img1.GetHeight(), 0, 0);
As far as I know: to place elements like images in a cell i can't use "img.SetFixedPosition". Because SetFixedPosition always uses “global” document coordinates and not “local” table cell coordinates:
img.SetFixedPosition(0, 0); //Coord: 0,0 on document postiton NOT on cell pos. 0,0.
table.AddCell(img);
Full code Example:
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(documentName));
Document doc = new Document(pdfDoc);
var rawImage1 = ImageDataFactory.Create(pathToImage1);
var rawImage2 = ImageDataFactory.Create(pathToImage2);
Table table = new Table(UnitValue.CreatePercentArray(3)).UseAllAvailableWidth();
for (int i = 0; i < 15; i++)
{
table.AddCell("data");
if(i % 5 == 0)
{
Image img1 = new Image(rawImage1);//a new image must be created to move it
Image img2 = new Image(rawImage2);
img1.SetRelativePosition(0, 0, 0, 0);
img2.SetRelativePosition(0, -img1.GetHeight(), 0, 0);
table.AddCell(img1);
table.AddCell(img2);
}
}
doc.Add(table);
doc.Close();
Is there a better sulution to draw pictures on top of each other?
(This is just an example! Real world problems are a bit more complex - we need to use separate images to do everything we need to show in our table calculations)
You could try something as below, the code is using the java SDK but should be quite easy to convert to C# its just renaming the methods to start with upper case.
What it does: It takes the image data and draws it manually on on the PDFcanvas. To do this first we have to calculated the max height/width to the image so container has the correct height and width. This is so the layout engine can correctly calculate other parts of the layout for example the height of the row.
Then when the drawing happens we know where we should draw, so we can simply place the images on the canvas ourselves. See the draw method in the custom renderer.
If we look at the content stream we see the images for each of our custom elements are placed on the exact same location.
And the end result would be something like this:
I don't have access to transparent svg's to test the visual but basing myself on the generated content stream this should work.
public static void main(String[] args) throws MalformedURLException, FileNotFoundException {
PdfDocument pdfDoc = new PdfDocument(new PdfWriter("hello.pdf"));
Document doc = new Document(pdfDoc);
Table table = new Table(2);
ImageData img = ImageDataFactory.create("<path-to-png>");
ImageData img2 = ImageDataFactory.create("<path-to-png>");
table.addCell("cell1");
table.addCell("cell1");
table.addCell(new OverLappingCellDiv(Arrays.asList(img, img2)));
table.addCell("cell1");
table.addCell(new OverLappingCellDiv(Arrays.asList(img, img2)));
doc.add(table);
doc.close();
}
static class OverLappingCellDiv extends Div {
ArrayList<ImageData> images = new ArrayList<>();
public OverLappingCellDiv(List<ImageData> imageData) {
images.addAll(imageData);
float maxHeight = 0;
float maxWidth = 0;
for (ImageData image : images) {
maxHeight = Math.max(maxHeight, image.getHeight());
maxWidth = Math.max(maxWidth, image.getWidth());
}
this.setWidth(maxWidth);
this.setHeight(maxHeight);
}
public List<ImageData> getImages() {
return images;
}
@Override
protected IRenderer makeNewRenderer() {
return new OverlappingImageRenderer(this);
}
}
static class OverlappingImageRenderer extends DivRenderer {
public OverlappingImageRenderer(Div modelElement) {
super(modelElement);
}
@Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
OverLappingCellDiv overLappingCellDiv = (OverLappingCellDiv) this.getModelElement();
Rectangle innerArea = this.getInnerAreaBBox();
for (ImageData image : overLappingCellDiv.getImages()) {
drawContext.getCanvas().addImageAt(image, innerArea.getLeft(), innerArea.getBottom(), false);
}
}
}