PdfString.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <?php
  2. /**
  3. * This file is part of FPDI
  4. *
  5. * @package Fpdi
  6. * @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com)
  7. * @license http://opensource.org/licenses/mit-license The MIT License
  8. */
  9. namespace Fpdi\PdfParser\Type;
  10. use Fpdi\PdfParser\StreamReader;
  11. /**
  12. * Class representing a PDF string object
  13. */
  14. class PdfString extends PdfType
  15. {
  16. /**
  17. * Parses a string object from the stream reader.
  18. *
  19. * @param StreamReader $streamReader
  20. * @return self
  21. */
  22. public static function parse(StreamReader $streamReader)
  23. {
  24. $pos = $startPos = $streamReader->getOffset();
  25. $openBrackets = 1;
  26. do {
  27. $buffer = $streamReader->getBuffer(false);
  28. for ($length = \strlen($buffer); $openBrackets !== 0 && $pos < $length; $pos++) {
  29. switch ($buffer[$pos]) {
  30. case '(':
  31. $openBrackets++;
  32. break;
  33. case ')':
  34. $openBrackets--;
  35. break;
  36. case '\\':
  37. $pos++;
  38. }
  39. }
  40. } while ($openBrackets !== 0 && $streamReader->increaseLength());
  41. $result = \substr($buffer, $startPos, $openBrackets + $pos - $startPos - 1);
  42. $streamReader->setOffset($pos);
  43. $v = new self();
  44. $v->value = $result;
  45. return $v;
  46. }
  47. /**
  48. * Helper method to create an instance.
  49. *
  50. * @param string $value The string needs to be escaped accordingly.
  51. * @return self
  52. */
  53. public static function create($value)
  54. {
  55. $v = new self();
  56. $v->value = $value;
  57. return $v;
  58. }
  59. /**
  60. * Ensures that the passed value is a PdfString instance.
  61. *
  62. * @param mixed $string
  63. * @return self
  64. * @throws PdfTypeException
  65. */
  66. public static function ensure($string)
  67. {
  68. return PdfType::ensureType(self::class, $string, 'String value expected.');
  69. }
  70. /**
  71. * Unescapes escaped sequences in a PDF string according to the PDF specification.
  72. *
  73. * @param string $s
  74. * @return string
  75. */
  76. public static function unescape($s)
  77. {
  78. $out = '';
  79. /** @noinspection ForeachInvariantsInspection */
  80. for ($count = 0, $n = \strlen($s); $count < $n; $count++) {
  81. if ($s[$count] !== '\\') {
  82. $out .= $s[$count];
  83. } else {
  84. // A backslash at the end of the string - ignore it
  85. if ($count === ($n - 1)) {
  86. break;
  87. }
  88. switch ($s[++$count]) {
  89. case ')':
  90. case '(':
  91. case '\\':
  92. $out .= $s[$count];
  93. break;
  94. case 'f':
  95. $out .= "\x0C";
  96. break;
  97. case 'b':
  98. $out .= "\x08";
  99. break;
  100. case 't':
  101. $out .= "\x09";
  102. break;
  103. case 'r':
  104. $out .= "\x0D";
  105. break;
  106. case 'n':
  107. $out .= "\x0A";
  108. break;
  109. case "\r":
  110. if ($count !== $n - 1 && $s[$count + 1] === "\n") {
  111. $count++;
  112. }
  113. break;
  114. case "\n":
  115. break;
  116. default:
  117. $actualChar = \ord($s[$count]);
  118. // ascii 48 = number 0
  119. // ascii 57 = number 9
  120. if ($actualChar >= 48 && $actualChar <= 57) {
  121. $oct = '' . $s[$count];
  122. /** @noinspection NotOptimalIfConditionsInspection */
  123. if (
  124. $count + 1 < $n
  125. && \ord($s[$count + 1]) >= 48
  126. && \ord($s[$count + 1]) <= 57
  127. ) {
  128. $count++;
  129. $oct .= $s[$count];
  130. /** @noinspection NotOptimalIfConditionsInspection */
  131. if (
  132. $count + 1 < $n
  133. && \ord($s[$count + 1]) >= 48
  134. && \ord($s[$count + 1]) <= 57
  135. ) {
  136. $oct .= $s[++$count];
  137. }
  138. }
  139. $out .= \chr(\octdec($oct));
  140. } else {
  141. // If the character is not one of those defined, the backslash is ignored
  142. $out .= $s[$count];
  143. }
  144. }
  145. }
  146. }
  147. return $out;
  148. }
  149. }