How to make Flying Saucer generate an image with transparent background? - pdf-generation

I'm using flyingsaucer to render HTML to an image, using Java2DRenderer:
Map<Key,Object> renderingHints = new HashMap<Key,Object>();
renderingHints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
renderingHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
renderingHints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
hints = new RenderingHints(renderingHints);
Java2DRenderer renderer = new Java2DRenderer(dom, width, height);
renderer.setRenderingHints(renderingHints);
renderer.getSharedContext().setDPI(DPI);
renderer.setBufferedImageType(BufferedImage.TYPE_INT_ARGB);
img = renderer.getImage();
Then I add the rendered image to a PDF document, scaling it to fit the entire page.
com.lowagie.text.Document pdf = new com.lowagie.text.Document(isLandscape ? PageSize.A4.rotate() : PageSize.A4);
pdf.setMargins(MARGIN, MARGIN, MARGIN, MARGIN);
Rectangle ps = pdf.getPageSize();
PdfWriter.getInstance(pdf, outputStream);
pdf.open();
com.lowagie.text.Image pdfImage = com.lowagie.text.Image.getInstance(img, null);
pdfImage.scaleAbsolute(
ps.getWidth() - pdf.leftMargin() - pdf.rightMargin(),
ps.getHeight() - pdf.topMargin() - pdf.bottomMargin()
);
pdf.add(pdfImage);
pdf.close();
The problem is that the image rendered by FS has as background color solid white... so the printer fills the entire page with a very light gray (I believe that scaling transforms the solid white into a light grey).
How can I force flyingsaucer generate a transparent image so I can avoid this problem?

For the Java2DRenderer there is a way by subclassing the Java2DRender
final java.awt.Color TRANSPARENT = new Color(255, 255, 255, 0);
final int imageType = BufferedImage.TYPE_INT_ARGB;
final Java2DRenderer java2dRenderer = new Java2DRenderer(doc, width, height) {
#Override
protected BufferedImage createBufferedImage(final int width, final int height) {
final BufferedImage image = org.xhtmlrenderer.util.ImageUtil.createCompatibleBufferedImage(width, height, imageType);
org.xhtmlrenderer.util.ImageUtil.clearImage(image, TRANSPARENT);
return image;
}
};
java2dRenderer.setBufferedImageType(imageType);

Related

Image auto-cropping, issue with borders

I'm talking photo in my application. After I take it, on next screen in layout I want it be automatically cropped like in image.
But I'm constantly lose the boundaries of the photo and I have strange borders (indicated by RED in the image).
Here is my code:
Android code
private void NewElement_OnDrawBitmap(object sender, EventArgs e)
{
if (this.ViewGroup != null)
{
//get the subview
Android.Views.View subView = ViewGroup.GetChildAt(0);
int width = subView.Width;
int height = subView.Height;
//create and draw the bitmap
Bitmap b = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
Canvas c = new Canvas(b);
ViewGroup.Draw(c);
//save the bitmap to file
bytes = SaveBitmapToFile(b);
}
}
iOS code
UIGraphics.BeginImageContextWithOptions(this.Bounds.Size, true, 0);
this.Layer.RenderInContext(UIGraphics.GetCurrentContext());
var img = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
using (NSData imageData = img.AsPNG())
{
bytes = new Byte[imageData.Length];
System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, bytes, 0, Convert.ToInt32(imageData.Length));
}
private byte[] SaveBitmapToFile(Bitmap bm)
{
MemoryStream ms = new MemoryStream();
bm.Compress(Bitmap.CompressFormat.Png, 100, ms);
return ms.ToArray();
}
}
From your shared code, you're not doing any cropping, so your app just put your origin picture into the view, and the border you mentioned, is probably the background of your view.
And your project code, I saw you added
Bitmap bitmap = Bitmap.CreateBitmap(b, 0, 0, (98 * width) / 100, (82 * height) / 100);
Yes this is cropping the picture, but this just crop the image from top-left corner to 98% width and 82% height. much like
If you want to crop the picture into a square focused in center, you can simply:
int offset = (height - width) / 2;
Bitmap bitmap = Bitmap.CreateBitmap(b, 0, offset, width, height- offset);
But if you want to do more operations on Image like move/zoom... and then crop, I'd like to suggest you turn to existing solutions like FFImageLoading or Syncfusion. Or you'll have to calculate all the move/zoom data to do the crop.

Canvas drawImage scale height and width in CustomPainter

I am developing an application in Flutter where I am using CustomPainter to draw an image which the user picks from gallery/camera. In addition to this the use can draw lines as well as change the stroke value, opacity colour and colour on it its own. For this I have created 2 classes DrawEditor and DrawingPainter the code for those two classes can be found below. Once the user picks an image
the image is passed to the DrawingPainter class where paint() is called and I draw my lines and image. The issue is in _paintBackgroundImage() in this method I draw the image by using canvas.drawImage(paintedImage, Offset.zero, Paint()); which does not scale the image.
Earlier I tried a different approach instead of drawing the image with canvas.drawImage(paintedImage, Offset.zero, Paint()) I used canvas.drawImageRect(paintedImage, inputSubRect, outputSubRect, Paint()); as can be seen below. However with this approach the draw picture Is pixelated so I prefer canvas.drawImage(paintedImage, Offset.zero, Paint()) as this does not damage the picture.
Any help with scaling the image will be greatly appreciated.
//Example 1 : Code with canvas.drawImageRect but image pixelated
final UI.Rect rect = UI.Offset.zero & _canvasSize;
final Size imageSize =Size(paintedImage.width.toDouble(), paintedImage.height.toDouble());
FittedSizes sizes = applyBoxFit(BoxFit.contain, imageSize, _canvasSize);
final Rect inputSubRect =
Alignment.center.inscribe(sizes.source, Offset.zero & imageSize);
final Rect outputSubRect =
Alignment.center.inscribe(sizes.destination, rect);
canvas.drawImageRect(paintedImage, inputSubRect, outputSubRect, Paint());
//Example 2 : Code with canvas.drawImageRect but image pixelated
canvas.drawRect(Rect.fromPoints(blurStartOffset, blurIndicatorOffset),
blurPaintSettings)
class DrawingPainter extends CustomPainter {
static int blurColor = 0xFFB3E5FC;
UI.Image paintedImage;
List<DrawingPoints> pointsList;
List<DrawingPoints> blurPointsList;
List<Offset> offsetPoints = List();
Size _canvasSize;
Offset blurIndicatorOffset;
Offset blurStartOffset;
bool isBlur;
List<BlurIndicatorOffsetWrapper> wrapperList = new List();
/// To blur an image we need a [MaskFilter]
Paint blurPaintSettings = new Paint()
..style = PaintingStyle.fill
..color = Color(blurColor)
..maskFilter = MaskFilter.blur(BlurStyle.normal, 3.0);
DrawingPainter(
{this.pointsList,
this.paintedImage,
this.blurPointsList,
this.blurIndicatorOffset,
this.blurStartOffset}) {
isBlur = blurIndicatorOffset != null;
}
#override
void paint(Canvas canvas, Size size) {
_canvasSize = size;
_paintBackgroundImage(canvas);
_drawPoints(canvas);
_drawBlurIndicator(canvas);
}
/// Paints the image onto the canvas
void _paintBackgroundImage(Canvas canvas) {
if (paintedImage == null) {
return;
}
final UI.Rect rect = UI.Offset.zero & _canvasSize;
final Size imageSize =
Size(paintedImage.width.toDouble(), paintedImage.height.toDouble());
FittedSizes sizes = applyBoxFit(BoxFit.contain, imageSize, _canvasSize);
final Rect inputSubRect =
Alignment.center.inscribe(sizes.source, Offset.zero & imageSize);
final Rect outputSubRect =
Alignment.center.inscribe(sizes.destination, rect);
canvas.drawImageRect(paintedImage, inputSubRect, outputSubRect, Paint());
}
/// Paints the lines onto the canvas
void _drawPoints(Canvas canvas) {
for (int i = 0; i < pointsList.length - 1; i++) {
if (pointsList[i] != null && pointsList[i + 1] != null) {
canvas.drawLine(pointsList[i].points, pointsList[i + 1].points,
pointsList[i].paint);
}
}
}
/// Paints the blur indicator onto the canvas
void _drawBlurIndicator(Canvas canvas) {
if (blurStartOffset != null && blurIndicatorOffset != null) {
canvas.drawRect(Rect.fromPoints(blurStartOffset, blurIndicatorOffset),
blurPaintSettings);
}
}
void setBlurIndicator(Offset localOffset) {
blurIndicatorOffset = localOffset;
}
#override
bool shouldRepaint(DrawingPainter oldDelegate) {
return true;
}
Future<Uint8List> save() async {
//Create canvas
// Set PictureRecorder on the canvas and start recording
UI.PictureRecorder recorder = UI.PictureRecorder();
Canvas canvas = Canvas(recorder);
//Draw image on new canvas
if (paintedImage != null) {
final Size imageSize = Size(paintedImage.width.toDouble(), paintedImage.height.toDouble());
//Here image is the problem
canvas.drawImage(paintedImage, Offset.zero, Paint());
}
//Draw points on new canvas
for (int i = 0; i < pointsList.length - 1; i++) {
if (pointsList[i] != null && pointsList[i + 1] != null) {
canvas.drawLine(
pointsList[i].points,
pointsList[i + 1].points,
pointsList[i].paint,
);
}
}
//End recording
final resultImage = await recorder.endRecording().toImage(
_canvasSize.width.floor(),
_canvasSize.height.floor(),
);
final imageBytes =
await resultImage.toByteData(format: UI.ImageByteFormat.png);
return imageBytes.buffer.asUint8List();
}
}
class DrawingPoints {
Paint paint;
Offset points;
DrawingPoints({this.points, this.paint});
}
enum SelectedMode { StrokeWidth, Opacity, Color, Blur }
I had a very similar requirement and the comment about using paintImage was exactly what I was looking for, so I figured I'd share what I ended up with.
I needed to scale down an image and draw overlays on top of that image. image is my original (unscaled) Image object.
var recorder = ui.PictureRecorder();
var imageCanvas = new Canvas(recorder);
var painter = _MarkupPainter(_overlays);
//Paint the image into a rectangle that matches the requested width/height.
//This will handle rescaling the image into the rectangle so that it will not be clipped.
paintImage(
canvas: imageCanvas,
rect: Rect.fromLTWH(0, 0, scaledWidth, scaledHeight),
image: image,
fit: BoxFit.scaleDown,
repeat: ImageRepeat.noRepeat,
scale: 1.0,
alignment: Alignment.center,
flipHorizontally: false,
filterQuality: FilterQuality.high
);
//Add the markup overlays.
painter.paint(imageCanvas, Size(scaledWidth, scaledHeight));
var picture = recorder.endRecording();
return picture.toImage(scaledWidth.toInt(), scaledHeight.toInt());

How to merge two images in Xamarin Android and position it the same way like CGRect for iOS?

I successfully get out the two images in my resources folder in my project like this.
string BackGroundImage = "background_image";
string ObjectImage = "object_image";
var TheBackGroundImage = BitmapFactory.DecodeResource(Resources, Resources.GetIdentifier(BackGroundImage, "drawable", PackageName));
var TheObjectImage = BitmapFactory.DecodeResource(Resources, Resources.GetIdentifier(ObjectImage, "mipmap", PackageName));
What i have done after is very the tricky part comes in and I do not know how to quite get it right. What i try to do is create a new Bitmap where the BackgroundImage is the base. Then i create a canvas with my second image (ObjectImage) that is the image that will be on top of the BackgroundImage and try to merge it all together.
Bitmap Result = Bitmap.CreateBitmap(TheBackGroundImage.Width, TheBackGroundImage.Height, TheBackGroundImage.GetConfig());
Canvas canvas = new Canvas(Result);
canvas.DrawBitmap(ObjectImage, new Matrix(), null);
canvas.DrawBitmap(ObjectImage, 79, 79, null);
This does not work as anticipated, is canvas the way to go or is there somethinig else i should look at?
If we look at my iOS solution then i do it like this:
UIImage PhoneImage = UIImage.FromFile(PhonePath);
UIImage IconImage = UIImage.FromFile(IconPath);
UIImage ResultImage;
CGSize PhoneSize = PhoneImage.Size;
CGSize IconSize = IconImage.Size;
UIGraphics.BeginImageContextWithOptions(IconSize, false, IconImage.CurrentScale); //UIGraphics.BeginImageContextWithOptions(IconSize, false, IconImage.CurrentScale);
UIGraphics.BeginImageContext(PhoneSize);
CGRect Frame = (new CoreGraphics.CGRect(25, 29.5, 79, 79));
UIBezierPath RoundImageCorner = new UIBezierPath();
RoundImageCorner = UIBezierPath.FromRoundedRect(Frame, cornerRadius: 15);
PhoneImage.Draw(PhoneImage.AccessibilityActivationPoint);
RoundImageCorner.AddClip();
IconImage.Draw(Frame);
UIColor.LightGray.SetStroke();
RoundImageCorner.LineWidth = 2;
RoundImageCorner.Stroke();
ResultImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
var documentsDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string jpgFilename = System.IO.Path.Combine(documentsDirectory, "app.png");
NSData image = ResultImage.AsPNG();
And it works beautifully with a border around my second image as well.
How can i adjust my code to successfully merge two images together and position the second image preferably like a CGRect?
Try this:
public Bitmap mergeBitmap(Bitmap backBitmap, Bitmap frontBitmap)
{
Bitmap bitmap = backBitmap.Copy(Bitmap.Config.Argb8888, true);
Canvas canvas = new Canvas(bitmap);
Rect baseRect = new Rect(0, 0, backBitmap.Width, backBitmap.Height);
Rect frontRect = new Rect(0, 0, frontBitmap.Width, frontBitmap.Height);
canvas.DrawBitmap(frontBitmap, frontRect, baseRect, null);
return bitmap;
}
Update:
Here is the DrawBitmap method's introduce. I add annotations in the method.
public Bitmap mergeBitmap(Bitmap backBitmap, Bitmap frontBitmap)
{
Bitmap bitmap = backBitmap.Copy(Bitmap.Config.Argb8888, true);
Canvas canvas = new Canvas(bitmap);
//this Rect will decide which part of your frontBitmap will be drawn,
//(0,0,frontBitmap.Width, frontBitmap.Height) means that the whole of frontBitmap will be drawn,
//(0,0,frontBitmap.Width/2, frontBitmap.Height/2) means that the half of frontBitmap will be drawn.
Rect frontRect = new Rect(0, 0, frontBitmap.Width, frontBitmap.Height);
//this Rect will decide where the frontBitmap will be drawn on the backBitmap,
//(200, 200, 200+ frontBitmap.Width, 200+frontBitmap.Height) means that
//the fontBitmap will drawn into the Rect which left is 200, top is 200, and its width and
//height are your frontBitmap's width and height.
//I suggest the baseRect's width and height should be your fontBitmap's width and height,
//or, your fontBitmap will be stretched or shrunk.
Rect baseRect = new Rect(200, 200, 200+ frontBitmap.Width, 200+frontBitmap.Height);
canvas.DrawBitmap(frontBitmap, frontRect, baseRect, null);
return bitmap;
}

Rotate Bitmap Xamarin

I am loading an image from a webserver, I want to rotate it if the orientation is wrong. I've seen how to do it from a bitmap on my phone, it takes the filename but nothing with an actual bitmap. I am using this to resize it, but unsure on the rotate part.
public Bitmap resizeAndRotate(Bitmap image, int width, int height)
{
Bitmap newImage= Bitmap.createScaledBitmap(image, newWidth, newHeight, true);
return newImage;
}
You can scale and rotate the bitmap in one call by passing a Android.Graphics.Matrix that include both the scale and rotation in the transformation to Bitmap.CreateBitmap:
public Bitmap resizeAndRotate(Bitmap image, int width, int height)
{
var matrix = new Matrix();
var scaleWidth = ((float)width) / image.Width;
var scaleHeight = ((float)height) / image.Height;
matrix.PostRotate(90);
matrix.PreScale(scaleWidth, scaleHeight);
return Bitmap.CreateBitmap(image, 0, 0, image.Width, image.Height, matrix, true);
}

How to set 3 components in one Horizontal field without adjusting editfield width

HorizontalFieldManager hfm = new HorizontalFieldManager();
this.add(hfm);
LabelField lblheight = new LabelField("Height");
EditField lField = new EditField() {
protected void layout(int width, int height) {
super.layout(width, height);
this.setExtent(200, this.getHeight());
}
public int getPreferredWidth() {
return 200;
}
};
Background editFieldBackground = BackgroundFactory
.createSolidBackground(0X00F7F7FF);
XYEdges edges = new XYEdges(5, 5, 5, 5);
Border border = BorderFactory.createRoundedBorder(edges, 0X00D6DBDE,
Border.STYLE_FILLED);
Bitmap switchOn = Bitmap.getBitmapResource("switch_left.png");
Bitmap switchOff = Bitmap.getBitmapResource("switch_right.png");
Bitmap switchOnFocus = Bitmap
.getBitmapResource("switch_left_focus.png");
Bitmap switchOffFocus = Bitmap
.getBitmapResource("switch_right_focus.png");
SwitchField sw = new SwitchField(switchOn, switchOff, switchOnFocus,
switchOffFocus, true);
lField.setBackground(editFieldBackground);
lField.setBorder(border);
hfm.add(lblheight);
hfm.add(lField);
hfm.add(sw);
I used the above code for setting 3 components in one horizontal field manager, but my issue is that here I set the width for the edittext.
So, is there any other option to display 3 components in the proper manner on all devices, without adjusting the width of the edittext?

Resources