All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.phloc.css.utils.CSSDataURLHelper Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2006-2014 phloc systems
 * http://www.phloc.com
 * office[at]phloc[dot]com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.phloc.css.utils;

import java.nio.charset.Charset;

import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.phloc.commons.annotations.PresentForCodeCoverage;
import com.phloc.commons.base64.Base64Helper;
import com.phloc.commons.charset.CCharset;
import com.phloc.commons.charset.CharsetManager;
import com.phloc.commons.mime.CMimeType;
import com.phloc.commons.mime.EMimeQuoting;
import com.phloc.commons.mime.IMimeType;
import com.phloc.commons.mime.MimeType;
import com.phloc.commons.mime.MimeTypeParser;
import com.phloc.commons.mime.MimeTypeUtils;
import com.phloc.commons.string.StringHelper;

/**
 * Provides data URL handling sanity methods (RFC 2397).
 * 
 * @author Philip Helger
 */
@Immutable
public final class CSSDataURLHelper
{
  /** The default charset to be used for Data URLs: US-ASCII */
  public static final Charset DEFAULT_CHARSET = CCharset.CHARSET_US_ASCII_OBJ;

  /** The default MIME type for Data URLs: text/plain;charset=US-ASCII */
  public static final IMimeType DEFAULT_MIME_TYPE = new MimeType (CMimeType.TEXT_PLAIN).addParameter (CMimeType.PARAMETER_NAME_CHARSET,
                                                                                                      DEFAULT_CHARSET.name ());

  /** The default prefix for data URLs */
  public static final String PREFIX_DATA_URL = "data:";

  /** The marker for Base64 encoding */
  public static final String BASE64_MARKER = ";base64";

  /** The separator that starts the content */
  public static final char SEPARATOR_CONTENT = ',';

  /** Data URLs should use the URL code to quote values! */
  public static final EMimeQuoting MIME_QUOTING = EMimeQuoting.URL_ESCAPE;

  private static final Logger s_aLogger = LoggerFactory.getLogger (CSSDataURLHelper.class);

  @PresentForCodeCoverage
  @SuppressWarnings ("unused")
  private static final CSSDataURLHelper s_aInstance = new CSSDataURLHelper ();

  private CSSDataURLHelper ()
  {}

  /**
   * Check if the passed URL is a data URL. It is checked, whether the passed
   * URL starts with {@value #PREFIX_DATA_URL} (after trimming).
   * 
   * @param sURL
   *        The URL to check. May be null.
   * @return true if the passed URL is a data URL,
   *         false if not.
   */
  public static boolean isDataURL (@Nullable final String sURL)
  {
    if (sURL == null)
      return false;
    final String sRealURL = sURL.trim ();
    return sRealURL.startsWith (PREFIX_DATA_URL);
  }

  /**
   * Parse a data URL into this type.
   * 
   * 
   * Syntax
   *   dataurl    := "data:" [ mediatype ] [ ";base64" ] "," data
   *   mediatype  := [ type "/" subtype ] *( ";" parameter )
   *   data       := *urlchar
   *   parameter  := attribute "=" value
   * 
* * @param sDataURL * The data URL to be parsed. May be null. * @return null if parsing failed */ @Nullable public static CSSDataURL parseDataURL (@Nullable final String sDataURL) { if (!CSSDataURLHelper.isDataURL (sDataURL)) return null; // Skip the constant prefix final String sRest = StringHelper.trimStart (sDataURL.trim (), CSSDataURLHelper.PREFIX_DATA_URL); if (StringHelper.hasNoText (sRest)) { // Plain "data:" URL - no content return new CSSDataURL (); } // comma is a special character and must be quoted in MIME type parameters final int nIndexComma = sRest.indexOf (SEPARATOR_CONTENT); int nIndexBase64 = sRest.indexOf (CSSDataURLHelper.BASE64_MARKER); boolean bBase64EncodingUsed = false; int nMIMETypeEnd; if (nIndexBase64 >= 0) { // We have Base64 if (nIndexBase64 < nIndexComma || nIndexComma < 0) { // Base64 before comma or no comma // ==> check if it is a MIME type parameter name (in // which case it is followed by a '=' character before the comma) or if // it is really the base64-encoding flag (no further '=' or '=' after // the comma). while (true) { final int nIndexEquals = sRest.indexOf (CMimeType.SEPARATOR_PARAMETER_NAME_VALUE, nIndexBase64); if (nIndexEquals < 0 || nIndexEquals > nIndexComma || nIndexComma < 0) { // It's a real base64 indicator nMIMETypeEnd = nIndexBase64; bBase64EncodingUsed = true; break; } // base64 as a MIME type parameter - check for next ;base64 nIndexBase64 = sRest.indexOf (CSSDataURLHelper.BASE64_MARKER, nIndexBase64 + CSSDataURLHelper.BASE64_MARKER.length ()); if (nIndexBase64 < 0) { // Found no base64 encoding nMIMETypeEnd = nIndexComma; break; } // Found another base64 marker -> continue } } else { // Base64 as part of data! nMIMETypeEnd = nIndexComma; } } else { // No Base64 found nMIMETypeEnd = nIndexComma; } String sMimeType = nMIMETypeEnd < 0 ? null : sRest.substring (0, nMIMETypeEnd).trim (); IMimeType aMimeType; Charset aCharset = null; if (StringHelper.hasNoText (sMimeType)) { // If no MIME type is specified, the default is used aMimeType = DEFAULT_MIME_TYPE.getClone (); aCharset = DEFAULT_CHARSET; } else { // A MIME type is present if (sMimeType.charAt (0) == CMimeType.SEPARATOR_PARAMETER) { // Weird stuff from the specs: if only ";charset=utf-8" is present than // text/plain should be used sMimeType = DEFAULT_MIME_TYPE.getAsStringWithoutParameters () + sMimeType; } // try to parse it aMimeType = MimeTypeParser.safeParseMimeType (sMimeType, EMimeQuoting.URL_ESCAPE); if (aMimeType == null) { s_aLogger.warn ("Data URL contains invalid MIME type: '" + sMimeType + "'"); return null; } // Check if a "charset" MIME type parameter is present final String sCharsetParam = MimeTypeUtils.getCharsetNameFromMimeType (aMimeType); if (sCharsetParam != null) { try { aCharset = CharsetManager.getCharsetFromName (sCharsetParam); } catch (final IllegalArgumentException ex) { // Illegal charset } if (aCharset == null) { s_aLogger.warn ("Illegal charset '" + sCharsetParam + "' contained. Defaulting to '" + DEFAULT_CHARSET.name () + "'"); } } if (aCharset == null) aCharset = DEFAULT_CHARSET; } // Get the main content data String sContent = nIndexComma < 0 ? "" : sRest.substring (nIndexComma + 1).trim (); byte [] aContent = CharsetManager.getAsBytes (sContent, aCharset); if (bBase64EncodingUsed) { // Base64 decode the content data aContent = Base64Helper.safeDecode (aContent); if (aContent == null) { s_aLogger.warn ("Failed to decode Base64 value: " + sContent); return null; } // Ignore the String content (and don't create it) because for binary // stuff like images, it does not make sense and it is most likely, that // the String content will never be used. In case it is required, the // String content is lazily initialized. sContent = null; } final CSSDataURL ret = new CSSDataURL (aMimeType, bBase64EncodingUsed, aContent, aCharset, sContent); return ret; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy