Skip to content

Commit

Permalink
Html import dimension conversions (#2152)
Browse files Browse the repository at this point in the history
Allows basic column width conversion when importing from Html that includes UoM... while not overly-sophisticated in converting units to MS Excel's column width units, it should allow import without errors

Also provides a general conversion helper class, and allows column width getters/setters to specify a UoM for easier usage
  • Loading branch information
Mark Baker authored Jun 11, 2021
1 parent a911e9b commit 05466e9
Show file tree
Hide file tree
Showing 15 changed files with 464 additions and 69 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- Support for passing flags in the Reader `load()` and Writer `save()`methods, and through the IOFactory, to set behaviours. [PR #2136](https://github.com/PHPOffice/PhpSpreadsheet/pull/2136)
- See [documentation](https://phpspreadsheet.readthedocs.io/en/latest/topics/reading-and-writing-to-file/) for details
- More flexibility in the StringValueBinder to determine what datatypes should be treated as strings [PR #2138](https://github.com/PHPOffice/PhpSpreadsheet/pull/2138)
- Helper class for conversion between css size Units of measure (`px`, `pt`, `pc`, `in`, `cm`, `mm`). [PR #2152](https://github.com/PHPOffice/PhpSpreadsheet/issues/2145)
- Allow Row height and Column Width to be set using different units of measure (`px`, `pt`, `pc`, `in`, `cm`, `mm`), rather than only in points or MS Excel column width units. [PR #2152](https://github.com/PHPOffice/PhpSpreadsheet/issues/2145)

### Changed

Expand All @@ -27,7 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).

### Fixed

- Nothing.
- Column width and Row height styles in the Html Reader when the value includes a unit of measure. [Issue #2145](https://github.com/PHPOffice/PhpSpreadsheet/issues/2145).


## 1.18.0 - 2021-05-31

Expand Down
49 changes: 49 additions & 0 deletions docs/topics/recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,16 @@ A column's width can be set using the following code:
$spreadsheet->getActiveSheet()->getColumnDimension('D')->setWidth(12);
```

If you want to set a column width using a different unit of measure,
then you can do so by telling PhpSpreadsheet what UoM the width value
that you are setting is measured in.
Valid units are `pt` (points), `px` (pixels), `pc` (pica), `in` (inches),
`cm` (centimeters) and `mm` (millimeters).

```php
$spreadsheet->getActiveSheet()->getColumnDimension('D')->setWidth(120, 'pt');
```

If you want PhpSpreadsheet to perform an automatic width calculation,
use the following code. PhpSpreadsheet will approximate the column with
to the width of the widest column value.
Expand Down Expand Up @@ -1207,6 +1217,16 @@ Excel measures row height in points, where 1 pt is 1/72 of an inch (or
about 0.35mm). The default value is 12.75 pts; and the permitted range
of values is between 0 and 409 pts, where 0 pts is a hidden row.

If you want to set a row height using a different unit of measure,
then you can do so by telling PhpSpreadsheet what UoM the height value
that you are setting is measured in.
Valid units are `pt` (points), `px` (pixels), `pc` (pica), `in` (inches),
`cm` (centimeters) and `mm` (millimeters).

```php
$spreadsheet->getActiveSheet()->getRowDimension('10')->setRowHeight(100, 'pt');
```

## Show/hide a row

To set a worksheet''s row visibility, you can use the following code.
Expand Down Expand Up @@ -1560,6 +1580,20 @@ Default column width can be set using the following code:
$spreadsheet->getActiveSheet()->getDefaultColumnDimension()->setWidth(12);
```

Excel measures column width in its own proprietary units, based on the number
of characters that will be displayed in the default font.

If you want to set the default column width using a different unit of measure,
then you can do so by telling PhpSpreadsheet what UoM the width value
that you are setting is measured in.
Valid units are `pt` (points), `px` (pixels), `pc` (pica), `in` (inches),
`cm` (centimeters) and `mm` (millimeters).

```php
$spreadsheet->getActiveSheet()->getDefaultColumnDimension()->setWidth(400, 'pt');
```
and PhpSpreadsheet will handle the internal conversion.

## Setting the default row height

Default row height can be set using the following code:
Expand All @@ -1568,6 +1602,21 @@ Default row height can be set using the following code:
$spreadsheet->getActiveSheet()->getDefaultRowDimension()->setRowHeight(15);
```

Excel measures row height in points, where 1 pt is 1/72 of an inch (or
about 0.35mm). The default value is 12.75 pts; and the permitted range
of values is between 0 and 409 pts, where 0 pts is a hidden row.

If you want to set a row height using a different unit of measure,
then you can do so by telling PhpSpreadsheet what UoM the height value
that you are setting is measured in.
Valid units are `pt` (points), `px` (pixels), `pc` (pica), `in` (inches),
`cm` (centimeters) and `mm` (millimeters).

```php
$spreadsheet->getActiveSheet()->getDefaultRowDimension()->setRowHeight(100, 'pt');
```


## Add a GD drawing to a worksheet

There might be a situation where you want to generate an in-memory image
Expand Down
22 changes: 1 addition & 21 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -2487,7 +2487,7 @@ parameters:

-
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#"
count: 4
count: 2
path: src/PhpSpreadsheet/Reader/Html.php

-
Expand Down Expand Up @@ -4100,11 +4100,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Shared/Date.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Drawing\\:\\:pixelsToCellDimension\\(\\) should return int but returns float\\|int\\.$#"
count: 1
path: src/PhpSpreadsheet/Shared/Drawing.php

-
message: "#^Parameter \\#1 \\$fp of function fread expects resource, resource\\|false given\\.$#"
count: 2
Expand Down Expand Up @@ -4240,11 +4235,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Shared/Font.php

-
message: "#^Parameter \\#1 \\$pValue of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Drawing\\:\\:pixelsToCellDimension\\(\\) expects int, float\\|int given\\.$#"
count: 1
path: src/PhpSpreadsheet/Shared/Font.php

-
message: "#^Parameter \\#2 \\$pDefaultFont of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Drawing\\:\\:pixelsToCellDimension\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font, PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null given\\.$#"
count: 1
Expand Down Expand Up @@ -4860,16 +4850,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Shared/XMLWriter.php

-
message: "#^Parameter \\#1 \\$pValue of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Drawing\\:\\:pointsToPixels\\(\\) expects int, float given\\.$#"
count: 1
path: src/PhpSpreadsheet/Shared/Xls.php

-
message: "#^Parameter \\#1 \\$fontSizeInPoints of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Font\\:\\:fontSizeToPixels\\(\\) expects int, float given\\.$#"
count: 1
path: src/PhpSpreadsheet/Shared/Xls.php

-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\:\\:\\$workbookViewVisibilityValues has no typehint specified\\.$#"
count: 1
Expand Down
103 changes: 103 additions & 0 deletions src/PhpSpreadsheet/Helper/Dimension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace PhpOffice\PhpSpreadsheet\Helper;

use PhpOffice\PhpSpreadsheet\Exception;
use PhpOffice\PhpSpreadsheet\Shared\Drawing;
use PhpOffice\PhpSpreadsheet\Style\Font;

class Dimension
{
public const UOM_CENTIMETERS = 'cm';
public const UOM_MILLIMETERS = 'mm';
public const UOM_INCHES = 'in';
public const UOM_PIXELS = 'px';
public const UOM_POINTS = 'pt';
public const UOM_PICA = 'pc';

/**
* Based on 96 dpi.
*/
const ABSOLUTE_UNITS = [
self::UOM_CENTIMETERS => 96.0 / 2.54,
self::UOM_MILLIMETERS => 96.0 / 25.4,
self::UOM_INCHES => 96.0,
self::UOM_PIXELS => 1.0,
self::UOM_POINTS => 96.0 / 72,
self::UOM_PICA => 96.0 * 12 / 72,
];

/**
* Based on a standard column width of 8.54 units in MS Excel.
*/
const RELATIVE_UNITS = [
'em' => 10.0 / 8.54,
'ex' => 10.0 / 8.54,
'ch' => 10.0 / 8.54,
'rem' => 10.0 / 8.54,
'vw' => 8.54,
'vh' => 8.54,
'vmin' => 8.54,
'vmax' => 8.54,
'%' => 8.54 / 100,
];

/**
* @var float|int If this is a width, then size is measured in pixels (if is set)
* or in Excel's default column width units if $unit is null.
* If this is a height, then size is measured in pixels ()
* or in points () if $unit is null.
*/
protected $size;

/**
* @var null|string
*/
protected $unit;

public function __construct(string $dimension)
{
[$size, $unit] = sscanf($dimension, '%[1234567890.]%s');
$unit = strtolower(trim($unit));

// If a UoM is specified, then convert the size to pixels for internal storage
if (isset(self::ABSOLUTE_UNITS[$unit])) {
$size *= self::ABSOLUTE_UNITS[$unit];
$this->unit = self::UOM_PIXELS;
} elseif (isset(self::RELATIVE_UNITS[$unit])) {
$size *= self::RELATIVE_UNITS[$unit];
$size = round($size, 4);
}

$this->size = $size;
}

public function width(): float
{
return (float) ($this->unit === null)
? $this->size
: round(Drawing::pixelsToCellDimension((int) $this->size, new Font(false)), 4);
}

public function height(): float
{
return (float) ($this->unit === null)
? $this->size
: $this->toUnit(self::UOM_POINTS);
}

public function toUnit(string $unitOfMeasure): float
{
$unitOfMeasure = strtolower($unitOfMeasure);
if (!array_key_exists($unitOfMeasure, self::ABSOLUTE_UNITS)) {
throw new Exception("{$unitOfMeasure} is not a vaid unit of measure");
}

$size = $this->size;
if ($this->unit === null) {
$size = Drawing::cellDimensionToPixels($size, new Font(false));
}

return $size / self::ABSOLUTE_UNITS[$unitOfMeasure];
}
}
9 changes: 5 additions & 4 deletions src/PhpSpreadsheet/Reader/Html.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use DOMNode;
use DOMText;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Helper\Dimension as CssDimension;
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Border;
Expand Down Expand Up @@ -527,14 +528,14 @@ private function processDomElementBgcolor(Worksheet $sheet, int $row, string $co
private function processDomElementWidth(Worksheet $sheet, string $column, array $attributeArray): void
{
if (isset($attributeArray['width'])) {
$sheet->getColumnDimension($column)->setWidth($attributeArray['width']);
$sheet->getColumnDimension($column)->setWidth((new CssDimension($attributeArray['width']))->width());
}
}

private function processDomElementHeight(Worksheet $sheet, int $row, array $attributeArray): void
{
if (isset($attributeArray['height'])) {
$sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
$sheet->getRowDimension($row)->setRowHeight((new CssDimension($attributeArray['height']))->height());
}
}

Expand Down Expand Up @@ -878,14 +879,14 @@ private function applyInlineStyle(&$sheet, $row, $column, $attributeArray): void

case 'width':
$sheet->getColumnDimension($column)->setWidth(
(float) str_replace(['px', 'pt'], '', $styleValue)
(new CssDimension($styleValue ?? ''))->width()
);

break;

case 'height':
$sheet->getRowDimension($row)->setRowHeight(
(float) str_replace(['px', 'pt'], '', $styleValue)
(new CssDimension($styleValue ?? ''))->height()
);

break;
Expand Down
Loading

0 comments on commit 05466e9

Please sign in to comment.