What value to use for .MoveUp of canvas - c#

The code below copies all pages from a PDF file to a new file and inserts on the first page a rectangle at the top with a red border holding a short text.
If I don't move it, a gap will be left at the top (here enlarged a lot, font size is 8 only):
However, if I move the rectangle up by an empiric value of 4:
iText.Kernel.Geom.Rectangle pageSize = firstPage.GetCropBox().MoveUp(4);
there will be a perfect match at the top:
The value 4 is not related to the font size.
I dislike magic numbers in code so, my question is: Why 4? What expression would reveal this value of 4?
The code line is in the first method here. The second is where it is used, and third is called from the first; it just supplies a style:
private static void RegisterDocument(PdfDocument pdfDocument, string registration)
{
// Magic value to close gap between top of page and top of rectangle with the registration.
const float moveUp = 4F;
Document document = new Document(pdfDocument, new PageSize(PageSize.A4));
PdfPage firstPage = document.GetPdfDocument().GetFirstPage();
Paragraph paragraph = new Paragraph(registration).AddStyle(RegistrationStyle());
iText.Kernel.Geom.Rectangle pageSize = firstPage.GetCropBox().MoveUp(moveUp);
LayoutContext layoutContext = new LayoutContext(new LayoutArea(1, pageSize));
IRenderer renderer = paragraph.CreateRendererSubTree();
renderer.SetParent(document.GetRenderer()).Layout(layoutContext);
Canvas canvas = new Canvas(new PdfCanvas(firstPage, true), pageSize);
canvas.Add(paragraph);
document.Close();
}
public static void RegisterPdf(string sourceFilename, string targetFilename, string registration)
{
if (registration.Length > 0)
{
// Open source and target PDF files.
PdfDocument sourcePdf = new PdfDocument(new PdfReader(sourceFilename));
PdfDocument targetPdf = new PdfDocument(new PdfWriter(targetFilename));
// Copy all pages from source PDF to target PDF.
sourcePdf.CopyPagesTo(1, sourcePdf.GetNumberOfPages(), targetPdf);
// Add registration to page 1 of target and save the document.
RegisterDocument(targetPdf, registration);
// Close the files.
sourcePdf.Close();
targetPdf.Close();
}
}
private static Style RegistrationStyle()
{
// Fixed design values for font and rectangle.
PdfFont font = PdfFontFactory.CreateFont(StandardFonts.HELVETICA);
const float fontSize = 8F;
const float rightPadding = 3F;
TextAlignment textAlignment = TextAlignment.RIGHT;
iText.Kernel.Colors.Color borderColor = ColorConstants.RED;
iText.Kernel.Colors.Color fillColor = ColorConstants.WHITE;
const float borderWidth = 0.7F;
Style style = new Style()
.SetFont(font)
.SetFontSize(fontSize)
.SetPaddingRight(rightPadding)
.SetTextAlignment(textAlignment)
.SetBackgroundColor(fillColor)
.SetBorder(new SolidBorder(borderColor, borderWidth));
return style;
}

You wonder
I dislike magic numbers in code so, my question is: Why 4? What expression would reveal this value of 4?
iText, when calculating the layout of some entity, retrieves properties from multiple sources, in particular the entity itself and its renderer. And it does not only ask them for explicitly set properties but also for defaults.
In the case at hand you see the default top margin value of the Paragraph class at work:
public override T1 GetDefaultProperty<T1>(int property) {
switch (property) {
case Property.LEADING: {
return (T1)(Object)new Leading(Leading.MULTIPLIED, childElements.Count == 1 && childElements[0] is Image ?
1 : 1.35f);
}
case Property.FIRST_LINE_INDENT: {
return (T1)(Object)0f;
}
case Property.MARGIN_TOP:
case Property.MARGIN_BOTTOM: {
return (T1)(Object)UnitValue.CreatePointValue(4f);
}
case Property.TAB_DEFAULT: {
return (T1)(Object)50f;
}
default: {
return base.GetDefaultProperty<T1>(property);
}
}
}
(iText Layout Paragraph method)
If you set the top margin of your paragraph to 0, you can simplify your code considerably:
public static void RegisterPdfImproved(string sourceFilename, string targetFilename, string registration)
{
using (PdfDocument pdf = new PdfDocument(new PdfReader(sourceFilename), new PdfWriter(targetFilename)))
using (Document document = new Document(pdf))
{
document.SetMargins(0, 0, 0, 0);
Paragraph paragraph = new Paragraph(registration)
.AddStyle(RegistrationStyle())
.SetMarginTop(0);
document.Add(paragraph);
}
}
Without any magic values you now get

There is no way to tell, without seeing all the details of your code. It could depend on an arbitrary number of circumstances and combinations of them. Examples:
default values in the PDF library you are using
margins defined in the document

Related

Resize Page Height and Width with image inside c#

I use Aspose.Word. When you try to resize the page, everything changes. BUT the images go beyond the boundaries of the text space.
There are several images in the document and I have no idea how to fix it.
`
var input = #"d:\1.docx";
var output = #"d:\2.docx";
Document doc = new Document(input);
DocumentBuilder builder = new DocumentBuilder(doc);
if (project.Variables["flagsize"].Value=="69")
{
builder.PageSetup.PageWidth = ConvertUtil.MillimeterToPoint(152.4);
builder.PageSetup.PageHeight = ConvertUtil.MillimeterToPoint(228.6);
Node[] runs = doc.GetChildNodes(NodeType.Run, true).ToArray();
for (int j = 0; j < runs.Length; j++)
{ Run run = (Run)runs[j];
run.Font.Size = 18;
}
}
foreach (Section section in doc)
{
section.PageSetup.PaperSize = Aspose.Words.PaperSize.Custom;
section.PageSetup.LeftMargin= ConvertUtil.MillimeterToPoint(22);
section.PageSetup.RightMargin= ConvertUtil.MillimeterToPoint(22);
}
doc.Save(output);
`
Try to find correct method of word.
Expecting all images at doc will be right dimensions
I think this code i need:
foreach (Aspose.Words.Drawing.Shape shape in doc)
{
shape.Width ...
}
But i have error :
Не удалось привести тип объекта "Aspose.Words.Section" к типу "Aspose.Words.Drawing.Shape".
To get all shapes in the document, you can use Document.GetChildNodes method passing the appropriate NodeType as a parameter. For example the following code returns all shapes in the document:
NodeCollection shapes = doc.GetChildNodes(NodeType.Shape, true);
You can use LINQ to filter the collection, for example the following code returns shapes that has an image:
List<Shape> shapes = doc.GetChildNodes(NodeType.Shape, true)
.Cast<Shape>().Where(s => s.HasImage).ToList();
It looks like your requirement is to fit the image to image size. I think the example provided here might be useful for you. In the provided example an image is instead into the document and page is adjusted to the actual image size. Then the result document is converted to PDF.
NodeCollection shapes = doc.GetChildNodes(NodeType.Shape, true);
PageSetup page_Setup = doc.FirstSection.PageSetup;
foreach (Shape shape in shapes)
{
shape.HorizontalAlignment = HorizontalAlignment.Center;
shape.Width = page_Setup.PageWidth - page_Setup.LeftMargin - page_Setup.RightMargin;
}

iText 7 ImageRenderInfo Matrix contains negative height on Even number Pages

I have a PDF with four pages. Two images on the first page, one on the second, and one on the third. When I retrieve the value of the image on the second page or fourth,, I get a negative height. I tried setting it to Absolute as a quick fix but the Y position of the image was still slightly off. Also, the height and positioning on page three was fine.
Update: So far, this only seems to be a problem with PDF's created in Google Docs.
My code to extract the PDF images was taken from this thread Using iText 7, what's the proper way to export a Flate encoded image?.
This is how I access the height
var currentPDFImageInfo = extractedImages[i];
var currentPDFImageMatrix = currentPDFImageInfo.RenderInfo.GetImageCtm();
float pdfImageWidth = currentPDFImageMatrix.Get(iText.Kernel.Geom.Matrix.I11);
How I retrieve the PDF image data
public static List<PDFImageInfo> ExtractImagesFromPDF(string filePath)
{
Reader = new PdfReader(filePath);
Document = new PdfDocument(Reader);
var strategy = new ImageRenderListener();
PdfCanvasProcessor parser = new PdfCanvasProcessor(strategy);
for (int pageNumber = 1; pageNumber <= Document.GetNumberOfPages(); pageNumber++)
{
strategy.CurrentPageNumber = pageNumber;
parser.ProcessPageContent(Document.GetPage(pageNumber));
}
return strategy.ImageInfoList;
}
And of course the Strategy class
public class ImageRenderListener : IEventListener
{
public void EventOccurred(IEventData data, EventType type)
{
if (data is ImageRenderInfo imageData)
{
try
{
if (imageData.GetImage() == null)
{
Console.WriteLine("Image could not be read.");
}
else
{
var pdfImageInfo = new PDFImageInfo(CurrentPageNumber, imageData);
ImageInfoList.Add(pdfImageInfo);
}
}
catch (Exception ex)
{
Console.WriteLine("Image could not be read: {0}.", ex.Message);
}
}
}
public ICollection<EventType> GetSupportedEvents()
{
return null;
}
public int CurrentPageNumber { get; set; }
public List<PDFImageInfo> ImageInfoList { get; set; } = new List<PDFImageInfo>();
}
This is how I access the height
var currentPDFImageInfo = extractedImages[i];
var currentPDFImageMatrix = currentPDFImageInfo.RenderInfo.GetImageCtm();
float pdfImageWidth = currentPDFImageMatrix.Get(iText.Kernel.Geom.Matrix.I11);
This value is the height only under certain circumstances.
Some backgrounds: The contents of a PDF page are drawn by a sequence of instructions in some content stream. Some of these instructions can manipulate the so called current transformation matrix (CTM) which represents an affine transformation, i.e. some combination of a rotation, translation, mirroring, and skewing. Everything other instructions draw is manipulated by the CTM value at the time that instruction is executed.
When a bitmap image is drawn, it is conceptually first reduced to a 1×1 square which then is transformed by the CTM to the final form of the image on the page.
If the image is displayed upright, no rotation or anything else involved, then indeed the I11 value is the width of the displayed image and the I22 value is the height. The I12 and I21 values are 0 then
But often bitmaps are displayed at 90° clockwise or counterclockwise (e.g. because someone held the camera at an 90° angle while shooting). In these cases I11 and I22 are 0 while I12 and I21 are the height and width respectively, with one or the other having a negative sign depending on the direction of the rotation.
If the bitmap is rotated by 180°, I11 and I22 again contain width and height, but both with a negative sign. If it's mirrored along the x axis or the y axis, one of them is negative.
And if the transformation is something else, e.g. a rotation by an angle that's not a multiple of 90°, finding the height and width becomes more complicated.
Actually then it is not even clear what height and width of the skewed, rotated, and mirrored form shall mean.
Thus, as a start please define which values you exactly are after; based on that you can try and determine them from arbitrary transformation matrices.
Another possible cause for unexplainable coordinate data for pages after the first one is that your code re-uses the PdfCanvasProcessor for each page without resetting:
var strategy = new ImageRenderListener();
PdfCanvasProcessor parser = new PdfCanvasProcessor(strategy);
for (int pageNumber = 1; pageNumber <= Document.GetNumberOfPages(); pageNumber++)
{
strategy.CurrentPageNumber = pageNumber;
parser.ProcessPageContent(Document.GetPage(pageNumber));
}
This causes the graphics state at the end of one page incorrectly to be used as starting graphics state of the next one. Instead you should either use a new PdfCanvasProcessor instance for each page or call parser.Reset() at the start of each page.

Text scaling problem with itext7, hidden margins?

I'm trying to get this c# function from within a asp.netcore razor project using itext7 (7.1.7) to output a div containing text that scales up to within the constraints given with height and width. However, for some reason it seems there's some kind of hidden margin that will not scale the text to the boundaries of the given width and height, even though the margins are set to 0.
At the moment the maximum text i get is only half of what it should be.
Also, the paragraph, nor the div is actually set to the min width and min height.
Any help how to fix this is appreciated
public Div MakeScaledTxtDiv(string _displayTxt ,PdfFont _Pdffont, float _maxHeight,
float _maxWidth, Canvas _canvas, bool _showBorder)
{
Div nameDiv = new Div();
iText.Kernel.Geom.Rectangle posRect = new iText.Kernel.Geom.Rectangle(_maxWidth,_maxHeight);
Paragraph txtPara = new Paragraph()
.SetVerticalAlignment(VerticalAlignment.MIDDLE)
.SetTextAlignment(iText.Layout.Properties.TextAlignment.CENTER);
if (_showBorder)
txtPara.SetBorder(new DashedBorder(1));
if (_maxHeight > 0)
txtPara.SetMaxHeight(_maxHeight);
if (_maxWidth > 0)
txtPara.SetMaxWidth(_maxWidth);
txtPara
.SetMargin(0f)
.SetPadding(0f)
.SetFont(_Pdffont)
.Add(_displayTxt);
float fontSizeToSet = 1;
float fontSizeMax = 42;
float curFontSize = fontSizeMax;
bool m_bGoodfit = false;
while (!m_bGoodfit)
{
txtPara.SetFontSize(curFontSize);
ParagraphRenderer renderer = (ParagraphRenderer)txtPara.CreateRendererSubTree().SetParent(_canvas.GetRenderer());
LayoutContext context = new LayoutContext(new LayoutArea(1, posRect));
var fits = renderer.Layout(context).GetStatus();
if (fits == LayoutResult.FULL)
{
fontSizeToSet = curFontSize;
m_bGoodfit = true;
}
else
{
curFontSize -= 1;
if (curFontSize == 1)
{
fontSizeToSet = curFontSize;
m_bGoodfit = true;
}
}
}
txtPara.SetFontSize(fontSizeToSet);
if (_maxHeight > 0)
{
nameDiv.SetMinHeight(_maxHeight)
.SetMaxHeight(_maxHeight)
.SetHeight(_maxHeight);
}
if (_maxWidth > 0)
{
nameDiv.SetMinWidth(_maxWidth)
.SetMaxWidth(_maxWidth)
.SetWidth(_maxWidth);
}
nameDiv
.SetMargin(0f)
.SetPadding(0f)
.SetVerticalAlignment(VerticalAlignment.MIDDLE)
.SetTextAlignment(iText.Layout.Properties.TextAlignment.CENTER);
if (_showBorder)
nameDiv.SetBorder(new DottedBorder(1));
nameDiv.Add(txtPara).SetVerticalAlignment(VerticalAlignment.MIDDLE);
return nameDiv;
}
What worked for me was to change the multiplier number 0.7f slightly bigger in the code below (code referenced from https://stackoverflow.com/a/57929067/5490413).
It seems as that multiplier gets bigger/smaller, the space between the text and field bottom border grows/shrinks accordingly. Maybe start with 1.2f and find one that fits.
Paragraph linePara = new Paragraph().Add(lineTxt)
.SetTextAlignment(iText.Layout.Properties.TextAlignment.CENTER).SetBorder(new DottedBorder(1))
.SetMultipliedLeading(0.7f);
lineDiv.Add(linePara);
FYI I came across the exact issue you were facing: How to scale text within a fixed rectangle with itext7?
I tried with the answer posted there https://stackoverflow.com/a/57929067/5490413 and faced the same hidden bottom margin issue here.

Passing the current platform font into SKTypeface?

When attempting to render Chinese (or other symbolic) text. SkiSharp will render boxes instead of the correct Chinese characters. Obviously the font that Skia is using by default doesn't support those characters. So we have to assign our own SKTypeface using a font that does support those characters.
My initial strategy was to simply include the necessary fonts to render those characters, which worked fine. However when supporting multiple different symbolic languages with their own fonts, the application size grows dramatically (about 15 mb per font).
So thinking about this a bit more... The default platform fonts seems to support any of these symbolic characters just fine. What I mean by this, is that the font that is used by default renders Buttons, labels and titles perfectly.
So my current thought is, why can't I just pass, whatever font that is into a SKTypeface for my control?
The problem is that I don't know how to get ahold of whatever that fall-back or default font is in order to create a new SKTypeface with it.
My Question
How can I create an SKTypeface from the same font that is rendering these buttons, labels and titles just fine?
note: if you need anything from me to help you understand the problem
or solve the problem just let me know.
You should be able to use SKFontManager.MatchCharacter or one of its overloads in order to:
Use the system fallback to find a typeface for the given character.
Below is an example based on SkiaSharp WindowsSample's TextSample:
public class TextSample : SampleBase
{
// ...
protected override void OnDrawSample(SKCanvas canvas, int width, int height)
{
canvas.DrawColor(SKColors.White);
const string text = "象形字";
var x = width / 2f;
// in my case the line below returns typeface with FamilyName `Yu Gothic UI`
var typeface = SKFontManager.Default.MatchCharacter(text[0]);
using (var paint = new SKPaint())
{
paint.TextSize = 64.0f;
paint.IsAntialias = true;
paint.Color = (SKColor)0xFF4281A4;
paint.IsStroke = false;
paint.Typeface = typeface; // use typeface for the first one
paint.TextAlign = SKTextAlign.Center;
canvas.DrawText(text, x, 64.0f, paint);
}
using (var paint = new SKPaint())
{
// no typeface here
paint.TextSize = 64.0f;
paint.IsAntialias = true;
paint.Color = (SKColor)0xFF9CAFB7;
paint.IsStroke = true;
paint.StrokeWidth = 3;
paint.TextAlign = SKTextAlign.Center;
canvas.DrawText(text, x, 144.0f, paint);
}
}
}
And below is the output

Extract font height and rotation from PDF files with iText/iTextSharp

I created some code to extract text and font height from a PDF file using iTextSharp, but does not handle text rotation. How can that information be extracted/computed?
Here is the code:
// Create PDF reader
var reader = new PdfReader("myfile.pdf");
for (var k = 1; k <= reader.NumberOfPages; ++k)
{
// Get page resources
var page = reader.GetPageN(k);
var pdfResources = page.GetAsDict(PdfName.RESOURCES);
// Create custom render listener, processor, and process page!
var listener = new FunnyRenderListener();
var processor = new PdfContentStreamProcessor(listener);
var bytes = ContentByteUtils.GetContentBytesForPage(reader, k);
processor.ProcessContent(bytes, pdfResources);
}
[...]
public class FunnyRenderListener : IRenderListener
{
[...]
void RenderText(TextRenderInfo renderInfo)
{
// Get text
var text = renderInfo.GetText();
// Get (computed) font size
var bottomLeftPoint = renderInfo.GetDescentLine().GetStartPoint();
var topRightPoint = renderInfo.GetAscentLine().GetEndPoint();
var rectangle = new Rectangle(
bottomLeftPoint[Vector.I1], bottomLeftPoint[Vector.I2],
topRightPoint[Vector.I1], topRightPoint[Vector.I2]
);
var fontSize = Convert.ToDouble(rectangle.Height);
Console.WriteLine("Text: {0}, FontSize: {1}", text, fontSize);
}
}
The information you need, i.e. the text rotation, is not directly available via a TextRenderInfo member but it does have the method
/**
* Gets the baseline for the text (i.e. the line that the text 'sits' on)
* This value includes the Rise of the draw operation - see getRise() for the amount added by Rise
*/
public LineSegment GetBaseline()
Most likely by text rotation you mean the rotation of this line against a horizontal one. Doing some easy math, therefore, you can calculate the rotation from this LineSegment.
PS: Looking at your code you actually already use the ascent line and descent line. You can use any of these lines as well instead of the base line.

Categories

Resources