/** * Creates a control symbol token from stream * * @param Jstewmc\Stream $stream a stream of characters (the current character * must be the backslash character, and the next character should be non- * alphanumeric) * @return Jstewmc\Rtf\Token\Control\Symbol|false * @throws InvalidArgumentException if the current character in $stream is * not a backslash * @throws InvalidArgumentException if the next character in $stream is not * a non-alphanumeric character * @since 0.1.0 * @since 0.2.0 renamed from createFromSource() to createFromStream; replaced * argument $characters, an array of characters, with $stream, an instance * of Jstewmc\STream */ public static function createFromStream(\Jstewmc\Stream\Stream $stream) { $symbol = false; // if a current character exists if ($stream->current()) { // if the current character is a backslash if ($stream->current() === '\\') { // if the next character exists if ($stream->next() !== false) { // if the now current character is not alphanumeric if (!ctype_alnum($stream->current())) { // create a new control symbol $symbol = new Symbol($stream->current()); // if the current character is an apostrophe, get the symbol's parameter if ($stream->current() === '\'') { $parameter = $stream->next() . $stream->next(); $symbol->setParameter($parameter); } // if the next character is a space, the control symbol is space-delimited, // and we should set the flag; otherwise, it's not, and we should rollback // to leave the pointer on the last character in the token (i.e., the // symbol) // if ($stream->next() === ' ') { $symbol->setIsSpaceDelimited(true); } else { $symbol->setIsSpaceDelimited(false); $stream->previous(); } } else { throw new \InvalidArgumentException(__METHOD__ . "() expects the next element in parameter one, characters, to " . "be a non-alphanumeric character"); } } else { // hmm, do nothing? } } else { throw new \InvalidArgumentException(__METHOD__ . "() expects the current element in parameter one, characters, to " . "be the backslash character"); } } return $symbol; }
/** * Lexes the backslash character ("\") * * The backslash character ("\") is arguably the most important character in the * RTF specification. A backslash character can indicate the following: * * 1. a control word (e.g., "\foo") * 2. a control symbol (e.g., "\-") * 3. an escaped special character (e.g., "\\") * 4. an escaped new-line or carriage return (e.g., "\\n") * * @param Jstewmc\Stream\Stream $stream the character stream * @return Jstewmc\Rtf\Token\Token|false * @throws InvalidArgumentException if the current character in $stream is not * the backslash character ("\") * @since 0.2.0 */ protected function lexBackslash(\Jstewmc\Stream\Stream $stream) { if ($stream->current() !== '\\') { throw new \InvalidArgumentExeption(__METHOD__ . "() expects the current character in the stream to be a '\\'"); } // look ahead to the next character, it'll determine what we do; just be sure // you rollback to the current character // $next = $stream->next(); $stream->previous(); // if a next character exists if ($next !== false) { // the next character may be a literal character, an escaped new-line or // carriage-return (i.e., an implicit "\par" control word), a control // word, or a control symbol // if (in_array($next, ['\\', '{', '}'])) { $token = Token\Text::createFromStream($stream); } elseif ($next == "\n" || $next == "\r") { $token = new Token\Control\Word('par'); $stream->next(); // consume the current "\" character } elseif (ctype_alpha($next)) { $token = Token\Control\Word::createFromStream($stream); } else { $token = Token\Control\Symbol::createFromStream($stream); } } return $token; }
/** * Creates a new text token from a stream * * @param Jstewmc\Stream\Stream $stream a stream of characters * @return Jstewmc\Rtf\Token\Text|false * @since 0.1.0 * @since 0.2.0 renamed from createFromSource() to createFromStream(); * replaced argument $characters, an array of characters, with $stream, an * instance of Jstewmc\Stream */ public static function createFromStream(\Jstewmc\Stream\Stream $stream) { $token = false; // loop through the characters until a group-open, group-close, control word, // or control symbol occurs and append the plain-text // $text = ''; while (false !== ($character = $stream->current())) { // if the current characer isn't ignored if (!in_array($character, ["\n", "\r", "\f", ""])) { // if the current character is a backslash if ($character == '\\') { // if the next character exists if (false !== ($next = $stream->next())) { // if the next character is a control character if (in_array($next, ['\\', '{', '}'])) { // it's a literal control character // ignore the backslash and append the character // $text .= $next; } else { // otherwise, the backslash is the start of a control word // rollback two characters (i.e., put the pointer on the character before // the control word's backslash) // $stream->previous(); $stream->previous(); break; } } else { // hmmm, do nothing? } } elseif ($character == '{' || $character == '}') { // otherwise, the current group is closing or a sub-group is opening // rollback to the previous character (so it isn't consumed) // $stream->previous(); break; } else { // otherwise, it's text! $text .= $character; } } // advance to the next character $stream->next(); } // if $text is not empty, create a new token // keep in mind, empty() will consider '0'to be empty, and it's a valid value // if (!empty($text) || $text === '0') { $token = new Text($text); } return $token; }