Пример #1
0
  protected void checkHRef(XMLElement e, String attrNS, String attr) {
    String href = e.getAttributeNS(attrNS, attr);
    if (href == null) {
      return;
    }
    href = href.trim();
    if (href.isEmpty()) {
      // if href="" then selfreference which is valid,
      // but as per issue 225, issue a hint
      report.message(
          MessageId.HTM_045,
          EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber(), href));
      return;
    } else if (href.contains("#epubcfi")) {
      return; // temp until cfi implemented
    } else if (".".equals(href)) {
      // selfreference, no need to check
      return;
    }

    URI uri = checkURI(href);
    if (uri == null) return;

    if ("http".equals(uri.getScheme())) {
      report.info(path, FeatureEnum.REFERENCE, href);
    }

    /*
     * mgy 20120417 adding check for base to initial if clause as part of
     * solution to issue 155
     */
    if (URISchemes.contains(uri.getScheme())
        || (null != base && URISchemes.contains(base.getScheme()))) {
      return;
    }
    // This if statement is needed to make sure XML Fragment identifiers
    // are not reported as non-registered URI scheme types
    else if (uri.getScheme() != null) {
      report.message(
          MessageId.HTM_025,
          EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber(), href));
      return;
    }

    try {
      href = PathUtil.resolveRelativeReference(path, href, base == null ? null : base.toString());
    } catch (IllegalArgumentException err) {
      report.message(
          MessageId.OPF_010,
          EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber(), href),
          err.getMessage());
      return;
    }
    processHyperlink(href);
  }
Пример #2
0
 public void runChecks() {
   OCFPackage ocf = context.ocf.get();
   if (!ocf.hasEntry(path)) {
     report.message(MessageId.RSC_001, EPUBLocation.create(ocf.getName()), path);
   } else if (!ocf.canDecrypt(path)) {
     report.message(MessageId.RSC_004, EPUBLocation.create(ocf.getName()), path);
   } else if (!path.endsWith(".xml")) {
     report.message(MessageId.OPF_080, EPUBLocation.create(path));
   } else {
     validate();
   }
 }
Пример #3
0
  public void endElement() {
    openElements--;
    XMLElement e = parser.getCurrentElement();
    String ns = e.getNamespace();
    String name = e.getName();

    if (openElements == 0) {
      report.info(path, FeatureEnum.CHARS_COUNT, Long.toString(charsCount));
      if (!epubTypeInUse) {
        if (context.version == EPUBVersion.VERSION_3) {
          report.message(MessageId.ACC_007, EPUBLocation.create(path));
        }
      } else {
        epubTypeInUse = false;
      }
    }

    ElementLocation currentLocation = elementLocationStack.pop();

    if (EpubConstants.HtmlNamespaceUri.equals(ns)) {

      if ("script".equals(name)) {
        String attr = e.getAttribute("type");
        report.info(path, FeatureEnum.HAS_SCRIPTS, (attr == null) ? "" : attr);
      } else if ("style".equals(name)) {
        String style = textNode.toString();
        if (style.length() > 0) {
          CSSCheckerFactory.getInstance()
              .newInstance(context, style, currentLocation.getLineNumber(), false)
              .runChecks();
        }
        textNode = null;
      } else if ("head".equals(name)) {
        checkStylesheetFallback();
      } else if ("table".equals(name)) {
        if (tableDepth > 0) {
          --tableDepth;
          EPUBLocation location =
              EPUBLocation.create(
                  path,
                  currentLocation.getLineNumber(),
                  currentLocation.getColumnNumber(),
                  "table");

          checkDependentCondition(MessageId.ACC_005, tableDepth == 0, hasTh, location);
          checkDependentCondition(MessageId.ACC_006, tableDepth == 0, hasThead, location);
          checkDependentCondition(MessageId.ACC_012, tableDepth == 0, hasCaption, location);

          hasTh = hasThead = hasCaption = false;
        }
      }
    }
  }
Пример #4
0
 protected URI checkURI(String uri) {
   try {
     return new URI(Preconditions.checkNotNull(uri).trim());
   } catch (URISyntaxException e) {
     report.message(MessageId.RSC_020, parser.getLocation(), uri);
     return null;
   }
 }
Пример #5
0
  @Override
  public boolean validate() {
    SearchDictionary validTypes =
        new SearchDictionary(SearchDictionary.DictionaryType.SVG_MEDIA_TYPES);

    boolean isGlobalFixed = EpubPackage.isGlobalFixed(this.epack);

    Hashtable<String, SpineItem> spineItems = new Hashtable<String, SpineItem>();
    for (int i = 0; i < epack.getSpine().itemsLength(); ++i) {
      SpineItem si = epack.getSpine().getItem(i);
      spineItems.put(si.getIdref(), si);
    }

    for (int i = 0; i < epack.getManifest().itemsLength(); i++) {
      ManifestItem itemEntry = epack.getManifest().getItem(i);

      if (validTypes.isValidMediaType(itemEntry.getMediaType())) {
        String fileToParse = epack.getManifestItemFileName(itemEntry);

        ZipEntry entry = epack.getZip().getEntry(fileToParse);
        if (entry == null) {
          report.message(
              MessageId.RSC_001, new MessageLocation(epack.getFileName(), -1, -1), fileToParse);
          continue;
        }

        SpineItem si = spineItems.get(itemEntry.getId());
        boolean itemIsFixedFormat = isGlobalFixed;
        if (si != null) {

          String properties = si.getProperties();
          if (properties != null) {
            if (properties.length() != 0) {
              properties = properties.replaceAll("[\\s]+", " ");
              String propertyArray[] = properties.split(" ");
              for (String prop : propertyArray) {
                if (prop.equals("rendition:layout-pre-paginated")) {
                  itemIsFixedFormat = true;
                } else if (prop.equals("rendition:layout-reflowable")) {
                  itemIsFixedFormat = false;
                }
              }
            }
          }
        }
        if (itemIsFixedFormat) {
          checkSvgDoc(fileToParse);
        }
      }
    }
    return true;
  }
Пример #6
0
 protected void checkStylesheetFallback() {
   // stylesheet is considered as having "built-in" fallback if
   // at least one is found with a blessed CMT (i.e. text/css).
   // Implem note: xrefChecker is necessarily present if
   // nonStandardStylesheetLink is present.
   if (nonStandardStylesheetLink.isPresent()
       && !hasCss
       && !xrefChecker
           .get()
           .hasValidFallback(nonStandardStylesheetLink.get().getContext().get())
           .or(false)) {
     report.message(MessageId.CSS_010, nonStandardStylesheetLink.get());
   }
 }
Пример #7
0
 void checkViewBox(String svgDocEntry, Document doc) {
   NodeList n = doc.getElementsByTagNameNS(svgNS, "svg");
   for (int i = 0; i < n.getLength(); i++) {
     Element svgElement = (Element) n.item(i);
     String viewport = svgElement.getAttributeNS(svgNS, "viewBox");
     if (viewport == null || viewport.length() == 0) {
       report.message(
           MessageId.HTM_048,
           new MessageLocation(
               svgDocEntry,
               XmlDocParser.getElementLineNumber(svgElement),
               XmlDocParser.getElementColumnNumber(svgElement)));
     }
   }
 }
Пример #8
0
 void checkImageXlinkHrefInline(String svgDocEntry, Document doc) {
   NodeList n = doc.getElementsByTagNameNS(svgNS, "image");
   for (int i = 0; i < n.getLength(); i++) {
     Element svgElement = (Element) n.item(i);
     String href = svgElement.getAttributeNS(xlinkNS, "href");
     if (href != null && href.length() > 0) {
       if (!href.startsWith("data:image")) {
         report.message(
             MessageId.MED_006,
             new MessageLocation(
                 svgDocEntry,
                 XmlDocParser.getElementLineNumber(svgElement),
                 XmlDocParser.getElementColumnNumber(svgElement)));
       }
     }
   }
 }
Пример #9
0
 private void processRef(String ref, XRefChecker.Type type) {
   if (ref != null && context.xrefChecker.isPresent()) {
     ref = PathUtil.resolveRelativeReference(path, ref, null);
     if (type == XRefChecker.Type.AUDIO) {
       String mimeType = context.xrefChecker.get().getMimeType(ref);
       if (mimeType != null && !OPFChecker30.isBlessedAudioType(mimeType)) {
         report.message(
             MessageId.MED_005,
             EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()),
             ref,
             mimeType);
       }
     }
     context
         .xrefChecker
         .get()
         .registerReference(path, parser.getLineNumber(), parser.getColumnNumber(), ref, type);
   }
 }
Пример #10
0
  public boolean validate() {
    int fatalErrorsSoFar = report.getFatalErrorCount();
    int errorsSoFar = report.getErrorCount();
    int warningsSoFar = report.getWarningCount();
    SearchKeyMapHandler handler;
    XMLParser parser = new XMLParser(context);
    handler = new SearchKeyMapHandler(context, parser);
    parser.addValidator(XMLValidators.SEARCH_KEY_MAP_RNC.get());
    parser.addXMLHandler(handler);
    parser.process();

    return fatalErrorsSoFar == report.getFatalErrorCount()
        && errorsSoFar == report.getErrorCount()
        && warningsSoFar == report.getWarningCount();
  }
Пример #11
0
  public void endElement() {

    XMLElement e = parser.getCurrentElement();
    if (e.getNamespace().equals("http://purl.org/dc/elements/1.1/")) {
      String name = e.getName();
      if (name.equals("identifier")) {
        String idAttr = e.getAttribute("id");
        if (idAttr != null && !idAttr.equals("") && idAttr.equals(uniqueIdent)) {
          //					String idval = (String) e.getPrivateData();
          //					if (idval != null && ocf != null)
          //						ocf.setUniqueIdentifier(idval);
        }
      } else if (name.equals("date")) {
        String dateval = (String) e.getPrivateData();
        boolean valid = true;
        String detail = null;

        if (dateval == null || "".equals(dateval)) {
          valid = false;
          detail = "zero-length string";
        } else {
          DateParser dateParser = new DateParser();
          try {
            Date date = dateParser.parse(dateval.trim());
            /*
             * mg: DateParser does not enforce four-digit years,
             * which http://www.w3.org/TR/NOTE-datetime seems to want
             */
            String year = new SimpleDateFormat("yyyy").format(date);
            if (year.length() > 4) throw new InvalidDateException(year);
          } catch (InvalidDateException d) {
            valid = false;
            detail = d.getMessage();
          }
        }

        if (!valid) {
          if (this.version == EPUBVersion.VERSION_3) {
            report.warning(
                path,
                parser.getLineNumber(),
                parser.getColumnNumber(),
                "date value '"
                    + (dateval == null ? "" : dateval)
                    + "' does not follow recommended syntax as per http://www.w3.org/TR/NOTE-datetime:"
                    + detail);
          } else {
            report.error(
                path,
                parser.getLineNumber(),
                parser.getColumnNumber(),
                "date value '"
                    + (dateval == null ? "" : dateval)
                    + "' is not valid as per http://www.w3.org/TR/NOTE-datetime:"
                    + detail);
          }
        }
      } else if (name.equals("title") || name.equals("language")) {
        // issue 138: issue a warning if dc:title and dc:language is empty for 2.0 and 2.0.1
        // note that an empty dc:identifier is checked in opf20.rng and will
        // therefore be reported as an error, that may or may not be a good idea.

        if (version == EPUBVersion.VERSION_2) {
          String value = (String) e.getPrivateData();
          if (value == null || value.trim().length() < 1) {
            report.warning(
                path, parser.getLineNumber(), parser.getColumnNumber(), name + " element is empty");
          }
        }
      }
    }
  }
Пример #12
0
  public void startElement() {
    boolean registerEntry = true;
    XMLElement e = parser.getCurrentElement();
    String ns = e.getNamespace();
    if (ns == null
        || ns.equals("")
        || ns.equals("http://openebook.org/namespaces/oeb-package/1.0/")
        || ns.equals("http://www.idpf.org/2007/opf")) {
      String name = e.getName();
      if (name.equals("package")) {
        if (!ns.equals("http://www.idpf.org/2007/opf")) {
          report.warning(
              path,
              parser.getLineNumber(),
              parser.getColumnNumber(),
              "OPF file is using OEBPS 1.2 syntax allowing backwards compatibility");
          opf12PackageFile = true;
        }
        /*
         * This section checks to see the value of the unique-identifier
         * attribute and stores it in the String uniqueIdent or reports
         * an error if the unique-identifier attribute is missing or
         * does not have a value
         */
        String uniqueIdentAttr = e.getAttribute("unique-identifier");
        if (uniqueIdentAttr != null && !uniqueIdentAttr.equals("")) {
          uniqueIdent = uniqueIdentAttr;
        } else {
          report.error(
              path,
              parser.getLineNumber(),
              parser.getColumnNumber(),
              "unique-identifier attribute in package element must be present and have a value");
        }
      } else if (name.equals("item")) {
        String id = e.getAttribute("id");
        String href = e.getAttribute("href");
        if (href != null && !(version == EPUBVersion.VERSION_3 && href.startsWith("http://"))) {
          try {
            href = PathUtil.resolveRelativeReference(path, href, null);
          } catch (IllegalArgumentException ex) {
            report.error(path, parser.getLineNumber(), parser.getColumnNumber(), ex.getMessage());
            href = null;
          }
        }
        String mimeType = e.getAttribute("media-type");
        String fallback = e.getAttribute("fallback");
        String fallbackStyle = e.getAttribute("fallback-style");
        String namespace = e.getAttribute("island-type");
        String properties = e.getAttribute("properties");
        if (properties != null) properties = properties.replaceAll("[\\s]+", " ");

        if (version == EPUBVersion.VERSION_3
            && href.startsWith("http://")
            && !OPFChecker30.isBlessedAudioType(mimeType))
          if (OPFChecker30.isCoreMediaType(mimeType)) {
            report.error(
                path,
                parser.getLineNumber(),
                parser.getColumnNumber(),
                "Only audio and video remote resources are permitted");
          } else {
            // mgy 20120414: this shouldn't even be a warning
            //						report.warning(
            //								path,
            //								parser.getLineNumber(),
            //								parser.getColumnNumber(),
            //								"Remote resource not validated");
          }

        OPFItem item =
            new OPFItem(
                id,
                href,
                mimeType,
                fallback,
                fallbackStyle,
                namespace,
                properties,
                parser.getLineNumber(),
                parser.getColumnNumber());

        if (id != null) itemMapById.put(id, item);
        if (properties != null) {
          String propertyArray[] = properties.split(" ");
          for (int i = 0; i < propertyArray.length; i++) {
            if (propertyArray[i].equals("nav")) item.setNav(true);
            if (propertyArray[i].equals("scripted")) item.setScripted(true);
          }
        }
        if (href != null && registerEntry) {
          itemMapByPath.put(href, item);
          items.add(item);
        }
      } else if (name.equals("reference")) {
        String type = e.getAttribute("type");
        String title = e.getAttribute("title");
        String href = e.getAttribute("href");
        if (href != null && xrefChecker != null) {
          try {
            href = PathUtil.resolveRelativeReference(path, href, null);
            xrefChecker.registerReference(
                path,
                parser.getLineNumber(),
                parser.getColumnNumber(),
                href,
                XRefChecker.RT_GENERIC);
          } catch (IllegalArgumentException ex) {
            report.error(path, parser.getLineNumber(), parser.getColumnNumber(), ex.getMessage());
            href = null;
          }
        }
        OPFReference ref =
            new OPFReference(type, title, href, parser.getLineNumber(), parser.getColumnNumber());
        refs.add(ref);
      } else if (name.equals("spine")) {
        String idref = e.getAttribute("toc");
        if (idref != null) {
          toc = (OPFItem) itemMapById.get(idref);
          if (toc == null)
            report.error(
                path,
                parser.getLineNumber(),
                parser.getColumnNumber(),
                "item with id '" + idref + "' not found");
          else {
            toc.setNcx(true);
            if (toc.getMimeType() != null && !toc.getMimeType().equals("application/x-dtbncx+xml"))
              report.error(
                  path,
                  parser.getLineNumber(),
                  parser.getColumnNumber(),
                  "toc attribute references resource with non-NCX mime type; \"application/x-dtbncx+xml\" is expected");
          }
        }
      } else if (name.equals("itemref")) {
        String idref = e.getAttribute("idref");
        if (idref != null) {
          OPFItem item = getItemById(idref);
          if (item != null) {
            spine.add(item);
            item.setInSpine(true);

            String linear = e.getAttribute("linear");
            if (linear != null && "no".equals(linear.trim())) {
              item.setSpineLinear(false);
            } else {
              item.setSpineLinear(true);
            }

          } else {
            report.error(
                path,
                parser.getLineNumber(),
                parser.getColumnNumber(),
                "item with id '" + idref + "' not found");
          }
        }
      } else if (name.equals("dc-metadata") || name.equals("x-metadata")) {
        if (!opf12PackageFile)
          report.error(
              path,
              parser.getLineNumber(),
              parser.getColumnNumber(),
              "use of deprecated element '" + name + "'");
      }
    } else if (ns.equals("http://purl.org/dc/elements/1.1/")) {
      // in the DC metadata, when the <identifier> element is parsed, if
      // it has a non-null and non-empty id attribute value that is the
      // same as the value of the unique-identifier attribute of the
      // package element, set uniqueIdentExists = true (to make sure that
      // the unique-identifier attribute references an existing
      // <identifier> id attribute
      String name = e.getName();
      if (name.equals("identifier")) {
        String idAttr = e.getAttribute("id");
        if (idAttr != null && !idAttr.equals("") && idAttr.equals(uniqueIdent))
          uniqueIdentExists = true;
      } else if (name.equals("creator")) {
        String role = e.getAttributeNS("http://www.idpf.org/2007/opf", "role");
        if (role != null && !role.equals("")) {
          if (!isValidRole(role))
            report.error(
                path,
                parser.getLineNumber(),
                parser.getColumnNumber(),
                "role value '" + role + "' is not valid");
        }
      }
    }
  }
Пример #13
0
 // Report the message id when primary condition1 is true but dependent
 // condition2 is false.
 protected void checkDependentCondition(
     MessageId id, boolean condition1, boolean condition2, EPUBLocation location) {
   if (condition1 && !condition2) {
     report.message(id, location);
   }
 }
Пример #14
0
 protected void checkIFrame(XMLElement e) {
   report.message(
       MessageId.HTM_036,
       EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber(), e.getName()));
 }