Migradoc setting an image next to text - image

I want to float an image on the right of some text in a table cell.
cell.AddParagraph("some text");
cell.AddParagraph("next line other text");
Paragraph p = cell.AddParagraph();
p.Format.Alignment = ParagraphAlignment.Right;
p.Format.RightIndent = 12;
Image image = p.AddImage("image.png");
image.ScaleWidth = 0.07;
This code puts the image to the right, but I can't move it up.
p.Format.Linespacing = -10;
image.WrapFormat.DistanceTop = -10;
These settings did not work.

For moving elements up or down SpaceBefore and SpaceAfter can be used.
In this case:
p.Format.SpaceBefore = - 20;

Related

Something strange with positioning text with custom font in Itext7

I'm working on a program that creates several pdf docs and puts different text in the same location in them.
Text should be placed in a particular area and if it doesn't fit it in width it should wrap. It also has a custom font and may be differently aligned in that area. It should be Vertically aligned to Top because when the area is laid out for three lines and I has only one, it should appear on the top. Finally, I need to preserve leading on the level of font-size.
It is important to be precise in text positioning (e.g. I need an upper left corner of "H" from "Hello world" to appear definitely at 0, 100).
Now, I'm using
canvas.showTextAligned(paragraph, 0, 300,
TextAlignment.valueOf(alignment),
VerticalAlignment.TOP);
However, when I try to implement it with different fonts it has a various offset from desired y = 300. Moreover, offset differ from font to font. For Helvetica (everywhere 50 fontSize is used) offset is about 13 px, for Oswald about 17 px and for SedgwickAveDisplay it is massive 90 px.
I added borders to paragraph for debugging purpose and things become more strange.
Helvetica:
SedgwickAveDisplay:
The full snippet of my code to create pdf is below:
public byte[] createBadgeInMemory(int i) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PdfDocument newPdf = new PdfDocument(new PdfWriter(out));
srcPdf.copyPagesTo(1,1,newPdf);
PdfPage page = newPdf.getFirstPage();
PdfCanvas pdfCanvas = new PdfCanvas(page);
Canvas canvas = new Canvas(pdfCanvas, newPdf, pageSize);
File defaultFont = new File("src/main/resources/fonts/Helvetica.otf");
PdfFont font = PdfFontFactory
.createFont(fontPath == null ? defaultFont.getAbsolutePath() : fontPath,
PdfEncodings.IDENTITY_H, true);
String value = "Example word";
Paragraph paragraph = new Paragraph(value);
float textWidth = font.getWidth("Example", 50);
paragraph.setWidth(textWidth);
switch (alignment) {
case("CENTER"):
textWidth /= 2;
break;
case("RIGHT"):
break;
default:
textWidth = 0;
break;
}
paragraph.setFont(font)
.setFontSize(fontSize)
.setFixedLeading(fontSize)
.setFontColor(new DeviceRgb(red, green, blue))
.setMargin(0)
.setPadding(0);
paragraph.setBorderTop(new DashedBorder(Color.BLACK, 0.5f))
.setBorderBottom(new DashedBorder(Color.BLACK, 0.5f))
.setBorderRight(new DashedBorder(Color.BLACK, 0.5f));
paragraph.setHyphenation(new HyphenationConfig(0,
"Example".length()));
canvas.showTextAligned(paragraph,
0 + textWidth,
300,
TextAlignment.valueOf(alignment),
VerticalAlignment.TOP);
newPdf.close();
return out.toByteArray();
}
I also tried variant from here, but for some reason text inside rectangle cuts out at some point (for instance, if I have area width 100px and text snippet I put in that I know occupies exactly 100 px (with the help of font.getWidth(value)), I have my text cut at nearly 80 px).
I also haven't found a way to align text inside a rectangle.
This is the result with Rectangle. A solid border is Rectangle border. As you can see it cuts letter "t" in "Redundant". It also should contain "word" on the second line, but it doesn't.
I copied code from an example.
I need your help. What am I doing wrong or may be there is another way to layout text in particular area with alignment and font?
Thank you in advance.
Update 21.09.17
Also tried variant from this question with SedgwickAveDisplay:
paragraph.setHorizontalAlignment(HorizontalAlignment.LEFT);
paragraph.setVerticalAlignment(VerticalAlignment.TOP);
paragraph.setFixedPosition( 0, 300 - textHeight, "Example".length());
doc.add(paragraph);
The result is the same as on the second screenshot.
This is a font-specific problem. iText guesses information about font glyphs, namely the bboxes incorrectly.
There is a quick and dirty method to adjust this behavior. You can create a custom renderer for text and adjust the calculated positions in it. An example of such a class would be as follows:
class CustomTextRenderer extends TextRenderer {
private CustomTextRenderer(Text text) {
super(text);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
Rectangle oldBbox = this.occupiedArea.getBBox().clone();
// you can also make the height more than font size or less if needed
this.occupiedArea.setBBox(oldBbox.moveUp(oldBbox.getHeight() - fontSize).setHeight(fontSize));
yLineOffset = fontSize * 0.8f; // here you config the proportion of the ascender
return result;
}
#Override
public IRenderer getNextRenderer() {
return new CustomTextRenderer((Text) modelElement);
}
}
In order that new rendering logic to be applied, you have to use it in the following way:
Text text = new Text(value);
text.setNextRenderer(new CustomTextRenderer(text));
Paragraph paragraph = new Paragraph(text);
Please note that you have to be very careful with this kind of low-level layouting, be aware of you are doing and use this as seldom as possible.
Finally, I created a variant that worked for me.
pdfCanvas.beginText()
.setFontAndSize(font, fontSize)
.setLeading(fontSize)
.moveText(0, 300);
numberOfLines = 0;
sumOfShifts = 0;
float maxWidth = computeStringWidth("Exaxple");
String[] words = value.split("\\s");
StringBuilder line = new StringBuilder();
line.append(words[0]);
float spaceWidth = computeStringWidth(" ") ;
float lineWidth;
for (int index = 1; index < words.length; index++) {
String word = words[index];
float wordWidth = computeStringWidth(word) ;
lineWidth = computeStringWidth(line.toString()) ;
if (lineWidth + spaceWidth + wordWidth <= maxWidth) {
line.append(" ").append(word);
} else {
showTextAligned(alignment, pdfCanvas, line.toString(), lineWidth, maxWidth);
line.delete(0, line.length());
line.append(word);
}
}
if(line.length() != 0) {
lineWidth = computeStringWidth(line.toString()) ;
showTextAligned(alignment, pdfCanvas, line.toString(), lineWidth, maxWidth);
}
pdfCanvas.endText();
As computeStringWidth(String str) I used
Toolkit.getToolkit().getFontLoader().computeStringWidth(String str, Font font);
from import com.sun.javafx.tk.Toolkit with Font from javafx.scene.text.Font. I've chosen it because I use it in other parts of my app.
showTextAligned(...) is my own method that looks this way:
private void showTextAligned(String alignment,
PdfCanvas pdfCanvas,
String line,
float lineWidth,
float maxWidth) {
switch (alignment) {
case "CENTER": {
float shift = (maxWidth - lineWidth) / 2 - sumOfShifts;
pdfCanvas.moveText(shift, 0);
if(numberOfLines == 0) pdfCanvas.showText(line);
else pdfCanvas.newlineShowText(line);
numberOfLines++;
sumOfShifts += shift;
break;
}
case "RIGHT": {
float shift = maxWidth - lineWidth - sumOfShifts;
pdfCanvas.moveText(shift, 0);
if(numberOfLines == 0) pdfCanvas.showText(line);
else pdfCanvas.newlineShowText(line);
numberOfLines++;
sumOfShifts += shift;
break;
}
default:
pdfCanvas.moveText(0, 0);
if(numberOfLines == 0) pdfCanvas.showText(line);
else pdfCanvas.newlineShowText(line);
numberOfLines++;
break;
}
}
In my project, I used my variant, because it gives me an opportunity to work with hyphenation deeper (for instance, I can in future add functionality to avoid putting prepositions as a last word in the line).

iText header: add logo img on the left side and text on the right side

I´m new to iText.
I would like to make a header that will be the same on each page.
The page header will look something like this:
logo.jpg some text
How can I do that?
I have read this link:
http://developers.itextpdf.com/question/how-generate-report-dynamic-header-pdf-using-itextsharp
and I have a problem because I cant add an image to pharse.
Image image = Image.GetInstance(Server.MapPath(mclLogo));
phrase.Add(image);
Throws the Error:
Insertion of illegal Element: 32
Edit: I have tried with paragraph and glue:
Paragraph paragraph = new Paragraph();
paragraph.Add(image);
paragraph.Add(new Chunk(glue));
paragraph.Add("text on the right");
and the output is 2 lines.
somthing link this:
logo.jpg
some text
edit 2:
I have read this:
http://developers.itextpdf.com/content/itext-7-jump-start-tutorial/chapter-1-introducing-basic-building-blocks
espacially the part:
Image fox = new Image(ImageDataFactory.create(FOX));
Image dog = new Image(ImageDataFactory.create(DOG));
Paragraph p = new Paragraph("The quick brown ")
.add(fox)
.add(" jumps over the lazy ")
.add(dog);
document.add(p);
but I use iText5 and I dont find any way how to make a picture and text in the same line.
Try below code, which works with two different Header plot which plotted at right end with image and Left end one with text.
public override void OnStartPage(PdfWriter writer, iTextSharp.text.Document document)
{
PdfPTable HeaderPlot = new PdfPTable(new float[] { 10F });//Header plot 1
PdfPTable HeaderPlot2 = new PdfPTable(new float[] { 10F });//Header plot 2
PdfPCell cell;//cell 1
PdfPCell cell2;// cell 2
HeaderPlot.TotalWidth = 570F; //width for Header plot 1
HeaderPlot2.TotalWidth = 570F;//width for Header plot 2
cell = new PdfPCell();
cell2 = new PdfPCell();
string path = headerpath;
FileInfo f2 = new FileInfo(path);
FileStream fs = new FileStream(f2.FullName,
FileMode.Open, FileAccess.Read);
BinaryReader rdr = new BinaryReader(fs);
byte[] fileData = rdr.ReadBytes((int)fs.Length);
Image image = Image.GetInstance(fileData);
image.ScaleAbsolute(80, 40); //adjusting image size
image.Alignment = Element.ALIGN_CENTER;
cell = new PdfPCell(image)
{
Border = 0,
HorizontalAlignment = Element.ALIGN_TOP,
VerticalAlignment = Element.ALIGN_TOP
};//Header image position
Font font = FontFactory.GetFont("Calibri Light", 8f, Font.NORMAL,
iTextSharp.text.BaseColor.BLACK);//Initializing font
cell2 = new PdfPCell(new Phrase("Text", font))
{
Border = 0,
HorizontalAlignment = Element.ALIGN_LEFT,
VerticalAlignment = Element.ALIGN_TOP
};//Header Text position
HeaderPlot.AddCell(cell);//adding cell 1 to Headerplot 1
HeaderPlot.WriteSelectedRows(0, -1, 480, 835,
writer.DirectContent);//Position of the header on right end, coordinates on right end(480,835)
HeaderPlot2.AddCell(cell2);//adding cell 2 to Headerplot 2
HeaderPlot2.WriteSelectedRows(0, -1, 40, 835,
writer.DirectContent);//Position of the header on left end, coordinates on left end(40,835)
}

Windows Forms chart with two aligned/overlapping areas don't use the entire control

A chart on a form I created has two overlapping areas. The overlapping part works just fine. The problem is that visible graph only takes up half the height of the chart control:
The bottom half of the control is left empty (presumably because that's where the second area would have gone were the two areas not aligned?). I can't figure out how to get the chart to use the entire control. The code is below:
chart1.Dock = DockStyle.Fill;
chart1.Legends.Add(new Legend { Name = "Legend1" });
chart1.Location = new Point(435, 3);
chart1.Name = "chart1";
chart1.Size = new Size(426, 287);
chart1.TabIndex = 2;
chart1.Text = "chart1";
var firstArea = chart1.ChartAreas.Add("First Area");
var seriesFirst = chart1.Series.Add("First Series");
seriesFirst.ChartType = SeriesChartType.Line;
seriesFirst.Points.Add(new DataPoint(10, 55));
seriesFirst.Points.Add(new DataPoint(11, 56));
seriesFirst.Points.Add(new DataPoint(12, 59));
var secondArea = chart1.ChartAreas.Add("Second Area");
secondArea.BackColor = Color.Transparent;
secondArea.AlignmentOrientation = AreaAlignmentOrientations.All;
secondArea.AlignmentStyle = AreaAlignmentStyles.All;
secondArea.AlignWithChartArea = firstArea.Name;
secondArea.AxisY.LabelStyle.Enabled = false;
secondArea.AxisX.LabelStyle.Enabled = false;
var seriesSecond = chart1.Series.Add("Second Series");
seriesSecond.ChartType = SeriesChartType.Line;
seriesSecond.ChartArea = secondArea.Name;
seriesSecond.Points.Add(new DataPoint(10, 1001));
seriesSecond.Points.Add(new DataPoint(11, 1015));
seriesSecond.Points.Add(new DataPoint(12, 1016));
This is some old code I've dug out and modified to suit your example. The problem is the InnerPlotPosition.Auto and Position.Auto status of the ChartAreas, thats why after you add the second chart the first charts auto position jumps up and then the second chart aligns with the new InnerPlotPosition.Auto values.
You can try turning this off but I think its easier to just position the first chart manually and then allow the second to align with the new manual position. It produces the below image (minus your legend you can work the values needed yourself)
Bit of pain in the ass solution but hopefully it helps
Dim chart1 As New Chart
Me.Controls.Add(chart1)
chart1.Location = New Point(435, 3)
chart1.Name = "chart1"
chart1.Size = New Size(426, 287)
chart1.TabIndex = 2
chart1.Text = "chart1"
Dim firstArea As ChartArea = chart1.ChartAreas.Add("First Area")
Dim seriesFirst = chart1.Series.Add("First Series")
seriesFirst.ChartType = SeriesChartType.Line
seriesFirst.Points.Add(New DataPoint(10, 55))
seriesFirst.Points.Add(New DataPoint(11, 56))
seriesFirst.Points.Add(New DataPoint(12, 59))
Dim secondArea As ChartArea = chart1.ChartAreas.Add("Second Area")
secondArea.BackColor = Color.Transparent
secondArea.AlignmentOrientation = AreaAlignmentOrientations.All
secondArea.AlignmentStyle = AreaAlignmentStyles.All
secondArea.AlignWithChartArea = firstArea.Name
secondArea.AxisY.LabelStyle.Enabled = False
secondArea.AxisX.LabelStyle.Enabled = False
Dim seriesSecond = chart1.Series.Add("Second Series")
seriesSecond.ChartType = SeriesChartType.Line
seriesSecond.ChartArea = secondArea.Name
seriesSecond.Points.Add(New DataPoint(10, 1001))
seriesSecond.Points.Add(New DataPoint(11, 1015))
seriesSecond.Points.Add(New DataPoint(12, 1016))
' *** Set locational values here for your first chart***
Dim heightAboveChartArea As Integer = 20
Dim heightBelowChartArea As Integer = 20
Dim axisLabelHeight As Integer = 40
Dim widthLeftOfChartArea As Integer = 20
Dim widthRightOfChartArea As Integer = 20
Dim heightPerBar As Integer = 20
Dim numberOfPoints As Integer = chart1.Series(0).Points.Count
' *** The following code should not normally be modified ***
chart1.Height = heightAboveChartArea + heightBelowChartArea + axisLabelHeight + (numberOfPoints * heightPerBar)
chart1.ChartAreas(0).Position.X = widthLeftOfChartArea / chart1.Width * 100
chart1.ChartAreas(0).Position.Width = 100 - (widthRightOfChartArea / chart1.Width * 100) - chart1.ChartAreas(0).Position.X
chart1.ChartAreas(0).Position.Y = (heightAboveChartArea / chart1.Height * 100)
chart1.ChartAreas(0).Position.Height = 100 - (heightBelowChartArea / chart1.Height * 100) - chart1.ChartAreas(0).Position.Y
I thought about monkeying with the position, but I'd have to take into account borders and the legend and other chart components and assumed I'd never get it as good as the auto-positioning provided by the chart - and it would drive me nuts. However, the suggestion by TylerDurden led me to the idea of simply delaying the addition of the second series/area until after the chart had rendered at least once and had calculated the position. This turned out to be non-trivial, since for most of the chart's initialization the X, Y, Height and Width are still zero. The best way I found was to add the second series in the Form's Shown event:
private void OnShown(object sender, EventArgs eventArgs)
{
Application.DoEvents();
var f = chart1.ChartAreas[0].Position.ToRectangleF();
chart1.ChartAreas[0].Position.Auto = false;
chart1.ChartAreas[0].Position.X = f.X;
chart1.ChartAreas[0].Position.Y = f.Y;
chart1.ChartAreas[0].Position.Height = f.Height;
chart1.ChartAreas[0].Position.Width = f.Width;
// add second area/series here
The call to Application.DoEvents() is required to force the chart to render and calculate the Position. Since Position is a percentage, both chart areas will always occupy the full height and width of the parent Chart.

Google Script Image Resizing

I'm trying to make a script that will resize the images in a google doc. What I have is:
var imgs = currentDoc.getImages();
for (var i = 1; i < imgs.length; i++)
{
cell = row.insertTableCell(1);
imgNew = imgs[i].setWidth(365);
cell.insertImage(1, imgNew.getBlob());
}
The image gets inserted correctly but the size does not change regardless of what I set the width to. Since the image is going into a cell (width=370), is it possible to just make the image take up 100% of the width and scale the height proportionally? If not I can deal with manually setting the number of pixels but that is not working either. Any ideas?
The problem is that the image size should be changed after it is inserted to a table. The following code works correctly
function test() {
var doc = DocumentApp.openById('here_is_doc_id');
var imgs = doc.getImages();
var table = doc.getTables()[0];
for (var i = 0; i < imgs.length; i++) {
var row = table.appendTableRow();
var cell = row.insertTableCell(0);
var imgNew = imgs[i].copy();
cell.insertImage(0, imgNew);
imgNew.setWidth(365);
}
}
Please mention, that array indexes, cells numbers, etc. start from 0 and not 1.
Just as an FYI, you don't need to call getBlob()... anything that has a getBlob() can be passed in directly wherever a Blob is needed.
Have you tried:
imgs[i].attr('width', '370');
Or try assigning a class that has width: 100%

How to solve scrolling size for MSchart controls graph c#

I made a graph using MSChart and I was able to implement the zooming ability.
The graph can zoom but the problem arises when I want to move around after I zoom. When I click up and down on the y axis scrollbar, the scrolling increment is fine. However, for the x axis scrollbar, the scrolling increment is horrible. It will always go the very end even though there is data in the middle.
I tried looking online for the solution but was out of luck
here is my code:
// Chart area (where the axes and series are plotted)
ChartArea chartArea = new ChartArea();
chartArea.AxisX.Minimum = DateTime.MinValue.ToOADate();
chartArea.AxisY.Minimum = 0;
chartArea.AxisY.Maximum = 100;
//chartArea.AxisY.IntervalAutoMode = IntervalAutoMode.VariableCount;
chartArea.AxisX.Title = "Time";
chartArea.AxisX.LabelStyle.Format = DEFAULT_TIME_FORMAT_STRING;
chartArea.AxisY.LabelStyle.Format = "#########################";
chartArea.AxisX.MajorGrid.LineDashStyle = ChartDashStyle.NotSet;
chartArea.AxisY.MajorGrid.LineDashStyle = ChartDashStyle.NotSet;
chartArea.BackColor = Color.Transparent;
m_chart.ChartAreas.Add(chartArea);
//add zoom-in features for x and y axis
m_chart.ChartAreas[0].CursorY.Interval = 0;
m_chart.ChartAreas[0].CursorY.IsUserEnabled = true;
m_chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
m_chart.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
m_chart.ChartAreas[0].AxisY.ScrollBar.IsPositionedInside = true;
m_chart.ChartAreas[0].CursorX.Interval = 0;
//m_chart.ChartAreas[0].CursorX.AutoScroll = true;
//m_chart.ChartAreas[0].AxisX.IntervalAutoMode = IntervalAutoMode.VariableCount;
m_chart.ChartAreas[0].CursorX.IsUserEnabled = true;
m_chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
m_chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
m_chart.ChartAreas[0].AxisX.ScrollBar.IsPositionedInside = true;
I dont see what I am doing wrong
Set the scaleview type to the appropriate time selection.
m_chart.ChartAreas[0].AxisX.ScaleView.SizeType = "Seconds";
m_chart.ChartAreas[0].AxisX.ScaleView.MinSizeType= "Seconds";
m_chart.ChartAreas[0].AxisX.ScaleView.SmallScrollMinSizeType= "Seconds";
m_chart.ChartAreas[0].AxisX.ScaleView.SmallScrollSizeType= "Seconds";
Just change
m_chart.ChartAreas[0].AxisX.ScrollBar.IsPositionedInside = false;
then the scroll bar will comes outside the chartarea and you will not face that problem.

Resources