Skip to content

Instantly share code, notes, and snippets.

@DenesKellner
Last active September 21, 2020 11:49
Show Gist options
  • Save DenesKellner/d72a868ae9af761210c5dcdac1feb10d to your computer and use it in GitHub Desktop.
Save DenesKellner/d72a868ae9af761210c5dcdac1feb10d to your computer and use it in GitHub Desktop.
Simple Excel XML spreadsheet generator that can convert your 2D array to a valid XML that you can open with Excel. It's based on https://en.wikipedia.org/wiki/Microsoft_Office_XML_formats - but adds a few perks like text width calculation.
<?php
class SimpleSheet {
private $data = [];
private $worksheetName = "Export";
private $widthMultiplier = 6; // how many points for one character; gives rough approximations
private $cellLimiter = " "; // add extra character after each cell value (overflow limiter)
private $rowLimiter = " "; // add extra cell at the end of each row (overflow limiter)
public function __construct ($a=null) {if($a) $this->loadFromArray($a);}
public function loadFromArray ($a) {foreach($a as $row) $this->data[]=$row;}
public function setTitle ($name) {$this->worksheetName = $name;}
public function saveAs ($filename) {file_put_contents($filename,$this->toString());}
private function toString() {
$C = '<Cell><Data ss:Type="String">%s%s</Data></Cell>'."\n";
$R = '<Row>'."\n".'%s</Row>'."\n";
$M = '<Column ss:Width="%s"/>'."\n";
$T = '
<?xml version="1.0" encoding="utf-8" ?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<Styles>
<Style ss:ID="Default">
<Alignment ss:Indent="1"/>
</Style>
</Styles>
<Worksheet ss:Name="%s">
<Table>
%s
%s
</Table>
</Worksheet>
</Workbook>
';
$e = error_reporting();error_reporting(E_ALL - E_NOTICE); // notices are NOT errors.
$m = $this->widthMultiplier;
$l = $this->cellLimiter;
$z = $this->rowLimiter;
$N = $this->worksheetName;
$D = $this->data;
$z = $z?sprintf($C,"",$z):"";
$cw=[];foreach($D as $r) {foreach($r as $n=>$s) $cw[$n]=max($cw[$n],$this->nWidth($s));}
$cp="";foreach($cw as $x) {$x=($x+2)*$m; if($x>300) $x=300; $cp.=sprintf($M,$x)."\n";}
$tx="";foreach($D as $r) {$rt="";foreach($r as $c) {$rt.=sprintf($C,$c,$l)."\n";} $tx.=sprintf($R,"$rt$z");}
$text = sprintf($T,$N,$cp,$tx);$text = trim(preg_replace('/\s*\n\s*/',"\n",$text));
error_reporting($e);
return $text;
}
private function nWidth($str) { // how many "n"-s wide is your text; good estimation for calibri-alike fonts
$trickySigns = "l ,:;fJ-t()[]{}!sxzGHNOUmwW";
$similarChar = "iijjjtttttttttttcccVVVVVMMM";
$str = strtr($str,$trickySigns,$similarChar);
$cch = count_chars($str,1);
$text = strlen($str)*168 // count all characters as average (letter "n")
+ $cch[ord("i")]*( 70 -168) // compensate by differences
+ $cch[ord("j")]*( 84 -168) // half "n"
+ $cch[ord("t")]*( 105 -168)
+ $cch[ord("r")]*( 120 -168)
+ $cch[ord("x")]*( 140 -168) // almost the same as "n"
+ $cch[ord("V")]*( 210 -168) // these are wider than "n"
+ $cch[ord("M")]*( 280 -168) // very wide
;
return (int)($text/168);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment