. */ require_once('SWF.php'); require_once('TTF.php'); $s = new SWFextractText(); $text = $s->doExtractText(file_get_contents($argv[1])); var_dump($text); class SWFextractText { private $swf; // SWF file parser private $fonts; // Fonts collected private $text; // Text collected public function doExtractText($b) { $this->swf = new SWF($b); // Parse .swf file $this->fonts = array(); $this->text = array(); // Collect and process DefineFont... DefineFontInfo... DefineText tags foreach ($this->swf->tags as $tag) { switch ($tag['type']) { case 10: // DefineFont $this->defineFont($tag); break; case 48: // DefineFont2 case 75: // DefineFont3 $this->defineFont23($tag); break; case 91: // DefineFont4 $this->defineFont4($tag); break; case 13: // DefineFontInfo case 62: // DefineFontInfo2 $this->defineFontInfo($tag); break; case 11: // DefineText case 33: // DefineText2 $this->defineText($tag); break; } } return $this->text; } private function defineFont($tag) { $ret = $this->swf->parseTag($tag); $this->fonts[] = array('fontId' => $ret['fontId'], 'codeTable' => null); } private function defineFont23($tag) { $ret = $this->swf->parseTag($tag); $this->fonts[] = array('fontId' => $ret['fontId'], 'codeTable' => $ret['codeTable']); } private function defineFont4($tag) { $ret = $this->swf->parseTag($tag); $codeTable = array(); // Parse ttf file $ttf = new TTF($ret['fontData']); // Collect cmap table $cmap = $ttf->unmarshalCmap(); // Collect unicode encoding table if (($encodingTable = $this->collectUnicodeTable($cmap)) == null) { error_log(sprintf('Cannot locate unicode table for fontID %d', $fontId)); return; } if ($encodingTable['format'] != 4) { error_log(sprintf('Unicode table for fontID %d not in format 4', $fontId)); return; } // Construct glyph->character mapping $segCount = $encodingTable['segCount']; $endCountArray = $encodingTable['endCountArray']; $startCountArray = $encodingTable['startCountArray']; $idDeltaArray = $encodingTable['idDeltaArray']; $idRangeOffsetArray = $encodingTable['idRangeOffsetArray']; $glyphIdArray = $encodingTable['glyphIdArray']; for ($seg = 0; $seg < $segCount; $seg++) { $endCount = $endCountArray[$seg]; $startCount = $startCountArray[$seg]; $idDelta = $idDeltaArray[$seg]; $idRangeOffset = $idRangeOffsetArray[$seg]; for ($charCode = $startCount; $charCode <= $endCount; $charCode++) { if ($idRangeOffset != 0) { $j = $charCode - $startCount + $seg + $idRangeOffset / 2 - $segCount; $gid = $glyphIdArray[$j]; } else { $gid = $idDelta + $charCode; } $gid %= 65536; $codeTable[$gid] = $charCode; } } $this->fonts[] = array('fontId' => $ret['fontId'], 'codeTable' => $codeTable); } private function defineFontInfo($tag) { $ret = $this->swf->parseTag($tag); $fontId = $ret['fontId']; $codeTable = $ret['codeTable']; // Find and update the font for ($i = 0; $i < count($this->fonts); $i++) { $font = $this->fonts[$i]; if ($this->fonts[$i]['fontId'] == $fontId) { $this->fonts[$i]['codeTable'] = $codeTable; break; } } if ($i >= count($this->fonts)) { error_log(sprintf('Cannot locate fontID %d', $fontId)); } } private function defineText($tag) { $ret = $this->swf->parseTag($tag); $codeTable = null; // Must be set before first access foreach ($ret['textRecords'] as $textRecord) { if ($textRecord['styleFlagsHasFont'] != 0) { $codeTable = $this->getCodeTable($textRecord['fontId']); } $sb = ''; foreach ($textRecord['glyphEntries'] as $glyphEntry) { $code = $codeTable[$glyphEntry['glyphIndex']]; $sb .= chr($code / 256) . chr($code % 256); } $this->text[] = mb_convert_encoding($sb, 'UTF-8', 'UTF-16BE'); } } // Given a fontId, return the codeTable for this font private function getCodeTable($fontId) { foreach ($this->fonts as $font) { if ($font['fontId'] == $fontId) { return $font['codeTable']; } } return null; } // Return a unicode encoding table from a cmap table private function collectUnicodeTable($cmap) { foreach ($cmap['tables'] as $table) { $platformId = $table['platformID']; $platformSpecificId = $table['platformSpecificID']; if (($platformId == 0 && ($platformSpecificId == 4 || $platformSpecificId == 3)) || ($platformId == 3 && ($platformSpecificId == 10 || $platformSpecificId == 1 || $platformSpecificId == 0))) { return $table; } } return null; } } ?>