private LevelResults compareWords(BxPage expected, BxPage actual) {
    Map<BxChunk, BxWord> map = BxModelUtils.mapChunksToWords(actual);

    LevelResults results = new LevelResults();
    for (BxZone expectedZone : expected) {
      if (ignoredLabels.contains(expectedZone.getLabel())) {
        continue;
      }
      for (BxLine expectedLine : expectedZone) {
        for (BxWord expectedWord : expectedLine) {
          Set<BxWord> actualWords = new HashSet<BxWord>();
          for (BxChunk chunk : expectedWord) {
            actualWords.add(map.get(chunk));
          }
          if (actualWords.size() == 1) {
            for (BxWord actualWord : actualWords) {
              if (actualWord.childrenCount() == expectedWord.childrenCount()) {
                results.matched++;
              }
            }
          }
          results.all++;
        }
      }
    }

    return results;
  }
 private void appendWord(Document doc, Element parent, BxWord word, Object... hints) {
   Element node = doc.createElement("Word");
   appendPropertyIfNotNull(doc, node, "WordID", word.getId());
   appendBounds(doc, node, "WordCorners", word.getBounds(), hints);
   appendPropertyIfNotNull(doc, node, "WordNext", word.getNextId());
   appendProperty(doc, node, "WordNumChars", "");
   for (BxChunk chunk : word.getChunks()) {
     appendCharacter(doc, node, chunk, hints);
   }
   parent.appendChild(node);
 }
  private BxWord parseWordElement(Element wordE) {
    BxWord word = new BxWord();
    if (!(getChildren("WordCorners", wordE).isEmpty())) {
      word.setBounds(parseElementContainingVertexes(getChildren("WordCorners", wordE).get(0)));
    }

    List<Element> e = getChildren("Character", wordE);
    for (Element caE : e) {
      BxChunk ch = parseCharacterElement(caE);
      word.addChunks(ch);
    }
    return word;
  }