Example #1
0
    def newString(cls,
                  s,
                  context,
                  e=None,
                  style=None,
                  w=None,
                  h=None,
                  pixelFit=True):
        """Answer a InDesignString instance from valid attributes in *style*. Set all values after testing
        their existence, so they can inherit from previous style formats.
        If target width *w* or height *h* is defined, then *fontSize* is scaled to make the string fit *w* or *h*.

        >>> from pagebot.toolbox.units import pt
        >>> from pagebot.contexts.indesigncontext import InDesignContext
        >>> context = InDesignContext()
        >>> bs = InDesignString.newString('AAA', context, style=dict(fontSize=pt(30)))
        >>> #bs.s.lines()
        >>> #'indesign.text.text' in str(bs)
        True
        """
        if style is None:
            style = {}

        sUpperCase = css('uppercase', e, style)
        sLowercase = css('lowercase', e, style)
        sCapitalized = css('capitalized', e, style)
        if sUpperCase:
            s = s.upper()
        elif sLowercase:
            s = s.lower()
        elif sCapitalized:
            s = s.capitalize()

        # Since Indesign does not do font GSUB feature compile, we'll make the transformed string here,
        # using Tal's https://github.com/typesupply/compositor
        # This needs to be installed, in case PageBot is running outside of DrawBot.

        font = style.get('font')
        if font is not None and not isinstance(font, str):
            font = font.path
        if font is None or not os.path.exists(font):
            font = DEFAULT_FONT_PATH
        fontSize = style.get('fontSize', DEFAULT_FONT_SIZE)
        assert isUnit(fontSize), (
            '%s.newString: FontSize %s must be of type Unit' %
            (cls.__name__, fontSize))
        leading = style.get('leading', DEFAULT_LEADING)
        assert isUnit(leading), (
            '%s.newString: Leading %s must be of type Unit' %
            (cls.__name__, leading))
        inDesignFont = findFont(font)
        #strike = context.b.strike(indesignFont)
        #strike.size(fontSize.pt, leading.pt, units='pt')
        #if w is not None:
        #    strike.width = w
        #s = strike.text(s)
        s = ''
        return cls(s, context=context,
                   style=style)  # Make real Indesign flavor BabelString here.
Example #2
0
def getLineHeight(leading, fontSize):
    """

    >>> from pagebot.toolbox.units import pt, em
    >>> getLineHeight(em(1.5), pt(12))
    18
    >>> getLineHeight(pt(15), pt(12))
    15
    >>> getLineHeight(pt(19), None)
    19
    >>> getLineHeight(15, pt(16))
    240
    """
    assert leading is not None

    if isinstance(leading, RelativeUnit):
        lineHeight = upt(leading.byBase(fontSize))
    elif isinstance(leading, Unit):
        lineHeight = upt(leading)
    elif isUnit(fontSize):
        # Leading is scalar?
        lineHeight = leading * fontSize.pt
    else:
        # Both scalar?
        lineHeight = leading * fontSize

    return lineHeight
Example #3
0
 def _set_padding(self, padding):
     # Can be 123, [123], [123, 234] or [123, 234, 345, 4565, ]
     if isUnit(padding) or isinstance(padding, (int, float)):
         padding = [padding]
     if len(padding) == 1:  # All same value
         padding = (padding[0], padding[0], padding[0], padding[0],
                    padding[0], padding[0])
     elif len(padding) == 2:  # pt == pb, pl == pr, pzf == pzb
         padding = (padding[0], padding[1], padding[0], padding[1],
                    padding[0], padding[1])
     elif len(padding) == 3:  # pt == pl == pzf, pb == pr == pzb
         padding = (padding[0], padding[1], padding[2], padding[0],
                    padding[1], padding[2])
     elif len(padding) == 4:  # pt, pr, pb, pl, 0, 0
         padding = (padding[0], padding[1], padding[2], padding[3], 0, 0)
     elif len(padding) == 6:
         pass
     else:
         raise ValueError
     self.pt, self.pr, self.pb, self.pl, self.pzf, self.pzb = padding
Example #4
0
def getRootStyle(u=None, w=None, h=None, **kwargs):
    """Answers the main root style tha contains all default style attributes of
    PageBot. To be overwritten when needed by calling applications.
    CAPITALIZED attribute names are for reference only. Not used directly from
    styles. They can be copied on other style attributes. Note that if the
    overall unit style.u is changed by the calling application, also the
    U-based values must be recalculated for proper measures.

    >>> rs = getRootStyle()
    >>> rs['name']
    'root'
    >>> rs['pt'] # Padding top: U*7
    42pt
    """
    if u is None:
        u = U

    # Some calculations to show dependencies.
    defaultLeading = DEFAULT_LEADING
    baselineGrid = pt(BASELINE_GRID)
    # Indent of lists. Needs to be the same as in tabs, to position rightly after bullets
    if not isUnit(u):
        u = pt(u)
    listIndent = u * 0.8  # The order or multiplication matters, in case u is a Unit instance.
    gutter = u  # Make default gutter equal to the page unit.

    pt0 = pt(0)  # Standard initialize on Unit zero
    em0 = em(0)

    rs = dict(  # Answer the default root style. Style is a clean dictionary
        name='root',  # Name of the style, key in document.getRootstyle( )
        cssClass=None,  # Optional CSS class of local element. Ignored if None.
        tag=None,  # Optional marker to match the style with the running tag.
        show=
        True,  # If set to False, then the element does not evaluate in the self.elements loop.

        # Basic page/template/element positions. Can contain number values or Unit instances.
        x=pt0,  # Default local origin, relative to parent.
        y=pt0,
        z=pt0,
        # Basic page/template/element proportions of box. Can contain number values or Unit instances.
        w=w,  # Default page width, basis size of the document. Point rounding of 210mm, international generic fit.
        h=h,  # Default page height, basic size of the document. 11", international generic fit.
        d=pt0,  # Optional "depth" of an document, page or element. Default has all element in the same z-level.

        # For rotation, the point (x+rx, y+ry) is used as rotation center. Default is (x, y).
        rx=pt0,
        ry=pt0,
        angle=degrees(0),  # Angle in degrees or radians units.

        # In "time-dimension" this is an overall value for export. This works independent from
        # the time-marks of element attributes.
        # In case saving as .mov or .gif, this value defines 1/frames_per_second
        frameDuration=DEFAULT_FRAME_DURATION,

        # Resolution in dpi for pixel based publications and elements.
        resolution=pt(72),

        # Optional folds property. Keep None or empty list if no folds. Otherwise list of [(x1, None), ...]
        # for vertical folds or [(None, y1), ...] for horizontal folds. Also the x and y values can be
        # combined as in [(x1, y1), ...]
        folds=None,

        # Position of origin. DrawBot has y on bottom-left. In PageBot it is optional. Default is top-left.
        # Note that the direcion of display is always upwards. This means that the position of text and elements
        # goes downward from the top, they are not flipped vertical. It is up to the caller to make sure
        # there is enough space for elements to show themselves on top of a given position.
        # originTop often goes with yAlign = TOP.
        originTop=
        False,  # TODO: Setting to  default True has currently positioning bugs.

        # Alignment of origin on element. Note that formatted text string are aligned by the xTextAlign attribute.
        xAlign=LEFT,  # Default alignment, one of ('left', 'center'. 'right')
        yAlign=
        TOP,  # Default alignment for elements like image, that float in their designated space.
        zAlign=
        FRONT,  # Default alignment in z-axis is in front, closest to the viewer.

        # Although it is common to talk about the "margins" on a page, as the space between elements
        # and the side of the page, this naming is not conform the current CSS definition.
        # To guarantee compatibility with CSS export, it seems better to use the same naming.
        # Margins define the space outside an element (or page) around the object.
        # Padding defines the space inside the element.

        # Margins, outside element box. Can contain number values or Unit instances.
        mt=pt0,  # Margin top
        ml=pt0,  # Margin left
        mr=pt0,  # Margin right
        mb=pt0,  # Margin bottom
        mzf=pt0,  # Margin “near” front in z-axis direction, closest to viewer.
        mzb=pt0,  # Margin “far” back in z-axis direction.

        # Basic grid units in 3 directions. In case it holds a number it interprets as pt(value), points as 1/72".
        # Otherwise a Unit instance should be used.
        xUnits=u,  # Base unit for Dutch/Swiss typography :)
        yUnits=u,
        zUnits=u,

        # Padding where needed, inside elemen box. Can contain number values or Unit instances.
        # Multiplication order (u*7 instead of 7*u) matters, in case u is a Unit instance.
        pt=u * 7,  # Padding top, identical to default start of baseline grid
        pl=u * 7,  # Padding left
        pr=u * 6,  # Padding right
        pb=u * 6,  # Padding bottom
        pzf=pt0,  # Padding “near” front in z-axis direction, closest to viewer.
        pzb=pt0,  # Padding ”far” back in z-axis direction.

        # Borders, independent for all sides, value is thickness of the line.
        # None will show no border. Single value > 0 shows black line of that thickness.
        # Other options need to be store in dictionary value.
        # Borders hold dictionaries of format
        # border = dict(strokeWidth=3, line=lineType, stroke=color(1, 0, 0, 0,5), dash=(4,4))
        # where lineType is one of (INLINE, ONLINE, OUTLINE)
        borderTop=None,  # Border top.
        borderLeft=None,  # Border left
        borderRight=None,  # Border right
        borderBottom=None,  # Border bottom

        # Grid definitions, used by static media as well as CSS display: grid; exports.
        # gridX, gridY and gridZ are optional lists of grid line positions, to allow the use of non-repeating grids.
        # The format is [(width1, gutter1), (width2, gutter2), (None, 0)] in case different gutters are needed.
        # If the format is [width1, width2, (width3, gutter3)], then the missing gutters are used from gw or gh.
        # If this paramater is set, then the style values for column width "cw" and column gutter "gw" are ignored.
        # If a width is None, it is assumed to fill the rest of the available space. If there are multiple widths
        # defined as None, then the remaining width is equally devided from the element.parent.w
        # If the width is a float between 0..1 or a string with format "50%" then these are interpreted as percentages.
        # If there are multiple None widths, then their values are calculated from an equal division of available space.
        # It is up to the caller to make sure that the grid values fit the width of the current element.
        #
        # HTML/CSS builders convert to:
        # grid-template-columns, grid-template-rows, grid-auto-rows, grid-column-gap, grid-row-gap,
        gridX=None,
        # Optional list of vertical grid line positions, to force the use of non-repeating grids.
        # Format is [(height1, gutter1), (None, gutter2), (None, 0)]
        gridY=None,
        gridZ=None,  # Similar to gridX and gridY.

        # Gutter is used a standard distance between columns. Note that when not-justifying, the visual
        # gutter on the right side of columns seems to be larger. This can be compensated for in the
        # distance between images.
        gw=gutter,  # Main gutter width of page columns. Based on U.
        gh=gutter,  # Gutter height
        gd=gutter,  # Optional gutter depth, in z-direction

        # The columns with, height and depth are used to fit a certain amount of colums with defined
        # width on the page padded width (self.pw), using (self.gw, self.gh, self.gd) as gutters.
        # If (cw, ch, cd) are defined, (gridX, gridY, gridZ) must be None or order to be used.
        cw=None,
        ch=
        None,  # Approximately square with cw + gutter: 77. Order matters in case u is Unit
        cd=None,  # Optional column "depth"

        # Flags indicating on which side of self.pw the columns start. The rest space will be posisioned
        # on the other side.
        columnAlignX=LEFT,
        columnAlignY=TOP,

        # If gridX and cd are undefined, then use the self.columnsX count and self.gw to fit that number
        # of columns on page padded width (self.pw), where the column widths are calculated.
        # If columnsX, etc. are defined, self.cw and self.gridX must be None, in order to be used.
        columnsX=2,
        columnsY=1,
        columnsZ=1,

        # Overall content scaling.
        scaleX=
        1,  # If set, then the overall scaling of an element draw is done, keeping the (x,y) unscaled.
        scaleY=
        1,  # To be used in pairing of x, y = e._setScale(x, y) and e._resetScale()
        scaleZ=1,  # Optional scaling in z-direction, depth.

        # Shadow & Gradient
        shadow=None,  # Contains options Shadow instance.
        gradient=None,  # Contains optional Gradient instance.

        # Typographic defaults
        font=
        DEFAULT_FONT_PATH,  # Default is to avoid existing font and fontSize in the graphic state.
        fallbackFont=DEFAULT_FALLBACK_FONT_PATH,
        fontSize=
        DEFAULT_FONT_SIZE,  # Default font size in points, related to U. If FIT, size is elastic to width.
        uppercase=False,  # All text in upper case
        lowercase=False,  # All text in lower case (only if uppercase is False
        capitalized=
        False,  # All words with initial capitals. (only of not uppercase and not lowercase)

        # Axis location of the Variable Font to create the font instance. E.g. dict(wght=45, opsz=12)
        variableLocation=None,
        # If True, round the location values for fitString to whole numbers, to avoid too many cached instances.
        roundVariableLocation=True,

        # List of supported OpenType features.
        # c2pc, c2sc, calt, case, cpsp, cswh, dlig, frac, liga, lnum, onum, ordn, pnum, rlig, sinf,
        # smcp, ss01, ss02, ss03, ss04, ss05, ss06, ss07, ss08, ss09, ss10, ss11, ss12, ss13, ss14,
        # ss15, ss16, ss17, ss18, ss19, ss20, subs, sups, swsh, titl, tnum
        openTypeFeatures=None,

        # Horizontal spacing for absolute and fontsize-related measures
        tracking=
        pt0,  # Absolute tracking value. Note that this is different from standard name definition.
        # Set tabs,tuples of (float, alignment) Alignment can be “left”, “center”, “right”
        # or any other character. If a character is provided the alignment will be right and
        # centered on the specified character.
        listTabs=[
            (listIndent, LEFT)
        ],  # Default indent for bullet lists. Copy onto style.tabs for usage.
        listIndent=
        listIndent,  # Indent for bullet lists, Copy on style.indent for usage in list related styles.
        listBullet=
        u'•\t',  # Default bullet for bullet list. Can be changed for ordered/numbered lists.
        tabs=
        None,  # Tabs for FormattedString, copy e.g. from listTabs. [(index, alignment), ...]
        firstLineIndent=
        pt0,  # Indent of first line of a paragraph in a text tag.
        firstParagraphIndent=
        pt0,  # Indent of first line of first paragraph in a text tag.
        firstColumnIndent=
        pt0,  # Indent of first line in a column, after start of new column (e.g. by overflow)
        indent=pt0,  # Left indent (for left-right based scripts)
        tailIndent=pt0,  # Tail/right indent (for left-right based scripts)

        # Vertical spacing of baselines by TextBox
        # Note that PageView is drawing the baseline grid color as defined by viewGridStrokeX and viewGridStrokeXWidth
        baselineGrid=baselineGrid,
        baselineGridStart=
        None,  # Optional baselineGridStart if different from top padding page.pt
        baseLineMarkerSize=pt(
            8),  # FontSize of markers showing base line grid info.
        baselineShift=
        pt0,  # Absolute baseline shift in points. Positive value is upward.
        baselineColor=color(
            0.7),  # Baseline color, drawn by PageView and TextBox
        baselineWidth=pt(0.5),  # Baseline width, drawn by TextBox
        baselineGridFit=False,
        firstLineGridFit=True,
        # Leading and vertical space
        leading=defaultLeading,  # Relative factor to current fontSize.
        paragraphTopSpacing=
        pt0,  # Only works if there is a prefix style value != 0
        paragraphBottomSpacing=
        pt0,  # Only works if there is a postfix style value != 0
        # Keep all of the lines of the node text block in the same column.
        keepInColumn=False,
        # Check if this space is available above, to get amount of text lines above headings.
        needsAbove=pt0,
        # Check if this relative fontSize space is available above, to get amount of text lines above headings.
        rNeedsAbove=em0,
        # Check if this point space is available below, to get amount of text lines below headings.
        needsBelow=pt0,
        # CSS-behavior as <div> and <span>, adding trailing \n to block context is value set to DISPLAY_BLOCK
        # Interpreted by
        display=DISPLAY_INLINE,

        # Language and hyphenation
        language=
        DEFAULT_LANGUAGE,  # Language for hyphenation and spelling. Can be altered per style in FormattedString.
        encoding='utf-8',
        hyphenation=True,
        # Strip pre/post white space from e.text and e.tail and substitute by respectively prefix and postfix
        # if they are not None. Set to e.g. newline(s) "\n" or empty string, if tags need to glue together.
        # Make None for no stripping
        prefix=
        '',  # Default is to strip white space from a block. Make None for no stripping.
        postfix=
        '',  # Default is to strip white space from tail of XML tag block into a single space.

        # Paging
        pageIdMarker=
        '#??#',  # The text pattern will be replaced by current page id.
        # First page number of the document. Note that “page numbers” can be strings too, as long as pages
        # can define what is “next page”, when referred to by a flow.
        firstPageId=1,  # Needs to be a number.

        # Flag that indicates if errors and warning should be written to the element.report list.
        verbose=True,

        # Element color
        fill=
        noColor,  # Default is no color for filling rectangle. Instead textFill color is set default black.
        stroke=
        noColor,  # Default is to have no stroke on drawing elements. Not for text.
        strokeWidth=None,  # Stroke thickness for drawing element, not text.

        # Text color
        textFill=
        blackColor,  # Separate between the fill of a text box and the color of the text itself.
        textStroke=noColor,  # No stroke of color text by default.
        textStrokeWidth=None,
        textShadow=None,
        textGradient=None,
        xTextAlign=
        LEFT,  # Alignment of text inside text boxes, one of (LEFT, CENTER, RIGHT, JUSTIFIED), independent of inside FS.
        yTextAlign=
        TOP,  # Alignment of text inside text boxes, one of (TOP, MIDDLE, BOTTOM)
        zTextAligh=
        FRONT,  # Alignment of text inside a 3d text box, one of (FRONT, MIDDLE, BACK)
        underlinePosition=
        None,  # Underline position and thickness of BabelString/FormattedString
        underlineThickness=None,

        # V I E W S

        # These parameters are used by viewers (implemented as properties), normally not part
        # of direct elements.css( ) queries as views may locally change these values.
        # However, in some situations elements may overwrite the settings (e.g. TextBot baseline color)

        # Paging
        showSpread=
        False,  # If True, show even pages on left of fold, odd on the right.
        showSpreadMiddleAsGap=
        0,  # If showing as spread, this is the gap between them.

        # Document/page stuff
        viewMinInfoPadding=pt(
            20
        ),  # Minimum padding needed to show meta info. Otherwise truncated to 0 and not showing meta info.
        showCropMarks=False,
        showRegistrationMarks=False,
        showOrigin=False,  # Show page origin crosshair marker
        showPadding=False,
        showFrame=False,  # Draw frame on page.size
        showNameInfo=False,  # Show file/name/pagenumber ourside cropping area
        showPageMetaInfo=False,

        # Element info showing
        showElementInfo=False,
        showDimensions=False,  # TODO: Does not work if there is view padding.
        showMissingElement=True,

        # Grid stuff using a selected set of (GRID_COL, GRID_ROW, GRID_SQR, GRID_COL_BG, GRID_ROW_BG, GRID_SQR_BG)
        # See pagebot.constants for the types of grid that can be drawn.
        showGrid=set(
        ),  # If set, display the type of grid elements on foreground and background

        # Types of baseline grid to be drawn using conbination set of (BASE_LINE, BASE_INDEX_LEFT, BASE_Y_LEFT)
        showBaselines=set(
        ),  # If set, display options defined the type of grid to show.
        showBaselinesBackground=set(
        ),  # If set, display options defined the type of grid to show on background.
        showLeading=False,  # Show distance of leading on the side [LEFT, RIGHT]

        # Flow stuff
        showFlowConnections=False,
        showTextOverflowMarker=
        False,  # If True, a [+] marker is shown where text boxes have overflow.

        # Image stuff
        showImageReference=False,

        # Spread stuff
        showSpreadPages=
        False,  # Show even/odd pages as spread, as well as pages that share the same pagenumber.

        # CSS flags
        cssVerbose=
        True,  # Adds information comments with original values to CSS export.

        # Exporting
        doExport=
        True,  # Flag to turn off any export, e.g. in case of testing with docTest

        # Grid stuff for showing
        viewGridFill=color(r=200 / 255.0, g=230 / 255.0, b=245 / 255.0,
                           a=0.6),  # Fill color for column/row squares.
        viewGridStrokeX=color(
            0.7),  # Stroke of page grid lines in horizontal direction.
        viewGridStrokeWidthX=pt(
            0.5),  # Line thickness of grid lines in horizontal direction.
        viewGridStrokeY=color(
            0.7),  # Stroke of grid lines in vertical direction.
        viewGridStrokeWidthY=pt(
            0.5),  # Line thickness of grid lines in vertical direction.

        # Page padding grid
        viewPaddingStroke=color(
            r=0.4, g=0.4, b=0.7
        ),  # Stroke of page padding lines, if view.showPadding is True
        viewPaddingStrokeWidth=pt(
            0.5),  # Line thickness of the page padding lines.

        # Draw connection arrows between the flow boxes on a page.
        viewFlowConnectionStroke1=color(
            r=0.2, g=0.5, b=0.1,
            a=1),  # Stroke color of flow lines inside column,
        viewFlowConnectionStroke2=color(
            r=1, g=0, b=0, a=1),  # Stroke color of flow lines between columns.
        viewFlowConnectionStrokeWidth=pt(
            1.5),  # Line width of curved flow lines.
        viewFlowMarkerFill=color(r=0.8, g=0.8, b=0.8,
                                 a=0.5),  # Fill of flow curve marker circle.
        viewFlowMarkerSize=pt(8),  # Size of flow marker circle.
        viewFlowCurvatureFactor=
        0.15,  # Factor of curved flow lines. 0 = straight lines.

        # Draw page crop marks if document size (docW, docH) is larger than page (w, h)
        bleedTop=pt0,  # Bleeding images or color rectangles over page edge.
        bleedBottom=pt0,
        bleedRight=pt0,
        bleedLeft=pt0,
        viewCropMarkDistance=pt(8),  # Distance of crop-marks from page frame
        viewCropMarkSize=pt(
            40),  # Length of crop marks, including bleed distance.
        viewCropMarkStrokeWidth=pt(
            0.25),  # Stroke width of crop-marks, registration crosses, etc.
        viewNameFont=DEFAULT_FONT_PATH,  # Name of the page outside frame.
        viewNameFontSize=pt(6),
        viewMarkerFont=DEFAULT_MARKER_FONT,

        # Element info box
        viewInfoFont=DEFAULT_FONT_PATH,  # Font of text in element infoBox.
        viewInfoFontSize=pt(4),  # Font size of text in element info box.
        viewInfoLeading=pt(5),  # Leading of text in element info box.
        viewInfoFill=color(r=0.8, g=0.8, b=0.8,
                           a=0.9),  # Color of text in element info box.
        viewInfoTextFill=color(r=0.1, g=0.1,
                               b=0.1),  # Color of text in element info box.

        # Origin marker, show for view.show
        viewInfoOriginMarkerSize=pt(
            5),  # Radius of the info origin crosshair marker.
        viewInfoOriginMarkerFill=color(
            0.5, 0.5, 0.5, 0.1),  # Color of info origin crosshair marker.
        viewInfoOriginMarkerStroke=
        blackColor,  # Color of info origin crosshair marker.
        viewInfoOriginMarkerStrokeWidth=pt(0.25),

        # Generic element stuff
        viewMissingElementFill=color(
            r=0.7, g=0.7, b=0.7,
            a=0.8),  # Background color of missing element rectangles.
    )

    # Assume all the other arguments overwriting the default values of the root style,
    for name, value in kwargs.items():
        rs[name] = value
    return rs
    def getFSAttrs(cls, t, context, e=None, style=None, w=None, h=None, pixelFit=True):
        fsAttrs = {}

        # Font selection.
        sFont = css('font', e, style)

        if sFont is not None:
            # If the Font instance was supplied, then use it's path.
            if hasattr(sFont, 'path'):
                sFont = sFont.path
            fsAttrs['font'] = sFont
        else:
            fsAttrs['font'] = DEFAULT_FONT_PATH

        sFallbackFont = css('fallbackFont', e, style)

        if isinstance(sFallbackFont, Font):
            sFallbackFont = sFallbackFont.path
        elif sFallbackFont is None:
            sFallbackFont = DEFAULT_FALLBACK_FONT_PATH

        fsAttrs['fallbackFont'] = sFallbackFont

        '''
        If there is a target (pixel) width or height defined, ignore the
        requested fontSize and try the width or height first for fontSize =
        100. The resulting width or height is then used as base value to
        calculate the needed point size.

        Forced fontSize, then this overwrites the style['fontSize'] if it is
        there.

        TODO: add calculation of rFontSize (relative float based on
        root-fontSize) here too.
        '''
        if w is not None or h is not None:
            uFontSize = pt(100) # Start with large font size to scale for fitting.
        else:
            # May be scaled to fit w or h if target is defined.
            uFontSize = css('fontSize', e, style, default=DEFAULT_FONT_SIZE)

        if uFontSize is not None:
            # Remember as base for relative units.
            fsAttrs['fontSize'] = fontSizePt = upt(uFontSize)
        else:
            fontSizePt = DEFAULT_FONT_SIZE

        uLeading = css('leading', e, style)

        # Base for em or percent.
        fsAttrs['lineHeight'] = upt(uLeading or DEFAULT_LEADING, base=fontSizePt)

        # Color values for text fill
        # Color: Fill the text with this color instance
        # noColor: Set the value to None, no fill will be drawn
        # inheritColor: Don't set color, inherit the current setting for fill
        cFill = css('textFill', e, style, default=blackColor)

        if cFill is not inheritColor:
            if isinstance(cFill, (tuple, list, int, float)):
                cFill = color(cFill)
            elif cFill is None:
                cFill = noColor
            assert isinstance(cFill, Color), ('DrawBotString.newString: Fill color "%s" is not Color in style %s' % (cFill, style))
            if cFill is noColor:
                fsAttrs['fill'] = None
            elif cFill.isCmyk:
                fsAttrs['cmykFill'] = cFill.cmyk
            elif cFill.isRgba:
                fsAttrs['fill'] = cFill.rgba
            else:
                fsAttrs['fill'] = cFill.rgb

        # Color values for text stroke
        # Color: Stroke the text with this color instance
        # noColor: Set the value to None, no stroke will be drawn
        # inheritColor: Don't set color, inherit the current setting for stroke
        cStroke = css('textStroke', e, style, default=noColor)
        strokeWidth = css('textStrokeWidth', e, style)

        if strokeWidth is not None:
            assert isUnit(strokeWidth), ('DrawBotString.newString: strokeWidth %s must of type Unit' % strokeWidth)
            fsAttrs['strokeWidth'] = upt(strokeWidth, base=fontSizePt)

        if cStroke is not inheritColor:
            if isinstance(cStroke, (tuple, list, int, float)):
                cStroke = color(cStroke)
            elif cStroke is None:
                cStroke = noColor

            assert isinstance(cStroke, Color), ('DrawBotString.newString] Stroke color "%s" is not Color in style %s' % (cStroke, style))

            if cStroke is noColor: # None is value to disable stroke drawing
                fsAttrs['stroke'] = None
            elif cStroke.isCmyk:
                fsAttrs['cmykStroke'] = cStroke.cmyk
            elif cStroke.isRgba:
                fsAttrs['stroke'] = cStroke.rgba
            else:
                fsAttrs['stroke'] = cStroke.rgb

        # NOTE: xAlign is used for element alignment, not text.
        sAlign = css('xTextAlign', e, style)

        if sAlign is not None: # yTextAlign must be solved by parent container element.
            fsAttrs['align'] = sAlign

        sUnderline = css('underline', e, style)

        if sUnderline in ('single', None): # Only these values work in FormattedString
            fsAttrs['underline'] = sUnderline

        uParagraphTopSpacing = css('paragraphTopSpacing', e, style)

        if uParagraphTopSpacing is not None:
            fsAttrs['paragraphTopSpacing'] = upt(uParagraphTopSpacing, base=fontSizePt) # Base for em or perc

        uParagraphBottomSpacing = css('paragraphBottomSpacing', e, style)

        if uParagraphBottomSpacing:
            fsAttrs['paragraphBottomSpacing'] = upt(uParagraphBottomSpacing, base=fontSizePt) # Base for em or perc

        uTracking = css('tracking', e, style)

        if uTracking is not None:
            fsAttrs['tracking'] = upt(uTracking, base=fontSizePt) # Base for em or perc

        uBaselineShift = css('baselineShift', e, style)

        if uBaselineShift is not None:
            fsAttrs['baselineShift'] = upt(uBaselineShift, base=fontSizePt) # Base for em or perc

        openTypeFeatures = css('openTypeFeatures', e, style)

        if openTypeFeatures is not None:
            fsAttrs['openTypeFeatures'] = openTypeFeatures

        tabs = []
        for tab in (css('tabs', e, style) or []): # Can be [(10, LEFT), ...] or [10, 20, ...]
            if not isinstance(tab, (list, tuple)):
                tab = upt(tab), LEFT
            else:
                tab = upt(tab[0]), tab[1]
            tabs.append(tab)
        if tabs:
            fsAttrs['tabs'] = tabs

        # Set the hyphenation flag from style, as in DrawBot this is set by a
        # global function, not as FormattedString attribute.
        # FIX IN DRAWBOT fsAttrs['language'] = bool(css('language', e, style))
        # FIX IN DRAWBOT
        #fsAttrs['hyphenation'] = bool(css('hyphenation', e, style))

        uFirstLineIndent = css('firstLineIndent', e, style)
        # TODO: Use this value instead, if current tag is different from
        # previous tag. How to get this info?
        # firstTagIndent = style.get('firstTagIndent')
        # TODO: Use this value instead, if currently on top of a new string.
        if uFirstLineIndent is not None:
            fsAttrs['firstLineIndent'] = upt(uFirstLineIndent, base=fontSizePt) # Base for em or perc

        uIndent = css('indent', e, style)

        if uIndent is not None:
            fsAttrs['indent'] = upt(uIndent, base=fontSizePt) # Base for em or perc

        uTailIndent = css('tailIndent', e, style)
        if uTailIndent is not None:
            fsAttrs['tailIndent'] = upt(uTailIndent, base=fontSizePt) # Base for em or perc

        sLanguage = css('language', e, style)

        if sLanguage is not None:
            fsAttrs['language'] = sLanguage

        return fsAttrs