Forgive my poor English,I'm a programmer from Asia.
I want to insert multiple images in merged cells,but all the images are overlapping.So I write code like this:
//merge cells
$column = 0;
$cell = $position[$column].$row;
$merge_str = $position[$column] . $row . ":" . $position[$column] . $last_row;
$objExcel->getActiveSheet()->mergeCells($merge_str);
$cell_value = '';
$objExcel->setExcelFontFormat($cell, $cell_value, $font_size, false, 'left', 'center');
$offSetY = 10;
//loop $export_data_item['images_path'] ,$image_nums is the mount of images
for($i=0;$i<$image_nums;$i++){
if(file_exists($export_data_item['images_path'][$i])){
$objDrawing = new PHPExcel_Worksheet_Drawing();
$objDrawing->setPath($export_data_item['images_path'][$i]);
$objDrawing->setOffsetX(10);
$objDrawing->setOffsetY($offSetY);
$objDrawing->setRotation(15);
$objDrawing->setHeight($export_data_item['images'][$i]['height']);
$objDrawing->setWidth($export_data_item['images'][$i]['width']);
$objDrawing->setCoordinates($cell);
$objDrawing->setWorksheet($objExcel->getActiveSheet());
$offSetY = $export_data_item['images'][$i]['height'] + $offSetY + 10;
}
}
I hope use 'offsetY' to space every images in vertical direction,but all images squeezed together.I think the reason is that I use "$objDrawing->setCoordinates($cell);", all images only in the $cell position.
I want to set all images be arranged according to the sequence and interval.Someone can help me?
Reason is (according to this) that OffsetY is specific to the cell.
Unfortunately I can't think of a solution. Ask them maybe.
This function works for me. It returns an array with Coordinate and OffsetX from a desired Position X and row number.
Maybe someone helps.
function getCoordOffx($posX, $row) {//Parameters: (desired Position X, row number)
global $objPHPExcel;
$cpos=$widthpix=0;//Set to 0 Current position and width pixels
$col=1;//First column
$colname="A";//If posX=0 not enter in while, and assign colname=A
while ($posX>$cpos) {
$colname=chr(64+$col);//Convert column to Letter. chr(65)=A
$width=$objPHPExcel->getActiveSheet()->getColumnDimension($colname)->getWidth(); //Get width of Column
$font=$objPHPExcel->getActiveSheet()->getStyle($colname.$row)->getFont();//Get Font of column and row
$widthpix= PHPExcel_Shared_Drawing::cellDimensionToPixels($width,$font); // convert to pixels
$cpos+=$widthpix;//Add pixels of current column to cpos
$col++;//Next column
}
$offsX=(int)($posX-($cpos-$widthpix));//Offset is Desired Position X minus start of current column
return array($colname,$offsX);//Returns Column Name Letter (A or C or E...) and OffsetX
}
$coAndOf=getCoordOffx(195,3); //Desired Position X=195, row = 3
$colL=$coAndOf[0];
$offX=$coAndOf[1];
Related
I am making a solution that allows a multi incremented progressbar based on float values from a array.
I am trying to achieve this (progress illustration):
[1#####2###############3###########]
1, 2, 3 etc represents the float value (in this case, 3 floats are read from array and set as width in no specific order).
I wrote this
$i = 0;
echo '<div style="padding:5px;width:50px">';
$progress = array(99.9, 99.3, 85.9, 30.8, 30.8);
foreach ($progress as $perc) {
$i++;
$calc = count($progress) / $i * 100;
echo "<div style=\"display:inline-block;border:1px solid black;height:25px;width:$calc%\">$calc</div>";
}
echo '</div>';
Which produces this
Great, so that will work in a specified container width.
But, is the formula correct? I want to see variable progress within this container, as many keys as they are in the array.
How can I show variable width (and not just leading smaller to the last)?
Any help is appreciated
Can somebody tell me please how to generate column indexes (like BB) if the number of cells is optional? Currently my code explicitly sets the cell like
$list->setCellValue("D1", "Date"));
but is there a way to generate column index "D" automatically or not? I would like to have col index like GA it means from A to Z and than double the char part if columns number exceeds the range of A-Z. Is PHPExcel able to generate this indexes automatically or not?
$colIndex = PHPExcel_Cell::columnIndexFromString($cell->getColumn());
Instead of using $cell->getColumn() you can set your letter manually
So for now I has to write a function to do this:
public static function indexToExcelIndex( $index )
{
$div = intdiv( $index, 26 );
$modulo = $index % 26;
$result = '';
do
{
$result .= chr( 65 + $modulo );
}
while( --$div > -1 );
return $result;
}
EDIT: As find out there is a method setCellValueByColumnAndRow() which accepts numeric coordinates of col and row. So I dont need alphabetical coordinates.
I use the FPDF library to export some document files as PDF. One document includes a list of strings which have a different length. I print all strings as $pdf->MultiCell(). Now I would like to have the current height of that MultiCell to have the same line spacing in case that they have just one line or more.
Code Example:
//MySQL Query
while($row = mysql_fetch_array($res) {
$pdf->SetXY(18, $x);
$pdf->MultiCell(80, 5, $rowr['text']); //text has one or more lines
$x = $x + 10; // Here I would prefer a solution to say: $x = $x + 2 + height of the MultiCell()
}
I had the exact same problem; I use FPDF to generate invoices and there are four cells per row with first cell being a MultiCell with varying height (default height is 5, but if order name is too long it adds another line to a total height of 10, and so on). The problem is that the remaining 3 cells have fixed height.
After looking for solutions it seems the only way is to write a complex function or use some third party tool. Since I want my app to be as light as possible I used the following approach to solve it which in my opinion is way simpler than external plugin.
Rows with details on the Invoice start at Y=95 so I use $Y=95;
before my while loop
My first cell (the MultiCell) is as follows:
$pdf->SetXY(10,$Y);
$pdf->MultiCell(60,5,$row['Name'],1,1,'L');
I use the FPDF's GetY() function to get current height and save it as H:
$H = $pdf->GetY();
If the MultiCell's height is 5 GetY() will give back 100, if the height is 10 GetY() gives back 105 and so on.
I add new variable $height:
$height= $H-$Y;
Which as result gives me precisely the height of the MultiCell.
I use $Y and $height to set current position and column height:
$pdf->SetXY(130,$Y);
$pdf->Cell(40,$height,$row['RowName'],1,1,'L');
Before finishing the while loop set give $Y the $H's value:
$Y=$H;
Entire loop looks as follows and works perfectly:
$Y= 95;
$query = mysqli_query($con,"SELECT * FROM table");
while($row = mysqli_fetch_array($query)) {
$pdf->SetXY(10,$Y);
$pdf->MultiCell(60,5,$row['ROW1'],1,1,'L');
$H = $pdf->GetY();
$height= $H-$Y;
$pdf->SetXY(70,$Y);
$pdf->Cell(60,$height,$row['ROW2'],1,1,'L');
$pdf->SetXY(130,$Y);
$pdf->Cell(40,$height,$row['ROW3'],1,1,'L');
$pdf->SetXY(170,$Y);
$pdf->Cell(30,$height,$row['ROW4'],1,1,'L');
$Y=$H;
}
If you have 2 MultiCell columns or more in each row it gets tricky but still can be solved in a similar manner.
I found interesting solution here - https://github.com/artkonekt/pdf-invoice/blob/master/src/InvoicePrinter.php#L469
$calculateHeight = new self;
$calculateHeight->addPage();
$calculateHeight->setXY(0, 0);
$calculateHeight->SetFont($this->font, '', 7);
$calculateHeight->MultiCell($this->firstColumnWidth, 3, $item['description'], 0, 'L', 1);
$descriptionHeight = $calculateHeight->getY() + $cellHeight + 2;
So, he literally create a 'temporary' PDF, add multicell, and then simply measure height (newY - oldY)
Also, keep in mind that if text goes to new line - height of cell will be = number_of_lines * $height (height passed to MultiCell as second parameter)
So, if you passed 5 as $height, and temporary PDF measure that cell will be 15, you can be sure that text will spread to 3 lines.
I'm coding in golang so I'll show some pseudo-code. I hope the accessible methods are the same in php as in golang.
There is a method called pdf.SplitLines(text, width). You will pass your string content and the desired width and it will return an array of strings that represents the lines that'll be computed to display that content.
With that its easy. In pseudo-code it could look like:
fontSize = 10;
lineHeight = 12;
targetWidth = 50;
pdf.SetFontSize(fontSize)
nLines = length(pdf.SplitLines(content, targetWidth));
multiCellHeight = nLines * lineHeight;
pdf.Multicell(targetWidth, lineHeight, content, ...)
The rendered MultiCell will have the exact same size as stored in multiCellHeight. This way you'll get the hight before rendering.
This is working because the passed height for the MultiCell is the lineHeight of each row. If you know the rows before rendering, you'll get the total height.
I'm sorry if this fails for any reason for php. Just let me know if that's the case.
Wouldn't it be easier to simply NOT use boarders when you print out the text.
You can just use $pdf->GetY(); to get the curent y value.
Then when you have printed all the text, you can use $pdf->GetY(); to get the height after each piece of text. Compare y values to see which one is the biggest.
All you need to do then is $pdf-> SetY($y); to the original y value and paint the boarders with $pdf->Cell() now that you know height and width;
That's how I'd do it.
Edit: tested it, and seems to work. Now sing with me - "you get the beeeeest of both worlds..." no?
$cellData = array();
$cellData[0] = array();
$cellData[0][0] = array();
$cellData[0][0]['text'] = 'Audiometry';
$cellData[0][0]['width'] = '47';
$cellData[0][1] = array();
$cellData[0][1]['text'] = 'Control of Noise at Work Regulations 2005.';
$cellData[0][1]['width'] = '47';
$cellData[0][2] = array();
$cellData[0][2]['text'] = 'Fit with restrictions (as detailed below) to work in a noise controlled zone.';
$cellData[0][2]['width'] = '47';
$cellData[0][3] = array();
$cellData[0][3]['text'] = 'Recommended review date: 2021-11-27
Referral: Referred to OHP';
$cellData[0][3]['width'] = '47';
setAllCellSizes($cellData, $pdf);
function setAllCellSizes($cellData, $pdf){
$y = $pdf->GetY();
$x = $pdf->GetX();
$largestCell = 0;
for($cordI = 0; $cordI < count($cellData); $cordI++){
$curX = 10;
for($cordJ = 0; $cordJ < count($cellData[$cordI]); $cordJ++){
$pdf->SetXY($curX,$y);
$pdf-> MultiCell($cellData[$cordI][$cordJ]['width'], 5, $cellData[$cordI][$cordJ]['text'], '0', 'L');
$curX = $curX + $cellData[$cordI][$cordJ]['width'];
$cellHeight = $pdf->GetY() - $y;
if($largestCell < $cellHeight){
$largestCell = $cellHeight;
}
}
$curX = 10;
for($cordJ = 0; $cordJ < count($cellData[$cordI]); $cordJ++){
$pdf->SetXY($curX,$y);
$pdf->Cell($cellData[$cordI][$cordJ]['width'],$largestCell,'',1,1,'L');
$curX = $curX + $cellData[$cordI][$cordJ]['width'];
}
}
return $cellData;
}
I have a problem drawing different functions with PHP (GD, of course).
I managed to draw different functions but whenever the parameters of the function change - the function floats wherever it wants.
Let us say that I have a first function y=x^2 and I have to draw it from -5 to 5. This means that the first point would be at (-5;25). And I can move that to whatever point I want if I know that. But if I choose y=2x^2 with an interval x=(-5;5). The first point is at (-5;50). So I need help in calculating how to move any function to, let's say, (0;0).
The functions are parabola/catenary alike.
What you want to do is find the maximum boundaries of the graph you are making. To do this you have to check each inflection point as well as the range bounds. Store each coordinate pair in an array
Part 1 [Range Bounds]:
Collect the coordinates from the range bounds.
<?php
$ybound[] = f($minX);
$ybound[] = f($maxX);
Part 2 [Inflections]:
This part is more difficult. You can either have a series of equations to solve for inflections for each type of parabola, or you can just brute force it. To do this, just choose a small increment, (what ever your small increment is for drawing the line), I will use 0.1
<?php
for($x = $minX; $x <= $maxX; $x += 0.1) {
$ybound[] = f($x);
}
Note, if you brute force, you can skip Part 1, otherwise, it would be faster if you could figure out the inflections for the scope of your project
Part 3 [Min Max]:
Now you get the min and max values from the array of possible y values.
<?php
$minY = min($ybound);
$maxY = max($ybound);
Part 4 [Shift]:
Now that you have this, it should be very simple to adjust. You take the top left corner and set that to 0,0 by adjusting each new coordinate to that value.
<?php
$shiftX = -$minX;
$shiftY = $maxY;
With this info, you can also determine your image size
<?php
$imageX = $maxX - $minX;
$imageY = $maxY - $minY;
Then as you generate your coordinates, you will shift each one, by adding the shift value to the coordinate.
<?php
for($x = -$minX; $x <= $maxX; $x += 0.1) {
$ycoor = $shiftY - f($x);
$xcoor = $x + $shiftX;
//draw ...
}
Drawing the axis is also easy,
<?php
$xaxis = $shiftY;
$yaxis = $shiftX;
(I think I have all my signs correct. Forgive me if they are off)
You first need to determine the bounding box of your function. Then, you calculate the width and the height, and you normalize so it fits into a rectangle whose top left coordinate is (0,0). Maybe you will also need to scale the figure to get it at a specific size.
I am generating a PDF with fPDF.
I need to strikethrough a long text inside a MultiCell. The text is justified to left and right, which probably is the source of the problem.
Here is my code:
//get the starting x and y of the cell to strikeout
$strikeout_y_start = $pdf->GetY();
$strikeout_x = $pdf->getX();
$strikeText = "Some text with no New Lines (\n), which is wrapped automaticly, cause it is very very very very very very very very very very long long long long long long long long long long long long long long long long long long"
//draw the text
$pdf->MultiCell(180, 4, $strikeText);
//get the y end of cell
$strikeout_y_end = $pdf->GetY();
$strikeout_y = $strikeout_y_start+2;
$strikeCount = 0;
for ($strikeout_y; $strikeout_y < $strikeout_y_end - 4; $strikeout_y+=4) {
$strikeCount++;
//strike out the full width of all lines but last one - works OK
$pdf->Line($strikeout_x, $strikeout_y, $strikeout_x + 180, $strikeout_y);
}
//this works, but gives incorrect results
$width = $pdf->GetStringWidth($strikeText);
$width = $width - $strikeCount*180;
//the line below will strike out some text, but not all the letters of last line
$pdf->line($strikeout_x, $strikeout_y, $strikeout_x+$width, $strikeout_y);
The problem is that as the text in multicell is justified (and have to be), the spacec in previous lines are wider than the GetStringWidth assumes, so GetStringWidth underestimates the full width of this text.
As a result, the last line is stroked out in, say, 70%, and some letters on the end of it are not stroked out.
Any ideas how to calculate the width of last line in multicell?
I found the solution myself.
Sorry for asking unnecessary questions.
Here is what I had done:
class VeraPDF extends FPDF {
/**
* Returns width of the last line in a multicell
* useful for strike out / strike through
*
*
* #param string $s - the string measured
* #param int $lineWidth - with of the cell/line
* #return int
*/
function GetStringWidth_JustifiedLastLineWidth($s, $lineWidth)
{
//Get width of a string in the current font
$s=(string)$s;
$words = split(' ',$s);
$cw=&$this->CurrentFont['cw'];
$w=0;
$spaceWidth = $this->GetStringWidth(' ');
for($i=0, $wordsCount = count($words); $i<$wordsCount; $i++){
// sum up all the words width, and add space withs between the words
$w += $this->GetStringWidth($words[$i]) + $spaceWidth;
if ($w > $lineWidth) {
//if current width is more than the line width, then the current word
//will be moved to next line, we need to count it again
$i--;
}
if ($w >= $lineWidth) {
//if the current width is equal or grater than the line width,
//we need to reset current width, and count the width of remaining text
$w = 0;
}
}
//at last, we have only the width of the text that remain on the last line!
return $w;
}
}
Hope this helped someone :)
the spacec in previous lines are wider
than the GetStringWidth assumes, so
GetStringWidth underestimates the full
width of this text.
Have you tried to count the spaces and add the missing width yourself. Say every space is supposed to be 5px wide, but fpdf etimates it to be 4px, maybe you could add 1px per space to the total width in the end.