PHPSpreadsheet --> How to delete a single image/drawing - phpspreadsheet

I use PHPSpreadsheet to open an Excel-Sheet and exchange an image in cell A27.
My actual code doesn't replace the image but add it as overlay to the existing image:
$inputFileName = $_SERVER['DOCUMENT_ROOT']."output/test.xlsx";
$spreadsheet = $reader->load($inputFileName);
$sheet = $spreadsheet->getActiveSheet();
// Remove old image --> Fails
$sheet->setCellValue("A27", "");
echo "Bild ".$pfad_bild." in Zelle ".$zelle." einfügen<br>";
$image_placer = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$image_placer->setName("myimg");
$image_placer->setDescription("myimg");
$image_placer->setPath("path/to/new/image");
$image_placer->setHeight(500);
$image_placer->setCoordinates('A27');
$image_placer->setOffsetX(0);
$image_placer->setOffsetY(0);
$image_placer->setWorksheet($spreadsheet->getActiveSheet());
// Save file with new tmp-name
$writer = new Xlsx($spreadsheet);
$writer->save($inputFileName."_");
// Delete old file
unlink($inputFileName);
// Rename new file to correct name
rename($inputFileName."_", $inputFileName);
Does anyone has an idea how to delete the Image from the Cell?

Here's the answer, i actually found out:
global $reader;
$reader = IOFactory::createReader('Xlsx');
function exchange_image_within_cell($path_file,$cell,$path_img) {
global $reader;
$inputFileName = $_SERVER['DOCUMENT_ROOT'].$path_file;
$spreadsheet = $reader->load($inputFileName);
$bilder_array=$spreadsheet->getActiveSheet()->getDrawingCollection();
$bilder_array_copy = $bilder_array->getArrayCopy();
$i = 0;
foreach ($bilder_array_copy as $drawing) {
$coordinates=$drawing->getCoordinates();
if ($coordinates==$cell) {
// Delete Image from Array/Cell
unset($bilder_array_copy[$i]);
}
$i++;
}
// Reorder and exchange array
$bilder_array_copy = array_values($bilder_array_copy);
$bilder_array->exchangeArray($bilder_array_copy);
$sheet = $spreadsheet->getActiveSheet();
// Place new image
$sheet->setCellValue($cell, "");
$image_placer = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$image_placer->setName($path_img);
$image_placer->setDescription($path_img);
$image_placer->setPath($path_img);
$image_placer->setHeight(500);
$image_placer->setCoordinates(''.$cell.'');
$image_placer->setOffsetX(0);
$image_placer->setOffsetY(0);
$image_placer->setWorksheet($spreadsheet->getActiveSheet());
// Save file with new tmp-name;
$writer = new Xlsx($spreadsheet);
$writer->save($inputFileName."_");
// Delete old file
unlink($inputFileName);
// Rename new file
rename($inputFileName."_", $inputFileName);
}

Daniel's answer is correct, but is not brief at all. Basically, once you know the index $i of the drawing to be removed from the worksheet $sheet, you can use:
$drawings = $sheet->getDrawingCollection();
unset($drawings[$i]);

Related

How to ignore first row (header row) on insert using PhpOffice\PhpSpreadsheet

I am looking for a way to ignore first row which is the header row when inserting data into mysql database using PhpOffice\PhpSpreadsheet.I have followed this but not workingSkip First Row in PHPSpreadsheet ImportMy problem is how to ignore the header row?
Below is my clean code...
use Phppot\DataSource;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Csv;
require_once ('./vendor/autoload.php');
if (isset($_POST["import"])) {
$allowedFileType = [
'application/vnd.ms-excel',
'text/xls',
'text/xlsx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
];
if (in_array($_FILES["file"]["type"], $allowedFileType)) {
$date = date('Y-m-d');
$targetPath = 'uploads/'.$date." ".$_FILES['file']['name'];
move_uploaded_file($_FILES['file']['tmp_name'], $targetPath);
$Reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
if($Reader) {
$Reader->setReadDataOnly(true);
$spreadSheet = $Reader->load($targetPath);
$sheetData = $spreadSheet->getActiveSheet()->toArray();
// Loop through the rows from xlsx file for insert
foreach($sheetData as $row) {
// get columns in a contigeous order from xlsx file
$regno = isset($row[0]) ? $row[0] : "";
$fullname = isset($row[1]) ? $row[1] : "";
$course = isset($row[2]) ? $row[2] : "";
$status = 1;
$pin = strtoupper(substr(md5(mt_rand()), 0, 10));
// Insert into db
$fieUploaded = $conn->itStudentsFileUpload($regno,$fullname,$course,$pin,$current_session,$status);
// If all is well send success message
if ($fieUploaded) {
echo "Success";
}
}
}
} else {
$type = "error";
$message = "Invalid File Type. Upload Excel File.";
}
}
After several tests, I solve the problem as follows:
class FirstRowFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter
{
public function readCell($column, $row, $worksheetName = '') {
// Return true for rows after first row
return $row > 1;
}
}
$Reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader("Xlsx");
$filterRow = new FirstRowFilter();
$Reader->setReadFilter($filterRow);
$spreadSheet = $Reader->load($targetPath);
$sheetData = $spreadSheet->getActiveSheet()->toArray();
Note:
If you have additional fields that are not from the excel file eg. date, you need to check for emptiness of the first row from the excel file and then insert if not empty to ignore inserting empty values.

Adding multiple images in excel using PhpSpreadsheet

This issue is solved with workaround. If I copy multiple images, using PhpSpreadsheet in excel, having same image name and different paths, than only last image is added multiple times. But if I rename images with different names, than it's working fine. Eg, if I copy path1/thumb.png and path2/thumb.png to excel, than path2/thumb.png will be copied two times. But if if I copy, path1/image1.png and path2/image2.png, than it's working fine. Example
<?php
require 'vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$spreadsheet = new Spreadsheet();
$name = "image.xlsx";
$writer = new Xlsx($spreadsheet);
$writer->save($name);
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
//$drawing->setPath("path1/thumb.png"); I AM COPING AND UPDATING NAME
copy("path1/thumb1.png","thumb1.png");//WORKAROUND FOR THIS ISSUE
$drawing->setPath("thumb1.png");
$drawing->setName('Logo');
$drawing->setCoordinates('B2');
$drawing->setWidthAndHeight(500, 500);
$drawing->setWorksheet($spreadsheet->setActiveSheetIndex(0));
$writer->save( $name );
$writer2 = new Xlsx($spreadsheet);
$writer2->save($name);
$writer2 = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$drawing1 = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
//$drawing->setPath("path2/thumb.png"); THIS WILL OVERWRITE ABOVE path1/thumb1.png, and will be inserted twice.
copy("path2/thumb1.png","thumb2.png");//WORKAROUND FOR THIS ISSUE
$drawing1->setPath("thumb2.png");
$drawing->setName('New image');
$drawing1->setCoordinates('B24');
$drawing1->setWidthAndHeight(500, 500);
$drawing1->setWorksheet($spreadsheet->setActiveSheetIndex(0));
$writer2->save( $name );
?>
...
You overwrite with
$writer2 = new Xlsx($spreadsheet);
the first image.
Try
<?php
require 'vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$spreadsheet = new Spreadsheet();
$name = "image.xlsx";
$writer = new Xlsx($spreadsheet);
$writer->save($name);
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$drawing->setPath("thumb1.png");
$drawing->setName('Logo');
$drawing->setCoordinates('B2');
$drawing->setWidthAndHeight(500, 500);
$drawing->setWorksheet($spreadsheet->setActiveSheetIndex(0));
$drawing1 = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$drawing1->setPath("thumb2.png");
$drawing1->setName('New image');
$drawing1->setCoordinates('B24');
$drawing1->setWidthAndHeight(500, 500);
$drawing1->setWorksheet($spreadsheet->setActiveSheetIndex(0));
$writer->save( $name );
?>

how to create .txt in local file system using Firefox extension

I am currently working on ffsniff extension code. In that I have to save data containing password information into a file in my local system. I have written my code but it is not even creating the file in my local system. (working in mozilla firefox)
Here is my code please help me out.
//// here data variable contains all the information
var fso = new ActiveXObject("Scripting.FileSystemObject");
varFileObject = fso.OpenTextFile("C:\\logs.txt", 2, true,0);
varFileObject.write(data);
varFileObject.close();
after this i tried different code:
Components.utils.import("resource://gre/modules/NetUtil.jsm");
Components.utils.import("resource://gre/modules/FileUtils.jsm");
var file = Components.classes["#mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties).
get("Desk", Components.interfaces.nsIFile);
file.append("logs.txt");
var ostream = FileUtils.openSafeFileOutputStream(file)
var converter = Components.classes["#mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
var istream = converter.convertToInputStream(data);
}
});
but none of them is working..
Here's a working snippet that creates the destination directory if necessary and writes (overwrites) to file (in this case d:\temp-directory\temp-file.txt):
var {Cc,Ci,Cu}=require("chrome"); //for jetpack sdk.
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
var localFile = Cc["#mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
var data="test file content";
//localFile.initWithPath("D:\\temp-directory\\temp-file.txt"); //full path is okay if directory exists
localFile.initWithPath("D:\\temp-directory\\"); //otherwise specifiy directory, create it if necessary, and append leaf.
if(!localFile.exists()){
localFile.create(localFile.DIRECTORY_TYPE,FileUtils.PERMS_DIRECTORY);
}
localFile.append("temp-file.txt");
//localFile.createUnique(localFile.NORMAL_FILE_TYPE,FileUtils.PERMS_FILE); //optional: create a new unique file.
asyncSave(localFile,data,onDone);
function asyncSave(file,data,callbackDone){
// file is nsIFile, data is a string, optional: callbackDone(path,leafName,statusCode)
// default flags: FileUtils.openSafeFileOutputStream(file, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE);
var ostream = FileUtils.openSafeFileOutputStream(file);
var converter = Cc["#mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
var istream = converter.convertToInputStream(data);
// optional: callbackSaved(status).
NetUtil.asyncCopy(istream, ostream, callbackSaved);
function callbackSaved (status) {
if(callbackDone){
if(status===0)callbackDone( file.path, file.leafName, status); //sucess.
else callbackDone( null, null, status); //failure.
};
}
}
function onDone(path,leafName,statusCode){
console.log([statusCode===0?"OK":"error",path,leafName].join("\n"));
}
More information:
https://developer.mozilla.org/en-US/docs/Code_snippets/File_I_O
https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/FileUtils.jsm
https://developer.mozilla.org/en-US/docs/PR_Open
https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/NetUtil.jsm
https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIFile
https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsILocalFile
A simple example of how to read/write a file from the filesystem in windows, using Firefox Extension:
// Write File to filesystem
Components.utils.import("resource://gre/modules/osfile.jsm"); // load the OS module
var encoder = new TextEncoder(); // This encoder can be reused for several writes
var array = encoder.encode("just some text"); // Convert the text to an array
var promise = OS.File.writeAtomic("C:\\foo.txt", array,{tmpPath: "foo.txt.tmp"}); // Write the array atomically to "file.txt", using as temporary
alert("URL HOST has been saved");
//Read File from filesystem
var decoder = new TextDecoder(); // This decoder can be reused for several reads
var promise = OS.File.read("C:\\foo.txt"); // Read the complete file as an array
promise = promise.then(
function onSuccess(array) {
alert(decoder.decode(array)); // Convert this array to a text
}
);
This solution is for making file in ubuntu, hope this helps others:
var file = Components.classes["#mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties).
get("ProfD", Components.interfaces.nsIFile);
file.append("trick_new");
if( !file.exists() || !file.isDirectory() ) { // if it doesn't exist, create
file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0777);
}
this.log_file = file.path + "/newlog.html";
You can also use text-stream to write to a local file.
function writeTextToFile(text, filename) {
var fileIO = require("sdk/io/file");
var TextWriter = fileIO.open(filename, "w");
if (!TextWriter.closed) {
TextWriter.write(text);
TextWriter.close();
}
}

Streaming Word Doc in OpenXML SDK using ASP.NET MVC 4 gets corrupt document

I am trying to do this on ASP.NET MVC 4:
MemoryStream mem = new MemoryStream();
using (WordprocessingDocument wordDoc =
WordprocessingDocument.Create(mem, DocumentFormat.OpenXml.WordprocessingDocumentType.Document, true))
{
// instantiate the members of the hierarchy
Document doc = new Document();
Body body = new Body();
Paragraph para = new Paragraph();
Run run = new Run();
Text text = new Text() { Text = "The OpenXML SDK rocks!" };
// put the hierarchy together
run.Append(text);
para.Append(run);
body.Append(para);
doc.Append(body);
//wordDoc.Close();
///wordDoc.Save();
}
return File(mem.ToArray(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "ABC.docx");
However the ABC.docx opens as corrupted and it wouldn't open even after fixing it.
Any ideas?
Linked Qs:
Streaming In Memory Word Document using OpenXML SDK w/ASP.NET results in "corrupt" document
Apparently the problem comes from missing this 2 lines:
wordDoc.AddMainDocumentPart();
wordDoc.MainDocumentPart.Document = doc;
Updated the code to below and it now works flawlessly, even without any extra flushing, etc necessary.
MemoryStream mem = new MemoryStream();
using (WordprocessingDocument wordDoc =
WordprocessingDocument.Create(mem, DocumentFormat.OpenXml.WordprocessingDocumentType.Document, true))
{
wordDoc.AddMainDocumentPart();
// instantiate the members of the hierarchy
Document doc = new Document();
Body body = new Body();
Paragraph para = new Paragraph();
Run run = new Run();
Text text = new Text() { Text = "The OpenXML SDK rocks!" };
// put the hierarchy together
run.Append(text);
para.Append(run);
body.Append(para);
doc.Append(body);
wordDoc.MainDocumentPart.Document = doc;
wordDoc.Close();
}
return File(mem.ToArray(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "ABC.docx");

How do I get an in-memory chart (or image) into an in-memory OpenXML document?

I'm having a nightmare of a time trying to add a Chart to a MemoryStream in-memory.
I'm creating a Word document on the fly using OpenXML and I have a chart that is also being dynamically generated from data in the database.
I get the template from the database as a byte array, passing that into a method that also takes a business object that holds a bunch of data to populate bookmarks held within that template.
Here's the method:
public Stream Parse(byte[] array, AudiometryReport AudReport)
{
using (MemoryStream Stream = new MemoryStream())
{
Stream.Write(array, 0, (int)array.Length);
Stream.Position = 0;
using (document = WordprocessingDocument.Open(Stream, true))
{
XDocument doc = document.MainDocumentPart.GetXDocument();
List<XElement> bookmarks = doc.Descendants()
.Where(n => n.NodeType == XmlNodeType.Element && n.Name.LocalName == "bookmarkStart")
.ToList();
PropertyInfo[] reportInfo = AudReport.GetType().GetProperties();
foreach (XElement bm in bookmarks)
{
try
{
if (bm.LastAttribute.Value == "AudiometryChart")
{
string partId = InsertImage(document.MainDocumentPart);
var element = AddImageToDocument(document.MainDocumentPart, partId);
//var element = InsertImageXElement(partId);
//bm.ReplaceWith(new XElement(w + "r", element));
}
else
{
string val = reportInfo.Single(x => x.Name == bm.LastAttribute.Value).GetValue(AudReport, null).ToString();
bm.ReplaceWith(new XElement(w + "r",
new XElement(w + "t", val)));
}
}
catch
{ }
}
document.MainDocumentPart.PutXDocument();
//foreach (BookmarkStart bm in (IEnumerable<BookmarkStart>)document.MainDocumentPart.Document.Descendants<BookmarkStart>())
//{
// if (bm.Name == "AudiometryChart")
// {
// // Insert the chart object here.
// //AddImage(document);
// }
// populateStaffDetails(AudReport.Report.Employee, bm);
// populateAudiometryDetails(AudReport, bm);
//}
}
MemoryStream s = new MemoryStream();
Stream.WriteTo(s);
s.Position = 0;
return s;
}
}
The InsertImage image takes the MainDocumentPart and attaches a new ImagePart from the image I stream from the database. I pass the ID of that part back to the calling method.
private string InsertImage(MainDocumentPart docPart)
{
//DrawingsPart dp = docPart.AddNewPart<DrawingsPart>();
//ImagePart part = dp.AddImagePart(ImagePartType.Png, docPart.GetIdOfPart(dp));
ImagePart part = docPart.AddImagePart(ImagePartType.Png);
Chart cht = new ChartBuilder().DoChart(Data, new string[] { "Left", "Right", "Normal" });
using (MemoryStream ms = new MemoryStream())
{
cht.SaveImage(ms, ChartImageFormat.Png);
ms.Position = 0;
part.FeedData(ms);
}
//int count = dp.ImageParts.Count<ImagePart>();
int count = docPart.ImageParts.Count<ImagePart>();
return docPart.GetIdOfPart(part);
}
The last part is some serious nastiness that is allegdly required to add one image to one word document, but what the hell - here it is anyway:
private Run AddImageToDocument(MainDocumentPart docPart, string ImageRelId)
{
string ImageFileName = "Audiometry Chart Example";
string GraphicDataUri = "http://schemas.openxmlformats.org/drawingml/2006/picture";
long imageLength = 990000L;
long imageHeight = 792000L;
var run = new Run(
new Drawing(
new wp.Inline(
new wp.Extent() { Cx = imageLength, Cy = imageHeight },
new wp.EffectExtent()
{
LeftEdge = 19050L,
TopEdge = 0L,
RightEdge = 9525L,
BottomEdge = 0L
},
new wp.DocProperties()
{
Id = (UInt32Value)1U,
Name = "Inline Text Wrapping Picture",
Description = ImageFileName
},
new wp.NonVisualGraphicFrameDrawingProperties(
new a.GraphicFrameLocks() { NoChangeAspect = true }),
new a.Graphic(
new a.GraphicData(
new pic.Picture(
new pic.NonVisualPictureProperties(
new pic.NonVisualDrawingProperties() { Id = (UInt32Value)0U, Name = ImageFileName },
new pic.NonVisualPictureDrawingProperties()),
new pic.BlipFill(
new a.Blip() { Embed = ImageRelId },
new a.Stretch(
new a.FillRectangle())),
new pic.ShapeProperties(
new a.Transform2D(
new a.Offset() { X = 0L, Y = 0L },
new a.Extents() { Cx = imageLength, Cy = imageHeight }),
new a.PresetGeometry(
new a.AdjustValueList()) { Preset = a.ShapeTypeValues.Rectangle }))
) { Uri = GraphicDataUri }))
{
DistanceFromTop = (UInt32Value)0U,
DistanceFromBottom = (UInt32Value)0U,
DistanceFromLeft = (UInt32Value)0U,
DistanceFromRight = (UInt32Value)0U
}
));
return run;
}
So I've solved issues where the memory stream was causing problems by closing prematurely and probably a dozen other unnecessary amateur garden path problems but that image will just not show up in my document. Frustrating. Suggestions or divine inspiration very welcome right now.
(this question has been heavily edited so some answers may not relate to the wording of this question).
I've just tested your AddImageToDocument function in a small test
scenario using the following code:
string partId = ...
Run element = AddImageToDocument(newdoc.MainDocumentPart, partId);
Paragraph p = new Paragraph() { RsidParagraphAddition = "00EA6221", RsidRunAdditionDefault = "008D25CC" };
p.AppendChild(element);
newdoc.MainDocumentPart.Document.Body.Append(p);
// Save the word document here...
Everything works as expected and the image shows up in the word document.
Then I've come to the conclusion that the problem in your code must be the replacement of the bookmarkStart tag and the conversion
of the Run (containing the image) to an XElement.
So, I've modified your code in the following way (using XElement.Parse to convert
an OpenXmlElement to a XElement):
foreach (XElement bm in bookmarks)
{
try
{
if (bm.LastAttribute.Value == "AudiometryChart")
{
string partId = InsertImage(document.MainDocumentPart);
Run element = AddImageToDocument(document.MainDocumentPart, partId);
bm.ReplaceWith(XElement.Parse(element.OuterXml)); // Use XElement.Parse to convert an OpenXmlElement to an XElement.
}
else
{
... }
}
}
catch
{
}
}
The image now shows up in the word document.
Then I've analyzed the word document using the
OpenXml SDK productivity tool and found that the bookmarkEnd tags still exist in the document.
To remove those tags use the following code:
List<XElement> bookmarksEnd = doc.Descendants()
.Where(n => n.NodeType == XmlNodeType.Element && n.Name.LocalName == "bookmarkEnd")
.ToList();
foreach (XElement x in bookmarksEnd)
{
x.Remove();
}
Edit 3: :o)
Ok, I found the problem.
If you initialize the document's MemoryStream with the doc content, the buffer will be fixed in size and not editable. Just changed the init to write the doc content after creation and all seemd to work fine.
//using (MemoryStream stream = new MemoryStream (docxFile))
using (MemoryStream stream = new MemoryStream ())
{
stream.Write (docxFile, 0, docxFile.Length);
stream.Position = 0;
using (WordprocessingDocument docx = WordprocessingDocument.Open (stream, true))
{
[....]
Cheers

Resources