I'm currently creating a PDF with PdfSharp which mostly consists of text and some images.
The text elements have different colors. My problem is that as soon as I use a different color than the color I started with, the text is not visible in the resulting PDF (e.g. I start with black text, switch to a red text, the red text is not visible). All text elements are in the resulting PDF (I can select them), but the red elements are invisible.
So here is the code:
// Create a new PDF document with one page
var document = new PdfDocument();
var page = document.AddPage();
page.Width = 800;
page.Height = 600;
var defaultFont = new XFont("Arial", 12, XFontStyle.Regular, new XPdfFontOptions(PdfFontEmbedding.Always));
var gfx = XGraphics.FromPdfPage(page);
// black text
gfx.DrawString("black", defaultFont, XBrushes.Black, new XRect(x, y, width, height), XStringFormats.Center);
// red text
gfx.DrawString("red", defaultFont, XBrushes.Red, new XRect(x2, y2, width2, height2), XStringFormats.Center);
I've already found a solution (re-creating the XGraphics object) but it's quiete messy because it needs to be called after each color change:
// ...
// black text
gfx.DrawString("black", defaultFont, XBrushes.Black, new XRect(x, y, width, height), XStringFormats.Center);
// disposing the old graphics context and creating a new one seems to help
gfx.Dispose();
gfx = XGraphics.FromPdfPage(page);
// red text
gfx.DrawString("red", defaultFont, XBrushes.Red, new XRect(x2, y2, width2, height2), XStringFormats.Center);
I guess there is a better solution, but I couldn't find one yet.
Edit
As suggested in this answer, I wanted to create a SSCCE. During the creation I found the actual bug. Instead of XBrushes.Red I used an own defined XBrush, but didn't mention it in the above code snippet, because I thought it was unnecessary.
As already mentioned in the last section of the question, I used an own defined brush instead of XBrushes.Red.
I defined it the following way:
XBrush redBrush = new XSolidBrush(new XColor {R = 207, G = 0, B = 44});
This way the brush only worked after I disposed the graphics object and created a new one. But after some googling I found the correct way to define a brush:
XBrush redBrush = new XSolidBrush(XColor.FromArgb(207, 0, 44));
I tried to replicate your problem using your code snippet and PDFsharp version 1.32. I used VS Express 2013 which automatically converted all projects to .NET 4.5.
I tried both builds (GDI+ and WPF) and all colours worked fine for me.
So instead of just a code snippet you should provide an SSCCE.
See also:
http://forum.pdfsharp.net/viewtopic.php?p=2094#p2094
Related
This is the code I use to draw a line.
double[] lineArray = annotation.getAsArray(PdfName.L).asDoubleArray();
double x1 = lineArray[0] - rect.getAsNumber(0).doubleValue();
double y1 = lineArray[1] - rect.getAsNumber(1).doubleValue();
double x2 = lineArray[2] - rect.getAsNumber(0).doubleValue();
double y2 = lineArray[3] - rect.getAsNumber(1).doubleValue();
cs.moveTo(x1, y1);
cs.lineTo(x2, y2);
Where cs is PdfAppearance, annotation is PdfAnnotation and rect is
PdfArray rect = annotation.getAsArray(PdfName.RECT);
This works ok in portrait. but come, landscape mode e.g. 270 rotation, the coordinates get misplaced. I also did a rotate via cs.transform() so my 0,0 would be rotated but it does nothing.
Any idea what could be lacking?
The source
This answer covers the updated source code provided by the OP via a google drive link in comments:
public static void main(String[] args) throws Exception {
PdfReader reader = new PdfReader("src");
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("dest"));
Rectangle location = new Rectangle(544.8f, 517.65f, 663f, 373.35f);
PdfArray lineEndings = new PdfArray();
lineEndings.add(new PdfName("None"));
lineEndings.add(new PdfName("None"));
PdfAnnotation stamp = PdfAnnotation.createLine(stamper.getWriter(), location,
"comment", 550.05f, 510.9f, 656.25f, 378.6f);
stamp.put(new PdfName("LE"), lineEndings);
stamp.put(new PdfName("IT"), new PdfName("Line"));
stamp.setBorderStyle(new PdfBorderDictionary(1, PdfBorderDictionary.STYLE_SOLID));
stamp.setColor(PdfGraphics2D.prepareColor(Color.RED));
stamp.put(PdfName.ROTATE, new PdfNumber(270));
stamper.addAnnotation(stamp, 1);
addAppearance(stamper, stamp, location);
stamper.close();
reader.close();
}
private static void addAppearance(PdfStamper stamper, PdfAnnotation stamp, Rectangle location) {
PdfContentByte cb = stamper.getOverContent(1);
PdfAppearance app = cb.createAppearance(location.getWidth(), location.getHeight());
PdfArray rect = stamp.getAsArray(PdfName.RECT);
Rectangle bbox = app.getBoundingBox();
double[] lineArray = stamp.getAsArray(PdfName.L).asDoubleArray();
double x1 = lineArray[0] - rect.getAsNumber(0).doubleValue();
double y1 = lineArray[1] - rect.getAsNumber(1).doubleValue();
double x2 = lineArray[2] - rect.getAsNumber(0).doubleValue();
double y2 = lineArray[3] - rect.getAsNumber(1).doubleValue();
app.moveTo(x1, y1);
app.lineTo(x2, y2);
app.stroke();
stamp.setAppearance(PdfName.N, app);
}
No appearance
The first observation when viewing the resulting PDF in Chrome is, as the OP put it in a comment:
nothing shows up
Inspecting the PDF the cause is clear: The annotation has no appearance stream. Thus, limited PDF viewers which only can show annotations by their appearance stream, not by their descriptive values, like the integrated viewer in Chrome don't show it.
This is due to the order in which the OP calls iText functionalities in his code:
[... create annotation object stamp ...]
stamper.addAnnotation(stamp, 1);
addAppearance(stamper, stamp, location);
So he first adds the annotation to the PDF by means of stamper.addAnnotation and thereafter creates an appearance and attaches it to the stamp object.
This order is wrong. In context with iText one has to be aware that the library attempts to write additions as early as possible to the output stream to reduce its memory footprint. (This by the way is one of the important features of iText in the context of server applications in which multiple PDFs may have to be processed in parallel.)
So already during stamper.addAnnotation(stamp, 1) the annotation is written to the output stream, and as it has no appearance yet, the annotation in the output stream is without appearance. The later addAppearance call only adds an appearance to the in-memory representation of the annotation which won't be serialized anymore.
Changing the order to
[... create annotation object stamp ...]
addAppearance(stamper, stamp, location);
stamper.addAnnotation(stamp, 1);
results in a PDF with a line drawn. Unfortunately not at the desired position, but that is another problem.
Wrong position
The reason why the line is both in the wrong location and has the wrong direction, is based in a feature of iText which has already been a topic in this answer and in this answer:
For rotated pages iText attempts to lift the burden of adding the rotation and translation to page content required to draw upright text and have the coordinate system origin in the lower left of the page of the users' shoulders, so that the users don't have to deal with page rotation at all. Consequently, it also does so for annotations.
As you already have the actual coordinates to use, this "help" by iText damages your annotation. As discussed in those other answers, there unfortunately is no explicit switch to turn off that mechanism; there is an easy work-around, though: before your manipulation simply remove the page rotation entry, and afterwards add it back again:
PdfReader reader = ...;
PdfStamper stamper = ...;
// hide the page rotation
PdfDictionary pageDict = reader.getPageN(1);
PdfNumber rotation = pageDict.getAsNumber(PdfName.ROTATE);
pageDict.remove(PdfName.ROTATE);
Rectangle location = new Rectangle(544.8f, 517.65f, 663f, 373.35f);
PdfArray lineEndings = new PdfArray();
lineEndings.add(new PdfName("None"));
lineEndings.add(new PdfName("None"));
PdfAnnotation stamp = PdfAnnotation.createLine(stamper.getWriter(), location,
"comment", 550.05f, 510.9f, 656.25f, 378.6f);
stamp.put(new PdfName("LE"), lineEndings);
stamp.put(new PdfName("IT"), new PdfName("Line"));
stamp.setBorderStyle(new PdfBorderDictionary(1, PdfBorderDictionary.STYLE_SOLID));
stamp.setColor(PdfGraphics2D.prepareColor(Color.RED));
stamp.put(PdfName.ROTATE, new PdfNumber(270));
addAppearance(stamper, stamp, location);
stamper.addAnnotation(stamp, 1);
// add page rotation again if required
if (rotation != null)
pageDict.put(PdfName.ROTATE, rotation);
stamper.close();
reader.close();
This appears to create the annotation appearance as required.
I am using pdfbox to add a line to pdf file. but the text i am adding is reversed.
File file = new File(filePath);
PDDocument document = PDDocument.load(file);
PDPage page = document.getPage(0);
PDPageContentStream contentStream = new PDPageContentStream(document, page,PDPageContentStream.AppendMode.APPEND,true);
int stampFontSize = grailsApplication.config.pdfStamp.stampFontSize ? grailsApplication.config.pdfStamp.stampFontSize : 20
contentStream.beginText();
contentStream.setFont(PDType1Font.TIMES_ROMAN, stampFontSize);
int leftOffset = grailsApplication.config.pdfStamp.leftOffset ? grailsApplication.config.pdfStamp.leftOffset : 10
int bottomOffset = grailsApplication.config.pdfStamp.bottomOffset ? grailsApplication.config.pdfStamp.bottomOffset : 20
contentStream.moveTextPositionByAmount(grailsApplication.config.xMove,grailsApplication.config.yMove)
contentStream.newLineAtOffset(leftOffset, bottomOffset)
String text = "i have added this line...!!!!";
contentStream.showText(text);
contentStream.endText();
contentStream.close();
document.save(new File(filePath));
document.close();
byte[] pdfData;
pdfData = Files.readAllBytes(file.toPath());
return pdfData;
i tried using moveTextPositionByAmount method but this does not seem to have any effect on text. why is my text reversed and how can i set it to correct orientation.
Your code is not causing the mirrored output by itself, so the cause must be inside the PDF you are stamping. Unfortunately you did not provide the PDF in question, so we have to guess here.
Most likely the issue is caused by the pre-existing page content having set the current transformation matrix to a mirroring affine transformation without resetting it at the end.
If that indeed is the case, PDFBox provides an easy work-around:
You construct your PDPageContentStream like this:
PDPageContentStream contentStream = new PDPageContentStream(document, page,PDPageContentStream.AppendMode.APPEND,true);
There is another constructor accepting an additional boolean argument. If you use that constructor setting the additional argument to true, PDFBox attempts to reset the graphics state of the content:
PDPageContentStream contentStream = new PDPageContentStream(document, page,PDPageContentStream.AppendMode.APPEND,true,true);
Beware: If this indeed fixes the issue, the coordinates and offsets you currently use rely on the transformation matrix being changed as it is. In that case you will have to update them accordingly.
Alternatively introducing a counter-mirroring may help, e.g. by setting the text matrix like this at the start of each of your text objects:
contentStream.beginText();
contentStream.setTextMatrix(new Matrix(1f, 0f, 0f, -1f, 0f, 0f));
Thereafter all y coordinate changes need to be negated, in particular the second argument of contentStream.moveTextPositionByAmount and contentStream.newLineAtOffset.
(By the way, moveTextPositionByAmount and newLineAtOffset do the same, the former merely is the deprecated variant, so you might want to use the latter in both cases.)
I'm trying to develop an application to watermark PDF's. I'm having an issue with saving the PDF document in the correct format.
I've noticed that some documents when pulled in will read the page size as one thing but when I go to save the document part of it will be cut off. For example I have a document saved in landscape at 792x612 pts which correlates to a letter size. However when saved in letter it cuts part of the page off. I noticed that if I save the document in A1 that the entire document will then be present. Here is my code:
Dim gfx As XGraphics = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Append)
gfx.RotateTransform(-90)
Dim font As XFont = New XFont("Times New Romans", 6, XFontStyle.Bold)
Dim tf As XTextFormatter = New XTextFormatter(gfx)
Dim rect As XRect = New XRect(-550, 500, 250, 150)
gfx.DrawRectangle(XBrushes.Transparent, rect)
tf.DrawString(Text, font, XBrushes.Red, rect)
page.Size = PdfSharp.PageSize.A1
page.Orientation = PdfSharp.PageOrientation.Landscape
Doc.save(strFileSaveLocation)
Is there any reason why this could be happening? I am reading the page.height and page.width and confirming these to be correct before continuing.
There is a known bug in PDFsharp when it comes to handling pages in landscape format. This bug is present up to PDFsharp 1.50 beta 3b, but should be fixed with the next release.
See discussion in the PDFsharp forum:
http://forum.pdfsharp.net/viewtopic.php?p=9591#p9591
Suggested fix in PdfPage.cs:
internal PdfPage(PdfDictionary dict)
: base(dict)
{
// Set Orientation depending on /Rotate.
//int rotate = Elements.GetInteger(InheritablePageKeys.Rotate);
//if (Math.Abs((rotate / 90)) % 2 == 1)
// _orientation = PageOrientation.Landscape;
}
If you do not want to modify PDFsharp, maybe try the workaround from the first post:
http://forum.pdfsharp.net/viewtopic.php?p=9587#p9587
For others running into the same issue as me, the answer is to simply set the orientation to portrait. This for some reason will trigger the document to save correctly. The answer by PDFsharp Novice will allow you to fix your code to some extent, but if you're like me and need to know the orientation in order to process the page the solution is setting the orientation back to portrait. You may need to reset page width and height also.
Code:
page.Orientation = PdfSharp.PageOrientation.Portrait
page.Width = dblPageWidth
page.Height = dblPageHeight
This tool I wrote in Visual Basic 2010 should add an author text to images. The user is able to set the font opacity and position. To make things easier I wanted some position presets as one can see in the bottom right corner. The calculation I am using is (bottom right in this case:
Dim textSize As Size = TextRenderer.MeasureText(tagString + curText, curFont)
tmpPos = New Point(srcImg.Width - textSize.Width - 10, srcImg.Height - textSize.Height - 10)
As you can see this works perfectly for this example picture. Where as on some the text just clips out.
First One: 1024x768 | Detected Font Size: 680x72
Second One: 1688x1125 | Detected Font Size: 680x72
I suspect this has something to do with the aspect ratio of the images but I do not know how to fix it.
The text is drawn like that:
brush = New SolidBrush(color.FromArgb(alpha, color))
gr = Graphics.FromImage(editImg)
gr.DrawString(tagString + text, font, brush, pos)
HauptBild.Image = editImg
I found this http://www.codeproject.com/Articles/20923/Mouse-Position-over-Image-in-a-PictureBox and it answered my questions.
is this problem only occuring in your preview or also in the converted File? Please post the Code how you save the New Image. I think you have Set a sizemode in your picturebox which is the Problem. Try it without the sizemode.
Will be better to see more your code, but, as i understand by TextRenderer class it is System.Windows.Forms. Just do not use Graphics, created from control (i suppose it is pictureBox with sizemode:Zoom), use Graphics, created from your image instead.
Here is code (sorry, C#), which loads image from file, draws text starting from the same coordinate and places on puctureBox1. Text always starts from Point(100,100).
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.Filter = "Image files|*.jpeg;*.png;*.jpg;*.gif;*.bmp";
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
Bitmap orig=(Bitmap)Bitmap.FromFile(openFileDialog1.FileName);
//workaround for images with color table, see remarks here https://msdn.microsoft.com/en-us/library/system.drawing.graphics.fromimage(v=vs.110).aspx
Bitmap bmp=orig.Clone(new Rectangle(0, 0, orig.Width, orig.Height), System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
Graphics g = Graphics.FromImage(bmp);
g.DrawString("hello", new Font(this.Font.FontFamily,30,FontStyle.Bold ) , new System.Drawing.SolidBrush(System.Drawing.Color.Yellow ), new Point(100, 100));
this.pictureBox1.Image = bmp;
orig.Dispose();
}
catch (Exception ex)
{
MessageBox.Show("Something goes wrong: " + ex.Message+ "\\n"+ ex.StackTrace );
}
}
I am using OxyPlot to export plots.
When I export them, I want to add a footer to these plots with information like the path it is saved, a time-stamp, and so on...
Right now I am doing this by creating an extra X-axis on a different position-tier and then setting the sizes of all ticks and labels to zero except for the title font-size.
This works, but as you might imagine, this is quite hacky and does not look that good (as you cannot set for example the aligning).
So my question is, is there a possibility to add such a footer to the exported plot?
EDIT:
var xAxis = new OxyPlot.Axes.LinearAxis
{
Position = AxisPosition.Bottom,
PositionTier = 1,
Title = "Footer: i.e. path to my file",
MinorTickSize = 0.0,
MajorTickSize = 0.0,
FontSize = 0.0,
TitleFontSize = 12,
AxisDistance = 10
};
This is the workaround I mentioned.
I create an axis at position-tier 1, which is below the first one and then disable all visuals of it except the title.
And in the end I add it to my plotmodel pm.Axes.Add(xAxis).
To export my plotmodel I use PdfExporter like this:
using (var stream = File.Create(testFile.pdf))
{
PdfExporter.Export(pm, stream, 800, 500);
}
Greetings
Chriz
Just had to do the same thing with my project and thought I'd share how I managed it for anyone else in need of a footer.
I couldn't find any built in OxyPlot methods to add a header or footer but if you use OxyPlot.PDF it's built on top of PDFSharp and you have more options to customize your PDF export.
Remove any previous reference to OxyPlot.Pdf in your project.
Download OxyPlot.Pdf source code from: https://github.com/oxyplot/oxyplot
In your project in VS, right click your solution in 'Solution Explorer' and Add Existing Project.
Navigate to the downloaded source code and add OxyPlot.Pdf.csproj
Right click your project and Add Reference
Select 'Projects' on the left and check the box for OxyPlot.Pdf on the right. Hit OK.
Check that it's working by building and running project.
Go to PdfRenderContext.cs file and find the PdfRenderContext method near the top.
Add the code below then build and run your project.
This code creates a MigraDoc Document and then merges it with the OxyPlot PdfDocument.
public PdfRenderContext(double width, double height, OxyColor background)
{
//*** Original Code - Don't change **//
this.RendersToScreen = false;
this.doc = new PdfDocument();
var page = new PdfPage { Width = new XUnit(width), Height = new XUnit(height) };
this.doc.AddPage(page);
this.g = XGraphics.FromPdfPage(page);
if (background.IsVisible())
{
this.g.DrawRectangle(ToBrush(background), 0, 0, width, height);
}
//*** New Code to add footer **//
Document myDoc = new Document();
Section mySection = myDoc.AddSection();
Paragraph footerParagraph = mySection.Footers.Primary.AddParagraph();
footerParagraph.AddText(DateTime.Now.ToShortDateString());
footerParagraph.Format.Alignment = ParagraphAlignment.Right;
MigraDoc.Rendering.DocumentRenderer docRenderer = new MigraDoc.Rendering.DocumentRenderer(myDoc);
docRenderer.PrepareDocument();
docRenderer.RenderObject(g, XUnit.FromInch(9.5), XUnit.FromInch(8), "1in", footerParagraph);
}
When you export the PDF a date stamp is now added to the lower right corner of the PDF. Note that I was working with landscape 8.5x11 inch paper so you may need to change position if you don't see it on the plot. Upper left corner is 0,0. Once it's working, build the OxyPlot.Pdf project to create the dll and then you can add it as a reference to your project and remove the source code.
Result: