Skip to content

Commit 84f0d04

Browse files
Mellthasbsweeney
authored andcommitted
Refactor stream and output methods
Additional notes: * Fix compression not being used by default in the CPDF::stream method * Remove Accept-Ranges option from Cpdf::stream. The value sent, if enabled, was wrong (see https://tools.ietf.org/html/rfc2616#section-3.12, should be literally 'bytes'), so it should not have worked anyway. It seems byte serving (https://en.wikipedia.org/wiki/Byte_serving) would have to implemented for the stream method for this to work as intended. * Fix broken JPEG output/streaming * Send Content-Length if using the PDFLib backend
1 parent 4aeba18 commit 84f0d04

File tree

7 files changed

+135
-154
lines changed

7 files changed

+135
-154
lines changed

lib/Cpdf.php

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
*/
1818
use FontLib\Font;
1919
use FontLib\BinaryStream;
20-
use Dompdf\Helpers;
2120

2221
class Cpdf
2322
{
@@ -3800,53 +3799,40 @@ function newPage($insert = 0, $id = 0, $pos = 'after')
38003799
}
38013800

38023801
/**
3803-
* output the pdf code, streaming it to the browser
3804-
* the relevant headers are set so that hopefully the browser will recognise it
3802+
* Streams the PDF to the client.
38053803
*
3806-
* @param string $options
3804+
* @param string $filename The filename to present to the client.
3805+
* @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
38073806
*/
3808-
function stream($options = '')
3807+
function stream($filename = "document.pdf", $options = array())
38093808
{
3810-
// setting the options allows the adjustment of the headers
3811-
// values at the moment are:
3812-
// 'Content-Disposition' => 'filename' - sets the filename, though not too sure how well this will
3813-
// work as in my trial the browser seems to use the filename of the php file with .pdf on the end
3814-
// 'Accept-Ranges' => 1 or 0 - if this is not set to 1, then this header is not included, off by default
3815-
// this header seems to have caused some problems despite tha fact that it is supposed to solve
3816-
// them, so I am leaving it off by default.
3817-
// 'compress' = > 1 or 0 - apply content stream compression, this is on (1) by default
3818-
// 'Attachment' => 1 or 0 - if 1, force the browser to open a download dialog
3819-
if (!is_array($options)) {
3820-
$options = array();
3821-
}
3822-
38233809
if (headers_sent()) {
38243810
die("Unable to stream pdf: headers already sent");
38253811
}
38263812

3827-
$debug = empty($options['compression']);
3813+
if (!isset($options["compress"])) $options["compress"] = true;
3814+
if (!isset($options["Attachment"])) $options["Attachment"] = true;
3815+
3816+
$debug = !$options['compress'];
38283817
$tmp = ltrim($this->output($debug));
38293818

38303819
header("Cache-Control: private");
3831-
header("Content-type: application/pdf");
3820+
header("Content-Type: application/pdf");
3821+
header("Content-Length: " . mb_strlen($tmp, "8bit"));
38323822

3833-
//FIXME: I don't know that this is sufficient for determining content length (i.e. what about transport compression?)
3834-
header("Content-Length: " . mb_strlen($tmp, '8bit'));
3835-
$filename = (isset($options['Content-Disposition']) ? $options['Content-Disposition'] : 'document.pdf');
38363823
$filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf";
3837-
3838-
if (!isset($options["Attachment"])) {
3839-
$options["Attachment"] = true;
3840-
}
3841-
38423824
$attachment = $options["Attachment"] ? "attachment" : "inline";
38433825

3844-
header(Helpers::buildContentDispositionHeader($attachment, $filename));
3826+
$encoding = mb_detect_encoding($filename);
3827+
$fallbackfilename = mb_convert_encoding($filename, "ISO-8859-1", $encoding);
3828+
$fallbackfilename = str_replace("\"", "", $fallbackfilename);
3829+
$encodedfilename = rawurlencode($filename);
38453830

3846-
if (isset($options['Accept-Ranges']) && $options['Accept-Ranges'] == 1) {
3847-
//FIXME: Is this the correct value ... spec says 1#range-unit
3848-
header("Accept-Ranges: " . mb_strlen($tmp, '8bit'));
3831+
$contentDisposition = "Content-Disposition: $attachment; filename=\"$fallbackfilename\"";
3832+
if ($fallbackfilename !== $filename) {
3833+
$contentDisposition .= "; filename*=UTF-8''$encodedfilename";
38493834
}
3835+
header($contentDisposition);
38503836

38513837
echo $tmp;
38523838
flush();

src/Adapter/CPDF.php

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,31 +1100,50 @@ protected function _add_page_text()
11001100
}
11011101

11021102
/**
1103-
* Streams the PDF directly to the browser
1103+
* Streams the PDF to the client.
11041104
*
1105-
* @param string $filename the name of the PDF file
1106-
* @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
1105+
* @param string $filename The filename to present to the client.
1106+
* @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
11071107
*/
1108-
public function stream($filename, $options = null)
1108+
public function stream($filename = "document.pdf", $options = array())
11091109
{
1110-
// Add page text
1110+
if (headers_sent()) {
1111+
die("Unable to stream pdf: headers already sent");
1112+
}
1113+
1114+
if (!isset($options["compress"])) $options["compress"] = true;
1115+
if (!isset($options["Attachment"])) $options["Attachment"] = true;
1116+
11111117
$this->_add_page_text();
11121118

1113-
$options["Content-Disposition"] = $filename;
1114-
$this->_pdf->stream($options);
1119+
$debug = !$options['compress'];
1120+
$tmp = ltrim($this->_pdf->output($debug));
1121+
1122+
header("Cache-Control: private");
1123+
header("Content-Type: application/pdf");
1124+
header("Content-Length: " . mb_strlen($tmp, "8bit"));
1125+
1126+
$filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf";
1127+
$attachment = $options["Attachment"] ? "attachment" : "inline";
1128+
header(Helpers::buildContentDispositionHeader($attachment, $filename));
1129+
1130+
echo $tmp;
1131+
flush();
11151132
}
11161133

11171134
/**
1118-
* Returns the PDF as a string
1135+
* Returns the PDF as a string.
11191136
*
1120-
* @param array $options Output options
1137+
* @param array $options Associative array: 'compress' => 1 or 0 (default 1).
11211138
* @return string
11221139
*/
1123-
public function output($options = null)
1140+
public function output($options = array())
11241141
{
1142+
if (!isset($options["compress"])) $options["compress"] = true;
1143+
11251144
$this->_add_page_text();
11261145

1127-
$debug = isset($options["compress"]) && $options["compress"] != 1;
1146+
$debug = !$options['compress'];
11281147

11291148
return $this->_pdf->output($debug);
11301149
}

src/Adapter/GD.php

Lines changed: 46 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -979,93 +979,81 @@ public function page_text()
979979
}
980980

981981
/**
982-
* Streams the image directly to the browser
982+
* Streams the image to the client.
983983
*
984-
* @param string $filename the name of the image file (ignored)
985-
* @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only)
984+
* @param string $filename The filename to present to the client.
985+
* @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
986+
* 'page' => Number of the page to output (defaults to the first); 'Attachment': 1 or 0 (default 1).
986987
*/
987-
public function stream($filename, $options = null)
988+
public function stream($filename, $options = array())
988989
{
989-
$img = $this->_imgs[0];
990-
991-
if (isset($options['page']) && isset($this->_imgs[$options['page'] - 1])) {
992-
$img = $this->_imgs[$options['page'] - 1];
993-
}
994-
995-
// Perform any antialiasing
996-
if ($this->_aa_factor != 1) {
997-
$dst_w = $this->_actual_width / $this->_aa_factor;
998-
$dst_h = $this->_actual_height / $this->_aa_factor;
999-
$dst = imagecreatetruecolor($dst_w, $dst_h);
1000-
imagecopyresampled($dst, $img, 0, 0, 0, 0,
1001-
$dst_w, $dst_h,
1002-
$this->_actual_width, $this->_actual_height);
1003-
} else {
1004-
$dst = $img;
1005-
}
1006-
1007-
if (!isset($options["type"])) {
1008-
$options["type"] = "png";
990+
if (headers_sent()) {
991+
die("Unable to stream image: headers already sent");
1009992
}
1010993

994+
if (!isset($options["type"])) $options["type"] = "png";
995+
if (!isset($options["Attachment"])) $options["Attachment"] = true;
1011996
$type = strtolower($options["type"]);
1012997

1013-
header("Cache-Control: private");
1014-
1015-
$filename = str_replace(array("\n", "'"), "", basename($filename, ".$type"));
1016998
switch ($type) {
1017999
case "jpg":
10181000
case "jpeg":
1019-
$filename .= ".jpg";
1001+
$contentType = "image/jpeg";
1002+
$extension = ".jpg";
10201003
break;
1021-
10221004
case "png":
10231005
default:
1024-
$filename .= ".png";
1006+
$contentType = "image/png";
1007+
$extension = ".png";
10251008
break;
10261009
}
1027-
$attachment = (isset($options["Attachment"]) && $options["Attachment"]) ? "attachment" : "inline";
10281010

1029-
header(Helpers::buildContentDispositionHeader($attachment, $filename));
1011+
header("Cache-Control: private");
1012+
header("Content-Type: $contentType");
10301013

1031-
switch ($type) {
1014+
$filename = str_replace(array("\n", "'"), "", basename($filename, ".$type")) . $extension;
1015+
$attachment = $options["Attachment"] ? "attachment" : "inline";
1016+
header(Helpers::buildContentDispositionHeader($attachment, $filename));
10321017

1033-
case "jpg":
1034-
case "jpeg":
1035-
if (!isset($options["quality"])) {
1036-
$options["quality"] = 75;
1037-
}
1018+
$this->_output($options);
1019+
flush();
1020+
}
10381021

1039-
header("Content-type: image/jpeg");
1040-
imagejpeg($dst, '', $options["quality"]);
1041-
break;
1022+
/**
1023+
* Returns the image as a string.
1024+
*
1025+
* @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
1026+
* 'page' => Number of the page to output (defaults to the first).
1027+
* @return string
1028+
*/
1029+
public function output($options = array())
1030+
{
1031+
ob_start();
10421032

1043-
case "png":
1044-
default:
1045-
header("Content-type: image/png");
1046-
imagepng($dst);
1047-
break;
1048-
}
1033+
$this->_output($options);
10491034

1050-
if ($this->_aa_factor != 1) {
1051-
imagedestroy($dst);
1052-
}
1035+
return ob_get_clean();
10531036
}
10541037

10551038
/**
1056-
* Returns the PNG as a string
1039+
* Outputs the image stream directly.
10571040
*
1058-
* @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only)
1059-
* @return string
1041+
* @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
1042+
* 'page' => Number of the page to output (defaults to the first).
10601043
*/
1061-
public function output($options = null)
1044+
private function _output($options = array())
10621045
{
1063-
$img = $this->_imgs[0];
1046+
if (!isset($options["type"])) $options["type"] = "png";
1047+
if (!isset($options["page"])) $options["page"] = 1;
1048+
$type = strtolower($options["type"]);
10641049

1065-
if (isset($options['page']) && isset($this->_imgs[$options['page'] - 1])) {
1066-
$img = $this->_imgs[$options['page'] - 1];
1050+
if (isset($this->_imgs[$options["page"] - 1])) {
1051+
$img = $this->_imgs[$options["page"] - 1];
1052+
} else {
1053+
$img = $this->_imgs[0];
10671054
}
10681055

1056+
// Perform any antialiasing
10691057
if ($this->_aa_factor != 1) {
10701058
$dst_w = $this->_actual_width / $this->_aa_factor;
10711059
$dst_h = $this->_actual_height / $this->_aa_factor;
@@ -1077,35 +1065,23 @@ public function output($options = null)
10771065
$dst = $img;
10781066
}
10791067

1080-
if (!isset($options["type"])) {
1081-
$options["type"] = "png";
1082-
}
1083-
1084-
$type = $options["type"];
1085-
1086-
ob_start();
1087-
10881068
switch ($type) {
10891069
case "jpg":
10901070
case "jpeg":
10911071
if (!isset($options["quality"])) {
10921072
$options["quality"] = 75;
10931073
}
10941074

1095-
imagejpeg($dst, '', $options["quality"]);
1075+
imagejpeg($dst, null, $options["quality"]);
10961076
break;
10971077
case "png":
10981078
default:
10991079
imagepng($dst);
11001080
break;
11011081
}
11021082

1103-
$image = ob_get_clean();
1104-
11051083
if ($this->_aa_factor != 1) {
11061084
imagedestroy($dst);
11071085
}
1108-
1109-
return $image;
11101086
}
11111087
}

0 commit comments

Comments
 (0)