2013-08-26 5 views
0

일부 swf 파일에서 모든 텍스트를 추출해야합니다. 나는이 언어로 개발 된 많은 모듈을 가지고 있기 때문에 자바를 사용하고있다. 그래서 웹에서 SWF 파일을 처리하는 데 사용되는 모든 무료 Java 라이브러리를 검색했습니다. 마지막으로, 라이브러리가 StuartMacKay에 의해 개발 된 것을 발견했습니다. 변형 -swf이라는 라이브러리는 here을 클릭하여 GitHub에서 찾을 수 있습니다.StuartMacKay의 transform-swf 라이브러리로 swf에서 텍스트 읽기

질문 : TextSpan에서 GlyphIndex을 추출한 후 글자를 문자로 변환하려면 어떻게해야합니까?

완전한 작동 및 테스트를 거친 예제를 제공해주십시오. 이론적 인 대답은 허용되지 않으며 "는 할 수 없습니다"와 같은 대답은, "그것은 불가능하다"것 등

내가 알고 내가 무엇을 않았다 나는 GlyphIndex ES를 사용하여 구축하는 것을 알고 TextTableDefineFont2 개체가 제공하는 글꼴 크기 및 글꼴 설명을 나타내는 정수로 반복하여 구성되지만 모든 DefineFont2를 디코딩하면 모두 길이가 0입니다.

다음은 내가 한 일입니다.

//Creating a Movie object from an swf file. 
Movie movie = new Movie(); 
movie.decodeFromFile(new File(out)); 

//Saving all the decoded DefineFont2 objects. 
Map<Integer,DefineFont2> fonts = new HashMap<>(); 
for (MovieTag object : list) { 
    if (object instanceof DefineFont2) { 
    DefineFont2 df2 = (DefineFont2) object; 
    fonts.put(df2.getIdentifier(), df2); 
    } 
} 
//Now I retrieve all the texts  
for (MovieTag object : list) { 
    if (object instanceof DefineText2) { 
     DefineText2 dt2 = (DefineText2) object; 
     for (TextSpan ts : dt2.getSpans()) { 
      Integer fontIdentifier = ts.getIdentifier(); 
      if (fontIdentifier != null) { 
       int fontSize = ts.getHeight(); 
       // Here I try to create an object that should 
       // reverse the process done by a TextTable 
       ReverseTextTable rtt = 
        new ReverseTextTable(fonts.get(fontIdentifier), fontSize); 
       System.out.println(rtt.charactersForText(ts.getCharacters())); 
      } 
     } 
    } 
} 

클래스 ReverseTextTable 여기에 다음과

public final class ReverseTextTable { 


    private final transient Map<Character, GlyphIndex> characters; 
    private final transient Map<GlyphIndex, Character> glyphs; 

    public ReverseTextTable(final DefineFont2 font, final int fontSize) {  
     characters = new LinkedHashMap<>(); 
     glyphs = new LinkedHashMap<>(); 

     final List<Integer> codes = font.getCodes(); 
     final List<Integer> advances = font.getAdvances(); 
     final float scale = fontSize/EMSQUARE; 
     final int count = codes.size(); 

     for (int i = 0; i < count; i++) { 
      characters.put((char) codes.get(i).intValue(), new GlyphIndex(i, 
        (int) (advances.get(i) * scale))); 
      glyphs.put(new GlyphIndex(i, 
        (int) (advances.get(i) * scale)), (char) codes.get(i).intValue()); 
     } 
    }  

    //This method should reverse from a list of GlyphIndexes to a String 
    public String charactersForText(final List<GlyphIndex> list) { 
     String text=""; 
     for(GlyphIndex gi: list){ 
      text+=glyphs.get(gi); 
     } 
     return text; 
    }   
} 

불행하게도, DefineFont2에서 진보의 목록이 비어 다음 ReverseTableText의 생성자가 ArrayIndexOutOfBoundException를 얻을.

+0

이 문제의 해결책을 찾았습니까? 나는 똑같은 문제가있어, 타이 알려줘. – Hamix

답변

0

그게 당신이 달성하려는 노력에 어려울 것 같아요, 당신의 파일을 불러 오려고 노력하고 있습니다. 불가능하다고 말하면서 유감입니다. 가능한 한 그것을 비트 맵으로 변환하는 것이 좋습니다.) 또는 다른 방법으로 문자를 읽으려고 시도하십시오 OCR

일부 문자는 software's이며, 그 중 일부는 forums입니다. 일단 swf의 컴파일 된 버전은 매우 어렵습니다 (그리고 내가 아는 한 불가능합니다). 당신은 당신이 원하는 경우이 decompiler를 확인하거나 정직 프로젝트 here

+0

BMP!?!?! OCR!?!? 아니요, swf에서 텍스트 추출을 수행하는 많은 자바가 아닌 소프트웨어가 있습니다. 예 : [swftools] (http://swftools.org/)는 전체 작업을 수행하는 exe를 제공합니다. 그래서, 자바에서 그렇게 할 수있는 방법이 있어야합니다. –

+0

다음은 현상금에 대한 나의 결정입니다. 두 가지 대답 모두 내 요구를 충족시키지 못합니다. 다음 요구 사항을 충족하도록 요청했습니다. "_ 제발, 완전한 작동 예제를 제공하십시오. 코드를 테스트해야합니다.이론적 인 대답은 받아 들일 수 없으며 "할 수 없다", "불가능하다"등의 답이 있습니다. 세 가지 요구 사항을 모두 빠뜨린 이후 200 가지의 평판을 얻을 수 없습니다. 다른 대답은 StuartMacKay 라이브러리를 사용하여 답변을 제공하지 않았으므로 그 지침에 따라 200 개의 명성을 얻었습니다 승인 된 것으로 표시하지 않습니다 –

1

같은 다른 언어를 사용하여 시도 할 수 있습니다, 나는 자바에서 그렇게하는 방법을 모르겠어요. 나는 그것이 가능하지 않다고 주장하지 않으며, 나는 또한 그렇게 할 수있는 방법이 있다고 믿는다. 그러나, 당신은 그렇게하는 많은 도서관이 있다고 말했습니다. 라이브러리 (예 : swftools)도 제안했습니다. 따라서 플래시 파일에서 텍스트를 추출하려면 해당 라이브러리를 재귀 적으로 사용하는 것이 좋습니다. 그렇게하려면 Runtime.exec()을 사용하여 명령 행을 실행하여 해당 라이브러리를 실행할 수 있습니다.

필자는 개인적으로 JDK와 함께 출시 된 표준 라이브러리보다는 Apache Commons exec을 선호합니다. 글쎄, 어떻게해야하는지 알려주지. 실행 파일은 "swfstrings.exe"입니다. "C:\"이라고 가정합니다. 같은 폴더에서 플래시 파일을 찾을 수 있다고 가정합니다. page.swf.다음, 나는 (그것을 잘 작동) 다음 코드를 시도 :

내가 아는
Path pathToSwfFile = Paths.get("C:\" + File.separator + "page.swf"); 
    CommandLine commandLine = CommandLine.parse("C:\" + File.separator + "swfstrings.exe"); 
    commandLine.addArgument("\"" + swfFile.toString() + "\""); 
    DefaultExecutor executor = new DefaultExecutor(); 
    executor.setExitValues(new int[]{0, 1}); //Notice that swfstrings.exe returns 1 for success, 
              //0 for file not found, -1 for error 

    ByteArrayOutputStream stdout = new ByteArrayOutputStream(); 
    PumpStreamHandler psh = new PumpStreamHandler(stdout); 
    executor.setStreamHandler(psh); 
    int exitValue; 
    try{ 
     exitValue = executor.execute(commandLine); 
    }catch(org.apache.commons.exec.ExecuteException ex){ 
     psh.stop(); 
    } 
    if(!executor.isFailure(exitValue)){ 
     String out = stdout.toString("UTF-8"); // here you have the extracted text 
    } 

, 이것은 정확히 요구 대답 아니지만, 잘 작동합니다.

+0

아니요, 제 대답은 아니지만 고맙습니다. –

+0

자, 코드가 작동하지만 exe 파일을 사용합니다. 완전한 자바가 아닙니다 –

+0

다음은 내 결정입니다. 200 가지의 명성을 잃어 버리게 될 것이므로, 그랬습니다 : "제발, 완전한 작동 예제를 제공하십시오. 코드를 테스트해야합니다. 이론적 인 대답이나 "할 수 없다", "불가능하다"등과 같은 대답은 받아 들여지지 않을 것입니다 1) 당신은 실례를 제시했습니다 2) 당신은 이론적 인 대답을하지 않았습니다 3) 내 질문에 답하기가 불가능하다고 말하지 않았으므로 StuartMacKay 라이브러리를 사용하여 답변을 제공하지 않았더라도 200 점의 평판을 부여 하겠지만 답변을 수락 된 것으로 표시하지 않겠습니다. –

0

변형 -swf 라이브러리를 사용하는 긴 문자열에서 비슷한 문제가있었습니다.

소스 코드를 얻은 다음 디버깅하십시오.
클래스 com.flagstone.transform.coder.SWFDecoder에 작은 버그가 있다고 생각합니다. (버전 3.0.2에 적용)

라인 (540)은,

의 최종 도착 + = 길이를 변경;

과 최종 도착

+ = 카운트;

이렇게하면됩니다. 문자열을 추출하는 것이 좋습니다. 스튜어트에게도 통보했습니다. 문자열이 매우 큰 경우에만 문제가 나타납니다.

0

이제 Java에서 SWF를 디 컴파일하는 작업을했는데 원본 텍스트를 리버스 엔지니어링하는 방법을 알아내는 동안이 질문을 보았습니다.

소스 코드를보고 나면 그 코드는 매우 간단합니다. 각 글꼴에는 DefineFont2.getCodes()을 호출하여 검색 할 수있는 지정된 문자 시퀀스가 ​​있고 glyphIndex는 DefineFont2.getCodes()의 일치하는 문자 색인입니다.

그러나, 하나의 SWF 파일에 사용되는 복수의 폰트가있는 경우에, 각 DefineText에 사용되는 DefineFont2를 식별에 속성이 없기 때문에 해당 DefineFont2DefineText 일치하기는 어렵다.

이 문제를 해결하려면 각 DefineText에 대해 DefineFont2을 맞춰 본 다음 올바르게 원래 텍스트를 유도하는 자체 학습 알고리즘을 생각해 냈습니다.

다시 원래의 텍스트를 리버스 엔지니어링, 나는 FontLearner라는 클래스를 만들었 :

public class FontLearner { 

    private final ArrayList<DefineFont2> fonts = new ArrayList<DefineFont2>(); 
    private final HashMap<Integer, HashMap<Character, Integer>> advancesMap = new HashMap<Integer, HashMap<Character, Integer>>(); 

    /** 
    * The same characters from the same font will have similar advance values. 
    * This constant defines the allowed difference between two advance values 
    * before they are treated as the same character 
    */ 
    private static final int ADVANCE_THRESHOLD = 10; 

    /** 
    * Some characters have outlier advance values despite being compared 
    * to the same character 
    * This constant defines the minimum accuracy level for each String 
    * before it is associated with the given font 
    */ 
    private static final double ACCURACY_THRESHOLD = 0.9; 

    /** 
    * This method adds a DefineFont2 to the learner, and a DefineText 
    * associated with the font to teach the learner about the given font. 
    * 
    * @param font The font to add to the learner 
    * @param text The text associated with the font 
    */ 
    private void addFont(DefineFont2 font, DefineText text) { 
     fonts.add(font); 
     HashMap<Character, Integer> advances = new HashMap<Character, Integer>(); 
     advancesMap.put(font.getIdentifier(), advances); 

     List<Integer> codes = font.getCodes(); 

     List<TextSpan> spans = text.getSpans(); 
     for (TextSpan span : spans) { 
      List<GlyphIndex> characters = span.getCharacters(); 
      for (GlyphIndex character : characters) { 
       int glyphIndex = character.getGlyphIndex(); 
       char c = (char) (int) codes.get(glyphIndex); 

       int advance = character.getAdvance(); 
       advances.put(c, advance); 
      } 
     } 
    } 

    /** 
    * 
    * @param text The DefineText to retrieve the original String from 
    * @return The String retrieved from the given DefineText 
    */ 
    public String getString(DefineText text) { 
     StringBuilder sb = new StringBuilder(); 

     List<TextSpan> spans = text.getSpans(); 

     DefineFont2 font = null; 
     for (DefineFont2 getFont : fonts) { 
      List<Integer> codes = getFont.getCodes(); 
      HashMap<Character, Integer> advances = advancesMap.get(getFont.getIdentifier()); 
      if (advances == null) { 
       advances = new HashMap<Character, Integer>(); 
       advancesMap.put(getFont.getIdentifier(), advances); 
      } 

      boolean notFound = true; 
      int totalMisses = 0; 
      int totalCount = 0; 

      for (TextSpan span : spans) { 
       List<GlyphIndex> characters = span.getCharacters(); 
       totalCount += characters.size(); 

       int misses = 0; 
       for (GlyphIndex character : characters) { 
        int glyphIndex = character.getGlyphIndex(); 
        if (codes.size() > glyphIndex) { 
         char c = (char) (int) codes.get(glyphIndex); 

         Integer getAdvance = advances.get(c); 
         if (getAdvance != null) { 
          notFound = false; 

          if (Math.abs(character.getAdvance() - getAdvance) > ADVANCE_THRESHOLD) { 
           misses += 1; 
          } 
         } 
        } else { 
         notFound = false; 
         misses = characters.size(); 

         break; 
        } 
       } 

       totalMisses += misses; 
      } 

      double accuracy = (totalCount - totalMisses) * 1.0/totalCount; 

      if (accuracy > ACCURACY_THRESHOLD && !notFound) { 
       font = getFont; 

       // teach this DefineText to the FontLearner if there are 
       // any new characters 
       for (TextSpan span : spans) { 
        List<GlyphIndex> characters = span.getCharacters(); 
        for (GlyphIndex character : characters) { 
         int glyphIndex = character.getGlyphIndex(); 
         char c = (char) (int) codes.get(glyphIndex); 

         int advance = character.getAdvance(); 
         if (advances.get(c) == null) { 
          advances.put(c, advance); 
         } 
        } 
       } 
       break; 
      } 
     } 

     if (font != null) { 
      List<Integer> codes = font.getCodes(); 

      for (TextSpan span : spans) { 
       List<GlyphIndex> characters = span.getCharacters(); 
       for (GlyphIndex character : characters) { 
        int glyphIndex = character.getGlyphIndex(); 
        char c = (char) (int) codes.get(glyphIndex); 
        sb.append(c); 
       } 
       sb = new StringBuilder(sb.toString().trim()); 
       sb.append(" "); 
      } 
     } 

     return sb.toString().trim(); 
    } 
} 

사용 :이 방법은 나에게 100 %의 정확도를 제공했다고 말할 행복

Movie movie = new Movie(); 
movie.decodeFromStream(response.getEntity().getContent()); 

FontLearner learner = new FontLearner(); 
DefineFont2 font = null; 

List<MovieTag> objects = movie.getObjects(); 
for (MovieTag object : objects) { 
if (object instanceof DefineFont2) { 
    font = (DefineFont2) object; 
} else if (object instanceof DefineText) { 
    DefineText text = (DefineText) object; 
    if (font != null) { 
     learner.addFont(font, text); 
     font = null; 
    } 
    String line = learner.getString(text); // reverse engineers the line 
} 

StuartMacKay의 transform-swf 라이브러리를 사용하여 원래 String을 리버스 엔지니어링합니다.