SpreadSheet-Coding.com

PhpSpreadsheet

Rotate Text In A Cell In Excel Files In PHP Using PHPSpreadSheet

Tilting a header row 45 degrees is a single call to getAlignment()->setTextRotation(), and it saves you from either stretching every column to the width of its title or truncating all of them. The legal range is -90 to 90, plus one value outside it, 255, which stacks the letters vertically instead of tilting them. Two details bite: 255 reads back as -165, and an out-of-range angle is rejected rather than clamped.

August 8, 2026

This article shows how to rotate text in PHP with the latest version of PhpSpreadsheet. The usual reason is a header row: you have eight narrow yes/no columns and headings like “Answered on time”, and laying them flat either stretches every column to the width of its title or truncates all of them. Tilting the headings 45 degrees fixes both at once, and it is a single call.

The call is getAlignment()->setTextRotation(), and it takes an angle in degrees. Positive angles tilt upward, negative angles tilt downward, and the legal range is -90 to 90. However, there is one value outside that range which is also legal, and it does something different from rotating: 255 stacks the letters vertically, one under the next, each still upright. That is the mode Excel calls Vertical Text.

Two details bite people, and we prove both below rather than just mentioning them. First, 255 does not stay 255 — read the property back and you get -165. Second, an out-of-range angle is rejected, not clamped, so a value your code computed rather than typed will throw.

Requirements to rotate text in PHP:

Step 1.

First, set up the dependencies. Here we pin the latest major release of PhpSpreadsheet (the 5.x line, tested with 5.9).

composer.json
{
    "require": {
        "phpoffice/phpspreadsheet": "^5.0"
    }
}

Step 2.

Next, install phpspreadsheet.

command line
$ composer install

Step 3.

Then create a new PHP file. Alongside the usual imports we bring in Alignment, because rotation lives on the alignment object rather than on the font, and also the exception class so the last step can catch it.

rotate-text.php
<?php

require 'vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

Step 4.

Next, build the sheet this is for. Notice the shape of the problem: three long headings sitting over columns that only ever hold Yes or No.

rotate-text.php
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->setTitle('Survey');

$worksheet->fromArray([
    ['Rep', 'Answered on time', 'Escalated twice', 'Left a comment', 'Notes'],
    ['Ivy', 'Yes', 'No', 'Yes', 'steady'],
    ['Marco', 'Yes', 'Yes', 'No', 'watch'],
    ['Dan', 'No', 'No', 'Yes', 'steady'],
], null, 'A1');

Step 5.

Then rotate the headings. One call covers the whole range, so B1:D1 tilts together. Also set the vertical alignment to bottom; otherwise the tilted headings float in the middle of a tall row and stop lining up with the columns they label.

rotate-text.php
// Long headings over narrow columns: rotate them 45 degrees.
$worksheet->getStyle('B1:D1')->getAlignment()
    ->setTextRotation(45)
    ->setVertical(Alignment::VERTICAL_BOTTOM);

// The 255 special case: letters stacked one under the next, not rotated.
$worksheet->getStyle('E1')->getAlignment()->setTextRotation(255);

Step 6.

Next, make room. This is the step people skip, and then the headings arrive clipped to a single line of text. Rotating does not change how much space a cell asks for, so you set the row height yourself. Meanwhile the columns can be narrow, which was the point of rotating in the first place.

rotate-text.php
// Rotated text needs the room; Excel will not grow the row for you here.
$worksheet->getRowDimension(1)->setRowHeight(90);
foreach (range('B', 'D') as $column) {
    $worksheet->getColumnDimension($column)->setWidth(6);
}

(new Xlsx($spreadsheet))->save('survey.xlsx');
echo "Wrote survey.xlsx\n\n";

Step 7.

Finally, read the angles back, and try an illegal one. The 255 you set comes back as -165, because PhpSpreadsheet stores the stacked mode internally as 90 - 255 and only converts it back to 255 when writing the file. So compare against Alignment::TEXTROTATION_STACK_PHPSPREADSHEET rather than against 255 when you test for it. Then 120 throws, which is worth knowing if your angle comes from user input or a calculation.

rotate-text.php
printf("%-6s %-14s %s\n", 'Cell', 'Set to', 'Stored as');
foreach (['B1' => 45, 'C1' => 45, 'D1' => 45, 'E1' => 255] as $cell => $set) {
    printf("%-6s %-14s %d\n", $cell, $set, $worksheet->getStyle($cell)->getAlignment()->getTextRotation());
}

echo "\nRow 1 height: " . $worksheet->getRowDimension(1)->getRowHeight() . "\n";

// Anything outside -90..90 (except 255) is rejected, not clamped.
try {
    $worksheet->getStyle('A1')->getAlignment()->setTextRotation(120);
} catch (SpreadsheetException $e) {
    echo "\nsetTextRotation(120): " . $e->getMessage() . "\n";
}

$reloaded = IOFactory::load('survey.xlsx')->getActiveSheet();
echo "\nAfter a save and reload:\n";
printf("  B1 %d\n  E1 %d\n",
    $reloaded->getStyle('B1')->getAlignment()->getTextRotation(),
    $reloaded->getStyle('E1')->getAlignment()->getTextRotation()
);

Complete code to rotate text in PHP.

rotate-text.php
<?php

require 'vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->setTitle('Survey');

$worksheet->fromArray([
    ['Rep', 'Answered on time', 'Escalated twice', 'Left a comment', 'Notes'],
    ['Ivy', 'Yes', 'No', 'Yes', 'steady'],
    ['Marco', 'Yes', 'Yes', 'No', 'watch'],
    ['Dan', 'No', 'No', 'Yes', 'steady'],
], null, 'A1');

// Long headings over narrow columns: rotate them 45 degrees.
$worksheet->getStyle('B1:D1')->getAlignment()
    ->setTextRotation(45)
    ->setVertical(Alignment::VERTICAL_BOTTOM);

// The 255 special case: letters stacked one under the next, not rotated.
$worksheet->getStyle('E1')->getAlignment()->setTextRotation(255);

// Rotated text needs the room; Excel will not grow the row for you here.
$worksheet->getRowDimension(1)->setRowHeight(90);
foreach (range('B', 'D') as $column) {
    $worksheet->getColumnDimension($column)->setWidth(6);
}

(new Xlsx($spreadsheet))->save('survey.xlsx');
echo "Wrote survey.xlsx\n\n";

printf("%-6s %-14s %s\n", 'Cell', 'Set to', 'Stored as');
foreach (['B1' => 45, 'C1' => 45, 'D1' => 45, 'E1' => 255] as $cell => $set) {
    printf("%-6s %-14s %d\n", $cell, $set, $worksheet->getStyle($cell)->getAlignment()->getTextRotation());
}

echo "\nRow 1 height: " . $worksheet->getRowDimension(1)->getRowHeight() . "\n";

// Anything outside -90..90 (except 255) is rejected, not clamped.
try {
    $worksheet->getStyle('A1')->getAlignment()->setTextRotation(120);
} catch (SpreadsheetException $e) {
    echo "\nsetTextRotation(120): " . $e->getMessage() . "\n";
}

$reloaded = IOFactory::load('survey.xlsx')->getActiveSheet();
echo "\nAfter a save and reload:\n";
printf("  B1 %d\n  E1 %d\n",
    $reloaded->getStyle('B1')->getAlignment()->getTextRotation(),
    $reloaded->getStyle('E1')->getAlignment()->getTextRotation()
);

Test rotating text in PHP.

Command line testing.

command line
$ php rotate-text.php

Result of rotating text in PHP.

First, the three 45 degree headings read back exactly as set. Then E1 makes the special case visible: you asked for 255 and the object holds -165. Next, 120 throws rather than snapping to 90. Finally the reload shows the values are stable across a save, so -165 is the value you compare against in your own code:

command line
Wrote survey.xlsx

Cell   Set to         Stored as
B1     45             45
C1     45             45
D1     45             45
E1     255            -165

Row 1 height: 90

setTextRotation(120): Text rotation 120 should be a value between -90 and 90.

After a save and reload:
  B1 45
  E1 -165
Terminal output of rotating text in PHP: cells B1 to D1 stored as 45 degrees, E1 set to 255 but stored as -165, and setTextRotation(120) throwing the message that text rotation should be a value between -90 and 90

So open survey.xlsx and the three headings sit at a tidy diagonal above six-character columns, while Notes runs top to bottom with its letters upright. The error message is worth one last note: it says “between -90 and 90” and does not mention 255, so if you hit it while passing a legal stacked value, check that you sent 255 and not -165.

The finished survey.xlsx: the headings Answered on time, Escalated twice and Left a comment tilted 45 degrees above six-character columns of Yes and No, Notes in column E with its letters stacked upright one under the next, and Rep sitting flat at the bottom of the 90 point row

References for rotating text in PHP: