blob: 44dc2e716aa15a1526959dadfb4c2dcf83931ef8 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
#pragma once
/**
\file
# %CSSStyle
A CSSStyle object represents a set of rules in a CSS stylesheet which all have a common **base
selector**, consisting of an element name and optional class name (e.g. `h1` or `h1.Appendix`). The
base selector is used to identify the style in a CSSSheet object, and is passed to
CSSSheetLookupSelector() to add or retrieve a particular style.
Each CSSStyle object has one or more **rules** associated with it. A rule is identified by its
**suffix**, which is the text that comes immediately after the base selector in the textual
representation of the stylesheet. For example, a rule defined in a CSS stylesheet with a selector
of `h1.Appendix::before` would have a base selector of `h1.Appendix`, and a suffix of `::before`.
In the typical case, a style will have only a single rule with an empty suffix, and this rule will
define all of the properties of that style. For example, a style with a base selector of `p.Warning`
and properties defining the color and font size would appear in the textual representation of the
stylesheet like this:
p.Warning {
color: red;
font-size: 18pt;
}
In other cases, a style may have multiple rules, each with a different suffix. Each rule defines
the properties to be applied to a certain aspect of that element, such as particular rows of a
table. For example, style with a base selector of `table.Statistics` which defines a black border,
and colors odd rows blue and odd rows red, would have three separate rules:
table.Statistics {
border: 1px solid black;
}
table.Statistics > * > tr:nth-of-type(odd) > td {
color: blue;
}
table.Statistics > * > tr:nth-of-type(even) > td {
color: red;
}
## Creating styles
You should not create CSSStyle objects directly --- instead, call CSSSheetLookupSelector() with
the `add` parameter set to 1. This ensures that if there is already a style with that selector
present in the stylesheet, you will not end up with a duplicate. CSSSheetLookupSelector() returns
the new or existing style, and calls CSSStyleNew() internally if necessary.
All though CSSStyle objects are reference-counted, you generally do not need to retain or release
them yourself, as the CSSSheet object itself maintains a reference to all styles within it. When
a CSSSheet object is freed, it releases all references to the styles it contains, so assuming there
are no additional references to them, they will be freed at that time as well.
## Working with rules
A rule is represented by a CSSProperties object. The main function for accessing rules is
CSSStyleRuleForSuffix(), which requires the desired suffix to be specified. There are also several
short-hand funcitons for obtaining certain commonly-used rules; CSSStyleRule() returns the default
rule (the one with an empty suffix), while CSSStyleCell() and CSSStyleBefore() returns the rules
for table cells and content displayed before an element, respectively.
When you call any of these functions and the rule does not exist, it is created. When the
stylesheet is converted to its textual representation using CSSSheetCopyCSSText(), empty rules
(those with no properties) are excluded --- so it is never necessary to remove a rule as such.
You can, however, clear all the properties on it, which will have the same effect.
## Style inheritance
To support working with documents in formats like docx and ODF, which support style inheritance, we
use the special `-uxwrite-parent` CSS property to record the base selector of the parent style, if
any. This should not be set directly, however, as some characters in the style name may not be
valid in CSS values. Instead, the parent value is stored as a quoted string, according to the
escaping rules defined in the CSS specification. You should always use CSSStyleCopyParent() and
CSSStyleSetParent() to access this property; as this handles quoting for you.
Note that since CSSStyle objects do not contain direct references to each other or the stylesheet
in which they are contained, if you want to obtain the actual CSSStyle object representing the
parent style, you need to do so from the CSSSheet object. This can be done using
CSSSheetParentOfStyle().
## Style for next paragraph
Microsoft Word and OpenOffice both allow a style to specify which style should be used for the next
paragraph, if the user presses enter within a paragraph of the given style. For example, it is
typical for heading styles to specify "Normal" as the next style, so when a user types in a heading
and presses enter, the program adds a normal paragraph, instead of another heading.
As with parent styles, we use a special property to record this, `-uxwrite-next`. The UX Write
editing code knows about this property, and uses it to determine what style to associate with
newly-created paragraphs after enter is pressed. As with the `-uxwrite-parent` property, this
needs to be quoted, and you should always use CSSStyleCopyNext() and CSSStyleSetNext() to access it.
@see CSSSheet.h
@see CSSProperties.h
*/
#include <DocFormats/DFXMLForward.h>
#include "CSSSelector.h"
#include "CSSProperties.h"
#include "DFCallback.h"
#include "DFTypes.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// CSSStyle //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
#define DFTableSuffixWholeTable ""
#define DFTableSuffixCell " > * > tr > td"
#define DFTableSuffixFirstRow " > * > tr:first-of-type > td"
#define DFTableSuffixLastRow " > * > tr:last-of-type > td"
#define DFTableSuffixFirstCol " > * > tr > td:first-of-type"
#define DFTableSuffixLastCol " > * > tr > td:last-of-type"
#define DFTableSuffixBand1Vert " > * > tr:nth-of-type(odd) > td"
#define DFTableSuffixBand2Vert " > * > tr:nth-of-type(even) > td"
#define DFTableSuffixBand1Horz " > * > tr > td:nth-of-type(odd)"
#define DFTableSuffixBand2Horz " > * > tr > td:nth-of-type(even)"
#define DFTableSuffixNWCell " > * > tr:first-of-type > td:first-of-type"
#define DFTableSuffixNECell " > * > tr:first-of-type > td:last-of-type"
#define DFTableSuffixSWCell " > * > tr:last-of-type > td:first-of-type"
#define DFTableSuffixSECell " > * > tr:last-of-type > td:last-of-type"
typedef struct CSSStyle CSSStyle;
struct CSSStyle {
size_t retainCount;
char *selector;
char *elementName;
char *className;
Tag tag;
int headingLevel; // 0 for all non-heading styles, 1-6 otherwise
StyleFamily family;
struct DFHashTable *rules;
DFCallback *changeCallbacks;
int latent;
int dirty;
};
/**
Create a new CSSStyle object, with the specified identifier. In addition to initialising the
`selector` field, this also sets the `elementName`, `className`, `tag`, `headingLevel`, and `family`
fields based on the content of the selector.
When working with stylesheets, you should never call this function yourself. Instead, call
CSSSheetLookupSelector() with the `add` parameter set to 1. This way, if a style already exists
in the stylesheet with the same name, you will not end up with a duplicate.
*/
CSSStyle *CSSStyleNew(const char *ident);
CSSStyle *CSSStyleRetain(CSSStyle *style);
void CSSStyleRelease(CSSStyle *style);
/**
Change the selector associated with the style. Like CSSStyleNew(), this also sets the
`elementName`, `className`, `tag`, `headingLevel`, and `family` fields appropriately.
To maintain consistency with the hash table maintained by CSSStyle, you should remove the style
from the stylesheet before changing the selector, and add it back again afterwards. This way, it
will maintain the correct mapping from selectors to styles, so that subsequent calls to
CSSSheetLookupSelector() with the new selector will work correctly --- i.e. by returning this
object. Note that when removing the style from the stylesheet, the reference count will be
decremented, so you should temporarily grab an additional reference to this object using
CSSStyleRetain() and later release it with CSSStyleRelease(). See replaceSelectorsInSheet() for an
example of where this is done.
*/
void CSSStyleSetSelector(CSSStyle *style, const char *selector);
char *CSSStyleCopyDisplayName(CSSStyle *style);
void CSSStyleSetDisplayName(CSSStyle *style, const char *newDisplayName);
char *CSSStyleCopyParent(CSSStyle *style);
void CSSStyleSetParent(CSSStyle *style, const char *newParent);
char *CSSStyleCopyNext(CSSStyle *style);
void CSSStyleSetNext(CSSStyle *style, const char *newNext);
int CSSStyleIsCustom(CSSStyle *style);
/**
Get the default rule (the one with an empty prefix) for this style
*/
CSSProperties *CSSStyleRule(CSSStyle *style);
int CSSStyleIsEmpty(CSSStyle *style);
/**
Get the rule whose properties apply to cells within a table, assuming this style is a table style.
This is equivalent to calling CSSStyleRuleForSuffix() with a parameter of `" > * > tr > td"`.
*/
CSSProperties *CSSStyleCell(CSSStyle *style);
/**
Get the rule whose properties define content and its appearance that should appear before elements
associated with this style. This is equivalent to calling CSSStyleRuleForSuffix() with a parameter
of `"::before"`.
*/
CSSProperties *CSSStyleBefore(CSSStyle *style);
/**
Retrieve a NULL-terminated array of suffixes corresponding to the rules in this style. This array
can still be used after changes to the style object, as contains its own copy of the suffixes.
The returned array is newly-allocated, and must be freed by the caller. The strings are allocated
in the same block of memory as the array itself, so a single call to `free` is sufficient.
@see DFHashTableCopyKeys()
*/
const char **CSSStyleCopySuffixes(CSSStyle *style);
/**
Get the rule associated with this style that has the specified suffix. The `suffix` parameter must
be non-NULL, but you can pass the empty string to retrive the default rule.
@see CSSStyleRule()
@see CSSStyleCell()
@see CSSStyleBefore()
*/
CSSProperties *CSSStyleRuleForSuffix(CSSStyle *style, const char *suffix);
/**
Get the rule associated with a particular aspect of table formatting, such as the first row or
column. The `tableComponent` parameter should be one of the macros defined near the top of this
file, such as `DFTableSuffixFirstRow`.
*/
CSSProperties *CSSStyleRuleForTableComponent(CSSStyle *style, const char *tableComponent);
/**
Set the default properties that are used by typical web browser for this style. With a regular HTML
file, if you specify e.g. a `h1` element, the browser knows to display that in a larger, bold font.
This is because browsers have internal defaults for certain built-in elements. However, when
converting to other file formats such as docx and ODF, these built-in defaults are not present, so
we need to make the properties explicit.
The purpose of this function is to set those properties, so that a document converted from HTML to
another format will have the defaults set explicitly. It should be called when you are adding a new
style to an non-HTML document, and you want to make sure the apperance matches that of the HTML
file as displayed by a browser.
*/
void CSSStyleAddDefaultHTMLProperties(CSSStyle *style);
void CSSStylePrint(CSSStyle *style, const char *indent);