I'm trying to place text into a ColumnText object and calculate its exact height. I'm using iTextSharp 5.5.9
What I'm finding is that there appears to be some "padding" at the top of the ColumnText object and it causes the height calculation to be flawed and the text to be misplaced. I'm trying to understand exactly what's happening - here's my code:
var doc = new Document(PageSize.LETTER, DocumentRenderer.PageMarginSize, DocumentRenderer.PageMarginSize, DocumentRenderer.PageTopMarginSize, DocumentRenderer.PageMarginSize);
doc.SetPageSize(PageSize.LETTER.Rotate());
var writer = PdfWriter.GetInstance(doc, fs);
doc.Open();
var x = 5;
var y = doc.PageSize.Height - 5;
var width = doc.PageSize.Width/ 2;
var height = doc.PageSize.Height - 10;
var lines = new List<string> { "Test string", };// "Test string 2", "Test string 3", "Test string 4" };
for (int i = 0; i < 6; i++)
{
lines.Add("Test String " + i);
}
lines.Add(lines.Aggregate((a, b) => a + ", " + b));
lines.Add(lines.Aggregate((a, b) => a + "\n" + b));
PdfContentByte cb = writer.DirectContent;
cb.SetTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL);
cb.SetCMYKColorFill(DocumentRenderer.DarkColor.C, DocumentRenderer.DarkColor.M, DocumentRenderer.DarkColor.Y, DocumentRenderer.DarkColor.K);
Font ListFont = new Font(DocumentRenderer.TextFont, 12);
ColumnText ct = new ColumnText(cb);
ct.Leading = 14;
//ct.UseAscender = true;
ct.Alignment = Element.ALIGN_LEFT;
ct.SetSimpleColumn(x, y - height, x + width, y);
lines.ForEach(line =>
{
ct.AddText(new Phrase(line + "\n", ListFont));
});
ct.Go();
var height1 = (ct.LinesWritten * ct.Leading);
var height2 = y - ct.YLine;
DocumentRenderer.DrawBox(ref doc, ref writer, new BoxInfo(x, y, width, height2), DocumentRenderer.HighlightColor);
if (doc.IsOpen())
doc.Close();
Here are the results - please note that I highlighted the text with my cursor for effect.
I stumbled into this post (iText placement of Phrase within ColumnText) regarding UseAscender and tried it out by uncommenting that line in the code. You can see that on the right in the picture and it didn't appear to work like I was hoping.
What I really want is the text to still have its leading drawn correctly but for that to start at the top of the ColumnText. Also, I'm trying to get an accurate height for the text. The ct.YLine appears to give me the line at the bottom of the text but the text will overhang below that slightly. I'm not sure why that's not correct but I need to know the exact height if that's possible...
Anyone know how I might achieve these two things?
Related
I am rendering 3 characters to the bottom left of a PDF document. I am doing this by creating a Column and then positioning the column accordingly with the correct dimensions to match the Chunk (or in this case, Paragraph)
If you run the following code in LINQPad (or anywhere really) you'll see that the 'Z' character is missing from the output and I can't work out why. The X axis is set to the same width as the width of the chunk.
var pdfDocument = new Document(PageSize.A4);
using (var ms = new MemoryStream())
{
using (pdfDocument)
{
using (var writer = PdfWriter.GetInstance(pdfDocument, ms))
{
pdfDocument.Open();
pdfDocument.NewPage();
var cb = writer.DirectContent;
var font = FontFactory.GetFont("Arial", 16);
var chunk = new Chunk("XYZ", font);
float textWidth = chunk.GetWidthPoint();
var p = new Paragraph(chunk);
var ct = new ColumnText(cb);
ct.SetSimpleColumn(0, 0, textWidth, p.TotalLeading);
ct.AddElement(p);
ct.Go();
pdfDocument.Close();
}
}
var tmpPdfPath = System.IO.Path.GetTempFileName() + ".pdf";
File.WriteAllBytes(tmpPdfPath, ms.ToArray());
Process.Start(tmpPdfPath);
}
I cannot get the text to be centered on the page. What am I doing wrong? I have tried several ways to get the page with, but none seem to make the text centered on the page.....
BaseFont bf = BaseFont.CreateFont("c:\\windows\\fonts\\calibri.ttf", BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
PdfReader reader = new PdfReader("C:\\temp\\Certificate12.pdf");
var pageSize = reader.GetPageSize(1);
iTextSharp.text.Rectangle rec2 = new iTextSharp.text.Rectangle(PageSize.LETTER);
PdfStamper stamper = new PdfStamper(reader, stream1);
PdfContentByte canvas = stamper.GetUnderContent(1);
canvas.BeginText();
canvas.SetFontAndSize(bf, 24);
string nameText = "First Name Last Name";
int textWidth = (int)nameText.Length;
int canvasWidth = (int)canvas.PdfDocument.PageSize.Width;
float xStart = (canvasWidth / 2) - (textWidth / 2);
canvas.ShowTextAligned(PdfContentByte.ALIGN_CENTER, nameText, xStart, pageSize.GetTop(Utilities.MillimetersToPoints(145)), 0);
First of all, if you use ShowTextAligned with ALIGN_CENTER, iTextSharp will center the text for you, so you do not have to deal with the text width at all. You merely need to tell it to center the text on which center point.
Thus, you can center your text on the page like this:
BaseFont bf = BaseFont.CreateFont("c:\\windows\\fonts\\calibri.ttf", BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
using (PdfReader reader = new PdfReader(source))
using (PdfStamper stamper = new PdfStamper(reader, new FileStream(dest, FileMode.Create, FileAccess.Write)))
{
Rectangle pageSize = reader.GetPageSize(1);
PdfContentByte canvas = stamper.GetUnderContent(1);
string nameText = "First Name Last Name";
canvas.BeginText();
canvas.SetFontAndSize(bf, 24);
canvas.ShowTextAligned(PdfContentByte.ALIGN_CENTER, nameText, (pageSize.Left + pageSize.Right) / 2, pageSize.GetTop(Utilities.MillimetersToPoints(145)), 0);
canvas.EndText();
}
is there a way to modify this code for animate cc to make object in the stage and interact with it ?
it is a bit of pain to make drag and drop in createjs for animate cc
there is nothing in the web that describe how to do it for animate cc or flash cc even the documentation has nothing to tell about drag and drop in the canvas
//Stage
var stage = new createjs.Stage("demoCanvas");
//VARIABLES
//Drag Object Size
dragRadius = 40;
//Destination Size
destHeight = 100;
destWidth = 100;
//Circle Creation
var label = new createjs.Text("DRAG ME", "14px Lato", "#fff");
label.textAlign="center";
label.y -= 7;
var circle = new createjs.Shape();
circle.graphics.setStrokeStyle(2).beginStroke("black")
.beginFill("red").drawCircle(0,0, dragRadius);
//Drag Object Creation
//Placed inside a container to hold both label and shape
var dragger = new createjs.Container();
dragger.x = dragger.y = 100;
dragger.addChild(circle, label);
dragger.setBounds(100, 100, dragRadius*2, dragRadius*2);
//DragRadius * 2 because 2*r = width of the bounding box
var label2 = new createjs.Text("HERE", "bold 14px Lato", "#000");
label2.textAlign = "center";
label2.x += 50;
label2.y += 40;
var box = new createjs.Shape();
box.graphics.setStrokeStyle(2).beginStroke("black").rect(0, 0, destHeight, destWidth);
var destination = new createjs.Container();
destination.x = 350;
destination.y = 50;
destination.setBounds(350, 50, destHeight, destWidth);
destination.addChild(label2, box);
//DRAG FUNCTIONALITY =====================
dragger.on("pressmove", function(evt){
evt.currentTarget.x = evt.stageX;
evt.currentTarget.y = evt.stageY;
stage.update(); //much smoother because it refreshes the screen every pixel movement instead of the FPS set on the Ticker
if(intersect(evt.currentTarget, destination)){
evt.currentTarget.alpha=0.2;
box.graphics.clear();
box.graphics.setStrokeStyle(3)
.beginStroke("#0066A4")
.rect(0, 0, destHeight, destWidth);
}else{
evt.currentTarget.alpha=1;
box.graphics.clear(); box.graphics.setStrokeStyle(2).beginStroke("black").rect(0, 0, destHeight, destWidth);
}
});
//Mouse UP and SNAP====================
dragger.on("pressup", function(evt) {
if(intersect(evt.currentTarget, destination)){
dragger.x = destination.x + destWidth/2;
dragger.y = destination.y + destHeight/2;
dragger.alpha = 1;
box.graphics.clear();
box.graphics.setStrokeStyle(2).beginStroke("black").rect(0, 0, destHeight, destWidth);
stage.update(evt);
}
});
//Tests if two objects are intersecting
//Sees if obj1 passes through the first and last line of its
//bounding box in the x and y sectors
//Utilizes globalToLocal to get the x and y of obj1 in relation
//to obj2
//PRE: Must have bounds set for each object
//Post: Returns true or false
function intersect(obj1, obj2){
var objBounds1 = obj1.getBounds().clone();
var objBounds2 = obj2.getBounds().clone();
var pt = obj1.globalToLocal(objBounds2.x, objBounds2.y);
var h1 = -(objBounds1.height / 2 + objBounds2.height);
var h2 = objBounds2.width / 2;
var w1 = -(objBounds1.width / 2 + objBounds2.width);
var w2 = objBounds2.width / 2;
if(pt.x > w2 || pt.x < w1) return false;
if(pt.y > h2 || pt.y < h1) return false;
return true;
}
//Adds the object into stage
stage.addChild(destination, dragger);
stage.mouseMoveOutside = true;
stage.update();
thanks
I am not exactly sure what you are asking. The demo you showed works fine (looks like it came from this codepen), and it is not clear what you are trying to add. This demo was made directly in code, not with Animate CC - which is really good for building assets, animations, and display list structure, but you should write application code around what gets exported.
There are plenty of documentation and examples online for Drag and Drop, in the EaselJS GitHub, and EaselJS docs:
DragAndDrop demo in GitHub
Live demo on EaselJS demos page
Documentation on pressMove
Tutorial on Mouse Events which includes Drag and Drop
I recommend narrowing down what you are trying to do, show what code or approaches you have tried so far, and posting specific questions here.
Lastly, here is the first part of an ongoing series for working with Animate CC: http://blog.gskinner.com/archives/2015/04/introduction-to-the-flash-cc-html5-canvas-document.html
Cheers.
I'm lost at the moment.
What I try to accomplish is adding one PDF on another (like a watermark).
The problem is that I dont seems to understand the coordinate system that is used because
my watermark just behaves unexpected.
The two PDFs have different dimensions.
My target has the following dimensions:
595 height
842 width
The PDF that shall be added has this dimension:
41 height
552 width
In my code I do the following:
public bool AddPdf(ref PdfReader pdfSource, ref PdfReader pdfTarget, ref FileStream destination)
{
PdfStamper stamper = null;
try
{
stamper = new PdfStamper( pdfSource, destination );
PdfImportedPage importatedPage = stamper.GetImportedPage(pdfTarget, 1);
PdfContentByte background;
for (int iPage = 1; iPage <= pdfSource.NumberOfPages; iPage++)
{
background = stamper.GetOverContent(iPage);
background.AddTemplate(importatedPage, 0, 0 + importHeight);
}
}
When I do this I would expect my watermark to appear in the bottom left.
Instead it is somewhere of the page (I dont see it). Just for testing I hardcoded 600 as y position and then it is centered vertically on the page.
Can someone give me a tip please?
So i solved the issue.
The problem was that the sourcepdf had a cropbox - i only needed to correct my x and y position with that information:
PdfStamper stamper = null;
try
{
stamper = new PdfStamper(pdfSource, destination);
PdfImportedPage importatedPage = stamper.GetImportedPage(pdfTarget, 1);
PdfContentByte background;
for (int iPage = 1; iPage <= pdfSource.NumberOfPages; iPage++)
{
background = stamper.GetOverContent(iPage);
// here comes the important part
Rectangle cropBox = pdfSource.GetCropBox(iPage);
float xCorrected = 0 + cropBox.Left;
float yCorrected = 0 + cropBox.Bottom;
background.AddTemplate(importatedPage, xCorrected, yCorrected);
}
}
Take in mind that in case the pdf that you want to stamp on your original has also a cropbox, you need to reduce the x,y by x,y of that cropbox again.
I'm using iTextSharp 5.0.6 to read an existing PDF, iterate each page stamping text on each, and then writing out the newly stamped PDF. The issue I'm faced with is that this isn't working 100% of the time. For some PDFs every page is stamped as expected, for others most pages are stamped while some are not. Seems as if there's potentially an issue where the stamper's GetOverContent() is not returning the top-most layer, but that's just an assumption. Has anyone had a similar issue?
using iTextSharp.text;
using iTextSharp.text.pdf;
const string WATERMARK_TEXT = "John Doe";
static void Main(string[] args)
{
string masterPdf = "master.pdf";
string pdfToCreate = "watermark.pdf";
byte[] bytes = StampPDF(masterPdf);
using (FileStream stream = new FileStream(pdfToCreate, FileMode.Create))
{
stream.Write(bytes, 0, bytes.Length);
}
}
static byte[] StampPDF(string PdfPath)
{
using (MemoryStream memoryStream = new MemoryStream())
{
PdfReader reader = new PdfReader(PdfPath);
int pageCount = reader.NumberOfPages;
PdfStamper stamper = new PdfStamper(reader, memoryStream);
float fontSize = 9;
float textAngle = 0f;
BaseFont font = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.WINANSI, BaseFont.EMBEDDED);
BaseColor backgroundColor = new BaseColor(0, 0, 0);
BaseColor fontColor = new BaseColor(255, 255, 255);
float padding = 2f;
float fontWidth = font.GetWidthPoint(WATERMARK_TEXT, fontSize);
iTextSharp.text.Rectangle pageSize;
PdfContentByte pageContents;
for (int i = 1; i <= pageCount; i++)
{
pageSize = reader.GetPageSize(i);
pageContents = stamper.GetOverContent(i);
//draw a rectangle
pageContents.SetColorFill(backgroundColor);
pageContents.MoveTo(pageSize.Width - (fontWidth + padding), 0f);
pageContents.LineTo(pageSize.Width, 0f);
pageContents.LineTo(pageSize.Width, 14f);
pageContents.LineTo(pageSize.Width - (fontWidth + padding), 14f);
pageContents.Fill();
//drop our watermark on top of the rectangle we just created
pageContents.BeginText();
pageContents.SetColorFill(fontColor);
pageContents.SetFontAndSize(font, fontSize);
pageContents.ShowTextAligned(PdfContentByte.ALIGN_LEFT, WATERMARK_TEXT, pageSize.Width - fontWidth, 4, textAngle);
pageContents.EndText();
}
stamper.Close();
reader.Close();
return memoryStream.ToArray();
}
}
For those that may encounter the same problem the key is inspecting the CropBox. Since the dimensions of a PDF's CropBox may be less than that of its PageSize you need to conditionally use one or the other. So, based on the code sample above the for loop would be altered as so:
for (int i = 1; i <= pageCount; i++)
{
mediaBox = reader.GetPageSize(i);
cropBox = reader.GetCropBox(i);
overContent = stamper.GetOverContent(i);
if (cropBox != null && (cropBox.Width < mediaBox.Width || cropBox.Height < cropBox.Height))
mediaBox = cropBox;
//draw a rectangle
overContent.SetColorFill(backgroundColor);
overContent.MoveTo(mediaBox.Right - (fontWidth + fontPadding), mediaBox.Bottom);
overContent.LineTo(mediaBox.Right, mediaBox.Bottom);
overContent.LineTo(mediaBox.Right, mediaBox.Bottom + rectangleHeight);
overContent.LineTo(mediaBox.Right - (fontWidth + fontPadding), mediaBox.Bottom + rectangleHeight);
overContent.ClosePathFillStroke();
//drop our watermark on top of the rectangle we just created
overContent.BeginText();
overContent.SetColorFill(fontColor);
overContent.SetFontAndSize(font, fontSize);
overContent.ShowTextAligned(PdfContentByte.ALIGN_LEFT, WATERMARK_TEXT, mediaBox.Right - fontWidth, mediaBox.Bottom + (rectangleHeight - fontSize), textAngle);
overContent.EndText();
}
You've made two mistakes:
You're assuming that the pages aren't rotated, but they can be: 90, 180, 270. Note that I've never seen a 180 page, but its legal. When drawing to a rotated page, you have to take that rotation into account when drawing on it. Fun with transformation matrices.
You're assuming that the page's (unrotated) lower left corner is 0,0. You're basing your measurements on the page's width and height (close), but aren't adjusting for any offset in that bottom left corner.
There are three ways to do a landscape page:
11"x8.5"
8.5"x11" # 90 degrees rotation
8.5"x11" # 270 degrees rotation
Technically, a 4th way is to build an 11x8.5 # 180, but anyone writing such code should be Punished. A lot.
There are various SO questions floating about that give details on how to deal with page rotation. Going by your code, I'd say you'll figure out the llx,lly thing pretty quickly.