Hi i'm using SelectPdf for Net Core.
First i generate a method for convert html content to pdf, then i configure my pdf options and finally i add footer in all pages, this work but I can't find any property to center my footer in the document.
public string _htmlStringToBase64(string htmlContent)
{
HtmlToPdf converter = new HtmlToPdf();
converter.Options.PdfPageSize = PdfPageSize.A4;
converter.Options.DisplayFooter = true;
converter.Footer.DisplayOnFirstPage = true;
converter.Footer.DisplayOnOddPages = true;
converter.Footer.DisplayOnEvenPages = true;
converter.Footer.Height = 80;
converter.Options.MarginLeft = 20;
converter.Options.MarginRight = 20;
converter.Options.MarginBottom = 30;
converter.Options.MarginTop = 15;
//this footer options
PdfHtmlSection footerHtml = new PdfHtmlSection(ExternalResources.FooterBase64Img);
footerHtml.AutoFitHeight = HtmlToPdfPageFitMode.ShrinkOnly;
footerHtml.CustomCSS = "text-align: center;"; //I tried that but it doesn't work
converter.Footer.Add(footerHtml);
var docPDF = converter.ConvertHtmlString(htmlContent);
MemoryStream stream = new MemoryStream();
docPDF.Save(stream);
docPDF.Close();
return Convert.ToBase64String(stream.ToArray())
}
As I said, the footer unfolds without problems but I need to center it
The constructor of the PdfHtmlSection class has several overloads, one of them allows the X, Y axes and the url of the image.
You can try assigning a value to the X axis which is the one that aligns horizontally, you can try the following code:
PdfHtmlSection footerHtml = new PdfHtmlSection(100, 0,ExternalResources.FooterBase64Img);
where the 100 value is the position offset from left
the next url show all properties: https://selectpdf.com/docs/Overload_SelectPdf_PdfHtmlSection__ctor.htm
Related
We are creating a PDF, it contains a large table, with headers (bold), sub-headers (non bold), rows and cells.
One of the requirements is to add an already existing PDF (can be multiple pages) inside the cell (under the sub-header). And this is where we are struggling. What we have tried so far:
Converting the PDF to a FormXObject, and adding that into the cell.
FontProvider fontProvider = new FontProvider();
ConverterProperties props = new ConverterProperties();
props.SetFontProvider(fontProvider);
var historyId = text.Replace("=Spectec_template", "");
var pdfTemplate = templateToPdfClient.GetPdf(historyId, "HI").Result;
PdfDocument srcDoc = new PdfDocument(new PdfReader(pdfTemplate.FileStream));
for (int i = 1; i < srcDoc.GetNumberOfPages() + 1; i++)
{
Cell newCell = new Cell(1, columnInfo.ColumnSpan).SetFont(PdfFontFactory.CreateFont(_fontName));
PdfPage origPage = srcDoc.GetPage(i);
Rectangle rect = origPage.GetPageSize();
PdfFormXObject pageCopy = origPage.CopyAsFormXObject(pdf);
Image image = new Image(pageCopy);
image.SetBorder(Border.NO_BORDER);
image.SetMaxWidth(UnitValue.CreatePercentValue(100));
image.SetMaxHeight(UnitValue.CreatePercentValue(50));
Div div = new Div();
div.Add(image.SetMaxWidth(UnitValue.CreatePercentValue(100)));
newCell.Add(div);
newCell = ApplyLayoutsToCell(newCell, cellStyles);
table.AddCell(newCell);
}
But the end result is that we only see 1 page and it's completely overlapping at the end of the page.
When we set image.SetAutoScale(true); the PDF is visible but it's extremely small.
When adding the Image to a Paragraph instead of a DIV the PDF pages display next to each other.
Any ideas, suggestions?
Thank you
Thanks to #mkl and #KJ I figured this out.
I ended up adding each PDF page in a new Cell, and setting the scaling of image to 0.6f.
for (int i = 1; i < srcDoc.GetNumberOfPages() + 1; i++)
{
newCell = new Cell(1, columnInfo.ColumnSpan).SetFont(PdfFontFactory.CreateFont(_fontName));
PdfPage origPage = srcDoc.GetPage(i);
PdfFormXObject pageCopy = origPage.CopyAsFormXObject(pdf);
Image image = new Image(pageCopy);
image.SetBorder(Border.NO_BORDER);
image.Scale(0.6f, 0.6f);
newCell.Add(image);
newCell = ApplyLayoutsToCell(newCell, cellStyles);
table.AddCell(newCell);
}
I have been using iText7 latest .net package to create watermark annotation on pdf document. I see for most of the noraml pdf files watermark is stamped properly in the desired position however for some special 3d(dimensional) pdf files the watermark is strangely not appearning properly, what I believe that some part of the watermark is gettting cut by the other 3d layers. Here is the screenshot of distortion experienced. I am trying to understand and solve this problem but unable to get any clue. Can someone help? Here is the sample pdf in case you want to take a look.
Here is the very short relevant code which I am using to embed watermark.
float watermarkTrimmingRectangleWidth = 500;
float watermarkTrimmingRectangleHeight = 500;
float formWidth = 500;
float formHeight = 500;
float formXOffset = -40;
float formYOffset = 0;
float xTranslation = 50;
float yTranslation = 25;
double rotationInRads = Math.PI / 4.2;
PdfDocument pdfDoc = new PdfDocument(new PdfReader(filePath), new PdfWriter(destinationfile));
var numberOfPages = pdfDoc.GetNumberOfPages();
PdfPage page = null;
for (var i = 1; i <= numberOfPages; i++)
{
page = pdfDoc.GetPage(i);
Rectangle ps = page.GetPageSize();
float bottomLeftX = ps.GetWidth() / 2 - watermarkTrimmingRectangleWidth / 2;
float bottomLeftY = ps.GetHeight() / 2 - watermarkTrimmingRectangleHeight / 2;
Rectangle watermarkTrimmingRectangle = new Rectangle(bottomLeftX, bottomLeftY, ps.GetWidth(), watermarkTrimmingRectangleHeight);
PdfWatermarkAnnotation watermark = new PdfWatermarkAnnotation(watermarkTrimmingRectangle);
AffineTransform transform = new AffineTransform();
transform.Translate(xTranslation, yTranslation);
transform.Rotate(rotationInRads);
PdfFixedPrint fixedPrint = new PdfFixedPrint();
watermark.SetFixedPrint(fixedPrint);
Rectangle formRectangle = new Rectangle(formXOffset, formYOffset, formWidth, formHeight);
//Observation: font XObject will be resized to fit inside the watermark rectangle
PdfFormXObject form = new PdfFormXObject(formRectangle);
PdfExtGState gs1 = new PdfExtGState().SetFillOpacity(0.8f).SetStrokeOpacity(1.5f);
PdfCanvas canvas = new PdfCanvas(form, pdfDoc);
float[] transformValues = new float[6];
transform.GetMatrix(transformValues);
canvas.SaveState()
.BeginText().SetExtGState(gs1)
.SetTextMatrix(transformValues[0], transformValues[1], transformValues[2], transformValues[3], transformValues[4], transformValues[5])
.SetFontAndSize(font, fontSize)
.ShowText("Restricted Internal")
.EndText()
.RestoreState();
canvas.Release();
watermark.SetAppearance(PdfName.N, new PdfAnnotationAppearance(form.GetPdfObject()));
watermark.SetFlags(PdfAnnotation.READ_ONLY);
page.AddAnnotation(watermark);
}
page?.Flush();
pdfDoc.Close();
Your watermark annotation is drawn on top of some form fields.
Because the display order for page elements is page content, annotations, form fields, your annotation appearance is blocked by the form fields.
I have a task of adding watermarks to a lot of existing PDFs and I use iText7 for this in C#. The result can be seen in this picture (Blank pdf used)
It renders fine everywhere and prints perfectly from both Chrome and Edge. However, when printed from Adobe Acrobat Reader, this is what happens:
Anyone knowing more about PDF than I, who can help with this issue? I am using version 7.1.13 of iText.
The test pdf is available here:
https://potanteststorage.blob.core.windows.net/pdf/Test.pdf
C# Code:
public static void AddProductionWatermarks(string sourceFile, string destinationPath)
{
float watermarkTrimmingRectangleWidth = 75;
float watermarkTrimmingRectangleHeight = 250;
//Custom text
float formWidth = 75;
float formHeight = 250;
float formXOffset = 0;
float formYOffset = 0;
float xTranslation = 50;
float yTranslation = 0;
double rotationInRads = Math.PI / 2;
PdfFont font = PdfFontFactory.CreateFont(StandardFonts.TIMES_ROMAN);
float fontSize = 12;
PdfDocument pdfDoc = new PdfDocument(new PdfReader(sourceFile), new PdfWriter(destinationPath));
var numberOfPages = pdfDoc.GetNumberOfPages();
PdfPage page = null;
for (var i = 1; i <= numberOfPages; i++)
{
page = pdfDoc.GetPage(i);
Rectangle ps = page.GetPageSize();
//PRODUCTION watermark -------------------------------------------------------
float prodBottomLeftX = -20;
float prodBottomLeftY = ps.GetHeight() / 2;
Rectangle prodWatermarkTrimmingRectangle = new Rectangle(prodBottomLeftX, prodBottomLeftY, watermarkTrimmingRectangleWidth, watermarkTrimmingRectangleHeight);
PdfWatermarkAnnotation prodWatermark = new PdfWatermarkAnnotation(prodWatermarkTrimmingRectangle);
AffineTransform transform2 = new AffineTransform();
transform2.Translate(xTranslation, yTranslation);
transform2.Rotate(rotationInRads);
PdfFixedPrint fixedPrint2 = new PdfFixedPrint();
prodWatermark.SetFixedPrint(fixedPrint2);
PdfFormXObject form2 = new PdfFormXObject(formRectangle);
PdfCanvas canvas2 = new PdfCanvas(form2, pdfDoc);
transform2.GetMatrix(transformValues);
canvas2.SaveState()
.BeginText().SetColor(new DeviceRgb(255, 36, 0), true)
.SetTextMatrix(transformValues[0], transformValues[1], transformValues[2], transformValues[3], transformValues[4], transformValues[5])
.SetFontAndSize(font, fontSize)
.ShowText("PRODUCTION")
.EndText()
.RestoreState();
canvas2.Release();
prodWatermark.SetAppearance(PdfName.N, new PdfAnnotationAppearance(form2.GetPdfObject()));
prodWatermark.SetFlags(PdfAnnotation.PRINT);
page.AddAnnotation(prodWatermark);
}
page?.Flush();
pdfDoc.Close();
}
You use FixedPrint dictionaries in your annotations:
PdfFixedPrint fixedPrint2 = new PdfFixedPrint();
prodWatermark.SetFixedPrint(fixedPrint2);
This additional entry for Watermark annotations specifies how this annotation shall be drawn relative to the dimensions of the target media during printing. Thus, you ask for special treatment of the watermark during printing.
So if you indeed want special treatment during printing (merely not the current treatment), simply set the PdfFixedPrint properties accordingly.
If you don't want any special treatment during printing, don't set a PdfFixedPrint object at all.
I am using System.Web.UI.DataVisualization.Charting 4.0 in my ASP.NET app. It works fine in some environments thusly:
var chart = new Chart();
// Define the chart area
Grid grid = new Grid();
ChartArea chartArea = new ChartArea();
[... setting lots of chartArea properties here...]
ChartArea3DStyle areaStyle = new ChartArea3DStyle(chartArea);
chart.ChartAreas.Add(chartArea);
[... more ...]
[... build Series, Legends, etc here ...]
chart.SaveImage("c:\fakepath\myreport.png", ChartImageFormat.Png);
I have omitted 95% of the code for building up the chart (very complex logic, lots of looping through complex data structures), which is then saved to disk, and rendered from the aspx page like this:
<img src="http://fakeserver/fakepath/myreport.png">
In some customer environments this approach won't work because the IIS process doesn't have permission to write to the local disk, and they don't want to open that up. Additionally it's not very scalable when multiple users are viewing (and generating) charts, which will be different for each user.
How can I generate this chart purely in memory?
I have been looking at the ChartHttpHandler class, but can't find relevant code samples. What would be ideal is if I could build the chart exactly as shown above, but instead of saving to disk, I could store the chart in-memory (Cache? Session?) in such a way that I could point my <img> tag in the aspx page to that in-memory data and the image will render on the page. I don't want to resort to declarative aspx chart building because the logic is too complex and needs to all be done in c# code. I also can't use any approach in which the chart image is written to disk.
How is it done?
I made a complete example including the creation of a chart for other users. But basically you save the chart image to a stream instead of a file.
using (MemoryStream stream = new MemoryStream())
{
chart.SaveImage(stream, ChartImageFormat.Png);
}
I created a Generic Handler to display the images in a browser. But once the image is in a stream you can do much more, like putting it in a PDF or email message.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI.DataVisualization.Charting;
using System.Web.UI.WebControls;
namespace YourNameSpace
{
public class Handler1 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
//needed to generate random numbers
Random rnd = new Random();
//select 12 random numbers between 1 and 50 for column chart
int[] yRange1 = Enumerable.Range(1, 50).OrderBy(i => rnd.Next()).Take(12).ToArray();
//select 12 random numbers between 1 and 25 for line chart
int[] yRange2 = Enumerable.Range(0, 25).OrderBy(i => rnd.Next()).Take(12).ToArray();
//select all the month names for the labels
string[] xLabels = Enumerable.Range(1, 12).Select(i => DateTimeFormatInfo.CurrentInfo.GetMonthName(i)).ToArray();
//create the chart
Chart chart = new Chart();
//add the bar chart series
Series series = new Series("ChartBar");
series.ChartType = SeriesChartType.Column;
chart.Series.Add(series);
//add the line chart series
Series series2 = new Series("ChartLine");
series2.ChartType = SeriesChartType.Line;
series2.Color = System.Drawing.Color.Purple;
series2.BorderWidth = 2;
chart.Series.Add(series2);
//define the chart area
ChartArea chartArea = new ChartArea();
Axis yAxis = new Axis(chartArea, AxisName.Y);
Axis xAxis = new Axis(chartArea, AxisName.X);
//add the data and define color
chart.Series["ChartBar"].Points.DataBindXY(xLabels, yRange1);
chart.Series["ChartLine"].Points.DataBindXY(xLabels, yRange2);
chart.Series["ChartBar"].Color = System.Drawing.Color.Green;
chart.ChartAreas.Add(chartArea);
//set the dimensions of the chart
chart.Width = new Unit(600, UnitType.Pixel);
chart.Height = new Unit(400, UnitType.Pixel);
//create an empty byte array
byte[] bin = new byte[0];
//save the chart to the stream instead of a file
using (MemoryStream stream = new MemoryStream())
{
chart.SaveImage(stream, ChartImageFormat.Png);
//write the stream to a byte array
bin = stream.ToArray();
}
//send the result to the browser
context.Response.ContentType = "image/png";
context.Response.AddHeader("content-length", bin.Length.ToString());
context.Response.AddHeader("content-disposition", "attachment; filename=\"chart.png\"");
context.Response.OutputStream.Write(bin, 0, bin.Length);
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
To display the image in a browser simply point the src of the img elment to the handler.
<img src="Handler1.ashx" width="600" height="400" />
An alternative is to embed the chart directly into the html using the a base64 encoded source on your image tag (via src=data:image/base64); this also has the advantage of not requiring any further http requests to get the images from the web server.
e.g.
<img src='data:image/png;base64,src='data:image/png;base64,[base64data==]'>
To do this a number of things are required.
A controller to get the image data as an action result:
public ActionResult SomeChart(SomeViewModel model, int width, int height)
{
model.ChartKey = "SomeChart";
return Content(getChartImage(() => CreateSomeChart(model, width, height), model, width, height));
}
This uses a method to create the chart as follows:
private Chart CreateSomeChart(SomeViewModel vm, int width, int height)
{
int Id = vm.Id;
System.Web.UI.DataVisualization.Charting.Chart Chart1 = new System.Web.UI.DataVisualization.Charting.Chart();
// usual code to draw a chart here...
return Chart1;
}
Code in your razor view to get the chart; in my view model I have a list of charts which are just action result methods in the controller that are to be rendered. Using render action will happen before the html is sent to the browser and result in the chart being inlined in a base64 encoded manner.
It is the public ActionResult SomeChart controller that should be called by the Html.RenderAction method, and for this example Model.ChartList[] = {"SomeChart"}
if (Model.ChartList.Count() > 1)
{
xs = 450;
ys = 300;
}
foreach(var chart in Model.ChartList)
{
Html.RenderAction(chart, "SomeController", new { model = Model, width = xs, height = ys });
}
The final bit that ties it all together is the getChartImage method.
private string getChartImage(Func<Chart> getChart, object routevals, int width, int height, string chartKey="")
{
string name = "Chart";
if (routevals is SomeViewModel)
{
var cvm = routevals as SomeViewModel;
chartKey = cvm.ChartKey;
name = cvm.ChartKey;
}
using (var stream = new MemoryStream())
{
var Chart1 = getChart();
Chart1.ImageType = ChartImageType.Png;
Chart1.Palette = ChartColorPalette.BrightPastel;
Chart1.SuppressExceptions = true;
// Set chart custom palette
Chart1.Palette = ChartColorPalette.None;
Chart1.PaletteCustomColors = Dashboard.Support.ChartPalettes.ColorsBigList;
Chart1.Width = width;
Chart1.Height = height;
Chart1.RenderType = RenderType.ImageTag;
Chart1.BackColor = Color.White;
Chart1.BackImageAlignment = ChartImageAlignmentStyle.BottomLeft;
Chart1.BorderSkin.SkinStyle = BorderSkinStyle.None;
Chart1.BorderSkin.BorderWidth = 0;
//Chart1.BackImageTransparentColor
Chart1.BorderColor = System.Drawing.Color.FromArgb(26, 59, 105);
Chart1.BorderlineDashStyle = ChartDashStyle.Solid;
Chart1.BorderWidth = 0;
Chart1.SaveImage(stream, ChartImageFormat.Png);
string encoded = Convert.ToBase64String(stream.ToArray());
if (Request != null && Request.Browser != null && Request.Browser.Browser == "IE" && Request.Browser.MajorVersion <= 8)
{
/*
* IE8 can only handle 32k of data inline so do it via the cached getChart
* method - i.e. store the chart in the cache and return a normal image.
*/
if (encoded.Length > 32000)
{
StringBuilder result = new StringBuilder();
if (string.IsNullOrEmpty(chartKey))
chartKey = String.Format("{0}{1}", name, Guid.NewGuid());
System.Web.Helpers.WebCache.Set(chartKey, stream.ToArray());
result.Append(String.Format("<img width='{0}' height='{1}' src='{3}' alt='' usemap='#ImageMap{2}'>", width, height, name,
Url.Action("getChart", new { key = chartKey })));
return result.ToString() + Chart1.GetHtmlImageMap("ImageMap" + name); ;
}
}
/*
* render using data inline for speed
*/
string img = String.Format("<img width='{0}' height='{1}' src='data:image/png;base64,{{0}}' alt='' usemap='#ImageMap{2}'>", Chart1.Width, Chart1.Height, name);
return String.Format(img, encoded) + Chart1.GetHtmlImageMap("ImageMap" + name);
}
}
I have a problem with my program. I need to add a picture in the footer of a word document.
I have 2 functions, replacing bookmarked text and add images. The replace bookmarked text works. add images doesnt work, i searched for days on stackoverflow, but i cant find any sollution. I hope someone can help me out.
private static void AddImages(Document wordDoc, string imagePath){
var sec = wordDoc.Application.Selection.Sections[1];
var ft = sec.Footers[WdHeaderFooterIndex.wdHeaderFooterPrimary];
var rngFooter = ft.Range;
object oRange = rngFooter;
var autoScaledInlineShape = ft.Shapes.AddPicture(imagePath);
var scaledWidth = autoScaledInlineShape.Width;
var scaledHeight = autoScaledInlineShape.Height;
autoScaledInlineShape.Delete();
// Create a new Shape and fill it with the picture
var newShape = wordDoc.Shapes.AddShape(1, 0, 0, scaledWidth, scaledHeight);
newShape.Fill.UserPicture(imagePath);
// Convert the Shape to an InlineShape and optional disable Border
var finalInlineShape = newShape.ConvertToInlineShape();
finalInlineShape.Line.Visible = Microsoft.Office.Core.MsoTriState.msoFalse;
// Cut the range of the InlineShape to clipboard
finalInlineShape.Range.Cut();
// And paste it to the target Range
ft.Paste();
}
Found sollution
private static void FindAndReplaceImages(Document wordDoc, string imagePath){
var sec = wordDoc.Application.Selection.Sections[1];
foreach (Section wordSection in wordDoc.Sections)
{
var footer = sec.Footers[WdHeaderFooterIndex.wdHeaderFooterPrimary];
var footerImage = footer.Shapes.AddShape(1, 0, 0, 594, 280);
footerImage.Fill.UserPicture(imagePath);
footerImage.WrapFormat.Type = WdWrapType.wdWrapThrough;
footerImage.WrapFormat.AllowOverlap = -1;
footerImage.WrapFormat.Side = WdWrapSideType.wdWrapBoth;
footerImage.RelativeHorizontalPosition = WdRelativeHorizontalPosition.wdRelativeHorizontalPositionPage;
footerImage.RelativeVerticalPosition = WdRelativeVerticalPosition.wdRelativeVerticalPositionPage;
footerImage.Top = (float)561.2;
}
}