Return character weights for a text document with highlighted features.
    If preserve_density is True, then color for longer fragments will be
    less intensive than for shorter fragments, so that "sum" of intensities
    will correspond to feature weight.
    If preserve_density is None, then it's value is taken from
    the corresponding attribute of doc_weighted_spans.

def get_char_weights(doc_weighted_spans, preserve_density=None):
    # type: (DocWeightedSpans, Optional[bool]) -> np.ndarray
    """ Return character weights for a text document with highlighted features.
    If preserve_density is True, then color for longer fragments will be
    less intensive than for shorter fragments, so that "sum" of intensities
    will correspond to feature weight.
    If preserve_density is None, then it's value is taken from
    the corresponding attribute of doc_weighted_spans.
    """
    if preserve_density is None:
        preserve_density = doc_weighted_spans.preserve_density
    char_weights = np.zeros(len(doc_weighted_spans.document))
    feature_counts = Counter(f for f, _, __ in doc_weighted_spans.spans)
    for feature, spans, weight in doc_weighted_spans.spans:
        for start, end in spans:
            # start can be -1 for char_wb at the start of the document.
            start = max(0, start)
            if preserve_density:
                weight /= (end - start)
            weight /= feature_counts[feature]
            char_weights[start:end] += weight
    return char_weights